--- 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 " </head>\n" " <body>\n" " <h1>")); - 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 "</html>\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;