Merge branch 'nginx' (nginx-1.19.2).

Change-Id: I1045719e4bb1eeb8d8b9a69d245d2371e5640393
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
diff --git a/.hgtags b/.hgtags
index 7c9513a..edcbdac 100644
--- a/.hgtags
+++ b/.hgtags
@@ -451,3 +451,4 @@
 c44970de01474f6f3e01b0adea85ec1d03e3a5f2 release-1.17.10
 cbe6ba650211541310618849168631ce0b788f35 release-1.19.0
 062920e2f3bf871ef7a3d8496edec1b3065faf80 release-1.19.1
+a7b46539f507e6c64efa0efda69ad60b6f4ffbce release-1.19.2
diff --git a/BUILD b/BUILD
index bd6af32..8f78db1 100644
--- a/BUILD
+++ b/BUILD
@@ -1520,5 +1520,5 @@
     preinst = "@nginx_pkgoss//:debian_preinst",
     prerm = "@nginx_pkgoss//:debian_prerm",
     section = "httpd",
-    version = "1.19.1",
+    version = "1.19.2",
 )
diff --git a/build.bzl b/build.bzl
index dd14d2b..beed032 100644
--- a/build.bzl
+++ b/build.bzl
@@ -673,9 +673,9 @@
         name = "nginx_pkgoss",
         build_file_content = _PKGOSS_BUILD_FILE.format(nginx = nginx) +
                              _PKGOSS_BUILD_FILE_TAIL,
-        commit = "cfc029dae67e72379ec03a297648c6e6a3e71f0c",  # nginx-1.19.1
+        commit = "d02e5c2fdd53ca365e688602851c8f89526b9404",  # nginx-1.19.2
         remote = "https://nginx.googlesource.com/nginx-pkgoss",
-        shallow_since = "1594137158 +0300",
+        shallow_since = "1597157447 +0300",
     )
 
 def nginx_repositories_zlib(bind):
diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml
index 2f1bc48..2106c87 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 24db599..1720d9e 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -13,8 +13,8 @@
 #define NGINX_NAME         "nginx"
 #endif
 
-#define nginx_version      1019001
-#define NGINX_VERSION      "1.19.1"
+#define nginx_version      1019002
+#define NGINX_VERSION      "1.19.2"
 #define NGINX_VER          NGINX_NAME "/" NGINX_VERSION
 
 #ifdef NGX_BUILD
diff --git a/src/core/ngx_buf.h b/src/core/ngx_buf.h
index 12781a7..4b66562 100644
--- a/src/core/ngx_buf.h
+++ b/src/core/ngx_buf.h
@@ -125,20 +125,20 @@
 #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_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 88fefce..c082d0d 100644
--- a/src/core/ngx_connection.c
+++ b/src/core/ngx_connection.c
@@ -1107,12 +1107,9 @@
         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,6 +1295,21 @@
     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 764cf46..d7479fa 100644
--- a/src/core/ngx_cycle.c
+++ b/src/core/ngx_cycle.c
@@ -1009,6 +1009,7 @@
 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 @@
         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_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 57bfd89..f24ec43 100644
--- a/src/core/ngx_cycle.h
+++ b/src/core/ngx_cycle.h
@@ -55,6 +55,7 @@
 
     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 33af90a..9bf976a 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -2797,8 +2797,9 @@
 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);
 
@@ -2839,55 +2840,71 @@
 
     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);
 
-    if (n != 1 && ERR_peek_error()) {
+        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;
+        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;
 }
 
 
diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c
index 0e79d6c..9d92421 100644
--- a/src/event/ngx_event_openssl_stapling.c
+++ b/src/event/ngx_event_openssl_stapling.c
@@ -883,6 +883,7 @@
 
     ocsp = ngx_pcalloc(c->pool, sizeof(ngx_ssl_ocsp_t));
     if (ocsp == NULL) {
+        X509_free(cert);
         return NGX_ERROR;
     }
 
@@ -899,6 +900,7 @@
     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 @@
         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 @@
         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_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 @@
         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_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 e50d1a7..5191880 100644
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -2306,6 +2306,18 @@
             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 @@
             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 @@
             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 @@
             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 c1edbca..186380a 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 @@
     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 b2f107d..8afd656 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_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 c28844d..e8dbb03 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1802,6 +1802,12 @@
     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);
@@ -3157,6 +3163,12 @@
         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 c4f092e..71d7e9a 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 @@
         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 +341,15 @@
             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 +370,6 @@
 
         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);
 
@@ -412,6 +381,10 @@
         }
     }
 
+    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);
     }
@@ -426,6 +399,88 @@
 
 
 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;
@@ -670,8 +725,7 @@
 
     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) {
@@ -705,15 +759,24 @@
             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) {
 
@@ -768,7 +831,10 @@
 
                 /* 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;
             }
 
@@ -936,6 +1002,7 @@
     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;
 
@@ -949,8 +1016,10 @@
             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;
@@ -958,6 +1027,8 @@
 
     for (cl = in; cl; cl = cl->next) {
 
+        b = NULL;
+
         for ( ;; ) {
 
             ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
@@ -992,6 +1063,29 @@
                     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;
@@ -1057,7 +1151,10 @@
 
                 /* 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 c013756..90dcfd4 100644
--- a/src/http/v2/ngx_http_v2.c
+++ b/src/http/v2/ngx_http_v2.c
@@ -476,6 +476,7 @@
         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;
     }