Merge branch 'nginx' (nginx-1.21.1).

Change-Id: Idf23a3d2ebd74b7468481f80e1222a5e18cbcd4c
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
diff --git a/.hgtags b/.hgtags
index ea345c9..ecf429e 100644
--- a/.hgtags
+++ b/.hgtags
@@ -461,3 +461,4 @@
 da571b8eaf8f30f36c43b3c9b25e01e31f47149c release-1.19.9
 ffcbb9980ee2bad27b4d7b1cd680b14ff47b29aa release-1.19.10
 df34dcc9ac072ffd0945e5a1f3eb7987e8275375 release-1.21.0
+a68ac0677f8553b1f84d357bc9da114731ab5f47 release-1.21.1
diff --git a/contrib/vim/syntax/nginx.vim b/contrib/vim/syntax/nginx.vim
index 88ec847..4907e2a 100644
--- a/contrib/vim/syntax/nginx.vim
+++ b/contrib/vim/syntax/nginx.vim
@@ -152,6 +152,7 @@
 syn keyword ngxDirective contained auth_jwt_key_file
 syn keyword ngxDirective contained auth_jwt_key_request
 syn keyword ngxDirective contained auth_jwt_leeway
+syn keyword ngxDirective contained auth_jwt_type
 syn keyword ngxDirective contained auth_request
 syn keyword ngxDirective contained auth_request_set
 syn keyword ngxDirective contained autoindex
@@ -332,16 +333,20 @@
 syn keyword ngxDirective contained iocp_threads
 syn keyword ngxDirective contained ip_hash
 syn keyword ngxDirective contained js_access
+syn keyword ngxDirective contained js_body_filter
 syn keyword ngxDirective contained js_content
 syn keyword ngxDirective contained js_filter
+syn keyword ngxDirective contained js_header_filter
 syn keyword ngxDirective contained js_import
 syn keyword ngxDirective contained js_include
 syn keyword ngxDirective contained js_path
 syn keyword ngxDirective contained js_preread
 syn keyword ngxDirective contained js_set
+syn keyword ngxDirective contained js_var
 syn keyword ngxDirective contained keepalive
 syn keyword ngxDirective contained keepalive_disable
 syn keyword ngxDirective contained keepalive_requests
+syn keyword ngxDirective contained keepalive_time
 syn keyword ngxDirective contained keepalive_timeout
 syn keyword ngxDirective contained keyval
 syn keyword ngxDirective contained keyval_zone
@@ -373,6 +378,7 @@
 syn keyword ngxDirective contained map_hash_bucket_size
 syn keyword ngxDirective contained map_hash_max_size
 syn keyword ngxDirective contained master_process
+syn keyword ngxDirective contained max_errors
 syn keyword ngxDirective contained max_ranges
 syn keyword ngxDirective contained memcached_bind
 syn keyword ngxDirective contained memcached_buffer_size
@@ -1080,6 +1086,8 @@
 syn keyword ngxDirectiveThirdParty contained nchan_subscribe_request
 syn keyword ngxDirectiveThirdParty contained nchan_subscriber_first_message
 syn keyword ngxDirectiveThirdParty contained nchan_subscriber_http_raw_stream_separator
+syn keyword ngxDirectiveThirdParty contained nchan_subscriber_info
+syn keyword ngxDirectiveThirdParty contained nchan_subscriber_info_string
 syn keyword ngxDirectiveThirdParty contained nchan_subscriber_last_message_id
 syn keyword ngxDirectiveThirdParty contained nchan_subscriber_location
 syn keyword ngxDirectiveThirdParty contained nchan_subscriber_message_id_custom_etag_header
@@ -2368,9 +2376,9 @@
 
 " IP2Location Nginx
 " https://github.com/ip2location/ip2location-nginx
-syn keyword ngxDirectiveThirdParty contained ip2location_proxy
-syn keyword ngxDirectiveThirdParty contained ip2location_proxy_recursive
+syn keyword ngxDirectiveThirdParty contained ip2location_addresstype
 syn keyword ngxDirectiveThirdParty contained ip2location_areacode
+syn keyword ngxDirectiveThirdParty contained ip2location_category
 syn keyword ngxDirectiveThirdParty contained ip2location_city
 syn keyword ngxDirectiveThirdParty contained ip2location_country_long
 syn keyword ngxDirectiveThirdParty contained ip2location_country_short
@@ -2384,6 +2392,8 @@
 syn keyword ngxDirectiveThirdParty contained ip2location_mnc
 syn keyword ngxDirectiveThirdParty contained ip2location_mobilebrand
 syn keyword ngxDirectiveThirdParty contained ip2location_netspeed
+syn keyword ngxDirectiveThirdParty contained ip2location_proxy
+syn keyword ngxDirectiveThirdParty contained ip2location_proxy_recursive
 syn keyword ngxDirectiveThirdParty contained ip2location_region
 syn keyword ngxDirectiveThirdParty contained ip2location_timezone
 syn keyword ngxDirectiveThirdParty contained ip2location_usagetype
@@ -2403,6 +2413,7 @@
 syn keyword ngxDirectiveThirdParty contained ip2proxy_isp
 syn keyword ngxDirectiveThirdParty contained ip2proxy_is_proxy
 syn keyword ngxDirectiveThirdParty contained ip2proxy_last_seen
+syn keyword ngxDirectiveThirdParty contained ip2proxy_provider
 syn keyword ngxDirectiveThirdParty contained ip2proxy_proxy
 syn keyword ngxDirectiveThirdParty contained ip2proxy_proxy_recursive
 syn keyword ngxDirectiveThirdParty contained ip2proxy_proxy_type
diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml
index ea3551a..1fb7634 100644
--- a/docs/xml/nginx/changes.xml
+++ b/docs/xml/nginx/changes.xml
@@ -5,6 +5,125 @@
 <change_log title="nginx">
 
 
+<changes ver="1.21.1" date="2021-07-06">
+
+<change type="change">
+<para lang="ru">
+теперь nginx для метода CONNECT всегда возвращает ошибку.
+</para>
+<para lang="en">
+now nginx always returns an error for the CONNECT method.
+</para>
+</change>
+
+<change type="change">
+<para lang="ru">
+теперь nginx всегда возвращает ошибку,
+если в запросе одновременно присутствуют строки заголовка "Content-Length"
+и "Transfer-Encoding".
+</para>
+<para lang="en">
+now nginx always returns an error
+if both "Content-Length" and "Transfer-Encoding" header lines
+are present in the request.
+</para>
+</change>
+
+<change type="change">
+<para lang="ru">
+теперь nginx всегда возвращает ошибку,
+если в строке запроса используются пробелы или управляющие символы.
+</para>
+<para lang="en">
+now nginx always returns an error
+if spaces or control characters are used in the request line.
+</para>
+</change>
+
+<change type="change">
+<para lang="ru">
+теперь nginx всегда возвращает ошибку,
+если в имени заголовка используются пробелы или управляющие символы.
+</para>
+<para lang="en">
+now nginx always returns an error
+if spaces or control characters are used in a header name.
+</para>
+</change>
+
+<change type="change">
+<para lang="ru">
+теперь nginx всегда возвращает ошибку,
+если в строке "Host" заголовка запроса
+используются пробелы или управляющие символы.
+</para>
+<para lang="en">
+now nginx always returns an error
+if spaces or control characters
+are used in the "Host" request header line.
+</para>
+</change>
+
+<change type="change">
+<para lang="ru">
+оптимизация тестирования конфигурации
+при использовании большого количества listen-сокетов.
+</para>
+<para lang="en">
+optimization of configuration testing
+when using many listening sockets.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+nginx не экранировал
+символы """, "&lt;", ">", "\", "^", "`", "{", "|", и "}"
+при проксировании с изменением URI запроса.
+</para>
+<para lang="en">
+nginx did not escape
+""", "&lt;", ">", "\", "^", "`", "{", "|", and "}" characters
+when proxying with changed URI.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+SSL-переменные могли быть пустыми при записи в лог;
+ошибка появилась в 1.19.5.
+</para>
+<para lang="en">
+SSL variables might be empty when used in logs;
+the bug had appeared in 1.19.5.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+keepalive-соединения с gRPC-бэкендами могли не закрываться
+после получения GOAWAY-фрейма.
+</para>
+<para lang="en">
+keepalive connections with gRPC backends might not be closed
+after receiving a GOAWAY frame.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+уменьшено потребление памяти для долгоживущих запросов
+при проксировании с использованием более 64 буферов.
+</para>
+<para lang="en">
+reduced memory consumption for long-lived requests
+when proxying with more than 64 buffers.
+</para>
+</change>
+
+</changes>
+
+
 <changes ver="1.21.0" date="2021-05-25">
 
 <change type="security">
diff --git a/src/core/nginx.h b/src/core/nginx.h
index be3ee7c..a7a34de 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -13,8 +13,8 @@
 #define NGINX_NAME         "nginx"
 #endif
 
-#define nginx_version      1021000
-#define NGINX_VERSION      "1.21.0"
+#define nginx_version      1021001
+#define NGINX_VERSION      "1.21.1"
 #define NGINX_VER          NGINX_NAME "/" NGINX_VERSION
 
 #ifdef NGX_BUILD
diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c
index 8339e2b..fe729a7 100644
--- a/src/core/ngx_connection.c
+++ b/src/core/ngx_connection.c
@@ -495,21 +495,24 @@
                 return NGX_ERROR;
             }
 
-            if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
-                           (const void *) &reuseaddr, sizeof(int))
-                == -1)
-            {
-                ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
-                              "setsockopt(SO_REUSEADDR) %V failed",
-                              &ls[i].addr_text);
+            if (ls[i].type != SOCK_DGRAM || !ngx_test_config) {
 
-                if (ngx_close_socket(s) == -1) {
+                if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+                               (const void *) &reuseaddr, sizeof(int))
+                    == -1)
+                {
                     ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
-                                  ngx_close_socket_n " %V failed",
+                                  "setsockopt(SO_REUSEADDR) %V failed",
                                   &ls[i].addr_text);
-                }
 
-                return NGX_ERROR;
+                    if (ngx_close_socket(s) == -1) {
+                        ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                                      ngx_close_socket_n " %V failed",
+                                      &ls[i].addr_text);
+                    }
+
+                    return NGX_ERROR;
+                }
             }
 
 #if (NGX_HAVE_REUSEPORT)
diff --git a/src/core/ngx_rbtree.h b/src/core/ngx_rbtree.h
index 97f0e3e..e8c3582 100644
--- a/src/core/ngx_rbtree.h
+++ b/src/core/ngx_rbtree.h
@@ -47,6 +47,9 @@
     (tree)->sentinel = s;                                                     \
     (tree)->insert = i
 
+#define ngx_rbtree_data(node, type, link)                                     \
+    (type *) ((u_char *) (node) - offsetof(type, link))
+
 
 void ngx_rbtree_insert(ngx_rbtree_t *tree, ngx_rbtree_node_t *node);
 void ngx_rbtree_delete(ngx_rbtree_t *tree, ngx_rbtree_node_t *node);
diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c
index 58d5f3e..6d129e5 100644
--- a/src/core/ngx_resolver.c
+++ b/src/core/ngx_resolver.c
@@ -51,9 +51,7 @@
 } ngx_resolver_an_t;
 
 
-#define ngx_resolver_node(n)                                                 \
-    (ngx_resolver_node_t *)                                                  \
-        ((u_char *) (n) - offsetof(ngx_resolver_node_t, node))
+#define ngx_resolver_node(n)  ngx_rbtree_data(n, ngx_resolver_node_t, node)
 
 
 static ngx_int_t ngx_udp_connect(ngx_resolver_connection_t *rec);
diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c
index 5cc9b26..98f270a 100644
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -1493,19 +1493,32 @@
     uint32_t       *escape;
     static u_char   hex[] = "0123456789ABCDEF";
 
-                    /* " ", "#", "%", "?", %00-%1F, %7F-%FF */
+    /*
+     * Per RFC 3986 only the following chars are allowed in URIs unescaped:
+     *
+     * unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
+     * gen-delims    = ":" / "/" / "?" / "#" / "[" / "]" / "@"
+     * sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
+     *               / "*" / "+" / "," / ";" / "="
+     *
+     * And "%" can appear as a part of escaping itself.  The following
+     * characters are not allowed and need to be escaped: %00-%1F, %7F-%FF,
+     * " ", """, "<", ">", "\", "^", "`", "{", "|", "}".
+     */
+
+                    /* " ", "#", "%", "?", not allowed */
 
     static uint32_t   uri[] = {
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
 
                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
-        0x80000029, /* 1000 0000 0000 0000  0000 0000 0010 1001 */
+        0xd000002d, /* 1101 0000 0000 0000  0000 0000 0010 1101 */
 
                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
-        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
+        0x50000000, /* 0101 0000 0000 0000  0000 0000 0000 0000 */
 
                     /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
-        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */
+        0xb8000001, /* 1011 1000 0000 0000  0000 0000 0000 0001 */
 
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
@@ -1513,19 +1526,19 @@
         0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
     };
 
