diff options
Diffstat (limited to 'src/connections.c')
-rw-r--r-- | src/connections.c | 1599 |
1 files changed, 0 insertions, 1599 deletions
diff --git a/src/connections.c b/src/connections.c deleted file mode 100644 index 1069adab..00000000 --- a/src/connections.c +++ /dev/null @@ -1,1599 +0,0 @@ -#include <sys/stat.h> - -#include <stdlib.h> -#include <stdio.h> -#include <errno.h> -#include <string.h> -#include <fcntl.h> -#include <assert.h> - -#include "settings.h" - -#include "server.h" -#include "connections.h" -#include "fdevent.h" -#include "log.h" - -#include "request.h" -#include "response.h" -#include "network.h" -#include "stat_cache.h" -#include "joblist.h" - -#include "plugin.h" - -#include "inet_ntop_cache.h" -#include "configfile.h" -#include "http_req.h" - -#ifdef USE_OPENSSL -# include <openssl/ssl.h> -# include <openssl/err.h> -#endif - -#ifdef HAVE_SYS_FILIO_H -# include <sys/filio.h> -#endif - -#include "sys-socket.h" -#include "sys-files.h" - -typedef struct { - PLUGIN_DATA; -} plugin_data; - -static connection *connections_get_new_connection(server *srv) { - connections *conns = srv->conns; - size_t i; - - if (conns->size == 0) { - conns->size = 128; - conns->ptr = NULL; - conns->ptr = malloc(sizeof(*conns->ptr) * conns->size); - 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); - - for (i = conns->used; i < conns->size; i++) { - conns->ptr[i] = connection_init(srv); - } - } - - connection_reset(srv, conns->ptr[conns->used]); - - conns->ptr[conns->used]->ndx = conns->used; - return conns->ptr[conns->used++]; -} - -static int connection_del(server *srv, connection *con) { - size_t i; - connections *conns = srv->conns; - connection *temp; - - if (con == NULL) return -1; - - if (-1 == con->ndx) return -1; - - i = con->ndx; - - /* not last element */ - - if (i != conns->used - 1) { - temp = conns->ptr[i]; - conns->ptr[i] = conns->ptr[conns->used - 1]; - conns->ptr[conns->used - 1] = temp; - - conns->ptr[i]->ndx = i; - conns->ptr[conns->used - 1]->ndx = -1; - } - - conns->used--; - - con->ndx = -1; - - return 0; -} - -int connection_close(server *srv, connection *con) { -#ifdef USE_OPENSSL - /* should be in iosocket_close() */ - - if (con->sock->ssl) { - int ret, ssl_r; - unsigned long err; - - ERR_clear_error(); - switch (ret = SSL_shutdown(con->sock->ssl)) { - case 1: - /* done */ - break; - case 0: - /* wait for fd-event - * - * FIXME: wait for fdevent and call SSL_shutdown again - * (But it is not that important as we close the underlying connection anyway) - */ - - break; - default: - switch ((ssl_r = SSL_get_error(con->sock->ssl, ret))) { - case SSL_ERROR_WANT_WRITE: - case SSL_ERROR_WANT_READ: - break; - case SSL_ERROR_SYSCALL: - /* perhaps we have error waiting in our error-queue */ - if (0 != (err = ERR_get_error())) { - do { - ERROR("SSL_shutdown failed (%i, %i): %s", ssl_r, ret, ERR_error_string(err, NULL)); - } while((err = ERR_get_error())); - } else { - ERROR("SSL_shutdown failed (%i, %i, %i): %s", ssl_r, ret, errno, strerror(errno)); - } - - break; - default: - while((err = ERR_get_error())) { - ERROR("SSL_shutdown failed (%i, %i): %s", ssl_r, ret, ERR_error_string(err, NULL)); - } - } - } - - SSL_free(con->sock->ssl); - ERR_clear_error(); - con->sock->ssl = NULL; - } -#endif - - fdevent_event_del(srv->ev, con->sock); - fdevent_unregister(srv->ev, con->sock); - - if (closesocket(con->sock->fd)) { - ERROR("close failed (%i): %s", con->sock->fd, strerror(errno)); - } - - connection_del(srv, con); - connection_set_state(srv, con, CON_STATE_CONNECT); - - return 0; -} - -#if 0 -static void dump_packet(const unsigned char *data, size_t len) { - size_t i, j; - - 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"); - } - } - - if (len % 16 != 0) { - for (j = i % 16; j < 16; j++) { - fprintf(stderr, " "); - } - - fprintf(stderr, " "); - for (j = i & ~0xf; j < len; j++) { - unsigned char c; - - c = data[j]; - fprintf(stderr, "%c", c > 32 && c < 128 ? c : '.'); - } - fprintf(stderr, "\n"); - } -} -#endif - -static int connection_handle_response_header(server *srv, connection *con) { - int no_response_body = 0; - - if (con->mode == DIRECT) { - /* static files */ - switch(con->request.http_method) { - case HTTP_METHOD_GET: - case HTTP_METHOD_POST: - case HTTP_METHOD_HEAD: - /* webdav */ - case HTTP_METHOD_PUT: - case HTTP_METHOD_MKCOL: - case HTTP_METHOD_DELETE: - case HTTP_METHOD_COPY: - case HTTP_METHOD_MOVE: - case HTTP_METHOD_PROPFIND: - case HTTP_METHOD_PROPPATCH: - case HTTP_METHOD_LOCK: - case HTTP_METHOD_UNLOCK: - break; - case HTTP_METHOD_OPTIONS: - /* - * 400 is coming from the request-parser BEFORE uri.path is set - * 403 is from the response handler when noone else catched it - * - * */ - if ((!con->http_status || con->http_status == 200 || con->http_status == 403) && - con->uri.path->used && con->uri.path->ptr[0] != '*') { - response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); - - /* trash the content */ - no_response_body = 1; - - con->http_status = 200; - } - break; - default: - switch(con->http_status) { - case 400: /* bad request */ - case 414: /* overload request header */ - case 505: /* unknown protocol */ - case 207: /* this was webdav */ - break; - default: - con->http_status = 501; - break; - } - break; - } - } - - if (con->http_status == 0) { - TRACE("%s", "no status, setting 403"); - con->http_status = 403; - } - - switch(con->http_status) { - case 400: /* class: header + custom body */ - case 401: - case 403: - case 404: - case 408: - case 409: - case 410: - case 411: - case 416: - case 423: - case 500: - case 501: - case 502: - case 503: - case 504: - case 505: - case 509: - if (con->mode != DIRECT) break; - - con->send->is_closed = 0; - con->response.content_length = -1; - - buffer_reset(con->physical.path); - - /* try to send static errorfile */ - if (!buffer_is_empty(con->conf.errorfile_prefix)) { - stat_cache_entry *sce = NULL; - - buffer_copy_string_buffer(con->physical.path, con->conf.errorfile_prefix); - buffer_append_string(con->physical.path, get_http_status_body_name(con->http_status)); - - if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { - chunkqueue_append_file(con->send, con->physical.path, 0, sce->st.st_size); - con->send->bytes_in += sce->st.st_size; - con->send->is_closed = 1; - if (buffer_is_empty(sce->content_type)) { - /* for error docs default to html instead of application-data */ - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); - } else { - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); - } - } - } - - if (!con->send->is_closed) { - buffer *b; - - buffer_reset(con->physical.path); - - con->send->is_closed = 1; - b = chunkqueue_get_append_buffer(con->send); - - /* build default error-page */ - buffer_copy_string_len(b, CONST_STR_LEN( - "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" - "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" - " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" - "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" - " <head>\n" - " <title>")); - buffer_append_long(b, con->http_status); - buffer_append_string_len(b, CONST_STR_LEN(" - ")); - buffer_append_string(b, get_http_status_name(con->http_status)); - - buffer_append_string(b, - "</title>\n" - " </head>\n" - " <body>\n" - " <h1>"); - buffer_append_long(b, con->http_status); - buffer_append_string_len(b, CONST_STR_LEN(" - ")); - buffer_append_string(b, get_http_status_name(con->http_status)); - - buffer_append_string(b,"</h1>\n" - " </body>\n" - "</html>\n" - ); - - con->send->bytes_in += b->used - 1; - - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); - } - /* fall through */ - case 207: - case 200: /* class: header + body */ - case 201: - case 301: - case 302: - case 303: - break; - - case 206: /* write_queue is already prepared */ - break; - case 205: /* class: header only */ - case 304: - default: - if (con->mode == DIRECT) { - /* only if we have handled the request internally - * we will see no response-content - * - * if it was a fastcgi request we will see a END_REQUEST packet - * after the header was parsed. - * - * */ - no_response_body = 1; - } - break; - } - - if (no_response_body) { - /* disable chunked encoding again as we have no body */ - con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; - chunkqueue_reset(con->send); - - con->send->is_closed = 1; - } - - return 0; -} - -connection *connection_init(server *srv) { - connection *con; - - UNUSED(srv); - - con = calloc(1, sizeof(*con)); - - con->sock = iosocket_init(); - con->ndx = -1; - con->bytes_written = 0; - con->bytes_read = 0; - con->bytes_header = 0; - con->loops_per_request = 0; - -#define CLEAN(x) \ - con->x = buffer_init(); - - CLEAN(request.uri); - CLEAN(request.request); - CLEAN(request.pathinfo); - CLEAN(request.http_host); - - CLEAN(request.orig_uri); - - CLEAN(uri.scheme); - CLEAN(uri.authority); - CLEAN(uri.path); - CLEAN(uri.path_raw); - CLEAN(uri.query); - - CLEAN(physical.doc_root); - CLEAN(physical.path); - CLEAN(physical.basedir); - CLEAN(physical.rel_path); - CLEAN(physical.etag); - CLEAN(parse_request); - - CLEAN(authed_user); - CLEAN(server_name); - CLEAN(error_handler); - CLEAN(dst_addr_buf); - -#undef CLEAN - con->send_filters = filter_chain_init(); - /* send is the chunkqueue of the first send filter */ - con->send = con->send_filters->first->cq; - con->recv = chunkqueue_init(); - chunkqueue_set_tempdirs(con->recv, srv->srvconf.upload_tempdirs); - - con->send_raw = chunkqueue_init(); - con->recv_raw = chunkqueue_init(); - - con->request.headers = array_init(); - con->response.headers = array_init(); - con->environment = array_init(); - - con->http_req = http_request_init(); - - /* init plugin specific connection structures */ - - con->plugin_ctx = calloc(1, (srv->plugins.used + 1) * sizeof(void *)); - - con->cond_cache = calloc(srv->config_context->used, sizeof(cond_cache_t)); - config_setup_connection(srv, con); - - return con; -} - -void connections_free(server *srv) { - connections *conns = srv->conns; - size_t i; - - for (i = 0; i < conns->size; i++) { - connection *con = conns->ptr[i]; - - connection_reset(srv, con); - iosocket_free(con->sock); - - filter_chain_free(con->send_filters); - con->send = NULL; - chunkqueue_free(con->recv); - chunkqueue_free(con->send_raw); - chunkqueue_free(con->recv_raw); - array_free(con->request.headers); - array_free(con->response.headers); - array_free(con->environment); - -#define CLEAN(x) \ - buffer_free(con->x); - - CLEAN(request.uri); - CLEAN(request.request); - CLEAN(request.pathinfo); - CLEAN(request.http_host); - - CLEAN(request.orig_uri); - - CLEAN(uri.scheme); - CLEAN(uri.authority); - CLEAN(uri.path); - CLEAN(uri.path_raw); - CLEAN(uri.query); - - CLEAN(physical.doc_root); - CLEAN(physical.path); - CLEAN(physical.basedir); - CLEAN(physical.etag); - CLEAN(physical.rel_path); - CLEAN(parse_request); - - CLEAN(authed_user); - CLEAN(server_name); - CLEAN(error_handler); - CLEAN(dst_addr_buf); -#undef CLEAN - free(con->plugin_ctx); - free(con->cond_cache); - - http_request_free(con->http_req); - - free(con); - } - - free(conns->ptr); -} - - -int connection_reset(server *srv, connection *con) { - size_t i; - - plugins_call_connection_reset(srv, con); - - con->is_readable = 1; - con->is_writable = 1; - con->http_status = 0; - con->file_started = 0; - con->got_response = 0; - - con->bytes_written = 0; - con->bytes_written_cur_second = 0; - con->bytes_read = 0; - con->bytes_header = 0; - con->loops_per_request = 0; - - con->request.http_method = HTTP_METHOD_UNSET; - con->request.http_version = HTTP_VERSION_UNSET; - con->request.content_length = -1; - - con->response.keep_alive = 0; - con->response.content_length = -1; - con->response.transfer_encoding = 0; - - con->mode = DIRECT; - -#define CLEAN(x) \ - if (con->x) buffer_reset(con->x); - - CLEAN(request.uri); - CLEAN(request.pathinfo); - CLEAN(request.request); - CLEAN(request.http_host); - - CLEAN(request.orig_uri); - - CLEAN(uri.scheme); - CLEAN(uri.authority); - CLEAN(uri.path); - CLEAN(uri.path_raw); - CLEAN(uri.query); - - CLEAN(physical.doc_root); - CLEAN(physical.path); - CLEAN(physical.basedir); - CLEAN(physical.rel_path); - CLEAN(physical.etag); - - CLEAN(parse_request); - - CLEAN(authed_user); - CLEAN(server_name); - CLEAN(error_handler); -#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT - CLEAN(sock->tlsext_server_name); -#endif -#undef CLEAN - -#define CLEAN(x) \ - if (con->x) con->x->used = 0; - -#undef CLEAN - - array_reset(con->request.headers); - array_reset(con->response.headers); - array_reset(con->environment); - - filter_chain_reset(con->send_filters); - con->send = con->send_filters->first->cq; - chunkqueue_reset(con->recv); - chunkqueue_reset(con->send_raw); - - http_request_reset(con->http_req); - - /* the plugins should cleanup themself */ - for (i = 0; i < srv->plugins.used; i++) { - plugin *p = ((plugin **)(srv->plugins.ptr))[i]; - plugin_data *pd = p->data; - - if (!pd) continue; - - if (con->plugin_ctx[pd->id] != NULL) { - ERROR("missing cleanup in %s", SAFE_BUF_STR(p->name)); - } - - con->plugin_ctx[pd->id] = NULL; - } - - config_cond_cache_reset(srv, con); - - con->header_len = 0; - con->in_error_handler = 0; - - config_setup_connection(srv, con); - - return 0; -} - -/** - * handle all header and content read - * - * we get called by the state-engine and by the fdevent-handler - */ -static handler_t connection_handle_read_request_header(server *srv, connection *con) { - /* let's see if we need more data later */ - fdevent_event_del(srv->ev, con->sock); - - con->read_idle_ts = srv->cur_ts; /* start a read-call() */ - - /* read from the network */ - switch (network_read(srv, con, con->sock, con->recv_raw)) { - case NETWORK_STATUS_SUCCESS: - /* we read everything from the socket, do we have a full header ? */ - break; - case NETWORK_STATUS_WAIT_FOR_EVENT: - fdevent_event_add(srv->ev, con->sock, FDEVENT_IN); - return HANDLER_WAIT_FOR_EVENT; - case NETWORK_STATUS_CONNECTION_CLOSE: - /* the connection went away before we got something back */ - con->close_timeout_ts = srv->cur_ts - 2; - connection_set_state(srv, con, CON_STATE_CLOSE); - - return HANDLER_GO_ON; - default: - ERROR("++ %s", "oops, something went wrong while reading"); - return HANDLER_ERROR; - } - - switch (http_request_parse_cq(con->recv_raw, con->http_req)) { - case PARSE_ERROR: - con->http_status = 400; /* the header is broken */ - con->send->is_closed = 1; /* we have nothing to send */ - - chunkqueue_remove_finished_chunks(con->recv_raw); - - return HANDLER_FINISHED; - case PARSE_NEED_MORE: - /* we need more */ - fdevent_event_add(srv->ev, con->sock, FDEVENT_IN); - - return HANDLER_WAIT_FOR_EVENT; - case PARSE_SUCCESS: - chunkqueue_remove_finished_chunks(con->recv_raw); - break; - default: - chunkqueue_remove_finished_chunks(con->recv_raw); - TRACE("%s", "(error)"); - return HANDLER_ERROR; - } - - return HANDLER_GO_ON; -} - -/* decode the HTTP/1.1 chunk encoding */ - -static handler_t connection_handle_read_request_content(server *srv, connection *con) { - /* read data from the socket and push it to the backend */ - - chunkqueue *in = con->recv_raw; - chunkqueue *out = con->recv; /* the pure content */ - chunk *c; - - /* let's see if we need more data later */ - fdevent_event_del(srv->ev, con->sock); - - con->read_idle_ts = srv->cur_ts; /* start a read-call() */ - - if (con->request.content_length == -1) return HANDLER_GO_ON; - - /* if the content was short enough, it might be read already */ - if (in->first && - chunkqueue_length(in) - in->first->offset > 0) { - - /* - * looks like the request-header also had some content for us - */ - - } else { - /* read from the network */ - switch (network_read(srv, con, con->sock, in)) { - case NETWORK_STATUS_SUCCESS: - /* we have data */ - break; - case NETWORK_STATUS_WAIT_FOR_EVENT: - fdevent_event_add(srv->ev, con->sock, FDEVENT_IN); - return HANDLER_WAIT_FOR_EVENT; - case NETWORK_STATUS_CONNECTION_CLOSE: - /* the connection went away before we got something back */ - con->close_timeout_ts = srv->cur_ts - 2; - connection_set_state(srv, con, CON_STATE_CLOSE); - - return HANDLER_GO_ON; - default: - ERROR("++ %s", "oops, something went wrong while reading"); - return HANDLER_ERROR; - } - } - - /* how much data do we want to extract ? */ - for (c = in->first; c && (out->bytes_in != con->request.content_length); c = c->next) { - off_t weWant, weHave, toRead; - - weWant = con->request.content_length - out->bytes_in; - - if (c->mem->used == 0) continue; - - 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 (out->last && - out->last->type == FILE_CHUNK && - out->last->file.is_temp && - out->last->offset == 0) { - /* ok, take the last chunk for our job */ - - if (out->last->file.length < 1 * 1024 * 1024) { - dst_c = out->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); - } - } else { - /* the chunk is too large now, close it */ - dst_c = out->last; - - if (dst_c->file.fd != -1) { - close(dst_c->file.fd); - dst_c->file.fd = -1; - } - dst_c = chunkqueue_get_append_tempfile(out); - } - } else { - dst_c = chunkqueue_get_append_tempfile(out); - } - - /* 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 - * */ - - ERROR("denying upload as opening to temp-file for upload failed: '%s': %s", - SAFE_BUF_STR(dst_c->file.name), strerror(errno)); - - con->http_status = 413; /* Request-Entity too large */ - con->keep_alive = 0; - return HANDLER_FINISHED; - } - - if (toRead != write(dst_c->file.fd, c->mem->ptr + c->offset, toRead)) { - /* write failed for some reason ... disk full ? */ - ERROR("denying upload as writing to file failed: '%s': %s", - SAFE_BUF_STR(dst_c->file.name), strerror(errno)); - - con->http_status = 413; /* Request-Entity too large */ - con->keep_alive = 0; - - close(dst_c->file.fd); - dst_c->file.fd = -1; - - return HANDLER_FINISHED; - } - - dst_c->file.length += toRead; - - if (out->bytes_in + toRead == con->request.content_length) { - /* we read everything, close the chunk */ - close(dst_c->file.fd); - dst_c->file.fd = -1; - } - } else { - buffer *b; - - b = (out->last) ? out->last->mem : NULL; - - if (NULL == b) { - b = chunkqueue_get_append_buffer(out); - buffer_prepare_copy(b, con->request.content_length - out->bytes_in + 1); - } - - buffer_append_string_len(b, c->mem->ptr + c->offset, toRead); - } - - c->offset += toRead; - - out->bytes_in += toRead; - in->bytes_out += toRead; - } - - if (out->bytes_in < con->request.content_length) { - /* we have to read more content */ - fdevent_event_add(srv->ev, con->sock, FDEVENT_IN); - } - - return HANDLER_GO_ON; -} - -static handler_t connection_handle_fdevent(void *s, void *context, int revents) { - server *srv = (server *)s; - connection *con = context; - - if (revents & FDEVENT_IN) { - switch (con->state) { - case CON_STATE_READ_REQUEST_HEADER: - case CON_STATE_READ_REQUEST_CONTENT: - joblist_append(srv, con); - break; - case CON_STATE_CLOSE: /* ignore the even, we will clean-up soon */ - break; - case CON_STATE_ERROR: - ERROR("we are in (CON_STATE_ERROR), but still get a FDEVENT_IN, removing event from fd = %d, %04x for (%s)", - con->sock->fd, - revents, - SAFE_BUF_STR(con->uri.path)); - - fdevent_event_del(srv->ev, con->sock); - - joblist_append(srv, con); - break; - default: - ERROR("I thought only READ_REQUEST_* need fdevent-in: %d, fd = %d, %04x for (%s)", - con->state, - con->sock->fd, - revents, - SAFE_BUF_STR(con->uri.path)); - break; - } - } - - if (revents & FDEVENT_OUT) { - switch (con->state) { - case CON_STATE_WRITE_RESPONSE_HEADER: - case CON_STATE_WRITE_RESPONSE_CONTENT: - joblist_append(srv, con); - break; - case CON_STATE_ERROR: - ERROR("we are in (CON_STATE_ERROR), but still get a FDEVENT_OUT, removing event from fd = %d, %04x for (%s)", - con->sock->fd, - revents, - SAFE_BUF_STR(con->uri.path)); - - fdevent_event_del(srv->ev, con->sock); - - joblist_append(srv, con); - break; - default: - TRACE("got FDEVENT_OUT for state %d, calling the job-handler, let's see what happens", con->state); - joblist_append(srv, con); - break; - } - } - - if (revents & ~(FDEVENT_IN | FDEVENT_OUT)) { - /* looks like an error */ - - connection_set_state(srv, con, CON_STATE_ERROR); - joblist_append(srv, con); - } - - - return HANDLER_FINISHED; -} - - -connection *connection_accept(server *srv, server_socket *srv_socket) { - /* accept everything */ - - /* search an empty place */ - int cnt; - sock_addr cnt_addr; - socklen_t cnt_len; - /* accept it and register the fd */ - - cnt_len = sizeof(cnt_addr); - - if (-1 == (cnt = accept(srv_socket->sock->fd, (struct sockaddr *) &cnt_addr, &cnt_len))) { -#ifdef _WIN32 - errno = WSAGetLastError(); -#endif - switch (errno) { - case EAGAIN: -#if EWOULDBLOCK != EAGAIN - case EWOULDBLOCK: -#endif - case EINTR: - /* we were stopped _before_ we had a connection */ - case ECONNABORTED: /* this is a FreeBSD thingy */ - /* we were stopped _after_ we had a connection */ - break; - - case EMFILE: /* we are out of FDs */ - server_out_of_fds(srv, NULL); - break; - default: - ERROR("accept failed on fd=%d with error: (%d) %s", srv_socket->sock->fd, errno, strerror(errno)); - break; - } - return NULL; - } else { - connection *con; - - /* ok, we have the connection, register it */ -#if 0 - TRACE("appected() = %i", cnt); -#endif - srv->con_opened++; - - con = connections_get_new_connection(srv); - con->sock->fd = cnt; - con->sock->fde_ndx = -1; -#if 0 - gettimeofday(&(con->start_tv), NULL); -#endif - fdevent_register(srv->ev, con->sock, connection_handle_fdevent, con); - - connection_set_state(srv, con, CON_STATE_REQUEST_START); - - con->connection_start = srv->cur_ts; - con->dst_addr = cnt_addr; - buffer_copy_string(con->dst_addr_buf, inet_ntop_cache_get_ip(srv, &(con->dst_addr))); - con->srv_socket = srv_socket; - - if (-1 == (fdevent_fcntl_set(srv->ev, con->sock))) { - ERROR("fcntl failed: %s", strerror(errno)); - connection_close(srv, con); - return NULL; - } - -#ifdef USE_OPENSSL - /* connect FD to SSL */ - if (srv_socket->is_ssl) { - if (NULL == (con->sock->ssl = SSL_new(srv_socket->ssl_ctx))) { - ERROR("SSL: %s", - ERR_error_string(ERR_get_error(), NULL)); - connection_close(srv, con); - return NULL; - } - -#ifndef OPENSSL_NO_TLSEXT - SSL_set_app_data(con->sock->ssl, con); -#endif - SSL_set_accept_state(con->sock->ssl); - con->conf.is_ssl=1; - - if (1 != (SSL_set_fd(con->sock->ssl, cnt))) { - ERROR("SSL: %s", - ERR_error_string(ERR_get_error(), NULL)); - connection_close(srv, con); - return NULL; - } - } -#endif - return con; - } -} - -void connection_state_machine(server *srv, connection *con) { - int done = 0, r; - off_t bytes_moved = 0; -#ifdef USE_OPENSSL - server_socket *srv_sock = con->srv_socket; -#endif - - if (srv->srvconf.log_state_handling) { - TRACE("state at start for fd %i: %s", con->sock->fd, connection_get_state(con->state)); - } - - while (done == 0) { - size_t ostate = con->state; - int b; - - switch (con->state) { - case CON_STATE_CONNECT: - if (srv->srvconf.log_state_handling) { - TRACE("state for fd %i: %s", con->sock->fd, connection_get_state(con->state)); - } - - chunkqueue_reset(con->recv_raw); /* this is a new connection, trash the pipeline */ - - con->request_count = 0; - - break; - case CON_STATE_REQUEST_START: - /* init the request handling */ - if (srv->srvconf.log_state_handling) { - TRACE("state for fd %i: %s", con->sock->fd, connection_get_state(con->state)); - } - - con->request_start = srv->cur_ts; /* start of the request */ - con->read_idle_ts = srv->cur_ts; /* start a read-call() */ - - con->request_count++; /* max-keepalive requests */ - con->loops_per_request = 0; /* infinite loops */ - - /* if the content was short enough, it might have a header already in the pipe */ - if (con->recv_raw->first && - chunkqueue_length(con->recv_raw) - con->recv_raw->first->offset > 0) { - /* pipelining */ - - switch (http_request_parse_cq(con->recv_raw, con->http_req)) { - case PARSE_ERROR: - con->http_status = 400; /* the header is broken */ - con->keep_alive = 0; - - chunkqueue_remove_finished_chunks(con->recv_raw); - - connection_set_state(srv, con, CON_STATE_HANDLE_RESPONSE_HEADER); - break; - case PARSE_NEED_MORE: - /* the read() call is on the way */ - connection_set_state(srv, con, CON_STATE_READ_REQUEST_HEADER); - break; - case PARSE_SUCCESS: - /* pipelining worked, validate the header */ - chunkqueue_remove_finished_chunks(con->recv_raw); - - connection_set_state(srv, con, CON_STATE_VALIDATE_REQUEST_HEADER); - break; - default: - chunkqueue_remove_finished_chunks(con->recv_raw); - TRACE("%s", "(error)"); - connection_set_state(srv, con, CON_STATE_ERROR); - break; - } - } else { - connection_set_state(srv, con, CON_STATE_READ_REQUEST_HEADER); - } - - break; - case CON_STATE_READ_REQUEST_HEADER: - /* read us much data as needed into the recv_raw-cq for a valid header */ - if (srv->srvconf.log_state_handling) { - TRACE("state for fd %i: %s", con->sock->fd, connection_get_state(con->state)); - } - - switch (connection_handle_read_request_header(srv, con)) { - case HANDLER_GO_ON: /** we have a full header, or connection close */ - if (con->state == CON_STATE_READ_REQUEST_HEADER) { - connection_set_state(srv, con, CON_STATE_VALIDATE_REQUEST_HEADER); - } - break; - case HANDLER_FINISHED: /** parsing failed, ->http_status is set */ - connection_set_state(srv, con, CON_STATE_WRITE_RESPONSE_HEADER); - break; - case HANDLER_WAIT_FOR_EVENT: - return; - case HANDLER_ERROR: - TRACE("%s", "(error)"); - connection_set_state(srv, con, CON_STATE_ERROR); - break; - default: - TRACE("%s", "(error)"); - connection_set_state(srv, con, CON_STATE_ERROR); - break; - } - break; - case CON_STATE_VALIDATE_REQUEST_HEADER: - /* we have the full header, parse it */ - - http_request_parse(srv, con, con->http_req); - - if (con->http_status != 0) { - con->keep_alive = 0; - con->send->is_closed = 1; /* there is no content */ - - connection_set_state(srv, con, CON_STATE_HANDLE_RESPONSE_HEADER); - } else if (array_get_element(con->request.headers, CONST_STR_LEN("Expect"))) { - /* write */ - con->http_status = 100; - con->send->is_closed = 1; - - connection_set_state(srv, con, CON_STATE_WRITE_RESPONSE_HEADER); - } else { - /* parsing the request went fine - * let's find a handler for this request */ - connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST_HEADER); - } - - break; - case CON_STATE_HANDLE_REQUEST_HEADER: - /* the request header is parsed, - * find someone who wants to handle this request */ - - if (srv->srvconf.log_state_handling) { - TRACE("state for fd %i: %s", con->sock->fd, connection_get_state(con->state)); - } - - switch (r = handle_get_backend(srv, con)) { - case HANDLER_GO_ON: - case HANDLER_FINISHED: - break; - case HANDLER_WAIT_FOR_FD: - connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST_HEADER); - - server_out_of_fds(srv, con); - - break; - case HANDLER_COMEBACK: - done = -1; - case HANDLER_WAIT_FOR_EVENT: - /* come back here */ - connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST_HEADER); - - break; - case HANDLER_ERROR: - /* something went wrong */ - TRACE("%s", "(error)"); - connection_set_state(srv, con, CON_STATE_ERROR); - break; - default: - TRACE("handle_get_backend returned %d on fd %d", r, con->sock->fd); - break; - } - - if (r != HANDLER_FINISHED && r != HANDLER_GO_ON) break; - - if (con->http_status == 404 || - con->http_status == 403) { - /* 404 error-handler */ - - if (con->in_error_handler == 0 && - (!buffer_is_empty(con->conf.error_handler) || - !buffer_is_empty(con->error_handler))) { - /* call error-handler */ - - con->error_handler_saved_status = con->http_status; - con->http_status = 0; - - if (buffer_is_empty(con->error_handler)) { - buffer_copy_string_buffer(con->request.uri, con->conf.error_handler); - } else { - buffer_copy_string_buffer(con->request.uri, con->error_handler); - } - buffer_reset(con->physical.path); - - con->in_error_handler = 1; - - connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST_HEADER); - - /* need to reset condition cache since request uri changed. */ - config_cond_cache_reset(srv, con); - - done = -1; - break; - } else if (con->in_error_handler) { - /* error-handler is a 404 */ - - /* continue as normal, status is the same */ - ERROR("Warning: Either the error-handler returned status 404 or the error-handler itself was not found: %s", SAFE_BUF_STR(con->request.uri)); - ERROR("returning the original status: %i", con->error_handler_saved_status); - ERROR("%s", "If this is a rails app: check your production.log"); - 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 */ - con->http_status = con->error_handler_saved_status; - } - - if (con->http_status == 0) con->http_status = 200; - - /* the backend is prepared, forward the content */ - connection_set_state(srv, con, CON_STATE_READ_REQUEST_CONTENT); - - break; - case CON_STATE_READ_REQUEST_CONTENT: - /* the request-handle is setup, read all the data and push it into the request-handler */ - - if (con->request.content_length == -1 || - con->request.content_length == 0) { - con->recv->is_closed = 1; - } - - if (!con->recv->is_closed && - con->recv->bytes_in < con->request.content_length) { - switch (connection_handle_read_request_content(srv, con)) { - case HANDLER_GO_ON: - break; - case HANDLER_ERROR: - TRACE("%s", "(error)"); - connection_set_state(srv, con, CON_STATE_ERROR); - break; - case HANDLER_WAIT_FOR_EVENT: - break; - default: - ERROR("%s", "oops, unknown return value: ..."); - } - - if (con->recv->bytes_in == con->request.content_length) { - /* we read everything */ - fdevent_event_del(srv->ev, con->sock); - con->recv->is_closed = 1; - } - chunkqueue_remove_finished_chunks(con->recv_raw); - } - - chunkqueue_remove_finished_chunks(con->recv); - - /* - * this should call the backend - * they might build the connection now or stream the content to the upstream server - * */ - - switch(r = plugins_call_handle_send_request_content(srv, con)) { - case HANDLER_GO_ON: - /* everything was forwarded */ - break; - case HANDLER_FINISHED: - /* oops, we don't want this here */ - break; - case HANDLER_COMEBACK: - break; - case HANDLER_WAIT_FOR_EVENT: - return; - default: - /* something strange happened */ - TRACE("(error) plugins_call_handle_send_request_content(): r = %d", r); - connection_set_state(srv, con, CON_STATE_ERROR); - break; - } - - chunkqueue_remove_finished_chunks(con->recv); - - /* jump back, this should be proceeded by mod_staticfile */ - if (r == HANDLER_COMEBACK && con->mode == DIRECT) { - connection_set_state(srv,con,CON_STATE_HANDLE_REQUEST_HEADER); - break; - } - - if (con->state == CON_STATE_ERROR) break; - - if (con->recv->is_closed && - con->recv->bytes_in == con->recv->bytes_out) { - /* everything we read is sent */ - connection_set_state(srv, con, CON_STATE_HANDLE_RESPONSE_HEADER); - } - - break; - case CON_STATE_HANDLE_RESPONSE_HEADER: - /* handle the HTTP response headers, or generate error-page */ - connection_handle_response_header(srv, con); - - /* we got a response header from the backend - * call all plugins who want to modify the response header - * - mod_compress/deflate - * - HTTP/1.1 chunking - * - */ - switch (plugins_call_handle_response_header(srv, con)) { - case HANDLER_GO_ON: - case HANDLER_FINISHED: - break; - case HANDLER_WAIT_FOR_EVENT: - /* need to wait for more data */ - return; - default: - /* something strange happened */ - if (con->conf.log_request_handling) { - TRACE("%s", "handle response header failed"); - } - connection_set_state(srv, con, CON_STATE_ERROR); - break; - } - - if (con->state == CON_STATE_ERROR) break; - - /* all the response-headers are set, check if we have a */ - - connection_set_state(srv, con, CON_STATE_WRITE_RESPONSE_HEADER); - - break; - case CON_STATE_WRITE_RESPONSE_HEADER: - /* write response headers */ - http_response_write_header(srv, con, con->send_raw); - - connection_set_state(srv, con, CON_STATE_WRITE_RESPONSE_CONTENT); - - if (con->request.http_method == HTTP_METHOD_HEAD) { - /* remove the content now */ - chunkqueue_reset(con->send); - - con->send->is_closed = 1; - } - - break; - case CON_STATE_WRITE_RESPONSE_CONTENT: - fdevent_event_del(srv->ev, con->sock); - - con->write_request_ts = srv->cur_ts; - - /* looks like we shall read some content from the backend */ - switch (plugins_call_handle_read_response_content(srv, con)) { - case HANDLER_WAIT_FOR_EVENT: - if (!con->send->is_closed && con->send->bytes_in == con->send->bytes_out) { - /* need to wait for more data */ - return; - } - break; - case HANDLER_GO_ON: - case HANDLER_FINISHED: - break; - default: - /* something strange happened */ - if (con->conf.log_request_handling) { - TRACE("%s", "read response content failed"); - } - connection_set_state(srv, con, CON_STATE_ERROR); - break; - } - - if (con->state == CON_STATE_ERROR) break; - - /* we might have new content in the con->send buffer - * encode it for the network - * - chunking - * - compression - */ - switch (plugins_call_handle_filter_response_content(srv, con)) { - case HANDLER_GO_ON: - case HANDLER_FINISHED: - break; - case HANDLER_WAIT_FOR_EVENT: - /* need to wait for more data */ - return; - default: - /* something strange happened */ - if (con->conf.log_request_handling) { - TRACE("%s", "filter response content failed"); - } - connection_set_state(srv, con, CON_STATE_ERROR); - break; - } - - if (con->state == CON_STATE_ERROR) break; - - /* copy output from filters into send_raw. */ - bytes_moved = filter_chain_copy_output(con->send_filters, con->send_raw); - - /** - * check that all previous filters have cleaned there chunkqueue - */ - if (con->send_raw->is_closed) { - filter *f; - - for (f = con->send_filters->first; - f; - f = f->next) { - off_t cq_len; - - cq_len = chunkqueue_length(f->cq); - - if (cq_len > 0) { - TRACE("filter[%d] is not empty: %jd (report me)", f->id, (intmax_t) cq_len); - } - } - } - - - /* limit download speed. */ - if (con->traffic_limit_reached) { - return; - } - - /* no response data available to send right now. wait for more. */ - if (!con->send_raw->is_closed && - con->send_raw->bytes_in == con->send_raw->bytes_out) { - return; - } - - switch(network_write_chunkqueue(srv, con, con->send_raw)) { - case NETWORK_STATUS_SUCCESS: - /* we send everything from the chunkqueue and the chunkqueue-sender signaled it is finished */ - if (con->send_raw->is_closed) { - if (con->http_status == 100) { - /* send out the 100 Continue header and handle the request as normal afterwards */ - con->http_status = 0; - - /* cleanup send chunkqueue's. */ - chunkqueue_reset(con->send); - chunkqueue_reset(con->send_raw); - - connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST_HEADER); - } else { - connection_set_state(srv, con, CON_STATE_RESPONSE_END); - } - } else { - /* still have data to send in send_raw queue */ - fdevent_event_add(srv->ev, con->sock, FDEVENT_OUT); - return; - } - break; - case NETWORK_STATUS_FATAL_ERROR: /* error on our side */ - TRACE("%s", "(network-subsys sent us a fatal-error)"); - connection_set_state(srv, con, CON_STATE_ERROR); - break; - case NETWORK_STATUS_CONNECTION_CLOSE: /* remote close */ - connection_set_state(srv, con, CON_STATE_ERROR); - break; - case NETWORK_STATUS_WAIT_FOR_AIO_EVENT: - return; - case NETWORK_STATUS_WAIT_FOR_EVENT: - fdevent_event_add(srv->ev, con->sock, FDEVENT_OUT); - - return; - case NETWORK_STATUS_WAIT_FOR_FD: - /* the backend received a EMFILE - * - e.g. for a mmap() of /dev/zero */ - - server_out_of_fds(srv, con); - - return; - case NETWORK_STATUS_INTERRUPTED: - case NETWORK_STATUS_UNSET: - break; - } - - - break; - case CON_STATE_RESPONSE_END: /* transient */ - /* log the request */ - - if (srv->srvconf.log_state_handling) { - TRACE("state for fd %i: %s", con->sock->fd, connection_get_state(con->state)); - } - - plugins_call_handle_response_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: - ERROR("unhandled return value from plugins_call_handle_connection_close: %i", r); - break; - } - - connection_close(srv, con); - - srv->con_closed++; - } - - connection_reset(srv, con); - - break; - case CON_STATE_CLOSE: - if (srv->srvconf.log_state_handling) { - TRACE("state for fd %i: %s", con->sock->fd, connection_get_state(con->state)); - } - - if (con->keep_alive) { - if (ioctl(con->sock->fd, FIONREAD, &b)) { - ERROR("ioctl() failed: %s", strerror(errno)); - } - if (b > 0) { - char buf[1024]; - ERROR("CLOSE-read() for fd %i, %i bytes", con->sock->fd, b); - - /* */ - sockread(con->sock->fd, buf, sizeof(buf)); - } else { - /* nothing to read */ - - con->close_timeout_ts = srv->cur_ts - 2; - } - } else { - con->close_timeout_ts = srv->cur_ts - 2; - } - - if (srv->cur_ts - con->close_timeout_ts > 1) { - connection_close(srv, con); - - if (srv->srvconf.log_state_handling) { - TRACE("connection closed for fd %i", con->sock->fd); - } - } - - 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_response_done(srv, con); - } -#ifdef USE_OPENSSL - if (srv_sock->is_ssl) { - int ret; - switch ((ret = SSL_shutdown(con->sock->ssl))) { - case 1: - /* ok */ - break; - case 0: - SSL_shutdown(con->sock->ssl); - break; - default: - ERROR("SSL (%i): %s", - SSL_get_error(con->sock->ssl, ret), - ERR_error_string(ERR_get_error(), NULL)); - break; - } - } -#endif - - switch(con->mode) { - case DIRECT: -#if 0 - TRACE("emergency exit for fd %i: direct", con->sock->fd); -#endif - break; - default: - switch(r = plugins_call_handle_connection_close(srv, con)) { - case HANDLER_GO_ON: - case HANDLER_FINISHED: - break; - default: - ERROR("unhandled return value from plugins_call_handle_connection_close: %i", r); - break; - } - break; - } - - connection_reset(srv, con); - - /* close the connection */ - if ((con->keep_alive == 1) && - (0 == shutdown(con->sock->fd, SHUT_WR))) { - con->close_timeout_ts = srv->cur_ts; - connection_set_state(srv, con, CON_STATE_CLOSE); - - if (srv->srvconf.log_state_handling) { - TRACE("shutdown for fd %i", con->sock->fd); - } - } else { - connection_close(srv, con); - } - - con->keep_alive = 0; - - srv->con_closed++; - - break; - default: - ERROR("unknown state for fd %i: %i", con->sock->fd, con->state); - connection_set_state(srv, con, CON_STATE_ERROR); - break; - } - - if (done == -1) { - done = 0; - } else if (ostate == con->state) { - done = 1; - } - } - - if (srv->srvconf.log_state_handling) { - TRACE("state at exit for fd %i: %s", con->sock->fd, connection_get_state(con->state)); - } - - return; -} |