aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoman Arutyunyan <arut@nginx.com>2020-08-18 16:22:00 +0300
committerRoman Arutyunyan <arut@nginx.com>2020-08-18 16:22:00 +0300
commitfd6df645ebf2de0ba7bcd8df2dfd7337f5ab516f (patch)
treee80276b4b7c41ed0d6c4f960064d59f50d7ddb62
parentff1941d6ddb014da8b085c7ca9ba1098b4ec35a5 (diff)
parentb2d09a4cdd865a8997f68fbaa1e928f0dc0b6ef0 (diff)
downloadnginx-fd6df645ebf2de0ba7bcd8df2dfd7337f5ab516f.tar.gz
nginx-fd6df645ebf2de0ba7bcd8df2dfd7337f5ab516f.zip
Merged with the default branch.
-rw-r--r--.hgtags1
-rw-r--r--docs/xml/nginx/changes.xml102
-rw-r--r--src/core/nginx.h4
-rw-r--r--src/core/ngx_buf.h20
-rw-r--r--src/core/ngx_connection.c22
-rw-r--r--src/core/ngx_cycle.c7
-rw-r--r--src/core/ngx_cycle.h1
-rw-r--r--src/event/ngx_event_openssl.c79
-rw-r--r--src/event/ngx_event_openssl_stapling.c9
-rw-r--r--src/http/modules/ngx_http_fastcgi_module.c28
-rw-r--r--src/http/modules/ngx_http_slice_filter_module.c5
-rw-r--r--src/http/modules/ngx_http_xslt_filter_module.c1
-rw-r--r--src/http/ngx_http_request.c12
-rw-r--r--src/http/ngx_http_request_body.c204
-rw-r--r--src/http/v2/ngx_http_v2.c1
15 files changed, 385 insertions, 111 deletions
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
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 @@
<change_log title="nginx">
+<changes ver="1.19.2" date="2020-08-11">
+
+<change type="change">
+<para lang="ru">
+теперь nginx начинает закрывать keepalive-соединения,
+не дожидаясь исчерпания всех свободных соединений,
+а также пишет об этом предупреждение в лог ошибок.
+</para>
+<para lang="en">
+now nginx starts closing keepalive connections
+before all free worker connections are exhausted,
+and logs a warning about this to the error log.
+</para>
+</change>
+
+<change type="change">
+<para lang="ru">
+оптимизация чтения тела запроса
+при использовании chunked transfer encoding.
+</para>
+<para lang="en">
+optimization of client request body reading
+when using chunked transfer encoding.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+утечки памяти при использовании директивы ssl_ocsp.
+</para>
+<para lang="en">
+memory leak if the "ssl_ocsp" directive was used.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+в логах могли появляться сообщения "zero size buf in output",
+если FastCGI-сервер возвращал некорректный ответ;
+ошибка появилась в 1.19.1.
+</para>
+<para lang="en">
+"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.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+в рабочем процессе мог произойти segmentation fault,
+если размеры large_client_header_buffers отличались
+в разных виртуальных серверах.
+</para>
+<para lang="en">
+a segmentation fault might occur in a worker process
+if different large_client_header_buffers sizes were used
+in different virtual servers.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+SSL shutdown мог не работать.
+</para>
+<para lang="en">
+SSL shutdown might not work.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+в логах могли появляться сообщения
+"SSL_shutdown() failed (SSL: ... bad write retry)".
+</para>
+<para lang="en">
+"SSL_shutdown() failed (SSL: ... bad write retry)"
+messages might appear in logs.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+в модуле ngx_http_slice_module.
+</para>
+<para lang="en">
+in the ngx_http_slice_module.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+в модуле ngx_http_xslt_filter_module.
+</para>
+<para lang="en">
+in the ngx_http_xslt_filter_module.
+</para>
+</change>
+
+</changes>
+
+
<changes ver="1.19.1" date="2020-07-07">
<change type="change">
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
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)
diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c
index 1fb569d30..21f9980f2 100644
--- a/src/core/ngx_connection.c
+++ b/src/core/ngx_connection.c
@@ -1113,12 +1113,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,
@@ -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
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;
}
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;
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index dc0861ce9..8077f40a9 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;
#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);
+ 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;
+ }
}
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));
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,
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;
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;
}
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index 30a22fa22..7dbbcceb2 100644
--- 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_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);
@@ -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
index 568f11f02..204253ca2 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,
@@ -282,28 +284,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) {
@@ -354,17 +340,15 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r)
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_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);
@@ -411,6 +380,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);
}
@@ -425,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)
{
ssize_t n;
@@ -669,8 +724,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) {
@@ -704,15 +758,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) {
@@ -776,7 +839,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;
}
@@ -945,6 +1011,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;
@@ -958,17 +1025,16 @@ 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 = 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;
}
+#endif
}
out = NULL;
@@ -976,6 +1042,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,
@@ -1019,6 +1087,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;
@@ -1084,7 +1175,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;
}
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;
}