From 826fb0d45c964d597975c1a08070cf9a579a08ac Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 9 Jul 2020 17:33:22 +0300 Subject: [PATCH 01/18] Version bump. --- src/core/nginx.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/nginx.h b/src/core/nginx.h index 49d35c2b9..10799a453 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1019001 -#define NGINX_VERSION "1.19.1" +#define nginx_version 1019002 +#define NGINX_VERSION "1.19.2" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD From 5cef7de7a116bab3af9097dac5a22f7652be4273 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 9 Jul 2020 16:21:37 +0300 Subject: [PATCH 02/18] Slice filter: clear original Accept-Ranges. The slice filter allows ranges for the response by setting the r->allow_ranges flag, which enables the range filter. If the range was not requested, the range filter adds an Accept-Ranges header to the response to signal the support for ranges. Previously, if an Accept-Ranges header was already present in the first slice response, client received two copies of this header. Now, the slice filter removes the Accept-Ranges header from the response prior to setting the r->allow_ranges flag. --- src/http/modules/ngx_http_slice_filter_module.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/http/modules/ngx_http_slice_filter_module.c b/src/http/modules/ngx_http_slice_filter_module.c index c1edbca2b..186380a2f 100644 --- a/src/http/modules/ngx_http_slice_filter_module.c +++ b/src/http/modules/ngx_http_slice_filter_module.c @@ -180,6 +180,11 @@ ngx_http_slice_header_filter(ngx_http_request_t *r) r->headers_out.content_range->hash = 0; r->headers_out.content_range = NULL; + if (r->headers_out.accept_ranges) { + r->headers_out.accept_ranges->hash = 0; + r->headers_out.accept_ranges = NULL; + } + r->allow_ranges = 1; r->subrequest_ranges = 1; r->single_range = 1; From 80daef96f1e1de397b8bc455ac3a3fbc060607cf Mon Sep 17 00:00:00 2001 From: Ruslan Ermilov Date: Tue, 21 Jul 2020 20:34:29 +0300 Subject: [PATCH 03/18] Core: close PID file when writing fails. Reported by Jinhua Tan. --- src/core/ngx_cycle.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c index 764cf46ba..d7479fa41 100644 --- a/src/core/ngx_cycle.c +++ b/src/core/ngx_cycle.c @@ -1009,6 +1009,7 @@ ngx_int_t ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log) { size_t len; + ngx_int_t rc; ngx_uint_t create; ngx_file_t file; u_char pid[NGX_INT64_LEN + 2]; @@ -1033,11 +1034,13 @@ ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log) return NGX_ERROR; } + rc = NGX_OK; + if (!ngx_test_config) { len = ngx_snprintf(pid, NGX_INT64_LEN + 2, "%P%N", ngx_pid) - pid; if (ngx_write_file(&file, pid, len, 0) == NGX_ERROR) { - return NGX_ERROR; + rc = NGX_ERROR; } } @@ -1046,7 +1049,7 @@ ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log) ngx_close_file_n " \"%s\" failed", file.name.data); } - return NGX_OK; + return rc; } From 4dd43dfca71f3fc2c6768606ff3700a4317a9176 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 22 Jul 2020 22:16:19 +0300 Subject: [PATCH 04/18] Xslt: disabled ranges. Previously, the document generated by the xslt filter was always fully sent to client even if a range was requested and response status was 206 with appropriate Content-Range. The xslt module is unable to serve a range because of suspending the header filter chain. By the moment full response xml is buffered by the xslt filter, range header filter is not called yet, but the range body filter has already been called and did nothing. The fix is to disable ranges by resetting the r->allow_ranges flag much like the image filter that employs a similar technique. --- src/http/modules/ngx_http_xslt_filter_module.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/http/modules/ngx_http_xslt_filter_module.c b/src/http/modules/ngx_http_xslt_filter_module.c index b2f107dc0..8afd656af 100644 --- a/src/http/modules/ngx_http_xslt_filter_module.c +++ b/src/http/modules/ngx_http_xslt_filter_module.c @@ -233,6 +233,7 @@ ngx_http_xslt_header_filter(ngx_http_request_t *r) ngx_http_set_ctx(r, ctx, ngx_http_xslt_filter_module); r->main_filter_need_in_memory = 1; + r->allow_ranges = 0; return NGX_OK; } From 4ee66b3f7bb176915cfb0e7f3ab37d06fd6924bd Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Thu, 23 Jul 2020 17:31:09 +0300 Subject: [PATCH 05/18] OCSP: fixed certificate reference leak. --- src/event/ngx_event_openssl_stapling.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c index 0e79d6cc4..9d92421d6 100644 --- a/src/event/ngx_event_openssl_stapling.c +++ b/src/event/ngx_event_openssl_stapling.c @@ -883,6 +883,7 @@ ngx_ssl_ocsp_validate(ngx_connection_t *c) ocsp = ngx_pcalloc(c->pool, sizeof(ngx_ssl_ocsp_t)); if (ocsp == NULL) { + X509_free(cert); return NGX_ERROR; } @@ -899,6 +900,7 @@ ngx_ssl_ocsp_validate(ngx_connection_t *c) if (ocsp->certs) { ocsp->certs = X509_chain_up_ref(ocsp->certs); if (ocsp->certs == NULL) { + X509_free(cert); return NGX_ERROR; } } @@ -910,6 +912,7 @@ ngx_ssl_ocsp_validate(ngx_connection_t *c) if (store == NULL) { ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "SSL_CTX_get_cert_store() failed"); + X509_free(cert); return NGX_ERROR; } @@ -917,6 +920,7 @@ ngx_ssl_ocsp_validate(ngx_connection_t *c) if (store_ctx == NULL) { ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "X509_STORE_CTX_new() failed"); + X509_free(cert); return NGX_ERROR; } @@ -926,6 +930,7 @@ ngx_ssl_ocsp_validate(ngx_connection_t *c) ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "X509_STORE_CTX_init() failed"); X509_STORE_CTX_free(store_ctx); + X509_free(cert); return NGX_ERROR; } @@ -933,6 +938,7 @@ ngx_ssl_ocsp_validate(ngx_connection_t *c) if (rc <= 0) { ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "X509_verify_cert() failed"); X509_STORE_CTX_free(store_ctx); + X509_free(cert); return NGX_ERROR; } @@ -941,12 +947,15 @@ ngx_ssl_ocsp_validate(ngx_connection_t *c) ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "X509_STORE_CTX_get1_chain() failed"); X509_STORE_CTX_free(store_ctx); + X509_free(cert); return NGX_ERROR; } X509_STORE_CTX_free(store_ctx); } + X509_free(cert); + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "ssl ocsp validate, certs:%d", sk_X509_num(ocsp->certs)); From c3db6f729fcaece7943757c4dfb4ec58304cbad4 Mon Sep 17 00:00:00 2001 From: balus Date: Mon, 27 Jul 2020 13:21:51 +0300 Subject: [PATCH 06/18] Core: enclosed parameters of the ngx_buf.h macros in parentheses. --- src/core/ngx_buf.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/core/ngx_buf.h b/src/core/ngx_buf.h index 12781a782..4b665629c 100644 --- a/src/core/ngx_buf.h +++ b/src/core/ngx_buf.h @@ -125,20 +125,20 @@ typedef struct { #define NGX_CHAIN_ERROR (ngx_chain_t *) NGX_ERROR -#define ngx_buf_in_memory(b) (b->temporary || b->memory || b->mmap) -#define ngx_buf_in_memory_only(b) (ngx_buf_in_memory(b) && !b->in_file) +#define ngx_buf_in_memory(b) ((b)->temporary || (b)->memory || (b)->mmap) +#define ngx_buf_in_memory_only(b) (ngx_buf_in_memory(b) && !(b)->in_file) #define ngx_buf_special(b) \ - ((b->flush || b->last_buf || b->sync) \ - && !ngx_buf_in_memory(b) && !b->in_file) + (((b)->flush || (b)->last_buf || (b)->sync) \ + && !ngx_buf_in_memory(b) && !(b)->in_file) #define ngx_buf_sync_only(b) \ - (b->sync \ - && !ngx_buf_in_memory(b) && !b->in_file && !b->flush && !b->last_buf) + ((b)->sync && !ngx_buf_in_memory(b) \ + && !(b)->in_file && !(b)->flush && !(b)->last_buf) #define ngx_buf_size(b) \ - (ngx_buf_in_memory(b) ? (off_t) (b->last - b->pos): \ - (b->file_last - b->file_pos)) + (ngx_buf_in_memory(b) ? (off_t) ((b)->last - (b)->pos): \ + ((b)->file_last - (b)->file_pos)) ngx_buf_t *ngx_create_temp_buf(ngx_pool_t *pool, size_t size); ngx_chain_t *ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs); @@ -149,8 +149,8 @@ ngx_chain_t *ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs); ngx_chain_t *ngx_alloc_chain_link(ngx_pool_t *pool); #define ngx_free_chain(pool, cl) \ - cl->next = pool->chain; \ - pool->chain = cl + (cl)->next = (pool)->chain; \ + (pool)->chain = (cl) From d2744ad26fef1e4f4f6e9c12e95b57866345c071 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Mon, 27 Jul 2020 16:02:15 +0300 Subject: [PATCH 07/18] FastCGI: fixed zero size buf alerts on extra data (ticket #2018). After 05e42236e95b (1.19.1) responses with extra data might result in zero size buffers being generated and "zero size buf" alerts in writer (if f->rest happened to be 0 when processing additional stdout data). --- src/http/modules/ngx_http_fastcgi_module.c | 28 +++++++++++++++++----- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c index e50d1a70d..5191880e3 100644 --- a/src/http/modules/ngx_http_fastcgi_module.c +++ b/src/http/modules/ngx_http_fastcgi_module.c @@ -2306,6 +2306,18 @@ ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) break; } + if (f->rest == -2) { + f->rest = r->upstream->headers_in.content_length_n; + } + + if (f->rest == 0) { + ngx_log_error(NGX_LOG_WARN, p->log, 0, + "upstream sent more data than specified in " + "\"Content-Length\" header"); + p->upstream_done = 1; + break; + } + cl = ngx_chain_get_free_buf(p->pool, &p->free); if (cl == NULL) { return NGX_ERROR; @@ -2349,11 +2361,7 @@ ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) b->last = f->last; } - if (f->rest == -2) { - f->rest = r->upstream->headers_in.content_length_n; - } - - if (f->rest >= 0) { + if (f->rest > 0) { if (b->last - b->pos > f->rest) { ngx_log_error(NGX_LOG_WARN, p->log, 0, @@ -2564,6 +2572,14 @@ ngx_http_fastcgi_non_buffered_filter(void *data, ssize_t bytes) break; } + if (f->rest == 0) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "upstream sent more data than specified in " + "\"Content-Length\" header"); + u->length = 0; + break; + } + cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs); if (cl == NULL) { return NGX_ERROR; @@ -2594,7 +2610,7 @@ ngx_http_fastcgi_non_buffered_filter(void *data, ssize_t bytes) b->last = f->last; } - if (f->rest >= 0) { + if (f->rest > 0) { if (b->last - b->pos > f->rest) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, From bd7dad5b0eb9f667a9c66ea5175a017ac51cd027 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 6 Aug 2020 05:02:22 +0300 Subject: [PATCH 08/18] Added size check to ngx_http_alloc_large_header_buffer(). This ensures that copying won't write more than the buffer size even if the buffer comes from hc->free and it is smaller than the large client header buffer size in the virtual host configuration. This might happen if size of large client header buffers is different in name-based virtual hosts, similarly to the problem with number of buffers fixed in 6926:e662cbf1b932. --- src/http/ngx_http_request.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 6feb6cc31..257c4064b 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1647,6 +1647,12 @@ ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http large header copy: %uz", r->header_in->pos - old); + if (r->header_in->pos - old > b->end - b->start) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "too large header to copy"); + return NGX_ERROR; + } + new = b->start; ngx_memcpy(new, old, r->header_in->pos - old); From 9edc93fe0ed60bac336d11f7d20d3c2ed9db3227 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 6 Aug 2020 05:02:44 +0300 Subject: [PATCH 09/18] Request body: all read data are now sent to filters. This is a prerequisite for the next change to allow large reads on chunk boundaries. --- src/http/ngx_http_request_body.c | 53 ++++++-------------------------- 1 file changed, 10 insertions(+), 43 deletions(-) diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index c4f092e59..d21c45863 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -282,28 +282,12 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) for ( ;; ) { if (rb->buf->last == rb->buf->end) { - if (rb->buf->pos != rb->buf->last) { + /* update chains */ - /* pass buffer to request body filter chain */ + rc = ngx_http_request_body_filter(r, NULL); - out.buf = rb->buf; - out.next = NULL; - - rc = ngx_http_request_body_filter(r, &out); - - if (rc != NGX_OK) { - return rc; - } - - } else { - - /* update chains */ - - rc = ngx_http_request_body_filter(r, NULL); - - if (rc != NGX_OK) { - return rc; - } + if (rc != NGX_OK) { + return rc; } if (rb->busy != NULL) { @@ -355,17 +339,15 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) rb->buf->last += n; r->request_length += n; - if (n == rest) { - /* pass buffer to request body filter chain */ + /* pass buffer to request body filter chain */ - out.buf = rb->buf; - out.next = NULL; + out.buf = rb->buf; + out.next = NULL; - rc = ngx_http_request_body_filter(r, &out); + rc = ngx_http_request_body_filter(r, &out); - if (rc != NGX_OK) { - return rc; - } + if (rc != NGX_OK) { + return rc; } if (rb->rest == 0) { @@ -386,21 +368,6 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) if (!c->read->ready) { - if (r->request_body_no_buffering - && rb->buf->pos != rb->buf->last) - { - /* pass buffer to request body filter chain */ - - out.buf = rb->buf; - out.next = NULL; - - rc = ngx_http_request_body_filter(r, &out); - - if (rc != NGX_OK) { - return rc; - } - } - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ngx_add_timer(c->read, clcf->client_body_timeout); From 150cbb017b4fda599dcda172dca87ca11f6219f1 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 6 Aug 2020 05:02:55 +0300 Subject: [PATCH 10/18] Request body: allowed large reads on chunk boundaries. If some additional data from a pipelined request happens to be read into the body buffer, we copy it to r->header_in or allocate an additional large client header buffer for it. --- src/http/ngx_http_request_body.c | 121 +++++++++++++++++++++++++++++-- 1 file changed, 113 insertions(+), 8 deletions(-) diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index d21c45863..106ac3dc6 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -12,6 +12,8 @@ static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r); static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r); +static ngx_int_t ngx_http_copy_pipelined_header(ngx_http_request_t *r, + ngx_buf_t *buf); static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r); static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r); static ngx_int_t ngx_http_discard_request_body_filter(ngx_http_request_t *r, @@ -379,6 +381,10 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) } } + if (ngx_http_copy_pipelined_header(r, rb->buf) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + if (c->read->timer_set) { ngx_del_timer(c->read); } @@ -392,6 +398,88 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) } +static ngx_int_t +ngx_http_copy_pipelined_header(ngx_http_request_t *r, ngx_buf_t *buf) +{ + size_t n; + ngx_buf_t *b; + ngx_chain_t *cl; + ngx_http_connection_t *hc; + ngx_http_core_srv_conf_t *cscf; + + b = r->header_in; + n = buf->last - buf->pos; + + if (buf == b || n == 0) { + return NGX_OK; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http body pipelined header: %uz", n); + + /* + * if there is a pipelined request in the client body buffer, + * copy it to the r->header_in buffer if there is enough room, + * or allocate a large client header buffer + */ + + if (n > (size_t) (b->end - b->last)) { + + hc = r->http_connection; + + if (hc->free) { + cl = hc->free; + hc->free = cl->next; + + b = cl->buf; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http large header free: %p %uz", + b->pos, b->end - b->last); + + } else { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + b = ngx_create_temp_buf(r->connection->pool, + cscf->large_client_header_buffers.size); + if (b == NULL) { + return NGX_ERROR; + } + + cl = ngx_alloc_chain_link(r->connection->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http large header alloc: %p %uz", + b->pos, b->end - b->last); + } + + cl->next = hc->busy; + hc->busy = cl; + hc->nbusy++; + + r->header_in = b; + + if (n > (size_t) (b->end - b->last)) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "too large pipelined header after reading body"); + return NGX_ERROR; + } + } + + ngx_memcpy(b->last, buf->pos, n); + + b->last += n; + r->request_length -= n; + + return NGX_OK; +} + + static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r) { @@ -637,8 +725,7 @@ ngx_http_read_discarded_request_body(ngx_http_request_t *r) for ( ;; ) { if (r->headers_in.content_length_n == 0) { - r->read_event_handler = ngx_http_block_reading; - return NGX_OK; + break; } if (!r->connection->read->ready) { @@ -672,15 +759,24 @@ ngx_http_read_discarded_request_body(ngx_http_request_t *r) return rc; } } + + if (ngx_http_copy_pipelined_header(r, &b) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->read_event_handler = ngx_http_block_reading; + + return NGX_OK; } static ngx_int_t ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b) { - size_t size; - ngx_int_t rc; - ngx_http_request_body_t *rb; + size_t size; + ngx_int_t rc; + ngx_http_request_body_t *rb; + ngx_http_core_srv_conf_t *cscf; if (r->headers_in.chunked) { @@ -735,7 +831,10 @@ ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b) /* set amount of data we want to see next time */ - r->headers_in.content_length_n = rb->chunked->length; + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + r->headers_in.content_length_n = ngx_max(rb->chunked->length, + (off_t) cscf->large_client_header_buffers.size); break; } @@ -903,6 +1002,7 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in) ngx_chain_t *cl, *out, *tl, **ll; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; rb = r->request_body; @@ -916,8 +1016,10 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in) return NGX_HTTP_INTERNAL_SERVER_ERROR; } + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + r->headers_in.content_length_n = 0; - rb->rest = 3; + rb->rest = cscf->large_client_header_buffers.size; } out = NULL; @@ -1024,7 +1126,10 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in) /* set rb->rest, amount of data we want to see next time */ - rb->rest = rb->chunked->length; + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + rb->rest = ngx_max(rb->chunked->length, + (off_t) cscf->large_client_header_buffers.size); break; } From 130a5e71269200154b55e85d9e30186feaeb64a7 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 6 Aug 2020 05:02:57 +0300 Subject: [PATCH 11/18] Request body: optimized handling of small chunks. If there is a previous buffer, copy small chunks into it instead of allocating additional buffer. --- src/http/ngx_http_request_body.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index 106ac3dc6..71d7e9ab8 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -1027,6 +1027,8 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in) for (cl = in; cl; cl = cl->next) { + b = NULL; + for ( ;; ) { ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, @@ -1061,6 +1063,29 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in) return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; } + if (b + && rb->chunked->size <= 128 + && cl->buf->last - cl->buf->pos >= rb->chunked->size) + { + r->headers_in.content_length_n += rb->chunked->size; + + if (rb->chunked->size < 8) { + + while (rb->chunked->size) { + *b->last++ = *cl->buf->pos++; + rb->chunked->size--; + } + + } else { + ngx_memmove(b->last, cl->buf->pos, rb->chunked->size); + b->last += rb->chunked->size; + cl->buf->pos += rb->chunked->size; + rb->chunked->size = 0; + } + + continue; + } + tl = ngx_chain_get_free_buf(r->pool, &rb->free); if (tl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; From e01cdfbd8c1b757eaadad059cb7c9b9313e715a6 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Mon, 10 Aug 2020 18:52:09 +0300 Subject: [PATCH 12/18] SSL: fixed shutdown handling. Previously, bidirectional shutdown never worked, due to two issues in the code: 1. The code only tested SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE when there was an error in the error queue, which cannot happen. The bug was introduced in an attempt to fix unexpected error logging as reported with OpenSSL 0.9.8g (http://mailman.nginx.org/pipermail/nginx/2008-January/003084.html). 2. The code never called SSL_shutdown() for the second time to wait for the peer's close_notify alert. This change fixes both issues. Note that after this change bidirectional shutdown is expected to work for the first time, so c->ssl->no_wait_shutdown now makes a difference. This is not a problem for HTTP code which always uses c->ssl->no_wait_shutdown, but might be a problem for stream and mail code, as well as 3rd party modules. To minimize the effect of the change, the timeout, which was used to be 30 seconds and not configurable, though never actually used, is now set to 3 seconds. It is also expanded to apply to both SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE, so timeout is properly set if writing to the socket buffer is not possible. --- src/event/ngx_event_openssl.c | 93 +++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 38 deletions(-) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index f589b9812..f387c720d 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -2774,8 +2774,9 @@ ngx_ssl_free_buffer(ngx_connection_t *c) ngx_int_t ngx_ssl_shutdown(ngx_connection_t *c) { - int n, sslerr, mode; - ngx_err_t err; + int n, sslerr, mode; + ngx_err_t err; + ngx_uint_t tries; ngx_ssl_ocsp_cleanup(c); @@ -2816,55 +2817,71 @@ ngx_ssl_shutdown(ngx_connection_t *c) ngx_ssl_clear_error(c->log); - n = SSL_shutdown(c->ssl->connection); + tries = 2; - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n); + for ( ;; ) { - sslerr = 0; + /* + * For bidirectional shutdown, SSL_shutdown() needs to be called + * twice: first call sends the "close notify" alert and returns 0, + * second call waits for the peer's "close notify" alert. + */ - /* before 0.9.8m SSL_shutdown() returned 0 instead of -1 on errors */ + n = SSL_shutdown(c->ssl->connection); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n); + + if (n == 1) { + SSL_free(c->ssl->connection); + c->ssl = NULL; + + return NGX_OK; + } + + if (n == 0 && tries-- > 1) { + continue; + } + + /* before 0.9.8m SSL_shutdown() returned 0 instead of -1 on errors */ - if (n != 1 && ERR_peek_error()) { sslerr = SSL_get_error(c->ssl->connection, n); ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr); - } - if (n == 1 || sslerr == 0 || sslerr == SSL_ERROR_ZERO_RETURN) { + if (sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE) { + c->read->handler = ngx_ssl_shutdown_handler; + c->write->handler = ngx_ssl_shutdown_handler; + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + return NGX_ERROR; + } + + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + return NGX_ERROR; + } + + ngx_add_timer(c->read, 3000); + + return NGX_AGAIN; + } + + if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) { + SSL_free(c->ssl->connection); + c->ssl = NULL; + + return NGX_OK; + } + + err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; + + ngx_ssl_connection_error(c, sslerr, err, "SSL_shutdown() failed"); + SSL_free(c->ssl->connection); c->ssl = NULL; - return NGX_OK; + return NGX_ERROR; } - - if (sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE) { - c->read->handler = ngx_ssl_shutdown_handler; - c->write->handler = ngx_ssl_shutdown_handler; - - if (ngx_handle_read_event(c->read, 0) != NGX_OK) { - return NGX_ERROR; - } - - if (ngx_handle_write_event(c->write, 0) != NGX_OK) { - return NGX_ERROR; - } - - if (sslerr == SSL_ERROR_WANT_READ) { - ngx_add_timer(c->read, 30000); - } - - return NGX_AGAIN; - } - - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; - - ngx_ssl_connection_error(c, sslerr, err, "SSL_shutdown() failed"); - - SSL_free(c->ssl->connection); - c->ssl = NULL; - - return NGX_ERROR; } From 1d696cd37947ef816bde4d54d7b6f97374f1151d Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Mon, 10 Aug 2020 18:52:20 +0300 Subject: [PATCH 13/18] HTTP/2: fixed c->timedout flag on timed out connections. Without the flag, SSL shutdown is attempted on such connections, resulting in useless work and/or bogus "SSL_shutdown() failed (SSL: ... bad write retry)" critical log messages if there are blocked writes. --- src/http/v2/ngx_http_v2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index ec553ecfe..51c8b0a71 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -475,6 +475,7 @@ ngx_http_v2_write_handler(ngx_event_t *wev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http2 write event timed out"); c->error = 1; + c->timedout = 1; ngx_http_v2_finalize_connection(h2c, 0); return; } From eae2b2fdf15c52f058c0c08763a5c373997d0535 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Mon, 10 Aug 2020 18:52:34 +0300 Subject: [PATCH 14/18] SSL: disabled sending shutdown after ngx_http_test_reading(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sending shutdown when ngx_http_test_reading() detects the connection is closed can result in "SSL_shutdown() failed (SSL: ... bad write retry)" critical log messages if there are blocked writes. Fix is to avoid sending shutdown via the c->ssl->no_send_shutdown flag, similarly to how it is done in ngx_http_keepalive_handler() for kqueue when pending EOF is detected. Reported by Jan Prachař (http://mailman.nginx.org/pipermail/nginx-devel/2018-December/011702.html). --- src/http/ngx_http_request.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 257c4064b..f80785d8f 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -2992,6 +2992,12 @@ closed: rev->error = 1; } +#if (NGX_HTTP_SSL) + if (c->ssl) { + c->ssl->no_send_shutdown = 1; + } +#endif + ngx_log_error(NGX_LOG_INFO, c->log, err, "client prematurely closed connection"); From e240d88d4497afd2493358e938f74486750bc776 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Mon, 10 Aug 2020 18:52:59 +0300 Subject: [PATCH 15/18] Core: added a warning about reusing connections. Previously, reusing connections happened silently and was only visible in monitoring systems. This was shown to be not very user-friendly, and administrators often didn't realize there were too few connections available to withstand the load, and configured timeouts (keepalive_timeout and http2_idle_timeout) were effectively reduced to keep things running. To provide at least some information about this, a warning is now logged (at most once per second, to avoid flooding the logs). --- src/core/ngx_connection.c | 13 +++++++++++++ src/core/ngx_cycle.h | 1 + 2 files changed, 14 insertions(+) diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c index 88fefcea2..91e1b3b2e 100644 --- a/src/core/ngx_connection.c +++ b/src/core/ngx_connection.c @@ -1298,6 +1298,19 @@ ngx_drain_connections(ngx_cycle_t *cycle) ngx_queue_t *q; ngx_connection_t *c; + if (cycle->reusable_connections_n == 0) { + return; + } + + if (cycle->connections_reuse_time != ngx_time()) { + cycle->connections_reuse_time = ngx_time(); + + ngx_log_error(NGX_LOG_WARN, cycle->log, 0, + "%ui worker_connections are not enough, " + "reusing connections", + cycle->connection_n); + } + n = ngx_max(ngx_min(32, cycle->reusable_connections_n / 8), 1); for (i = 0; i < n; i++) { diff --git a/src/core/ngx_cycle.h b/src/core/ngx_cycle.h index 54fa2e6bf..0f7d9bc74 100644 --- a/src/core/ngx_cycle.h +++ b/src/core/ngx_cycle.h @@ -55,6 +55,7 @@ struct ngx_cycle_s { ngx_queue_t reusable_connections_queue; ngx_uint_t reusable_connections_n; + time_t connections_reuse_time; ngx_array_t listening; ngx_array_t paths; From 348bc94086d94f27dbda5904174cd379bce7f931 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Mon, 10 Aug 2020 18:53:07 +0300 Subject: [PATCH 16/18] Core: reusing connections in advance. Reworked connections reuse, so closing connections is attempted in advance, as long as number of free connections is less than 1/16 of worker connections configured. This ensures that new connections can be handled even if closing a reusable connection requires some time, for example, for a lingering close (ticket #2017). The 1/16 ratio is selected to be smaller than 1/8 used for disabling accept when working with accept mutex, so nginx will try to balance new connections to different workers first, and will start reusing connections only if this won't help. --- src/core/ngx_connection.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c index 91e1b3b2e..c082d0dac 100644 --- a/src/core/ngx_connection.c +++ b/src/core/ngx_connection.c @@ -1107,12 +1107,9 @@ ngx_get_connection(ngx_socket_t s, ngx_log_t *log) return NULL; } - c = ngx_cycle->free_connections; + ngx_drain_connections((ngx_cycle_t *) ngx_cycle); - if (c == NULL) { - ngx_drain_connections((ngx_cycle_t *) ngx_cycle); - c = ngx_cycle->free_connections; - } + c = ngx_cycle->free_connections; if (c == NULL) { ngx_log_error(NGX_LOG_ALERT, log, 0, @@ -1298,7 +1295,9 @@ ngx_drain_connections(ngx_cycle_t *cycle) ngx_queue_t *q; ngx_connection_t *c; - if (cycle->reusable_connections_n == 0) { + if (cycle->free_connection_n > cycle->connection_n / 16 + || cycle->reusable_connections_n == 0) + { return; } From e4c6918e0869f64d115c032c117481b93be2c91f Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Tue, 11 Aug 2020 17:52:30 +0300 Subject: [PATCH 17/18] nginx-1.19.2-RELEASE --- docs/xml/nginx/changes.xml | 102 +++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml index 2f1bc4879..2106c8789 100644 --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -5,6 +5,108 @@ + + + + +теперь nginx начинает закрывать keepalive-соединения, +не дожидаясь исчерпания всех свободных соединений, +а также пишет об этом предупреждение в лог ошибок. + + +now nginx starts closing keepalive connections +before all free worker connections are exhausted, +and logs a warning about this to the error log. + + + + + +оптимизация чтения тела запроса +при использовании chunked transfer encoding. + + +optimization of client request body reading +when using chunked transfer encoding. + + + + + +утечки памяти при использовании директивы ssl_ocsp. + + +memory leak if the "ssl_ocsp" directive was used. + + + + + +в логах могли появляться сообщения "zero size buf in output", +если FastCGI-сервер возвращал некорректный ответ; +ошибка появилась в 1.19.1. + + +"zero size buf in output" alerts might appear in logs +if a FastCGI server returned an incorrect response; +the bug had appeared in 1.19.1. + + + + + +в рабочем процессе мог произойти segmentation fault, +если размеры large_client_header_buffers отличались +в разных виртуальных серверах. + + +a segmentation fault might occur in a worker process +if different large_client_header_buffers sizes were used +in different virtual servers. + + + + + +SSL shutdown мог не работать. + + +SSL shutdown might not work. + + + + + +в логах могли появляться сообщения +"SSL_shutdown() failed (SSL: ... bad write retry)". + + +"SSL_shutdown() failed (SSL: ... bad write retry)" +messages might appear in logs. + + + + + +в модуле ngx_http_slice_module. + + +in the ngx_http_slice_module. + + + + + +в модуле ngx_http_xslt_filter_module. + + +in the ngx_http_xslt_filter_module. + + + + + + From b2d09a4cdd865a8997f68fbaa1e928f0dc0b6ef0 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Tue, 11 Aug 2020 17:52:30 +0300 Subject: [PATCH 18/18] release-1.19.2 tag --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 7c9513a09..edcbdac78 100644 --- a/.hgtags +++ b/.hgtags @@ -451,3 +451,4 @@ fdacd273711ddf20f778c1fb91529ab53979a454 release-1.17.8 c44970de01474f6f3e01b0adea85ec1d03e3a5f2 release-1.17.10 cbe6ba650211541310618849168631ce0b788f35 release-1.19.0 062920e2f3bf871ef7a3d8496edec1b3065faf80 release-1.19.1 +a7b46539f507e6c64efa0efda69ad60b6f4ffbce release-1.19.2