--- embedaddon/lighttpd/src/connections.c 2013/10/14 10:32:48 1.1.1.1
+++ embedaddon/lighttpd/src/connections.c 2016/11/02 10:35:00 1.1.1.3
@@ -1,9 +1,12 @@
+#include "first.h"
+
#include "buffer.h"
#include "server.h"
#include "log.h"
#include "connections.h"
#include "fdevent.h"
+#include "configfile.h"
#include "request.h"
#include "response.h"
#include "network.h"
@@ -48,12 +51,14 @@ static connection *connections_get_new_connection(serv
conns->size = 128;
conns->ptr = NULL;
conns->ptr = malloc(sizeof(*conns->ptr) * conns->size);
+ force_assert(NULL != conns->ptr);
for (i = 0; i < conns->size; i++) {
conns->ptr[i] = connection_init(srv);
}
} else if (conns->size == conns->used) {
conns->size += 128;
conns->ptr = realloc(conns->ptr, sizeof(*conns->ptr) * conns->size);
+ force_assert(NULL != conns->ptr);
for (i = conns->used; i < conns->size; i++) {
conns->ptr[i] = connection_init(srv);
@@ -113,12 +118,9 @@ static int connection_del(server *srv, connection *con
return 0;
}
-int connection_close(server *srv, connection *con) {
+static int connection_close(server *srv, connection *con) {
#ifdef USE_OPENSSL
server_socket *srv_sock = con->srv_socket;
-#endif
-
-#ifdef USE_OPENSSL
if (srv_sock->is_ssl) {
if (con->ssl) SSL_free(con->ssl);
con->ssl = NULL;
@@ -138,6 +140,7 @@ int connection_close(server *srv, connection *con) {
"(warning) close:", con->fd, strerror(errno));
}
#endif
+ con->fd = -1;
srv->cur_fds--;
#if 0
@@ -151,265 +154,173 @@ int connection_close(server *srv, connection *con) {
return 0;
}
-#if 0
-static void dump_packet(const unsigned char *data, size_t len) {
- size_t i, j;
+static void connection_handle_close_state(server *srv, connection *con) {
+ /* 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];
- if (len == 0) return;
-
- for (i = 0; i < len; i++) {
- if (i % 16 == 0) fprintf(stderr, " ");
-
- fprintf(stderr, "%02x ", data[i]);
-
- if ((i + 1) % 16 == 0) {
- fprintf(stderr, " ");
- for (j = 0; j <= i % 16; j++) {
- unsigned char c;
-
- if (i-15+j >= len) break;
-
- c = data[i-15+j];
-
- fprintf(stderr, "%c", c > 32 && c < 128 ? c : '.');
- }
-
- fprintf(stderr, "\n");
- }
+ 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 (len % 16 != 0) {
- for (j = i % 16; j < 16; j++) {
- fprintf(stderr, " ");
- }
+ if (srv->cur_ts - con->close_timeout_ts > HTTP_LINGER_TIMEOUT) {
+ connection_close(srv, con);
- fprintf(stderr, " ");
- for (j = i & ~0xf; j < len; j++) {
- unsigned char c;
-
- c = data[j];
- fprintf(stderr, "%c", c > 32 && c < 128 ? c : '.');
+ if (srv->srvconf.log_state_handling) {
+ log_error_write(srv, __FILE__, __LINE__, "sd",
+ "connection closed for fd", con->fd);
}
- fprintf(stderr, "\n");
}
}
-#endif
-static int connection_handle_read_ssl(server *srv, connection *con) {
-#ifdef USE_OPENSSL
- int r, ssl_err, len, count = 0, read_offset, toread;
- buffer *b = NULL;
+static void connection_handle_shutdown(server *srv, connection *con) {
+ int r;
- if (!con->srv_socket->is_ssl) return -1;
-
- ERR_clear_error();
- do {
- if (NULL != con->read_queue->last) {
- b = con->read_queue->last->mem;
- }
-
- if (NULL == b || b->size - b->used < 1024) {
- b = chunkqueue_get_append_buffer(con->read_queue);
- len = SSL_pending(con->ssl);
- if (len < 4*1024) len = 4*1024; /* always alloc >= 4k buffer */
- buffer_prepare_copy(b, len + 1);
-
- /* overwrite everything with 0 */
- memset(b->ptr, 0, b->size);
- }
-
- read_offset = (b->used > 0) ? b->used - 1 : 0;
- toread = b->size - 1 - read_offset;
-
- len = SSL_read(con->ssl, b->ptr + read_offset, toread);
-
- if (con->renegotiations > 1 && con->conf.ssl_disable_client_renegotiation) {
- log_error_write(srv, __FILE__, __LINE__, "s", "SSL: renegotiation initiated by client, killing connection");
- connection_set_state(srv, con, CON_STATE_ERROR);
- return -1;
- }
-
- if (len > 0) {
- if (b->used > 0) b->used--;
- b->used += len;
- b->ptr[b->used++] = '\0';
-
- con->bytes_read += len;
-
- count += len;
- }
- } while (len == toread && count < MAX_READ_LIMIT);
-
-
- if (len < 0) {
- int oerrno = errno;
- switch ((r = SSL_get_error(con->ssl, len))) {
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
- con->is_readable = 0;
-
- /* the manual says we have to call SSL_read with the same arguments next time.
- * we ignore this restriction; no one has complained about it in 1.5 yet, so it probably works anyway.
- */
-
- return 0;
- case SSL_ERROR_SYSCALL:
- /**
- * man SSL_get_error()
+#ifdef USE_OPENSSL
+ server_socket *srv_sock = con->srv_socket;
+ 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:
+ /* wait for fd-event
*
- * SSL_ERROR_SYSCALL
- * Some I/O error occurred. The OpenSSL error queue may contain more
- * information on the error. If the error queue is empty (i.e.
- * ERR_get_error() returns 0), ret can be used to find out more about
- * the error: If ret == 0, an EOF was observed that violates the
- * protocol. If ret == -1, the underlying BIO reported an I/O error
- * (for socket I/O on Unix systems, consult errno for details).
+ * FIXME: wait for fdevent and call SSL_shutdown again
*
*/
- while((ssl_err = ERR_get_error())) {
- /* get all errors from the error-queue */
- log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:",
- r, ERR_error_string(ssl_err, NULL));
- }
+ ERR_clear_error();
+ if (-1 != (ret = SSL_shutdown(con->ssl))) break;
- switch(oerrno) {
- default:
- log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:",
- len, r, oerrno,
- strerror(oerrno));
+ /* fall through */
+ default:
+
+ switch ((ssl_r = SSL_get_error(con->ssl, ret))) {
+ case SSL_ERROR_ZERO_RETURN:
break;
- }
+ case SSL_ERROR_WANT_WRITE:
+ /*con->is_writable = -1;*//*(no effect; shutdown() called below)*/
+ 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;
- case SSL_ERROR_ZERO_RETURN:
- /* clean shutdown on the remote side */
+ break;
+ default:
+ while((err = ERR_get_error())) {
+ log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:",
+ ssl_r, ret,
+ ERR_error_string(err, NULL));
+ }
- if (r == 0) {
- /* FIXME: later */
+ break;
}
-
- /* fall thourgh */
- default:
- while((ssl_err = ERR_get_error())) {
- switch (ERR_GET_REASON(ssl_err)) {
- case SSL_R_SSL_HANDSHAKE_FAILURE:
- case SSL_R_TLSV1_ALERT_UNKNOWN_CA:
- case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN:
- case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE:
- if (!con->conf.log_ssl_noise) continue;
- break;
- default:
- break;
- }
- /* get all errors from the error-queue */
- log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:",
- r, ERR_error_string(ssl_err, NULL));
- }
- break;
}
+ ERR_clear_error();
+ }
+#endif
- connection_set_state(srv, con, CON_STATE_ERROR);
+ 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;
+ }
- return -1;
- } else if (len == 0) {
- con->is_readable = 0;
- /* the other end close the connection -> KEEP-ALIVE */
+ srv->con_closed++;
+ connection_reset(srv, con);
- return -2;
+ /* 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 {
- joblist_append(srv, con);
+ connection_close(srv, con);
}
-
- return 0;
-#else
- UNUSED(srv);
- UNUSED(con);
- return -1;
-#endif
}
-/* 0: everything ok, -1: error, -2: con closed */
-static int connection_handle_read(server *srv, connection *con) {
- int len;
- buffer *b;
- int toread, read_offset;
-
- if (con->srv_socket->is_ssl) {
- return connection_handle_read_ssl(srv, con);
+static void connection_handle_response_end_state(server *srv, connection *con) {
+ /* log the request */
+ /* (even if error, connection dropped, still write to access log if http_status) */
+ if (con->http_status) {
+ plugins_call_handle_request_done(srv, con);
}
- b = (NULL != con->read_queue->last) ? con->read_queue->last->mem : NULL;
+ if (con->state != CON_STATE_ERROR) srv->con_written++;
- /* default size for chunks is 4kb; only use bigger chunks if FIONREAD tells
- * us more than 4kb is available
- * if FIONREAD doesn't signal a big chunk we fill the previous buffer
- * if it has >= 1kb free
- */
-#if defined(__WIN32)
- if (NULL == b || b->size - b->used < 1024) {
- b = chunkqueue_get_append_buffer(con->read_queue);
- buffer_prepare_copy(b, 4 * 1024);
+ if ((con->request.content_length
+ && (off_t)con->request.content_length > con->request_content_queue->bytes_in)
+ || con->state == CON_STATE_ERROR) {
+ /* request body is present and has not been read completely */
+ con->keep_alive = 0;
}
- read_offset = (b->used == 0) ? 0 : b->used - 1;
- len = recv(con->fd, b->ptr + read_offset, b->size - 1 - read_offset, 0);
-#else
- if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) {
- if (NULL == b || b->size - b->used < 1024) {
- b = chunkqueue_get_append_buffer(con->read_queue);
- buffer_prepare_copy(b, 4 * 1024);
- }
+ if (con->keep_alive) {
+ connection_reset(srv, con);
+#if 0
+ con->request_start = srv->cur_ts;
+ con->read_idle_ts = srv->cur_ts;
+#endif
+ connection_set_state(srv, con, CON_STATE_REQUEST_START);
} else {
- if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT;
- b = chunkqueue_get_append_buffer(con->read_queue);
- buffer_prepare_copy(b, toread + 1);
+ connection_handle_shutdown(srv, con);
}
+}
- read_offset = (b->used == 0) ? 0 : b->used - 1;
- len = read(con->fd, b->ptr + read_offset, b->size - 1 - read_offset);
-#endif
-
- if (len < 0) {
- con->is_readable = 0;
-
- if (errno == EAGAIN) return 0;
- if (errno == EINTR) {
- /* we have been interrupted before we could read */
- con->is_readable = 1;
- return 0;
+static void connection_handle_errdoc_init(server *srv, connection *con) {
+ /* modules that produce headers required with error response should
+ * typically also produce an error document. Make an exception for
+ * mod_auth WWW-Authenticate response header. */
+ buffer *www_auth = NULL;
+ if (401 == con->http_status) {
+ data_string *ds = (data_string *)array_get_element(con->response.headers, "WWW-Authenticate");
+ if (NULL != ds) {
+ www_auth = buffer_init_buffer(ds->value);
}
-
- if (errno != ECONNRESET) {
- /* expected for keep-alive */
- log_error_write(srv, __FILE__, __LINE__, "ssd", "connection closed - read failed: ", strerror(errno), errno);
- }
-
- connection_set_state(srv, con, CON_STATE_ERROR);
-
- return -1;
- } else if (len == 0) {
- con->is_readable = 0;
- /* the other end close the connection -> KEEP-ALIVE */
-
- /* pipelining */
-
- return -2;
- } else if ((size_t)len < b->size - 1) {
- /* we got less then expected, wait for the next fd-event */
-
- con->is_readable = 0;
}
- if (b->used > 0) b->used--;
- b->used += len;
- b->ptr[b->used++] = '\0';
+ con->response.transfer_encoding = 0;
+ buffer_reset(con->physical.path);
+ array_reset(con->response.headers);
+ chunkqueue_reset(con->write_queue);
- con->bytes_read += len;
-#if 0
- dump_packet(b->ptr, len);
-#endif
-
- return 0;
+ if (NULL != www_auth) {
+ response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(www_auth));
+ buffer_free(www_auth);
+ }
}
static int connection_handle_write_prepare(server *srv, connection *con) {
@@ -426,7 +337,7 @@ static int connection_handle_write_prepare(server *srv
* 403 is from the response handler when noone else catched it
*
* */
- if ((!con->http_status || con->http_status == 200) && con->uri.path->used &&
+ if ((!con->http_status || con->http_status == 200) && !buffer_string_is_empty(con->uri.path) &&
con->uri.path->ptr[0] != '*') {
response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST"));
@@ -470,21 +381,21 @@ static int connection_handle_write_prepare(server *srv
con->file_finished = 0;
- buffer_reset(con->physical.path);
+ connection_handle_errdoc_init(srv, con);
/* try to send static errorfile */
- if (!buffer_is_empty(con->conf.errorfile_prefix)) {
+ if (!buffer_string_is_empty(con->conf.errorfile_prefix)) {
stat_cache_entry *sce = NULL;
- buffer_copy_string_buffer(con->physical.path, con->conf.errorfile_prefix);
- buffer_append_long(con->physical.path, con->http_status);
+ buffer_copy_buffer(con->physical.path, con->conf.errorfile_prefix);
+ buffer_append_int(con->physical.path, con->http_status);
buffer_append_string_len(con->physical.path, CONST_STR_LEN(".html"));
- if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
+ if (0 == http_chunk_append_file(srv, con, con->physical.path)) {
con->file_finished = 1;
-
- http_chunk_append_file(srv, con, con->physical.path, 0, sce->st.st_size);
- response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type));
+ if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
+ response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type));
+ }
}
}
@@ -494,7 +405,7 @@ static int connection_handle_write_prepare(server *srv
buffer_reset(con->physical.path);
con->file_finished = 1;
- b = chunkqueue_get_append_buffer(con->write_queue);
+ b = buffer_init();
/* build default error-page */
buffer_copy_string_len(b, CONST_STR_LEN(
@@ -504,7 +415,7 @@ static int connection_handle_write_prepare(server *srv
"\n"
"
\n"
" "));
- buffer_append_long(b, con->http_status);
+ buffer_append_int(b, con->http_status);
buffer_append_string_len(b, CONST_STR_LEN(" - "));
buffer_append_string(b, get_http_status_name(con->http_status));
@@ -513,7 +424,7 @@ static int connection_handle_write_prepare(server *srv
" \n"
" \n"
" "));
- buffer_append_long(b, con->http_status);
+ buffer_append_int(b, con->http_status);
buffer_append_string_len(b, CONST_STR_LEN(" - "));
buffer_append_string(b, get_http_status_name(con->http_status));
@@ -522,6 +433,9 @@ static int connection_handle_write_prepare(server *srv
"\n"
));
+ (void)http_chunk_append_buffer(srv, con, b);
+ buffer_free(b);
+
response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
}
break;
@@ -554,7 +468,7 @@ static int connection_handle_write_prepare(server *srv
/* qlen = 0 is important for Redirects (301, ...) as they MAY have
* a content. Browsers are waiting for a Content otherwise
*/
- buffer_copy_off_t(srv->tmp_buf, qlen);
+ buffer_copy_int(srv->tmp_buf, qlen);
response_header_overwrite(srv, con, CONST_STR_LEN("Content-Length"), CONST_BUF_LEN(srv->tmp_buf));
}
@@ -570,7 +484,21 @@ static int connection_handle_write_prepare(server *srv
if (((con->parsed_response & HTTP_CONTENT_LENGTH) == 0) &&
((con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0)) {
- con->keep_alive = 0;
+ if (con->request.http_version == HTTP_VERSION_1_1) {
+ off_t qlen = chunkqueue_length(con->write_queue);
+ con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
+ if (qlen) {
+ /* create initial Transfer-Encoding: chunked segment */
+ buffer *b = srv->tmp_chunk_len;
+ buffer_string_set_length(b, 0);
+ buffer_append_uint_hex(b, (uintmax_t)qlen);
+ buffer_append_string_len(b, CONST_STR_LEN("\r\n"));
+ chunkqueue_prepend_buffer(con->write_queue, b);
+ chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("\r\n"));
+ }
+ } else {
+ con->keep_alive = 0;
+ }
}
/**
@@ -612,18 +540,15 @@ static int connection_handle_write(server *srv, connec
con->write_request_ts = srv->cur_ts;
if (con->file_finished) {
connection_set_state(srv, con, CON_STATE_RESPONSE_END);
- joblist_append(srv, con);
}
break;
case -1: /* error on our side */
log_error_write(srv, __FILE__, __LINE__, "sd",
"connection closed: write failed on fd", con->fd);
connection_set_state(srv, con, CON_STATE_ERROR);
- joblist_append(srv, con);
break;
case -2: /* remote close */
connection_set_state(srv, con, CON_STATE_ERROR);
- joblist_append(srv, con);
break;
case 1:
con->write_request_ts = srv->cur_ts;
@@ -644,6 +569,7 @@ connection *connection_init(server *srv) {
UNUSED(srv);
con = calloc(1, sizeof(*con));
+ force_assert(NULL != con);
con->fd = 0;
con->ndx = -1;
@@ -677,7 +603,6 @@ connection *connection_init(server *srv) {
CLEAN(parse_request);
CLEAN(server_name);
- CLEAN(error_handler);
CLEAN(dst_addr_buf);
#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT
CLEAN(tlsext_server_name);
@@ -687,7 +612,6 @@ connection *connection_init(server *srv) {
con->write_queue = chunkqueue_init();
con->read_queue = chunkqueue_init();
con->request_content_queue = chunkqueue_init();
- chunkqueue_set_tempdirs(con->request_content_queue, srv->srvconf.upload_tempdirs);
con->request.headers = array_init();
con->response.headers = array_init();
@@ -696,8 +620,10 @@ connection *connection_init(server *srv) {
/* init plugin specific connection structures */
con->plugin_ctx = calloc(1, (srv->plugins.used + 1) * sizeof(void *));
+ force_assert(NULL != con->plugin_ctx);
con->cond_cache = calloc(srv->config_context->used, sizeof(cond_cache_t));
+ force_assert(NULL != con->cond_cache);
config_setup_connection(srv, con);
return con;
@@ -743,7 +669,6 @@ void connections_free(server *srv) {
CLEAN(parse_request);
CLEAN(server_name);
- CLEAN(error_handler);
CLEAN(dst_addr_buf);
#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT
CLEAN(tlsext_server_name);
@@ -816,7 +741,6 @@ int connection_reset(server *srv, connection *con) {
CLEAN(parse_request);
CLEAN(server_name);
- CLEAN(error_handler);
#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT
CLEAN(tlsext_server_name);
#endif
@@ -861,7 +785,9 @@ int connection_reset(server *srv, connection *con) {
/* config_cond_cache_reset(srv, con); */
con->header_len = 0;
- con->in_error_handler = 0;
+ con->error_handler_saved_status = 0;
+ /*con->error_handler_saved_method = HTTP_METHOD_UNSET;*/
+ /*(error_handler_saved_method value is not valid unless error_handler_saved_status is set)*/
config_setup_connection(srv, con);
@@ -874,12 +800,12 @@ int connection_reset(server *srv, connection *con) {
* we get called by the state-engine and by the fdevent-handler
*/
static int connection_handle_read_state(server *srv, connection *con) {
- connection_state_t ostate = con->state;
chunk *c, *last_chunk;
off_t last_offset;
chunkqueue *cq = con->read_queue;
- chunkqueue *dst_cq = con->request_content_queue;
int is_closed = 0; /* the connection got closed, if we don't have a complete header, -> error */
+ /* when in CON_STATE_READ: about to receive first byte for a request: */
+ int is_request_start = chunkqueue_is_empty(cq);
if (con->is_readable) {
con->read_idle_ts = srv->cur_ts;
@@ -895,48 +821,19 @@ static int connection_handle_read_state(server *srv, c
}
}
- /* the last chunk might be empty */
- for (c = cq->first; c;) {
- if (cq->first == c && c->mem->used == 0) {
- /* the first node is empty */
- /* ... and it is empty, move it to unused */
+ chunkqueue_remove_finished_chunks(cq);
- cq->first = c->next;
- if (cq->first == NULL) cq->last = NULL;
-
- c->next = cq->unused;
- cq->unused = c;
- cq->unused_chunks++;
-
- c = cq->first;
- } else if (c->next && c->next->mem->used == 0) {
- chunk *fc;
- /* next node is the last one */
- /* ... and it is empty, move it to unused */
-
- fc = c->next;
- c->next = fc->next;
-
- fc->next = cq->unused;
- cq->unused = fc;
- cq->unused_chunks++;
-
- /* the last node was empty */
- if (c->next == NULL) {
- cq->last = c;
- }
-
- c = c->next;
- } else {
- c = c->next;
- }
- }
-
/* we might have got several packets at once
*/
- switch(ostate) {
- case CON_STATE_READ:
+ /* update request_start timestamp when first byte of
+ * next request is received on a keep-alive connection */
+ if (con->request_count > 1 && is_request_start) {
+ con->request_start = srv->cur_ts;
+ if (con->conf.high_precision_timestamps)
+ log_clock_gettime_realtime(&con->request_start_hp);
+ }
+
/* if there is a \r\n\r\n in the chunkqueue
*
* scan the chunk-queue twice
@@ -949,16 +846,13 @@ static int connection_handle_read_state(server *srv, c
last_offset = 0;
for (c = cq->first; c; c = c->next) {
- buffer b;
size_t i;
+ size_t len = buffer_string_length(c->mem) - c->offset;
+ const char *b = c->mem->ptr + c->offset;
- b.ptr = c->mem->ptr + c->offset;
- b.used = c->mem->used - c->offset;
- if (b.used > 0) b.used--; /* buffer "used" includes terminating zero */
+ for (i = 0; i < len; ++i) {
+ char ch = b[i];
- for (i = 0; i < b.used; i++) {
- char ch = b.ptr[i];
-
if ('\r' == ch) {
/* chec if \n\r\n follows */
size_t j = i+1;
@@ -967,13 +861,11 @@ static int connection_handle_read_state(server *srv, c
int header_end_match_pos = 1;
for ( ; cc; cc = cc->next, j = 0 ) {
- buffer bb;
- bb.ptr = cc->mem->ptr + cc->offset;
- bb.used = cc->mem->used - cc->offset;
- if (bb.used > 0) bb.used--; /* buffer "used" includes terminating zero */
+ size_t bblen = buffer_string_length(cc->mem) - cc->offset;
+ const char *bb = cc->mem->ptr + cc->offset;
- for ( ; j < bb.used; j++) {
- ch = bb.ptr[j];
+ for ( ; j < bblen; j++) {
+ ch = bb[j];
if (ch == header_end[header_end_match_pos]) {
header_end_match_pos++;
@@ -998,25 +890,17 @@ found_header_end:
buffer_reset(con->request.request);
for (c = cq->first; c; c = c->next) {
- buffer b;
+ size_t len = buffer_string_length(c->mem) - c->offset;
- b.ptr = c->mem->ptr + c->offset;
- b.used = c->mem->used - c->offset;
-
if (c == last_chunk) {
- b.used = last_offset + 1;
+ len = last_offset;
}
- buffer_append_string_buffer(con->request.request, &b);
+ buffer_append_string_len(con->request.request, c->mem->ptr + c->offset, len);
+ c->offset += len;
+ cq->bytes_out += len;
- if (c == last_chunk) {
- c->offset += last_offset;
-
- break;
- } else {
- /* the whole packet was copied */
- c->offset = c->mem->used - 1;
- }
+ if (c == last_chunk) break;
}
connection_set_state(srv, con, CON_STATE_REQUEST_END);
@@ -1026,141 +910,12 @@ found_header_end:
con->http_status = 414; /* Request-URI too large */
con->keep_alive = 0;
connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
+ } else if (is_closed) {
+ /* the connection got closed and we didn't got enough data to leave CON_STATE_READ;
+ * the only way is to leave here */
+ connection_set_state(srv, con, CON_STATE_ERROR);
}
- break;
- case CON_STATE_READ_POST:
- for (c = cq->first; c && (dst_cq->bytes_in != (off_t)con->request.content_length); c = c->next) {
- off_t weWant, weHave, toRead;
- weWant = con->request.content_length - dst_cq->bytes_in;
-
- assert(c->mem->used);
-
- weHave = c->mem->used - c->offset - 1;
-
- toRead = weHave > weWant ? weWant : weHave;
-
- /* the new way, copy everything into a chunkqueue whcih might use tempfiles */
- if (con->request.content_length > 64 * 1024) {
- chunk *dst_c = NULL;
- /* copy everything to max 1Mb sized tempfiles */
-
- /*
- * if the last chunk is
- * - smaller than 1Mb (size < 1Mb)
- * - not read yet (offset == 0)
- * -> append to it
- * otherwise
- * -> create a new chunk
- *
- * */
-
- if (dst_cq->last &&
- dst_cq->last->type == FILE_CHUNK &&
- dst_cq->last->file.is_temp &&
- dst_cq->last->offset == 0) {
- /* ok, take the last chunk for our job */
-
- if (dst_cq->last->file.length < 1 * 1024 * 1024) {
- dst_c = dst_cq->last;
-
- if (dst_c->file.fd == -1) {
- /* this should not happen as we cache the fd, but you never know */
- dst_c->file.fd = open(dst_c->file.name->ptr, O_WRONLY | O_APPEND);
-#ifdef FD_CLOEXEC
- fcntl(dst_c->file.fd, F_SETFD, FD_CLOEXEC);
-#endif
- }
- } else {
- /* the chunk is too large now, close it */
- dst_c = dst_cq->last;
-
- if (dst_c->file.fd != -1) {
- close(dst_c->file.fd);
- dst_c->file.fd = -1;
- }
- dst_c = chunkqueue_get_append_tempfile(dst_cq);
- }
- } else {
- dst_c = chunkqueue_get_append_tempfile(dst_cq);
- }
-
- /* we have a chunk, let's write to it */
-
- if (dst_c->file.fd == -1) {
- /* we don't have file to write to,
- * EACCES might be one reason.
- *
- * Instead of sending 500 we send 413 and say the request is too large
- * */
-
- log_error_write(srv, __FILE__, __LINE__, "sbs",
- "denying upload as opening to temp-file for upload failed:",
- dst_c->file.name, strerror(errno));
-
- con->http_status = 413; /* Request-Entity too large */
- con->keep_alive = 0;
- connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
-
- break;
- }
-
- if (toRead != write(dst_c->file.fd, c->mem->ptr + c->offset, toRead)) {
- /* write failed for some reason ... disk full ? */
- log_error_write(srv, __FILE__, __LINE__, "sbs",
- "denying upload as writing to file failed:",
- dst_c->file.name, strerror(errno));
-
- con->http_status = 413; /* Request-Entity too large */
- con->keep_alive = 0;
- connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
-
- close(dst_c->file.fd);
- dst_c->file.fd = -1;
-
- break;
- }
-
- dst_c->file.length += toRead;
-
- if (dst_cq->bytes_in + toRead == (off_t)con->request.content_length) {
- /* we read everything, close the chunk */
- close(dst_c->file.fd);
- dst_c->file.fd = -1;
- }
- } else {
- buffer *b;
-
- if (dst_cq->last &&
- dst_cq->last->type == MEM_CHUNK) {
- b = dst_cq->last->mem;
- } else {
- b = chunkqueue_get_append_buffer(dst_cq);
- /* prepare buffer size for remaining POST data; is < 64kb */
- buffer_prepare_copy(b, con->request.content_length - dst_cq->bytes_in + 1);
- }
- buffer_append_string_len(b, c->mem->ptr + c->offset, toRead);
- }
-
- c->offset += toRead;
- dst_cq->bytes_in += toRead;
- }
-
- /* Content is ready */
- if (dst_cq->bytes_in == (off_t)con->request.content_length) {
- connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
- }
-
- break;
- default: break;
- }
-
- /* the connection got closed and we didn't got enough data to leave one of the READ states
- * the only way is to leave here */
- if (is_closed && ostate == con->state) {
- connection_set_state(srv, con, CON_STATE_ERROR);
- }
-
chunkqueue_remove_finished_chunks(cq);
return 0;
@@ -1225,8 +980,7 @@ static handler_t connection_handle_fdevent(server *srv
}
}
- if (con->state == CON_STATE_READ ||
- con->state == CON_STATE_READ_POST) {
+ if (con->state == CON_STATE_READ) {
connection_handle_read_state(srv, con);
}
@@ -1298,6 +1052,14 @@ connection *connection_accept(server *srv, server_sock
}
return NULL;
} else {
+ if (cnt_addr.plain.sa_family != AF_UNIX) {
+ network_accept_tcp_nagle_disable(cnt);
+ }
+ return connection_accepted(srv, srv_socket, &cnt_addr, cnt);
+ }
+}
+
+connection *connection_accepted(server *srv, server_socket *srv_socket, sock_addr *cnt_addr, int cnt) {
connection *con;
srv->cur_fds++;
@@ -1313,15 +1075,12 @@ connection *connection_accept(server *srv, server_sock
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;
+ 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;
@@ -1351,15 +1110,11 @@ connection *connection_accept(server *srv, server_sock
}
#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",
@@ -1371,15 +1126,17 @@ int connection_state_machine(server *srv, connection *
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 */
- 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;
+ if (con->conf.high_precision_timestamps)
+ log_clock_gettime_realtime(&con->request_start_hp);
con->request_count++;
con->loops_per_request = 0;
@@ -1388,11 +1145,6 @@ int connection_state_machine(server *srv, connection *
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);
@@ -1409,6 +1161,7 @@ int connection_state_machine(server *srv, connection *
connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
break;
+ case CON_STATE_READ_POST:
case CON_STATE_HANDLE_REQUEST:
/*
* the request is parsed
@@ -1419,47 +1172,85 @@ int connection_state_machine(server *srv, connection *
*
*/
- 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_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->http_status == 404 ||
- con->http_status == 403) {
- /* 404 error-handler */
+ 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 (con->in_error_handler == 0 &&
- (!buffer_is_empty(con->conf.error_handler) ||
- !buffer_is_empty(con->error_handler))) {
+ if (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);
+ /* 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_reset(con->physical.path);
+ 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);
- con->in_error_handler = 1;
+ if (error_handler == con->conf.error_handler) {
+ plugins_call_connection_reset(srv, con);
- connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
+ if (con->request.content_length) {
+ if ((off_t)con->request.content_length != chunkqueue_length(con->request_content_queue)) {
+ 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;
- } 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;
@@ -1472,15 +1263,9 @@ int connection_state_machine(server *srv, connection *
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 */
@@ -1499,11 +1284,6 @@ int connection_state_machine(server *srv, connection *
*
*/
- 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);
@@ -1513,244 +1293,61 @@ int connection_state_machine(server *srv, connection *
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);
-
+ case CON_STATE_ERROR: /* transient */
+ connection_handle_response_end_state(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);
- }
- }
-
+ connection_handle_close_state(srv, con);
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);
+ 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;
}
- }
- 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:
+ 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 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;
- }
- }
-
+ case HANDLER_WAIT_FOR_FD:
+ srv->want_fds++;
+ fdwaitqueue_append(srv, con);
break;
+ case HANDLER_COMEBACK:
default:
- while((err = ERR_get_error())) {
- log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:",
- ssl_r, ret,
- ERR_error_string(err, NULL));
- }
-
+ 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;
}
}
- ERR_clear_error();
- }
-#endif
+ } while (con->state == CON_STATE_WRITE && (!chunkqueue_is_empty(con->write_queue) ? con->is_writable : con->file_finished));
- 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",
@@ -1773,11 +1370,11 @@ int connection_state_machine(server *srv, connection *
connection_get_state(con->state));
}
+ r = 0;
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);
+ r = FDEVENT_IN;
break;
case CON_STATE_WRITE:
/* request write-fdevent only if we really need it
@@ -1787,14 +1384,37 @@ int connection_state_machine(server *srv, connection *
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);
+ 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:
- fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd);
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;