int mk_conn_error(int socket) { int ret = -1; struct client_session *cs; struct sched_list_node *sched; MK_TRACE("Connection Handler, error on FD %i", socket); /* Plugin hook */ ret = mk_plugin_event_error(socket); switch(ret) { case MK_PLUGIN_RET_EVENT_OWNED: return MK_PLUGIN_RET_CONTINUE; case MK_PLUGIN_RET_EVENT_CLOSE: return -1; case MK_PLUGIN_RET_EVENT_CONTINUE: break; /* just return controller to invoker */ } sched = mk_sched_get_thread_conf(); mk_sched_remove_client(sched, socket); cs = mk_session_get(socket); if (cs) { mk_session_remove(socket); } return 0; }
static void mk_socket_safe_event_write(int socket) { struct sched_list_node *sched; sched = mk_sched_get_thread_conf(); MK_TRACE("[FD %i] Safe event write ON", socket); mk_event_add(sched->loop, socket, MK_EVENT_WRITE, NULL); }
int mk_conn_read(int socket) { int ret; struct client_session *cs; struct sched_list_node *sched; MK_TRACE("[FD %i] Connection Handler / read", socket); /* Plugin hook */ ret = mk_plugin_event_read(socket); switch(ret) { case MK_PLUGIN_RET_EVENT_OWNED: return MK_PLUGIN_RET_CONTINUE; case MK_PLUGIN_RET_EVENT_CLOSE: return -1; case MK_PLUGIN_RET_EVENT_CONTINUE: break; /* just return controller to invoker */ } sched = mk_sched_get_thread_conf(); cs = mk_session_get(socket); if (!cs) { /* Note: Linux don't set TCP_NODELAY socket flag by default */ if (mk_socket_set_tcp_nodelay(socket) != 0) { mk_warn("TCP_NODELAY failed"); } /* Create session for the client */ MK_TRACE("[FD %i] Create session", socket); cs = mk_session_create(socket, sched); if (!cs) { return -1; } } /* Read incomming data */ ret = mk_handler_read(socket, cs); if (ret > 0) { if (mk_http_pending_request(cs) == 0) { mk_epoll_change_mode(sched->epoll_fd, socket, MK_EPOLL_WRITE, MK_EPOLL_LEVEL_TRIGGERED); } else if (cs->body_length + 1 >= config->max_request_size) { /* * Request is incomplete and our buffer is full, * close connection */ mk_session_remove(socket); return -1; } else { MK_TRACE("[FD %i] waiting for pending data", socket); } } return ret; }
static void mk_socket_safe_event_write(int socket) { struct sched_list_node *sched; sched = mk_sched_get_thread_conf(); MK_TRACE("[FD %i] Safe event write ON", socket); mk_epoll_change_mode(sched->epoll_fd, socket, MK_EPOLL_WRITE, MK_EPOLL_LEVEL_TRIGGERED); }
/* * Move a http thread context from sched->thread to sched->threads_purge list. * On this way the scheduler will release or reasign the resource later. */ int mk_http_thread_purge(struct mk_http_thread *mth, int close) { struct mk_sched_worker *sched; sched = mk_sched_get_thread_conf(); if (!sched) { return -1; } mth->close = close; mk_list_del(&mth->_head); mk_list_add(&mth->_head, &sched->threads_purge); return 0; }
void *mk_epoll_init(int efd, mk_epoll_handlers * handler, int max_events) { int i, fd, ret = -1; int num_fds; int fds_timeout; struct epoll_event *events; struct sched_list_node *sched; /* Get thread conf */ sched = mk_sched_get_thread_conf(); fds_timeout = log_current_utime + config->timeout; events = mk_mem_malloc_z(max_events*sizeof(struct epoll_event)); while (1) { ret = -1; num_fds = epoll_wait(efd, events, max_events, MK_EPOLL_WAIT_TIMEOUT); for (i = 0; i < num_fds; i++) { fd = events[i].data.fd; if (events[i].events & EPOLLIN) { MK_TRACE("[FD %i] EPoll Event READ", fd); ret = (*handler->read) (fd); } else if (events[i].events & EPOLLOUT) { MK_TRACE("[FD %i] EPoll Event WRITE", fd); ret = (*handler->write) (fd); } else if (events[i].events & (EPOLLHUP | EPOLLERR | EPOLLRDHUP)) { MK_TRACE("[FD %i] EPoll Event EPOLLHUP/EPOLLER", fd); ret = (*handler->error) (fd); } if (ret < 0) { MK_TRACE("[FD %i] Epoll Event FORCE CLOSE | ret = %i", fd, ret); (*handler->close) (fd); } } /* Check timeouts and update next one */ if (log_current_utime >= fds_timeout) { mk_sched_check_timeouts(sched); fds_timeout = log_current_utime + config->timeout; } } }
struct mk_http_thread *mk_http_thread_create(int type, struct mk_vhost_handler *handler, struct mk_http_session *session, struct mk_http_request *request, int n_params, struct mk_list *params) { size_t stack_size; struct mk_thread *th = NULL; struct mk_http_thread *mth; struct mk_sched_worker *sched; sched = mk_sched_get_thread_conf(); if (!sched) { return NULL; } th = mk_thread_new(sizeof(struct mk_http_thread), NULL); if (!th) { return NULL; } mth = (struct mk_http_thread *) MK_THREAD_DATA(th); if (!mth) { return NULL; } mth->session = session; mth->request = request; mth->parent = th; mth->close = MK_FALSE; request->thread = mth; mk_list_add(&mth->_head, &sched->threads); th->caller = co_active(); th->callee = co_create(MK_THREAD_STACK_SIZE, thread_cb_init_vars, &stack_size); #ifdef MK_HAVE_VALGRIND th->valgrind_stack_id = VALGRIND_STACK_REGISTER(th->callee, ((char *)th->callee) + stack_size); #endif /* Workaround for makecontext() */ thread_params_set(th, type, handler, session, request, n_params, params); return mth; }
int mk_http_request_end(int socket) { int ka; struct client_session *cs; struct sched_list_node *sched; sched = mk_sched_get_thread_conf(); cs = mk_session_get(socket); if (!cs) { MK_TRACE("[FD %i] Not found", socket); return -1; } if (mk_unlikely(!sched)) { MK_TRACE("Could not find sched list node :/"); return -1; } /* * We need to ask to http_keepalive if this * connection can continue working or we must * close it. */ ka = mk_http_keepalive_check(cs); mk_request_free_list(cs); if (ka < 0) { MK_TRACE("[FD %i] No KeepAlive mode, remove", socket); mk_session_remove(socket); } else { mk_request_ka_next(cs); mk_epoll_change_mode(sched->epoll_fd, socket, MK_EPOLL_READ, MK_EPOLL_LEVEL_TRIGGERED); return 0; } return -1; }
int mk_conn_close(int socket) { int ret = -1; struct sched_list_node *sched; MK_TRACE("[FD %i] Connection Handler, closed", socket); /* Plugin hook */ ret = mk_plugin_event_close(socket); switch(ret) { case MK_PLUGIN_RET_EVENT_OWNED: return MK_PLUGIN_RET_CONTINUE; case MK_PLUGIN_RET_EVENT_CLOSE: return -1; case MK_PLUGIN_RET_EVENT_CONTINUE: break; /* just return controller to invoker */ } sched = mk_sched_get_thread_conf(); mk_sched_remove_client(sched, socket); return 0; }
int mk_conn_close(int socket, int event) { struct sched_list_node *sched; MK_TRACE("[FD %i] Connection Handler, closed", socket); /* Plugin hook: this is a wrap-workaround to do not * break plugins until the whole interface events and * return values are re-worked. */ if (event == MK_EP_SOCKET_CLOSED) mk_plugin_event_close(socket); else if (event == MK_EP_SOCKET_ERROR) { mk_plugin_event_error(socket); } else if (event == MK_EP_SOCKET_TIMEOUT) { mk_plugin_event_timeout(socket); } sched = mk_sched_get_thread_conf(); mk_sched_remove_client(sched, socket); return 0; }
int mk_conn_write(int socket) { int ret = -1; struct client_session *cs; struct sched_list_node *sched; struct sched_connection *conx; MK_TRACE("[FD %i] Connection Handler / write", socket); /* Plugin hook */ ret = mk_plugin_event_write(socket); switch(ret) { case MK_PLUGIN_RET_EVENT_OWNED: return MK_PLUGIN_RET_CONTINUE; case MK_PLUGIN_RET_EVENT_CLOSE: return -1; case MK_PLUGIN_RET_EVENT_CONTINUE: break; /* just return controller to invoker */ } MK_TRACE("[FD %i] Normal connection write handling", socket); sched = mk_sched_get_thread_conf(); conx = mk_sched_get_connection(sched, socket); if (!conx) { MK_TRACE("[FD %i] Registering new connection"); if (mk_sched_register_client(socket, sched) == -1) { MK_TRACE("[FD %i] Close requested", socket); return -1; } mk_epoll_change_mode(sched->epoll_fd, socket, MK_EPOLL_READ, MK_EPOLL_LEVEL_TRIGGERED); return 0; } mk_sched_update_conn_status(sched, socket, MK_SCHEDULER_CONN_PROCESS); /* Get node from schedule list node which contains * the information regarding to the current client/socket */ cs = mk_session_get(socket); if (!cs) { /* This is a ghost connection that doesn't exist anymore. * Closing it could accidentally close some other thread's * socket, so pass it to remove_client that checks it's ours. */ mk_sched_remove_client(sched, socket); return 0; } ret = mk_handler_write(socket, cs); /* if ret < 0, means that some error * happened in the writer call, in the * other hand, 0 means a successful request * processed, if ret > 0 means that some data * still need to be send. */ if (ret < 0) { mk_request_free_list(cs); mk_session_remove(socket); return -1; } else if (ret == 0) { return mk_http_request_end(socket); } else if (ret > 0) { return 0; } /* avoid to make gcc cry :_( */ return -1; }
int mk_conn_read(int socket) { int ret; struct client_session *cs; struct sched_list_node *sched; MK_TRACE("[FD %i] Connection Handler / read", socket); /* Plugin hook */ ret = mk_plugin_event_read(socket); switch(ret) { case MK_PLUGIN_RET_EVENT_OWNED: return MK_PLUGIN_RET_CONTINUE; case MK_PLUGIN_RET_EVENT_CLOSE: return -1; case MK_PLUGIN_RET_EVENT_CONTINUE: break; /* just return controller to invoker */ } sched = mk_sched_get_thread_conf(); cs = mk_session_get(socket); if (!cs) { /* Check if is this a new connection for the Scheduler */ if (!mk_sched_get_connection(sched, socket)) { MK_TRACE("[FD %i] Registering new connection"); if (mk_sched_register_client(socket, sched) == -1) { MK_TRACE("[FD %i] Close requested", socket); return -1; } /* * New connections are not registered yet into the epoll * event state list, we need to do it manually */ mk_epoll_state_set(socket, MK_EPOLL_READ, MK_EPOLL_LEVEL_TRIGGERED, (EPOLLERR | EPOLLHUP | EPOLLRDHUP | EPOLLIN)); return 0; } /* Create session for the client */ MK_TRACE("[FD %i] Create session", socket); cs = mk_session_create(socket, sched); if (!cs) { return -1; } } /* Read incomming data */ ret = mk_handler_read(socket, cs); if (ret > 0) { if (mk_http_pending_request(cs) == 0) { mk_epoll_change_mode(sched->epoll_fd, socket, MK_EPOLL_WRITE, MK_EPOLL_LEVEL_TRIGGERED); } else if (cs->body_length + 1 >= (unsigned int) config->max_request_size) { /* * Request is incomplete and our buffer is full, * close connection */ mk_session_remove(socket); return -1; } else { MK_TRACE("[FD %i] waiting for pending data", socket); } } return ret; }
int mk_conn_read(int socket) { int ret; int status; struct mk_http_session *cs; struct mk_http_request *sr; struct sched_list_node *sched; MK_TRACE("[FD %i] Connection Handler / read", socket); /* Plugin hook */ ret = mk_plugin_event_read(socket); switch (ret) { case MK_PLUGIN_RET_EVENT_OWNED: return MK_PLUGIN_RET_CONTINUE; case MK_PLUGIN_RET_EVENT_CLOSE: return -1; case MK_PLUGIN_RET_EVENT_CONTINUE: break; /* just return controller to invoker */ } sched = mk_sched_get_thread_conf(); cs = mk_http_session_get(socket); if (!cs) { /* Check if is this a new connection for the Scheduler */ if (!mk_sched_get_connection(sched, socket)) { MK_TRACE("[FD %i] Registering new connection"); if (mk_sched_register_client(socket, sched) == -1) { MK_TRACE("[FD %i] Close requested", socket); return -1; } /* * New connections are not registered yet into the * events loop, we need to do it manually: */ mk_event_add(sched->loop, socket, MK_EVENT_READ, NULL); return 0; } /* Create session for the client */ MK_TRACE("[FD %i] Create session", socket); cs = mk_http_session_create(socket, sched); if (!cs) { return -1; } } /* Invoke the read handler, on this case we only support HTTP (for now :) */ ret = mk_http_handler_read(socket, cs); if (ret > 0) { if (mk_list_is_empty(&cs->request_list) == 0) { /* Add the first entry */ sr = &cs->sr_fixed; mk_list_add(&sr->_head, &cs->request_list); mk_http_request_init(cs, sr); } else { sr = mk_list_entry_first(&cs->request_list, struct mk_http_request, _head); } status = mk_http_parser(sr, &cs->parser, cs->body, cs->body_length); if (status == MK_HTTP_PARSER_OK) { MK_TRACE("[FD %i] HTTP_PARSER_OK", socket); mk_http_status_completed(cs); mk_event_add(sched->loop, socket, MK_EVENT_WRITE, NULL); } else if (status == MK_HTTP_PARSER_ERROR) { mk_http_session_remove(socket); MK_TRACE("[FD %i] HTTP_PARSER_ERROR", socket); return -1; } else { MK_TRACE("[FD %i] HTTP_PARSER_PENDING", socket); } } if (ret == -EAGAIN) { return 1; } return ret; }
static inline void thread_cb_init_vars() { int close; int type = libco_param.type; struct mk_vhost_handler *handler = libco_param.handler; struct mk_http_session *session = libco_param.session; struct mk_http_request *request = libco_param.request; struct mk_thread *th = libco_param.th; struct mk_http_thread *mth; //struct mk_plugin *plugin; /* * Until this point the th->callee already set the variables, so we * wait until the core wanted to resume so we really trigger the * output callback. */ co_switch(th->caller); if (type == MK_HTTP_THREAD_LIB) { /* Invoke the handler callback */ handler->cb(request, handler->data); /* * Once the callback finished, we need to sanitize the connection * so other further requests can be processed. */ int ret; struct mk_sched_worker *sched; struct mk_channel *channel; channel = request->session->channel; sched = mk_sched_get_thread_conf(); MK_EVENT_NEW(channel->event); ret = mk_event_add(sched->loop, channel->fd, MK_EVENT_CONNECTION, MK_EVENT_READ, channel->event); if (ret == -1) { //return -1; } /* Save temporal session */ mth = request->thread; /* * Finalize request internally, if ret == -1 means we should * ask to shutdown the connection. */ ret = mk_http_request_end(session, session->server); if (ret == -1) { close = MK_TRUE; } else { close = MK_FALSE; } mk_http_thread_purge(mth, close); /* Return control to caller */ mk_thread_yield(th); } else if (type == MK_HTTP_THREAD_PLUGIN) { /* FIXME: call plugin handler callback with params */ } }