# HG changeset patch # User Roman Arutyunyan # Date 1597756920 -10800 # Node ID 532fe796b0e28e52466d97410f1435ccf03766fe # Parent bce9e9643444f2e2b4946df58aab698461367e9f# Parent 7d46c9f56c9afe34a38bb3aea99550a2fd884280 Merged with the default branch. diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -451,3 +451,4 @@ 5e8d52bca714d4b85284ddb649d1ba4a3ca978a8 c44970de01474f6f3e01b0adea85ec1d03e3a5f2 release-1.17.10 cbe6ba650211541310618849168631ce0b788f35 release-1.19.0 062920e2f3bf871ef7a3d8496edec1b3065faf80 release-1.19.1 +a7b46539f507e6c64efa0efda69ad60b6f4ffbce release-1.19.2 diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml --- 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. + + + + + + diff --git a/src/core/nginx.h b/src/core/nginx.h --- 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 diff --git a/src/core/ngx_buf.h b/src/core/ngx_buf.h --- 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(ng 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) diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c --- a/src/core/ngx_connection.c +++ b/src/core/ngx_connection.c @@ -1113,12 +1113,9 @@ ngx_get_connection(ngx_socket_t s, ngx_l 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, @@ -1304,6 +1301,21 @@ ngx_drain_connections(ngx_cycle_t *cycle ngx_queue_t *q; ngx_connection_t *c; + if (cycle->free_connection_n > cycle->connection_n / 16 + || 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.c b/src/core/ngx_cycle.c --- 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_ 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_ ngx_close_file_n " \"%s\" failed", file.name.data); } - return NGX_OK; + return rc; } diff --git a/src/core/ngx_cycle.h b/src/core/ngx_cycle.h --- 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; diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- 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; #if (NGX_QUIC) if (c->qs) { @@ -2823,55 +2824,71 @@ ngx_ssl_shutdown(ngx_connection_t *c) ngx_ssl_clear_error(c->log); - n = SSL_shutdown(c->ssl->connection); - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n); - - sslerr = 0; - - /* before 0.9.8m SSL_shutdown() returned 0 instead of -1 on errors */ - - if (n != 1 && ERR_peek_error()) { + 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. + */ + + 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 */ + 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; - } - - 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; + return NGX_ERROR; + } } diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c --- 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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)); diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c --- 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_ 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_ 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(voi 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(voi 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, diff --git a/src/http/modules/ngx_http_slice_filter_module.c b/src/http/modules/ngx_http_slice_filter_module.c --- 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_re 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; diff --git a/src/http/modules/ngx_http_xslt_filter_module.c b/src/http/modules/ngx_http_xslt_filter_module.c --- 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_req 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; } diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1723,6 +1723,12 @@ ngx_http_alloc_large_header_buffer(ngx_h 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); @@ -3120,6 +3126,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"); diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- 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, @@ -282,28 +284,12 @@ ngx_http_do_read_client_request_body(ngx for ( ;; ) { if (rb->buf->last == rb->buf->end) { - if (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); + /* update chains */ - if (rc != NGX_OK) { - return rc; - } - - } else { + rc = ngx_http_request_body_filter(r, NULL); - /* 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) { @@ -354,17 +340,15 @@ ngx_http_do_read_client_request_body(ngx rb->buf->last += n; r->request_length += n; - if (n == rest || n == 0) { - /* 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) { @@ -385,21 +369,6 @@ ngx_http_do_read_client_request_body(ngx 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); @@ -411,6 +380,10 @@ ngx_http_do_read_client_request_body(ngx } } + 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); } @@ -425,6 +398,88 @@ ngx_http_do_read_client_request_body(ngx 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) { ssize_t n; @@ -669,8 +724,7 @@ ngx_http_read_discarded_request_body(ngx 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) { @@ -704,15 +758,24 @@ ngx_http_read_discarded_request_body(ngx 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) { @@ -776,7 +839,10 @@ ngx_http_discard_request_body_filter(ngx /* 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; } @@ -945,6 +1011,7 @@ ngx_http_request_body_chunked_filter(ngx 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; @@ -958,17 +1025,16 @@ ngx_http_request_body_chunked_filter(ngx 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 = cscf->large_client_header_buffers.size; #if (NGX_HTTP_V3) if (r->http_version == NGX_HTTP_VERSION_30) { rb->rest = 1; - - } else + } #endif - { - rb->rest = 3; - } } out = NULL; @@ -976,6 +1042,8 @@ ngx_http_request_body_chunked_filter(ngx for (cl = in; cl; cl = cl->next) { + b = NULL; + for ( ;; ) { ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, @@ -1019,6 +1087,29 @@ ngx_http_request_body_chunked_filter(ngx 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; @@ -1084,7 +1175,10 @@ ngx_http_request_body_chunked_filter(ngx /* 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; } diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c --- 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 *w 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; }