-                    /* " ", "#", "%", "&", "+", "?", %00-%1F, %7F-%FF */
+                    /* " ", "#", "%", "&", "+", ";", "?", not allowed */
 
     static uint32_t   args[] = {
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
 
                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
-        0x88000869, /* 1000 1000 0000 0000  0000 1000 0110 1001 */
+        0xd800086d, /* 1101 1000 0000 0000  0000 1000 0110 1101 */
 
                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
-        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
+        0x50000000, /* 0101 0000 0000 0000  0000 0000 0000 0000 */
 
                     /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
-        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */
+        0xb8000001, /* 1011 1000 0000 0000  0000 0000 0000 0001 */
 
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
@@ -1553,19 +1566,19 @@
         0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
     };
 
-                    /* " ", "#", """, "%", "'", %00-%1F, %7F-%FF */
+                    /* " ", "#", """, "%", "'", not allowed */
 
     static uint32_t   html[] = {
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
 
                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
-        0x000000ad, /* 0000 0000 0000 0000  0000 0000 1010 1101 */
+        0x500000ad, /* 0101 0000 0000 0000  0000 0000 1010 1101 */
 
                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
-        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
+        0x50000000, /* 0101 0000 0000 0000  0000 0000 0000 0000 */
 
                     /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
-        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */
+        0xb8000001, /* 1011 1000 0000 0000  0000 0000 0000 0001 */
 
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
@@ -1573,19 +1586,19 @@
         0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
     };
 
-                    /* " ", """, "'", %00-%1F, %7F-%FF */
+                    /* " ", """, "'", not allowed */
 
     static uint32_t   refresh[] = {
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
 
                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
-        0x00000085, /* 0000 0000 0000 0000  0000 0000 1000 0101 */
+        0x50000085, /* 0101 0000 0000 0000  0000 0000 1000 0101 */
 
                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
-        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
+        0x50000000, /* 0101 0000 0000 0000  0000 0000 0000 0000 */
 
                     /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
-        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */
+        0xd8000001, /* 1011 1000 0000 0000  0000 0000 0000 0001 */
 
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c
index 49dca57..f0d3891 100644
--- a/src/event/ngx_event.c
+++ b/src/event/ngx_event.c
@@ -441,20 +441,23 @@
 
 #if (NGX_HAVE_REUSEPORT)
 
-    ls = cycle->listening.elts;
-    for (i = 0; i < cycle->listening.nelts; i++) {
-
-        if (!ls[i].reuseport || ls[i].worker != 0) {
-            continue;
-        }
-
-        if (ngx_clone_listening(cycle, &ls[i]) != NGX_OK) {
-            return NGX_CONF_ERROR;
-        }
-
-        /* cloning may change cycle->listening.elts */
+    if (!ngx_test_config) {
 
         ls = cycle->listening.elts;
+        for (i = 0; i < cycle->listening.nelts; i++) {
+
+            if (!ls[i].reuseport || ls[i].worker != 0) {
+                continue;
+            }
+
+            if (ngx_clone_listening(cycle, &ls[i]) != NGX_OK) {
+                return NGX_CONF_ERROR;
+            }
+
+            /* cloning may change cycle->listening.elts */
+
+            ls = cycle->listening.elts;
+        }
     }
 
 #endif
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index 69ec660..72b0da4 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -2919,9 +2919,12 @@
 ngx_ssl_shutdown(ngx_connection_t *c)
 {
     int         n, sslerr, mode;
+    ngx_int_t   rc;
     ngx_err_t   err;
     ngx_uint_t  tries;
 
+    rc = NGX_OK;
+
     ngx_ssl_ocsp_cleanup(c);
 
     if (SSL_in_init(c->ssl->connection)) {
@@ -2931,11 +2934,7 @@
          * Avoid calling SSL_shutdown() if handshake wasn't completed.
          */
 
-        SSL_free(c->ssl->connection);
-        c->ssl = NULL;
-        c->recv = ngx_recv;
-
-        return NGX_OK;
+        goto done;
     }
 
     if (c->timedout || c->error || c->buffered) {
@@ -2977,11 +2976,7 @@
         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;
-            c->recv = ngx_recv;
-
-            return NGX_OK;
+            goto done;
         }
 
         if (n == 0 && tries-- > 1) {
@@ -3007,11 +3002,11 @@
             }
 
             if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
-                return NGX_ERROR;
+                goto failed;
             }
 
             if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
-                return NGX_ERROR;
+                goto failed;
             }
 
             ngx_add_timer(c->read, 3000);
@@ -3020,23 +3015,33 @@
         }
 
         if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {
-            SSL_free(c->ssl->connection);
-            c->ssl = NULL;
-            c->recv = ngx_recv;
-
-            return NGX_OK;
+            goto done;
         }
 
         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;
