void li_connection_reset(liConnection *con) { if (LI_CON_STATE_DEAD != con->state) { con->state = LI_CON_STATE_DEAD; con_iostream_close(con); li_stream_reset(&con->in); li_stream_reset(&con->out); li_vrequest_reset(con->mainvr, TRUE); li_stream_release(&con->in); li_stream_release(&con->out); con->info.keep_alive = TRUE; if (con->keep_alive_data.link) { g_queue_delete_link(&con->wrk->keep_alive_queue, con->keep_alive_data.link); con->keep_alive_data.link = NULL; } con->keep_alive_data.timeout = 0; con->keep_alive_data.max_idle = 0; li_event_stop(&con->keep_alive_data.watcher); con->keep_alive_requests = 0; } li_connection_update_io_wait(con); li_job_later(&con->wrk->loop.jobqueue, &con->job_reset); }
static void li_connection_reset2(liConnection *con) { con->response_headers_sent = FALSE; con->expect_100_cont = FALSE; con->out_has_all_data = FALSE; con_iostream_close(con); li_server_socket_release(con->srv_sock); con->srv_sock = NULL; con->info.is_ssl = FALSE; con->info.aborted = FALSE; con->info.out_queue_length = 0; li_stream_reset(&con->in); li_stream_reset(&con->out); li_vrequest_reset(con->mainvr, FALSE); li_http_request_parser_reset(&con->req_parser_ctx); g_string_truncate(con->info.remote_addr_str, 0); li_sockaddr_clear(&con->info.remote_addr); g_string_truncate(con->info.local_addr_str, 0); li_sockaddr_clear(&con->info.local_addr); con->info.keep_alive = TRUE; if (con->keep_alive_data.link) { g_queue_delete_link(&con->wrk->keep_alive_queue, con->keep_alive_data.link); con->keep_alive_data.link = NULL; } con->keep_alive_data.timeout = 0; con->keep_alive_data.max_idle = 0; li_event_stop(&con->keep_alive_data.watcher); con->keep_alive_requests = 0; /* reset stats */ con->info.stats.bytes_in = G_GUINT64_CONSTANT(0); con->info.stats.bytes_in_5s = G_GUINT64_CONSTANT(0); con->info.stats.bytes_in_5s_diff = G_GUINT64_CONSTANT(0); con->info.stats.bytes_out = G_GUINT64_CONSTANT(0); con->info.stats.bytes_out_5s = G_GUINT64_CONSTANT(0); con->info.stats.bytes_out_5s_diff = G_GUINT64_CONSTANT(0); con->info.stats.last_avg = 0; /* remove from timeout queue */ li_waitqueue_remove(&con->wrk->io_timeout_queue, &con->io_timeout_elem); li_job_reset(&con->job_reset); }
void li_event_io_set_fd(liEventIO *io, int fd) { if (-1 == fd) { li_event_stop(io); ev_io_set(&io->libevmess.io, fd, io->libevmess.io.events); return; } if (li_event_attached(io) && li_event_active(io)) { liEventLoop *loop = io->base.link_watchers.data; LI_FORCE_ASSERT(NULL != loop); ev_ref(loop->loop); ev_io_stop(loop->loop, &io->libevmess.io); ev_io_set(&io->libevmess.io, fd, io->libevmess.io.events); ev_io_start(loop->loop, &io->libevmess.io); ev_unref(loop->loop); } else { ev_io_set(&io->libevmess.io, fd, io->libevmess.io.events); } }
static void li_connection_reset_keep_alive(liConnection *con) { liVRequest *vr = con->mainvr; if (NULL == con->con_sock.raw_in || NULL == con->con_sock.raw_out || con->in.source != con->con_sock.raw_in) { li_connection_reset(con); return; } /* only start keep alive watcher if there isn't more input data already */ if (con->con_sock.raw_in->out->length == 0) { li_event_stop(&con->keep_alive_data.watcher); { con->keep_alive_data.max_idle = CORE_OPTION(LI_CORE_OPTION_MAX_KEEP_ALIVE_IDLE).number; if (con->keep_alive_data.max_idle == 0) { con->state = LI_CON_STATE_CLOSE; con_iostream_shutdown(con); li_connection_reset(con); return; } con->keep_alive_data.timeout = li_cur_ts(con->wrk) + con->keep_alive_data.max_idle; if (con->keep_alive_data.max_idle == con->srv->keep_alive_queue_timeout) { /* queue is sorted by con->keep_alive_data.timeout */ gboolean need_start = (0 == con->wrk->keep_alive_queue.length); con->keep_alive_data.timeout = li_cur_ts(con->wrk) + con->srv->keep_alive_queue_timeout; g_queue_push_tail(&con->wrk->keep_alive_queue, con); con->keep_alive_data.link = g_queue_peek_tail_link(&con->wrk->keep_alive_queue); if (need_start) li_worker_check_keepalive(con->wrk); } else { li_event_timer_once(&con->keep_alive_data.watcher, con->keep_alive_data.max_idle); } } } else { li_stream_again_later(&con->in); } con->state = LI_CON_STATE_KEEP_ALIVE; con->response_headers_sent = FALSE; con->expect_100_cont = FALSE; con->out_has_all_data = FALSE; con->info.keep_alive = TRUE; li_connection_update_io_wait(con); li_vrequest_reset(con->mainvr, TRUE); li_http_request_parser_reset(&con->req_parser_ctx); li_stream_disconnect(&con->out); li_stream_disconnect_dest(&con->in); con->out.out->is_closed = FALSE; memset(&con->in_chunked_decode_state, 0, sizeof(con->in_chunked_decode_state)); /* restore chunkqueue limits */ li_chunkqueue_use_limit(con->con_sock.raw_in->out, LI_CONNECTION_DEFAULT_CHUNKQUEUE_LIMIT); li_chunkqueue_use_limit(con->con_sock.raw_out->out, LI_CONNECTION_DEFAULT_CHUNKQUEUE_LIMIT); /* reset stats */ con->info.stats.bytes_in = G_GUINT64_CONSTANT(0); con->info.stats.bytes_in_5s = G_GUINT64_CONSTANT(0); con->info.stats.bytes_in_5s_diff = G_GUINT64_CONSTANT(0); con->info.stats.bytes_out = G_GUINT64_CONSTANT(0); con->info.stats.bytes_out_5s = G_GUINT64_CONSTANT(0); con->info.stats.bytes_out_5s_diff = G_GUINT64_CONSTANT(0); con->info.stats.last_avg = 0; }
/* tcp/ssl -> http "parser" */ static void _connection_http_in_cb(liStream *stream, liStreamEvent event) { liConnection *con = LI_CONTAINER_OF(stream, liConnection, in); liChunkQueue *raw_in, *in; liVRequest *vr = con->mainvr; switch (event) { case LI_STREAM_NEW_DATA: /* handle below */ break; case LI_STREAM_DISCONNECTED_SOURCE: connection_close(con); return; case LI_STREAM_DESTROY: con->info.req = NULL; li_job_later(&con->wrk->loop.jobqueue, &con->job_reset); return; default: return; } if (NULL == stream->source) return; /* raw_in never gets closed normally - if we receive EOF from the client it means it cancelled the request */ raw_in = stream->source->out; if (raw_in->is_closed) { connection_close(con); return; } /* always close "in" after request body end. reopen it on keep-alive */ in = con->in.out; if (0 == raw_in->length) return; /* no (new) data */ if (LI_CON_STATE_UPGRADED == con->state) { li_chunkqueue_steal_all(in, raw_in); li_stream_notify(stream); return; } if (con->state == LI_CON_STATE_KEEP_ALIVE) { /* stop keep alive timeout watchers */ if (con->keep_alive_data.link) { g_queue_delete_link(&con->wrk->keep_alive_queue, con->keep_alive_data.link); con->keep_alive_data.link = NULL; } con->keep_alive_data.timeout = 0; li_event_stop(&con->keep_alive_data.watcher); con->keep_alive_requests++; /* disable keep alive if limit is reached */ if (con->keep_alive_requests == CORE_OPTION(LI_CORE_OPTION_MAX_KEEP_ALIVE_REQUESTS).number) con->info.keep_alive = FALSE; /* reopen stream for request body */ li_chunkqueue_reset(in); /* reset stuff from keep-alive and record timestamp */ li_vrequest_start(con->mainvr); con->state = LI_CON_STATE_READ_REQUEST_HEADER; /* put back in io timeout queue */ li_connection_update_io_wait(con); } else if (con->state == LI_CON_STATE_REQUEST_START) { con->state = LI_CON_STATE_READ_REQUEST_HEADER; li_connection_update_io_wait(con); } if (con->state == LI_CON_STATE_READ_REQUEST_HEADER) { liHandlerResult res; if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { VR_DEBUG(vr, "%s", "reading request header"); } res = li_http_request_parse(vr, &con->req_parser_ctx); /* max uri length 8 kilobytes */ /* TODO: check this and similar in request_parse and response_parse */ if (vr->request.uri.raw->len > 8*1024) { VR_INFO(vr, "request uri too large. limit: 8kb, received: %s", li_counter_format(vr->request.uri.raw->len, COUNTER_BYTES, vr->wrk->tmp_str)->str ); con->info.keep_alive = FALSE; vr->response.http_status = 414; /* Request-URI Too Large */ con->state = LI_CON_STATE_WRITE; li_connection_update_io_wait(con); li_stream_again(&con->out); return; } switch(res) { case LI_HANDLER_GO_ON: break; /* go on */ case LI_HANDLER_WAIT_FOR_EVENT: return; case LI_HANDLER_ERROR: case LI_HANDLER_COMEBACK: /* unexpected */ /* unparsable header */ if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { VR_DEBUG(vr, "%s", "parsing header failed"); } con->wrk->stats.requests++; con->info.keep_alive = FALSE; /* set status 400 if not already set to e.g. 413 */ if (vr->response.http_status == 0) vr->response.http_status = 400; con->state = LI_CON_STATE_WRITE; li_connection_update_io_wait(con); li_stream_again(&con->out); return; } con->wrk->stats.requests++; /* headers ready */ if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { VR_DEBUG(vr, "%s", "validating request header"); } if (!li_request_validate_header(con)) { /* set status 400 if not already set */ if (vr->response.http_status == 0) vr->response.http_status = 400; con->state = LI_CON_STATE_WRITE; con->info.keep_alive = FALSE; li_connection_update_io_wait(con); li_stream_again(&con->out); return; } /* When does a client ask for 100 Continue? probably not while trying to ddos us * as post content probably goes to a dynamic backend anyway, we don't * care about the rare cases we could determine that we don't want a request at all * before sending it to a backend - so just send the stupid header */ if (con->expect_100_cont) { if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { VR_DEBUG(vr, "%s", "send 100 Continue"); } li_chunkqueue_append_mem(con->out.out, CONST_STR_LEN("HTTP/1.1 100 Continue\r\n\r\n")); con->expect_100_cont = FALSE; li_stream_notify(&con->out); } con->state = LI_CON_STATE_HANDLE_MAINVR; li_connection_update_io_wait(con); li_action_enter(vr, con->srv->mainaction); li_vrequest_handle_request_headers(vr); } if (con->state != LI_CON_STATE_READ_REQUEST_HEADER && !in->is_closed) { goffset newbytes = 0; if (-1 == vr->request.content_length) { if (!in->is_closed) { if (!li_filter_chunked_decode(vr, in, raw_in, &con->in_chunked_decode_state)) { if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { VR_DEBUG(vr, "%s", "failed decoding chunked request body"); } li_connection_error(con); return; } if (in->is_closed) vr->request.content_length = in->bytes_in; newbytes = 1; /* always notify */ } } else { if (in->bytes_in < vr->request.content_length) { newbytes = li_chunkqueue_steal_len(in, raw_in, vr->request.content_length - in->bytes_in); } if (in->bytes_in == vr->request.content_length) { in->is_closed = TRUE; } } if (newbytes > 0 || in->is_closed) { li_stream_notify(&con->in); } } }
static void memcached_stop_io(liMemcachedCon *con) { if (li_event_active(&con->con_watcher)) { li_event_stop(&con->con_watcher); li_memcached_con_release(con); } }
static void read_pipe(liServer *srv, liErrorPipe *epipe, gboolean flush) { const ssize_t max_read = 8192; ssize_t r, toread = 0; GString *buf; int count = 10; if (-1 == epipe->fds[0]) return; for (;;) { if (ioctl(epipe->fds[0], FIONREAD, &toread) || toread == 0) { toread = 256; } else { if (toread < 0 || toread > max_read) toread = max_read; } buf = g_string_sized_new(toread); g_string_set_size(buf, toread); r = read(epipe->fds[0], buf->str, toread); if (r < 0) { g_string_free(buf, TRUE); switch (errno) { case EINTR: continue; case EAGAIN: #if EWOULDBLOCK != EAGAIN case EWOULDBLOCK: #endif return; /* come back later */ case ECONNRESET: goto close_epipe; default: ERROR(srv, "read error: %s", g_strerror(errno)); goto close_epipe; } } else if (r == 0) { /* EOF */ g_string_free(buf, TRUE); goto close_epipe; } g_string_set_size(buf, r); epipe->cb(srv, epipe, buf); g_string_free(buf, TRUE); if (!flush) break; if (--count <= 0) { buf = g_string_new("error while trying to flush error-pipe: didn't see EOF. closing"); epipe->cb(srv, epipe, buf); g_string_free(buf, TRUE); return; } } return; close_epipe: li_event_stop(&epipe->fd_watcher); close(epipe->fds[0]); epipe->fds[0] = -1; }