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); }
/* Create a new mqtt request instance */ struct tcp_conn *tcp_conn_add(int fd, struct flb_in_tcp_config *ctx) { int ret; struct tcp_conn *conn; struct mk_event *event; conn = flb_malloc(sizeof(struct tcp_conn)); if (!conn) { return NULL; } /* Set data for the event-loop */ event = &conn->event; MK_EVENT_NEW(event); event->fd = fd; event->type = FLB_ENGINE_EV_CUSTOM; event->handler = tcp_conn_event; /* Connection info */ conn->fd = fd; conn->ctx = ctx; conn->buf_len = 0; conn->rest = 0; conn->status = TCP_NEW; conn->buf_data = flb_malloc(ctx->chunk_size); if (!conn->buf_data) { perror("malloc"); close(fd); flb_error("[in_tcp] could not allocate new connection"); flb_free(conn); return NULL; } conn->buf_size = ctx->chunk_size; conn->in = ctx->in; /* Initialize JSON parser */ flb_pack_state_init(&conn->pack_state); /* Register instance into the event loop */ ret = mk_event_add(ctx->evl, fd, FLB_ENGINE_EV_CUSTOM, MK_EVENT_READ, conn); if (ret == -1) { flb_error("[in_tcp] could not register new connection"); close(fd); flb_free(conn->buf_data); flb_free(conn); return NULL; } mk_list_add(&conn->_head, &ctx->connections); return conn; }
static inline int io_tls_event_switch(struct flb_io_upstream *u, int mask) { int ret; struct mk_event *event; event = &u->event; if (event->mask & ~mask) { ret = mk_event_add(u->evl, event->fd, FLB_ENGINE_EV_THREAD, mask, &u->event); if (ret == -1) { flb_error("[io_tls] error changing mask to %i", mask); return -1; } } return 0; }
static inline struct mk_sched_conn *mk_server_listen_handler(struct mk_sched_worker *sched, void *data) { int ret; int client_fd = -1; struct mk_sched_conn *conn; struct mk_server_listen *listener = data; client_fd = mk_socket_accept(listener->server_fd); if (mk_unlikely(client_fd == -1)) { MK_TRACE("[server] Accept connection failed: %s", strerror(errno)); goto error; } conn = mk_sched_add_connection(client_fd, listener, sched); if (mk_unlikely(!conn)) { goto error; } ret = mk_event_add(sched->loop, client_fd, MK_EVENT_CONNECTION, MK_EVENT_READ, conn); if (mk_unlikely(ret != 0)) { mk_err("[server] Error registering file descriptor: %s", strerror(errno)); goto error; } sched->accepted_connections++; MK_TRACE("[server] New connection arrived: FD %i", client_fd); return conn; error: if (client_fd != -1) { listener->network->network->close(client_fd); } return NULL; }
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; }
int mk_conn_write(int socket) { int ret = -1; struct mk_http_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_event_add(sched->loop, socket, MK_EVENT_READ, NULL); 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_http_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_drop_connection(socket); return 0; } ret = mk_http_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 < MK_CHANNEL_ERROR) { mk_sched_drop_connection(socket); return -1; } else if (ret == MK_CHANNEL_DONE) { MK_TRACE("[FD %i] Request End", socket); return mk_http_request_end(socket); } else if (ret == MK_CHANNEL_FLUSH) { return 0; } /* avoid to make gcc cry :_( */ return -1; }
FLB_INLINE int flb_io_net_connect(struct flb_io_upstream *u, struct flb_thread *th) { int fd; int ret; int error = 0; socklen_t len = sizeof(error); if (u->fd > 0) { close(u->fd); } /* Create the socket */ fd = flb_net_socket_create(AF_INET, FLB_TRUE); if (fd == -1) { flb_error("[io] could not create socket"); return -1; } u->fd = fd; /* Make the socket non-blocking */ flb_net_socket_nonblocking(u->fd); /* Start the connection */ ret = flb_net_tcp_fd_connect(fd, u->tcp_host, u->tcp_port); if (ret == -1) { if (errno == EINPROGRESS) { flb_debug("[upstream] connection in process"); } else { } u->event.mask = MK_EVENT_EMPTY; u->event.status = MK_EVENT_NONE; u->thread = th; ret = mk_event_add(u->evl, fd, FLB_ENGINE_EV_THREAD, MK_EVENT_WRITE, &u->event); if (ret == -1) { /* * If we failed here there no much that we can do, just * let the caller we failed */ close(fd); return -1; } /* * Return the control to the parent caller, we need to wait for * the event loop to get back to us. */ flb_thread_yield(th, FLB_FALSE); /* We got a notification, remove the event registered */ ret = mk_event_del(u->evl, &u->event); assert(ret == 0); /* Check the connection status */ if (u->event.mask & MK_EVENT_WRITE) { ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len); if (ret == -1) { flb_error("[io] could not validate socket status"); return -1; } if (error != 0) { /* Connection is broken, not much to do here */ flb_debug("[io] connection failed"); return -1; } u->event.mask = MK_EVENT_EMPTY; u->event.status = MK_EVENT_NONE; } else { return -1; } } flb_debug("[io] connection OK"); return 0; }
/* This routine is called from a co-routine thread */ FLB_INLINE int net_io_write(struct flb_thread *th, struct flb_io_upstream *u, void *data, size_t len, size_t *out_len) { int ret = 0; ssize_t bytes; size_t total = 0; retry: bytes = write(u->fd, data + total, len - total); flb_debug("[io] write(2)=%d", bytes); if (bytes == -1) { if (errno == EAGAIN) { return 0; } else { /* The connection routine may yield */ flb_debug("[io] trying to reconnect"); ret = flb_io_net_connect(u, th); if (ret == -1) { return -1; } /* * Reach here means the connection was successful, let's retry * to write(2). * * note: the flb_io_connect() uses asyncronous sockets, register * the file descriptor with the main event loop and yields until * the main event loop returns the control back (resume), doing a * 'retry' it's pretty safe. */ goto retry; } } /* Update statistics */ //flb_stats_update(out->stats_fd, ret, 0); /* Update counters */ total += bytes; if (total < len) { if (u->event.status == MK_EVENT_NONE) { u->event.mask = MK_EVENT_EMPTY; u->thread = th; ret = mk_event_add(u->evl, u->fd, FLB_ENGINE_EV_THREAD, MK_EVENT_WRITE, &u->event); if (ret == -1) { /* * If we failed here there no much that we can do, just * let the caller we failed */ close(u->fd); return -1; } } flb_thread_yield(th, MK_FALSE); goto retry; } if (u->event.status == MK_EVENT_REGISTERED) { /* We got a notification, remove the event registered */ ret = mk_event_del(u->evl, &u->event); assert(ret == 0); } *out_len = total; return bytes; }
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 */ } }
/* * This routine perform a TCP connection and the required TLS/SSL * handshake, */ FLB_INLINE int flb_io_net_tls_connect(struct flb_io_upstream *u, struct flb_thread *th) { int fd; int ret; int error = 0; int flag; socklen_t len = sizeof(error); struct flb_tls_session *session; if (u->fd > 0) { close(u->fd); } /* Create the socket */ fd = flb_net_socket_create(AF_INET, FLB_TRUE); if (fd == -1) { flb_error("[io] could not create socket"); return -1; } u->fd = fd; /* Make the socket non-blocking */ flb_net_socket_nonblocking(u->fd); /* Start the connection */ ret = flb_net_tcp_fd_connect(fd, u->tcp_host, u->tcp_port); if (ret == -1) { if (errno == EINPROGRESS) { flb_debug("[upstream] connection in process"); } else { close(u->fd); if (u->tls_session) { tls_session_destroy(u->tls_session); u->tls_session = NULL; } return -1; } MK_EVENT_NEW(&u->event); u->thread = th; ret = mk_event_add(u->evl, fd, FLB_ENGINE_EV_THREAD, MK_EVENT_WRITE, &u->event); if (ret == -1) { /* * If we failed here there no much that we can do, just * let the caller we failed */ flb_error("[io_tls] connect failed registering event"); close(fd); return -1; } /* * Return the control to the parent caller, we need to wait for * the event loop to get back to us. */ flb_thread_yield(th, FLB_FALSE); /* Check the connection status */ if (u->event.mask & MK_EVENT_WRITE) { ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len); if (ret == -1) { flb_error("[io_tls] could not validate socket status"); goto error; } if (error != 0) { /* Connection is broken, not much to do here */ flb_error("[io_tls] connection failed"); goto error; } } else { return -1; } } /* Configure TLS and prepare handshake */ session = u->tls_session; mbedtls_ssl_set_bio(&session->ssl, u, mbedtls_net_send, mbedtls_net_recv, NULL); retry_handshake: ret = mbedtls_ssl_handshake(&session->ssl); if (ret != 0) { if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { io_tls_error(ret); goto error; } if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) { flag = MK_EVENT_WRITE; } else if (ret == MBEDTLS_ERR_SSL_WANT_READ) { flag = MK_EVENT_READ; } else { } /* * FIXME: if we need multiple reads we are invoking the same * system call multiple times. */ ret = mk_event_add(u->evl, u->event.fd, FLB_ENGINE_EV_THREAD, flag, &u->event); if (ret == -1) { goto error; } flb_thread_yield(th, FLB_FALSE); goto retry_handshake; } else { flb_debug("[io_tls] Handshake OK"); } if (u->event.status == MK_EVENT_REGISTERED) { mk_event_del(u->evl, &u->event); } flb_debug("[io_tls] connection OK"); return 0; error: if (u->event.status == MK_EVENT_REGISTERED) { mk_event_del(u->evl, &u->event); } return -1; }