-        c->recv = ngx_recv;
-
-        return NGX_ERROR;
+        break;
     }
+
+failed:
+
+    rc = NGX_ERROR;
+
+done:
+
+    if (c->ssl->shutdown_without_free) {
+        c->ssl->shutdown_without_free = 0;
+        c->recv = ngx_recv;
+        return rc;
+    }
+
+    SSL_free(c->ssl->connection);
+    c->ssl = NULL;
+    c->recv = ngx_recv;
+
+    return rc;
 }
 
 
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
index 49888d5..7d14647 100644
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -100,6 +100,7 @@
     unsigned                    buffer:1;
     unsigned                    no_wait_shutdown:1;
     unsigned                    no_send_shutdown:1;
+    unsigned                    shutdown_without_free:1;
     unsigned                    handshake_buffer_set:1;
     unsigned                    try_early_data:1;
     unsigned                    in_early:1;
diff --git a/src/event/ngx_event_timer.c b/src/event/ngx_event_timer.c
index 698b88f..35052bc 100644
--- a/src/event/ngx_event_timer.c
+++ b/src/event/ngx_event_timer.c
@@ -73,7 +73,7 @@
             return;
         }
 
-        ev = (ngx_event_t *) ((char *) node - offsetof(ngx_event_t, timer));
+        ev = ngx_rbtree_data(node, ngx_event_t, timer);
 
         ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
                        "event timer del: %d: %M",
@@ -113,7 +113,7 @@
          node;
          node = ngx_rbtree_next(&ngx_event_timer_rbtree, node))
     {
-        ev = (ngx_event_t *) ((char *) node - offsetof(ngx_event_t, timer));
+        ev = ngx_rbtree_data(node, ngx_event_t, timer);
 
         if (!ev->cancelable) {
             return NGX_AGAIN;
diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c
index 5191880..69ac0f7 100644
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -2019,10 +2019,12 @@
                 break;
             }
 
-            /* there was error while a header line parsing */
+            /* rc == NGX_HTTP_PARSE_INVALID_HEADER */
 
-            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                          "upstream sent invalid header");
+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+                          "upstream sent invalid header: \"%*s\\x%02xd...\"",
+                          r->header_end - r->header_name_start,
+                          r->header_name_start, *r->header_end);
 
             return NGX_HTTP_UPSTREAM_INVALID_HEADER;
         }
diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c
index 2e20e5f..65bd1e6 100644
--- a/src/http/modules/ngx_http_grpc_module.c
+++ b/src/http/modules/ngx_http_grpc_module.c
@@ -121,6 +121,7 @@
     unsigned                   done:1;
     unsigned                   status:1;
     unsigned                   rst:1;
+    unsigned                   goaway:1;
 
     ngx_http_request_t        *request;
 
@@ -1210,6 +1211,7 @@
     ctx->done = 0;
     ctx->status = 0;
     ctx->rst = 0;
+    ctx->goaway = 0;
     ctx->connection = NULL;
 
     return NGX_OK;
@@ -1565,6 +1567,7 @@
             && ctx->out == NULL
             && ctx->output_closed
             && !ctx->output_blocked
+            && !ctx->goaway
             && ctx->state == ngx_http_grpc_st_start)
         {
             u->keepalive = 1;
@@ -1714,6 +1717,8 @@
                 return NGX_HTTP_UPSTREAM_INVALID_HEADER;
             }
 
+            ctx->goaway = 1;
+
             continue;
         }
 
@@ -1907,6 +1912,7 @@
                         && ctx->out == NULL
                         && ctx->output_closed
                         && !ctx->output_blocked
+                        && !ctx->goaway
                         && b->last == b->pos)
                     {
                         u->keepalive = 1;
@@ -2035,6 +2041,7 @@
                     if (ctx->in == NULL
                         && ctx->output_closed
                         && !ctx->output_blocked
+                        && !ctx->goaway
                         && ctx->state == ngx_http_grpc_st_start)
                     {
                         u->keepalive = 1;
@@ -2170,6 +2177,8 @@
             }
 
             ctx->rst = 1;
+
+            continue;
         }
 
         if (ctx->type == NGX_HTTP_V2_GOAWAY_FRAME) {
@@ -2204,6 +2213,8 @@
                 return NGX_ERROR;
             }
 
+            ctx->goaway = 1;
+
             continue;
         }
 
@@ -3373,7 +3384,7 @@
             return NGX_ERROR;
         }
 
-        if (ch == '\0' || ch == CR || ch == LF) {
+        if (ch <= 0x20 || ch == 0x7f) {
             return NGX_ERROR;
         }
     }
@@ -3475,6 +3486,8 @@
         return NGX_AGAIN;
     }
 
