diff --git a/nginx/CHANGES b/nginx/CHANGES index 9ac6e69b..e54f1b86 100644 --- a/nginx/CHANGES +++ b/nginx/CHANGES @@ -1,4 +1,32 @@ +Changes with nginx 1.11.13 04 Apr 2017 + + *) Feature: the "http_429" parameter of the "proxy_next_upstream", + "fastcgi_next_upstream", "scgi_next_upstream", and + "uwsgi_next_upstream" directives. + Thanks to Piotr Sikora. + + *) Bugfix: in memory allocation error handling. + + *) Bugfix: requests might hang when using the "sendfile" and + "timer_resolution" directives on Linux. + + *) Bugfix: requests might hang when using the "sendfile" and "aio_write" + directives with subrequests. + + *) Bugfix: in the ngx_http_v2_module. + Thanks to Piotr Sikora. + + *) Bugfix: a segmentation fault might occur in a worker process when + using HTTP/2. + + *) Bugfix: requests might hang when using the "limit_rate", + "sendfile_max_chunk", "limit_req" directives, or the $r->sleep() + embedded perl method with subrequests. + + *) Bugfix: in the ngx_http_slice_module. + + Changes with nginx 1.11.12 24 Mar 2017 *) Bugfix: nginx might hog CPU; the bug had appeared in 1.11.11. diff --git a/nginx/CHANGES.ru b/nginx/CHANGES.ru index 27e490ae..029a2a75 100644 --- a/nginx/CHANGES.ru +++ b/nginx/CHANGES.ru @@ -1,4 +1,31 @@ +Изменения в nginx 1.11.13 04.04.2017 + + *) Добавление: параметр http_429 в директивах proxy_next_upstream, + fastcgi_next_upstream, scgi_next_upstream и uwsgi_next_upstream. + Спасибо Piotr Sikora. + + *) Исправление: в обработке ошибок выделения памяти. + + *) Исправление: при использовании директив sendfile и timer_resolution + на Linux запросы могли зависать. + + *) Исправление: при использовании с подзапросами директив sendfile и + aio_write запросы могли зависать. + + *) Исправление: в модуле ngx_http_v2_module. + Спасибо Piotr Sikora. + + *) Исправление: при использовании HTTP/2 в рабочем процессе мог + произойти segmentation fault. + + *) Исправление: запросы могли зависать при использовании с подзапросами + директив limit_rate, sendfile_max_chunk, limit_req или метода + $r->sleep() встроенного перла. + + *) Исправление: в модуле ngx_http_slice_module. + + Изменения в nginx 1.11.12 24.03.2017 *) Исправление: nginx мог нагружать процессор; ошибка появилась в diff --git a/nginx/src/core/nginx.h b/nginx/src/core/nginx.h index a5581a9f..5d3112f8 100644 --- a/nginx/src/core/nginx.h +++ b/nginx/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1011012 -#define NGINX_VERSION "1.11.12" +#define nginx_version 1011013 +#define NGINX_VERSION "1.11.13" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD diff --git a/nginx/src/core/ngx_cycle.c b/nginx/src/core/ngx_cycle.c index 3dfdf2e6..aee7a58f 100644 --- a/nginx/src/core/ngx_cycle.c +++ b/nginx/src/core/ngx_cycle.c @@ -115,16 +115,14 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) n = old_cycle->paths.nelts ? old_cycle->paths.nelts : 10; - cycle->paths.elts = ngx_pcalloc(pool, n * sizeof(ngx_path_t *)); - if (cycle->paths.elts == NULL) { + if (ngx_array_init(&cycle->paths, pool, n, sizeof(ngx_path_t *)) + != NGX_OK) + { ngx_destroy_pool(pool); return NULL; } - cycle->paths.nelts = 0; - cycle->paths.size = sizeof(ngx_path_t *); - cycle->paths.nalloc = n; - cycle->paths.pool = pool; + ngx_memzero(cycle->paths.elts, n * sizeof(ngx_path_t *)); if (ngx_array_init(&cycle->config_dump, pool, 1, sizeof(ngx_conf_dump_t)) @@ -175,16 +173,14 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) n = old_cycle->listening.nelts ? old_cycle->listening.nelts : 10; - cycle->listening.elts = ngx_pcalloc(pool, n * sizeof(ngx_listening_t)); - if (cycle->listening.elts == NULL) { + if (ngx_array_init(&cycle->listening, pool, n, sizeof(ngx_listening_t)) + != NGX_OK) + { ngx_destroy_pool(pool); return NULL; } - cycle->listening.nelts = 0; - cycle->listening.size = sizeof(ngx_listening_t); - cycle->listening.nalloc = n; - cycle->listening.pool = pool; + ngx_memzero(cycle->listening.elts, n * sizeof(ngx_listening_t)); ngx_queue_init(&cycle->reusable_connections_queue); @@ -768,15 +764,15 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) } n = 10; - ngx_old_cycles.elts = ngx_pcalloc(ngx_temp_pool, - n * sizeof(ngx_cycle_t *)); - if (ngx_old_cycles.elts == NULL) { + + if (ngx_array_init(&ngx_old_cycles, ngx_temp_pool, n, + sizeof(ngx_cycle_t *)) + != NGX_OK) + { exit(1); } - ngx_old_cycles.nelts = 0; - ngx_old_cycles.size = sizeof(ngx_cycle_t *); - ngx_old_cycles.nalloc = n; - ngx_old_cycles.pool = ngx_temp_pool; + + ngx_memzero(ngx_old_cycles.elts, n * sizeof(ngx_cycle_t *)); ngx_cleaner_event.handler = ngx_clean_old_cycles; ngx_cleaner_event.log = cycle->log; diff --git a/nginx/src/event/ngx_event.c b/nginx/src/event/ngx_event.c index dca41eec..57af8132 100644 --- a/nginx/src/event/ngx_event.c +++ b/nginx/src/event/ngx_event.c @@ -500,8 +500,7 @@ ngx_event_module_init(ngx_cycle_t *cycle) #endif shm.size = size; - shm.name.len = sizeof("nginx_shared_zone") - 1; - shm.name.data = (u_char *) "nginx_shared_zone"; + ngx_str_set(&shm.name, "nginx_shared_zone"); shm.log = cycle->log; if (ngx_shm_alloc(&shm) != NGX_OK) { diff --git a/nginx/src/http/modules/ngx_http_fastcgi_module.c b/nginx/src/http/modules/ngx_http_fastcgi_module.c index afdea2d0..06c19738 100644 --- a/nginx/src/http/modules/ngx_http_fastcgi_module.c +++ b/nginx/src/http/modules/ngx_http_fastcgi_module.c @@ -211,6 +211,7 @@ static ngx_conf_bitmask_t ngx_http_fastcgi_next_upstream_masks[] = { { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 }, { ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 }, { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 }, + { ngx_string("http_429"), NGX_HTTP_UPSTREAM_FT_HTTP_429 }, { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING }, { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF }, { ngx_null_string, 0 } diff --git a/nginx/src/http/modules/ngx_http_index_module.c b/nginx/src/http/modules/ngx_http_index_module.c index d3544db5..c144b31c 100644 --- a/nginx/src/http/modules/ngx_http_index_module.c +++ b/nginx/src/http/modules/ngx_http_index_module.c @@ -217,13 +217,13 @@ ngx_http_index_handler(ngx_http_request_t *r) if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err, - "%s \"%s\" failed", of.failed, path.data); - if (of.err == 0) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err, + "%s \"%s\" failed", of.failed, path.data); + #if (NGX_HAVE_OPENAT) if (of.err == NGX_EMLINK || of.err == NGX_ELOOP) diff --git a/nginx/src/http/modules/ngx_http_limit_req_module.c b/nginx/src/http/modules/ngx_http_limit_req_module.c index 2f695f21..579b13c8 100644 --- a/nginx/src/http/modules/ngx_http_limit_req_module.c +++ b/nginx/src/http/modules/ngx_http_limit_req_module.c @@ -276,6 +276,8 @@ ngx_http_limit_req_handler(ngx_http_request_t *r) r->read_event_handler = ngx_http_test_reading; r->write_event_handler = ngx_http_limit_req_delay; + + r->connection->write->delayed = 1; ngx_add_timer(r->connection->write, delay); return NGX_AGAIN; @@ -292,7 +294,7 @@ ngx_http_limit_req_delay(ngx_http_request_t *r) wev = r->connection->write; - if (!wev->timedout) { + if (wev->delayed) { if (ngx_handle_write_event(wev, 0) != NGX_OK) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); @@ -301,8 +303,6 @@ ngx_http_limit_req_delay(ngx_http_request_t *r) return; } - wev->timedout = 0; - if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; diff --git a/nginx/src/http/modules/ngx_http_log_module.c b/nginx/src/http/modules/ngx_http_log_module.c index 330dc7ec..917ed55f 100644 --- a/nginx/src/http/modules/ngx_http_log_module.c +++ b/nginx/src/http/modules/ngx_http_log_module.c @@ -552,6 +552,11 @@ ngx_http_log_script_write(ngx_http_request_t *r, ngx_http_log_script_t *script, if (ngx_open_cached_file(llcf->open_file_cache, &log, &of, r->pool) != NGX_OK) { + if (of.err == 0) { + /* simulate successful logging */ + return len; + } + ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, "%s \"%s\" failed", of.failed, log.data); /* simulate successful logging */ diff --git a/nginx/src/http/modules/ngx_http_proxy_module.c b/nginx/src/http/modules/ngx_http_proxy_module.c index 1a84d78b..e594d069 100644 --- a/nginx/src/http/modules/ngx_http_proxy_module.c +++ b/nginx/src/http/modules/ngx_http_proxy_module.c @@ -220,6 +220,7 @@ static ngx_conf_bitmask_t ngx_http_proxy_next_upstream_masks[] = { { ngx_string("http_504"), NGX_HTTP_UPSTREAM_FT_HTTP_504 }, { ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 }, { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 }, + { ngx_string("http_429"), NGX_HTTP_UPSTREAM_FT_HTTP_429 }, { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING }, { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF }, { ngx_null_string, 0 } diff --git a/nginx/src/http/modules/ngx_http_scgi_module.c b/nginx/src/http/modules/ngx_http_scgi_module.c index 288ba092..d1e37dde 100644 --- a/nginx/src/http/modules/ngx_http_scgi_module.c +++ b/nginx/src/http/modules/ngx_http_scgi_module.c @@ -82,6 +82,7 @@ static ngx_conf_bitmask_t ngx_http_scgi_next_upstream_masks[] = { { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 }, { ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 }, { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 }, + { ngx_string("http_429"), NGX_HTTP_UPSTREAM_FT_HTTP_429 }, { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING }, { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF }, { ngx_null_string, 0 } diff --git a/nginx/src/http/modules/ngx_http_slice_filter_module.c b/nginx/src/http/modules/ngx_http_slice_filter_module.c index 20059395..77583429 100644 --- a/nginx/src/http/modules/ngx_http_slice_filter_module.c +++ b/nginx/src/http/modules/ngx_http_slice_filter_module.c @@ -11,23 +11,25 @@ typedef struct { - size_t size; + size_t size; } ngx_http_slice_loc_conf_t; typedef struct { - off_t start; - off_t end; - ngx_str_t range; - ngx_str_t etag; - ngx_uint_t last; /* unsigned last:1; */ + off_t start; + off_t end; + ngx_str_t range; + ngx_str_t etag; + unsigned last:1; + unsigned active:1; + ngx_http_request_t *sr; } ngx_http_slice_ctx_t; typedef struct { - off_t start; - off_t end; - off_t complete_length; + off_t start; + off_t end; + off_t complete_length; } ngx_http_slice_content_range_t; @@ -169,6 +171,7 @@ ngx_http_slice_header_filter(ngx_http_request_t *r) } ctx->start = end; + ctx->active = 1; r->headers_out.status = NGX_HTTP_OK; r->headers_out.status_line.len = 0; @@ -209,7 +212,6 @@ ngx_http_slice_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_int_t rc; ngx_chain_t *cl; - ngx_http_request_t *sr; ngx_http_slice_ctx_t *ctx; ngx_http_slice_loc_conf_t *slcf; @@ -234,6 +236,16 @@ ngx_http_slice_body_filter(ngx_http_request_t *r, ngx_chain_t *in) return rc; } + if (ctx->sr && !ctx->sr->done) { + return rc; + } + + if (!ctx->active) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "missing slice response"); + return NGX_ERROR; + } + if (ctx->start >= ctx->end) { ngx_http_set_ctx(r, NULL, ngx_http_slice_filter_module); ngx_http_send_special(r, NGX_HTTP_LAST); @@ -244,14 +256,14 @@ ngx_http_slice_body_filter(ngx_http_request_t *r, ngx_chain_t *in) return rc; } - if (ngx_http_subrequest(r, &r->uri, &r->args, &sr, NULL, + if (ngx_http_subrequest(r, &r->uri, &r->args, &ctx->sr, NULL, NGX_HTTP_SUBREQUEST_CLONE) != NGX_OK) { return NGX_ERROR; } - ngx_http_set_ctx(sr, ctx, ngx_http_slice_filter_module); + ngx_http_set_ctx(ctx->sr, ctx, ngx_http_slice_filter_module); slcf = ngx_http_get_module_loc_conf(r, ngx_http_slice_filter_module); @@ -259,6 +271,8 @@ ngx_http_slice_body_filter(ngx_http_request_t *r, ngx_chain_t *in) ctx->start + (off_t) slcf->size - 1) - ctx->range.data; + ctx->active = 0; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http slice subrequest: \"%V\"", &ctx->range); diff --git a/nginx/src/http/modules/ngx_http_uwsgi_module.c b/nginx/src/http/modules/ngx_http_uwsgi_module.c index 2ba64af8..b7e7c121 100644 --- a/nginx/src/http/modules/ngx_http_uwsgi_module.c +++ b/nginx/src/http/modules/ngx_http_uwsgi_module.c @@ -114,6 +114,7 @@ static ngx_conf_bitmask_t ngx_http_uwsgi_next_upstream_masks[] = { { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 }, { ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 }, { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 }, + { ngx_string("http_429"), NGX_HTTP_UPSTREAM_FT_HTTP_429 }, { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING }, { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF }, { ngx_null_string, 0 } diff --git a/nginx/src/http/modules/perl/nginx.xs b/nginx/src/http/modules/perl/nginx.xs index 6716620a..cca64da3 100644 --- a/nginx/src/http/modules/perl/nginx.xs +++ b/nginx/src/http/modules/perl/nginx.xs @@ -1001,6 +1001,7 @@ sleep(r, sleep, next) ctx->next = SvRV(ST(2)); + r->connection->write->delayed = 1; ngx_add_timer(r->connection->write, sleep); r->write_event_handler = ngx_http_perl_sleep_handler; diff --git a/nginx/src/http/modules/perl/ngx_http_perl_module.c b/nginx/src/http/modules/perl/ngx_http_perl_module.c index 27963197..6d3be912 100644 --- a/nginx/src/http/modules/perl/ngx_http_perl_module.c +++ b/nginx/src/http/modules/perl/ngx_http_perl_module.c @@ -278,15 +278,16 @@ ngx_http_perl_sleep_handler(ngx_http_request_t *r) wev = r->connection->write; - if (wev->timedout) { - wev->timedout = 0; - ngx_http_perl_handle_request(r); + if (wev->delayed) { + + if (ngx_handle_write_event(wev, 0) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + return; } - if (ngx_handle_write_event(wev, 0) != NGX_OK) { - ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - } + ngx_http_perl_handle_request(r); } diff --git a/nginx/src/http/ngx_http_copy_filter_module.c b/nginx/src/http/ngx_http_copy_filter_module.c index c696fb6f..c8ad5dae 100644 --- a/nginx/src/http/ngx_http_copy_filter_module.c +++ b/nginx/src/http/ngx_http_copy_filter_module.c @@ -187,15 +187,24 @@ static void ngx_http_copy_aio_event_handler(ngx_event_t *ev) { ngx_event_aio_t *aio; + ngx_connection_t *c; ngx_http_request_t *r; aio = ev->data; r = aio->data; + c = r->connection; + + ngx_http_set_log_request(c->log, r); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http aio: \"%V?%V\"", &r->uri, &r->args); r->main->blocked--; r->aio = 0; - r->connection->write->handler(r->connection->write); + r->write_event_handler(r); + + ngx_http_run_posted_requests(c); } @@ -300,14 +309,33 @@ ngx_http_copy_thread_handler(ngx_thread_task_t *task, ngx_file_t *file) static void ngx_http_copy_thread_event_handler(ngx_event_t *ev) { + ngx_connection_t *c; ngx_http_request_t *r; r = ev->data; + c = r->connection; + + ngx_http_set_log_request(c->log, r); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http thread: \"%V?%V\"", &r->uri, &r->args); r->main->blocked--; r->aio = 0; - r->connection->write->handler(r->connection->write); + if (r->done) { + /* + * trigger connection event handler if the subrequest was + * already finalized; this can happen if the handler is used + * for sendfile() in threads + */ + + c->write->handler(c->write); + + } else { + r->write_event_handler(r); + ngx_http_run_posted_requests(c); + } } #endif diff --git a/nginx/src/http/ngx_http_core_module.c b/nginx/src/http/ngx_http_core_module.c index c3957ba3..292671d1 100644 --- a/nginx/src/http/ngx_http_core_module.c +++ b/nginx/src/http/ngx_http_core_module.c @@ -1314,6 +1314,11 @@ ngx_http_core_try_files_phase(ngx_http_request_t *r, if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { + if (of.err == 0) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + if (of.err != NGX_ENOENT && of.err != NGX_ENOTDIR && of.err != NGX_ENAMETOOLONG) diff --git a/nginx/src/http/ngx_http_header_filter_module.c b/nginx/src/http/ngx_http_header_filter_module.c index ddae6137..c09c5191 100644 --- a/nginx/src/http/ngx_http_header_filter_module.c +++ b/nginx/src/http/ngx_http_header_filter_module.c @@ -101,12 +101,16 @@ static ngx_str_t ngx_http_status_lines[] = { ngx_null_string, /* "419 unused" */ ngx_null_string, /* "420 unused" */ ngx_string("421 Misdirected Request"), - - /* ngx_null_string, */ /* "422 Unprocessable Entity" */ - /* ngx_null_string, */ /* "423 Locked" */ - /* ngx_null_string, */ /* "424 Failed Dependency" */ - -#define NGX_HTTP_LAST_4XX 422 + ngx_null_string, /* "422 Unprocessable Entity" */ + ngx_null_string, /* "423 Locked" */ + ngx_null_string, /* "424 Failed Dependency" */ + ngx_null_string, /* "425 unused" */ + ngx_null_string, /* "426 Upgrade Required" */ + ngx_null_string, /* "427 unused" */ + ngx_null_string, /* "428 Precondition Required" */ + ngx_string("429 Too Many Requests"), + +#define NGX_HTTP_LAST_4XX 430 #define NGX_HTTP_OFF_5XX (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX) ngx_string("500 Internal Server Error"), diff --git a/nginx/src/http/ngx_http_request.c b/nginx/src/http/ngx_http_request.c index dd549102..476f0398 100644 --- a/nginx/src/http/ngx_http_request.c +++ b/nginx/src/http/ngx_http_request.c @@ -2198,6 +2198,11 @@ ngx_http_request_handler(ngx_event_t *ev) ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http run request: \"%V?%V\"", &r->uri, &r->args); + if (ev->delayed && ev->timedout) { + ev->delayed = 0; + ev->timedout = 0; + } + if (ev->write) { r->write_event_handler(r); @@ -2607,7 +2612,7 @@ ngx_http_set_write_handler(ngx_http_request_t *r) static void ngx_http_writer(ngx_http_request_t *r) { - int rc; + ngx_int_t rc; ngx_event_t *wev; ngx_connection_t *c; ngx_http_core_loc_conf_t *clcf; @@ -2621,34 +2626,22 @@ ngx_http_writer(ngx_http_request_t *r) clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module); if (wev->timedout) { - if (!wev->delayed) { - ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, - "client timed out"); - c->timedout = 1; - - ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); - return; - } - - wev->timedout = 0; - wev->delayed = 0; - - if (!wev->ready) { - ngx_add_timer(wev, clcf->send_timeout); - - if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { - ngx_http_close_request(r, 0); - } - - return; - } + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "client timed out"); + c->timedout = 1; + ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); + return; } if (wev->delayed || r->aio) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http writer delayed"); + if (!wev->delayed) { + ngx_add_timer(wev, clcf->send_timeout); + } + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ngx_http_close_request(r, 0); } @@ -2659,7 +2652,7 @@ ngx_http_writer(ngx_http_request_t *r) rc = ngx_http_output_filter(r, NULL); ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http writer output filter: %d, \"%V?%V\"", + "http writer output filter: %i, \"%V?%V\"", rc, &r->uri, &r->args); if (rc == NGX_ERROR) { diff --git a/nginx/src/http/ngx_http_request.h b/nginx/src/http/ngx_http_request.h index 780a99f7..a68b9069 100644 --- a/nginx/src/http/ngx_http_request.h +++ b/nginx/src/http/ngx_http_request.h @@ -98,6 +98,7 @@ #define NGX_HTTP_UNSUPPORTED_MEDIA_TYPE 415 #define NGX_HTTP_RANGE_NOT_SATISFIABLE 416 #define NGX_HTTP_MISDIRECTED_REQUEST 421 +#define NGX_HTTP_TOO_MANY_REQUESTS 429 /* Our own HTTP codes */ diff --git a/nginx/src/http/ngx_http_script.c b/nginx/src/http/ngx_http_script.c index cc4d6794..96f3ec69 100644 --- a/nginx/src/http/ngx_http_script.c +++ b/nginx/src/http/ngx_http_script.c @@ -1513,6 +1513,12 @@ ngx_http_script_file_code(ngx_http_script_engine_t *e) if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { + if (of.err == 0) { + e->ip = ngx_http_script_exit; + e->status = NGX_HTTP_INTERNAL_SERVER_ERROR; + return; + } + if (of.err != NGX_ENOENT && of.err != NGX_ENOTDIR && of.err != NGX_ENAMETOOLONG) diff --git a/nginx/src/http/ngx_http_special_response.c b/nginx/src/http/ngx_http_special_response.c index 9de0d15a..c9b10172 100644 --- a/nginx/src/http/ngx_http_special_response.c +++ b/nginx/src/http/ngx_http_special_response.c @@ -225,6 +225,14 @@ static char ngx_http_error_421_page[] = ; +static char ngx_http_error_429_page[] = +"" CRLF +"429 Too Many Requests " CRLF +"" CRLF +"

