From 826fb0d45c964d597975c1a08070cf9a579a08ac Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 9 Jul 2020 17:33:22 +0300 Subject: Version bump. --- src/core/nginx.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') 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 -- cgit From 5cef7de7a116bab3af9097dac5a22f7652be4273 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 9 Jul 2020 16:21:37 +0300 Subject: 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(+) (limited to 'src') 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; -- cgit From 80daef96f1e1de397b8bc455ac3a3fbc060607cf Mon Sep 17 00:00:00 2001 From: Ruslan Ermilov Date: Tue, 21 Jul 2020 20:34:29 +0300 Subject: Core: close PID file when writing fails. Reported by Jinhua Tan. --- src/core/ngx_cycle.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') 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; } -- cgit From 4dd43dfca71f3fc2c6768606ff3700a4317a9176 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 22 Jul 2020 22:16:19 +0300 Subject: 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(+) (limited to 'src') 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; } -- cgit From 4ee66b3f7bb176915cfb0e7f3ab37d06fd6924bd Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Thu, 23 Jul 2020 17:31:09 +0300 Subject: OCSP: fixed certificate reference leak. --- src/event/ngx_event_openssl_stapling.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') 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)); -- cgit From c3db6f729fcaece7943757c4dfb4ec58304cbad4 Mon Sep 17 00:00:00 2001 From: balus Date: Mon, 27 Jul 2020 13:21:51 +0300 Subject: Core: enclosed parameters of the ngx_buf.h macros in parentheses. --- src/core/ngx_buf.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src') 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) -- cgit From d2744ad26fef1e4f4f6e9c12e95b57866345c071 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Mon, 27 Jul 2020 16:02:15 +0300 Subject: 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(-) (limited to 'src') 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, -- cgit From bd7dad5b0eb9f667a9c66ea5175a017ac51cd027 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 6 Aug 2020 05:02:22 +0300 Subject: 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(+) (limited to 'src') 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); -- cgit From 9edc93fe0ed60bac336d11f7d20d3c2ed9db3227 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 6 Aug 2020 05:02:44 +0300 Subject: 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(-) (limited to 'src') 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); -- cgit From 150cbb017b4fda599dcda172dca87ca11f6219f1 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 6 Aug 2020 05:02:55 +0300 Subject: 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(-) (limited to 'src') 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; } -- cgit From 130a5e71269200154b55e85d9e30186feaeb64a7 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 6 Aug 2020 05:02:57 +0300 Subject: 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(+) (limited to 'src') 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; -- cgit From e01cdfbd8c1b757eaadad059cb7c9b9313e715a6 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Mon, 10 Aug 2020 18:52:09 +0300 Subject: 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 | 79 ++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 31 deletions(-) (limited to 'src') 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; + + for ( ;; ) { + + /* + * 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. + */ - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n); + n = SSL_shutdown(c->ssl->connection); - sslerr = 0; + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n); - /* before 0.9.8m SSL_shutdown() returned 0 instead of -1 on errors */ + 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) { - SSL_free(c->ssl->connection); - c->ssl = NULL; + 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; - return NGX_OK; - } + if (ngx_handle_read_event(c->read, 0) != 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_write_event(c->write, 0) != NGX_OK) { + return NGX_ERROR; + } - if (ngx_handle_read_event(c->read, 0) != NGX_OK) { - return NGX_ERROR; - } + ngx_add_timer(c->read, 3000); - if (ngx_handle_write_event(c->write, 0) != NGX_OK) { - return NGX_ERROR; + return NGX_AGAIN; } - if (sslerr == SSL_ERROR_WANT_READ) { - ngx_add_timer(c->read, 30000); - } + if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) { + SSL_free(c->ssl->connection); + c->ssl = NULL; - return NGX_AGAIN; - } + return NGX_OK; + } - err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; + err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; - ngx_ssl_connection_error(c, sslerr, err, "SSL_shutdown() failed"); + ngx_ssl_connection_error(c, sslerr, err, "SSL_shutdown() failed"); - SSL_free(c->ssl->connection); - c->ssl = NULL; + SSL_free(c->ssl->connection); + c->ssl = NULL; - return NGX_ERROR; + return NGX_ERROR; + } } -- cgit From 1d696cd37947ef816bde4d54d7b6f97374f1151d Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Mon, 10 Aug 2020 18:52:20 +0300 Subject: 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(+) (limited to 'src') 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; } -- cgit From eae2b2fdf15c52f058c0c08763a5c373997d0535 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Mon, 10 Aug 2020 18:52:34 +0300 Subject: 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(+) (limited to 'src') 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"); -- cgit From e240d88d4497afd2493358e938f74486750bc776 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Mon, 10 Aug 2020 18:52:59 +0300 Subject: 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(+) (limited to 'src') 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; -- cgit From 348bc94086d94f27dbda5904174cd379bce7f931 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Mon, 10 Aug 2020 18:53:07 +0300 Subject: 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(-) (limited to 'src') 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; } -- cgit