Diff for /embedaddon/lighttpd/src/connections.c between versions 1.1.1.1 and 1.1.1.3

version 1.1.1.1, 2013/10/14 10:32:48 version 1.1.1.3, 2016/11/02 10:35:00
Line 1 Line 1
   #include "first.h"
   
 #include "buffer.h"  #include "buffer.h"
 #include "server.h"  #include "server.h"
 #include "log.h"  #include "log.h"
 #include "connections.h"  #include "connections.h"
 #include "fdevent.h"  #include "fdevent.h"
   
   #include "configfile.h"
 #include "request.h"  #include "request.h"
 #include "response.h"  #include "response.h"
 #include "network.h"  #include "network.h"
Line 48  static connection *connections_get_new_connection(serv Line 51  static connection *connections_get_new_connection(serv
                 conns->size = 128;                  conns->size = 128;
                 conns->ptr = NULL;                  conns->ptr = NULL;
                 conns->ptr = malloc(sizeof(*conns->ptr) * conns->size);                  conns->ptr = malloc(sizeof(*conns->ptr) * conns->size);
                   force_assert(NULL != conns->ptr);
                 for (i = 0; i < conns->size; i++) {                  for (i = 0; i < conns->size; i++) {
                         conns->ptr[i] = connection_init(srv);                          conns->ptr[i] = connection_init(srv);
                 }                  }
         } else if (conns->size == conns->used) {          } else if (conns->size == conns->used) {
                 conns->size += 128;                  conns->size += 128;
                 conns->ptr = realloc(conns->ptr, sizeof(*conns->ptr) * conns->size);                  conns->ptr = realloc(conns->ptr, sizeof(*conns->ptr) * conns->size);
                   force_assert(NULL != conns->ptr);
   
                 for (i = conns->used; i < conns->size; i++) {                  for (i = conns->used; i < conns->size; i++) {
                         conns->ptr[i] = connection_init(srv);                          conns->ptr[i] = connection_init(srv);
Line 113  static int connection_del(server *srv, connection *con Line 118  static int connection_del(server *srv, connection *con
         return 0;          return 0;
 }  }
   
int connection_close(server *srv, connection *con) {static int connection_close(server *srv, connection *con) {
 #ifdef USE_OPENSSL  #ifdef USE_OPENSSL
         server_socket *srv_sock = con->srv_socket;          server_socket *srv_sock = con->srv_socket;
 #endif  
   
 #ifdef USE_OPENSSL  
         if (srv_sock->is_ssl) {          if (srv_sock->is_ssl) {
                 if (con->ssl) SSL_free(con->ssl);                  if (con->ssl) SSL_free(con->ssl);
                 con->ssl = NULL;                  con->ssl = NULL;
Line 138  int connection_close(server *srv, connection *con) { Line 140  int connection_close(server *srv, connection *con) {
                                 "(warning) close:", con->fd, strerror(errno));                                  "(warning) close:", con->fd, strerror(errno));
         }          }
 #endif  #endif
           con->fd = -1;
   
         srv->cur_fds--;          srv->cur_fds--;
 #if 0  #if 0
Line 151  int connection_close(server *srv, connection *con) { Line 154  int connection_close(server *srv, connection *con) {
         return 0;          return 0;
 }  }
   
#if 0static void connection_handle_close_state(server *srv, connection *con) {
static void dump_packet(const unsigned char *data, size_t len) {        /* we have to do the linger_on_close stuff regardless
        size_t i, j;         * 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;        len = read(con->fd, buf, sizeof(buf));
        if (len == 0 || (len < 0 && errno != EAGAIN && errno != EINTR) ) {
        for (i = 0; i < len; i++) {                con->close_timeout_ts = srv->cur_ts - (HTTP_LINGER_TIMEOUT+1);
                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"); 
                } 
         }          }
   
        if (len % 16 != 0) {        if (srv->cur_ts - con->close_timeout_ts > HTTP_LINGER_TIMEOUT) {
                for (j = i % 16; j < 16; j++) {                connection_close(srv, con);
                        fprintf(stderr, "   "); 
                } 
   
                fprintf(stderr, "  ");                if (srv->srvconf.log_state_handling) {
                for (j = i & ~0xf; j < len; j++) {                        log_error_write(srv, __FILE__, __LINE__, "sd",
                        unsigned char c;                                        "connection closed for fd", con->fd);
 
                        c = data[j]; 
                        fprintf(stderr, "%c", c > 32 && c < 128 ? c : '.'); 
                 }                  }
                 fprintf(stderr, "\n");  
         }          }
 }  }
 #endif  
   
static int connection_handle_read_ssl(server *srv, connection *con) {static void connection_handle_shutdown(server *srv, connection *con) {
#ifdef USE_OPENSSL        int r;
        int r, ssl_err, len, count = 0, read_offset, toread; 
        buffer *b = NULL; 
   
        if (!con->srv_socket->is_ssl) return -1;#ifdef USE_OPENSSL
        server_socket *srv_sock = con->srv_socket;
        ERR_clear_error();        if (srv_sock->is_ssl) {
        do {                int ret, ssl_r;
                if (NULL != con->read_queue->last) {                unsigned long err;
                        b = con->read_queue->last->mem;                ERR_clear_error();
                }                switch ((ret = SSL_shutdown(con->ssl))) {
                case 1:
                if (NULL == b || b->size - b->used < 1024) {                        /* ok */
                        b = chunkqueue_get_append_buffer(con->read_queue);                        break;
                        len = SSL_pending(con->ssl);                case 0:
                        if (len < 4*1024) len = 4*1024; /* always alloc >= 4k buffer */                        /* wait for fd-event
                        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() 
                          *                           *
                         * SSL_ERROR_SYSCALL                         * FIXME: wait for fdevent and call SSL_shutdown again
                         *   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). 
                          *                           *
                          */                           */
                        while((ssl_err = ERR_get_error())) {                        ERR_clear_error();
                                /* get all errors from the error-queue */                        if (-1 != (ret = SSL_shutdown(con->ssl))) break;
                                log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:", 
                                                r, ERR_error_string(ssl_err, NULL)); 
                        } 
   
                        switch(oerrno) {                        /* fall through */
                        default:                default:
                                log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:",
                                                len, r, oerrno,                        switch ((ssl_r = SSL_get_error(con->ssl, ret))) {
                                                strerror(oerrno));                        case SSL_ERROR_ZERO_RETURN:
                                 break;                                  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;                                break;
                case SSL_ERROR_ZERO_RETURN:                        default:
                        /* clean shutdown on the remote side */                                while((err = ERR_get_error())) {
                                         log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:",
                                                         ssl_r, ret,
                                                         ERR_error_string(err, NULL));
                                 }
   
                        if (r == 0) {                                break;
                                /* FIXME: later */ 
                         }                          }
   
                         /* 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;        srv->con_closed++;
        } else if (len == 0) {        connection_reset(srv, con);
                con->is_readable = 0; 
                /* the other end close the connection -> KEEP-ALIVE */ 
   
                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 {          } 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 void connection_handle_response_end_state(server *srv, connection *con) {
static int connection_handle_read(server *srv, connection *con) {        /* log the request */
        int len;        /* (even if error, connection dropped, still write to access log if http_status) */
        buffer *b;        if (con->http_status) {
        int toread, read_offset;                plugins_call_handle_request_done(srv, con);
 
        if (con->srv_socket->is_ssl) { 
                return connection_handle_read_ssl(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        if ((con->request.content_length
         *  us more than 4kb is available             && (off_t)con->request.content_length > con->request_content_queue->bytes_in)
         * if FIONREAD doesn't signal a big chunk we fill the previous buffer            || con->state == CON_STATE_ERROR) {
         *  if it has >= 1kb free                /* request body is present and has not been read completely */
         */                con->keep_alive = 0;
#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); 
         }          }
   
        read_offset = (b->used == 0) ? 0 : b->used - 1;        if (con->keep_alive) {
        len = recv(con->fd, b->ptr + read_offset, b->size - 1 - read_offset, 0);                connection_reset(srv, con);
#else#if 0
        if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) {                con->request_start = srv->cur_ts;
                if (NULL == b || b->size - b->used < 1024) {                con->read_idle_ts = srv->cur_ts;
                        b = chunkqueue_get_append_buffer(con->read_queue);#endif
                        buffer_prepare_copy(b, 4 * 1024);                connection_set_state(srv, con, CON_STATE_REQUEST_START);
                } 
         } else {          } else {
                if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT;                connection_handle_shutdown(srv, con);
                b = chunkqueue_get_append_buffer(con->read_queue); 
                buffer_prepare_copy(b, toread + 1); 
         }          }
   }
   
        read_offset = (b->used == 0) ? 0 : b->used - 1;static void connection_handle_errdoc_init(server *srv, connection *con) {
        len = read(con->fd, b->ptr + read_offset, b->size - 1 - read_offset);        /* modules that produce headers required with error response should
#endif         * typically also produce an error document.  Make an exception for
         * mod_auth WWW-Authenticate response header. */
        if (len < 0) {        buffer *www_auth = NULL;
                con->is_readable = 0;        if (401 == con->http_status) {
                data_string *ds = (data_string *)array_get_element(con->response.headers, "WWW-Authenticate");
                if (errno == EAGAIN) return 0;                if (NULL != ds) {
                if (errno == EINTR) {                        www_auth = buffer_init_buffer(ds->value);
                        /* we have been interrupted before we could read */ 
                        con->is_readable = 1; 
                        return 0; 
                 }                  }
   
                 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--;        con->response.transfer_encoding = 0;
        b->used += len;        buffer_reset(con->physical.path);
        b->ptr[b->used++] = '\0';        array_reset(con->response.headers);
         chunkqueue_reset(con->write_queue);
   
        con->bytes_read += len;        if (NULL != www_auth) {
#if 0                response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(www_auth));
        dump_packet(b->ptr, len);                buffer_free(www_auth);
#endif        }
 
        return 0; 
 }  }
   
 static int connection_handle_write_prepare(server *srv, connection *con) {  static int connection_handle_write_prepare(server *srv, connection *con) {
Line 426  static int connection_handle_write_prepare(server *srv Line 337  static int connection_handle_write_prepare(server *srv
                          * 403 is from the response handler when noone else catched it                           * 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] != '*') {                              con->uri.path->ptr[0] != '*') {
                                 response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST"));                                  response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST"));
   
Line 470  static int connection_handle_write_prepare(server *srv Line 381  static int connection_handle_write_prepare(server *srv
   
                 con->file_finished = 0;                  con->file_finished = 0;
   
                buffer_reset(con->physical.path);                connection_handle_errdoc_init(srv, con);
   
                 /* try to send static errorfile */                  /* 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;                          stat_cache_entry *sce = NULL;
   
                        buffer_copy_string_buffer(con->physical.path, con->conf.errorfile_prefix);                        buffer_copy_buffer(con->physical.path, con->conf.errorfile_prefix);
                        buffer_append_long(con->physical.path, con->http_status);                        buffer_append_int(con->physical.path, con->http_status);
                         buffer_append_string_len(con->physical.path, CONST_STR_LEN(".html"));                          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;                                  con->file_finished = 1;
                                if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
                                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));
                                response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type));                                }
                         }                          }
                 }                  }
   
Line 494  static int connection_handle_write_prepare(server *srv Line 405  static int connection_handle_write_prepare(server *srv
                         buffer_reset(con->physical.path);                          buffer_reset(con->physical.path);
   
                         con->file_finished = 1;                          con->file_finished = 1;
                        b = chunkqueue_get_append_buffer(con->write_queue);                        b = buffer_init();
   
                         /* build default error-page */                          /* build default error-page */
                         buffer_copy_string_len(b, CONST_STR_LEN(                          buffer_copy_string_len(b, CONST_STR_LEN(
Line 504  static int connection_handle_write_prepare(server *srv Line 415  static int connection_handle_write_prepare(server *srv
                                            "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"                                             "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
                                            " <head>\n"                                             " <head>\n"
                                            "  <title>"));                                             "  <title>"));
                        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_len(b, CONST_STR_LEN(" - "));
                         buffer_append_string(b, get_http_status_name(con->http_status));                          buffer_append_string(b, get_http_status_name(con->http_status));
   
Line 513  static int connection_handle_write_prepare(server *srv Line 424  static int connection_handle_write_prepare(server *srv
                                              " </head>\n"                                               " </head>\n"
                                              " <body>\n"                                               " <body>\n"
                                              "  <h1>"));                                               "  <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_len(b, CONST_STR_LEN(" - "));
                         buffer_append_string(b, get_http_status_name(con->http_status));                          buffer_append_string(b, get_http_status_name(con->http_status));
   
Line 522  static int connection_handle_write_prepare(server *srv Line 433  static int connection_handle_write_prepare(server *srv
                                              "</html>\n"                                               "</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"));                          response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
                 }                  }
                 break;                  break;
Line 554  static int connection_handle_write_prepare(server *srv Line 468  static int connection_handle_write_prepare(server *srv
                                 /* qlen = 0 is important for Redirects (301, ...) as they MAY have                                  /* qlen = 0 is important for Redirects (301, ...) as they MAY have
                                  * a content. Browsers are waiting for a Content otherwise                                   * 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));                                  response_header_overwrite(srv, con, CONST_STR_LEN("Content-Length"), CONST_BUF_LEN(srv->tmp_buf));
                         }                          }
Line 570  static int connection_handle_write_prepare(server *srv Line 484  static int connection_handle_write_prepare(server *srv
   
                 if (((con->parsed_response & HTTP_CONTENT_LENGTH) == 0) &&                  if (((con->parsed_response & HTTP_CONTENT_LENGTH) == 0) &&
                     ((con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 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;
                         }
                 }                  }
   
                 /**                  /**
Line 612  static int connection_handle_write(server *srv, connec Line 540  static int connection_handle_write(server *srv, connec
                 con->write_request_ts = srv->cur_ts;                  con->write_request_ts = srv->cur_ts;
                 if (con->file_finished) {                  if (con->file_finished) {
                         connection_set_state(srv, con, CON_STATE_RESPONSE_END);                          connection_set_state(srv, con, CON_STATE_RESPONSE_END);
                         joblist_append(srv, con);  
                 }                  }
                 break;                  break;
         case -1: /* error on our side */          case -1: /* error on our side */
                 log_error_write(srv, __FILE__, __LINE__, "sd",                  log_error_write(srv, __FILE__, __LINE__, "sd",
                                 "connection closed: write failed on fd", con->fd);                                  "connection closed: write failed on fd", con->fd);
                 connection_set_state(srv, con, CON_STATE_ERROR);                  connection_set_state(srv, con, CON_STATE_ERROR);
                 joblist_append(srv, con);  
                 break;                  break;
         case -2: /* remote close */          case -2: /* remote close */
                 connection_set_state(srv, con, CON_STATE_ERROR);                  connection_set_state(srv, con, CON_STATE_ERROR);
                 joblist_append(srv, con);  
                 break;                  break;
         case 1:          case 1:
                 con->write_request_ts = srv->cur_ts;                  con->write_request_ts = srv->cur_ts;
Line 644  connection *connection_init(server *srv) { Line 569  connection *connection_init(server *srv) {
         UNUSED(srv);          UNUSED(srv);
   
         con = calloc(1, sizeof(*con));          con = calloc(1, sizeof(*con));
           force_assert(NULL != con);
   
         con->fd = 0;          con->fd = 0;
         con->ndx = -1;          con->ndx = -1;
Line 677  connection *connection_init(server *srv) { Line 603  connection *connection_init(server *srv) {
         CLEAN(parse_request);          CLEAN(parse_request);
   
         CLEAN(server_name);          CLEAN(server_name);
         CLEAN(error_handler);  
         CLEAN(dst_addr_buf);          CLEAN(dst_addr_buf);
 #if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT  #if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT
         CLEAN(tlsext_server_name);          CLEAN(tlsext_server_name);
Line 687  connection *connection_init(server *srv) { Line 612  connection *connection_init(server *srv) {
         con->write_queue = chunkqueue_init();          con->write_queue = chunkqueue_init();
         con->read_queue = chunkqueue_init();          con->read_queue = chunkqueue_init();
         con->request_content_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->request.headers      = array_init();
         con->response.headers     = array_init();          con->response.headers     = array_init();
Line 696  connection *connection_init(server *srv) { Line 620  connection *connection_init(server *srv) {
         /* init plugin specific connection structures */          /* init plugin specific connection structures */
   
         con->plugin_ctx = calloc(1, (srv->plugins.used + 1) * sizeof(void *));          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));          con->cond_cache = calloc(srv->config_context->used, sizeof(cond_cache_t));
           force_assert(NULL != con->cond_cache);
         config_setup_connection(srv, con);          config_setup_connection(srv, con);
   
         return con;          return con;
Line 743  void connections_free(server *srv) { Line 669  void connections_free(server *srv) {
                 CLEAN(parse_request);                  CLEAN(parse_request);
   
                 CLEAN(server_name);                  CLEAN(server_name);
                 CLEAN(error_handler);  
                 CLEAN(dst_addr_buf);                  CLEAN(dst_addr_buf);
 #if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT  #if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT
                 CLEAN(tlsext_server_name);                  CLEAN(tlsext_server_name);
Line 816  int connection_reset(server *srv, connection *con) { Line 741  int connection_reset(server *srv, connection *con) {
         CLEAN(parse_request);          CLEAN(parse_request);
   
         CLEAN(server_name);          CLEAN(server_name);
         CLEAN(error_handler);  
 #if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT  #if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT
         CLEAN(tlsext_server_name);          CLEAN(tlsext_server_name);
 #endif  #endif
Line 861  int connection_reset(server *srv, connection *con) { Line 785  int connection_reset(server *srv, connection *con) {
         /* config_cond_cache_reset(srv, con); */          /* config_cond_cache_reset(srv, con); */
   
         con->header_len = 0;          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);          config_setup_connection(srv, con);
   
Line 874  int connection_reset(server *srv, connection *con) { Line 800  int connection_reset(server *srv, connection *con) {
  * we get called by the state-engine and by the fdevent-handler   * we get called by the state-engine and by the fdevent-handler
  */   */
 static int connection_handle_read_state(server *srv, connection *con)  {  static int connection_handle_read_state(server *srv, connection *con)  {
         connection_state_t ostate = con->state;  
         chunk *c, *last_chunk;          chunk *c, *last_chunk;
         off_t last_offset;          off_t last_offset;
         chunkqueue *cq = con->read_queue;          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 */          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) {          if (con->is_readable) {
                 con->read_idle_ts = srv->cur_ts;                  con->read_idle_ts = srv->cur_ts;
Line 895  static int connection_handle_read_state(server *srv, c Line 821  static int connection_handle_read_state(server *srv, c
                 }                  }
         }          }
   
        /* the last chunk might be empty */        chunkqueue_remove_finished_chunks(cq);
        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 */ 
   
                         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          /* we might have got several packets at once
          */           */
   
        switch(ostate) {        /* update request_start timestamp when first byte of
        case CON_STATE_READ:         * 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                  /* if there is a \r\n\r\n in the chunkqueue
                  *                   *
                  * scan the chunk-queue twice                   * scan the chunk-queue twice
Line 949  static int connection_handle_read_state(server *srv, c Line 846  static int connection_handle_read_state(server *srv, c
                 last_offset = 0;                  last_offset = 0;
   
                 for (c = cq->first; c; c = c->next) {                  for (c = cq->first; c; c = c->next) {
                         buffer b;  
                         size_t i;                          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;                        for (i = 0; i < len; ++i) {
                        b.used = c->mem->used - c->offset;                                char ch = b[i];
                        if (b.used > 0) b.used--; /* buffer "used" includes terminating zero */ 
   
                         for (i = 0; i < b.used; i++) {  
                                 char ch = b.ptr[i];  
   
                                 if ('\r' == ch) {                                  if ('\r' == ch) {
                                         /* chec if \n\r\n follows */                                          /* chec if \n\r\n follows */
                                         size_t j = i+1;                                          size_t j = i+1;
Line 967  static int connection_handle_read_state(server *srv, c Line 861  static int connection_handle_read_state(server *srv, c
                                         int header_end_match_pos = 1;                                          int header_end_match_pos = 1;
   
                                         for ( ; cc; cc = cc->next, j = 0 ) {                                          for ( ; cc; cc = cc->next, j = 0 ) {
                                                buffer bb;                                                size_t bblen = buffer_string_length(cc->mem) - cc->offset;
                                                bb.ptr = cc->mem->ptr + cc->offset;                                                const char *bb = cc->mem->ptr + cc->offset;
                                                bb.used = cc->mem->used - cc->offset; 
                                                if (bb.used > 0) bb.used--; /* buffer "used" includes terminating zero */ 
   
                                                for ( ; j < bb.used; j++) {                                                for ( ; j < bblen; j++) {
                                                        ch = bb.ptr[j];                                                        ch = bb[j];
   
                                                         if (ch == header_end[header_end_match_pos]) {                                                          if (ch == header_end[header_end_match_pos]) {
                                                                 header_end_match_pos++;                                                                  header_end_match_pos++;
Line 998  found_header_end: Line 890  found_header_end:
                         buffer_reset(con->request.request);                          buffer_reset(con->request.request);
   
                         for (c = cq->first; c; c = c->next) {                          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) {                                  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) {                                if (c == last_chunk) break;
                                        c->offset += last_offset; 
 
                                        break; 
                                } else { 
                                        /* the whole packet was copied */ 
                                        c->offset = c->mem->used - 1; 
                                } 
                         }                          }
   
                         connection_set_state(srv, con, CON_STATE_REQUEST_END);                          connection_set_state(srv, con, CON_STATE_REQUEST_END);
Line 1026  found_header_end: Line 910  found_header_end:
                         con->http_status = 414; /* Request-URI too large */                          con->http_status = 414; /* Request-URI too large */
                         con->keep_alive = 0;                          con->keep_alive = 0;
                         connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);                          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);          chunkqueue_remove_finished_chunks(cq);
   
         return 0;          return 0;
Line 1225  static handler_t connection_handle_fdevent(server *srv Line 980  static handler_t connection_handle_fdevent(server *srv
                 }                  }
         }          }
   
        if (con->state == CON_STATE_READ ||        if (con->state == CON_STATE_READ) {
            con->state == CON_STATE_READ_POST) { 
                 connection_handle_read_state(srv, con);                  connection_handle_read_state(srv, con);
         }          }
   
Line 1298  connection *connection_accept(server *srv, server_sock Line 1052  connection *connection_accept(server *srv, server_sock
                 }                  }
                 return NULL;                  return NULL;
         } else {          } 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;                  connection *con;
   
                 srv->cur_fds++;                  srv->cur_fds++;
Line 1313  connection *connection_accept(server *srv, server_sock Line 1075  connection *connection_accept(server *srv, server_sock
   
                 con->fd = cnt;                  con->fd = cnt;
                 con->fde_ndx = -1;                  con->fde_ndx = -1;
 #if 0  
                 gettimeofday(&(con->start_tv), NULL);  
 #endif  
                 fdevent_register(srv->ev, con->fd, connection_handle_fdevent, con);                  fdevent_register(srv->ev, con->fd, connection_handle_fdevent, con);
   
                 connection_set_state(srv, con, CON_STATE_REQUEST_START);                  connection_set_state(srv, con, CON_STATE_REQUEST_START);
   
                 con->connection_start = srv->cur_ts;                  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)));                  buffer_copy_string(con->dst_addr_buf, inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
                 con->srv_socket = srv_socket;                  con->srv_socket = srv_socket;
   
Line 1351  connection *connection_accept(server *srv, server_sock Line 1110  connection *connection_accept(server *srv, server_sock
                 }                  }
 #endif  #endif
                 return con;                  return con;
         }  
 }  }
   
   
 int connection_state_machine(server *srv, connection *con) {  int connection_state_machine(server *srv, connection *con) {
         int done = 0, r;          int done = 0, r;
 #ifdef USE_OPENSSL  
         server_socket *srv_sock = con->srv_socket;  
 #endif  
   
         if (srv->srvconf.log_state_handling) {          if (srv->srvconf.log_state_handling) {
                 log_error_write(srv, __FILE__, __LINE__, "sds",                  log_error_write(srv, __FILE__, __LINE__, "sds",
Line 1371  int connection_state_machine(server *srv, connection * Line 1126  int connection_state_machine(server *srv, connection *
         while (done == 0) {          while (done == 0) {
                 size_t ostate = con->state;                  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) {                  switch (con->state) {
                 case CON_STATE_REQUEST_START: /* transient */                  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->request_start = srv->cur_ts;
                         con->read_idle_ts = 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->request_count++;
                         con->loops_per_request = 0;                          con->loops_per_request = 0;
Line 1388  int connection_state_machine(server *srv, connection * Line 1145  int connection_state_machine(server *srv, connection *
   
                         break;                          break;
                 case CON_STATE_REQUEST_END: /* transient */                  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.authority);
                         buffer_reset(con->uri.path);                          buffer_reset(con->uri.path);
                         buffer_reset(con->uri.query);                          buffer_reset(con->uri.query);
Line 1409  int connection_state_machine(server *srv, connection * Line 1161  int connection_state_machine(server *srv, connection *
                         connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);                          connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
   
                         break;                          break;
                   case CON_STATE_READ_POST:
                 case CON_STATE_HANDLE_REQUEST:                  case CON_STATE_HANDLE_REQUEST:
                         /*                          /*
                          * the request is parsed                           * the request is parsed
Line 1419  int connection_state_machine(server *srv, connection * Line 1172  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)) {                          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:                          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->mode == DIRECT) {
                                        if (con->http_status == 404 ||                                        if (con->error_handler_saved_status) {
                                            con->http_status == 403) {                                                if (con->error_handler_saved_status > 0) {
                                                /* 404 error-handler */                                                        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 &&                                                if (error_handler) {
                                                    (!buffer_is_empty(con->conf.error_handler) || 
                                                     !buffer_is_empty(con->error_handler))) { 
                                                         /* call error-handler */                                                          /* call error-handler */
   
                                                        con->error_handler_saved_status = con->http_status;                                                        /* set REDIRECT_STATUS to save current HTTP status code
                                                        con->http_status = 0;                                                         * for access by dynamic handlers
                                                         * https://redmine.lighttpd.net/issues/1828 */
                                                        if (buffer_is_empty(con->error_handler)) {                                                        data_string *ds;
                                                                buffer_copy_string_buffer(con->request.uri, con->conf.error_handler);                                                        if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) {
                                                        } else {                                                                ds = data_string_init();
                                                                buffer_copy_string_buffer(con->request.uri, con->error_handler); 
                                                         }                                                          }
                                                        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;                                                          done = -1;
                                                         break;                                                          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;                                  if (con->http_status == 0) con->http_status = 200;
Line 1472  int connection_state_machine(server *srv, connection * Line 1263  int connection_state_machine(server *srv, connection *
   
                                 fdwaitqueue_append(srv, con);                                  fdwaitqueue_append(srv, con);
   
                                 connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);  
   
                                 break;                                  break;
                         case HANDLER_COMEBACK:                          case HANDLER_COMEBACK:
                                 done = -1;                                  done = -1;
                         case HANDLER_WAIT_FOR_EVENT:  
                                 /* come back here */  
                                 connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);  
   
                                 break;                                  break;
                         case HANDLER_ERROR:                          case HANDLER_ERROR:
                                 /* something went wrong */                                  /* something went wrong */
Line 1499  int connection_state_machine(server *srv, connection * Line 1284  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)) {                          if (-1 == connection_handle_write_prepare(srv, con)) {
                                 connection_set_state(srv, con, CON_STATE_ERROR);                                  connection_set_state(srv, con, CON_STATE_ERROR);
   
Line 1513  int connection_state_machine(server *srv, connection * Line 1293  int connection_state_machine(server *srv, connection *
                         connection_set_state(srv, con, CON_STATE_WRITE);                          connection_set_state(srv, con, CON_STATE_WRITE);
                         break;                          break;
                 case CON_STATE_RESPONSE_END: /* transient */                  case CON_STATE_RESPONSE_END: /* transient */
                        /* log the request */                case CON_STATE_ERROR:        /* transient */
                        connection_handle_response_end_state(srv, con);
                        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;                          break;
                 case CON_STATE_CONNECT:                  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);                          chunkqueue_reset(con->read_queue);
   
                         con->request_count = 0;                          con->request_count = 0;
   
                         break;                          break;
                 case CON_STATE_CLOSE:                  case CON_STATE_CLOSE:
                        if (srv->srvconf.log_state_handling) {                        connection_handle_close_state(srv, con);
                                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;                          break;
                 case CON_STATE_READ_POST:  
                 case CON_STATE_READ:                  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);                          connection_handle_read_state(srv, con);
                         break;                          break;
                 case CON_STATE_WRITE:                  case CON_STATE_WRITE:
                        if (srv->srvconf.log_state_handling) {                        do {
                                log_error_write(srv, __FILE__, __LINE__, "sds",                                /* only try to write if we have something in the queue */
                                                "state for fd", con->fd, connection_get_state(con->state));                                if (!chunkqueue_is_empty(con->write_queue)) {
                        }                                        if (con->is_writable) {
                                                if (-1 == connection_handle_write(srv, con)) {
                        /* only try to write if we have something in the queue */                                                        log_error_write(srv, __FILE__, __LINE__, "ds",
                        if (!chunkqueue_is_empty(con->write_queue)) {                                                                        con->fd,
#if 0                                                                        "handle write failed.");
                                log_error_write(srv, __FILE__, __LINE__, "dsd",                                                        connection_set_state(srv, con, CON_STATE_ERROR);
                                                con->fd,                                                        break;
                                                "packets to write:",                                                }
                                                con->write_queue->used);                                                if (con->state != CON_STATE_WRITE) break;
#endif                                        }
                        }                                } else if (con->file_finished) {
                        if (!chunkqueue_is_empty(con->write_queue) && con->is_writable) {                                        connection_set_state(srv, con, CON_STATE_RESPONSE_END);
                                if (-1 == connection_handle_write(srv, con)) {                                        break;
                                        log_error_write(srv, __FILE__, __LINE__, "ds", 
                                                        con->fd, 
                                                        "handle write failed."); 
                                        connection_set_state(srv, con, CON_STATE_ERROR); 
                                 }                                  }
                         }  
   
                        break;                                if (con->mode != DIRECT && !con->file_finished) {
                case CON_STATE_ERROR: /* transient */                                        switch(r = plugins_call_handle_subrequest(srv, con)) {
                                        case HANDLER_WAIT_FOR_EVENT:
                        /* even if the connection was drop we still have to write it to the access log */                                        case HANDLER_FINISHED:
                        if (con->http_status) {                                        case HANDLER_GO_ON:
                                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;                                                  break;
                                        case SSL_ERROR_SYSCALL:                                        case HANDLER_WAIT_FOR_FD:
                                                /* perhaps we have error waiting in our error-queue */                                                srv->want_fds++;
                                                if (0 != (err = ERR_get_error())) {                                                fdwaitqueue_append(srv, con);
                                                        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;                                                  break;
                                           case HANDLER_COMEBACK:
                                         default:                                          default:
                                                while((err = ERR_get_error())) {                                                log_error_write(srv, __FILE__, __LINE__, "sdd", "unexpected subrequest handler ret-value: ", con->fd, r);
                                                        log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:",                                                /* fall through */
                                                                        ssl_r, ret,                                        case HANDLER_ERROR:
                                                                        ERR_error_string(err, NULL));                                                connection_set_state(srv, con, CON_STATE_ERROR);
                                                } 
 
                                                 break;                                                  break;
                                         }                                          }
                                 }                                  }
                                ERR_clear_error();                        } while (con->state == CON_STATE_WRITE && (!chunkqueue_is_empty(con->write_queue) ? con->is_writable : con->file_finished));
                        } 
#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;                          break;
                 default:                  default:
                         log_error_write(srv, __FILE__, __LINE__, "sdd",                          log_error_write(srv, __FILE__, __LINE__, "sdd",
Line 1773  int connection_state_machine(server *srv, connection * Line 1370  int connection_state_machine(server *srv, connection *
                                 connection_get_state(con->state));                                  connection_get_state(con->state));
         }          }
   
           r = 0;
         switch(con->state) {          switch(con->state) {
         case CON_STATE_READ_POST:  
         case CON_STATE_READ:          case CON_STATE_READ:
         case CON_STATE_CLOSE:          case CON_STATE_CLOSE:
                fdevent_event_set(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_IN);                r = FDEVENT_IN;
                 break;                  break;
         case CON_STATE_WRITE:          case CON_STATE_WRITE:
                 /* request write-fdevent only if we really need it                  /* request write-fdevent only if we really need it
Line 1787  int connection_state_machine(server *srv, connection * Line 1384  int connection_state_machine(server *srv, connection *
                 if (!chunkqueue_is_empty(con->write_queue) &&                  if (!chunkqueue_is_empty(con->write_queue) &&
                     (con->is_writable == 0) &&                      (con->is_writable == 0) &&
                     (con->traffic_limit_reached == 0)) {                      (con->traffic_limit_reached == 0)) {
                        fdevent_event_set(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_OUT);                        r |= FDEVENT_OUT;
                } else { 
                        fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); 
                 }                  }
                   /* fall through */
           case CON_STATE_READ_POST:
                   if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN) {
                           r |= FDEVENT_IN;
                   }
                 break;                  break;
         default:          default:
                 fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd);  
                 break;                  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;          return 0;

Removed from v.1.1.1.1  
changed lines
  Added in v.1.1.1.3


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>