Mercurial > hg > nginx-vendor-current
changeset 640:eb208e0cf44d NGINX_1_1_4
nginx 1.1.4
*) Feature: the ngx_http_upstream_keepalive module.
*) Feature: the "proxy_http_version" directive.
*) Feature: the "fastcgi_keep_conn" directive.
*) Feature: the "worker_aio_requests" directive.
*) Bugfix: if nginx was built --with-file-aio it could not be run on
Linux kernel which did not support AIO.
*) Bugfix: in Linux AIO error processing.
Thanks to Hagai Avrahami.
*) Bugfix: reduced memory consumption for long-lived requests.
*) Bugfix: the module ngx_http_mp4_module did not support 64-bit MP4
"co64" atom.
line wrap: on
line diff
--- a/CHANGES +++ b/CHANGES @@ -1,4 +1,26 @@ +Changes with nginx 1.1.4 20 Sep 2011 + + *) Feature: the ngx_http_upstream_keepalive module. + + *) Feature: the "proxy_http_version" directive. + + *) Feature: the "fastcgi_keep_conn" directive. + + *) Feature: the "worker_aio_requests" directive. + + *) Bugfix: if nginx was built --with-file-aio it could not be run on + Linux kernel which did not support AIO. + + *) Bugfix: in Linux AIO error processing. + Thanks to Hagai Avrahami. + + *) Bugfix: reduced memory consumption for long-lived requests. + + *) Bugfix: the module ngx_http_mp4_module did not support 64-bit MP4 + "co64" atom. + + Changes with nginx 1.1.3 14 Sep 2011 *) Feature: the module ngx_http_mp4_module.
--- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,26 @@ +Изменения в nginx 1.1.4 20.09.2011 + + *) Добавление: модуль ngx_http_upstream_keepalive. + + *) Добавление: директива proxy_http_version. + + *) Добавление: директива fastcgi_keep_conn. + + *) Добавление: директива worker_aio_requests. + + *) Исправление: если nginx был собран с файловым AIO, он не мог + запускаться на Linux без поддержки AIO. + + *) Исправление: в обработке ошибок при работе с Linux AIO. + Спасибо Hagai Avrahami. + + *) Исправление: уменьшено потребление памяти для долгоживущих запросов. + + *) Исправление: модуль ngx_http_mp4_module не поддерживал 64-битный + MP4-атом co64. + + Изменения в nginx 1.1.3 14.09.2011 *) Добавление: модуль ngx_http_mp4_module.
--- a/auto/modules +++ b/auto/modules @@ -344,6 +344,11 @@ if [ $HTTP_UPSTREAM_IP_HASH = YES ]; the HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_IP_HASH_SRCS" fi +if [ $HTTP_UPSTREAM_KEEPALIVE = YES ]; then + HTTP_MODULES="$HTTP_MODULES $HTTP_UPSTREAM_KEEPALIVE_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_KEEPALIVE_SRCS" +fi + if [ $HTTP_STUB_STATUS = YES ]; then have=NGX_STAT_STUB . auto/have HTTP_MODULES="$HTTP_MODULES ngx_http_stub_status_module"
--- a/auto/options +++ b/auto/options @@ -95,6 +95,7 @@ HTTP_FLV=NO HTTP_MP4=NO HTTP_GZIP_STATIC=NO HTTP_UPSTREAM_IP_HASH=YES +HTTP_UPSTREAM_KEEPALIVE=YES # STUB HTTP_STUB_STATUS=NO @@ -231,6 +232,7 @@ do --without-http_empty_gif_module) HTTP_EMPTY_GIF=NO ;; --without-http_browser_module) HTTP_BROWSER=NO ;; --without-http_upstream_ip_hash_module) HTTP_UPSTREAM_IP_HASH=NO ;; + --without-http_upstream_keepalive_module) HTTP_UPSTREAM_KEEPALIVE=NO ;; --with-http_perl_module) HTTP_PERL=YES ;; --with-perl_modules_path=*) NGX_PERL_MODULES="$value" ;;
--- a/auto/sources +++ b/auto/sources @@ -475,6 +475,11 @@ HTTP_UPSTREAM_IP_HASH_MODULE=ngx_http_up HTTP_UPSTREAM_IP_HASH_SRCS=src/http/modules/ngx_http_upstream_ip_hash_module.c +HTTP_UPSTREAM_KEEPALIVE_MODULE=ngx_http_upstream_keepalive_module +HTTP_UPSTREAM_KEEPALIVE_SRCS=" \ + src/http/modules/ngx_http_upstream_keepalive_module.c" + + MAIL_INCS="src/mail" MAIL_DEPS="src/mail/ngx_mail.h"
--- a/html/ngx_core_module.html +++ b/html/ngx_core_module.html @@ -1,4 +1,4 @@ -<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>Core Module</title></head><body><a name="example"></a><center><h4>Example Configuration</h4></center><p><blockquote><pre> +<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>Core Module</title></head><body><center><h3>Core Module</h3></center><a name="example"></a><center><h4>Example Configuration</h4></center><p><blockquote><pre> user www www; worker_processes 2; @@ -27,9 +27,8 @@ variable inheritance during a <a href="control.html#upgrade">live upgrade</a> of an executable file; </li><li> -use of variables by the -<a href="http/ngx_http_perl_module.html">http_perl</a> -module; +use of variables by the module +<a href="http/ngx_http_perl_module.html">ngx_http_perl_module</a>; </li><li> use of variables by worker processes. Please bear in mind that controlling system libraries in this way @@ -40,9 +39,9 @@ An exception from this is an above menti <a href="control.html#upgrade">live upgrade</a> of an executable file. </li></ul></p><p> -The TZ variable is always inherited and made available to the -<a href="http/ngx_http_perl_module.html">http_perl</a> -module, unless configured explicitly. +The TZ variable is always inherited and made available to the module +<a href="http/ngx_http_perl_module.html">ngx_http_perl_module</a>, +unless configured explicitly. </p><p> Usage example: <blockquote><pre>
--- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -8,8 +8,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1001003 -#define NGINX_VERSION "1.1.3" +#define nginx_version 1001004 +#define NGINX_VERSION "1.1.4" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX"
--- a/src/core/ngx_buf.c +++ b/src/core/ngx_buf.c @@ -180,7 +180,7 @@ ngx_chain_get_free_buf(ngx_pool_t *p, ng void -ngx_chain_update_chains(ngx_chain_t **free, ngx_chain_t **busy, +ngx_chain_update_chains(ngx_pool_t *p, ngx_chain_t **free, ngx_chain_t **busy, ngx_chain_t **out, ngx_buf_tag_t tag) { ngx_chain_t *cl; @@ -197,19 +197,21 @@ ngx_chain_update_chains(ngx_chain_t **fr *out = NULL; while (*busy) { - if (ngx_buf_size((*busy)->buf) != 0) { + cl = *busy; + + if (ngx_buf_size(cl->buf) != 0) { break; } - if ((*busy)->buf->tag != tag) { - *busy = (*busy)->next; + if (cl->buf->tag != tag) { + *busy = cl->next; + ngx_free_chain(p, cl); continue; } - (*busy)->buf->pos = (*busy)->buf->start; - (*busy)->buf->last = (*busy)->buf->start; + cl->buf->pos = cl->buf->start; + cl->buf->last = cl->buf->start; - cl = *busy; *busy = cl->next; cl->next = *free; *free = cl;
--- a/src/core/ngx_buf.h +++ b/src/core/ngx_buf.h @@ -154,8 +154,8 @@ ngx_int_t ngx_chain_writer(void *ctx, ng ngx_int_t ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, ngx_chain_t *in); ngx_chain_t *ngx_chain_get_free_buf(ngx_pool_t *p, ngx_chain_t **free); -void ngx_chain_update_chains(ngx_chain_t **free, ngx_chain_t **busy, - ngx_chain_t **out, ngx_buf_tag_t tag); +void ngx_chain_update_chains(ngx_pool_t *p, ngx_chain_t **free, + ngx_chain_t **busy, ngx_chain_t **out, ngx_buf_tag_t tag); #endif /* _NGX_BUF_H_INCLUDED_ */
--- a/src/core/ngx_cycle.c +++ b/src/core/ngx_cycle.c @@ -739,7 +739,7 @@ old_shm_zone_done: ngx_temp_pool = ngx_create_pool(128, cycle->log); if (ngx_temp_pool == NULL) { ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, - "can not create ngx_temp_pool"); + "could not create ngx_temp_pool"); exit(1); }
--- a/src/core/ngx_output_chain.c +++ b/src/core/ngx_output_chain.c @@ -208,7 +208,8 @@ ngx_output_chain(ngx_output_chain_ctx_t return last; } - ngx_chain_update_chains(&ctx->free, &ctx->busy, &out, ctx->tag); + ngx_chain_update_chains(ctx->pool, &ctx->free, &ctx->busy, &out, + ctx->tag); last_out = &out; } }
--- a/src/core/ngx_palloc.c +++ b/src/core/ngx_palloc.c @@ -68,7 +68,7 @@ ngx_destroy_pool(ngx_pool_t *pool) /* * we could allocate the pool->log from this pool - * so we can not use this log while the free()ing the pool + * so we cannot use this log while free()ing the pool */ for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
--- a/src/core/ngx_string.c +++ b/src/core/ngx_string.c @@ -381,7 +381,7 @@ ngx_vslprintf(u_char *buf, u_char *last, /* * (int64_t) cast is required for msvc6: - * it can not convert uint64_t to double + * it cannot convert uint64_t to double */ ui64 = (uint64_t) ((f - (int64_t) ui64) * scale + 0.5);
--- a/src/event/modules/ngx_epoll_module.c +++ b/src/event/modules/ngx_epoll_module.c @@ -86,6 +86,7 @@ int eventfd(u_int initval) typedef struct { ngx_uint_t events; + ngx_uint_t aio_requests; } ngx_epoll_conf_t; @@ -133,6 +134,13 @@ static ngx_command_t ngx_epoll_commands offsetof(ngx_epoll_conf_t, events), NULL }, + { ngx_string("worker_aio_requests"), + NGX_EVENT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + 0, + offsetof(ngx_epoll_conf_t, aio_requests), + NULL }, + ngx_null_command }; @@ -184,7 +192,7 @@ ngx_module_t ngx_epoll_module = { * into single eventfd() function with different number of parameters. */ -static long +static int io_setup(u_int nr_reqs, aio_context_t *ctx) { return syscall(SYS_io_setup, nr_reqs, ctx); @@ -198,13 +206,81 @@ io_destroy(aio_context_t ctx) } -static long +static int io_getevents(aio_context_t ctx, long min_nr, long nr, struct io_event *events, struct timespec *tmo) { return syscall(SYS_io_getevents, ctx, min_nr, nr, events, tmo); } + +static void +ngx_epoll_aio_init(ngx_cycle_t *cycle, ngx_epoll_conf_t *epcf) +{ + int n; + struct epoll_event ee; + + ngx_eventfd = syscall(SYS_eventfd, 0); + + if (ngx_eventfd == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "eventfd() failed"); + ngx_file_aio = 0; + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "eventfd: %d", ngx_eventfd); + + n = 1; + + if (ioctl(ngx_eventfd, FIONBIO, &n) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "ioctl(eventfd, FIONBIO) failed"); + goto failed; + } + + if (io_setup(epcf->aio_requests, &ngx_aio_ctx) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "io_setup() failed"); + goto failed; + } + + ngx_eventfd_event.data = &ngx_eventfd_conn; + ngx_eventfd_event.handler = ngx_epoll_eventfd_handler; + ngx_eventfd_event.log = cycle->log; + ngx_eventfd_event.active = 1; + ngx_eventfd_conn.fd = ngx_eventfd; + ngx_eventfd_conn.read = &ngx_eventfd_event; + ngx_eventfd_conn.log = cycle->log; + + ee.events = EPOLLIN|EPOLLET; + ee.data.ptr = &ngx_eventfd_conn; + + if (epoll_ctl(ep, EPOLL_CTL_ADD, ngx_eventfd, &ee) != -1) { + return; + } + + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "epoll_ctl(EPOLL_CTL_ADD, eventfd) failed"); + + if (io_destroy(ngx_aio_ctx) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "io_destroy() failed"); + } + +failed: + + if (close(ngx_eventfd) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "eventfd close() failed"); + } + + ngx_eventfd = -1; + ngx_aio_ctx = 0; + ngx_file_aio = 0; +} + #endif @@ -225,52 +301,9 @@ ngx_epoll_init(ngx_cycle_t *cycle, ngx_m } #if (NGX_HAVE_FILE_AIO) - { - int n; - struct epoll_event ee; - - ngx_eventfd = syscall(SYS_eventfd, 0); - - if (ngx_eventfd == -1) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, - "eventfd() failed"); - return NGX_ERROR; - } - - n = 1; - - if (ioctl(ngx_eventfd, FIONBIO, &n) == -1) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, - "ioctl(eventfd, FIONBIO) failed"); - } - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, - "eventfd: %d", ngx_eventfd); - n = io_setup(1024, &ngx_aio_ctx); - - if (n != 0) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, -n, "io_setup() failed"); - return NGX_ERROR; - } + ngx_epoll_aio_init(cycle, epcf); - ngx_eventfd_event.data = &ngx_eventfd_conn; - ngx_eventfd_event.handler = ngx_epoll_eventfd_handler; - ngx_eventfd_event.log = cycle->log; - ngx_eventfd_event.active = 1; - ngx_eventfd_conn.fd = ngx_eventfd; - ngx_eventfd_conn.read = &ngx_eventfd_event; - ngx_eventfd_conn.log = cycle->log; - - ee.events = EPOLLIN|EPOLLET; - ee.data.ptr = &ngx_eventfd_conn; - - if (epoll_ctl(ep, EPOLL_CTL_ADD, ngx_eventfd, &ee) == -1) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, - "epoll_ctl(EPOLL_CTL_ADD, eventfd) failed"); - return NGX_ERROR; - } - } #endif } @@ -316,9 +349,19 @@ ngx_epoll_done(ngx_cycle_t *cycle) #if (NGX_HAVE_FILE_AIO) - if (io_destroy(ngx_aio_ctx) != 0) { - ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, - "io_destroy() failed"); + if (ngx_eventfd != -1) { + + if (io_destroy(ngx_aio_ctx) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "io_destroy() failed"); + } + + if (close(ngx_eventfd) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "eventfd close() failed"); + } + + ngx_eventfd = -1; } ngx_aio_ctx = 0; @@ -667,8 +710,8 @@ ngx_epoll_process_events(ngx_cycle_t *cy static void ngx_epoll_eventfd_handler(ngx_event_t *ev) { - int n; - long i, events; + int n, events; + long i; uint64_t ready; ngx_err_t err; ngx_event_t *e; @@ -738,8 +781,9 @@ ngx_epoll_eventfd_handler(ngx_event_t *e return; } - /* events < 0 */ - ngx_log_error(NGX_LOG_ALERT, ev->log, -events, "io_getevents() failed"); + /* events == -1 */ + ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, + "io_getevents() failed"); return; } } @@ -758,6 +802,7 @@ ngx_epoll_create_conf(ngx_cycle_t *cycle } epcf->events = NGX_CONF_UNSET; + epcf->aio_requests = NGX_CONF_UNSET; return epcf; } @@ -769,6 +814,7 @@ ngx_epoll_init_conf(ngx_cycle_t *cycle, ngx_epoll_conf_t *epcf = conf; ngx_conf_init_uint_value(epcf->events, 512); + ngx_conf_init_uint_value(epcf->aio_requests, 32); return NGX_CONF_OK; }
--- a/src/event/ngx_event.c +++ b/src/event/ngx_event.c @@ -1027,7 +1027,7 @@ ngx_event_use(ngx_conf_t *cf, ngx_comman "when the server runs without a master process " "the \"%V\" event type must be the same as " "in previous configuration - \"%s\" " - "and it can not be changed on the fly, " + "and it cannot be changed on the fly, " "to change it you need to stop server " "and start it again", &value[1], old_ecf->name);
--- a/src/event/ngx_event_pipe.c +++ b/src/event/ngx_event_pipe.c @@ -392,8 +392,32 @@ ngx_event_pipe_read_upstream(ngx_event_p cl->buf->file_last - cl->buf->file_pos); } + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe length: %O", p->length); + #endif + if (p->free_raw_bufs && p->length != -1) { + cl = p->free_raw_bufs; + + if (cl->buf->last - cl->buf->pos >= p->length) { + + /* STUB */ cl->buf->num = p->num++; + + if (p->input_filter(p, cl->buf) == NGX_ERROR) { + return NGX_ABORT; + } + + p->free_raw_bufs = cl->next; + ngx_free_chain(p->pool, cl); + } + } + + if (p->length == 0) { + p->upstream_done = 1; + p->read = 1; + } + if ((p->upstream_eof || p->upstream_error) && p->free_raw_bufs) { /* STUB */ p->free_raw_bufs->buf->num = p->num++; @@ -633,13 +657,13 @@ ngx_event_pipe_write_to_downstream(ngx_e rc = p->output_filter(p->output_ctx, out); + ngx_chain_update_chains(p->pool, &p->free, &p->busy, &out, p->tag); + if (rc == NGX_ERROR) { p->downstream_error = 1; return ngx_event_pipe_drain_chains(p); } - ngx_chain_update_chains(&p->free, &p->busy, &out, p->tag); - for (cl = p->free; cl; cl = cl->next) { if (cl->buf->temp_file) { @@ -848,6 +872,12 @@ ngx_event_pipe_copy_input_filter(ngx_eve } p->last_in = &cl->next; + if (p->length == -1) { + return NGX_OK; + } + + p->length -= b->last - b->pos; + return NGX_OK; }
--- a/src/event/ngx_event_pipe.h +++ b/src/event/ngx_event_pipe.h @@ -65,6 +65,7 @@ struct ngx_event_pipe_s { ssize_t busy_size; off_t read_length; + off_t length; off_t max_temp_file_size; ssize_t temp_file_write_size;
--- a/src/event/ngx_event_timer.c +++ b/src/event/ngx_event_timer.c @@ -103,11 +103,11 @@ ngx_event_expire_timers(void) if (ngx_threaded && ngx_trylock(ev->lock) == 0) { /* - * We can not change the timer of the event that is been - * handling by another thread. And we can not easy walk - * the rbtree to find a next expired timer so we exit the loop. - * However it should be rare case when the event that is - * been handling has expired timer. + * We cannot change the timer of the event that is being + * handled by another thread. And we cannot easy walk + * the rbtree to find next expired timer so we exit the loop. + * However, it should be a rare case when the event that is + * being handled has an expired timer. */ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,
--- a/src/http/modules/ngx_http_chunked_filter_module.c +++ b/src/http/modules/ngx_http_chunked_filter_module.c @@ -9,6 +9,12 @@ #include <ngx_http.h> +typedef struct { + ngx_chain_t *free; + ngx_chain_t *busy; +} ngx_http_chunked_filter_ctx_t; + + static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf); @@ -50,7 +56,8 @@ static ngx_http_output_body_filter_pt static ngx_int_t ngx_http_chunked_header_filter(ngx_http_request_t *r) { - ngx_http_core_loc_conf_t *clcf; + ngx_http_core_loc_conf_t *clcf; + ngx_http_chunked_filter_ctx_t *ctx; if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED || r->headers_out.status == NGX_HTTP_NO_CONTENT @@ -70,6 +77,14 @@ ngx_http_chunked_header_filter(ngx_http_ if (clcf->chunked_transfer_encoding) { r->chunked = 1; + ctx = ngx_pcalloc(r->pool, + sizeof(ngx_http_chunked_filter_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module); + } else { r->keepalive = 0; } @@ -83,17 +98,21 @@ ngx_http_chunked_header_filter(ngx_http_ static ngx_int_t ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { - u_char *chunk; - off_t size; - ngx_buf_t *b; - ngx_chain_t out, tail, *cl, *tl, **ll; + u_char *chunk; + off_t size; + ngx_int_t rc; + ngx_buf_t *b; + ngx_chain_t *out, *cl, *tl, **ll; + ngx_http_chunked_filter_ctx_t *ctx; if (in == NULL || !r->chunked || r->header_only) { return ngx_http_next_body_filter(r, in); } - out.buf = NULL; - ll = &out.next; + ctx = ngx_http_get_module_ctx(r, ngx_http_chunked_filter_module); + + out = NULL; + ll = &out; size = 0; cl = in; @@ -127,31 +146,46 @@ ngx_http_chunked_body_filter(ngx_http_re } if (size) { - b = ngx_calloc_buf(r->pool); - if (b == NULL) { + tl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (tl == NULL) { return NGX_ERROR; } - /* the "0000000000000000" is 64-bit hexadimal string */ + b = tl->buf; + chunk = b->start; + + if (chunk == NULL) { + /* the "0000000000000000" is 64-bit hexadecimal string */ - chunk = ngx_palloc(r->pool, sizeof("0000000000000000" CRLF) - 1); - if (chunk == NULL) { - return NGX_ERROR; + chunk = ngx_palloc(r->pool, sizeof("0000000000000000" CRLF) - 1); + if (chunk == NULL) { + return NGX_ERROR; + } + + b->start = chunk; + b->end = chunk + sizeof("0000000000000000" CRLF) - 1; } + b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; + b->memory = 0; b->temporary = 1; b->pos = chunk; b->last = ngx_sprintf(chunk, "%xO" CRLF, size); - out.buf = b; + tl->next = out; + out = tl; } if (cl->buf->last_buf) { - b = ngx_calloc_buf(r->pool); - if (b == NULL) { + tl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (tl == NULL) { return NGX_ERROR; } + b = tl->buf; + + b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; + b->temporary = 0; b->memory = 1; b->last_buf = 1; b->pos = (u_char *) CRLF "0" CRLF CRLF; @@ -159,35 +193,38 @@ ngx_http_chunked_body_filter(ngx_http_re cl->buf->last_buf = 0; + *ll = tl; + if (size == 0) { b->pos += 2; - out.buf = b; - out.next = NULL; - - return ngx_http_next_body_filter(r, &out); } - } else { - if (size == 0) { - *ll = NULL; - return ngx_http_next_body_filter(r, out.next); - } - - b = ngx_calloc_buf(r->pool); - if (b == NULL) { + } else if (size > 0) { + tl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (tl == NULL) { return NGX_ERROR; } + b = tl->buf; + + b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; + b->temporary = 0; b->memory = 1; b->pos = (u_char *) CRLF; b->last = b->pos + 2; + + *ll = tl; + + } else { + *ll = NULL; } - tail.buf = b; - tail.next = NULL; - *ll = &tail; + rc = ngx_http_next_body_filter(r, out); - return ngx_http_next_body_filter(r, &out); + ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out, + (ngx_buf_tag_t) &ngx_http_chunked_filter_module); + + return rc; }
--- a/src/http/modules/ngx_http_dav_module.c +++ b/src/http/modules/ngx_http_dav_module.c @@ -158,7 +158,7 @@ ngx_http_dav_handler(ngx_http_request_t if (r->uri.data[r->uri.len - 1] == '/') { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "can not PUT to a collection"); + "cannot PUT to a collection"); return NGX_HTTP_CONFLICT; }
--- a/src/http/modules/ngx_http_fastcgi_module.c +++ b/src/http/modules/ngx_http_fastcgi_module.c @@ -26,6 +26,8 @@ typedef struct { ngx_hash_t headers_hash; ngx_uint_t header_params; + ngx_flag_t keep_conn; + #if (NGX_HTTP_CACHE) ngx_http_complex_value_t cache_key; #endif @@ -77,6 +79,8 @@ typedef struct { #define NGX_HTTP_FASTCGI_RESPONDER 1 +#define NGX_HTTP_FASTCGI_KEEP_CONN 1 + #define NGX_HTTP_FASTCGI_BEGIN_REQUEST 1 #define NGX_HTTP_FASTCGI_ABORT_REQUEST 2 #define NGX_HTTP_FASTCGI_END_REQUEST 3 @@ -130,6 +134,7 @@ static ngx_int_t ngx_http_fastcgi_create static ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r); static ngx_int_t ngx_http_fastcgi_reinit_request(ngx_http_request_t *r); static ngx_int_t ngx_http_fastcgi_process_header(ngx_http_request_t *r); +static ngx_int_t ngx_http_fastcgi_input_filter_init(void *data); static ngx_int_t ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf); static ngx_int_t ngx_http_fastcgi_process_record(ngx_http_request_t *r, @@ -437,6 +442,13 @@ static ngx_command_t ngx_http_fastcgi_c offsetof(ngx_http_fastcgi_loc_conf_t, catch_stderr), NULL }, + { ngx_string("fastcgi_keep_conn"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_fastcgi_loc_conf_t, keep_conn), + NULL }, + ngx_null_command }; @@ -600,6 +612,8 @@ ngx_http_fastcgi_handler(ngx_http_reques u->pipe->input_filter = ngx_http_fastcgi_input_filter; u->pipe->input_ctx = r; + u->input_filter_init = ngx_http_fastcgi_input_filter_init; + rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { @@ -841,6 +855,9 @@ ngx_http_fastcgi_create_request(ngx_http cl->buf = b; + ngx_http_fastcgi_request_start.br.flags = + flcf->keep_conn ? NGX_HTTP_FASTCGI_KEEP_CONN : 0; + ngx_memcpy(b->pos, &ngx_http_fastcgi_request_start, sizeof(ngx_http_fastcgi_request_start_t)); @@ -1574,14 +1591,30 @@ ngx_http_fastcgi_process_header(ngx_http static ngx_int_t +ngx_http_fastcgi_input_filter_init(void *data) +{ + ngx_http_request_t *r = data; + ngx_http_fastcgi_loc_conf_t *flcf; + + flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); + + r->upstream->pipe->length = flcf->keep_conn ? + (off_t) sizeof(ngx_http_fastcgi_header_t) : -1; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) { - u_char *m, *msg; - ngx_int_t rc; - ngx_buf_t *b, **prev; - ngx_chain_t *cl; - ngx_http_request_t *r; - ngx_http_fastcgi_ctx_t *f; + u_char *m, *msg; + ngx_int_t rc; + ngx_buf_t *b, **prev; + ngx_chain_t *cl; + ngx_http_request_t *r; + ngx_http_fastcgi_ctx_t *f; + ngx_http_fastcgi_loc_conf_t *flcf; if (buf->pos == buf->last) { return NGX_OK; @@ -1589,6 +1622,7 @@ ngx_http_fastcgi_input_filter(ngx_event_ r = p->input_ctx; f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module); + flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); b = NULL; prev = &buf->shadow; @@ -1611,7 +1645,10 @@ ngx_http_fastcgi_input_filter(ngx_event_ if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) { f->state = ngx_http_fastcgi_st_version; - p->upstream_done = 1; + + if (!flcf->keep_conn) { + p->upstream_done = 1; + } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0, "http fastcgi closed stdout"); @@ -1623,6 +1660,10 @@ ngx_http_fastcgi_input_filter(ngx_event_ f->state = ngx_http_fastcgi_st_version; p->upstream_done = 1; + if (flcf->keep_conn) { + r->upstream->keepalive = 1; + } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0, "http fastcgi sent end request"); @@ -1703,8 +1744,10 @@ ngx_http_fastcgi_input_filter(ngx_event_ } if (p->free) { - b = p->free->buf; - p->free = p->free->next; + cl = p->free; + b = cl->buf; + p->free = cl->next; + ngx_free_chain(p->pool, cl); } else { b = ngx_alloc_buf(p->pool); @@ -1781,6 +1824,23 @@ ngx_http_fastcgi_input_filter(ngx_event_ } + if (flcf->keep_conn) { + + /* set p->length, minimal amount of data we want to see */ + + if (f->state < ngx_http_fastcgi_st_data) { + p->length = 1; + + } else if (f->state == ngx_http_fastcgi_st_padding) { + p->length = f->padding; + + } else { + /* ngx_http_fastcgi_st_data */ + + p->length = f->length; + } + } + if (b) { b->shadow = buf; b->last_shadow = 1; @@ -2011,6 +2071,8 @@ ngx_http_fastcgi_create_loc_conf(ngx_con conf->catch_stderr = NGX_CONF_UNSET_PTR; + conf->keep_conn = NGX_CONF_UNSET; + ngx_str_set(&conf->upstream.module, "fastcgi"); return conf; @@ -2254,6 +2316,8 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf ngx_conf_merge_ptr_value(conf->catch_stderr, prev->catch_stderr, NULL); + ngx_conf_merge_value(conf->keep_conn, prev->keep_conn, 0); + ngx_conf_merge_str_value(conf->index, prev->index, "");
--- a/src/http/modules/ngx_http_gzip_filter_module.c +++ b/src/http/modules/ngx_http_gzip_filter_module.c @@ -378,7 +378,7 @@ ngx_http_gzip_body_filter(ngx_http_reque cl = NULL; - ngx_chain_update_chains(&ctx->free, &ctx->busy, &cl, + ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl, (ngx_buf_tag_t) &ngx_http_gzip_filter_module); ctx->nomem = 0; } @@ -448,7 +448,7 @@ ngx_http_gzip_body_filter(ngx_http_reque ngx_http_gzip_filter_free_copy_buf(r, ctx); - ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out, + ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out, (ngx_buf_tag_t) &ngx_http_gzip_filter_module); ctx->last_out = &ctx->out;
--- a/src/http/modules/ngx_http_headers_filter_module.c +++ b/src/http/modules/ngx_http_headers_filter_module.c @@ -507,7 +507,7 @@ ngx_http_headers_expires(ngx_conf_t *cf, minus = 0; if (hcf->expires == NGX_HTTP_EXPIRES_MODIFIED) { - return "daily time can not be used with \"modified\" parameter"; + return "daily time cannot be used with \"modified\" parameter"; } hcf->expires = NGX_HTTP_EXPIRES_DAILY;
--- a/src/http/modules/ngx_http_log_module.c +++ b/src/http/modules/ngx_http_log_module.c @@ -960,7 +960,7 @@ buffer: if (log->script) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "buffered logs can not have variables in name"); + "buffered logs cannot have variables in name"); return NGX_CONF_ERROR; }
--- a/src/http/modules/ngx_http_memcached_module.c +++ b/src/http/modules/ngx_http_memcached_module.c @@ -344,8 +344,8 @@ found: while (*p && *p++ != CR) { /* void */ } - r->headers_out.content_length_n = ngx_atoof(len, p - len - 1); - if (r->headers_out.content_length_n == -1) { + u->headers_in.content_length_n = ngx_atoof(len, p - len - 1); + if (u->headers_in.content_length_n == -1) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "memcached sent invalid length in response \"%V\" " "for key \"%V\"", @@ -366,6 +366,7 @@ found: u->headers_in.status_n = 404; u->state->status = 404; + u->keepalive = 1; return NGX_OK; } @@ -407,7 +408,7 @@ ngx_http_memcached_filter(void *data, ss u = ctx->request->upstream; b = &u->buffer; - if (u->length == ctx->rest) { + if (u->length == (ssize_t) ctx->rest) { if (ngx_strncmp(b->last, ngx_http_memcached_end + NGX_HTTP_MEMCACHED_END - ctx->rest, @@ -426,6 +427,10 @@ ngx_http_memcached_filter(void *data, ss u->length -= bytes; ctx->rest -= bytes; + if (u->length == 0) { + u->keepalive = 1; + } + return NGX_OK; } @@ -463,6 +468,13 @@ ngx_http_memcached_filter(void *data, ss if (ngx_strncmp(last, ngx_http_memcached_end, b->last - last) != 0) { ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0, "memcached sent invalid trailer"); + + b->last = last; + cl->buf->last = last; + u->length = 0; + ctx->rest = 0; + + return NGX_OK; } ctx->rest -= b->last - last; @@ -470,6 +482,10 @@ ngx_http_memcached_filter(void *data, ss cl->buf->last = last; u->length = ctx->rest; + if (u->length == 0) { + u->keepalive = 1; + } + return NGX_OK; }
--- a/src/http/modules/ngx_http_mp4_module.c +++ b/src/http/modules/ngx_http_mp4_module.c @@ -32,8 +32,10 @@ #define NGX_HTTP_MP4_STSZ_DATA 21 #define NGX_HTTP_MP4_STCO_ATOM 22 #define NGX_HTTP_MP4_STCO_DATA 23 - -#define NGX_HTTP_MP4_LAST_ATOM NGX_HTTP_MP4_STCO_DATA +#define NGX_HTTP_MP4_CO64_ATOM 24 +#define NGX_HTTP_MP4_CO64_DATA 25 + +#define NGX_HTTP_MP4_LAST_ATOM NGX_HTTP_MP4_CO64_DATA typedef struct { @@ -61,7 +63,7 @@ typedef struct { ngx_uint_t start_sample; ngx_uint_t start_chunk; ngx_uint_t chunk_samples; - ngx_uint_t chunk_samples_size; + uint64_t chunk_samples_size; off_t start_offset; size_t tkhd_size; @@ -96,8 +98,10 @@ typedef struct { ngx_buf_t stsc_data_buf; ngx_buf_t stsz_atom_buf; ngx_buf_t stsz_data_buf; - ngx_buf_t tsco_atom_buf; - ngx_buf_t tsco_data_buf; + ngx_buf_t stco_atom_buf; + ngx_buf_t stco_data_buf; + ngx_buf_t co64_atom_buf; + ngx_buf_t co64_data_buf; ngx_mp4_stsc_entry_t stsc_chunk_entry; } ngx_http_mp4_trak_t; @@ -268,6 +272,12 @@ static ngx_int_t ngx_http_mp4_update_stc ngx_http_mp4_trak_t *trak); static void ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4, ngx_http_mp4_trak_t *trak, int32_t adjustment); +static ngx_int_t ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4, + uint64_t atom_data_size); +static ngx_int_t ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4, + ngx_http_mp4_trak_t *trak); +static void ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4, + ngx_http_mp4_trak_t *trak, off_t adjustment); static char *ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static void *ngx_http_mp4_create_conf(ngx_conf_t *cf); static char *ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child); @@ -373,6 +383,7 @@ static ngx_http_mp4_atom_handler_t ngx_ { "stsc", ngx_http_mp4_read_stsc_atom }, { "stsz", ngx_http_mp4_read_stsz_atom }, { "stco", ngx_http_mp4_read_stco_atom }, + { "co64", ngx_http_mp4_read_co64_atom }, { NULL, NULL } }; @@ -676,8 +687,15 @@ ngx_http_mp4_process(ngx_http_mp4_file_t ngx_http_mp4_update_stsz_atom(mp4, &trak[i]); - if (ngx_http_mp4_update_stco_atom(mp4, &trak[i]) != NGX_OK) { - return NGX_ERROR; + if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) { + if (ngx_http_mp4_update_co64_atom(mp4, &trak[i]) != NGX_OK) { + return NGX_ERROR; + } + + } else { + if (ngx_http_mp4_update_stco_atom(mp4, &trak[i]) != NGX_OK) { + return NGX_ERROR; + } } ngx_http_mp4_update_stbl_atom(mp4, &trak[i]); @@ -721,7 +739,11 @@ ngx_http_mp4_process(ngx_http_mp4_file_t "mp4 adjustment:%D", adjustment); for (i = 0; i < mp4->trak.nelts; i++) { - ngx_http_mp4_adjust_stco_atom(mp4, &trak[i], (int32_t) adjustment); + if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) { + ngx_http_mp4_adjust_co64_atom(mp4, &trak[i], adjustment); + } else { + ngx_http_mp4_adjust_stco_atom(mp4, &trak[i], (int32_t) adjustment); + } } return NGX_OK; @@ -1702,7 +1724,6 @@ ngx_http_mp4_read_stsd_atom(ngx_http_mp4 { u_char *atom_header, *atom_table; size_t atom_size; - uint32_t entries; ngx_buf_t *atom; ngx_mp4_stsd_atom_t *stsd_atom; ngx_http_mp4_trak_t *trak; @@ -1718,10 +1739,9 @@ ngx_http_mp4_read_stsd_atom(ngx_http_mp4 ngx_mp4_set_32value(stsd_atom->size, atom_size); ngx_mp4_set_atom_name(stsd_atom, 's', 't', 's', 'd'); - entries = ngx_mp4_get_32value(stsd_atom->entries); - if ((uint64_t) (sizeof(ngx_mp4_stsd_atom_t) - sizeof(ngx_mp4_atom_header_t)) - > atom_data_size) { + > atom_data_size) + { ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, "\"%s\" mp4 stsd atom too large", mp4->file.name.data); @@ -2497,7 +2517,7 @@ ngx_http_mp4_update_stsz_atom(ngx_http_m } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, - "chunk samples sizes:%uD", trak->chunk_samples_size); + "chunk samples sizes:%uL", trak->chunk_samples_size); atom_size = sizeof(ngx_mp4_stsz_atom_t) + (data->last - data->pos); trak->size += atom_size; @@ -2554,12 +2574,12 @@ ngx_http_mp4_read_stco_atom(ngx_http_mp4 trak = ngx_mp4_last_trak(mp4); trak->chunks = entries; - atom = &trak->tsco_atom_buf; + atom = &trak->stco_atom_buf; atom->temporary = 1; atom->pos = atom_header; atom->last = atom_table; - data = &trak->tsco_data_buf; + data = &trak->stco_data_buf; data->temporary = 1; data->pos = atom_table; data->last = atom_end; @@ -2648,6 +2668,142 @@ ngx_http_mp4_adjust_stco_atom(ngx_http_m } +typedef struct { + u_char size[4]; + u_char name[4]; + u_char version[1]; + u_char flags[3]; + u_char entries[4]; +} ngx_mp4_co64_atom_t; + + +static ngx_int_t +ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) +{ + u_char *atom_header, *atom_table, *atom_end; + uint32_t entries; + ngx_buf_t *atom, *data; + ngx_mp4_co64_atom_t *co64_atom; + ngx_http_mp4_trak_t *trak; + + /* chunk offsets atom */ + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 co64 atom"); + + atom_header = ngx_mp4_atom_header(mp4); + co64_atom = (ngx_mp4_co64_atom_t *) atom_header; + ngx_mp4_set_atom_name(co64_atom, 'c', 'o', '6', '4'); + + entries = ngx_mp4_get_32value(co64_atom->entries); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "chunks:%uD", entries); + + atom_table = atom_header + sizeof(ngx_mp4_co64_atom_t); + atom_end = atom_table + entries * sizeof(uint64_t); + + if ((uint64_t) (atom_end - co64_atom->version) > atom_data_size) { + ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, + "\"%s\" mp4 co64 atom too large", mp4->file.name.data); + return NGX_ERROR; + } + + trak = ngx_mp4_last_trak(mp4); + trak->chunks = entries; + + atom = &trak->co64_atom_buf; + atom->temporary = 1; + atom->pos = atom_header; + atom->last = atom_table; + + data = &trak->co64_data_buf; + data->temporary = 1; + data->pos = atom_table; + data->last = atom_end; + + trak->out[NGX_HTTP_MP4_CO64_ATOM].buf = atom; + trak->out[NGX_HTTP_MP4_CO64_DATA].buf = data; + + ngx_mp4_atom_next(mp4, atom_data_size); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4, + ngx_http_mp4_trak_t *trak) +{ + size_t atom_size; + ngx_buf_t *atom, *data; + ngx_mp4_co64_atom_t *co64_atom; + + /* + * mdia.minf.stbl.co64 updating requires trak->start_chunk + * from mdia.minf.stbl.stsc which depends on value from mdia.mdhd + * atom which may reside after mdia.minf + */ + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "mp4 co64 atom update"); + + data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf; + + if (data == NULL) { + ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, + "no mp4 co64 atoms were found in \"%s\"", + mp4->file.name.data); + return NGX_ERROR; + } + + data->pos += trak->start_chunk * sizeof(uint64_t); + atom_size = sizeof(ngx_mp4_co64_atom_t) + (data->last - data->pos); + trak->size += atom_size; + + trak->start_offset = ngx_mp4_get_64value(data->pos); + trak->start_offset += trak->chunk_samples_size; + ngx_mp4_set_64value(data->pos, trak->start_offset); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "start chunk offset:%uL", trak->start_offset); + + atom = trak->out[NGX_HTTP_MP4_CO64_ATOM].buf; + co64_atom = (ngx_mp4_co64_atom_t *) atom->pos; + + ngx_mp4_set_32value(co64_atom->size, atom_size); + ngx_mp4_set_32value(co64_atom->entries, trak->chunks - trak->start_chunk); + + return NGX_OK; +} + + +static void +ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4, + ngx_http_mp4_trak_t *trak, off_t adjustment) +{ + uint64_t offset, *entry, *end; + ngx_buf_t *data; + + /* + * moov.trak.mdia.minf.stbl.co64 adjustment requires + * minimal start offset of all traks and new moov atom size + */ + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, + "mp4 co64 atom adjustment"); + + data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf; + entry = (uint64_t *) data->pos; + end = (uint64_t *) data->last; + + while (entry < end) { + offset = ngx_mp4_get_64value(entry); + offset += adjustment; + ngx_mp4_set_64value(entry, offset); + entry++; + } +} + + static char * ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
--- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -71,6 +71,8 @@ typedef struct { ngx_flag_t redirect; + ngx_uint_t http_version; + ngx_uint_t headers_hash_max_size; ngx_uint_t headers_hash_bucket_size; } ngx_http_proxy_loc_conf_t; @@ -80,6 +82,12 @@ typedef struct { ngx_http_status_t status; ngx_http_proxy_vars_t vars; size_t internal_body_length; + + ngx_uint_t state; + off_t size; + off_t length; + + ngx_uint_t head; /* unsigned head:1 */ } ngx_http_proxy_ctx_t; @@ -92,6 +100,15 @@ static ngx_int_t ngx_http_proxy_create_r static ngx_int_t ngx_http_proxy_reinit_request(ngx_http_request_t *r); static ngx_int_t ngx_http_proxy_process_status_line(ngx_http_request_t *r); static ngx_int_t ngx_http_proxy_process_header(ngx_http_request_t *r); +static ngx_int_t ngx_http_proxy_input_filter_init(void *data); +static ngx_int_t ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, + ngx_buf_t *buf); +static ngx_int_t ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, + ngx_buf_t *buf); +static ngx_int_t ngx_http_proxy_non_buffered_copy_filter(void *data, + ssize_t bytes); +static ngx_int_t ngx_http_proxy_non_buffered_chunked_filter(void *data, + ssize_t bytes); static void ngx_http_proxy_abort_request(ngx_http_request_t *r); static void ngx_http_proxy_finalize_request(ngx_http_request_t *r, ngx_int_t rc); @@ -157,6 +174,13 @@ static ngx_conf_bitmask_t ngx_http_prox }; +static ngx_conf_enum_t ngx_http_proxy_http_version[] = { + { ngx_string("1.0"), NGX_HTTP_VERSION_10 }, + { ngx_string("1.1"), NGX_HTTP_VERSION_11 }, + { ngx_null_string, 0 } +}; + + ngx_module_t ngx_http_proxy_module; @@ -432,6 +456,13 @@ static ngx_command_t ngx_http_proxy_com offsetof(ngx_http_proxy_loc_conf_t, upstream.ignore_headers), &ngx_http_upstream_ignore_headers_masks }, + { ngx_string("proxy_http_version"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_enum_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, http_version), + &ngx_http_proxy_http_version }, + #if (NGX_HTTP_SSL) { ngx_string("proxy_ssl_session_reuse"), @@ -479,6 +510,7 @@ ngx_module_t ngx_http_proxy_module = { static char ngx_http_proxy_version[] = " HTTP/1.0" CRLF; +static char ngx_http_proxy_version_11[] = " HTTP/1.1" CRLF; static ngx_keyval_t ngx_http_proxy_headers[] = { @@ -486,6 +518,7 @@ static ngx_keyval_t ngx_http_proxy_head { ngx_string("Connection"), ngx_string("close") }, { ngx_string("Keep-Alive"), ngx_string("") }, { ngx_string("Expect"), ngx_string("") }, + { ngx_string("Upgrade"), ngx_string("") }, { ngx_null_string, ngx_null_string } }; @@ -610,7 +643,12 @@ ngx_http_proxy_handler(ngx_http_request_ return NGX_HTTP_INTERNAL_SERVER_ERROR; } - u->pipe->input_filter = ngx_event_pipe_copy_input_filter; + u->pipe->input_filter = ngx_http_proxy_copy_filter; + u->pipe->input_ctx = r; + + u->input_filter_init = ngx_http_proxy_input_filter_init; + u->input_filter = ngx_http_proxy_non_buffered_copy_filter; + u->input_filter_ctx = r; u->accel = 1; @@ -866,14 +904,20 @@ ngx_http_proxy_create_request(ngx_http_r method.len++; } + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + + if (method.len == 5 + && ngx_strncasecmp(method.data, (u_char *) "HEAD ", 5) == 0) + { + ctx->head = 1; + } + len = method.len + sizeof(ngx_http_proxy_version) - 1 + sizeof(CRLF) - 1; escape = 0; loc_len = 0; unparsed_uri = 0; - ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); - if (plcf->proxy_lengths) { uri_len = ctx->vars.uri.len; @@ -1009,8 +1053,14 @@ ngx_http_proxy_create_request(ngx_http_r u->uri.len = b->last - u->uri.data; - b->last = ngx_cpymem(b->last, ngx_http_proxy_version, - sizeof(ngx_http_proxy_version) - 1); + if (plcf->http_version == NGX_HTTP_VERSION_11) { + b->last = ngx_cpymem(b->last, ngx_http_proxy_version_11, + sizeof(ngx_http_proxy_version_11) - 1); + + } else { + b->last = ngx_cpymem(b->last, ngx_http_proxy_version, + sizeof(ngx_http_proxy_version) - 1); + } ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); @@ -1158,8 +1208,11 @@ ngx_http_proxy_reinit_request(ngx_http_r ctx->status.count = 0; ctx->status.start = NULL; ctx->status.end = NULL; + ctx->state = 0; r->upstream->process_header = ngx_http_proxy_process_status_line; + r->upstream->pipe->input_filter = ngx_http_proxy_copy_filter; + r->upstream->input_filter = ngx_http_proxy_non_buffered_copy_filter; r->state = 0; return NGX_OK; @@ -1210,6 +1263,7 @@ ngx_http_proxy_process_status_line(ngx_h r->http_version = NGX_HTTP_VERSION_9; u->state->status = NGX_HTTP_OK; + u->headers_in.connection_close = 1; return NGX_OK; } @@ -1234,6 +1288,10 @@ ngx_http_proxy_process_status_line(ngx_h "http proxy status %ui \"%V\"", u->headers_in.status_n, &u->headers_in.status_line); + if (ctx->status.http_version < NGX_HTTP_VERSION_11) { + u->headers_in.connection_close = 1; + } + u->process_header = ngx_http_proxy_process_header; return ngx_http_proxy_process_header(r); @@ -1245,6 +1303,8 @@ ngx_http_proxy_process_header(ngx_http_r { ngx_int_t rc; ngx_table_elt_t *h; + ngx_http_upstream_t *u; + ngx_http_proxy_ctx_t *ctx; ngx_http_upstream_header_t *hh; ngx_http_upstream_main_conf_t *umcf; @@ -1340,6 +1400,30 @@ ngx_http_proxy_process_header(ngx_http_r h->lowcase_key = (u_char *) "date"; } + /* clear content length if response is chunked */ + + u = r->upstream; + + if (u->headers_in.chunked) { + u->headers_in.content_length_n = -1; + } + + /* + * set u->keepalive if response has no body; this allows to keep + * connections alive in case of r->header_only or X-Accel-Redirect + */ + + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + + if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT + || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED + || ctx->head + || (!u->headers_in.chunked + && u->headers_in.content_length_n == 0)) + { + u->keepalive = !u->headers_in.connection_close; + } + return NGX_OK; } @@ -1357,6 +1441,690 @@ ngx_http_proxy_process_header(ngx_http_r } +static ngx_int_t +ngx_http_proxy_input_filter_init(void *data) +{ + ngx_http_request_t *r = data; + ngx_http_upstream_t *u; + ngx_http_proxy_ctx_t *ctx; + + u = r->upstream; + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy filter init s:%d h:%d c:%d l:%O", + u->headers_in.status_n, ctx->head, u->headers_in.chunked, + u->headers_in.content_length_n); + + /* as per RFC2616, 4.4 Message Length */ + + if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT + || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED + || ctx->head) + { + /* 1xx, 204, and 304 and replies to HEAD requests */ + /* no 1xx since we don't send Expect and Upgrade */ + + u->pipe->length = 0; + u->length = 0; + u->keepalive = !u->headers_in.connection_close; + + } else if (u->headers_in.chunked) { + /* chunked */ + + u->pipe->input_filter = ngx_http_proxy_chunked_filter; + u->pipe->length = 3; /* "0" LF LF */ + + u->input_filter = ngx_http_proxy_non_buffered_chunked_filter; + u->length = -1; + + } else if (u->headers_in.content_length_n == 0) { + /* empty body: special case as filter won't be called */ + + u->pipe->length = 0; + u->length = 0; + u->keepalive = !u->headers_in.connection_close; + + } else { + /* content length or connection close */ + + u->pipe->length = u->headers_in.content_length_n; + u->length = u->headers_in.content_length_n; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) +{ + ngx_buf_t *b; + ngx_chain_t *cl; + ngx_http_request_t *r; + + if (buf->pos == buf->last) { + return NGX_OK; + } + + if (p->free) { + cl = p->free; + b = cl->buf; + p->free = cl->next; + ngx_free_chain(p->pool, cl); + + } else { + b = ngx_alloc_buf(p->pool); + if (b == NULL) { + return NGX_ERROR; + } + } + + ngx_memcpy(b, buf, sizeof(ngx_buf_t)); + b->shadow = buf; + b->tag = p->tag; + b->last_shadow = 1; + b->recycled = 1; + buf->shadow = b; + + cl = ngx_alloc_chain_link(p->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = NULL; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "input buf #%d", b->num); + + if (p->in) { + *p->last_in = cl; + } else { + p->in = cl; + } + p->last_in = &cl->next; + + if (p->length == -1) { + return NGX_OK; + } + + p->length -= b->last - b->pos; + + if (p->length == 0) { + r = p->input_ctx; + p->upstream_done = 1; + r->upstream->keepalive = !r->upstream->headers_in.connection_close; + + } else if (p->length < 0) { + r = p->input_ctx; + p->upstream_done = 1; + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent too many data"); + } + + return NGX_OK; +} + + +static ngx_inline ngx_int_t +ngx_http_proxy_parse_chunked(ngx_http_request_t *r, ngx_buf_t *buf) +{ + u_char *pos, ch, c; + ngx_int_t rc; + ngx_http_proxy_ctx_t *ctx; + enum { + sw_chunk_start = 0, + sw_chunk_size, + sw_chunk_extension, + sw_chunk_extension_almost_done, + sw_chunk_data, + sw_after_data, + sw_after_data_almost_done, + sw_last_chunk_extension, + sw_last_chunk_extension_almost_done, + sw_trailer, + sw_trailer_almost_done, + sw_trailer_header, + sw_trailer_header_almost_done + } state; + + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + state = ctx->state; + + if (state == sw_chunk_data && ctx->size == 0) { + state = sw_after_data; + } + + rc = NGX_AGAIN; + + for (pos = buf->pos; pos < buf->last; pos++) { + + ch = *pos; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy chunked byte: %02Xd s:%d", ch, state); + + switch (state) { + + case sw_chunk_start: + if (ch >= '0' && ch <= '9') { + state = sw_chunk_size; + ctx->size = ch - '0'; + break; + } + + c = (u_char) (ch | 0x20); + + if (c >= 'a' && c <= 'f') { + state = sw_chunk_size; + ctx->size = c - 'a' + 10; + break; + } + + goto invalid; + + case sw_chunk_size: + if (ch >= '0' && ch <= '9') { + ctx->size = ctx->size * 16 + (ch - '0'); + break; + } + + c = (u_char) (ch | 0x20); + + if (c >= 'a' && c <= 'f') { + ctx->size = ctx->size * 16 + (c - 'a' + 10); + break; + } + + if (ctx->size == 0) { + + switch (ch) { + case CR: + state = sw_last_chunk_extension_almost_done; + break; + case LF: + state = sw_trailer; + break; + case ';': + state = sw_last_chunk_extension; + break; + default: + goto invalid; + } + + break; + } + + switch (ch) { + case CR: + state = sw_chunk_extension_almost_done; + break; + case LF: + state = sw_chunk_data; + break; + case ';': + state = sw_chunk_extension; + break; + default: + goto invalid; + } + + break; + + case sw_chunk_extension: + switch (ch) { + case CR: + state = sw_chunk_extension_almost_done; + break; + case LF: + state = sw_chunk_data; + } + break; + + case sw_chunk_extension_almost_done: + if (ch == LF) { + state = sw_chunk_data; + break; + } + goto invalid; + + case sw_chunk_data: + rc = NGX_OK; + goto data; + + case sw_after_data: + switch (ch) { + case CR: + state = sw_after_data_almost_done; + break; + case LF: + state = sw_chunk_start; + } + break; + + case sw_after_data_almost_done: + if (ch == LF) { + state = sw_chunk_start; + break; + } + goto invalid; + + case sw_last_chunk_extension: + switch (ch) { + case CR: + state = sw_last_chunk_extension_almost_done; + break; + case LF: + state = sw_trailer; + } + break; + + case sw_last_chunk_extension_almost_done: + if (ch == LF) { + state = sw_trailer; + break; + } + goto invalid; + + case sw_trailer: + switch (ch) { + case CR: + state = sw_trailer_almost_done; + break; + case LF: + goto done; + default: + state = sw_trailer_header; + } + break; + + case sw_trailer_almost_done: + if (ch == LF) { + goto done; + } + goto invalid; + + case sw_trailer_header: + switch (ch) { + case CR: + state = sw_trailer_header_almost_done; + break; + case LF: + state = sw_trailer; + } + break; + + case sw_trailer_header_almost_done: + if (ch == LF) { + state = sw_trailer; + break; + } + goto invalid; + + } + } + +data: + + ctx->state = state; + buf->pos = pos; + + switch (state) { + + case sw_chunk_start: + ctx->length = 3 /* "0" LF LF */; + break; + case sw_chunk_size: + ctx->length = 2 /* LF LF */ + + (ctx->size ? ctx->size + 4 /* LF "0" LF LF */ : 0); + break; + case sw_chunk_extension: + case sw_chunk_extension_almost_done: + ctx->length = 1 /* LF */ + ctx->size + 4 /* LF "0" LF LF */; + break; + case sw_chunk_data: + ctx->length = ctx->size + 4 /* LF "0" LF LF */; + break; + case sw_after_data: + case sw_after_data_almost_done: + ctx->length = 4 /* LF "0" LF LF */; + break; + case sw_last_chunk_extension: + case sw_last_chunk_extension_almost_done: + ctx->length = 2 /* LF LF */; + break; + case sw_trailer: + case sw_trailer_almost_done: + ctx->length = 1 /* LF */; + break; + case sw_trailer_header: + case sw_trailer_header_almost_done: + ctx->length = 2 /* LF LF */; + break; + + } + + return rc; + +done: + + return NGX_DONE; + +invalid: + + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "upstream sent invalid chunked response"); + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) +{ + ngx_int_t rc; + ngx_buf_t *b, **prev; + ngx_chain_t *cl; + ngx_http_request_t *r; + ngx_http_proxy_ctx_t *ctx; + + if (buf->pos == buf->last) { + return NGX_OK; + } + + r = p->input_ctx; + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + + b = NULL; + prev = &buf->shadow; + + for ( ;; ) { + + rc = ngx_http_proxy_parse_chunked(r, buf); + + if (rc == NGX_OK) { + + /* a chunk has been parsed successfully */ + + if (p->free) { + cl = p->free; + b = cl->buf; + p->free = cl->next; + ngx_free_chain(p->pool, cl); + + } else { + b = ngx_alloc_buf(p->pool); + if (b == NULL) { + return NGX_ERROR; + } + } + + ngx_memzero(b, sizeof(ngx_buf_t)); + + b->pos = buf->pos; + b->start = buf->start; + b->end = buf->end; + b->tag = p->tag; + b->temporary = 1; + b->recycled = 1; + + *prev = b; + prev = &b->shadow; + + cl = ngx_alloc_chain_link(p->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = NULL; + + if (p->in) { + *p->last_in = cl; + } else { + p->in = cl; + } + p->last_in = &cl->next; + + /* STUB */ b->num = buf->num; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0, + "input buf #%d %p", b->num, b->pos); + + if (buf->last - buf->pos >= ctx->size) { + + buf->pos += ctx->size; + b->last = buf->pos; + ctx->size = 0; + + continue; + } + + ctx->size -= buf->last - buf->pos; + buf->pos = buf->last; + b->last = buf->last; + + continue; + } + + if (rc == NGX_DONE) { + + /* a whole response has been parsed successfully */ + + p->upstream_done = 1; + r->upstream->keepalive = !r->upstream->headers_in.connection_close; + + break; + } + + if (rc == NGX_AGAIN) { + + /* set p->length, minimal amount of data we want to see */ + + p->length = ctx->length; + + break; + } + + /* invalid response */ + + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "upstream sent invalid chunked response"); + + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy chunked state %d, length %d", + ctx->state, p->length); + + if (b) { + b->shadow = buf; + b->last_shadow = 1; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0, + "input buf %p %z", b->pos, b->last - b->pos); + + return NGX_OK; + } + + /* there is no data record in the buf, add it to free chain */ + + if (ngx_event_pipe_add_free_buf(p, buf) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_proxy_non_buffered_copy_filter(void *data, ssize_t bytes) +{ + ngx_http_request_t *r = data; + + ngx_buf_t *b; + ngx_chain_t *cl, **ll; + ngx_http_upstream_t *u; + + u = r->upstream; + + for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) { + ll = &cl->next; + } + + cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs); + if (cl == NULL) { + return NGX_ERROR; + } + + *ll = cl; + + cl->buf->flush = 1; + cl->buf->memory = 1; + + b = &u->buffer; + + cl->buf->pos = b->last; + b->last += bytes; + cl->buf->last = b->last; + cl->buf->tag = u->output.tag; + + if (u->length == -1) { + return NGX_OK; + } + + u->length -= bytes; + + if (u->length == 0) { + u->keepalive = !u->headers_in.connection_close; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes) +{ + ngx_http_request_t *r = data; + + ngx_int_t rc; + ngx_buf_t *b, *buf; + ngx_chain_t *cl, **ll; + ngx_http_upstream_t *u; + ngx_http_proxy_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + u = r->upstream; + buf = &u->buffer; + + buf->pos = buf->last; + buf->last += bytes; + + for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) { + ll = &cl->next; + } + + for ( ;; ) { + + rc = ngx_http_proxy_parse_chunked(r, buf); + + if (rc == NGX_OK) { + + /* a chunk has been parsed successfully */ + + cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs); + if (cl == NULL) { + return NGX_ERROR; + } + + *ll = cl; + ll = &cl->next; + + b = cl->buf; + + b->flush = 1; + b->memory = 1; + + b->pos = buf->pos; + b->tag = u->output.tag; + + if (buf->last - buf->pos >= ctx->size) { + buf->pos += ctx->size; + b->last = buf->pos; + ctx->size = 0; + + } else { + ctx->size -= buf->last - buf->pos; + buf->pos = buf->last; + b->last = buf->last; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy out buf %p %z", + b->pos, b->last - b->pos); + + continue; + } + + if (rc == NGX_DONE) { + + /* a whole response has been parsed successfully */ + + u->keepalive = !u->headers_in.connection_close; + u->length = 0; + + break; + } + + if (rc == NGX_AGAIN) { + break; + } + + /* invalid response */ + + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "upstream sent invalid chunked response"); + + return NGX_ERROR; + } + + /* provide continuous buffer for subrequests in memory */ + + if (r->subrequest_in_memory) { + + cl = u->out_bufs; + + if (cl) { + buf->pos = cl->buf->pos; + } + + buf->last = buf->pos; + + for (cl = u->out_bufs; cl; cl = cl->next) { + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy in memory %p-%p %uz", + cl->buf->pos, cl->buf->last, ngx_buf_size(cl->buf)); + + if (buf->last == cl->buf->pos) { + buf->last = cl->buf->last; + continue; + } + + buf->last = ngx_movemem(buf->last, cl->buf->pos, + cl->buf->last - cl->buf->pos); + + cl->buf->pos = buf->last - (cl->buf->last - cl->buf->pos); + cl->buf->last = buf->last; + } + } + + return NGX_OK; +} + + static void ngx_http_proxy_abort_request(ngx_http_request_t *r) { @@ -1705,6 +2473,8 @@ ngx_http_proxy_create_loc_conf(ngx_conf_ conf->redirect = NGX_CONF_UNSET; conf->upstream.change_buffering = 1; + conf->http_version = NGX_CONF_UNSET_UINT; + conf->headers_hash_max_size = NGX_CONF_UNSET_UINT; conf->headers_hash_bucket_size = NGX_CONF_UNSET_UINT; @@ -2008,6 +2778,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t } #endif + ngx_conf_merge_uint_value(conf->http_version, prev->http_version, + NGX_HTTP_VERSION_10); + ngx_conf_merge_uint_value(conf->headers_hash_max_size, prev->headers_hash_max_size, 512);
new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_upstream_keepalive_module.c @@ -0,0 +1,569 @@ + +/* + * Copyright (C) Maxim Dounin + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_http.h> + + +typedef struct { + ngx_uint_t max_cached; + ngx_uint_t single; /* unsigned:1 */ + + ngx_queue_t cache; + ngx_queue_t free; + + ngx_http_upstream_init_pt original_init_upstream; + ngx_http_upstream_init_peer_pt original_init_peer; + +} ngx_http_upstream_keepalive_srv_conf_t; + + +typedef struct { + ngx_http_upstream_keepalive_srv_conf_t *conf; + + ngx_http_upstream_t *upstream; + + void *data; + + ngx_event_get_peer_pt original_get_peer; + ngx_event_free_peer_pt original_free_peer; + +#if (NGX_HTTP_SSL) + ngx_event_set_peer_session_pt original_set_session; + ngx_event_save_peer_session_pt original_save_session; +#endif + + ngx_uint_t failed; /* unsigned:1 */ + +} ngx_http_upstream_keepalive_peer_data_t; + + +typedef struct { + ngx_http_upstream_keepalive_srv_conf_t *conf; + + ngx_queue_t queue; + ngx_connection_t *connection; + + socklen_t socklen; + u_char sockaddr[NGX_SOCKADDRLEN]; + +} ngx_http_upstream_keepalive_cache_t; + + +static ngx_int_t ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r, + ngx_http_upstream_srv_conf_t *us); +static ngx_int_t ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc, + void *data); +static void ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc, + void *data, ngx_uint_t state); + +static void ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev); +static void ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev); +static void ngx_http_upstream_keepalive_close(ngx_connection_t *c); + + +#if (NGX_HTTP_SSL) +static ngx_int_t ngx_http_upstream_keepalive_set_session( + ngx_peer_connection_t *pc, void *data); +static void ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc, + void *data); +#endif + +static void *ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf); +static char *ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +static ngx_command_t ngx_http_upstream_keepalive_commands[] = { + + { ngx_string("keepalive"), + NGX_HTTP_UPS_CONF|NGX_CONF_TAKE12, + ngx_http_upstream_keepalive, + 0, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_upstream_keepalive_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_http_upstream_keepalive_create_conf, /* create server configuration */ + NULL, /* merge server configuration */ + + NULL, /* create location configuration */ + NULL /* merge location configuration */ +}; + + +ngx_module_t ngx_http_upstream_keepalive_module = { + NGX_MODULE_V1, + &ngx_http_upstream_keepalive_module_ctx, /* module context */ + ngx_http_upstream_keepalive_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_int_t +ngx_http_upstream_init_keepalive(ngx_conf_t *cf, + ngx_http_upstream_srv_conf_t *us) +{ + ngx_uint_t i; + ngx_http_upstream_keepalive_srv_conf_t *kcf; + ngx_http_upstream_keepalive_cache_t *cached; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, + "init keepalive"); + + kcf = ngx_http_conf_upstream_srv_conf(us, + ngx_http_upstream_keepalive_module); + + if (kcf->original_init_upstream(cf, us) != NGX_OK) { + return NGX_ERROR; + } + + kcf->original_init_peer = us->peer.init; + + us->peer.init = ngx_http_upstream_init_keepalive_peer; + + /* allocate cache items and add to free queue */ + + cached = ngx_pcalloc(cf->pool, + sizeof(ngx_http_upstream_keepalive_cache_t) * kcf->max_cached); + if (cached == NULL) { + return NGX_ERROR; + } + + ngx_queue_init(&kcf->cache); + ngx_queue_init(&kcf->free); + + for (i = 0; i < kcf->max_cached; i++) { + ngx_queue_insert_head(&kcf->free, &cached[i].queue); + cached[i].conf = kcf; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r, + ngx_http_upstream_srv_conf_t *us) +{ + ngx_http_upstream_keepalive_peer_data_t *kp; + ngx_http_upstream_keepalive_srv_conf_t *kcf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "init keepalive peer"); + + kcf = ngx_http_conf_upstream_srv_conf(us, + ngx_http_upstream_keepalive_module); + + kp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_keepalive_peer_data_t)); + if (kp == NULL) { + return NGX_ERROR; + } + + if (kcf->original_init_peer(r, us) != NGX_OK) { + return NGX_ERROR; + } + + kp->conf = kcf; + kp->upstream = r->upstream; + kp->data = r->upstream->peer.data; + kp->original_get_peer = r->upstream->peer.get; + kp->original_free_peer = r->upstream->peer.free; + + r->upstream->peer.data = kp; + r->upstream->peer.get = ngx_http_upstream_get_keepalive_peer; + r->upstream->peer.free = ngx_http_upstream_free_keepalive_peer; + +#if (NGX_HTTP_SSL) + kp->original_set_session = r->upstream->peer.set_session; + kp->original_save_session = r->upstream->peer.save_session; + r->upstream->peer.set_session = ngx_http_upstream_keepalive_set_session; + r->upstream->peer.save_session = ngx_http_upstream_keepalive_save_session; +#endif + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc, void *data) +{ + ngx_http_upstream_keepalive_peer_data_t *kp = data; + ngx_http_upstream_keepalive_cache_t *item; + + ngx_int_t rc; + ngx_queue_t *q, *cache; + ngx_connection_t *c; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "get keepalive peer"); + + kp->failed = 0; + + /* single pool of cached connections */ + + if (kp->conf->single && !ngx_queue_empty(&kp->conf->cache)) { + + q = ngx_queue_head(&kp->conf->cache); + + item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue); + c = item->connection; + + ngx_queue_remove(q); + ngx_queue_insert_head(&kp->conf->free, q); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "get keepalive peer: using connection %p", c); + + c->idle = 0; + c->log = pc->log; + c->read->log = pc->log; + c->write->log = pc->log; + c->pool->log = pc->log; + + pc->connection = c; + pc->cached = 1; + + return NGX_DONE; + } + + rc = kp->original_get_peer(pc, kp->data); + + if (kp->conf->single || rc != NGX_OK) { + return rc; + } + + /* search cache for suitable connection */ + + cache = &kp->conf->cache; + + for (q = ngx_queue_head(cache); + q != ngx_queue_sentinel(cache); + q = ngx_queue_next(q)) + { + item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue); + c = item->connection; + + if (ngx_memn2cmp((u_char *) &item->sockaddr, (u_char *) pc->sockaddr, + item->socklen, pc->socklen) + == 0) + { + ngx_queue_remove(q); + ngx_queue_insert_head(&kp->conf->free, q); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "get keepalive peer: using connection %p", c); + + c->idle = 0; + c->log = pc->log; + c->read->log = pc->log; + c->write->log = pc->log; + c->pool->log = pc->log; + + pc->connection = c; + pc->cached = 1; + + return NGX_DONE; + } + } + + return NGX_OK; +} + + +static void +ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc, void *data, + ngx_uint_t state) +{ + ngx_http_upstream_keepalive_peer_data_t *kp = data; + ngx_http_upstream_keepalive_cache_t *item; + + ngx_queue_t *q; + ngx_connection_t *c; + ngx_http_upstream_t *u; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "free keepalive peer"); + + /* remember failed state - peer.free() may be called more than once */ + + if (state & NGX_PEER_FAILED) { + kp->failed = 1; + } + + /* cache valid connections */ + + u = kp->upstream; + c = pc->connection; + + if (kp->failed + || c == NULL + || c->read->eof + || c->read->error + || c->read->timedout + || c->write->error + || c->write->timedout) + { + goto invalid; + } + + if (!u->keepalive) { + goto invalid; + } + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + goto invalid; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "free keepalive peer: saving connection %p", c); + + if (ngx_queue_empty(&kp->conf->free)) { + + q = ngx_queue_last(&kp->conf->cache); + ngx_queue_remove(q); + + item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue); + + ngx_http_upstream_keepalive_close(item->connection); + + } else { + q = ngx_queue_head(&kp->conf->free); + ngx_queue_remove(q); + + item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue); + } + + item->connection = c; + ngx_queue_insert_head(&kp->conf->cache, q); + + pc->connection = NULL; + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + c->write->handler = ngx_http_upstream_keepalive_dummy_handler; + c->read->handler = ngx_http_upstream_keepalive_close_handler; + + c->data = item; + c->idle = 1; + c->log = ngx_cycle->log; + c->read->log = ngx_cycle->log; + c->write->log = ngx_cycle->log; + c->pool->log = ngx_cycle->log; + + item->socklen = pc->socklen; + ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen); + + if (c->read->ready) { + ngx_http_upstream_keepalive_close_handler(c->read); + } + +invalid: + + kp->original_free_peer(pc, kp->data, state); +} + + +static void +ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "keepalive dummy handler"); +} + + +static void +ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev) +{ + ngx_http_upstream_keepalive_srv_conf_t *conf; + ngx_http_upstream_keepalive_cache_t *item; + + int n; + char buf[1]; + ngx_connection_t *c; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "keepalive close handler"); + + c = ev->data; + + if (c->close) { + goto close; + } + + n = recv(c->fd, buf, 1, MSG_PEEK); + + if (n == -1 && ngx_socket_errno == NGX_EAGAIN) { + /* stale event */ + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + goto close; + } + + return; + } + +close: + + item = c->data; + conf = item->conf; + + ngx_http_upstream_keepalive_close(c); + + ngx_queue_remove(&item->queue); + ngx_queue_insert_head(&conf->free, &item->queue); +} + + +static void +ngx_http_upstream_keepalive_close(ngx_connection_t *c) +{ + +#if (NGX_HTTP_SSL) + + if (c->ssl) { + c->ssl->no_wait_shutdown = 1; + c->ssl->no_send_shutdown = 1; + + if (ngx_ssl_shutdown(c) == NGX_AGAIN) { + c->ssl->handler = ngx_http_upstream_keepalive_close; + return; + } + } + +#endif + + ngx_destroy_pool(c->pool); + ngx_close_connection(c); +} + + +#if (NGX_HTTP_SSL) + +static ngx_int_t +ngx_http_upstream_keepalive_set_session(ngx_peer_connection_t *pc, void *data) +{ + ngx_http_upstream_keepalive_peer_data_t *kp = data; + + return kp->original_set_session(pc, kp->data); +} + + +static void +ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc, void *data) +{ + ngx_http_upstream_keepalive_peer_data_t *kp = data; + + kp->original_save_session(pc, kp->data); + return; +} + +#endif + + +static void * +ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf) +{ + ngx_http_upstream_keepalive_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, + sizeof(ngx_http_upstream_keepalive_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * conf->original_init_upstream = NULL; + * conf->original_init_peer = NULL; + */ + + conf->max_cached = 1; + + return conf; +} + + +static char * +ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_upstream_srv_conf_t *uscf; + ngx_http_upstream_keepalive_srv_conf_t *kcf; + + ngx_int_t n; + ngx_str_t *value; + ngx_uint_t i; + + uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module); + + kcf = ngx_http_conf_upstream_srv_conf(uscf, + ngx_http_upstream_keepalive_module); + + kcf->original_init_upstream = uscf->peer.init_upstream + ? uscf->peer.init_upstream + : ngx_http_upstream_init_round_robin; + + uscf->peer.init_upstream = ngx_http_upstream_init_keepalive; + + /* read options */ + + value = cf->args->elts; + + n = ngx_atoi(value[1].data, value[1].len); + + if (n == NGX_ERROR || n == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%V\" in \"%V\" directive", + &value[1], &cmd->name); + return NGX_CONF_ERROR; + } + + kcf->max_cached = n; + + for (i = 2; i < cf->args->nelts; i++) { + + if (ngx_strcmp(value[i].data, "single") == 0) { + kcf->single = 1; + continue; + } + + goto invalid; + } + + return NGX_CONF_OK; + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + + return NGX_CONF_ERROR; +}
--- a/src/http/modules/perl/nginx.pm +++ b/src/http/modules/perl/nginx.pm @@ -48,7 +48,7 @@ our @EXPORT = qw( HTTP_INSUFFICIENT_STORAGE ); -our $VERSION = '1.1.3'; +our $VERSION = '1.1.4'; require XSLoader; XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1226,7 +1226,7 @@ ngx_http_add_addresses(ngx_conf_t *cf, n #endif /* - * we can not compare whole sockaddr struct's as kernel + * we cannot compare whole sockaddr struct's as kernel * may fill some fields in inherited sockaddr struct's */
--- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -52,6 +52,7 @@ struct ngx_http_log_ctx_s { typedef struct { + ngx_uint_t http_version; ngx_uint_t code; ngx_uint_t count; u_char *start;
--- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -402,7 +402,7 @@ static ngx_command_t ngx_http_core_comm { ngx_string("sendfile"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF - |NGX_CONF_TAKE1, + |NGX_CONF_FLAG, ngx_conf_set_flag_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_core_loc_conf_t, sendfile), @@ -639,7 +639,7 @@ static ngx_command_t ngx_http_core_comm NULL }, { ngx_string("chunked_transfer_encoding"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_core_loc_conf_t, chunked_transfer_encoding), @@ -1259,7 +1259,7 @@ ngx_http_core_try_files_phase(ngx_http_r tf++; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "try to use %s: \"%s\" \"%s\"", + "trying to use %s: \"%s\" \"%s\"", test_dir ? "dir" : "file", name, path.data); if (tf->lengths == NULL && tf->name.len == 0) { @@ -1897,7 +1897,7 @@ ngx_http_map_uri_to_path(ngx_http_reques if (alias && !r->valid_location) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "\"alias\" could not be used in location \"%V\" " + "\"alias\" cannot be used in location \"%V\" " "where URI was rewritten", &clcf->name); return NULL; } @@ -2468,7 +2468,7 @@ ngx_http_internal_redirect(ngx_http_requ if (r->uri_changes == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "rewrite or internal redirection cycle " - "while internal redirect to \"%V\"", uri); + "while internally redirecting to \"%V\"", uri); r->main->count++; ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); @@ -2862,7 +2862,7 @@ ngx_http_core_location(ngx_conf_t *cf, n if (pclcf->exact_match) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "location \"%V\" could not be inside " + "location \"%V\" cannot be inside " "the exact location \"%V\"", &clcf->name, &pclcf->name); return NGX_CONF_ERROR; @@ -2870,7 +2870,7 @@ ngx_http_core_location(ngx_conf_t *cf, n if (pclcf->named) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "location \"%V\" could not be inside " + "location \"%V\" cannot be inside " "the named location \"%V\"", &clcf->name, &pclcf->name); return NGX_CONF_ERROR; @@ -2878,8 +2878,8 @@ ngx_http_core_location(ngx_conf_t *cf, n if (clcf->named) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "named location \"%V\" must be " - "on server level only", + "named location \"%V\" can be " + "on the server level only", &clcf->name); return NGX_CONF_ERROR; } @@ -2948,7 +2948,7 @@ ngx_http_core_regex_location(ngx_conf_t #else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the using of the regex \"%V\" requires PCRE library", + "using regex \"%V\" requires PCRE library", regex); return NGX_ERROR; @@ -3024,9 +3024,9 @@ ngx_http_core_type(ngx_conf_t *cf, ngx_c type[n].value = content_type; ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "duplicate extention \"%V\", " + "duplicate extension \"%V\", " "content type: \"%V\", " - "old content type: \"%V\"", + "previous content type: \"%V\"", &value[i], content_type, old); continue; } @@ -3178,7 +3178,7 @@ ngx_http_core_merge_srv_conf(ngx_conf_t if (conf->large_client_header_buffers.size < conf->connection_pool_size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the \"large_client_header_buffers\" size must be " - "equal to or bigger than \"connection_pool_size\""); + "equal to or greater than \"connection_pool_size\""); return NGX_CONF_ERROR; } @@ -3191,7 +3191,7 @@ ngx_http_core_merge_srv_conf(ngx_conf_t prev->underscores_in_headers, 0); if (conf->server_names.nelts == 0) { - /* the array has 4 empty preallocated elements, so push can not fail */ + /* the array has 4 empty preallocated elements, so push cannot fail */ sn = ngx_array_push(&conf->server_names); #if (NGX_PCRE) sn->regex = NULL; @@ -3779,7 +3779,7 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx continue; #else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "bind ipv6only is not supported " + "ipv6only is not supported " "on this platform"); return NGX_CONF_ERROR; #endif @@ -3798,7 +3798,7 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the invalid \"%V\" parameter", &value[n]); + "invalid parameter \"%V\"", &value[n]); return NGX_CONF_ERROR; } @@ -3836,7 +3836,7 @@ ngx_http_core_server_name(ngx_conf_t *cf if (ngx_strchr(value[i].data, '/')) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "server name \"%V\" has strange symbols", + "server name \"%V\" has suspicious symbols", &value[i]); } @@ -3907,7 +3907,7 @@ ngx_http_core_server_name(ngx_conf_t *cf } #else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the using of the regex \"%V\" " + "using regex \"%V\" " "requires PCRE library", &value[i]); return NGX_CONF_ERROR; @@ -3939,7 +3939,7 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_c } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"%V\" directive is duplicate, " - "\"%s\" directive is specified before", + "\"%s\" directive was specified earlier", &cmd->name, clcf->alias ? "alias" : "root"); } @@ -3948,8 +3948,8 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_c if (clcf->named && alias) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the \"alias\" directive may not be used " - "inside named location"); + "the \"alias\" directive cannot be used " + "inside the named location"); return NGX_CONF_ERROR; } @@ -3960,7 +3960,7 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_c || ngx_strstr(value[1].data, "${document_root}")) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the $document_root variable may not be used " + "the $document_root variable cannot be used " "in the \"%V\" directive", &cmd->name); @@ -3971,7 +3971,7 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_c || ngx_strstr(value[1].data, "${realpath_root}")) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the $realpath_root variable may not be used " + "the $realpath_root variable cannot be used " "in the \"%V\" directive", &cmd->name); @@ -4430,7 +4430,7 @@ ngx_http_core_open_file_cache(ngx_conf_t if (max == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"open_file_cache\" must have \"max\" parameter"); + "\"open_file_cache\" must have the \"max\" parameter"); return NGX_CONF_ERROR; }
--- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -1403,6 +1403,7 @@ ngx_http_parse_status_line(ngx_http_requ return NGX_ERROR; } + r->http_major = ch - '0'; state = sw_major_digit; break; @@ -1417,6 +1418,7 @@ ngx_http_parse_status_line(ngx_http_requ return NGX_ERROR; } + r->http_major = r->http_major * 10 + ch - '0'; break; /* the first digit of minor HTTP version */ @@ -1425,6 +1427,7 @@ ngx_http_parse_status_line(ngx_http_requ return NGX_ERROR; } + r->http_minor = ch - '0'; state = sw_minor_digit; break; @@ -1439,6 +1442,7 @@ ngx_http_parse_status_line(ngx_http_requ return NGX_ERROR; } + r->http_minor = r->http_minor * 10 + ch - '0'; break; /* HTTP status code */ @@ -1516,6 +1520,7 @@ done: status->end = p; } + status->http_version = r->http_major * 1000 + r->http_minor; r->state = sw_start; return NGX_OK;
--- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -72,6 +72,8 @@ static void ngx_http_upstream_finalize_r static ngx_int_t ngx_http_upstream_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_process_content_length(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_process_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t @@ -89,6 +91,11 @@ static ngx_int_t ngx_http_upstream_proce ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_process_charset(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_process_connection(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t + ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_copy_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t @@ -96,8 +103,6 @@ static ngx_int_t ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_copy_content_type(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); -static ngx_int_t ngx_http_upstream_copy_content_length(ngx_http_request_t *r, - ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_copy_last_modified(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_rewrite_location(ngx_http_request_t *r, @@ -149,9 +154,9 @@ ngx_http_upstream_header_t ngx_http_ups ngx_http_upstream_copy_content_type, 0, 1 }, { ngx_string("Content-Length"), - ngx_http_upstream_process_header_line, + ngx_http_upstream_process_content_length, offsetof(ngx_http_upstream_headers_in_t, content_length), - ngx_http_upstream_copy_content_length, 0, 0 }, + ngx_http_upstream_ignore_header_line, 0, 0 }, { ngx_string("Date"), ngx_http_upstream_process_header_line, @@ -215,7 +220,7 @@ ngx_http_upstream_header_t ngx_http_ups offsetof(ngx_http_headers_out_t, accept_ranges), 1 }, { ngx_string("Connection"), - ngx_http_upstream_ignore_header_line, 0, + ngx_http_upstream_process_connection, 0, ngx_http_upstream_ignore_header_line, 0, 0 }, { ngx_string("Keep-Alive"), @@ -247,6 +252,10 @@ ngx_http_upstream_header_t ngx_http_ups ngx_http_upstream_process_charset, 0, ngx_http_upstream_copy_header_line, 0, 0 }, + { ngx_string("Transfer-Encoding"), + ngx_http_upstream_process_transfer_encoding, 0, + ngx_http_upstream_ignore_header_line, 0, 0 }, + #if (NGX_HTTP_GZIP) { ngx_string("Content-Encoding"), ngx_http_upstream_process_header_line, @@ -396,6 +405,8 @@ ngx_http_upstream_create(ngx_http_reques r->cache = NULL; #endif + u->headers_in.content_length_n = -1; + return NGX_OK; } @@ -800,6 +811,7 @@ ngx_http_upstream_cache_send(ngx_http_re u->buffer.pos += c->header_start; ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t)); + u->headers_in.content_length_n = -1; if (ngx_list_init(&u->headers_in.headers, r->pool, 8, sizeof(ngx_table_elt_t)) @@ -1136,8 +1148,20 @@ ngx_http_upstream_connect(ngx_http_reque c->sendfile &= r->connection->sendfile; u->output.sendfile = c->sendfile; - c->pool = r->pool; + if (c->pool == NULL) { + + /* we need separate pool here to be able to cache SSL connections */ + + c->pool = ngx_create_pool(128, r->connection->log); + if (c->pool == NULL) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + } + c->log = r->connection->log; + c->pool->log = c->log; c->read->log = c->log; c->write->log = c->log; @@ -1282,7 +1306,10 @@ ngx_http_upstream_reinit(ngx_http_reques return NGX_ERROR; } + u->keepalive = 0; + ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t)); + u->headers_in.content_length_n = -1; if (ngx_list_init(&u->headers_in.headers, r->pool, 8, sizeof(ngx_table_elt_t)) @@ -1924,14 +1951,9 @@ ngx_http_upstream_process_headers(ngx_ht r->headers_out.status = u->headers_in.status_n; r->headers_out.status_line = u->headers_in.status_line; - u->headers_in.content_length_n = r->headers_out.content_length_n; - - if (r->headers_out.content_length_n != -1) { - u->length = (size_t) r->headers_out.content_length_n; - - } else { - u->length = NGX_MAX_SIZE_T_VALUE; - } + r->headers_out.content_length_n = u->headers_in.content_length_n; + + u->length = u->headers_in.content_length_n; return NGX_OK; } @@ -1995,6 +2017,11 @@ ngx_http_upstream_process_body_in_memory } } + if (u->length == 0) { + ngx_http_upstream_finalize_request(r, u, 0); + return; + } + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, NGX_ERROR); return; @@ -2115,7 +2142,7 @@ ngx_http_upstream_send_response(ngx_http return; } - if (u->peer.connection->read->ready) { + if (u->peer.connection->read->ready || u->length == 0) { ngx_http_upstream_process_non_buffered_upstream(r, u); } } @@ -2293,6 +2320,15 @@ ngx_http_upstream_send_response(ngx_http p->send_timeout = clcf->send_timeout; p->send_lowat = clcf->send_lowat; + p->length = -1; + + if (u->input_filter_init + && u->input_filter_init(p->input_ctx) != NGX_OK) + { + ngx_http_upstream_finalize_request(r, u, 0); + return; + } + u->read_event_handler = ngx_http_upstream_process_upstream; r->write_event_handler = ngx_http_upstream_process_downstream; @@ -2382,7 +2418,7 @@ ngx_http_upstream_process_non_buffered_r return; } - ngx_chain_update_chains(&u->free_bufs, &u->busy_bufs, + ngx_chain_update_chains(r->pool, &u->free_bufs, &u->busy_bufs, &u->out_bufs, u->output.tag); } @@ -2403,10 +2439,6 @@ ngx_http_upstream_process_non_buffered_r size = b->end - b->last; - if (size > u->length) { - size = u->length; - } - if (size && upstream->read->ready) { n = upstream->recv(upstream, b->last, size); @@ -2503,7 +2535,7 @@ ngx_http_upstream_non_buffered_filter(vo cl->buf->last = b->last; cl->buf->tag = u->output.tag; - if (u->length == NGX_MAX_SIZE_T_VALUE) { + if (u->length == -1) { return NGX_OK; } @@ -2812,6 +2844,10 @@ ngx_http_upstream_next(ngx_http_request_ if (u->peer.cached && ft_type == NGX_HTTP_UPSTREAM_FT_ERROR) { status = 0; + /* TODO: inform balancer instead */ + + u->peer.tries++; + } else { switch(ft_type) { @@ -2886,6 +2922,10 @@ ngx_http_upstream_next(ngx_http_request_ } #endif + if (u->peer.connection->pool) { + ngx_destroy_pool(u->peer.connection->pool); + } + ngx_close_connection(u->peer.connection); } @@ -2980,6 +3020,10 @@ ngx_http_upstream_finalize_request(ngx_h "close http upstream connection: %d", u->peer.connection->fd); + if (u->peer.connection->pool) { + ngx_destroy_pool(u->peer.connection->pool); + } + ngx_close_connection(u->peer.connection); } @@ -3060,6 +3104,21 @@ ngx_http_upstream_ignore_header_line(ngx static ngx_int_t +ngx_http_upstream_process_content_length(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset) +{ + ngx_http_upstream_t *u; + + u = r->upstream; + + u->headers_in.content_length = h; + u->headers_in.content_length_n = ngx_atoof(h->value.data, h->value.len); + + return NGX_OK; +} + + +static ngx_int_t ngx_http_upstream_process_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { @@ -3321,6 +3380,40 @@ ngx_http_upstream_process_charset(ngx_ht static ngx_int_t +ngx_http_upstream_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + r->upstream->headers_in.connection = h; + + if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len, + (u_char *) "close", 5 - 1) + != NULL) + { + r->upstream->headers_in.connection_close = 1; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset) +{ + r->upstream->headers_in.transfer_encoding = h; + + if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len, + (u_char *) "chunked", 7 - 1) + != NULL) + { + r->upstream->headers_in.chunked = 1; + } + + return NGX_OK; +} + + +static ngx_int_t ngx_http_upstream_copy_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { @@ -3428,26 +3521,6 @@ ngx_http_upstream_copy_content_type(ngx_ static ngx_int_t -ngx_http_upstream_copy_content_length(ngx_http_request_t *r, ngx_table_elt_t *h, - ngx_uint_t offset) -{ - ngx_table_elt_t *ho; - - ho = ngx_list_push(&r->headers_out.headers); - if (ho == NULL) { - return NGX_ERROR; - } - - *ho = *h; - - r->headers_out.content_length = ho; - r->headers_out.content_length_n = ngx_atoof(h->value.data, h->value.len); - - return NGX_OK; -} - - -static ngx_int_t ngx_http_upstream_copy_last_modified(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) {
--- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -217,6 +217,7 @@ typedef struct { ngx_table_elt_t *location; ngx_table_elt_t *accept_ranges; ngx_table_elt_t *www_authenticate; + ngx_table_elt_t *transfer_encoding; #if (NGX_HTTP_GZIP) ngx_table_elt_t *content_encoding; @@ -225,6 +226,9 @@ typedef struct { off_t content_length_n; ngx_array_t cache_control; + + unsigned connection_close:1; + unsigned chunked:1; } ngx_http_upstream_headers_in_t; @@ -267,7 +271,7 @@ struct ngx_http_upstream_s { ngx_http_upstream_resolved_t *resolved; ngx_buf_t buffer; - size_t length; + off_t length; ngx_chain_t *out_bufs; ngx_chain_t *busy_bufs; @@ -308,6 +312,7 @@ struct ngx_http_upstream_s { #endif unsigned buffering:1; + unsigned keepalive:1; unsigned request_sent:1; unsigned header_sent:1;
--- a/src/os/unix/ngx_errno.c +++ b/src/os/unix/ngx_errno.c @@ -12,7 +12,7 @@ * The strerror() messages are copied because: * * 1) strerror() and strerror_r() functions are not Async-Signal-Safe, - * therefore, they can not be used in signal handlers; + * therefore, they cannot be used in signal handlers; * * 2) a direct sys_errlist[] array may be used instead of these functions, * but Linux linker warns about its usage:
--- a/src/os/unix/ngx_file_aio_read.c +++ b/src/os/unix/ngx_file_aio_read.c @@ -23,7 +23,7 @@ * kqueue EVFILT_AIO filter is level triggered only: an event repeats * until aio_return() will be called; * - * aio_cancel() can not cancel file AIO: it returns AIO_NOTCANCELED always. + * aio_cancel() cannot cancel file AIO: it returns AIO_NOTCANCELED always. */
--- a/src/os/unix/ngx_freebsd_sendfile_chain.c +++ b/src/os/unix/ngx_freebsd_sendfile_chain.c @@ -11,7 +11,7 @@ /* * Although FreeBSD sendfile() allows to pass a header and a trailer, - * it can not send a header with a part of the file in one packet until + * it cannot send a header with a part of the file in one packet until * FreeBSD 5.3. Besides, over the fast ethernet connection sendfile() * may send the partially filled packets, i.e. the 8 file pages may be sent * as the 11 full 1460-bytes packets, then one incomplete 324-bytes packet,
--- a/src/os/unix/ngx_linux_aio_read.c +++ b/src/os/unix/ngx_linux_aio_read.c @@ -16,7 +16,7 @@ extern aio_context_t ngx_aio_ctx; static void ngx_file_aio_event_handler(ngx_event_t *ev); -static long +static int io_submit(aio_context_t ctx, long n, struct iocb **paiocb) { return syscall(SYS_io_submit, ctx, n, paiocb); @@ -27,7 +27,7 @@ ssize_t ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset, ngx_pool_t *pool) { - long n; + ngx_err_t err; struct iocb *piocb[1]; ngx_event_t *ev; ngx_event_aio_t *aio; @@ -96,9 +96,7 @@ ngx_file_aio_read(ngx_file_t *file, u_ch piocb[0] = &aio->aiocb; - n = io_submit(ngx_aio_ctx, 1, piocb); - - if (n == 1) { + if (io_submit(ngx_aio_ctx, 1, piocb) == 1) { ev->active = 1; ev->ready = 0; ev->complete = 0; @@ -106,16 +104,16 @@ ngx_file_aio_read(ngx_file_t *file, u_ch return NGX_AGAIN; } - n = -n; + err = ngx_errno; - if (n == NGX_EAGAIN) { + if (err == NGX_EAGAIN) { return ngx_read_file(file, buf, size, offset); } - ngx_log_error(NGX_LOG_CRIT, file->log, n, + ngx_log_error(NGX_LOG_CRIT, file->log, err, "io_submit(\"%V\") failed", &file->name); - if (n == NGX_ENOSYS) { + if (err == NGX_ENOSYS) { ngx_file_aio = 0; return ngx_read_file(file, buf, size, offset); }
--- a/src/os/unix/ngx_process.c +++ b/src/os/unix/ngx_process.c @@ -541,7 +541,7 @@ ngx_process_get_status(void) if (WEXITSTATUS(status) == 2 && ngx_processes[i].respawn) { ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "%s %P exited with fatal code %d " - "and can not be respawn", + "and cannot be respawned", process, pid, WEXITSTATUS(status)); ngx_processes[i].respawn = 0; }
--- a/src/os/unix/ngx_process_cycle.c +++ b/src/os/unix/ngx_process_cycle.c @@ -620,7 +620,8 @@ ngx_reap_children(ngx_cycle_t *cycle) == NGX_INVALID_PID) { ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, - "can not respawn %s", ngx_processes[i].name); + "could not respawn %s", + ngx_processes[i].name); continue; }