+    ctx->state = ngx_http_grpc_st_start;
+
     return NGX_OK;
 }
 
diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c
index ac21451..bcbb244 100644
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -1193,7 +1193,7 @@
 
     loc_len = (r->valid_location && ctx->vars.uri.len) ? plcf->location.len : 0;
 
-    if (r->quoted_uri || r->space_in_uri || r->internal) {
+    if (r->quoted_uri || r->internal) {
         escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,
                                     r->uri.len - loc_len, NGX_ESCAPE_URI);
     } else {
@@ -1306,7 +1306,7 @@
         loc_len = (r->valid_location && ctx->vars.uri.len) ?
                       plcf->location.len : 0;
 
-        if (r->quoted_uri || r->space_in_uri || r->internal) {
+        if (r->quoted_uri || r->internal) {
             escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,
                                         r->uri.len - loc_len, NGX_ESCAPE_URI);
         }
@@ -2026,10 +2026,12 @@
             return NGX_AGAIN;
         }
 
-        /* there was error while a header line parsing */
+        /* rc == NGX_HTTP_PARSE_INVALID_HEADER */
 
-        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                      "upstream sent invalid header");
+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+                      "upstream sent invalid header: \"%*s\\x%02xd...\"",
+                      r->header_end - r->header_name_start,
+                      r->header_name_start, *r->header_end);
 
         return NGX_HTTP_UPSTREAM_INVALID_HEADER;
     }
diff --git a/src/http/modules/ngx_http_scgi_module.c b/src/http/modules/ngx_http_scgi_module.c
index 600999c..570713d 100644
--- a/src/http/modules/ngx_http_scgi_module.c
+++ b/src/http/modules/ngx_http_scgi_module.c
@@ -1140,10 +1140,12 @@
             return NGX_AGAIN;
         }
 
-        /* there was error while a header line parsing */
+        /* rc == NGX_HTTP_PARSE_INVALID_HEADER */
 
-        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                      "upstream sent invalid header");
+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+                      "upstream sent invalid header: \"%*s\\x%02xd...\"",
+                      r->header_end - r->header_name_start,
+                      r->header_name_start, *r->header_end);
 
         return NGX_HTTP_UPSTREAM_INVALID_HEADER;
     }
diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c
index 655be98..40a06c7 100644
--- a/src/http/modules/ngx_http_uwsgi_module.c
+++ b/src/http/modules/ngx_http_uwsgi_module.c
@@ -1361,10 +1361,12 @@
             return NGX_AGAIN;
         }
 
-        /* there was error while a header line parsing */
+        /* rc == NGX_HTTP_PARSE_INVALID_HEADER */
 
-        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                      "upstream sent invalid header");
+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+                      "upstream sent invalid header: \"%*s\\x%02xd...\"",
+                      r->header_end - r->header_name_start,
+                      r->header_name_start, *r->header_end);
 
         return NGX_HTTP_UPSTREAM_INVALID_HEADER;
     }
diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c
index 20ad89a..6460da2 100644
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -11,7 +11,7 @@
 
 
 static uint32_t  usual[] = {
-    0xffffdbfe, /* 1111 1111 1111 1111  1101 1011 1111 1110 */
+    0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
 
                 /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
     0x7fff37d6, /* 0111 1111 1111 1111  0011 0111 1101 0110 */
@@ -24,7 +24,7 @@
 #endif
 
                 /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
-    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
+    0x7fffffff, /* 0111 1111 1111 1111  1111 1111 1111 1111 */
 
     0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
     0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
@@ -116,10 +116,8 @@
         sw_host_end,
         sw_host_ip_literal,
         sw_port,
-        sw_host_http_09,
         sw_after_slash_in_uri,
         sw_check_uri,
-        sw_check_uri_http_09,
         sw_uri,
         sw_http_09,
         sw_http_H,
@@ -246,6 +244,11 @@
                         r->method = NGX_HTTP_OPTIONS;
                     }
 
+                    if (ngx_str7_cmp(m, 'C', 'O', 'N', 'N', 'E', 'C', 'T', ' '))
+                    {
+                        r->method = NGX_HTTP_CONNECT;
+                    }
+
                     break;
 
                 case 8:
@@ -393,7 +396,7 @@
                  */
                 r->uri_start = r->schema_end + 1;
                 r->uri_end = r->schema_end + 2;
-                state = sw_host_http_09;
+                state = sw_http_09;
                 break;
             default:
                 return NGX_HTTP_PARSE_INVALID_REQUEST;
@@ -467,35 +470,13 @@
                  */
                 r->uri_start = r->schema_end + 1;
                 r->uri_end = r->schema_end + 2;
-                state = sw_host_http_09;
+                state = sw_http_09;
                 break;
             default:
                 return NGX_HTTP_PARSE_INVALID_REQUEST;
             }
             break;
 
-        /* space+ after "http://host[:port] " */
-        case sw_host_http_09:
-            switch (ch) {
-            case ' ':
-                break;
-            case CR:
-                r->http_minor = 9;
-                state = sw_almost_done;
-                break;
-            case LF:
-                r->http_minor = 9;
-                goto done;
-            case 'H':
-                r->http_protocol.data = p;
-                state = sw_http_H;
-                break;
-            default:
-                return NGX_HTTP_PARSE_INVALID_REQUEST;
-            }
-            break;
-
-
         /* check "/.", "//", "%", and "\" (Win32) in URI */
         case sw_after_slash_in_uri:
 
