--- embedaddon/lighttpd/src/mod_proxy.c 2014/06/15 20:20:06 1.1.1.2 +++ embedaddon/lighttpd/src/mod_proxy.c 2016/11/02 10:35:00 1.1.1.3 @@ -1,3 +1,5 @@ +#include "first.h" + #include "buffer.h" #include "server.h" #include "keyvalue.h" @@ -26,10 +28,6 @@ #include -#ifdef HAVE_SYS_FILIO_H -# include -#endif - #include "sys-socket.h" #define data_proxy data_fastcgi @@ -84,8 +82,7 @@ typedef enum { PROXY_STATE_CONNECT, PROXY_STATE_PREPARE_WRITE, PROXY_STATE_WRITE, - PROXY_STATE_READ, - PROXY_STATE_ERROR + PROXY_STATE_READ } proxy_connection_state_t; enum { PROXY_STDOUT, PROXY_END_REQUEST }; @@ -100,6 +97,7 @@ typedef struct { buffer *response_header; chunkqueue *wb; + off_t wb_reqlen; int fd; /* fd to the proxy process */ int fde_ndx; /* index into the fd-event buffer */ @@ -127,6 +125,7 @@ static handler_ctx * handler_ctx_init(void) { hctx->response_header = buffer_init(); hctx->wb = chunkqueue_init(); + hctx->wb_reqlen = 0; hctx->fd = -1; hctx->fde_ndx = -1; @@ -167,12 +166,11 @@ FREE_FUNC(mod_proxy_free) { for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; - if (s) { + if (NULL == s) continue; - array_free(s->extensions); + array_free(s->extensions); - free(s); - } + free(s); } free(p->config_storage); } @@ -197,8 +195,8 @@ SETDEFAULTS_FUNC(mod_proxy_set_defaults) { p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; plugin_config *s; - array *ca; s = malloc(sizeof(plugin_config)); s->extensions = array_init(); @@ -211,13 +209,12 @@ SETDEFAULTS_FUNC(mod_proxy_set_defaults) { buffer_reset(p->balance_buf); p->config_storage[i] = s; - ca = ((data_config *)srv->config_context->data[i])->value; - if (0 != config_insert_values_global(srv, ca, cv)) { + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { return HANDLER_ERROR; } - if (buffer_is_empty(p->balance_buf)) { + if (buffer_string_is_empty(p->balance_buf)) { s->balance = PROXY_BALANCE_FAIR; } else if (buffer_is_equal_string(p->balance_buf, CONST_STR_LEN("fair"))) { s->balance = PROXY_BALANCE_FAIR; @@ -231,13 +228,13 @@ SETDEFAULTS_FUNC(mod_proxy_set_defaults) { return HANDLER_ERROR; } - if (NULL != (du = array_get_element(ca, "proxy.server"))) { + if (NULL != (du = array_get_element(config->value, "proxy.server"))) { size_t j; data_array *da = (data_array *)du; if (du->type != TYPE_ARRAY) { log_error_write(srv, __FILE__, __LINE__, "sss", - "unexpected type for key: ", "proxy.server", "array of strings"); + "unexpected type for key: ", "proxy.server", "expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))"); return HANDLER_ERROR; } @@ -254,7 +251,7 @@ SETDEFAULTS_FUNC(mod_proxy_set_defaults) { if (da_ext->type != TYPE_ARRAY) { log_error_write(srv, __FILE__, __LINE__, "sssbs", "unexpected type for key: ", "proxy.server", - "[", da->value->data[j]->key, "](string)"); + "[", da->value->data[j]->key, "](string); expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))"); return HANDLER_ERROR; } @@ -283,7 +280,7 @@ SETDEFAULTS_FUNC(mod_proxy_set_defaults) { log_error_write(srv, __FILE__, __LINE__, "ssSBS", "unexpected type for key:", "proxy.server", - "[", da_ext->value->data[n]->key, "](string)"); + "[", da_ext->value->data[n]->key, "](string); expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))"); return HANDLER_ERROR; } @@ -292,17 +289,17 @@ SETDEFAULTS_FUNC(mod_proxy_set_defaults) { df->port = 80; - buffer_copy_string_buffer(df->key, da_host->key); + buffer_copy_buffer(df->key, da_host->key); pcv[0].destination = df->host; pcv[1].destination = &(df->port); - if (0 != config_insert_values_internal(srv, da_host->value, pcv)) { + if (0 != config_insert_values_internal(srv, da_host->value, pcv, T_CONFIG_SCOPE_CONNECTION)) { df->free((data_unset*) df); return HANDLER_ERROR; } - if (buffer_is_empty(df->host)) { + if (buffer_string_is_empty(df->host)) { log_error_write(srv, __FILE__, __LINE__, "sbbbs", "missing key (string):", da->key, @@ -319,7 +316,7 @@ SETDEFAULTS_FUNC(mod_proxy_set_defaults) { if (NULL == (dfa = (data_array *)array_get_element(s->extensions, da_ext->key->ptr))) { dfa = data_array_init(); - buffer_copy_string_buffer(dfa->key, da_ext->key); + buffer_copy_buffer(dfa->key, da_ext->key); array_insert_unique(dfa->value, (data_unset *)df); array_insert_unique(s->extensions, (data_unset *)dfa); @@ -338,8 +335,6 @@ static void proxy_connection_close(server *srv, handle plugin_data *p; connection *con; - if (NULL == hctx) return; - p = hctx->plugin_data; con = hctx->remote_conn; @@ -357,11 +352,19 @@ static void proxy_connection_close(server *srv, handle handler_ctx_free(hctx); con->plugin_ctx[p->id] = NULL; + + /* finish response (if not already con->file_started, con->file_finished) */ + if (con->mode == p->id) { + http_response_backend_done(srv, con); + } } static int proxy_establish_connection(server *srv, handler_ctx *hctx) { struct sockaddr *proxy_addr; struct sockaddr_in proxy_addr_in; +#if defined(HAVE_SYS_UN_H) + struct sockaddr_un proxy_addr_un; +#endif #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON) struct sockaddr_in6 proxy_addr_in6; #endif @@ -372,6 +375,22 @@ static int proxy_establish_connection(server *srv, han int proxy_fd = hctx->fd; +#if defined(HAVE_SYS_UN_H) + if (strstr(host->host->ptr, "/")) { + if (buffer_string_length(host->host) + 1 > sizeof(proxy_addr_un.sun_path)) { + log_error_write(srv, __FILE__, __LINE__, "sB", + "ERROR: Unix Domain socket filename too long:", + host->host); + return -1; + } + + memset(&proxy_addr_un, 0, sizeof(proxy_addr_un)); + proxy_addr_un.sun_family = AF_UNIX; + memcpy(proxy_addr_un.sun_path, host->host->ptr, buffer_string_length(host->host) + 1); + servlen = sizeof(proxy_addr_un); + proxy_addr = (struct sockaddr *) &proxy_addr_un; + } else +#endif #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON) if (strstr(host->host->ptr, ":")) { memset(&proxy_addr_in6, 0, sizeof(proxy_addr_in6)); @@ -417,27 +436,27 @@ static int proxy_establish_connection(server *srv, han } static void proxy_set_header(connection *con, const char *key, const char *value) { - data_string *ds_dst; + data_string *ds_dst; - if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) { - ds_dst = data_string_init(); - } + if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) { + ds_dst = data_string_init(); + } - buffer_copy_string(ds_dst->key, key); - buffer_copy_string(ds_dst->value, value); - array_insert_unique(con->request.headers, (data_unset *)ds_dst); + buffer_copy_string(ds_dst->key, key); + buffer_copy_string(ds_dst->value, value); + array_insert_unique(con->request.headers, (data_unset *)ds_dst); } static void proxy_append_header(connection *con, const char *key, const char *value) { - data_string *ds_dst; + data_string *ds_dst; - if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) { - ds_dst = data_string_init(); - } + if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) { + ds_dst = data_string_init(); + } - buffer_copy_string(ds_dst->key, key); - buffer_append_string(ds_dst->value, value); - array_insert_unique(con->request.headers, (data_unset *)ds_dst); + buffer_copy_string(ds_dst->key, key); + buffer_append_string(ds_dst->value, value); + array_insert_unique(con->request.headers, (data_unset *)ds_dst); } @@ -449,7 +468,7 @@ static int proxy_create_env(server *srv, handler_ctx * /* build header */ - b = chunkqueue_get_append_buffer(hctx->wb); + b = buffer_init(); /* request line */ buffer_copy_string(b, get_http_method_name(con->request.http_method)); @@ -461,8 +480,7 @@ static int proxy_create_env(server *srv, handler_ctx * proxy_append_header(con, "X-Forwarded-For", (char *)inet_ntop_cache_get_ip(srv, &(con->dst_addr))); /* http_host is NOT is just a pointer to a buffer * which is NULL if it is not set */ - if (con->request.http_host && - !buffer_is_empty(con->request.http_host)) { + if (!buffer_string_is_empty(con->request.http_host)) { proxy_set_header(con, "X-Host", con->request.http_host->ptr); } proxy_set_header(con, "X-Forwarded-Proto", con->uri.scheme->ptr); @@ -473,9 +491,13 @@ static int proxy_create_env(server *srv, handler_ctx * ds = (data_string *)con->request.headers->data[i]; - if (ds->value->used && ds->key->used) { - if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Connection"))) continue; - if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Proxy-Connection"))) continue; + if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) { + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Connection"))) continue; + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Proxy-Connection"))) continue; + /* Do not emit HTTP_PROXY in environment. + * Some executables use HTTP_PROXY to configure + * outgoing proxy. See also https://httpoxy.org/ */ + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Proxy"))) continue; buffer_append_string_buffer(b, ds->key); buffer_append_string_len(b, CONST_STR_LEN(": ")); @@ -484,62 +506,17 @@ static int proxy_create_env(server *srv, handler_ctx * } } - buffer_append_string_len(b, CONST_STR_LEN("\r\n")); + buffer_append_string_len(b, CONST_STR_LEN("Connection: close\r\n\r\n")); - hctx->wb->bytes_in += b->used - 1; + hctx->wb_reqlen = buffer_string_length(b); + chunkqueue_append_buffer(hctx->wb, b); + buffer_free(b); + /* body */ if (con->request.content_length) { - chunkqueue *req_cq = con->request_content_queue; - chunk *req_c; - off_t offset; - - /* something to send ? */ - for (offset = 0, req_c = req_cq->first; offset != req_cq->bytes_in; req_c = req_c->next) { - off_t weWant = req_cq->bytes_in - offset; - off_t weHave = 0; - - /* we announce toWrite octects - * now take all the request_content chunk that we need to fill this request - * */ - - switch (req_c->type) { - case FILE_CHUNK: - weHave = req_c->file.length - req_c->offset; - - if (weHave > weWant) weHave = weWant; - - chunkqueue_append_file(hctx->wb, req_c->file.name, req_c->offset, weHave); - - req_c->offset += weHave; - req_cq->bytes_out += weHave; - - hctx->wb->bytes_in += weHave; - - break; - case MEM_CHUNK: - /* append to the buffer */ - weHave = req_c->mem->used - 1 - req_c->offset; - - if (weHave > weWant) weHave = weWant; - - b = chunkqueue_get_append_buffer(hctx->wb); - buffer_append_memory(b, req_c->mem->ptr + req_c->offset, weHave); - b->used++; /* add virtual \0 */ - - req_c->offset += weHave; - req_cq->bytes_out += weHave; - - hctx->wb->bytes_in += weHave; - - break; - default: - break; - } - - offset += weHave; - } - + chunkqueue_append_chunkqueue(hctx->wb, con->request_content_queue); + hctx->wb_reqlen += con->request.content_length;/* (eventual) total request size */ } return 0; @@ -559,18 +536,18 @@ static int proxy_response_parse(server *srv, connectio UNUSED(srv); - /* \r\n -> \0\0 */ + /* [\r]\n -> [\0]\0 */ - buffer_copy_string_buffer(p->parse_response, in); + buffer_copy_buffer(p->parse_response, in); - for (s = p->parse_response->ptr; NULL != (ns = strstr(s, "\r\n")); s = ns + 2) { + for (s = p->parse_response->ptr; NULL != (ns = strchr(s, '\n')); s = ns + 1) { char *key, *value; int key_len; data_string *ds; int copy_header; ns[0] = '\0'; - ns[1] = '\0'; + if (s != ns && ns[-1] == '\r') ns[-1] = '\0'; if (-1 == http_response_status) { /* The first line of a Response message is the Status-Line */ @@ -579,7 +556,7 @@ static int proxy_response_parse(server *srv, connectio if (*key) { http_response_status = (int) strtol(key, NULL, 10); - if (http_response_status <= 0) http_response_status = 502; + if (http_response_status < 100 || http_response_status >= 1000) http_response_status = 502; } else { http_response_status = 502; } @@ -622,7 +599,7 @@ static int proxy_response_parse(server *srv, connectio break; case 14: if (0 == strncasecmp(key, "Content-Length", key_len)) { - con->response.content_length = strtol(value, NULL, 10); + con->response.content_length = strtoul(value, NULL, 10); con->parsed_response |= HTTP_CONTENT_LENGTH; } break; @@ -655,41 +632,65 @@ static int proxy_demux_response(server *srv, handler_c int proxy_fd = hctx->fd; /* check how much we have to read */ + #if !defined(_WIN32) && !defined(__CYGWIN__) if (ioctl(hctx->fd, FIONREAD, &b)) { + if (errno == EAGAIN) { + fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); + return 0; + } log_error_write(srv, __FILE__, __LINE__, "sd", "ioctl failed: ", proxy_fd); return -1; } + #else + b = 4096; + #endif if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "sd", - "proxy - have to read:", b); + "proxy - have to read:", b); } if (b > 0) { - if (hctx->response->used == 0) { - /* avoid too small buffer */ - buffer_prepare_append(hctx->response, b + 1); - hctx->response->used = 1; - } else { - buffer_prepare_append(hctx->response, b); + if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)) { + off_t cqlen = chunkqueue_length(con->write_queue); + if (cqlen + b > 65536 - 4096) { + if (!con->is_writable) { + /*(defer removal of FDEVENT_IN interest since + * connection_state_machine() might be able to send data + * immediately, unless !con->is_writable, where + * connection_state_machine() might not loop back to call + * mod_proxy_handle_subrequest())*/ + fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); + } + if (cqlen >= 65536-1) return 0; + b = 65536 - 1 - (int)cqlen; + } } - if (-1 == (r = read(hctx->fd, hctx->response->ptr + hctx->response->used - 1, b))) { - if (errno == EAGAIN) return 0; + buffer_string_prepare_append(hctx->response, b); + + if (-1 == (r = read(hctx->fd, hctx->response->ptr + buffer_string_length(hctx->response), buffer_string_space(hctx->response)))) { + if (errno == EAGAIN) { + fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); + return 0; + } log_error_write(srv, __FILE__, __LINE__, "sds", "unexpected end-of-file (perhaps the proxy process died):", proxy_fd, strerror(errno)); return -1; } + #if defined(_WIN32) || defined(__CYGWIN__) + if (0 == r) return 1; /* fin */ + #endif + /* this should be catched by the b > 0 above */ force_assert(r); - hctx->response->used += r; - hctx->response->ptr[hctx->response->used - 1] = '\0'; + buffer_commit(hctx->response, r); #if 0 log_error_write(srv, __FILE__, __LINE__, "sdsbs", @@ -698,51 +699,55 @@ static int proxy_demux_response(server *srv, handler_c if (0 == con->got_response) { con->got_response = 1; - buffer_prepare_copy(hctx->response_header, 128); + buffer_string_prepare_copy(hctx->response_header, 1023); } if (0 == con->file_started) { char *c; /* search for the \r\n\r\n in the string */ - if (NULL != (c = buffer_search_string_len(hctx->response, "\r\n\r\n", 4))) { + if (NULL != (c = buffer_search_string_len(hctx->response, CONST_STR_LEN("\r\n\r\n")))) { size_t hlen = c - hctx->response->ptr + 4; - size_t blen = hctx->response->used - hlen - 1; + size_t blen = buffer_string_length(hctx->response) - hlen; /* found */ - buffer_append_string_len(hctx->response_header, hctx->response->ptr, c - hctx->response->ptr + 4); + buffer_append_string_len(hctx->response_header, hctx->response->ptr, hlen); #if 0 log_error_write(srv, __FILE__, __LINE__, "sb", "Header:", hctx->response_header); #endif /* parse the response header */ proxy_response_parse(srv, con, p, hctx->response_header); - /* enable chunked-transfer-encoding */ - if (con->request.http_version == HTTP_VERSION_1_1 && - !(con->parsed_response & HTTP_CONTENT_LENGTH)) { - con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; - } - con->file_started = 1; - if (blen) { - http_chunk_append_mem(srv, con, c + 4, blen + 1); + if (blen > 0) { + if (0 != http_chunk_append_mem(srv, con, c + 4, blen)) { + /* error writing to tempfile; + * truncate response or send 500 if nothing sent yet */ + fin = 1; + con->file_started = 0; + } } - hctx->response->used = 0; - joblist_append(srv, con); + buffer_reset(hctx->response); + } else { + /* no luck, no header found */ + /*(reuse MAX_HTTP_REQUEST_HEADER as max size for response headers from backends)*/ + if (buffer_string_length(hctx->response) > MAX_HTTP_REQUEST_HEADER) { + log_error_write(srv, __FILE__, __LINE__, "sb", "response headers too large for", con->uri.path); + con->http_status = 502; /* Bad Gateway */ + con->mode = DIRECT; + fin = 1; + } } } else { - http_chunk_append_mem(srv, con, hctx->response->ptr, hctx->response->used); - joblist_append(srv, con); - hctx->response->used = 0; + if (0 != http_chunk_append_buffer(srv, con, hctx->response)) { + /* error writing to tempfile; + * truncate response or send 500 if nothing sent yet */ + fin = 1; + } + buffer_reset(hctx->response); } - } else { /* reading from upstream done */ - con->file_finished = 1; - - http_chunk_append_mem(srv, con, NULL, 0); - joblist_append(srv, con); - fin = 1; } @@ -756,8 +761,7 @@ static handler_t proxy_write_request(server *srv, hand int ret; - if (!host || - (!host->host->used || !host->port)) return -1; + if (!host || buffer_string_is_empty(host->host) || !host->port) return HANDLER_ERROR; switch(hctx->state) { case PROXY_STATE_CONNECT: @@ -769,22 +773,28 @@ static handler_t proxy_write_request(server *srv, hand /* wait */ return HANDLER_WAIT_FOR_EVENT; - break; - case PROXY_STATE_INIT: +#if defined(HAVE_SYS_UN_H) + if (strstr(host->host->ptr,"/")) { + if (-1 == (hctx->fd = socket(AF_UNIX, SOCK_STREAM, 0))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed: ", strerror(errno)); + return HANDLER_ERROR; + } + } else +#endif #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON) if (strstr(host->host->ptr,":")) { - if (-1 == (hctx->fd = socket(AF_INET6, SOCK_STREAM, 0))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed: ", strerror(errno)); - return HANDLER_ERROR; - } + if (-1 == (hctx->fd = socket(AF_INET6, SOCK_STREAM, 0))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed: ", strerror(errno)); + return HANDLER_ERROR; + } } else #endif { - if (-1 == (hctx->fd = socket(AF_INET, SOCK_STREAM, 0))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed: ", strerror(errno)); - return HANDLER_ERROR; - } + if (-1 == (hctx->fd = socket(AF_INET, SOCK_STREAM, 0))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed: ", strerror(errno)); + return HANDLER_ERROR; + } } hctx->fde_ndx = -1; @@ -823,6 +833,7 @@ static handler_t proxy_write_request(server *srv, hand case PROXY_STATE_PREPARE_WRITE: proxy_create_env(srv, hctx); + fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); proxy_set_state(srv, hctx, PROXY_STATE_WRITE); /* fall through */ @@ -841,15 +852,37 @@ static handler_t proxy_write_request(server *srv, hand return HANDLER_ERROR; } - if (hctx->wb->bytes_out == hctx->wb->bytes_in) { + if (hctx->wb->bytes_out == hctx->wb_reqlen) { + fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); + #if (defined(__APPLE__) && defined(__MACH__)) \ + || defined(__FreeBSD__) || defined(__NetBSD__) \ + || defined(__OpenBSD__) || defined(__DragonflyBSD__) + /*(*BSD stack on remote might signal POLLHUP and remote + * might treat as socket error instead of half-close)*/ + #else + /*(remote could be different machine running affected OS, + * so only issue shutdown for known local sockets)*/ + if ( '/' == host->host->ptr[0] + || buffer_is_equal_string(host->host, CONST_STR_LEN("127.0.0.1")) + || buffer_is_equal_string(host->host, CONST_STR_LEN("::1"))) { + shutdown(hctx->fd, SHUT_WR);/* future: remove if HTTP/1.1 request */ + } + #endif proxy_set_state(srv, hctx, PROXY_STATE_READ); - - fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); - fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); } else { - fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); - - return HANDLER_WAIT_FOR_EVENT; + off_t wblen = hctx->wb->bytes_in - hctx->wb->bytes_out; + if (hctx->wb->bytes_in < hctx->wb_reqlen && wblen < 65536 - 16384) { + /*(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/ + if (!(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) { + con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN; + con->is_readable = 1; /* trigger optimistic read from client */ + } + } + if (0 == wblen) { + fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); + } else { + fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); + } } return HANDLER_WAIT_FOR_EVENT; @@ -860,8 +893,6 @@ static handler_t proxy_write_request(server *srv, hand log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state"); return HANDLER_ERROR; } - - return HANDLER_GO_ON; } #define PATCH(x) \ @@ -900,24 +931,15 @@ static int mod_proxy_patch_connection(server *srv, con } #undef PATCH -SUBREQUEST_FUNC(mod_proxy_handle_subrequest) { - plugin_data *p = p_d; - - handler_ctx *hctx = con->plugin_ctx[p->id]; - data_proxy *host; - - if (NULL == hctx) return HANDLER_GO_ON; - - mod_proxy_patch_connection(srv, con, p); - - host = hctx->host; - - /* not my job */ - if (con->mode != p->id) return HANDLER_GO_ON; - +static handler_t proxy_send_request(server *srv, handler_ctx *hctx) { /* ok, create the request */ - switch(proxy_write_request(srv, hctx)) { - case HANDLER_ERROR: + handler_t rc = proxy_write_request(srv, hctx); + if (HANDLER_ERROR != rc) { + return rc; + } else { + data_proxy *host = hctx->host; + connection *con = hctx->remote_conn; + plugin_data *p = hctx->plugin_data; log_error_write(srv, __FILE__, __LINE__, "sbdd", "proxy-server disabled:", host->host, host->port, @@ -927,71 +949,105 @@ SUBREQUEST_FUNC(mod_proxy_handle_subrequest) { host->is_disabled = 1; host->disable_ts = srv->cur_ts; + /* reset the enviroment and restart the sub-request */ + con->mode = DIRECT;/*(avoid changing con->state, con->http_status)*/ proxy_connection_close(srv, hctx); + con->mode = p->id; - /* reset the enviroment and restart the sub-request */ - buffer_reset(con->physical.path); - con->mode = DIRECT; + return HANDLER_COMEBACK; + } +} - joblist_append(srv, con); - /* mis-using HANDLER_WAIT_FOR_FD to break out of the loop - * and hope that the childs will be restarted - * - */ +static handler_t proxy_recv_response(server *srv, handler_ctx *hctx); - return HANDLER_WAIT_FOR_FD; - case HANDLER_WAIT_FOR_EVENT: - break; - case HANDLER_WAIT_FOR_FD: - return HANDLER_WAIT_FOR_FD; - default: - break; + +SUBREQUEST_FUNC(mod_proxy_handle_subrequest) { + plugin_data *p = p_d; + + handler_ctx *hctx = con->plugin_ctx[p->id]; + + if (NULL == hctx) return HANDLER_GO_ON; + + /* not my job */ + if (con->mode != p->id) return HANDLER_GO_ON; + + if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) + && con->file_started) { + if (chunkqueue_length(con->write_queue) > 65536 - 4096) { + fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); + } else if (!(fdevent_event_get_interest(srv->ev, hctx->fd) & FDEVENT_IN)) { + /* optimistic read from backend, which might re-enable FDEVENT_IN */ + handler_t rc = proxy_recv_response(srv, hctx); /*(might invalidate hctx)*/ + if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ + } } - if (con->file_started == 1) { - return HANDLER_FINISHED; - } else { - return HANDLER_WAIT_FOR_EVENT; + if (0 == hctx->wb->bytes_in + ? con->state == CON_STATE_READ_POST + : hctx->wb->bytes_in < hctx->wb_reqlen) { + /*(64k - 4k to attempt to avoid temporary files + * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/ + if (hctx->wb->bytes_in - hctx->wb->bytes_out > 65536 - 4096 + && (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)){ + con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN; + if (0 != hctx->wb->bytes_in) return HANDLER_WAIT_FOR_EVENT; + } else { + handler_t r = connection_handle_read_post_state(srv, con); + chunkqueue *req_cq = con->request_content_queue; + if (0 != hctx->wb->bytes_in && !chunkqueue_is_empty(req_cq)) { + chunkqueue_append_chunkqueue(hctx->wb, req_cq); + if (fdevent_event_get_interest(srv->ev, hctx->fd) & FDEVENT_OUT) { + return (r == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : r; + } + } + if (r != HANDLER_GO_ON) return r; + } } + + return ((0 == hctx->wb->bytes_in || !chunkqueue_is_empty(hctx->wb)) + && hctx->state != PROXY_STATE_CONNECT) + ? proxy_send_request(srv, hctx) + : HANDLER_WAIT_FOR_EVENT; } + +static handler_t proxy_recv_response(server *srv, handler_ctx *hctx) { + + switch (proxy_demux_response(srv, hctx)) { + case 0: + break; + case -1: + http_response_backend_error(srv, hctx->remote_conn); + /* fall through */ + case 1: + /* we are done */ + proxy_connection_close(srv, hctx); + + return HANDLER_FINISHED; + } + + return HANDLER_GO_ON; +} + + static handler_t proxy_handle_fdevent(server *srv, void *ctx, int revents) { handler_ctx *hctx = ctx; connection *con = hctx->remote_conn; plugin_data *p = hctx->plugin_data; + joblist_append(srv, con); - if ((revents & FDEVENT_IN) && - hctx->state == PROXY_STATE_READ) { + if (revents & FDEVENT_IN) { if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "sd", "proxy: fdevent-in", hctx->state); } - switch (proxy_demux_response(srv, hctx)) { - case 0: - break; - case 1: - /* we are done */ - proxy_connection_close(srv, hctx); - - joblist_append(srv, con); - return HANDLER_FINISHED; - case -1: - if (con->file_started == 0) { - /* nothing has been send out yet, send a 500 */ - connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); - con->http_status = 500; - con->mode = DIRECT; - } else { - /* response might have been already started, kill the connection */ - connection_set_state(srv, con, CON_STATE_ERROR); - } - - joblist_append(srv, con); - return HANDLER_FINISHED; + { + handler_t rc = proxy_recv_response(srv,hctx);/*(might invalidate hctx)*/ + if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ } } @@ -1005,16 +1061,11 @@ static handler_t proxy_handle_fdevent(server *srv, voi int socket_error; socklen_t socket_error_len = sizeof(socket_error); - /* we don't need it anymore */ - fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); - hctx->fde_ndx = -1; - /* try to finish the connect() */ if (0 != getsockopt(hctx->fd, SOL_SOCKET, SO_ERROR, &socket_error, &socket_error_len)) { log_error_write(srv, __FILE__, __LINE__, "ss", "getsockopt failed:", strerror(errno)); - joblist_append(srv, con); return HANDLER_FINISHED; } if (socket_error != 0) { @@ -1022,7 +1073,6 @@ static handler_t proxy_handle_fdevent(server *srv, voi "establishing connection failed:", strerror(socket_error), "port:", hctx->host->port); - joblist_append(srv, con); return HANDLER_FINISHED; } if (p->conf.debug) { @@ -1032,18 +1082,7 @@ static handler_t proxy_handle_fdevent(server *srv, voi proxy_set_state(srv, hctx, PROXY_STATE_PREPARE_WRITE); } - if (hctx->state == PROXY_STATE_PREPARE_WRITE || - hctx->state == PROXY_STATE_WRITE) { - /* we are allowed to send something out - * - * 1. after a just finished connect() call - * 2. in a unfinished write() call (long POST request) - */ - return mod_proxy_handle_subrequest(srv, con, p); - } else { - log_error_write(srv, __FILE__, __LINE__, "sd", - "proxy: out", hctx->state); - } + return proxy_send_request(srv, hctx); /*(might invalidate hctx)*/ } /* perhaps this issue is already handled */ @@ -1073,38 +1112,32 @@ static handler_t proxy_handle_fdevent(server *srv, voi hctx->host->is_disabled = 1; hctx->host->disable_ts = srv->cur_ts; + /* reset the environment and restart the sub-request */ + con->mode = DIRECT;/*(avoid changing con->state, con->http_status)*/ proxy_connection_close(srv, hctx); - - /* reset the enviroment and restart the sub-request */ - buffer_reset(con->physical.path); - con->mode = DIRECT; - - joblist_append(srv, con); + con->mode = p->id; } else { proxy_connection_close(srv, hctx); - joblist_append(srv, con); - - con->mode = DIRECT; con->http_status = 503; } - - return HANDLER_FINISHED; + } else if (con->file_started) { + /* drain any remaining data from kernel pipe buffers + * even if (con->conf.stream_response_body + * & FDEVENT_STREAM_RESPONSE_BUFMIN) + * since event loop will spin on fd FDEVENT_HUP event + * until unregistered. */ + handler_t rc; + do { + rc = proxy_recv_response(srv,hctx);/*(might invalidate hctx)*/ + } while (rc == HANDLER_GO_ON); /*(unless HANDLER_GO_ON)*/ + return rc; /* HANDLER_FINISHED or HANDLER_ERROR */ + } else { + proxy_connection_close(srv, hctx); } - - if (!con->file_finished) { - http_chunk_append_mem(srv, con, NULL, 0); - } - - con->file_finished = 1; - proxy_connection_close(srv, hctx); - joblist_append(srv, con); } else if (revents & FDEVENT_ERR) { - /* kill all connections to the proxy process */ - log_error_write(srv, __FILE__, __LINE__, "sd", "proxy-FDEVENT_ERR, but no HUP", revents); - con->file_finished = 1; - joblist_append(srv, con); + http_response_backend_error(srv, con); proxy_connection_close(srv, hctx); } @@ -1130,14 +1163,9 @@ static handler_t mod_proxy_check_extension(server *srv mod_proxy_patch_connection(srv, con, p); fn = con->uri.path; + if (buffer_string_is_empty(fn)) return HANDLER_ERROR; + s_len = buffer_string_length(fn); - if (fn->used == 0) { - return HANDLER_ERROR; - } - - s_len = fn->used - 1; - - path_info_offset = 0; if (p->conf.debug) { @@ -1151,9 +1179,9 @@ static handler_t mod_proxy_check_extension(server *srv ext = (data_array *)p->conf.extensions->data[k]; - if (ext->key->used == 0) continue; + if (buffer_is_empty(ext->key)) continue; - ct_len = ext->key->used - 1; + ct_len = buffer_string_length(ext->key); if (s_len < ct_len) continue; @@ -1335,11 +1363,11 @@ static handler_t mod_proxy_check_extension(server *srv return HANDLER_GO_ON; } -static handler_t mod_proxy_connection_close_callback(server *srv, connection *con, void *p_d) { +static handler_t mod_proxy_connection_reset(server *srv, connection *con, void *p_d) { plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + if (hctx) proxy_connection_close(srv, hctx); - proxy_connection_close(srv, con->plugin_ctx[p->id]); - return HANDLER_GO_ON; } @@ -1393,8 +1421,8 @@ int mod_proxy_plugin_init(plugin *p) { p->init = mod_proxy_init; p->cleanup = mod_proxy_free; p->set_defaults = mod_proxy_set_defaults; - p->connection_reset = mod_proxy_connection_close_callback; /* end of req-resp cycle */ - p->handle_connection_close = mod_proxy_connection_close_callback; /* end of client connection */ + p->connection_reset = mod_proxy_connection_reset; /* end of req-resp cycle */ + p->handle_connection_close = mod_proxy_connection_reset; /* end of client connection */ p->handle_uri_clean = mod_proxy_check_extension; p->handle_subrequest = mod_proxy_handle_subrequest; p->handle_trigger = mod_proxy_trigger;