diff options
author | nginx <nginx@nginx.org> | 2014-10-28 15:18:24 +0000 |
---|---|---|
committer | Jon Kolb <kolbyjack@gmail.com> | 2014-10-28 15:18:24 +0000 |
commit | 9a11306bd73c32aeea6f1d6587215dd8054559bb (patch) | |
tree | 435740e737335f93914d879f202d6f24521417a5 | |
parent | 4aeefe1f3cef9d863cc04f7ee3fe2c730e6cfdf1 (diff) | |
download | nginx-9a11306bd73c32aeea6f1d6587215dd8054559bb.tar.gz |
Changes with nginx 1.7.7 28 Oct 2014v1.7.7
*) Change: now nginx takes into account the "Vary" header line in a
backend response while caching.
*) Feature: the "proxy_force_ranges", "fastcgi_force_ranges",
"scgi_force_ranges", and "uwsgi_force_ranges" directives.
*) Feature: the "proxy_limit_rate", "fastcgi_limit_rate",
"scgi_limit_rate", and "uwsgi_limit_rate" directives.
*) Feature: the "Vary" parameter of the "proxy_ignore_headers",
"fastcgi_ignore_headers", "scgi_ignore_headers", and
"uwsgi_ignore_headers" directives.
*) Bugfix: the last part of a response received from a backend with
unbufferred proxy might not be sent to a client if "gzip" or "gunzip"
directives were used.
*) Bugfix: in the "proxy_cache_revalidate" directive.
Thanks to Piotr Sikora.
*) Bugfix: in error handling.
Thanks to Yichun Zhang and Daniil Bondarev.
*) Bugfix: in the "proxy_next_upstream_tries" and
"proxy_next_upstream_timeout" directives.
Thanks to Feng Gu.
*) Bugfix: nginx/Windows could not be built with MinGW-w64 gcc.
Thanks to Kouhei Sutou.
28 files changed, 662 insertions, 36 deletions
@@ -1,4 +1,37 @@ +Changes with nginx 1.7.7 28 Oct 2014 + + *) Change: now nginx takes into account the "Vary" header line in a + backend response while caching. + + *) Feature: the "proxy_force_ranges", "fastcgi_force_ranges", + "scgi_force_ranges", and "uwsgi_force_ranges" directives. + + *) Feature: the "proxy_limit_rate", "fastcgi_limit_rate", + "scgi_limit_rate", and "uwsgi_limit_rate" directives. + + *) Feature: the "Vary" parameter of the "proxy_ignore_headers", + "fastcgi_ignore_headers", "scgi_ignore_headers", and + "uwsgi_ignore_headers" directives. + + *) Bugfix: the last part of a response received from a backend with + unbufferred proxy might not be sent to a client if "gzip" or "gunzip" + directives were used. + + *) Bugfix: in the "proxy_cache_revalidate" directive. + Thanks to Piotr Sikora. + + *) Bugfix: in error handling. + Thanks to Yichun Zhang and Daniil Bondarev. + + *) Bugfix: in the "proxy_next_upstream_tries" and + "proxy_next_upstream_timeout" directives. + Thanks to Feng Gu. + + *) Bugfix: nginx/Windows could not be built with MinGW-w64 gcc. + Thanks to Kouhei Sutou. + + Changes with nginx 1.7.6 30 Sep 2014 *) Change: the deprecated "limit_zone" directive is not supported diff --git a/CHANGES.ru b/CHANGES.ru index 4eff3ccf9..988651ef7 100644 --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,36 @@ +Изменения в nginx 1.7.7 28.10.2014 + + *) Изменение: теперь nginx учитывает при кэшировании строку "Vary" в + заголовке ответа бэкенда. + + *) Добавление: директивы proxy_force_ranges, fastcgi_force_ranges, + scgi_force_ranges и uwsgi_force_ranges. + + *) Добавление: директивы proxy_limit_rate, fastcgi_limit_rate, + scgi_limit_rate и uwsgi_limit_rate. + + *) Добавление: параметр Vary директив proxy_ignore_headers, + fastcgi_ignore_headers, scgi_ignore_headers и uwsgi_ignore_headers. + + *) Исправление: последняя часть ответа, полученного от бэкенда при + небуферизированном проксировании, могла не отправляться клиенту, если + использовались директивы gzip или gunzip. + + *) Исправление: в директиве proxy_cache_revalidate. + Спасибо Piotr Sikora. + + *) Исправление: в обработке ошибок. + Спасибо Yichun Zhang и Даниилу Бондареву. + + *) Исправление: в директивах proxy_next_upstream_tries и + proxy_next_upstream_timeout. + Спасибо Feng Gu. + + *) Исправление: nginx/Windows не собирался с MinGW-w64 gcc. + Спасибо Kouhei Sutou. + + Изменения в nginx 1.7.6 30.09.2014 *) Изменение: устаревшая директива limit_zone больше не поддерживается. diff --git a/src/core/nginx.h b/src/core/nginx.h index 3030b44b4..2d923fb85 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1007006 -#define NGINX_VERSION "1.7.6" +#define nginx_version 1007007 +#define NGINX_VERSION "1.7.7" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD diff --git a/src/core/ngx_hash.c b/src/core/ngx_hash.c index c7bfed709..65ad83947 100644 --- a/src/core/ngx_hash.c +++ b/src/core/ngx_hash.c @@ -312,6 +312,8 @@ ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts) continue; } + size--; + ngx_log_error(NGX_LOG_WARN, hinit->pool->log, 0, "could not build optimal %s, you should increase " "either %s_max_size: %i or %s_bucket_size: %i; " diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 975a8e001..f34565277 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -1185,10 +1185,10 @@ ngx_ssl_handshake_handler(ngx_event_t *ev) ssize_t -ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl) +ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit) { u_char *last; - ssize_t n, bytes; + ssize_t n, bytes, size; ngx_buf_t *b; bytes = 0; @@ -1197,8 +1197,19 @@ ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl) last = b->last; for ( ;; ) { + size = b->end - last; - n = ngx_ssl_recv(c, last, b->end - last); + if (limit) { + if (bytes >= limit) { + return bytes; + } + + if (bytes + size > limit) { + size = (ssize_t) (limit - bytes); + } + } + + n = ngx_ssl_recv(c, last, size); if (n > 0) { last += n; diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index 408694035..08eff6445 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -194,7 +194,7 @@ ngx_int_t ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool, ngx_int_t ngx_ssl_handshake(ngx_connection_t *c); ssize_t ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size); ssize_t ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size); -ssize_t ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl); +ssize_t ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit); ngx_chain_t *ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit); void ngx_ssl_free_buffer(ngx_connection_t *c); diff --git a/src/event/ngx_event_pipe.c b/src/event/ngx_event_pipe.c index 64fb07bde..62663d5a4 100644 --- a/src/event/ngx_event_pipe.c +++ b/src/event/ngx_event_pipe.c @@ -66,11 +66,13 @@ ngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write) return NGX_ABORT; } - if (rev->active && !rev->ready) { - ngx_add_timer(rev, p->read_timeout); + if (!rev->delayed) { + if (rev->active && !rev->ready) { + ngx_add_timer(rev, p->read_timeout); - } else if (rev->timer_set) { - ngx_del_timer(rev); + } else if (rev->timer_set) { + ngx_del_timer(rev); + } } } @@ -99,9 +101,11 @@ ngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write) static ngx_int_t ngx_event_pipe_read_upstream(ngx_event_pipe_t *p) { + off_t limit; ssize_t n, size; ngx_int_t rc; ngx_buf_t *b; + ngx_msec_t delay; ngx_chain_t *chain, *cl, *ln; if (p->upstream_eof || p->upstream_error || p->upstream_done) { @@ -169,6 +173,25 @@ ngx_event_pipe_read_upstream(ngx_event_pipe_t *p) } #endif + if (p->limit_rate) { + if (p->upstream->read->delayed) { + break; + } + + limit = (off_t) p->limit_rate * (ngx_time() - p->start_sec + 1) + - p->read_length; + + if (limit <= 0) { + p->upstream->read->delayed = 1; + delay = (ngx_msec_t) (- limit * 1000 / p->limit_rate + 1); + ngx_add_timer(p->upstream->read, delay); + break; + } + + } else { + limit = 0; + } + if (p->free_raw_bufs) { /* use the free bufs if they exist */ @@ -270,7 +293,7 @@ ngx_event_pipe_read_upstream(ngx_event_pipe_t *p) break; } - n = p->upstream->recv_chain(p->upstream, chain); + n = p->upstream->recv_chain(p->upstream, chain, limit); ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe recv chain: %z", n); @@ -301,6 +324,8 @@ ngx_event_pipe_read_upstream(ngx_event_pipe_t *p) } } + delay = p->limit_rate ? (ngx_msec_t) n * 1000 / p->limit_rate : 0; + p->read_length += n; cl = chain; p->free_raw_bufs = NULL; @@ -337,6 +362,12 @@ ngx_event_pipe_read_upstream(ngx_event_pipe_t *p) ln->next = p->free_raw_bufs; p->free_raw_bufs = cl; } + + if (delay > 0) { + p->upstream->read->delayed = 1; + ngx_add_timer(p->upstream->read, delay); + break; + } } #if (NGX_DEBUG) diff --git a/src/event/ngx_event_pipe.h b/src/event/ngx_event_pipe.h index f24e6d148..451fc4c05 100644 --- a/src/event/ngx_event_pipe.h +++ b/src/event/ngx_event_pipe.h @@ -80,6 +80,9 @@ struct ngx_event_pipe_s { size_t preread_size; ngx_buf_t *buf_to_file; + size_t limit_rate; + time_t start_sec; + ngx_temp_file_t *temp_file; /* STUB */ int num; diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c index a49c93ca2..b7f7ea14a 100644 --- a/src/http/modules/ngx_http_fastcgi_module.c +++ b/src/http/modules/ngx_http_fastcgi_module.c @@ -326,6 +326,20 @@ static ngx_command_t ngx_http_fastcgi_commands[] = { offsetof(ngx_http_fastcgi_loc_conf_t, upstream.busy_buffers_size_conf), NULL }, + { ngx_string("fastcgi_force_ranges"), + 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, upstream.force_ranges), + NULL }, + + { ngx_string("fastcgi_limit_rate"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_fastcgi_loc_conf_t, upstream.limit_rate), + NULL }, + #if (NGX_HTTP_CACHE) { ngx_string("fastcgi_cache"), @@ -2332,6 +2346,7 @@ ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf) conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT; conf->upstream.buffering = NGX_CONF_UNSET; conf->upstream.ignore_client_abort = NGX_CONF_UNSET; + conf->upstream.force_ranges = NGX_CONF_UNSET; conf->upstream.local = NGX_CONF_UNSET_PTR; @@ -2342,6 +2357,7 @@ ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf) conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE; conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE; + conf->upstream.limit_rate = NGX_CONF_UNSET_SIZE; conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE; conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE; @@ -2413,6 +2429,9 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->upstream.ignore_client_abort, prev->upstream.ignore_client_abort, 0); + ngx_conf_merge_value(conf->upstream.force_ranges, + prev->upstream.force_ranges, 0); + ngx_conf_merge_ptr_value(conf->upstream.local, prev->upstream.local, NULL); @@ -2435,6 +2454,9 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) prev->upstream.buffer_size, (size_t) ngx_pagesize); + ngx_conf_merge_size_value(conf->upstream.limit_rate, + prev->upstream.limit_rate, 0); + ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs, 8, ngx_pagesize); diff --git a/src/http/modules/ngx_http_gunzip_filter_module.c b/src/http/modules/ngx_http_gunzip_filter_module.c index 70ec0aace..c1341f562 100644 --- a/src/http/modules/ngx_http_gunzip_filter_module.c +++ b/src/http/modules/ngx_http_gunzip_filter_module.c @@ -175,6 +175,7 @@ static ngx_int_t ngx_http_gunzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { int rc; + ngx_uint_t flush; ngx_chain_t *cl; ngx_http_gunzip_ctx_t *ctx; @@ -199,7 +200,7 @@ ngx_http_gunzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) } } - if (ctx->nomem || in == NULL) { + if (ctx->nomem) { /* flush busy buffers */ @@ -212,6 +213,10 @@ ngx_http_gunzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl, (ngx_buf_tag_t) &ngx_http_gunzip_filter_module); ctx->nomem = 0; + flush = 0; + + } else { + flush = ctx->busy ? 1 : 0; } for ( ;; ) { @@ -258,7 +263,7 @@ ngx_http_gunzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) /* rc == NGX_AGAIN */ } - if (ctx->out == NULL) { + if (ctx->out == NULL && !flush) { return ctx->busy ? NGX_AGAIN : NGX_OK; } @@ -276,6 +281,7 @@ ngx_http_gunzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) "gunzip out: %p", ctx->out); ctx->nomem = 0; + flush = 0; if (ctx->done) { return rc; diff --git a/src/http/modules/ngx_http_gzip_filter_module.c b/src/http/modules/ngx_http_gzip_filter_module.c index c57a4a3c7..f941e6397 100644 --- a/src/http/modules/ngx_http_gzip_filter_module.c +++ b/src/http/modules/ngx_http_gzip_filter_module.c @@ -316,6 +316,7 @@ static ngx_int_t ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { int rc; + ngx_uint_t flush; ngx_chain_t *cl; ngx_http_gzip_ctx_t *ctx; @@ -372,7 +373,7 @@ ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED; } - if (ctx->nomem || in == NULL) { + if (ctx->nomem) { /* flush busy buffers */ @@ -385,6 +386,10 @@ ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl, (ngx_buf_tag_t) &ngx_http_gzip_filter_module); ctx->nomem = 0; + flush = 0; + + } else { + flush = ctx->busy ? 1 : 0; } for ( ;; ) { @@ -432,7 +437,7 @@ ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) /* rc == NGX_AGAIN */ } - if (ctx->out == NULL) { + if (ctx->out == NULL && !flush) { ngx_http_gzip_filter_free_copy_buf(r, ctx); return ctx->busy ? NGX_AGAIN : NGX_OK; @@ -457,6 +462,7 @@ ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) ctx->last_out = &ctx->out; ctx->nomem = 0; + flush = 0; if (ctx->done) { return rc; diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c index c8a80ef48..ea4109c77 100644 --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -389,6 +389,20 @@ static ngx_command_t ngx_http_proxy_commands[] = { offsetof(ngx_http_proxy_loc_conf_t, upstream.busy_buffers_size_conf), NULL }, + { ngx_string("proxy_force_ranges"), + 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_proxy_loc_conf_t, upstream.force_ranges), + NULL }, + + { ngx_string("proxy_limit_rate"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, upstream.limit_rate), + NULL }, + #if (NGX_HTTP_CACHE) { ngx_string("proxy_cache"), @@ -2472,6 +2486,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf) conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT; conf->upstream.buffering = NGX_CONF_UNSET; conf->upstream.ignore_client_abort = NGX_CONF_UNSET; + conf->upstream.force_ranges = NGX_CONF_UNSET; conf->upstream.local = NGX_CONF_UNSET_PTR; @@ -2482,6 +2497,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf) conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE; conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE; + conf->upstream.limit_rate = NGX_CONF_UNSET_SIZE; conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE; conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE; @@ -2568,6 +2584,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->upstream.ignore_client_abort, prev->upstream.ignore_client_abort, 0); + ngx_conf_merge_value(conf->upstream.force_ranges, + prev->upstream.force_ranges, 0); + ngx_conf_merge_ptr_value(conf->upstream.local, prev->upstream.local, NULL); @@ -2590,6 +2609,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) prev->upstream.buffer_size, (size_t) ngx_pagesize); + ngx_conf_merge_size_value(conf->upstream.limit_rate, + prev->upstream.limit_rate, 0); + ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs, 8, ngx_pagesize); diff --git a/src/http/modules/ngx_http_scgi_module.c b/src/http/modules/ngx_http_scgi_module.c index 313f3b3ad..71dcd34f9 100644 --- a/src/http/modules/ngx_http_scgi_module.c +++ b/src/http/modules/ngx_http_scgi_module.c @@ -183,6 +183,20 @@ static ngx_command_t ngx_http_scgi_commands[] = { offsetof(ngx_http_scgi_loc_conf_t, upstream.busy_buffers_size_conf), NULL }, + { ngx_string("scgi_force_ranges"), + 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_scgi_loc_conf_t, upstream.force_ranges), + NULL }, + + { ngx_string("scgi_limit_rate"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_scgi_loc_conf_t, upstream.limit_rate), + NULL }, + #if (NGX_HTTP_CACHE) { ngx_string("scgi_cache"), @@ -1091,6 +1105,7 @@ ngx_http_scgi_create_loc_conf(ngx_conf_t *cf) conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT; conf->upstream.buffering = NGX_CONF_UNSET; conf->upstream.ignore_client_abort = NGX_CONF_UNSET; + conf->upstream.force_ranges = NGX_CONF_UNSET; conf->upstream.local = NGX_CONF_UNSET_PTR; @@ -1101,6 +1116,7 @@ ngx_http_scgi_create_loc_conf(ngx_conf_t *cf) conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE; conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE; + conf->upstream.limit_rate = NGX_CONF_UNSET_SIZE; conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE; conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE; @@ -1167,6 +1183,9 @@ ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->upstream.ignore_client_abort, prev->upstream.ignore_client_abort, 0); + ngx_conf_merge_value(conf->upstream.force_ranges, + prev->upstream.force_ranges, 0); + ngx_conf_merge_ptr_value(conf->upstream.local, prev->upstream.local, NULL); @@ -1189,6 +1208,9 @@ ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) prev->upstream.buffer_size, (size_t) ngx_pagesize); + ngx_conf_merge_size_value(conf->upstream.limit_rate, + prev->upstream.limit_rate, 0); + ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs, 8, ngx_pagesize); diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c index 9556aa4a0..5114782b6 100644 --- a/src/http/modules/ngx_http_uwsgi_module.c +++ b/src/http/modules/ngx_http_uwsgi_module.c @@ -238,6 +238,20 @@ static ngx_command_t ngx_http_uwsgi_commands[] = { offsetof(ngx_http_uwsgi_loc_conf_t, upstream.busy_buffers_size_conf), NULL }, + { ngx_string("uwsgi_force_ranges"), + 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_uwsgi_loc_conf_t, upstream.force_ranges), + NULL }, + + { ngx_string("uwsgi_limit_rate"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_uwsgi_loc_conf_t, upstream.limit_rate), + NULL }, + #if (NGX_HTTP_CACHE) { ngx_string("uwsgi_cache"), @@ -1271,6 +1285,7 @@ ngx_http_uwsgi_create_loc_conf(ngx_conf_t *cf) conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT; conf->upstream.buffering = NGX_CONF_UNSET; conf->upstream.ignore_client_abort = NGX_CONF_UNSET; + conf->upstream.force_ranges = NGX_CONF_UNSET; conf->upstream.local = NGX_CONF_UNSET_PTR; @@ -1281,6 +1296,7 @@ ngx_http_uwsgi_create_loc_conf(ngx_conf_t *cf) conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE; conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE; + conf->upstream.limit_rate = NGX_CONF_UNSET_SIZE; conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE; conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE; @@ -1354,6 +1370,9 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->upstream.ignore_client_abort, prev->upstream.ignore_client_abort, 0); + ngx_conf_merge_value(conf->upstream.force_ranges, + prev->upstream.force_ranges, 0); + ngx_conf_merge_ptr_value(conf->upstream.local, prev->upstream.local, NULL); @@ -1376,6 +1395,9 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) prev->upstream.buffer_size, (size_t) ngx_pagesize); + ngx_conf_merge_size_value(conf->upstream.limit_rate, + prev->upstream.limit_rate, 0); + ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs, 8, ngx_pagesize); diff --git a/src/http/ngx_http_cache.h b/src/http/ngx_http_cache.h index 1cfd9fe84..f89766d58 100644 --- a/src/http/ngx_http_cache.h +++ b/src/http/ngx_http_cache.h @@ -25,8 +25,9 @@ #define NGX_HTTP_CACHE_KEY_LEN 16 #define NGX_HTTP_CACHE_ETAG_LEN 42 +#define NGX_HTTP_CACHE_VARY_LEN 42 -#define NGX_HTTP_CACHE_VERSION 2 +#define NGX_HTTP_CACHE_VERSION 3 typedef struct { @@ -64,6 +65,7 @@ struct ngx_http_cache_s { ngx_array_t keys; uint32_t crc32; u_char key[NGX_HTTP_CACHE_KEY_LEN]; + u_char main[NGX_HTTP_CACHE_KEY_LEN]; ngx_file_uniq_t uniq; time_t valid_sec; @@ -71,6 +73,8 @@ struct ngx_http_cache_s { time_t date; ngx_str_t etag; + ngx_str_t vary; + u_char variant[NGX_HTTP_CACHE_KEY_LEN]; size_t header_start; size_t body_start; @@ -98,6 +102,8 @@ struct ngx_http_cache_s { unsigned updating:1; unsigned exists:1; unsigned temp_file:1; + unsigned reading:1; + unsigned secondary:1; }; @@ -112,6 +118,9 @@ typedef struct { u_short body_start; u_char etag_len; u_char etag[NGX_HTTP_CACHE_ETAG_LEN]; + u_char vary_len; + u_char vary[NGX_HTTP_CACHE_VARY_LEN]; + u_char variant[NGX_HTTP_CACHE_KEY_LEN]; } ngx_http_file_cache_header_t; diff --git a/src/http/ngx_http_file_cache.c b/src/http/ngx_http_file_cache.c index 2eebc3068..bc4c80602 100644 --- a/src/http/ngx_http_file_cache.c +++ b/src/http/ngx_http_file_cache.c @@ -29,6 +29,12 @@ static ngx_http_file_cache_node_t * ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key); static void ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +static void ngx_http_file_cache_vary(ngx_http_request_t *r, u_char *vary, + size_t len, u_char *hash); +static void ngx_http_file_cache_vary_header(ngx_http_request_t *r, + ngx_md5_t *md5, ngx_str_t *name); +static ngx_int_t ngx_http_file_cache_reopen(ngx_http_request_t *r, + ngx_http_cache_t *c); static void ngx_http_file_cache_cleanup(void *data); static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache); static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache); @@ -235,6 +241,8 @@ ngx_http_file_cache_create_key(ngx_http_request_t *r) ngx_crc32_final(c->crc32); ngx_md5_final(c->key, &md5); + + ngx_memcpy(c->main, c->key, NGX_HTTP_CACHE_KEY_LEN); } @@ -255,7 +263,7 @@ ngx_http_file_cache_open(ngx_http_request_t *r) return NGX_AGAIN; } - if (c->buf) { + if (c->reading) { return ngx_http_file_cache_read(r, c); } @@ -519,6 +527,23 @@ ngx_http_file_cache_read(ngx_http_request_t *r, ngx_http_cache_t *c) return NGX_DECLINED; } + if (h->vary_len > NGX_HTTP_CACHE_VARY_LEN) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, + "cache file \"%s\" has incorrect vary length", + c->file.name.data); + return NGX_DECLINED; + } + + if (h->vary_len) { + ngx_http_file_cache_vary(r, h->vary, h->vary_len, c->variant); + + if (ngx_memcmp(c->variant, h->variant, NGX_HTTP_CACHE_KEY_LEN) != 0) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache vary mismatch"); + return ngx_http_file_cache_reopen(r, c); + } + } + c->buf->last += n; c->valid_sec = h->valid_sec; @@ -599,9 +624,12 @@ ngx_http_file_cache_aio_read(ngx_http_request_t *r, ngx_http_cache_t *c) n = ngx_file_aio_read(&c->file, c->buf->pos, c->body_start, 0, r->pool); if (n != NGX_AGAIN) { + c->reading = 0; return n; } + c->reading = 1; + c->file.aio->data = r; c->file.aio->handler = ngx_http_cache_aio_event_handler; @@ -870,6 +898,193 @@ ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, } +static void +ngx_http_file_cache_vary(ngx_http_request_t *r, u_char *vary, size_t len, + u_char *hash) +{ + u_char *p, *last; + ngx_str_t name; + ngx_md5_t md5; + u_char buf[NGX_HTTP_CACHE_VARY_LEN]; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache vary: \"%*s\"", len, vary); + + ngx_md5_init(&md5); + ngx_md5_update(&md5, r->cache->main, NGX_HTTP_CACHE_KEY_LEN); + + ngx_strlow(buf, vary, len); + + p = buf; + last = buf + len; + + while (p < last) { + + while (p < last && (*p == ' ' || *p == ',')) { p++; } + + name.data = p; + + while (p < last && *p != ',' && *p != ' ') { p++; } + + name.len = p - name.data; + + if (name.len == 0) { + break; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache vary: %V", &name); + + ngx_md5_update(&md5, name.data, name.len); + ngx_md5_update(&md5, (u_char *) ":", sizeof(":") - 1); + + ngx_http_file_cache_vary_header(r, &md5, &name); + + ngx_md5_update(&md5, (u_char *) CRLF, sizeof(CRLF) - 1); + } + + ngx_md5_final(hash, &md5); +} + + +static void +ngx_http_file_cache_vary_header(ngx_http_request_t *r, ngx_md5_t *md5, + ngx_str_t *name) +{ + size_t len; + u_char *p, *start, *last; + ngx_uint_t i, multiple, normalize; + ngx_list_part_t *part; + ngx_table_elt_t *header; + + multiple = 0; + normalize = 0; + + if (name->len == sizeof("Accept-Charset") - 1 + && ngx_strncasecmp(name->data, (u_char *) "Accept-Charset", + sizeof("Accept-Charset") - 1) == 0) + { + normalize = 1; + + } else if (name->len == sizeof("Accept-Encoding") - 1 + && ngx_strncasecmp(name->data, (u_char *) "Accept-Encoding", + sizeof("Accept-Encoding") - 1) == 0) + { + normalize = 1; + + } else if (name->len == sizeof("Accept-Language") - 1 + && ngx_strncasecmp(name->data, (u_char *) "Accept-Language", + sizeof("Accept-Language") - 1) == 0) + { + normalize = 1; + } + + part = &r->headers_in.headers.part; + header = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + if (header[i].key.len != name->len) { + continue; + } + + if (ngx_strncasecmp(header[i].key.data, name->data, name->len) != 0) { + continue; + } + + if (!normalize) { + + if (multiple) { + ngx_md5_update(md5, (u_char *) ",", sizeof(",") - 1); + } + + ngx_md5_update(md5, header[i].value.data, header[i].value.len); + + multiple = 1; + + continue; + } + + /* normalize spaces */ + + p = header[i].value.data; + start = p; + last = p + header[i].value.len; + + while (p < last) { + + while (p < last && (*p == ' ' || *p == ',')) { p++; } + + start = p; + + while (p < last && *p != ',' && *p != ' ') { p++; } + + len = p - start; + + if (len == 0) { + break; + } + + if (multiple) { + ngx_md5_update(md5, (u_char *) ",", sizeof(",") - 1); + } + + ngx_md5_update(md5, start, len); + + multiple = 1; + } + } +} + + +static ngx_int_t +ngx_http_file_cache_reopen(ngx_http_request_t *r, ngx_http_cache_t *c) +{ + ngx_http_file_cache_t *cache; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->file.log, 0, + "http file cache reopen"); + + if (c->secondary) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, + "cache file \"%s\" has incorrect vary hash", + c->file.name.data); + return NGX_DECLINED; + } + + cache = c->file_cache; + + ngx_shmtx_lock(&cache->shpool->mutex); + + c->node->count--; + c->node = NULL; + + ngx_shmtx_unlock(&cache->shpool->mutex); + + c->secondary = 1; + c->file.name.len = 0; + c->body_start = c->buf->end - c->buf->start; + + ngx_memcpy(c->key, c->variant, NGX_HTTP_CACHE_KEY_LEN); + + return ngx_http_file_cache_open(r); +} + + void ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf) { @@ -901,6 +1116,22 @@ ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf) ngx_memcpy(h->etag, c->etag.data, c->etag.len); } + if (c->vary.len) { + if (c->vary.len > NGX_HTTP_CACHE_VARY_LEN) { + /* should not happen */ + c->vary.len = NGX_HTTP_CACHE_VARY_LEN; + } + + h->vary_len = (u_char) c->vary.len; + ngx_memcpy(h->vary, c->vary.data, c->vary.len); + + ngx_http_file_cache_vary(r, c->vary.data, c->vary.len, c->variant); + ngx_memcpy(h->variant, c->variant, NGX_HTTP_CACHE_KEY_LEN); + + } else { + ngx_memzero(c->variant, NGX_HTTP_CACHE_KEY_LEN); + } + p = buf + sizeof(ngx_http_file_cache_header_t); p = ngx_cpymem(p, ngx_http_file_cache_key, sizeof(ngx_http_file_cache_key)); @@ -934,11 +1165,43 @@ ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http file cache update"); + cache = c->file_cache; + + if (c->secondary + && ngx_memcmp(c->variant, c->key, NGX_HTTP_CACHE_KEY_LEN) != 0) + { + /* + * if the variant hash doesn't match one we used as a secondary + * cache key, switch back to the original key + */ + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache main key"); + + ngx_shmtx_lock(&cache->shpool->mutex); + + c->node->count--; + c->node->updating = 0; + c->node = NULL; + + ngx_shmtx_unlock(&cache->shpool->mutex); + + c->file.name.len = 0; + + ngx_memcpy(c->key, c->main, NGX_HTTP_CACHE_KEY_LEN); + + if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) { + return; + } + + if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) { + return; + } + } + c->updated = 1; c->updating = 0; - cache = c->file_cache; - uniq = 0; fs_size = 0; @@ -1093,6 +1356,19 @@ ngx_http_file_cache_update_header(ngx_http_request_t *r) ngx_memcpy(h.etag, c->etag.data, c->etag.len); } + if (c->vary.len) { + if (c->vary.len > NGX_HTTP_CACHE_VARY_LEN) { + /* should not happen */ + c->vary.len = NGX_HTTP_CACHE_VARY_LEN; + } + + h.vary_len = (u_char) c->vary.len; + ngx_memcpy(h.vary, c->vary.data, c->vary.len); + + ngx_http_file_cache_vary(r, c->vary.data, c->vary.len, c->variant); + ngx_memcpy(h.variant, c->variant, NGX_HTTP_CACHE_KEY_LEN); + } + (void) ngx_write_file(&file, (u_char *) &h, sizeof(ngx_http_file_cache_header_t), 0); diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 2b932dee4..18c3b04e4 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1227,12 +1227,11 @@ ngx_http_process_request_headers(ngx_event_t *rev) if (len > NGX_MAX_ERROR_STR - 300) { len = NGX_MAX_ERROR_STR - 300; - p[len++] = '.'; p[len++] = '.'; p[len++] = '.'; } ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent too long header line: \"%*s\"", - len, r->header_name_start); + "client sent too long header line: \"%*s...\"", + len, r->header_name_start); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index d547ca74e..3e8ce09e1 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -113,6 +113,8 @@ static ngx_int_t ngx_http_upstream_process_connection(ngx_http_request_t *r, 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_process_vary(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 @@ -250,6 +252,10 @@ ngx_http_upstream_header_t ngx_http_upstream_headers_in[] = { ngx_http_upstream_ignore_header_line, 0, ngx_http_upstream_ignore_header_line, 0, 0 }, + { ngx_string("Vary"), + ngx_http_upstream_process_vary, 0, + ngx_http_upstream_copy_header_line, 0, 0 }, + { ngx_string("X-Powered-By"), ngx_http_upstream_ignore_header_line, 0, ngx_http_upstream_copy_header_line, 0, 0 }, @@ -407,6 +413,7 @@ ngx_conf_bitmask_t ngx_http_upstream_ignore_headers_masks[] = { { ngx_string("Expires"), NGX_HTTP_UPSTREAM_IGN_EXPIRES }, { ngx_string("Cache-Control"), NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL }, { ngx_string("Set-Cookie"), NGX_HTTP_UPSTREAM_IGN_SET_COOKIE }, + { ngx_string("Vary"), NGX_HTTP_UPSTREAM_IGN_VARY }, { ngx_null_string, 0 } }; @@ -441,6 +448,7 @@ ngx_http_upstream_create(ngx_http_request_t *r) #endif u->headers_in.content_length_n = -1; + u->headers_in.last_modified_time = -1; return NGX_OK; } @@ -885,6 +893,7 @@ ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u) ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t)); u->headers_in.content_length_n = -1; + u->headers_in.last_modified_time = -1; if (ngx_list_init(&u->headers_in.headers, r->pool, 8, sizeof(ngx_table_elt_t)) @@ -972,6 +981,14 @@ ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx) ngx_resolve_name_done(ctx); ur->ctx = NULL; + u->peer.start_time = ngx_current_msec; + + if (u->conf->next_upstream_tries + && u->peer.tries > u->conf->next_upstream_tries) + { + u->peer.tries = u->conf->next_upstream_tries; + } + ngx_http_upstream_connect(r, u); failed: @@ -1582,6 +1599,7 @@ ngx_http_upstream_reinit(ngx_http_request_t *r, ngx_http_upstream_t *u) ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t)); u->headers_in.content_length_n = -1; + u->headers_in.last_modified_time = -1; if (ngx_list_init(&u->headers_in.headers, r->pool, 8, sizeof(ngx_table_elt_t)) @@ -2273,6 +2291,17 @@ ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u) r->disable_not_modified = !u->cacheable; + if (u->conf->force_ranges) { + r->allow_ranges = 1; + r->single_range = 1; + +#if (NGX_HTTP_CACHE) + if (r->cached) { + r->single_range = 0; + } +#endif + } + u->length = -1; return NGX_OK; @@ -2558,6 +2587,8 @@ ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u) p->downstream = c; p->pool = r->pool; p->log = c->log; + p->limit_rate = u->conf->limit_rate; + p->start_sec = ngx_time(); p->cacheable = u->cacheable || u->store; @@ -3232,21 +3263,61 @@ static void ngx_http_upstream_process_upstream(ngx_http_request_t *r, ngx_http_upstream_t *u) { + ngx_event_t *rev; + ngx_event_pipe_t *p; ngx_connection_t *c; c = u->peer.connection; + p = u->pipe; + rev = c->read; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http upstream process upstream"); c->log->action = "reading upstream"; - if (c->read->timedout) { - u->pipe->upstream_error = 1; - ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); + if (rev->timedout) { + + if (rev->delayed) { + + rev->timedout = 0; + rev->delayed = 0; + + if (!rev->ready) { + ngx_add_timer(rev, p->read_timeout); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + } + + return; + } + + if (ngx_event_pipe(p, 0) == NGX_ABORT) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + } else { + p->upstream_error = 1; + ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); + } } else { - if (ngx_event_pipe(u->pipe, 0) == NGX_ABORT) { + + if (rev->delayed) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream delayed"); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + } + + return; + } + + if (ngx_event_pipe(p, 0) == NGX_ABORT) { ngx_http_upstream_finalize_request(r, u, NGX_ERROR); return; } @@ -4125,6 +4196,39 @@ ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r, static ngx_int_t +ngx_http_upstream_process_vary(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.vary = h; + +#if (NGX_HTTP_CACHE) + + if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_VARY) { + return NGX_OK; + } + + if (r->cache == NULL) { + return NGX_OK; + } + + if (h->value.len > NGX_HTTP_CACHE_VARY_LEN + || (h->value.len == 1 && h->value.data[0] == '*')) + { + u->cacheable = 0; + } + + r->cache->vary = h->value; + +#endif + + 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) { @@ -4390,6 +4494,10 @@ ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r, { ngx_table_elt_t *ho; + if (r->upstream->conf->force_ranges) { + return NGX_OK; + } + #if (NGX_HTTP_CACHE) if (r->cached) { diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h index 2ee899d83..0032c2c22 100644 --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -50,6 +50,7 @@ #define NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE 0x00000040 #define NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING 0x00000080 #define NGX_HTTP_UPSTREAM_IGN_XA_CHARSET 0x00000100 +#define NGX_HTTP_UPSTREAM_IGN_VARY 0x00000200 typedef struct { @@ -140,6 +141,7 @@ typedef struct { size_t send_lowat; size_t buffer_size; + size_t limit_rate; size_t busy_buffers_size; size_t max_temp_file_size; @@ -162,6 +164,7 @@ typedef struct { ngx_flag_t ignore_client_abort; ngx_flag_t intercept_errors; ngx_flag_t cyclic_temp_file; + ngx_flag_t force_ranges; ngx_path_t *temp_path; @@ -243,6 +246,7 @@ typedef struct { ngx_table_elt_t *accept_ranges; ngx_table_elt_t *www_authenticate; ngx_table_elt_t *transfer_encoding; + ngx_table_elt_t *vary; #if (NGX_HTTP_GZIP) ngx_table_elt_t *content_encoding; diff --git a/src/http/ngx_http_write_filter_module.c b/src/http/ngx_http_write_filter_module.c index 83cb1fa1e..b19f75bda 100644 --- a/src/http/ngx_http_write_filter_module.c +++ b/src/http/ngx_http_write_filter_module.c @@ -219,8 +219,8 @@ ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in) if (limit <= 0) { c->write->delayed = 1; - ngx_add_timer(c->write, - (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1)); + delay = (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1); + ngx_add_timer(c->write, delay); c->buffered |= NGX_HTTP_WRITE_BUFFERED; diff --git a/src/os/unix/ngx_aio_read_chain.c b/src/os/unix/ngx_aio_read_chain.c index 8c831b951..d8722b2c1 100644 --- a/src/os/unix/ngx_aio_read_chain.c +++ b/src/os/unix/ngx_aio_read_chain.c @@ -11,7 +11,7 @@ ssize_t -ngx_aio_read_chain(ngx_connection_t *c, ngx_chain_t *cl) +ngx_aio_read_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit) { int n; u_char *buf, *prev; diff --git a/src/os/unix/ngx_darwin_sendfile_chain.c b/src/os/unix/ngx_darwin_sendfile_chain.c index dd574e5ac..8485f9749 100644 --- a/src/os/unix/ngx_darwin_sendfile_chain.c +++ b/src/os/unix/ngx_darwin_sendfile_chain.c @@ -308,6 +308,7 @@ ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) in = ngx_handle_sent_chain(in, sent); if (eintr) { + send = prev_send + sent; continue; } diff --git a/src/os/unix/ngx_freebsd_sendfile_chain.c b/src/os/unix/ngx_freebsd_sendfile_chain.c index f5d0f3a8a..88eacc2d1 100644 --- a/src/os/unix/ngx_freebsd_sendfile_chain.c +++ b/src/os/unix/ngx_freebsd_sendfile_chain.c @@ -378,6 +378,7 @@ ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) } if (eintr) { + send = prev_send + sent; continue; } diff --git a/src/os/unix/ngx_linux_sendfile_chain.c b/src/os/unix/ngx_linux_sendfile_chain.c index 1060852a0..60867c51e 100644 --- a/src/os/unix/ngx_linux_sendfile_chain.c +++ b/src/os/unix/ngx_linux_sendfile_chain.c @@ -316,6 +316,7 @@ ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) in = ngx_handle_sent_chain(in, sent); if (eintr) { + send = prev_send; continue; } diff --git a/src/os/unix/ngx_os.h b/src/os/unix/ngx_os.h index 1033d8825..a1586426c 100644 --- a/src/os/unix/ngx_os.h +++ b/src/os/unix/ngx_os.h @@ -17,7 +17,8 @@ typedef ssize_t (*ngx_recv_pt)(ngx_connection_t *c, u_char *buf, size_t size); -typedef ssize_t (*ngx_recv_chain_pt)(ngx_connection_t *c, ngx_chain_t *in); +typedef ssize_t (*ngx_recv_chain_pt)(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); typedef ssize_t (*ngx_send_pt)(ngx_connection_t *c, u_char *buf, size_t size); typedef ngx_chain_t *(*ngx_send_chain_pt)(ngx_connection_t *c, ngx_chain_t *in, off_t limit); @@ -41,7 +42,7 @@ ngx_int_t ngx_os_signal_process(ngx_cycle_t *cycle, char *sig, ngx_int_t pid); ssize_t ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size); -ssize_t ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *entry); +ssize_t ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *entry, off_t limit); ssize_t ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size); ssize_t ngx_unix_send(ngx_connection_t *c, u_char *buf, size_t size); ngx_chain_t *ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in, @@ -49,7 +50,7 @@ ngx_chain_t *ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in, #if (NGX_HAVE_AIO) ssize_t ngx_aio_read(ngx_connection_t *c, u_char *buf, size_t size); -ssize_t ngx_aio_read_chain(ngx_connection_t *c, ngx_chain_t *cl); +ssize_t ngx_aio_read_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit); ssize_t ngx_aio_write(ngx_connection_t *c, u_char *buf, size_t size); ngx_chain_t *ngx_aio_write_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit); diff --git a/src/os/unix/ngx_readv_chain.c b/src/os/unix/ngx_readv_chain.c index 3cba80ccc..3544b4b17 100644 --- a/src/os/unix/ngx_readv_chain.c +++ b/src/os/unix/ngx_readv_chain.c @@ -11,7 +11,7 @@ ssize_t -ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain) +ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain, off_t limit) { u_char *prev; ssize_t n, size; @@ -66,8 +66,20 @@ ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain) /* coalesce the neighbouring bufs */ while (chain) { + n = chain->buf->end - chain->buf->last; + + if (limit) { + if (size >= limit) { + break; + } + + if (size + n > limit) { + n = (ssize_t) (limit - size); + } + } + if (prev == chain->buf->last) { - iov->iov_len += chain->buf->end - chain->buf->last; + iov->iov_len += n; } else { if (vec.nelts >= IOV_MAX) { @@ -80,10 +92,10 @@ ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain) } iov->iov_base = (void *) chain->buf->last; - iov->iov_len = chain->buf->end - chain->buf->last; + iov->iov_len = n; } - size += chain->buf->end - chain->buf->last; + size += n; prev = chain->buf->end; chain = chain->next; } diff --git a/src/os/unix/ngx_solaris_sendfilev_chain.c b/src/os/unix/ngx_solaris_sendfilev_chain.c index ba328c810..1b71f1dd8 100644 --- a/src/os/unix/ngx_solaris_sendfilev_chain.c +++ b/src/os/unix/ngx_solaris_sendfilev_chain.c @@ -200,6 +200,7 @@ ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) in = ngx_handle_sent_chain(in, sent); if (eintr) { + send = prev_send + sent; continue; } diff --git a/src/os/unix/ngx_writev_chain.c b/src/os/unix/ngx_writev_chain.c index 95af2da7a..c7b6ebf7c 100644 --- a/src/os/unix/ngx_writev_chain.c +++ b/src/os/unix/ngx_writev_chain.c @@ -134,6 +134,7 @@ ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) in = ngx_handle_sent_chain(in, sent); if (eintr) { + send = prev_send; continue; } |