@@ -507,7 +488,7 @@
             switch (ch) {
             case ' ':
                 r->uri_end = p;
-                state = sw_check_uri_http_09;
+                state = sw_http_09;
                 break;
             case CR:
                 r->uri_end = p;
@@ -547,9 +528,10 @@
             case '+':
                 r->plus_in_uri = 1;
                 break;
-            case '\0':
-                return NGX_HTTP_PARSE_INVALID_REQUEST;
             default:
+                if (ch < 0x20 || ch == 0x7f) {
+                    return NGX_HTTP_PARSE_INVALID_REQUEST;
+                }
                 state = sw_check_uri;
                 break;
             }
@@ -579,7 +561,7 @@
                 break;
             case ' ':
                 r->uri_end = p;
-                state = sw_check_uri_http_09;
+                state = sw_http_09;
                 break;
             case CR:
                 r->uri_end = p;
@@ -611,36 +593,14 @@
             case '+':
                 r->plus_in_uri = 1;
                 break;
-            case '\0':
-                return NGX_HTTP_PARSE_INVALID_REQUEST;
-            }
-            break;
-
-        /* space+ after URI */
-        case sw_check_uri_http_09:
-            switch (ch) {
-            case ' ':
-                break;
-            case CR:
-                r->http_minor = 9;
-                state = sw_almost_done;
-                break;
-            case LF:
-                r->http_minor = 9;
-                goto done;
-            case 'H':
-                r->http_protocol.data = p;
-                state = sw_http_H;
-                break;
             default:
-                r->space_in_uri = 1;
-                state = sw_check_uri;
-                p--;
+                if (ch < 0x20 || ch == 0x7f) {
+                    return NGX_HTTP_PARSE_INVALID_REQUEST;
+                }
                 break;
             }
             break;
 
-
         /* URI */
         case sw_uri:
 
@@ -665,8 +625,11 @@
             case '#':
                 r->complex_uri = 1;
                 break;
-            case '\0':
-                return NGX_HTTP_PARSE_INVALID_REQUEST;
+            default:
+                if (ch < 0x20 || ch == 0x7f) {
+                    return NGX_HTTP_PARSE_INVALID_REQUEST;
+                }
+                break;
             }
             break;
 
@@ -687,10 +650,7 @@
                 state = sw_http_H;
                 break;
             default:
-                r->space_in_uri = 1;
-                state = sw_uri;
-                p--;
-                break;
+                return NGX_HTTP_PARSE_INVALID_REQUEST;
             }
             break;
 
@@ -933,7 +893,8 @@
                     break;
                 }
 
-                if (ch == '\0') {
+                if (ch <= 0x20 || ch == 0x7f || ch == ':') {
+                    r->header_end = p;
                     return NGX_HTTP_PARSE_INVALID_HEADER;
                 }
 
@@ -1001,7 +962,8 @@
                 break;
             }
 
-            if (ch == '\0') {
+            if (ch <= 0x20 || ch == 0x7f) {
+                r->header_end = p;
                 return NGX_HTTP_PARSE_INVALID_HEADER;
             }
 
@@ -1024,6 +986,7 @@
                 r->header_end = p;
                 goto done;
             case '\0':
+                r->header_end = p;
                 return NGX_HTTP_PARSE_INVALID_HEADER;
             default:
                 r->header_start = p;
@@ -1047,6 +1010,7 @@
                 r->header_end = p;
                 goto done;
             case '\0':
+                r->header_end = p;
                 return NGX_HTTP_PARSE_INVALID_HEADER;
             }
             break;
@@ -1062,6 +1026,7 @@
             case LF:
                 goto done;
             case '\0':
+                r->header_end = p;
                 return NGX_HTTP_PARSE_INVALID_HEADER;
             default:
                 state = sw_value;
@@ -1165,10 +1130,6 @@
             }
 
             switch (ch) {
-            case ' ':
-                r->space_in_uri = 1;
-                state = sw_check_uri;
-                break;
             case '.':
                 r->complex_uri = 1;
                 state = sw_uri;
@@ -1199,6 +1160,9 @@
                 r->plus_in_uri = 1;
                 break;
             default:
+                if (ch <= 0x20 || ch == 0x7f) {
+                    return NGX_ERROR;
+                }
                 state = sw_check_uri;
                 break;
             }
@@ -1226,9 +1190,6 @@
             case '.':
                 r->uri_ext = p + 1;
                 break;
-            case ' ':
-                r->space_in_uri = 1;
-                break;
 #if (NGX_WIN32)
             case '\\':
                 r->complex_uri = 1;
@@ -1250,6 +1211,11 @@
             case '+':
                 r->plus_in_uri = 1;
                 break;
+            default:
+                if (ch <= 0x20 || ch == 0x7f) {
+                    return NGX_ERROR;
+                }
+                break;
             }
             break;
 
@@ -1261,12 +1227,14 @@
             }
 
             switch (ch) {
-            case ' ':
-                r->space_in_uri = 1;
-                break;
             case '#':
                 r->complex_uri = 1;
                 break;
+            default:
+                if (ch <= 0x20 || ch == 0x7f) {
+                    return NGX_ERROR;
+                }
+                break;
             }
             break;
         }
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index eae56c6..de1b96b 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1428,7 +1428,7 @@
     r->unparsed_uri.len = r->uri_end - r->uri_start;
     r->unparsed_uri.data = r->uri_start;
 
-    r->valid_unparsed_uri = (r->space_in_uri || r->empty_path_in_uri) ? 0 : 1;
+    r->valid_unparsed_uri = r->empty_path_in_uri ? 0 : 1;
 
     if (r->uri_ext) {
         if (r->args_start) {
@@ -1686,7 +1686,9 @@
         /* rc == NGX_HTTP_PARSE_INVALID_HEADER */
 
         ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                      "client sent invalid header line");
+                      "client sent invalid header line: \"%*s\\x%02xd...\"",
+                      r->header_end - r->header_name_start,
+                      r->header_name_start, *r->header_end);
 
         ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
         break;
