int connection_state_machine(server *srv, connection *con) { int done = 0, r; if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state at start", con->fd, connection_get_state(con->state)); } while (done == 0) { size_t ostate = con->state; if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } switch (con->state) { case CON_STATE_REQUEST_START: /* transient */ con->request_start = srv->cur_ts; con->read_idle_ts = srv->cur_ts; if (con->conf.high_precision_timestamps) log_clock_gettime_realtime(&con->request_start_hp); con->request_count++; con->loops_per_request = 0; connection_set_state(srv, con, CON_STATE_READ); break; case CON_STATE_REQUEST_END: /* transient */ buffer_reset(con->uri.authority); buffer_reset(con->uri.path); buffer_reset(con->uri.query); buffer_reset(con->request.orig_uri); if (http_request_parse(srv, con)) { /* we have to read some data from the POST request */ connection_set_state(srv, con, CON_STATE_READ_POST); break; } connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); break; case CON_STATE_READ_POST: case CON_STATE_HANDLE_REQUEST: /* * the request is parsed * * decided what to do with the request * - * * */ switch (r = http_response_prepare(srv, con)) { case HANDLER_WAIT_FOR_EVENT: if (!con->file_finished && (!con->file_started || 0 == con->conf.stream_response_body)) { break; /* come back here */ } /* response headers received from backend; fall through to start response */ case HANDLER_FINISHED: if (con->error_handler_saved_status > 0) { con->request.http_method = con->error_handler_saved_method; } if (con->mode == DIRECT) { if (con->error_handler_saved_status) { if (con->error_handler_saved_status > 0) { con->http_status = con->error_handler_saved_status; } else if (con->http_status == 404 || con->http_status == 403) { /* error-handler-404 is a 404 */ con->http_status = -con->error_handler_saved_status; } else { /* error-handler-404 is back and has generated content */ /* if Status: was set, take it otherwise use 200 */ } } else if (con->http_status >= 400) { buffer *error_handler = NULL; if (!buffer_string_is_empty(con->conf.error_handler)) { error_handler = con->conf.error_handler; } else if ((con->http_status == 404 || con->http_status == 403) && !buffer_string_is_empty(con->conf.error_handler_404)) { error_handler = con->conf.error_handler_404; } if (error_handler) { /* call error-handler */ /* set REDIRECT_STATUS to save current HTTP status code * for access by dynamic handlers * https://redmine.lighttpd.net/issues/1828 */ data_string *ds; if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { ds = data_string_init(); } buffer_copy_string_len(ds->key, CONST_STR_LEN("REDIRECT_STATUS")); buffer_append_int(ds->value, con->http_status); array_insert_unique(con->environment, (data_unset *)ds); if (error_handler == con->conf.error_handler) { plugins_call_connection_reset(srv, con); if (con->request.content_length) { if (con->request.content_length != con->request_content_queue->bytes_in) { con->keep_alive = 0; } con->request.content_length = 0; chunkqueue_reset(con->request_content_queue); } con->is_writable = 1; con->file_finished = 0; con->file_started = 0; con->got_response = 0; con->parsed_response = 0; con->response.keep_alive = 0; con->response.content_length = -1; con->response.transfer_encoding = 0; con->error_handler_saved_status = con->http_status; con->error_handler_saved_method = con->request.http_method; con->request.http_method = HTTP_METHOD_GET; } else { /*(preserve behavior for server.error-handler-404)*/ con->error_handler_saved_status = -con->http_status; /*(negative to flag old behavior)*/ } buffer_copy_buffer(con->request.uri, error_handler); connection_handle_errdoc_init(srv, con); con->http_status = 0; /*(after connection_handle_errdoc_init())*/ done = -1; break; } } } if (con->http_status == 0) con->http_status = 200; /* we have something to send, go on */ connection_set_state(srv, con, CON_STATE_RESPONSE_START); break; case HANDLER_WAIT_FOR_FD: srv->want_fds++; fdwaitqueue_append(srv, con); break; case HANDLER_COMEBACK: done = -1; break; case HANDLER_ERROR: /* something went wrong */ connection_set_state(srv, con, CON_STATE_ERROR); break; default: log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown ret-value: ", con->fd, r); break; } if (con->state == CON_STATE_HANDLE_REQUEST && ostate == CON_STATE_READ_POST) { ostate = CON_STATE_HANDLE_REQUEST; } break; case CON_STATE_RESPONSE_START: /* * the decision is done * - create the HTTP-Response-Header * */ if (-1 == connection_handle_write_prepare(srv, con)) { connection_set_state(srv, con, CON_STATE_ERROR); break; } connection_set_state(srv, con, CON_STATE_WRITE); break; case CON_STATE_RESPONSE_END: /* transient */ case CON_STATE_ERROR: /* transient */ connection_handle_response_end_state(srv, con); break; case CON_STATE_CONNECT: chunkqueue_reset(con->read_queue); con->request_count = 0; break; case CON_STATE_CLOSE: connection_handle_close_state(srv, con); break; case CON_STATE_READ: connection_handle_read_state(srv, con); break; case CON_STATE_WRITE: do { /* only try to write if we have something in the queue */ if (!chunkqueue_is_empty(con->write_queue)) { if (con->is_writable) { if (-1 == connection_handle_write(srv, con)) { log_error_write(srv, __FILE__, __LINE__, "ds", con->fd, "handle write failed."); connection_set_state(srv, con, CON_STATE_ERROR); break; } if (con->state != CON_STATE_WRITE) break; } } else if (con->file_finished) { connection_set_state(srv, con, CON_STATE_RESPONSE_END); break; } if (con->mode != DIRECT && !con->file_finished) { switch(r = plugins_call_handle_subrequest(srv, con)) { case HANDLER_WAIT_FOR_EVENT: case HANDLER_FINISHED: case HANDLER_GO_ON: break; case HANDLER_WAIT_FOR_FD: srv->want_fds++; fdwaitqueue_append(srv, con); break; case HANDLER_COMEBACK: default: log_error_write(srv, __FILE__, __LINE__, "sdd", "unexpected subrequest handler ret-value: ", con->fd, r); /* fall through */ case HANDLER_ERROR: connection_set_state(srv, con, CON_STATE_ERROR); break; } } } while (con->state == CON_STATE_WRITE && (!chunkqueue_is_empty(con->write_queue) ? con->is_writable : con->file_finished)); break; default: log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown state:", con->fd, con->state); break; } if (done == -1) { done = 0; } else if (ostate == con->state) { done = 1; } } if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state at exit:", con->fd, connection_get_state(con->state)); } r = 0; switch(con->state) { case CON_STATE_READ: case CON_STATE_CLOSE: r = FDEVENT_IN; break; case CON_STATE_WRITE: /* request write-fdevent only if we really need it * - if we have data to write * - if the socket is not writable yet */ if (!chunkqueue_is_empty(con->write_queue) && (con->is_writable == 0) && (con->traffic_limit_reached == 0)) { r |= FDEVENT_OUT; } /* fall through */ case CON_STATE_READ_POST: if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN) { r |= FDEVENT_IN; } break; default: break; } if (-1 != con->fd) { const int events = fdevent_event_get_interest(srv->ev, con->fd); if (con->is_readable < 0) { con->is_readable = 0; r |= FDEVENT_IN; } if (con->is_writable < 0) { con->is_writable = 0; r |= FDEVENT_OUT; } if (r != events) { /* update timestamps when enabling interest in events */ if ((r & FDEVENT_IN) && !(events & FDEVENT_IN)) { con->read_idle_ts = srv->cur_ts; } if ((r & FDEVENT_OUT) && !(events & FDEVENT_OUT)) { con->write_request_ts = srv->cur_ts; } fdevent_event_set(srv->ev, &con->fde_ndx, con->fd, r); } } return 0; }
static handler_t connection_handle_fdevent(server *srv, void *context, int revents) { connection *con = context; joblist_append(srv, con); if (con->srv_socket->is_ssl) { /* ssl may read and write for both reads and writes */ if (revents & (FDEVENT_IN | FDEVENT_OUT)) { con->is_readable = 1; con->is_writable = 1; } } else { if (revents & FDEVENT_IN) { con->is_readable = 1; } if (revents & FDEVENT_OUT) { con->is_writable = 1; /* we don't need the event twice */ } } if (con->state == CON_STATE_READ) { connection_handle_read_state(srv, con); } if (con->state == CON_STATE_WRITE && !chunkqueue_is_empty(con->write_queue) && con->is_writable) { if (-1 == connection_handle_write(srv, con)) { connection_set_state(srv, con, CON_STATE_ERROR); log_error_write(srv, __FILE__, __LINE__, "ds", con->fd, "handle write failed."); } } if (con->state == CON_STATE_CLOSE) { /* flush the read buffers */ connection_read_for_eos(srv, con); } /* attempt (above) to read data in kernel socket buffers * prior to handling FDEVENT_HUP and FDEVENT_ERR */ if ((revents & ~(FDEVENT_IN | FDEVENT_OUT)) && con->state != CON_STATE_ERROR) { if (con->state == CON_STATE_CLOSE) { con->close_timeout_ts = srv->cur_ts - (HTTP_LINGER_TIMEOUT+1); } else if (revents & FDEVENT_HUP) { if (fdevent_is_tcp_half_closed(con->fd)) { con->keep_alive = 0; } else { connection_set_state(srv, con, CON_STATE_ERROR); } } else if (revents & FDEVENT_ERR) { /* error, connection reset */ connection_set_state(srv, con, CON_STATE_ERROR); } else { log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed: poll() -> ???", revents); } } return HANDLER_FINISHED; }
static handler_t connection_handle_fdevent(server *srv, void *context, int revents) { connection *con = context; joblist_append(srv, con); if (con->srv_socket->is_ssl) { /* ssl may read and write for both reads and writes */ if (revents & (FDEVENT_IN | FDEVENT_OUT)) { con->is_readable = 1; con->is_writable = 1; } } else { if (revents & FDEVENT_IN) { con->is_readable = 1; } if (revents & FDEVENT_OUT) { con->is_writable = 1; /* we don't need the event twice */ } } if (revents & ~(FDEVENT_IN | FDEVENT_OUT)) { /* looks like an error */ /* FIXME: revents = 0x19 still means that we should read from the queue */ if (revents & FDEVENT_HUP) { if (con->state == CON_STATE_CLOSE) { con->close_timeout_ts = srv->cur_ts - (HTTP_LINGER_TIMEOUT+1); } else { /* sigio reports the wrong event here * * there was no HUP at all */ #ifdef USE_LINUX_SIGIO if (srv->ev->in_sigio == 1) { log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed: poll() -> HUP", con->fd); } else { connection_set_state(srv, con, CON_STATE_ERROR); } #else connection_set_state(srv, con, CON_STATE_ERROR); #endif } } else if (revents & FDEVENT_ERR) { /* error, connection reset, whatever... we don't want to spam the logfile */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed: poll() -> ERR", con->fd); #endif connection_set_state(srv, con, CON_STATE_ERROR); } else { log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed: poll() -> ???", revents); } } if (con->state == CON_STATE_READ || con->state == CON_STATE_READ_POST) { connection_handle_read_state(srv, con); } if (con->state == CON_STATE_WRITE && !chunkqueue_is_empty(con->write_queue) && con->is_writable) { if (-1 == connection_handle_write(srv, con)) { connection_set_state(srv, con, CON_STATE_ERROR); log_error_write(srv, __FILE__, __LINE__, "ds", con->fd, "handle write failed."); } } if (con->state == CON_STATE_CLOSE) { /* flush the read buffers */ int len; char buf[1024]; len = read(con->fd, buf, sizeof(buf)); if (len == 0 || (len < 0 && errno != EAGAIN && errno != EINTR) ) { con->close_timeout_ts = srv->cur_ts - (HTTP_LINGER_TIMEOUT+1); } } return HANDLER_FINISHED; }
int connection_state_machine(server *srv, connection *con) { int done = 0, r; #ifdef USE_OPENSSL server_socket *srv_sock = con->srv_socket; #endif if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state at start", con->fd, connection_get_state(con->state)); } while (done == 0) { size_t ostate = con->state; switch (con->state) { case CON_STATE_REQUEST_START: /* transient */ if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } con->request_start = srv->cur_ts; con->read_idle_ts = srv->cur_ts; con->request_count++; con->loops_per_request = 0; connection_set_state(srv, con, CON_STATE_READ); break; case CON_STATE_REQUEST_END: /* transient */ if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } buffer_reset(con->uri.authority); buffer_reset(con->uri.path); buffer_reset(con->uri.query); buffer_reset(con->request.orig_uri); if (http_request_parse(srv, con)) { /* we have to read some data from the POST request */ connection_set_state(srv, con, CON_STATE_READ_POST); break; } connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); break; case CON_STATE_HANDLE_REQUEST: /* * the request is parsed * * decided what to do with the request * - * * */ if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } switch (r = http_response_prepare(srv, con)) { case HANDLER_FINISHED: if (con->mode == DIRECT) { if (con->http_status == 404 || con->http_status == 403) { /* 404 error-handler */ if (con->in_error_handler == 0 && (!buffer_string_is_empty(con->conf.error_handler) || !buffer_string_is_empty(con->error_handler))) { /* call error-handler */ con->error_handler_saved_status = con->http_status; con->http_status = 0; if (buffer_string_is_empty(con->error_handler)) { buffer_copy_buffer(con->request.uri, con->conf.error_handler); } else { buffer_copy_buffer(con->request.uri, con->error_handler); } buffer_reset(con->physical.path); con->in_error_handler = 1; connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); done = -1; break; } else if (con->in_error_handler) { /* error-handler is a 404 */ con->http_status = con->error_handler_saved_status; } } else if (con->in_error_handler) { /* error-handler is back and has generated content */ /* if Status: was set, take it otherwise use 200 */ } } if (con->http_status == 0) con->http_status = 200; /* we have something to send, go on */ connection_set_state(srv, con, CON_STATE_RESPONSE_START); break; case HANDLER_WAIT_FOR_FD: srv->want_fds++; fdwaitqueue_append(srv, con); connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); break; case HANDLER_COMEBACK: done = -1; /* fallthrough */ case HANDLER_WAIT_FOR_EVENT: /* come back here */ connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); break; case HANDLER_ERROR: /* something went wrong */ connection_set_state(srv, con, CON_STATE_ERROR); break; default: log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown ret-value: ", con->fd, r); break; } break; case CON_STATE_RESPONSE_START: /* * the decision is done * - create the HTTP-Response-Header * */ if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } if (-1 == connection_handle_write_prepare(srv, con)) { connection_set_state(srv, con, CON_STATE_ERROR); break; } connection_set_state(srv, con, CON_STATE_WRITE); break; case CON_STATE_RESPONSE_END: /* transient */ /* log the request */ if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } plugins_call_handle_request_done(srv, con); srv->con_written++; if (con->keep_alive) { connection_set_state(srv, con, CON_STATE_REQUEST_START); #if 0 con->request_start = srv->cur_ts; con->read_idle_ts = srv->cur_ts; #endif } else { switch(r = plugins_call_handle_connection_close(srv, con)) { case HANDLER_GO_ON: case HANDLER_FINISHED: break; default: log_error_write(srv, __FILE__, __LINE__, "sd", "unhandling return value", r); break; } #ifdef USE_OPENSSL if (srv_sock->is_ssl) { switch (SSL_shutdown(con->ssl)) { case 1: /* done */ break; case 0: /* wait for fd-event * * FIXME: wait for fdevent and call SSL_shutdown again * */ break; default: log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", ERR_error_string(ERR_get_error(), NULL)); } } #endif if ((0 == shutdown(con->fd, SHUT_WR))) { con->close_timeout_ts = srv->cur_ts; connection_set_state(srv, con, CON_STATE_CLOSE); } else { connection_close(srv, con); } srv->con_closed++; } connection_reset(srv, con); break; case CON_STATE_CONNECT: if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } chunkqueue_reset(con->read_queue); con->request_count = 0; break; case CON_STATE_CLOSE: if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } /* we have to do the linger_on_close stuff regardless * of con->keep_alive; even non-keepalive sockets may * still have unread data, and closing before reading * it will make the client not see all our output. */ { int len; char buf[1024]; len = read(con->fd, buf, sizeof(buf)); if (len == 0 || (len < 0 && errno != EAGAIN && errno != EINTR) ) { con->close_timeout_ts = srv->cur_ts - (HTTP_LINGER_TIMEOUT+1); } } if (srv->cur_ts - con->close_timeout_ts > HTTP_LINGER_TIMEOUT) { connection_close(srv, con); if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed for fd", con->fd); } } break; case CON_STATE_READ_POST: case CON_STATE_READ: if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } connection_handle_read_state(srv, con); break; case CON_STATE_WRITE: if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } /* only try to write if we have something in the queue */ if (!chunkqueue_is_empty(con->write_queue)) { if (con->is_writable) { if (-1 == connection_handle_write(srv, con)) { log_error_write(srv, __FILE__, __LINE__, "ds", con->fd, "handle write failed."); connection_set_state(srv, con, CON_STATE_ERROR); } } } else if (con->file_finished) { connection_set_state(srv, con, CON_STATE_RESPONSE_END); } break; case CON_STATE_ERROR: /* transient */ /* even if the connection was drop we still have to write it to the access log */ if (con->http_status) { plugins_call_handle_request_done(srv, con); } #ifdef USE_OPENSSL if (srv_sock->is_ssl) { int ret, ssl_r; unsigned long err; ERR_clear_error(); switch ((ret = SSL_shutdown(con->ssl))) { case 1: /* ok */ break; case 0: ERR_clear_error(); if (-1 != (ret = SSL_shutdown(con->ssl))) break; /* fall through */ default: switch ((ssl_r = SSL_get_error(con->ssl, ret))) { case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: break; case SSL_ERROR_SYSCALL: /* perhaps we have error waiting in our error-queue */ if (0 != (err = ERR_get_error())) { do { log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", ssl_r, ret, ERR_error_string(err, NULL)); } while((err = ERR_get_error())); } else if (errno != 0) { /* ssl bug (see lighttpd ticket #2213): sometimes errno == 0 */ switch(errno) { case EPIPE: case ECONNRESET: break; default: log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL (error):", ssl_r, ret, errno, strerror(errno)); break; } } break; default: while((err = ERR_get_error())) { log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", ssl_r, ret, ERR_error_string(err, NULL)); } break; } } ERR_clear_error(); } #endif switch(con->mode) { case DIRECT: #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "emergency exit: direct", con->fd); #endif break; default: switch(r = plugins_call_handle_connection_close(srv, con)) { case HANDLER_GO_ON: case HANDLER_FINISHED: break; default: log_error_write(srv, __FILE__, __LINE__, "sd", "unhandling return value", r); break; } break; } connection_reset(srv, con); /* close the connection */ if ((0 == shutdown(con->fd, SHUT_WR))) { con->close_timeout_ts = srv->cur_ts; connection_set_state(srv, con, CON_STATE_CLOSE); if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sd", "shutdown for fd", con->fd); } } else { connection_close(srv, con); } con->keep_alive = 0; srv->con_closed++; break; default: log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown state:", con->fd, con->state); break; } if (done == -1) { done = 0; } else if (ostate == con->state) { done = 1; } } if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state at exit:", con->fd, connection_get_state(con->state)); } switch(con->state) { case CON_STATE_READ_POST: case CON_STATE_READ: case CON_STATE_CLOSE: fdevent_event_set(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_IN); break; case CON_STATE_WRITE: /* request write-fdevent only if we really need it * - if we have data to write * - if the socket is not writable yet */ if (!chunkqueue_is_empty(con->write_queue) && (con->is_writable == 0) && (con->traffic_limit_reached == 0)) { fdevent_event_set(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_OUT); } else { fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); } break; default: fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); break; } return 0; }
int connection_state_machine(server *srv, connection *con) { int done = 0, r; #ifdef USE_OPENSSL server_socket *srv_sock = con->srv_socket; #endif chunkqueue *dst_cq = con->request_content_queue; if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state at start", con->fd, connection_get_state(con->state)); } while (done == 0) { size_t ostate = con->state; int b; switch (con->state) { case CON_STATE_REQUEST_START: /* transient */ if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } con->request_start = srv->cur_ts; con->read_idle_ts = srv->cur_ts; con->request_count++; con->loops_per_request = 0; connection_set_state(srv, con, CON_STATE_READ); /* patch con->conf.is_ssl if the connection is a ssl-socket already */ #ifdef USE_OPENSSL con->conf.is_ssl = srv_sock->is_ssl; #endif break; case CON_STATE_REQUEST_END: /* transient */ if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } if (http_request_parse(srv, con)) { /* we have to read some data from the POST request */ connection_set_state(srv, con, CON_STATE_READ_POST); break; } connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); break; case CON_STATE_HANDLE_REQUEST: /* * the request is parsed * * decided what to do with the request * - * * */ if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } switch (r = http_response_prepare(srv, con)) { case HANDLER_FINISHED: if (con->mode == DIRECT) { if (con->http_status == 404 || con->http_status == 403) { /* 404 error-handler */ if (con->in_error_handler == 0 && (!buffer_is_empty(con->conf.error_handler) || !buffer_is_empty(con->error_handler))) { /* call error-handler */ con->error_handler_saved_status = con->http_status; //log_error_write(srv, __FILE__, __LINE__, "s", "http_status set to 0"); con->http_status = 0; if (buffer_is_empty(con->error_handler)) { buffer_copy_string_buffer(con->request.uri, con->conf.error_handler); } else { buffer_copy_string_buffer(con->request.uri, con->error_handler); } buffer_reset(con->physical.path); con->in_error_handler = 1; connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); done = -1; break; } else if (con->in_error_handler) { /* error-handler is a 404 */ con->http_status = con->error_handler_saved_status; } } else if (con->in_error_handler) { /* error-handler is back and has generated content */ /* if Status: was set, take it otherwise use 200 */ } } if (con->http_status == 0) con->http_status = 200; /* we have something to send, go on */ connection_set_state(srv, con, CON_STATE_RESPONSE_START); break; case HANDLER_WAIT_FOR_FD: srv->want_fds++; fdwaitqueue_append(srv, con); connection_set_state(srv, con, CON_STATE_READ_POST); break; case HANDLER_COMEBACK: done = -1; case HANDLER_WAIT_FOR_EVENT: /* come back here */ connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); break; case HANDLER_ERROR: /* something went wrong */ connection_set_state(srv, con, CON_STATE_ERROR); break; default: log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown ret-value: ", con->fd, r); break; } break; case CON_STATE_RESPONSE_START: /* * the decision is done * - create the HTTP-Response-Header * */ if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } if (-1 == connection_handle_write_prepare(srv, con)) { connection_set_state(srv, con, CON_STATE_ERROR); break; } if (con->http_status = 416){ //log_error_write(srv, __FILE__, __LINE__, "s", "Resetting read_queue"); chunkqueue_reset(con->read_queue); } connection_set_state(srv, con, CON_STATE_WRITE); break; case CON_STATE_RESPONSE_END: /* transient */ /* log the request */ if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } plugins_call_handle_request_done(srv, con); srv->con_written++; if (con->keep_alive) { connection_set_state(srv, con, CON_STATE_REQUEST_START); #if 0 con->request_start = srv->cur_ts; con->read_idle_ts = srv->cur_ts; #endif } else { switch(r = plugins_call_handle_connection_close(srv, con)) { case HANDLER_GO_ON: case HANDLER_FINISHED: break; default: log_error_write(srv, __FILE__, __LINE__, "sd", "unhandling return value", r); break; } #ifdef USE_OPENSSL if (srv_sock->is_ssl) { switch (SSL_shutdown(con->ssl)) { case 1: /* done */ break; case 0: /* wait for fd-event * * FIXME: wait for fdevent and call SSL_shutdown again * */ break; default: log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", ERR_error_string(ERR_get_error(), NULL)); } } #endif connection_close(srv, con); srv->con_closed++; } connection_reset(srv, con); break; case CON_STATE_CONNECT: if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } /* if (con->read_queue != NULL) { chunk *c; size_t avail = 0; for (c= con->read_queue->first; c != NULL; c = c->next) { if(c->type == MEM_CHUNK) avail += c->mem->used - c->offset -1; } log_error_write(srv, __FILE__, __LINE__, "sd", "con->read_queue is not empty!", avail); }*/ chunkqueue_reset(con->read_queue); con->request_count = 0; break; case CON_STATE_CLOSE: //log_error_write(srv, __FILE__, __LINE__, "so", "con->range_offset", con->range_offset); //log_error_write(srv, __FILE__, __LINE__, "sdsd", "CON_STATE_ERROR: Bytes in", dst_cq->bytes_in, "Bytes out", dst_cq->bytes_out); /* Flush out any data that is currently sitting in the chunqueue */ if ((dst_cq->bytes_in - dst_cq->bytes_out) > 0) { log_error_write(srv, __FILE__, __LINE__, "sd", "Flush data, handle remaining chunks", dst_cq->bytes_in); connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); break; } if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } if (con->keep_alive) { if (ioctl(con->fd, FIONREAD, &b)) { log_error_write(srv, __FILE__, __LINE__, "ss", "ioctl() failed", strerror(errno)); } if (b > 0) { char buf[1024]; log_error_write(srv, __FILE__, __LINE__, "sdd", "CLOSE-read()", con->fd, b); /* */ read(con->fd, buf, sizeof(buf)); } else { /* nothing to read */ con->close_timeout_ts = 0; } } else { con->close_timeout_ts = 0; } if (srv->cur_ts - con->close_timeout_ts > 1) { connection_close(srv, con); log_error_write(srv, __FILE__, __LINE__, "so", "con->range_offset", con->range_offset); log_error_write(srv, __FILE__, __LINE__, "sdsd", "CON_STATE_ERROR: Bytes in", dst_cq->bytes_in, "Bytes out", dst_cq->bytes_out); /* Flush out any data that is currently sitting in the chunqueue */ if ((dst_cq->bytes_in - dst_cq->bytes_out) > 0) { log_error_write(srv, __FILE__, __LINE__, "sd", "Flush data, handle remaining chunks", dst_cq->bytes_in); connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); break; } if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed for fd", con->fd); } } break; case CON_STATE_READ_POST: case CON_STATE_READ: if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } connection_handle_read_state(srv, con); break; case CON_STATE_WRITE: if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } /* only try to write if we have something in the queue */ if (!chunkqueue_is_empty(con->write_queue)) { #if 0 log_error_write(srv, __FILE__, __LINE__, "dsd", con->fd, "packets to write:", con->write_queue->used); #endif } if (!chunkqueue_is_empty(con->write_queue) && con->is_writable) { if (-1 == connection_handle_write(srv, con)) { log_error_write(srv, __FILE__, __LINE__, "ds", con->fd, "handle write failed."); connection_set_state(srv, con, CON_STATE_ERROR); } else if (con->state == CON_STATE_WRITE) { con->write_request_ts = srv->cur_ts; } } break; case CON_STATE_ERROR: /* transient */ //log_error_write(srv, __FILE__, __LINE__, "so", "con->range_offset", con->range_offset); //log_error_write(srv, __FILE__, __LINE__, "sdsd", "CON_STATE_ERROR: Bytes in", dst_cq->bytes_in, "Bytes out", dst_cq->bytes_out); /* Flush out any data that is currently sitting in the chunqueue */ if ((dst_cq->bytes_in - dst_cq->bytes_out) > 0) { log_error_write(srv, __FILE__, __LINE__, "sd", "Flush data, handle remaining chunks", dst_cq->bytes_in); connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); break; } /* even if the connection was drop we still have to write it to the access log */ if (con->http_status) { plugins_call_handle_request_done(srv, con); } #ifdef USE_OPENSSL if (srv_sock->is_ssl) { int ret, ssl_r; unsigned long err; ERR_clear_error(); switch ((ret = SSL_shutdown(con->ssl))) { case 1: /* ok */ break; case 0: ERR_clear_error(); if (-1 != (ret = SSL_shutdown(con->ssl))) break; /* fall through */ default: switch ((ssl_r = SSL_get_error(con->ssl, ret))) { case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: break; case SSL_ERROR_SYSCALL: /* perhaps we have error waiting in our error-queue */ if (0 != (err = ERR_get_error())) { do { log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", ssl_r, ret, ERR_error_string(err, NULL)); } while((err = ERR_get_error())); } else { log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL (error):", ssl_r, ret, errno, strerror(errno)); } break; default: while((err = ERR_get_error())) { log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", ssl_r, ret, ERR_error_string(err, NULL)); } break; } } } ERR_clear_error(); #endif switch(con->mode) { case DIRECT: #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "emergency exit: direct", con->fd); #endif break; default: switch(r = plugins_call_handle_connection_close(srv, con)) { case HANDLER_GO_ON: case HANDLER_FINISHED: break; default: log_error_write(srv, __FILE__, __LINE__, ""); break; } break; } connection_reset(srv, con); /* close the connection */ if ((con->keep_alive == 1) && (0 == shutdown(con->fd, SHUT_WR))) { con->close_timeout_ts = srv->cur_ts; connection_set_state(srv, con, CON_STATE_CLOSE); if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sd", "shutdown for fd", con->fd); } } else { connection_close(srv, con); } con->keep_alive = 0; srv->con_closed++; break; default: log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown state:", con->fd, con->state); break; } if (done == -1) { done = 0; } else if (ostate == con->state) { done = 1; } } if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state at exit:", con->fd, connection_get_state(con->state)); } switch(con->state) { case CON_STATE_READ_POST: case CON_STATE_READ: case CON_STATE_CLOSE: fdevent_event_add(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_IN); break; case CON_STATE_WRITE: /* request write-fdevent only if we really need it * - if we have data to write * - if the socket is not writable yet */ if (!chunkqueue_is_empty(con->write_queue) && (con->is_writable == 0) && (con->traffic_limit_reached == 0)) { fdevent_event_add(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_OUT); } else { fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); } break; default: fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); break; } return 0; }
handler_t connection_handle_fdevent(void *s, void *context, int revents) { server *srv = (server *)s; connection *con = context; joblist_append(srv, con); if (revents & FDEVENT_IN) { con->is_readable = 1; #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "read-wait - done", con->fd); #endif } if (revents & FDEVENT_OUT) { con->is_writable = 1; /* we don't need the event twice */ } if (revents & ~(FDEVENT_IN | FDEVENT_OUT)) { /* looks like an error */ /* FIXME: revents = 0x19 still means that we should read from the queue */ if (revents & FDEVENT_HUP) { if (con->state == CON_STATE_CLOSE) { con->close_timeout_ts = 0; } else { /* sigio reports the wrong event here * * there was no HUP at all */ #ifdef USE_LINUX_SIGIO if (srv->ev->in_sigio == 1) { log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed: poll() -> HUP", con->fd); } else { connection_set_state(srv, con, CON_STATE_ERROR); } #else connection_set_state(srv, con, CON_STATE_ERROR); #endif } } else if (revents & FDEVENT_ERR) { #ifndef USE_LINUX_SIGIO log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed: poll() -> ERR", con->fd); #endif connection_set_state(srv, con, CON_STATE_ERROR); } else { log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed: poll() -> ???", revents); } } if (con->state == CON_STATE_READ || con->state == CON_STATE_READ_POST) { connection_handle_read_state(srv, con); } if (con->state == CON_STATE_WRITE && !chunkqueue_is_empty(con->write_queue) && con->is_writable) { if (-1 == connection_handle_write(srv, con)) { connection_set_state(srv, con, CON_STATE_ERROR); log_error_write(srv, __FILE__, __LINE__, "ds", con->fd, "handle write failed."); } else if (con->state == CON_STATE_WRITE) { con->write_request_ts = srv->cur_ts; } } if (con->state == CON_STATE_CLOSE) { /* flush the read buffers */ int b; if (ioctl(con->fd, FIONREAD, &b)) { log_error_write(srv, __FILE__, __LINE__, "ss", "ioctl() failed", strerror(errno)); } if (b > 0) { char buf[1024]; log_error_write(srv, __FILE__, __LINE__, "sdd", "CLOSE-read()", con->fd, b); /* */ read(con->fd, buf, sizeof(buf)); } else { /* nothing to read */ con->close_timeout_ts = 0; } } return HANDLER_FINISHED; }
connection *connection_accept(server *srv, server_socket *srv_socket) { /* accept everything */ /* search an empty place */ int cnt; sock_addr cnt_addr; #ifndef HAVE_LIBMTCP socklen_t cnt_len; #endif /* accept it and register the fd */ /** * check if we can still open a new connections * * see #1216 */ if (srv->conns->used >= srv->max_conns) { return NULL; } #ifdef HAVE_LIBMTCP if (-1 == (cnt = mtcp_accept(srv->mctx, srv_socket->fd, NULL, NULL))) { #else cnt_len = sizeof(cnt_addr); if (-1 == (cnt = accept(srv_socket->fd, (struct sockaddr *) &cnt_addr, &cnt_len))) { #endif switch (errno) { case EAGAIN: #if EWOULDBLOCK != EAGAIN case EWOULDBLOCK: #endif case EINTR: /* we were stopped _before_ we had a connection */ case ECONNABORTED: /* this is a FreeBSD thingy */ /* we were stopped _after_ we had a connection */ break; case EMFILE: /* out of fds */ break; default: log_error_write(srv, __FILE__, __LINE__, "ssd", "accept failed:", strerror(errno), errno); } return NULL; } else { connection *con; srv->cur_fds++; /* ok, we have the connection, register it */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "appected()", cnt); #endif srv->con_opened++; con = connections_get_new_connection(srv); con->fd = cnt; con->fde_ndx = -1; #if 0 gettimeofday(&(con->start_tv), NULL); #endif fdevent_register(srv->ev, con->fd, connection_handle_fdevent, con); connection_set_state(srv, con, CON_STATE_REQUEST_START); con->connection_start = srv->cur_ts; con->dst_addr = cnt_addr; buffer_copy_string(con->dst_addr_buf, inet_ntop_cache_get_ip(srv, &(con->dst_addr))); con->srv_socket = srv_socket; if (-1 == (fdevent_fcntl_set(srv->ev, con->fd))) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); return NULL; } #ifdef USE_OPENSSL /* connect FD to SSL */ if (srv_socket->is_ssl) { if (NULL == (con->ssl = SSL_new(srv_socket->ssl_ctx))) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", ERR_error_string(ERR_get_error(), NULL)); return NULL; } con->renegotiations = 0; SSL_set_app_data(con->ssl, con); SSL_set_accept_state(con->ssl); con->conf.is_ssl=1; if (1 != (SSL_set_fd(con->ssl, cnt))) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", ERR_error_string(ERR_get_error(), NULL)); return NULL; } } #endif return con; } } int connection_state_machine(server *srv, connection *con) { int done = 0, r; #ifdef USE_OPENSSL server_socket *srv_sock = con->srv_socket; #endif if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state at start", con->fd, connection_get_state(con->state)); } while (done == 0) { size_t ostate = con->state; switch (con->state) { case CON_STATE_REQUEST_START: /* transient */ if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } con->request_start = srv->cur_ts; con->read_idle_ts = srv->cur_ts; con->request_count++; con->loops_per_request = 0; connection_set_state(srv, con, CON_STATE_READ); /* patch con->conf.is_ssl if the connection is a ssl-socket already */ #ifdef USE_OPENSSL con->conf.is_ssl = srv_sock->is_ssl; #endif break; case CON_STATE_REQUEST_END: /* transient */ if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } buffer_reset(con->uri.authority); buffer_reset(con->uri.path); buffer_reset(con->uri.query); buffer_reset(con->request.orig_uri); if (http_request_parse(srv, con)) { /* we have to read some data from the POST request */ connection_set_state(srv, con, CON_STATE_READ_POST); break; } connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); break; case CON_STATE_HANDLE_REQUEST: /* * the request is parsed * * decided what to do with the request * - * * */ if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } switch (r = http_response_prepare(srv, con)) { case HANDLER_FINISHED: if (con->mode == DIRECT) { if (con->http_status == 404 || con->http_status == 403) { /* 404 error-handler */ if (con->in_error_handler == 0 && (!buffer_is_empty(con->conf.error_handler) || !buffer_is_empty(con->error_handler))) { /* call error-handler */ con->error_handler_saved_status = con->http_status; con->http_status = 0; if (buffer_is_empty(con->error_handler)) { buffer_copy_string_buffer(con->request.uri, con->conf.error_handler); } else { buffer_copy_string_buffer(con->request.uri, con->error_handler); } buffer_reset(con->physical.path); con->in_error_handler = 1; connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); done = -1; break; } else if (con->in_error_handler) { /* error-handler is a 404 */ con->http_status = con->error_handler_saved_status; } } else if (con->in_error_handler) { /* error-handler is back and has generated content */ /* if Status: was set, take it otherwise use 200 */ } } if (con->http_status == 0) con->http_status = 200; /* we have something to send, go on */ connection_set_state(srv, con, CON_STATE_RESPONSE_START); break; case HANDLER_WAIT_FOR_FD: srv->want_fds++; fdwaitqueue_append(srv, con); connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); break; case HANDLER_COMEBACK: done = -1; case HANDLER_WAIT_FOR_EVENT: /* come back here */ connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); break; case HANDLER_ERROR: /* something went wrong */ connection_set_state(srv, con, CON_STATE_ERROR); break; default: log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown ret-value: ", con->fd, r); break; } break; case CON_STATE_RESPONSE_START: /* * the decision is done * - create the HTTP-Response-Header * */ if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } if (-1 == connection_handle_write_prepare(srv, con)) { connection_set_state(srv, con, CON_STATE_ERROR); break; } connection_set_state(srv, con, CON_STATE_WRITE); break; case CON_STATE_RESPONSE_END: /* transient */ /* log the request */ if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } plugins_call_handle_request_done(srv, con); srv->con_written++; if (con->keep_alive) { connection_set_state(srv, con, CON_STATE_REQUEST_START); #if 0 con->request_start = srv->cur_ts; con->read_idle_ts = srv->cur_ts; #endif } else { switch(r = plugins_call_handle_connection_close(srv, con)) { case HANDLER_GO_ON: case HANDLER_FINISHED: break; default: log_error_write(srv, __FILE__, __LINE__, "sd", "unhandling return value", r); break; } #ifdef USE_OPENSSL if (srv_sock->is_ssl) { switch (SSL_shutdown(con->ssl)) { case 1: /* done */ break; case 0: /* wait for fd-event * * FIXME: wait for fdevent and call SSL_shutdown again * */ break; default: log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", ERR_error_string(ERR_get_error(), NULL)); } } #endif if ((0 == shutdown(con->fd, SHUT_WR))) { con->close_timeout_ts = srv->cur_ts; connection_set_state(srv, con, CON_STATE_CLOSE); } else { connection_close(srv, con); } srv->con_closed++; } connection_reset(srv, con); break; case CON_STATE_CONNECT: if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } chunkqueue_reset(con->read_queue); con->request_count = 0; break; case CON_STATE_CLOSE: if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } /* we have to do the linger_on_close stuff regardless * of con->keep_alive; even non-keepalive sockets may * still have unread data, and closing before reading * it will make the client not see all our output. */ { int len; char buf[1024]; #ifdef HAVE_LIBMTCP len = mtcp_read(srv->mctx, con->fd, buf, sizeof(buf)); #else len = read(con->fd, buf, sizeof(buf)); #endif if (len == 0 || (len < 0 && errno != EAGAIN && errno != EINTR) ) { con->close_timeout_ts = srv->cur_ts - (HTTP_LINGER_TIMEOUT+1); } } if (srv->cur_ts - con->close_timeout_ts > HTTP_LINGER_TIMEOUT) { connection_close(srv, con); if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed for fd", con->fd); } } break; case CON_STATE_READ_POST: case CON_STATE_READ: if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } connection_handle_read_state(srv, con); break; case CON_STATE_WRITE: if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } /* only try to write if we have something in the queue */ if (!chunkqueue_is_empty(con->write_queue)) { #if 0 log_error_write(srv, __FILE__, __LINE__, "dsd", con->fd, "packets to write:", con->write_queue->used); #endif } if (!chunkqueue_is_empty(con->write_queue) && con->is_writable) { if (-1 == connection_handle_write(srv, con)) { log_error_write(srv, __FILE__, __LINE__, "ds", con->fd, "handle write failed."); connection_set_state(srv, con, CON_STATE_ERROR); } } break; case CON_STATE_ERROR: /* transient */ /* even if the connection was drop we still have to write it to the access log */ if (con->http_status) { plugins_call_handle_request_done(srv, con); } #ifdef USE_OPENSSL if (srv_sock->is_ssl) { int ret, ssl_r; unsigned long err; ERR_clear_error(); switch ((ret = SSL_shutdown(con->ssl))) { case 1: /* ok */ break; case 0: ERR_clear_error(); if (-1 != (ret = SSL_shutdown(con->ssl))) break; /* fall through */ default: switch ((ssl_r = SSL_get_error(con->ssl, ret))) { case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: break; case SSL_ERROR_SYSCALL: /* perhaps we have error waiting in our error-queue */ if (0 != (err = ERR_get_error())) { do { log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", ssl_r, ret, ERR_error_string(err, NULL)); } while((err = ERR_get_error())); } else if (errno != 0) { /* ssl bug (see lighttpd ticket #2213): sometimes errno == 0 */ switch(errno) { case EPIPE: case ECONNRESET: break; default: log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL (error):", ssl_r, ret, errno, strerror(errno)); break; } } break; default: while((err = ERR_get_error())) { log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", ssl_r, ret, ERR_error_string(err, NULL)); } break; } } } ERR_clear_error(); #endif switch(con->mode) { case DIRECT: #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "emergency exit: direct", con->fd); #endif break; default: switch(r = plugins_call_handle_connection_close(srv, con)) { case HANDLER_GO_ON: case HANDLER_FINISHED: break; default: log_error_write(srv, __FILE__, __LINE__, "sd", "unhandling return value", r); break; } break; } connection_reset(srv, con); /* close the connection */ if ((0 == shutdown(con->fd, SHUT_WR))) { con->close_timeout_ts = srv->cur_ts; connection_set_state(srv, con, CON_STATE_CLOSE); if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sd", "shutdown for fd", con->fd); } } else { connection_close(srv, con); } con->keep_alive = 0; srv->con_closed++; break; default: log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown state:", con->fd, con->state); break; } if (done == -1) { done = 0; } else if (ostate == con->state) { done = 1; } } if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state at exit:", con->fd, connection_get_state(con->state)); } switch(con->state) { case CON_STATE_READ_POST: case CON_STATE_READ: case CON_STATE_CLOSE: fdevent_event_set(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_IN); break; case CON_STATE_WRITE: /* request write-fdevent only if we really need it * - if we have data to write * - if the socket is not writable yet */ if (!chunkqueue_is_empty(con->write_queue) && (con->is_writable == 0) && (con->traffic_limit_reached == 0)) { fdevent_event_set(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_OUT); } else { fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); } break; default: fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); break; } return 0; }