429 Too Many Requests

" CRLF +; + + static char ngx_http_error_494_page[] = "" CRLF "400 Request Header Or Cookie Too Large " @@ -354,8 +362,16 @@ static ngx_str_t ngx_http_error_pages[] = { ngx_null_string, /* 419 */ ngx_null_string, /* 420 */ ngx_string(ngx_http_error_421_page), - -#define NGX_HTTP_LAST_4XX 422 + ngx_null_string, /* 422 */ + ngx_null_string, /* 423 */ + ngx_null_string, /* 424 */ + ngx_null_string, /* 425 */ + ngx_null_string, /* 426 */ + ngx_null_string, /* 427 */ + ngx_null_string, /* 428 */ + ngx_string(ngx_http_error_429_page), + +#define NGX_HTTP_LAST_4XX 430 #define NGX_HTTP_OFF_5XX (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX) ngx_string(ngx_http_error_494_page), /* 494, request header too large */ diff --git a/nginx/src/http/ngx_http_upstream.c b/nginx/src/http/ngx_http_upstream.c index 14046935..36952860 100644 --- a/nginx/src/http/ngx_http_upstream.c +++ b/nginx/src/http/ngx_http_upstream.c @@ -436,6 +436,7 @@ static ngx_http_upstream_next_t ngx_http_upstream_next_errors[] = { { 504, NGX_HTTP_UPSTREAM_FT_HTTP_504 }, { 403, NGX_HTTP_UPSTREAM_FT_HTTP_403 }, { 404, NGX_HTTP_UPSTREAM_FT_HTTP_404 }, + { 429, NGX_HTTP_UPSTREAM_FT_HTTP_429 }, { 0, 0 } }; @@ -1232,6 +1233,11 @@ ngx_http_upstream_handler(ngx_event_t *ev) ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http upstream request: \"%V?%V\"", &r->uri, &r->args); + if (ev->delayed && ev->timedout) { + ev->delayed = 0; + ev->timedout = 0; + } + if (ev->write) { u->write_event_handler(r, u); @@ -3736,9 +3742,19 @@ ngx_http_upstream_thread_event_handler(ngx_event_t *ev) r->main->blocked--; r->aio = 0; - r->write_event_handler(r); + if (r->done) { + /* + * trigger connection event handler if the subrequest was + * already finalized; this can happen if the handler is used + * for sendfile() in threads + */ + + c->write->handler(c->write); - ngx_http_run_posted_requests(c); + } else { + r->write_event_handler(r); + ngx_http_run_posted_requests(c); + } } #endif @@ -3786,31 +3802,9 @@ ngx_http_upstream_process_downstream(ngx_http_request_t *r) if (wev->timedout) { - if (wev->delayed) { - - wev->timedout = 0; - wev->delayed = 0; - - if (!wev->ready) { - ngx_add_timer(wev, p->send_timeout); - - if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) { - ngx_http_upstream_finalize_request(r, u, NGX_ERROR); - } - - return; - } - - if (ngx_event_pipe(p, wev->write) == NGX_ABORT) { - ngx_http_upstream_finalize_request(r, u, NGX_ERROR); - return; - } - - } else { - p->downstream_error = 1; - c->timedout = 1; - ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); - } + p->downstream_error = 1; + c->timedout = 1; + ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); } else { @@ -3855,30 +3849,8 @@ ngx_http_upstream_process_upstream(ngx_http_request_t *r, 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"); - } + p->upstream_error = 1; + ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); } else { @@ -4155,6 +4127,10 @@ ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u, status = NGX_HTTP_NOT_FOUND; break; + case NGX_HTTP_UPSTREAM_FT_HTTP_429: + status = NGX_HTTP_TOO_MANY_REQUESTS; + break; + /* * NGX_HTTP_UPSTREAM_FT_BUSY_LOCK and NGX_HTTP_UPSTREAM_FT_MAX_WAITING * never reach here diff --git a/nginx/src/http/ngx_http_upstream.h b/nginx/src/http/ngx_http_upstream.h index 625889b6..c552ac0c 100644 --- a/nginx/src/http/ngx_http_upstream.h +++ b/nginx/src/http/ngx_http_upstream.h @@ -26,10 +26,11 @@ #define NGX_HTTP_UPSTREAM_FT_HTTP_504 0x00000080 #define NGX_HTTP_UPSTREAM_FT_HTTP_403 0x00000100 #define NGX_HTTP_UPSTREAM_FT_HTTP_404 0x00000200 -#define NGX_HTTP_UPSTREAM_FT_UPDATING 0x00000400 -#define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK 0x00000800 -#define NGX_HTTP_UPSTREAM_FT_MAX_WAITING 0x00001000 -#define NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT 0x00002000 +#define NGX_HTTP_UPSTREAM_FT_HTTP_429 0x00000400 +#define NGX_HTTP_UPSTREAM_FT_UPDATING 0x00000800 +#define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK 0x00001000 +#define NGX_HTTP_UPSTREAM_FT_MAX_WAITING 0x00002000 +#define NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT 0x00004000 #define NGX_HTTP_UPSTREAM_FT_NOLIVE 0x40000000 #define NGX_HTTP_UPSTREAM_FT_OFF 0x80000000 @@ -38,7 +39,8 @@ |NGX_HTTP_UPSTREAM_FT_HTTP_503 \ |NGX_HTTP_UPSTREAM_FT_HTTP_504 \ |NGX_HTTP_UPSTREAM_FT_HTTP_403 \ - |NGX_HTTP_UPSTREAM_FT_HTTP_404) + |NGX_HTTP_UPSTREAM_FT_HTTP_404 \ + |NGX_HTTP_UPSTREAM_FT_HTTP_429) #define NGX_HTTP_UPSTREAM_INVALID_HEADER 40 diff --git a/nginx/src/http/v2/ngx_http_v2.c b/nginx/src/http/v2/ngx_http_v2.c index f3050f1d..55db58e7 100644 --- a/nginx/src/http/v2/ngx_http_v2.c +++ b/nginx/src/http/v2/ngx_http_v2.c @@ -783,9 +783,12 @@ ngx_http_v2_state_head(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) static u_char * ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) { + size_t size; ngx_http_v2_node_t *node; ngx_http_v2_stream_t *stream; + size = h2c->state.length; + if (h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG) { if (h2c->state.length == 0) { @@ -802,33 +805,33 @@ ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) } h2c->state.padding = *pos++; - h2c->state.length--; - if (h2c->state.padding > h2c->state.length) { + if (h2c->state.padding >= size) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, "client sent padded DATA frame " "with incorrect length: %uz, padding: %uz", - h2c->state.length, h2c->state.padding); + size, h2c->state.padding); - return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_PROTOCOL_ERROR); } - h2c->state.length -= h2c->state.padding; + h2c->state.length -= 1 + h2c->state.padding; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, "http2 DATA frame"); - if (h2c->state.length > h2c->recv_window) { + if (size > h2c->recv_window) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, "client violated connection flow control: " "received DATA frame length %uz, available window %uz", - h2c->state.length, h2c->recv_window); + size, h2c->recv_window); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_FLOW_CTRL_ERROR); } - h2c->recv_window -= h2c->state.length; + h2c->recv_window -= size; if (h2c->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4) { @@ -854,11 +857,11 @@ ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) stream = node->stream; - if (h2c->state.length > stream->recv_window) { + if (size > stream->recv_window) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, "client violated flow control for stream %ui: " "received DATA frame length %uz, available window %uz", - node->id, h2c->state.length, stream->recv_window); + node->id, size, stream->recv_window); if (ngx_http_v2_terminate_stream(h2c, stream, NGX_HTTP_V2_FLOW_CTRL_ERROR) @@ -871,7 +874,7 @@ ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) return ngx_http_v2_state_skip_padded(h2c, pos, end); } - stream->recv_window -= h2c->state.length; + stream->recv_window -= size; if (stream->no_flow_control && stream->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4) @@ -938,7 +941,7 @@ ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos, if (size >= h2c->state.length) { size = h2c->state.length; - stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG; + stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG; } r = stream->request; @@ -1053,7 +1056,8 @@ ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos, "with incorrect length: %uz, padding: %uz", h2c->state.length, h2c->state.padding); - return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_PROTOCOL_ERROR); } h2c->state.length -= h2c->state.padding; @@ -1901,7 +1905,7 @@ ngx_http_v2_state_rst_stream(ngx_http_v2_connection_t *h2c, u_char *pos, if (node == NULL || node->stream == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "unknown http2 stream"); + "unknown http2 stream"); return ngx_http_v2_state_complete(h2c, pos, end); } @@ -2015,6 +2019,7 @@ ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos, break; case NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING: + if (value > NGX_HTTP_V2_MAX_FRAME_SIZE || value < NGX_HTTP_V2_DEFAULT_FRAME_SIZE) { @@ -3072,7 +3077,7 @@ ngx_http_v2_pseudo_header(ngx_http_request_t *r, ngx_http_v2_header_t *header) } ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent unknown pseudo header \"%V\"", + "client sent unknown pseudo-header \":%V\"", &header->name); return NGX_DECLINED; @@ -3219,14 +3224,14 @@ ngx_http_v2_parse_scheme(ngx_http_request_t *r, ngx_http_v2_header_t *header) { if (r->schema_start) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent duplicate :schema header"); + "client sent duplicate :scheme header"); return NGX_DECLINED; } if (header->value.len == 0) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent empty :schema header"); + "client sent empty :scheme header"); return NGX_DECLINED; } @@ -4130,6 +4135,14 @@ ngx_http_v2_handle_connection_handler(ngx_event_t *rev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http2 handle connection handler"); + c = rev->data; + h2c = c->data; + + if (c->error) { + ngx_http_v2_finalize_connection(h2c, 0); + return; + } + rev->handler = ngx_http_v2_read_handler; if (rev->ready) { @@ -4137,9 +4150,6 @@ ngx_http_v2_handle_connection_handler(ngx_event_t *rev) return; } - c = rev->data; - h2c = c->data; - if (h2c->last_out && ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) { ngx_http_v2_finalize_connection(h2c, 0); return; @@ -4262,7 +4272,10 @@ ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c, if (stream->queued) { stream->queued = 0; + ev = fc->write; + ev->active = 0; + ev->ready = 1; } else { ev = fc->read; diff --git a/nginx/src/http/v2/ngx_http_v2.h b/nginx/src/http/v2/ngx_http_v2.h index cddfccd2..7d2a2ea0 100644 --- a/nginx/src/http/v2/ngx_http_v2.h +++ b/nginx/src/http/v2/ngx_http_v2.h @@ -249,8 +249,8 @@ ngx_http_v2_queue_blocked_frame(ngx_http_v2_connection_t *h2c, { ngx_http_v2_out_frame_t **out; - for (out = &h2c->last_out; *out; out = &(*out)->next) - { + for (out = &h2c->last_out; *out; out = &(*out)->next) { + if ((*out)->blocked || (*out)->stream == NULL) { break; } diff --git a/nginx/src/http/v2/ngx_http_v2_filter_module.c b/nginx/src/http/v2/ngx_http_v2_filter_module.c index 42fa0c50..dac50463 100644 --- a/nginx/src/http/v2/ngx_http_v2_filter_module.c +++ b/nginx/src/http/v2/ngx_http_v2_filter_module.c @@ -769,6 +769,8 @@ ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos, rest -= frame_size; if (rest) { + frame->length += NGX_HTTP_V2_FRAME_HEADER_SIZE; + type = NGX_HTTP_V2_CONTINUATION_FRAME; flags = NGX_HTTP_V2_NO_FLAG; continue; @@ -1209,6 +1211,9 @@ ngx_http_v2_headers_frame_handler(ngx_http_v2_connection_t *h2c, "http2:%ui HEADERS frame %p was sent", stream->node->id, frame); + stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE + + frame->length; + ngx_http_v2_handle_frame(stream, frame); ngx_http_v2_handle_stream(h2c, stream); diff --git a/nginx/src/os/unix/ngx_linux_sendfile_chain.c b/nginx/src/os/unix/ngx_linux_sendfile_chain.c index 3c0696a0..b44724cd 100644 --- a/nginx/src/os/unix/ngx_linux_sendfile_chain.c +++ b/nginx/src/os/unix/ngx_linux_sendfile_chain.c @@ -20,8 +20,8 @@ static ssize_t ngx_linux_sendfile(ngx_connection_t *c, ngx_buf_t *file, #error sendfile64() is required! #endif -static ngx_int_t ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, - size_t size, size_t *sent); +static ssize_t ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, + size_t size); static void ngx_linux_sendfile_thread_handler(void *data, ngx_log_t *log); #endif @@ -56,10 +56,6 @@ ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) ngx_chain_t *cl; ngx_iovec_t header; struct iovec headers[NGX_IOVS_PREALLOCATE]; -#if (NGX_THREADS) - ngx_int_t rc; - ngx_uint_t thread_handled, thread_complete; -#endif wev = c->write; @@ -82,10 +78,6 @@ ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) for ( ;; ) { prev_send = send; -#if (NGX_THREADS) - thread_handled = 0; - thread_complete = 0; -#endif /* create the iovec and coalesce the neighbouring bufs */ @@ -179,38 +171,19 @@ ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) } #endif -#if (NGX_THREADS) - if (file->file->thread_handler) { - rc = ngx_linux_sendfile_thread(c, file, file_size, &sent); - - switch (rc) { - case NGX_OK: - thread_handled = 1; - break; - - case NGX_DONE: - thread_complete = 1; - break; - - case NGX_AGAIN: - break; - - default: /* NGX_ERROR */ - return NGX_CHAIN_ERROR; - } - - } else -#endif - { - n = ngx_linux_sendfile(c, file, file_size); + n = ngx_linux_sendfile(c, file, file_size); - if (n == NGX_ERROR) { - return NGX_CHAIN_ERROR; - } + if (n == NGX_ERROR) { + return NGX_CHAIN_ERROR; + } - sent = (n == NGX_AGAIN) ? 0 : n; + if (n == NGX_DONE) { + /* thread task posted */ + return in; } + sent = (n == NGX_AGAIN) ? 0 : n; + } else { n = ngx_writev(c, &header); @@ -225,21 +198,27 @@ ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) in = ngx_chain_update_sent(in, sent); - if ((size_t) (send - prev_send) != sent) { -#if (NGX_THREADS) - if (thread_handled) { - return in; - } - - if (thread_complete) { - send = prev_send + sent; - continue; - } -#endif + if (n == NGX_AGAIN) { wev->ready = 0; return in; } + if ((size_t) (send - prev_send) != sent) { + + /* + * sendfile() on Linux 4.3+ might be interrupted at any time, + * and provides no indication if it was interrupted or not, + * so we have to retry till an explicit EAGAIN + * + * sendfile() in threads can also report less bytes written + * than we are prepared to send now, since it was started in + * some point in the past, so we again have to retry + */ + + send = prev_send + sent; + continue; + } + if (send >= limit || in == NULL) { return in; } @@ -258,6 +237,14 @@ ngx_linux_sendfile(ngx_connection_t *c, ngx_buf_t *file, size_t size) ssize_t n; ngx_err_t err; +#if (NGX_THREADS) + + if (file->file->thread_handler) { + return ngx_linux_sendfile_thread(c, file, size); + } + +#endif + #if (NGX_HAVE_SENDFILE64) offset = file->file_pos; #else @@ -324,9 +311,8 @@ typedef struct { } ngx_linux_sendfile_ctx_t; -static ngx_int_t -ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, size_t size, - size_t *sent) +static ssize_t +ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, size_t size) { ngx_event_t *wev; ngx_thread_task_t *task; @@ -356,10 +342,14 @@ ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, size_t size, task->event.complete = 0; if (ctx->err == NGX_EAGAIN) { - *sent = 0; + /* + * if wev->complete is set, this means that a write event + * happened while we were waiting for the thread task, so + * we have to retry sending even on EAGAIN + */ if (wev->complete) { - return NGX_DONE; + return 0; } return NGX_AGAIN; @@ -384,13 +374,7 @@ ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, size_t size, return NGX_ERROR; } - *sent = ctx->sent; - - if (ctx->sent == ctx->size || wev->complete) { - return NGX_DONE; - } - - return NGX_AGAIN; + return ctx->sent; } if (task->event.active && ctx->file == file) { @@ -399,9 +383,7 @@ ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, size_t size, * or multiple calls of the next body filter from a filter */ - *sent = 0; - - return NGX_OK; + return NGX_DONE; } ctx->file = file; @@ -414,9 +396,7 @@ ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, size_t size, return NGX_ERROR; } - *sent = 0; - - return NGX_OK; + return NGX_DONE; } diff --git a/nginx/src/stream/ngx_stream_log_module.c b/nginx/src/stream/ngx_stream_log_module.c index 6b293403..466bdda5 100644 --- a/nginx/src/stream/ngx_stream_log_module.c +++ b/nginx/src/stream/ngx_stream_log_module.c @@ -443,6 +443,11 @@ ngx_stream_log_script_write(ngx_stream_session_t *s, s->connection->pool) != NGX_OK) { + if (of.err == 0) { + /* simulate successful logging */ + return len; + } + ngx_log_error(NGX_LOG_CRIT, s->connection->log, ngx_errno, "%s \"%s\" failed", of.failed, log.data); /* simulate successful logging */