@@ -2160,20 +2162,20 @@
         }
     }
 
-    if (r->method == NGX_HTTP_TRACE) {
-        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
-                      "client sent TRACE method");
-        ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
-        return NGX_ERROR;
-    }
-
     if (r->headers_in.transfer_encoding) {
         if (r->headers_in.transfer_encoding->value.len == 7
             && ngx_strncasecmp(r->headers_in.transfer_encoding->value.data,
                                (u_char *) "chunked", 7) == 0)
         {
-            r->headers_in.content_length = NULL;
-            r->headers_in.content_length_n = -1;
+            if (r->headers_in.content_length) {
+                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+                              "client sent \"Content-Length\" and "
+                              "\"Transfer-Encoding\" headers "
+                              "at the same time");
+                ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+                return NGX_ERROR;
+            }
+
             r->headers_in.chunked = 1;
 
         } else {
@@ -2193,6 +2195,20 @@
         }
     }
 
+    if (r->method == NGX_HTTP_CONNECT) {
+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+                      "client sent CONNECT method");
+        ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
+        return NGX_ERROR;
+    }
+
+    if (r->method == NGX_HTTP_TRACE) {
+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+                      "client sent TRACE method");
+        ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
+        return NGX_ERROR;
+    }
+
     return NGX_OK;
 }
 
@@ -2340,15 +2356,16 @@
             }
             break;
 
-        case '\0':
-            return NGX_DECLINED;
-
         default:
 
             if (ngx_path_separator(ch)) {
                 return NGX_DECLINED;
             }
 
+            if (ch <= 0x20 || ch == 0x7f) {
+                return NGX_DECLINED;
+            }
+
             if (ch >= 'A' && ch <= 'Z') {
                 alloc = 1;
             }
@@ -3580,6 +3597,8 @@
     if (c->ssl) {
         ngx_int_t  rc;
 
+        c->ssl->shutdown_without_free = 1;
+
         rc = ngx_ssl_shutdown(c);
 
         if (rc == NGX_ERROR) {
diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h
index 68d5c18..4e0ca8a 100644
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -25,22 +25,23 @@
 #define NGX_HTTP_VERSION_11                1001
 #define NGX_HTTP_VERSION_20                2000
 
-#define NGX_HTTP_UNKNOWN                   0x0001
-#define NGX_HTTP_GET                       0x0002
-#define NGX_HTTP_HEAD                      0x0004
-#define NGX_HTTP_POST                      0x0008
-#define NGX_HTTP_PUT                       0x0010
-#define NGX_HTTP_DELETE                    0x0020
-#define NGX_HTTP_MKCOL                     0x0040
-#define NGX_HTTP_COPY                      0x0080
-#define NGX_HTTP_MOVE                      0x0100
-#define NGX_HTTP_OPTIONS                   0x0200
-#define NGX_HTTP_PROPFIND                  0x0400
-#define NGX_HTTP_PROPPATCH                 0x0800
-#define NGX_HTTP_LOCK                      0x1000
-#define NGX_HTTP_UNLOCK                    0x2000
-#define NGX_HTTP_PATCH                     0x4000
-#define NGX_HTTP_TRACE                     0x8000
+#define NGX_HTTP_UNKNOWN                   0x00000001
+#define NGX_HTTP_GET                       0x00000002
+#define NGX_HTTP_HEAD                      0x00000004
+#define NGX_HTTP_POST                      0x00000008
+#define NGX_HTTP_PUT                       0x00000010
+#define NGX_HTTP_DELETE                    0x00000020
+#define NGX_HTTP_MKCOL                     0x00000040
+#define NGX_HTTP_COPY                      0x00000080
+#define NGX_HTTP_MOVE                      0x00000100
+#define NGX_HTTP_OPTIONS                   0x00000200
+#define NGX_HTTP_PROPFIND                  0x00000400
+#define NGX_HTTP_PROPPATCH                 0x00000800
+#define NGX_HTTP_LOCK                      0x00001000
+#define NGX_HTTP_UNLOCK                    0x00002000
+#define NGX_HTTP_PATCH                     0x00004000
+#define NGX_HTTP_TRACE                     0x00008000
+#define NGX_HTTP_CONNECT                   0x00010000
 
 #define NGX_HTTP_CONNECTION_CLOSE          1
 #define NGX_HTTP_CONNECTION_KEEP_ALIVE     2
@@ -467,9 +468,6 @@
     /* URI with "+" */
     unsigned                          plus_in_uri:1;
 
-    /* URI with " " */
-    unsigned                          space_in_uri:1;
-
     /* URI with empty path */
     unsigned                          empty_path_in_uri:1;
 
diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c
index 8bf51da..82279eb 100644
--- a/src/http/v2/ngx_http_v2.c
+++ b/src/http/v2/ngx_http_v2.c
@@ -3500,7 +3500,7 @@
             continue;
         }
 
-        if (ch == '\0' || ch == LF || ch == CR || ch == ':'
+        if (ch <= 0x20 || ch == 0x7f || ch == ':'
             || (ch >= 'A' && ch <= 'Z'))
         {
             ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
@@ -3649,7 +3649,8 @@
         { 4, "LOCK",      NGX_HTTP_LOCK },
         { 6, "UNLOCK",    NGX_HTTP_UNLOCK },
         { 5, "PATCH",     NGX_HTTP_PATCH },
-        { 5, "TRACE",     NGX_HTTP_TRACE }
+        { 5, "TRACE",     NGX_HTTP_TRACE },
+        { 7, "CONNECT",   NGX_HTTP_CONNECT }
     }, *test;
 
     if (r->method_name.len) {
diff --git a/src/os/unix/ngx_readv_chain.c b/src/os/unix/ngx_readv_chain.c
index a3577ce..b1ae4b5 100644
--- a/src/os/unix/ngx_readv_chain.c
+++ b/src/os/unix/ngx_readv_chain.c
@@ -96,7 +96,7 @@
             iov->iov_len += n;
 
         } else {
-            if (vec.nelts >= IOV_MAX) {
+            if (vec.nelts == vec.nalloc) {
                 break;
             }
 
diff --git a/src/os/win32/ngx_win32_init.c b/src/os/win32/ngx_win32_init.c
index 3249fb2..de66a44 100644
--- a/src/os/win32/ngx_win32_init.c
+++ b/src/os/win32/ngx_win32_init.c
@@ -295,7 +295,7 @@
         osviex_stub = (ngx_osviex_stub_t *) &osvi.wServicePackMinor;
 
         ngx_log_error(NGX_LOG_INFO, log, 0,
-                      "OS: %ud build:%ud, \"%s\", suite:%Xd, type:%ud",
+                      "OS: %ui build:%ud, \"%s\", suite:%Xd, type:%ud",
                       ngx_win32_version, osvi.dwBuildNumber, osvi.szCSDVersion,
                       osviex_stub->wSuiteMask, osviex_stub->wProductType);
 
@@ -305,7 +305,7 @@
             /* Win9x build */
 
             ngx_log_error(NGX_LOG_INFO, log, 0,
-                          "OS: %u build:%ud.%ud.%ud, \"%s\"",
+                          "OS: %ui build:%ud.%ud.%ud, \"%s\"",
                           ngx_win32_version,
                           osvi.dwBuildNumber >> 24,
                           (osvi.dwBuildNumber >> 16) & 0xff,
@@ -321,7 +321,7 @@
              * and we do not support VER_PLATFORM_WIN32s at all
              */
 
-            ngx_log_error(NGX_LOG_INFO, log, 0, "OS: %ud build:%ud, \"%s\"",
+            ngx_log_error(NGX_LOG_INFO, log, 0, "OS: %ui build:%ud, \"%s\"",
                           ngx_win32_version, osvi.dwBuildNumber,
                           osvi.szCSDVersion);
         }
diff --git a/src/os/win32/ngx_wsarecv_chain.c b/src/os/win32/ngx_wsarecv_chain.c
index 87f0239..4f95d5a 100644
--- a/src/os/win32/ngx_wsarecv_chain.c
+++ b/src/os/win32/ngx_wsarecv_chain.c
@@ -10,7 +10,7 @@
 #include <ngx_event.h>
 
 
-#define NGX_WSABUFS  8
+#define NGX_WSABUFS  64
 
 
 ssize_t
@@ -57,6 +57,10 @@
             wsabuf->len += n;
 
         } else {
+            if (vec.nelts == vec.nalloc) {
+                break;
+            }
+
             wsabuf = ngx_array_push(&vec);
             if (wsabuf == NULL) {
                 return NGX_ERROR;
diff --git a/src/os/win32/ngx_wsasend_chain.c b/src/os/win32/ngx_wsasend_chain.c
index e2dde22..cd50e71 100644
--- a/src/os/win32/ngx_wsasend_chain.c
+++ b/src/os/win32/ngx_wsasend_chain.c
@@ -10,7 +10,7 @@
 #include <ngx_event.h>
 
 
-#define NGX_WSABUFS  8
+#define NGX_WSABUFS  64
 
 
 ngx_chain_t *
@@ -47,7 +47,7 @@
 
     vec.elts = wsabufs;
     vec.size = sizeof(WSABUF);
-    vec.nalloc = NGX_WSABUFS;
+    vec.nalloc = ngx_min(NGX_WSABUFS, ngx_max_wsabufs);
     vec.pool = c->pool;
 
     for ( ;; ) {
@@ -59,10 +59,8 @@
 
         /* create the WSABUF and coalesce the neighbouring bufs */
 
-        for (cl = in;
-             cl && vec.nelts < ngx_max_wsabufs && send < limit;
-             cl = cl->next)
-        {
+        for (cl = in; cl && send < limit; cl = cl->next) {
+
             if (ngx_buf_special(cl->buf)) {
                 continue;
             }
@@ -77,6 +75,10 @@
                 wsabuf->len += cl->buf->last - cl->buf->pos;
 
             } else {
+                if (vec.nelts == vec.nalloc) {
+                    break;
+                }
+
                 wsabuf = ngx_array_push(&vec);
                 if (wsabuf == NULL) {
                     return NGX_CHAIN_ERROR;
@@ -169,7 +171,7 @@
         vec.elts = wsabufs;
         vec.nelts = 0;
         vec.size = sizeof(WSABUF);
-        vec.nalloc = NGX_WSABUFS;
+        vec.nalloc = ngx_min(NGX_WSABUFS, ngx_max_wsabufs);
         vec.pool = c->pool;
 
         send = 0;
@@ -178,10 +180,8 @@
 
         /* create the WSABUF and coalesce the neighbouring bufs */
 
-        for (cl = in;
-             cl && vec.nelts < ngx_max_wsabufs && send < limit;
-             cl = cl->next)
-        {
+        for (cl = in; cl && send < limit; cl = cl->next) {
+
             if (ngx_buf_special(cl->buf)) {
                 continue;
             }
@@ -196,6 +196,10 @@
                 wsabuf->len += cl->buf->last - cl->buf->pos;
 
             } else {
+                if (vec.nelts == vec.nalloc) {
+                    break;
+                }
+
                 wsabuf = ngx_array_push(&vec);
                 if (wsabuf == NULL) {
                     return NGX_CHAIN_ERROR;