Merge branch 'nginx' (nginx-1.19.1).

Change-Id: I92dececf032ea3e1da47f475a578840e4681117d
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
diff --git a/.hgtags b/.hgtags
index cf161ee..7c9513a 100644
--- a/.hgtags
+++ b/.hgtags
@@ -450,3 +450,4 @@
 5e8d52bca714d4b85284ddb649d1ba4a3ca978a8 release-1.17.9
 c44970de01474f6f3e01b0adea85ec1d03e3a5f2 release-1.17.10
 cbe6ba650211541310618849168631ce0b788f35 release-1.19.0
+062920e2f3bf871ef7a3d8496edec1b3065faf80 release-1.19.1
diff --git a/BUILD b/BUILD
index d0c27fe..bd6af32 100644
--- a/BUILD
+++ b/BUILD
@@ -1520,5 +1520,5 @@
     preinst = "@nginx_pkgoss//:debian_preinst",
     prerm = "@nginx_pkgoss//:debian_prerm",
     section = "httpd",
-    version = "1.19.0",
+    version = "1.19.1",
 )
diff --git a/build.bzl b/build.bzl
index 7af80d0..dd14d2b 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 = "96e072b9f1d1975340f7c9d9ed7f8827d7e4d36f",  # nginx-1.19.0
+        commit = "cfc029dae67e72379ec03a297648c6e6a3e71f0c",  # nginx-1.19.1
         remote = "https://nginx.googlesource.com/nginx-pkgoss",
-        shallow_since = "1590505389 +0300",
+        shallow_since = "1594137158 +0300",
     )
 
 def nginx_repositories_zlib(bind):
diff --git a/contrib/vim/syntax/nginx.vim b/contrib/vim/syntax/nginx.vim
index 1a3a7b7..8304708 100644
--- a/contrib/vim/syntax/nginx.vim
+++ b/contrib/vim/syntax/nginx.vim
@@ -141,6 +141,7 @@
 syn keyword ngxDirective contained api
 syn keyword ngxDirective contained auth_basic
 syn keyword ngxDirective contained auth_basic_user_file
+syn keyword ngxDirective contained auth_delay
 syn keyword ngxDirective contained auth_http
 syn keyword ngxDirective contained auth_http_header
 syn keyword ngxDirective contained auth_http_pass_client_cert
@@ -332,6 +333,7 @@
 syn keyword ngxDirective contained js_access
 syn keyword ngxDirective contained js_content
 syn keyword ngxDirective contained js_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
@@ -348,6 +350,7 @@
 syn keyword ngxDirective contained least_conn
 syn keyword ngxDirective contained least_time
 syn keyword ngxDirective contained limit_conn
+syn keyword ngxDirective contained limit_conn_dry_run
 syn keyword ngxDirective contained limit_conn_log_level
 syn keyword ngxDirective contained limit_conn_status
 syn keyword ngxDirective contained limit_conn_zone
@@ -595,6 +598,9 @@
 syn keyword ngxDirective contained ssl_ecdh_curve
 syn keyword ngxDirective contained ssl_engine
 syn keyword ngxDirective contained ssl_handshake_timeout
+syn keyword ngxDirective contained ssl_ocsp
+syn keyword ngxDirective contained ssl_ocsp_cache
+syn keyword ngxDirective contained ssl_ocsp_responder
 syn keyword ngxDirective contained ssl_password_file
 syn keyword ngxDirective contained ssl_prefer_server_ciphers
 syn keyword ngxDirective contained ssl_preread
@@ -770,6 +776,7 @@
 syn keyword ngxDirectiveThirdParty contained auth_gss_force_realm
 syn keyword ngxDirectiveThirdParty contained auth_gss_format_full
 syn keyword ngxDirectiveThirdParty contained auth_gss_keytab
+syn keyword ngxDirectiveThirdParty contained auth_gss_map_to_local
 syn keyword ngxDirectiveThirdParty contained auth_gss_realm
 syn keyword ngxDirectiveThirdParty contained auth_gss_service_name
 
@@ -791,8 +798,8 @@
 
 " AJP protocol proxy
 " https://github.com/yaoweibin/nginx_ajp_module
-syn keyword ngxDirectiveThirdParty contained ajp_buffer_size
 syn keyword ngxDirectiveThirdParty contained ajp_buffers
+syn keyword ngxDirectiveThirdParty contained ajp_buffer_size
 syn keyword ngxDirectiveThirdParty contained ajp_busy_buffers_size
 syn keyword ngxDirectiveThirdParty contained ajp_cache
 syn keyword ngxDirectiveThirdParty contained ajp_cache_key
@@ -818,6 +825,7 @@
 syn keyword ngxDirectiveThirdParty contained ajp_pass_request_body
 syn keyword ngxDirectiveThirdParty contained ajp_pass_request_headers
 syn keyword ngxDirectiveThirdParty contained ajp_read_timeout
+syn keyword ngxDirectiveThirdParty contained ajp_secret
 syn keyword ngxDirectiveThirdParty contained ajp_send_lowat
 syn keyword ngxDirectiveThirdParty contained ajp_send_timeout
 syn keyword ngxDirectiveThirdParty contained ajp_store
@@ -854,8 +862,8 @@
 syn keyword ngxDirectiveThirdParty contained content_handler_type
 syn keyword ngxDirectiveThirdParty contained handler_code
 syn keyword ngxDirectiveThirdParty contained handler_name
-syn keyword ngxDirectiveThirdParty contained handler_type
 syn keyword ngxDirectiveThirdParty contained handlers_lazy_init
+syn keyword ngxDirectiveThirdParty contained handler_type
 syn keyword ngxDirectiveThirdParty contained header_filter_code
 syn keyword ngxDirectiveThirdParty contained header_filter_name
 syn keyword ngxDirectiveThirdParty contained header_filter_property
@@ -871,6 +879,10 @@
 syn keyword ngxDirectiveThirdParty contained jvm_path
 syn keyword ngxDirectiveThirdParty contained jvm_var
 syn keyword ngxDirectiveThirdParty contained jvm_workers
+syn keyword ngxDirectiveThirdParty contained log_handler_code
+syn keyword ngxDirectiveThirdParty contained log_handler_name
+syn keyword ngxDirectiveThirdParty contained log_handler_property
+syn keyword ngxDirectiveThirdParty contained log_handler_type
 syn keyword ngxDirectiveThirdParty contained max_balanced_tcp_connections
 syn keyword ngxDirectiveThirdParty contained rewrite_handler_code
 syn keyword ngxDirectiveThirdParty contained rewrite_handler_name
@@ -879,6 +891,7 @@
 syn keyword ngxDirectiveThirdParty contained shared_map
 syn keyword ngxDirectiveThirdParty contained write_page_size
 
+
 " Certificate Transparency
 " https://github.com/grahamedgecombe/nginx-ct
 syn keyword ngxDirectiveThirdParty contained ssl_ct
@@ -942,6 +955,7 @@
 syn keyword ngxDirectiveThirdParty contained fancyindex_ignore
 syn keyword ngxDirectiveThirdParty contained fancyindex_localtime
 syn keyword ngxDirectiveThirdParty contained fancyindex_name_length
+syn keyword ngxDirectiveThirdParty contained fancyindex_show_dotfiles
 syn keyword ngxDirectiveThirdParty contained fancyindex_show_path
 syn keyword ngxDirectiveThirdParty contained fancyindex_time_format
 
@@ -991,8 +1005,8 @@
 syn keyword ngxDirectiveThirdParty contained nchan_benchmark_subscriber_distribution
 syn keyword ngxDirectiveThirdParty contained nchan_benchmark_subscribers_per_channel
 syn keyword ngxDirectiveThirdParty contained nchan_benchmark_time
-syn keyword ngxDirectiveThirdParty contained nchan_channel_event_string
 syn keyword ngxDirectiveThirdParty contained nchan_channel_events_channel_id
+syn keyword ngxDirectiveThirdParty contained nchan_channel_event_string
 syn keyword ngxDirectiveThirdParty contained nchan_channel_group
 syn keyword ngxDirectiveThirdParty contained nchan_channel_group_accounting
 syn keyword ngxDirectiveThirdParty contained nchan_channel_id
@@ -1000,6 +1014,10 @@
 syn keyword ngxDirectiveThirdParty contained nchan_channel_timeout
 syn keyword ngxDirectiveThirdParty contained nchan_deflate_message_for_websocket
 syn keyword ngxDirectiveThirdParty contained nchan_eventsource_event
+syn keyword ngxDirectiveThirdParty contained nchan_eventsource_ping_comment
+syn keyword ngxDirectiveThirdParty contained nchan_eventsource_ping_data
+syn keyword ngxDirectiveThirdParty contained nchan_eventsource_ping_event
+syn keyword ngxDirectiveThirdParty contained nchan_eventsource_ping_interval
 syn keyword ngxDirectiveThirdParty contained nchan_group_location
 syn keyword ngxDirectiveThirdParty contained nchan_group_max_channels
 syn keyword ngxDirectiveThirdParty contained nchan_group_max_messages
@@ -1047,10 +1065,10 @@
 syn keyword ngxDirectiveThirdParty contained nchan_stub_status
 syn keyword ngxDirectiveThirdParty contained nchan_sub_channel_id
 syn keyword ngxDirectiveThirdParty contained nchan_subscribe_existing_channels_only
-syn keyword ngxDirectiveThirdParty contained nchan_subscribe_request
 syn keyword ngxDirectiveThirdParty contained nchan_subscriber
 syn keyword ngxDirectiveThirdParty contained nchan_subscriber_channel_id
 syn keyword ngxDirectiveThirdParty contained nchan_subscriber_compound_etag_message_id
+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_last_message_id
@@ -1987,11 +2005,7 @@
 " update upstreams' config by restful interface
 " https://github.com/yzprofile/ngx_http_dyups_module
 syn keyword ngxDirectiveThirdParty contained dyups_interface
-syn keyword ngxDirectiveThirdParty contained dyups_read_msg_log
-syn keyword ngxDirectiveThirdParty contained dyups_read_msg_timeout
 syn keyword ngxDirectiveThirdParty contained dyups_shm_zone_size
-syn keyword ngxDirectiveThirdParty contained dyups_trylock
-syn keyword ngxDirectiveThirdParty contained dyups_upstream_conf
 
 " add given content to the end of the response according to the condition specified
 " https://github.com/flygoast/ngx_http_footer_if_filter
@@ -2308,6 +2322,62 @@
 " https://github.com/flygoast/ngx_http_upstream_ketama_chash
 syn keyword ngxDirectiveThirdParty contained ketama_chash
 
+" nginx-sticky-module-ng
+" https://github.com/ayty-adrianomartins/nginx-sticky-module-ng
+syn keyword ngxDirectiveThirdParty contained sticky_no_fallback
+
+" dynamic linking and call the function of your application
+" https://github.com/Taymindis/nginx-link-function
+syn keyword ngxDirectiveThirdParty contained ngx_link_func_add_prop
+syn keyword ngxDirectiveThirdParty contained ngx_link_func_add_req_header
+syn keyword ngxDirectiveThirdParty contained ngx_link_func_ca_cert
+syn keyword ngxDirectiveThirdParty contained ngx_link_func_call
+syn keyword ngxDirectiveThirdParty contained ngx_link_func_download_link_lib
+syn keyword ngxDirectiveThirdParty contained ngx_link_func_lib
+syn keyword ngxDirectiveThirdParty contained ngx_link_func_shm_size
+syn keyword ngxDirectiveThirdParty contained ngx_link_func_subrequest
+
+" purge content from FastCGI, proxy, SCGI and uWSGI caches
+" https://github.com/torden/ngx_cache_purge
+syn keyword ngxDirectiveThirdParty contained cache_purge_response_type
+
+" set the flags "HttpOnly", "secure" and "SameSite" for cookies
+" https://github.com/AirisX/nginx_cookie_flag_module
+syn keyword ngxDirectiveThirdParty contained set_cookie_flag
+
+" Embed websockify into Nginx (convert any tcp connection into websocket)
+" https://github.com/tg123/websockify-nginx-module
+syn keyword ngxDirectiveThirdParty contained websockify_buffer_size
+syn keyword ngxDirectiveThirdParty contained websockify_connect_timeout
+syn keyword ngxDirectiveThirdParty contained websockify_pass
+syn keyword ngxDirectiveThirdParty contained websockify_read_timeout
+syn keyword ngxDirectiveThirdParty contained websockify_send_timeout
+
+" IP2Location Nginx
+" https://github.com/ip2location/ip2location-nginx
+syn keyword ngxDirectiveThirdParty contained ip2location
+syn keyword ngxDirectiveThirdParty contained ip2location_access_type
+syn keyword ngxDirectiveThirdParty contained ip2location_proxy
+syn keyword ngxDirectiveThirdParty contained ip2location_proxy_recursive
+
+" IP2Proxy module for Nginx
+" https://github.com/ip2location/ip2proxy-nginx
+syn keyword ngxDirectiveThirdParty contained ip2proxy
+syn keyword ngxDirectiveThirdParty contained ip2proxy_access_type
+syn keyword ngxDirectiveThirdParty contained ip2proxy_as
+syn keyword ngxDirectiveThirdParty contained ip2proxy_asn
+syn keyword ngxDirectiveThirdParty contained ip2proxy_city
+syn keyword ngxDirectiveThirdParty contained ip2proxy_country_long
+syn keyword ngxDirectiveThirdParty contained ip2proxy_country_short
+syn keyword ngxDirectiveThirdParty contained ip2proxy_database
+syn keyword ngxDirectiveThirdParty contained ip2proxy_domain
+syn keyword ngxDirectiveThirdParty contained ip2proxy_is_proxy
+syn keyword ngxDirectiveThirdParty contained ip2proxy_isp
+syn keyword ngxDirectiveThirdParty contained ip2proxy_last_seen
+syn keyword ngxDirectiveThirdParty contained ip2proxy_proxy_type
+syn keyword ngxDirectiveThirdParty contained ip2proxy_region
+syn keyword ngxDirectiveThirdParty contained ip2proxy_reverse_proxy
+syn keyword ngxDirectiveThirdParty contained ip2proxy_usage_type
 
 
 
diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml
index 423ccac..2f1bc48 100644
--- a/docs/xml/nginx/changes.xml
+++ b/docs/xml/nginx/changes.xml
@@ -5,6 +5,130 @@
 <change_log title="nginx">
 
 
+<changes ver="1.19.1" date="2020-07-07">
+
+<change type="change">
+<para lang="ru">
+директивы lingering_close, lingering_time и lingering_timeout
+теперь работают при использовании HTTP/2.
+</para>
+<para lang="en">
+the "lingering_close", "lingering_time", and "lingering_timeout" directives
+now work when using HTTP/2.
+</para>
+</change>
+
+<change type="change">
+<para lang="ru">
+теперь лишние данные, присланные бэкендом, всегда отбрасываются.
+</para>
+<para lang="en">
+now extra data sent by a backend are always discarded.
+</para>
+</change>
+
+<change type="change">
+<para lang="ru">
+теперь при получении слишком короткого ответа от FastCGI-сервера
+nginx пытается отправить клиенту доступную часть ответа,
+после чего закрывает соединение с клиентом.
+</para>
+<para lang="en">
+now after receiving a too short response from a FastCGI server
+nginx tries to send the available part of the response to the client,
+and then closes the client connection.
+</para>
+</change>
+
+<change type="change">
+<para lang="ru">
+теперь при получении ответа некорректной длины от gRPC-бэкенда
+nginx прекращает обработку ответа с ошибкой.
+</para>
+<para lang="en">
+now after receiving a response with incorrect length from a gRPC backend
+nginx stops response processing with an error.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+параметр min_free в директивах proxy_cache_path, fastcgi_cache_path,
+scgi_cache_path и uwsgi_cache_path.<br/>
+Спасибо Adam Bambuch.
+</para>
+<para lang="en">
+the "min_free" parameter of the "proxy_cache_path", "fastcgi_cache_path",
+"scgi_cache_path", and "uwsgi_cache_path" directives.<br/>
+Thanks to Adam Bambuch.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+nginx не удалял unix domain listen-сокеты
+при плавном завершении по сигналу SIGQUIT.
+</para>
+<para lang="en">
+nginx did not delete unix domain listen sockets
+during graceful shutdown on the SIGQUIT signal.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+UDP-пакеты нулевого размера не проксировались.
+</para>
+<para lang="en">
+zero length UDP datagrams were not proxied.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+проксирование на uwsgi-бэкенды с использованием SSL могло не работать.<br/>
+Спасибо Guanzhong Chen.
+</para>
+<para lang="en">
+proxying to uwsgi backends using SSL might not work.<br/>
+Thanks to Guanzhong Chen.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+в обработке ошибок при использовании директивы ssl_ocsp.
+</para>
+<para lang="en">
+in error handling when using the "ssl_ocsp" directive.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+при использовании файловых систем XFS и NFS
+размер кэша на диске мог считаться некорректно.
+</para>
+<para lang="en">
+on XFS and NFS file systems
+disk cache size might be calculated incorrectly.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+если сервер memcached возвращал некорректный ответ,
+в логах могли появляться сообщения "negative size buf in writer".
+</para>
+<para lang="en">
+"negative size buf in writer" alerts might appear in logs
+if a memcached server returned a malformed response.
+</para>
+</change>
+
+</changes>
+
+
 <changes ver="1.19.0" date="2020-05-26">
 
 <change type="feature">
diff --git a/src/core/nginx.c b/src/core/nginx.c
index 271d595..0730440 100644
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -508,6 +508,7 @@
             ngx_memzero(ls, sizeof(ngx_listening_t));
 
             ls->fd = (ngx_socket_t) s;
+            ls->inherited = 1;
         }
     }
 
diff --git a/src/core/nginx.h b/src/core/nginx.h
index 0e99cf4..24db599 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -13,8 +13,8 @@
 #define NGINX_NAME         "nginx"
 #endif
 
-#define nginx_version      1019000
-#define NGINX_VERSION      "1.19.0"
+#define nginx_version      1019001
+#define NGINX_VERSION      "1.19.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 3368253..88fefce 100644
--- a/src/core/ngx_connection.c
+++ b/src/core/ngx_connection.c
@@ -1070,7 +1070,8 @@
 
         if (ls[i].sockaddr->sa_family == AF_UNIX
             && ngx_process <= NGX_PROCESS_MASTER
-            && ngx_new_binary == 0)
+            && ngx_new_binary == 0
+            && (!ls[i].inherited || ngx_getppid() != ngx_parent))
         {
             u_char *name = ls[i].addr_text.data + sizeof("unix:") - 1;
 
diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c
index 95f4bdf..764cf46 100644
--- a/src/core/ngx_cycle.c
+++ b/src/core/ngx_cycle.c
@@ -520,6 +520,7 @@
                     == NGX_OK)
                 {
                     nls[n].fd = ls[i].fd;
+                    nls[n].inherited = ls[i].inherited;
                     nls[n].previous = &ls[i];
                     ls[i].remain = 1;
 
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index 2202857..33af90a 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -943,6 +943,9 @@
 ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
     ngx_int_t depth)
 {
+    SSL_CTX_set_verify(ssl->ctx, SSL_CTX_get_verify_mode(ssl->ctx),
+                       ngx_ssl_verify_callback);
+
     SSL_CTX_set_verify_depth(ssl->ctx, depth);
 
     if (cert->len == 0) {
diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c
index a0a63c1..0e79d6c 100644
--- a/src/event/ngx_event_openssl_stapling.c
+++ b/src/event/ngx_event_openssl_stapling.c
@@ -980,6 +980,7 @@
         if (ocsp->ncert == n - 1 || (ocf->depth == 2 && ocsp->ncert == 1)) {
             ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
                            "ssl ocsp validated, certs:%ui", ocsp->ncert);
+            rc = NGX_OK;
             goto done;
         }
 
@@ -988,7 +989,8 @@
 
         ctx = ngx_ssl_ocsp_start(c->log);
         if (ctx == NULL) {
-            goto failed;
+            rc = NGX_ERROR;
+            goto done;
         }
 
         ocsp->ctx = ctx;
@@ -1012,8 +1014,9 @@
         ctx->uri = ocf->uri;
         ctx->port = ocf->port;
 
-        if (ngx_ssl_ocsp_responder(c, ctx) != NGX_OK) {
-            goto failed;
+        rc = ngx_ssl_ocsp_responder(c, ctx);
+        if (rc != NGX_OK) {
+            goto done;
         }
 
         if (ctx->uri.len == 0) {
@@ -1025,7 +1028,7 @@
         rc = ngx_ssl_ocsp_cache_lookup(ctx);
 
         if (rc == NGX_ERROR) {
-            goto failed;
+            goto done;
         }
 
         if (rc == NGX_DECLINED) {
@@ -1051,12 +1054,12 @@
 
 done:
 
-    ocsp->status = NGX_OK;
-    return;
+    ocsp->status = rc;
 
-failed:
-
-    ocsp->status = NGX_ERROR;
+    if (c->ssl->in_ocsp) {
+        c->ssl->handshaked = 1;
+        c->ssl->handler(c);
+    }
 }
 
 
@@ -1073,22 +1076,16 @@
 
     rc = ngx_ssl_ocsp_verify(ctx);
     if (rc != NGX_OK) {
-        ocsp->status = rc;
-        ngx_ssl_ocsp_done(ctx);
         goto done;
     }
 
     rc = ngx_ssl_ocsp_cache_store(ctx);
     if (rc != NGX_OK) {
-        ocsp->status = rc;
-        ngx_ssl_ocsp_done(ctx);
         goto done;
     }
 
     if (ctx->status != V_OCSP_CERTSTATUS_GOOD) {
         ocsp->cert_status = ctx->status;
-        ocsp->status = NGX_OK;
-        ngx_ssl_ocsp_done(ctx);
         goto done;
     }
 
@@ -1096,15 +1093,17 @@
 
     ngx_ssl_ocsp_validate_next(c);
 
+    return;
+
 done:
 
-    if (ocsp->status == NGX_AGAIN || !c->ssl->in_ocsp) {
-        return;
+    ocsp->status = rc;
+    ngx_ssl_ocsp_done(ctx);
+
+    if (c->ssl->in_ocsp) {
+        c->ssl->handshaked = 1;
+        c->ssl->handler(c);
     }
-
-    c->ssl->handshaked = 1;
-
-    c->ssl->handler(c);
 }
 
 
diff --git a/src/event/ngx_event_pipe.c b/src/event/ngx_event_pipe.c
index 531b13a..54412e1 100644
--- a/src/event/ngx_event_pipe.c
+++ b/src/event/ngx_event_pipe.c
@@ -960,6 +960,22 @@
         return NGX_OK;
     }
 
+    if (p->upstream_done) {
+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,
+                       "input data after close");
+        return NGX_OK;
+    }
+
+    if (p->length == 0) {
+        p->upstream_done = 1;
+
+        ngx_log_error(NGX_LOG_WARN, p->log, 0,
+                      "upstream sent more data than specified in "
+                      "\"Content-Length\" header");
+
+        return NGX_OK;
+    }
+
     cl = ngx_chain_get_free_buf(p->pool, &p->free);
     if (cl == NULL) {
         return NGX_ERROR;
@@ -987,6 +1003,18 @@
         return NGX_OK;
     }
 
+    if (b->last - b->pos > p->length) {
+
+        ngx_log_error(NGX_LOG_WARN, p->log, 0,
+                      "upstream sent more data than specified in "
+                      "\"Content-Length\" header");
+
+        b->last = b->pos + p->length;
+        p->upstream_done = 1;
+
+        return NGX_OK;
+    }
+
     p->length -= b->last - b->pos;
 
     return NGX_OK;
diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c
index 2be0672..e50d1a7 100644
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -81,12 +81,15 @@
     size_t                         length;
     size_t                         padding;
 
+    off_t                          rest;
+
     ngx_chain_t                   *free;
     ngx_chain_t                   *busy;
 
     unsigned                       fastcgi_stdout:1;
     unsigned                       large_stderr:1;
     unsigned                       header_sent:1;
+    unsigned                       closed:1;
 
     ngx_array_t                   *split_parts;
 
@@ -2075,13 +2078,31 @@
 static ngx_int_t
 ngx_http_fastcgi_input_filter_init(void *data)
 {
-    ngx_http_request_t           *r = data;
+    ngx_http_request_t  *r = data;
+
+    ngx_http_upstream_t          *u;
+    ngx_http_fastcgi_ctx_t       *f;
     ngx_http_fastcgi_loc_conf_t  *flcf;
 
+    u = r->upstream;
+
+    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
     flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
 
-    r->upstream->pipe->length = flcf->keep_conn ?
-                                (off_t) sizeof(ngx_http_fastcgi_header_t) : -1;
+    u->pipe->length = flcf->keep_conn ?
+                      (off_t) sizeof(ngx_http_fastcgi_header_t) : -1;
+
+    if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT
+        || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED)
+    {
+        f->rest = 0;
+
+    } else if (r->method == NGX_HTTP_HEAD) {
+        f->rest = -2;
+
+    } else {
+        f->rest = u->headers_in.content_length_n;
+    }
 
     return NGX_OK;
 }
@@ -2106,6 +2127,15 @@
     f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
     flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
 
+    if (p->upstream_done || f->closed) {
+        r->upstream->keepalive = 0;
+
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
+                       "http fastcgi data after close");
+
+        return NGX_OK;
+    }
+
     b = NULL;
     prev = &buf->shadow;
 
@@ -2128,13 +2158,25 @@
             if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {
                 f->state = ngx_http_fastcgi_st_padding;
 
+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
+                               "http fastcgi closed stdout");
+
+                if (f->rest > 0) {
+                    ngx_log_error(NGX_LOG_ERR, p->log, 0,
+                                  "upstream prematurely closed "
+                                  "FastCGI stdout");
+
+                    p->upstream_error = 1;
+                    p->upstream_eof = 0;
+                    f->closed = 1;
+
+                    break;
+                }
+
                 if (!flcf->keep_conn) {
                     p->upstream_done = 1;
                 }
 
-                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
-                               "http fastcgi closed stdout");
-
                 continue;
             }
 
@@ -2143,6 +2185,18 @@
                 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
                                "http fastcgi sent end request");
 
+                if (f->rest > 0) {
+                    ngx_log_error(NGX_LOG_ERR, p->log, 0,
+                                  "upstream prematurely closed "
+                                  "FastCGI request");
+
+                    p->upstream_error = 1;
+                    p->upstream_eof = 0;
+                    f->closed = 1;
+
+                    break;
+                }
+
                 if (!flcf->keep_conn) {
                     p->upstream_done = 1;
                     break;
@@ -2289,15 +2343,31 @@
             f->pos += f->length;
             b->last = f->pos;
 
-            continue;
+        } else {
+            f->length -= f->last - f->pos;
+            f->pos = f->last;
+            b->last = f->last;
         }
 
-        f->length -= f->last - f->pos;
+        if (f->rest == -2) {
+            f->rest = r->upstream->headers_in.content_length_n;
+        }
 
-        b->last = f->last;
+        if (f->rest >= 0) {
 
-        break;
+            if (b->last - b->pos > f->rest) {
+                ngx_log_error(NGX_LOG_WARN, p->log, 0,
+                              "upstream sent more data than specified in "
+                              "\"Content-Length\" header");
 
+                b->last = b->pos + f->rest;
+                p->upstream_done = 1;
+
+                break;
+            }
+
+            f->rest -= b->last - b->pos;
+        }
     }
 
     if (flcf->keep_conn) {
@@ -2391,6 +2461,14 @@
 
             if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
 
+                if (f->rest > 0) {
+                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                                  "upstream prematurely closed "
+                                  "FastCGI request");
+                    u->error = 1;
+                    break;
+                }
+
                 if (f->pos + f->padding < f->last) {
                     u->length = 0;
                     break;
@@ -2510,13 +2588,27 @@
             f->pos += f->length;
             b->last = f->pos;
 
-            continue;
+        } else {
+            f->length -= f->last - f->pos;
+            f->pos = f->last;
+            b->last = f->last;
         }
 
-        f->length -= f->last - f->pos;
-        b->last = f->last;
+        if (f->rest >= 0) {
 
-        break;
+            if (b->last - b->pos > f->rest) {
+                ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
+                              "upstream sent more data than specified in "
+                              "\"Content-Length\" header");
+
+                b->last = b->pos + f->rest;
+                u->length = 0;
+
+                break;
+            }
+
+            f->rest -= b->last - b->pos;
+        }
     }
 
     return NGX_OK;
diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c
index 992211e..ab4ad6b 100644
--- a/src/http/modules/ngx_http_grpc_module.c
+++ b/src/http/modules/ngx_http_grpc_module.c
@@ -84,6 +84,8 @@
     ngx_uint_t                 pings;
     ngx_uint_t                 settings;
 
+    off_t                      length;
+
     ssize_t                    send_window;
     size_t                     recv_window;
 
@@ -1953,10 +1955,28 @@
     r = ctx->request;
     u = r->upstream;
 
-    u->length = 1;
+    if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT
+        || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED
+        || r->method == NGX_HTTP_HEAD)
+    {
+        ctx->length = 0;
+
+    } else {
+        ctx->length = u->headers_in.content_length_n;
+    }
 
     if (ctx->end_stream) {
+
+        if (ctx->length > 0) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "upstream prematurely closed stream");
+            return NGX_ERROR;
+        }
+
         u->length = 0;
+
+    } else {
+        u->length = 1;
     }
 
     return NGX_OK;
@@ -1999,6 +2019,12 @@
 
                 if (ctx->done) {
 
+                    if (ctx->length > 0) {
+                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                                      "upstream prematurely closed stream");
+                        return NGX_ERROR;
+                    }
+
                     /*
                      * We have finished parsing the response and the
                      * remaining control frames.  If there are unsent
@@ -2052,6 +2078,17 @@
                     return NGX_ERROR;
                 }
 
+                if (ctx->length != -1) {
+                    if ((off_t) ctx->rest > ctx->length) {
+                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                                      "upstream sent response body larger "
+                                      "than indicated content length");
+                        return NGX_ERROR;
+                    }
+
+                    ctx->length -= ctx->rest;
+                }
+
                 if (ctx->rest > ctx->recv_window) {
                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                                   "upstream violated stream flow control, "
diff --git a/src/http/modules/ngx_http_memcached_module.c b/src/http/modules/ngx_http_memcached_module.c
index 775bd7e..c82df6e 100644
--- a/src/http/modules/ngx_http_memcached_module.c
+++ b/src/http/modules/ngx_http_memcached_module.c
@@ -485,10 +485,11 @@
 
     if (u->length == (ssize_t) ctx->rest) {
 
-        if (ngx_strncmp(b->last,
+        if (bytes > u->length
+            || ngx_strncmp(b->last,
                    ngx_http_memcached_end + NGX_HTTP_MEMCACHED_END - ctx->rest,
                    bytes)
-            != 0)
+               != 0)
         {
             ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
                           "memcached sent invalid trailer");
@@ -540,7 +541,9 @@
 
     last += (size_t) (u->length - NGX_HTTP_MEMCACHED_END);
 
-    if (ngx_strncmp(last, ngx_http_memcached_end, b->last - last) != 0) {
+    if (bytes > u->length
+        || ngx_strncmp(last, ngx_http_memcached_end, b->last - last) != 0)
+    {
         ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
                       "memcached sent invalid trailer");
 
diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c
index 3aa3c7f..68ad6d3 100644
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -2022,6 +2022,25 @@
         return NGX_OK;
     }
 
+    if (p->upstream_done) {
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
+                       "http proxy data after close");
+        return NGX_OK;
+    }
+
+    if (p->length == 0) {
+
+        ngx_log_error(NGX_LOG_WARN, p->log, 0,
+                      "upstream sent more data than specified in "
+                      "\"Content-Length\" header");
+
+        r = p->input_ctx;
+        r->upstream->keepalive = 0;
+        p->upstream_done = 1;
+
+        return NGX_OK;
+    }
+
     cl = ngx_chain_get_free_buf(p->pool, &p->free);
     if (cl == NULL) {
         return NGX_ERROR;
@@ -2049,20 +2068,23 @@
         return NGX_OK;
     }
 
+    if (b->last - b->pos > p->length) {
+
+        ngx_log_error(NGX_LOG_WARN, p->log, 0,
+                      "upstream sent more data than specified in "
+                      "\"Content-Length\" header");
+
+        b->last = b->pos + p->length;
+        p->upstream_done = 1;
+
+        return NGX_OK;
+    }
+
     p->length -= b->last - b->pos;
 
     if (p->length == 0) {
         r = p->input_ctx;
-        p->upstream_done = 1;
         r->upstream->keepalive = !r->upstream->headers_in.connection_close;
-
-    } else if (p->length < 0) {
-        r = p->input_ctx;
-        p->upstream_done = 1;
-
-        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
-                      "upstream sent more data than specified in "
-                      "\"Content-Length\" header");
     }
 
     return NGX_OK;
@@ -2089,6 +2111,23 @@
         return NGX_ERROR;
     }
 
+    if (p->upstream_done) {
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
+                       "http proxy data after close");
+        return NGX_OK;
+    }
+
+    if (p->length == 0) {
+
+        ngx_log_error(NGX_LOG_WARN, p->log, 0,
+                      "upstream sent data after final chunk");
+
+        r->upstream->keepalive = 0;
+        p->upstream_done = 1;
+
+        return NGX_OK;
+    }
+
     b = NULL;
     prev = &buf->shadow;
 
@@ -2151,9 +2190,15 @@
 
             /* a whole response has been parsed successfully */
 
-            p->upstream_done = 1;
+            p->length = 0;
             r->upstream->keepalive = !r->upstream->headers_in.connection_close;
 
+            if (buf->pos != buf->last) {
+                ngx_log_error(NGX_LOG_WARN, p->log, 0,
+                              "upstream sent data after final chunk");
+                r->upstream->keepalive = 0;
+            }
+
             break;
         }
 
@@ -2168,13 +2213,13 @@
 
         /* invalid response */
 
-        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+        ngx_log_error(NGX_LOG_ERR, p->log, 0,
                       "upstream sent invalid chunked response");
 
         return NGX_ERROR;
     }
 
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, p->log, 0,
                    "http proxy chunked state %ui, length %O",
                    ctx->chunked.state, p->length);
 
@@ -2234,6 +2279,18 @@
         return NGX_OK;
     }
 
+    if (bytes > u->length) {
+
+        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
+                      "upstream sent more data than specified in "
+                      "\"Content-Length\" header");
+
+        cl->buf->last = cl->buf->pos + u->length;
+        u->length = 0;
+
+        return NGX_OK;
+    }
+
     u->length -= bytes;
 
     if (u->length == 0) {
@@ -2320,6 +2377,12 @@
             u->keepalive = !u->headers_in.connection_close;
             u->length = 0;
 
+            if (buf->pos != buf->last) {
+                ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
+                              "upstream sent data after final chunk");
+                u->keepalive = 0;
+            }
+
             break;
         }
 
diff --git a/src/http/modules/ngx_http_scgi_module.c b/src/http/modules/ngx_http_scgi_module.c
index 7216f78..600999c 100644
--- a/src/http/modules/ngx_http_scgi_module.c
+++ b/src/http/modules/ngx_http_scgi_module.c
@@ -49,6 +49,7 @@
 static ngx_int_t ngx_http_scgi_reinit_request(ngx_http_request_t *r);
 static ngx_int_t ngx_http_scgi_process_status_line(ngx_http_request_t *r);
 static ngx_int_t ngx_http_scgi_process_header(ngx_http_request_t *r);
+static ngx_int_t ngx_http_scgi_input_filter_init(void *data);
 static void ngx_http_scgi_abort_request(ngx_http_request_t *r);
 static void ngx_http_scgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc);
 
@@ -534,6 +535,10 @@
     u->pipe->input_filter = ngx_event_pipe_copy_input_filter;
     u->pipe->input_ctx = r;
 
+    u->input_filter_init = ngx_http_scgi_input_filter_init;
+    u->input_filter = ngx_http_upstream_non_buffered_filter;
+    u->input_filter_ctx = r;
+
     if (!scf->upstream.request_buffering
         && scf->upstream.pass_request_body
         && !r->headers_in.chunked)
@@ -1145,6 +1150,37 @@
 }
 
 
+static ngx_int_t
+ngx_http_scgi_input_filter_init(void *data)
+{
+    ngx_http_request_t   *r = data;
+    ngx_http_upstream_t  *u;
+
+    u = r->upstream;
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http scgi filter init s:%ui l:%O",
+                   u->headers_in.status_n, u->headers_in.content_length_n);
+
+    if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT
+        || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED)
+    {
+        u->pipe->length = 0;
+        u->length = 0;
+
+    } else if (r->method == NGX_HTTP_HEAD) {
+        u->pipe->length = -1;
+        u->length = -1;
+
+    } else {
+        u->pipe->length = u->headers_in.content_length_n;
+        u->length = u->headers_in.content_length_n;
+    }
+
+    return NGX_OK;
+}
+
+
 static void
 ngx_http_scgi_abort_request(ngx_http_request_t *r)
 {
diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c
index 56dc236..fe15ee8 100644
--- a/src/http/modules/ngx_http_uwsgi_module.c
+++ b/src/http/modules/ngx_http_uwsgi_module.c
@@ -67,6 +67,7 @@
 static ngx_int_t ngx_http_uwsgi_reinit_request(ngx_http_request_t *r);
 static ngx_int_t ngx_http_uwsgi_process_status_line(ngx_http_request_t *r);
 static ngx_int_t ngx_http_uwsgi_process_header(ngx_http_request_t *r);
+static ngx_int_t ngx_http_uwsgi_input_filter_init(void *data);
 static void ngx_http_uwsgi_abort_request(ngx_http_request_t *r);
 static void ngx_http_uwsgi_finalize_request(ngx_http_request_t *r,
     ngx_int_t rc);
@@ -703,6 +704,10 @@
     u->pipe->input_filter = ngx_event_pipe_copy_input_filter;
     u->pipe->input_ctx = r;
 
+    u->input_filter_init = ngx_http_uwsgi_input_filter_init;
+    u->input_filter = ngx_http_upstream_non_buffered_filter;
+    u->input_filter_ctx = r;
+
     if (!uwcf->upstream.request_buffering
         && uwcf->upstream.pass_request_body
         && !r->headers_in.chunked)
@@ -1141,6 +1146,7 @@
         r->upstream->request_bufs = cl;
     }
 
+    b->flush = 1;
     cl->next = NULL;
 
     return NGX_OK;
@@ -1355,6 +1361,37 @@
 }
 
 
+static ngx_int_t
+ngx_http_uwsgi_input_filter_init(void *data)
+{
+    ngx_http_request_t   *r = data;
+    ngx_http_upstream_t  *u;
+
+    u = r->upstream;
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http uwsgi filter init s:%ui l:%O",
+                   u->headers_in.status_n, u->headers_in.content_length_n);
+
+    if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT
+        || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED)
+    {
+        u->pipe->length = 0;
+        u->length = 0;
+
+    } else if (r->method == NGX_HTTP_HEAD) {
+        u->pipe->length = -1;
+        u->length = -1;
+
+    } else {
+        u->pipe->length = u->headers_in.content_length_n;
+        u->length = u->headers_in.content_length_n;
+    }
+
+    return NGX_OK;
+}
+
+
 static void
 ngx_http_uwsgi_abort_request(ngx_http_request_t *r)
 {
diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c
index 79ef9c6..a35e9bb 100644
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -1469,14 +1469,14 @@
                                   NGX_HASH_WILDCARD_KEY);
 
             if (rc == NGX_ERROR) {
-                return NGX_ERROR;
+                goto failed;
             }
 
             if (rc == NGX_DECLINED) {
                 ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                               "invalid server name or wildcard \"%V\" on %V",
                               &name[n].name, &addr->opt.addr_text);
-                return NGX_ERROR;
+                goto failed;
             }
 
             if (rc == NGX_BUSY) {
diff --git a/src/http/ngx_http_cache.h b/src/http/ngx_http_cache.h
index f9e9664..cd0b4bb 100644
--- a/src/http/ngx_http_cache.h
+++ b/src/http/ngx_http_cache.h
@@ -160,6 +160,7 @@
 
     ngx_path_t                      *path;
 
+    off_t                            min_free;
     off_t                            max_size;
     size_t                           bsize;
 
diff --git a/src/http/ngx_http_file_cache.c b/src/http/ngx_http_file_cache.c
index ecdf11e..e985f27 100644
--- a/src/http/ngx_http_file_cache.c
+++ b/src/http/ngx_http_file_cache.c
@@ -1959,7 +1959,7 @@
 {
     ngx_http_file_cache_t  *cache = data;
 
-    off_t       size;
+    off_t       size, free;
     time_t      wait;
     ngx_msec_t  elapsed, next;
     ngx_uint_t  count, watermark;
@@ -1988,7 +1988,19 @@
                        size, count, (ngx_int_t) watermark);
 
         if (size < cache->max_size && count < watermark) {
-            break;
+
+            if (!cache->min_free) {
+                break;
+            }
+
+            free = ngx_fs_available(cache->path->name.data);
+
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+                           "http file cache free: %O", free);
+
+            if (free > cache->min_free) {
+                break;
+            }
         }
 
         wait = ngx_http_file_cache_forced_expire(cache);
@@ -2304,7 +2316,7 @@
 {
     char  *confp = conf;
 
-    off_t                   max_size;
+    off_t                   max_size, min_free;
     u_char                 *last, *p;
     time_t                  inactive;
     ssize_t                 size;
@@ -2341,6 +2353,7 @@
     name.len = 0;
     size = 0;
     max_size = NGX_MAX_OFF_T_VALUE;
+    min_free = 0;
 
     value = cf->args->elts;
 
@@ -2476,6 +2489,29 @@
             continue;
         }
 
+        if (ngx_strncmp(value[i].data, "min_free=", 9) == 0) {
+
+#if (NGX_WIN32 || NGX_HAVE_STATFS || NGX_HAVE_STATVFS)
+
+            s.len = value[i].len - 9;
+            s.data = value[i].data + 9;
+
+            min_free = ngx_parse_offset(&s);
+            if (min_free < 0) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "invalid min_free value \"%V\"", &value[i]);
+                return NGX_CONF_ERROR;
+            }
+
+#else
+            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                               "min_free is not supported "
+                               "on this platform, ignored");
+#endif
+
+            continue;
+        }
+
         if (ngx_strncmp(value[i].data, "loader_files=", 13) == 0) {
 
             loader_files = ngx_atoi(value[i].data + 13, value[i].len - 13);
@@ -2607,6 +2643,7 @@
 
     cache->inactive = inactive;
     cache->max_size = max_size;
+    cache->min_free = min_free;
 
     caches = (ngx_array_t *) (confp + cmd->offset);
 
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index d754019..a7b8339 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -77,9 +77,6 @@
 static void
     ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,
     ngx_uint_t do_write);
-static ngx_int_t ngx_http_upstream_non_buffered_filter_init(void *data);
-static ngx_int_t ngx_http_upstream_non_buffered_filter(void *data,
-    ssize_t bytes);
 #if (NGX_THREADS)
 static ngx_int_t ngx_http_upstream_thread_handler(ngx_thread_task_t *task,
     ngx_file_t *file);
@@ -1919,6 +1916,7 @@
 
     u->keepalive = 0;
     u->upgrade = 0;
+    u->error = 0;
 
     ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));
     u->headers_in.content_length_n = -1;
@@ -3631,7 +3629,7 @@
                     return;
                 }
 
-                if (upstream->read->error) {
+                if (upstream->read->error || u->error) {
                     ngx_http_upstream_finalize_request(r, u,
                                                        NGX_HTTP_BAD_GATEWAY);
                     return;
@@ -3709,14 +3707,14 @@
 }
 
 
-static ngx_int_t
+ngx_int_t
 ngx_http_upstream_non_buffered_filter_init(void *data)
 {
     return NGX_OK;
 }
 
 
-static ngx_int_t
+ngx_int_t
 ngx_http_upstream_non_buffered_filter(void *data, ssize_t bytes)
 {
     ngx_http_request_t  *r = data;
@@ -3752,6 +3750,18 @@
         return NGX_OK;
     }
 
+    if (bytes > u->length) {
+
+        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
+                      "upstream sent more data than specified in "
+                      "\"Content-Length\" header");
+
+        cl->buf->last = cl->buf->pos + u->length;
+        u->length = 0;
+
+        return NGX_OK;
+    }
+
     u->length -= bytes;
 
     return NGX_OK;
diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h
index 6a3b302..a568899 100644
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -392,6 +392,7 @@
     unsigned                         buffering:1;
     unsigned                         keepalive:1;
     unsigned                         upgrade:1;
+    unsigned                         error:1;
 
     unsigned                         request_sent:1;
     unsigned                         request_body_sent:1;
@@ -415,6 +416,8 @@
 
 ngx_int_t ngx_http_upstream_create(ngx_http_request_t *r);
 void ngx_http_upstream_init(ngx_http_request_t *r);
+ngx_int_t ngx_http_upstream_non_buffered_filter_init(void *data);
+ngx_int_t ngx_http_upstream_non_buffered_filter(void *data, ssize_t bytes);
 ngx_http_upstream_srv_conf_t *ngx_http_upstream_add(ngx_conf_t *cf,
     ngx_url_t *u, ngx_uint_t flags);
 char *ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c
index c2f278a..c013756 100644
--- a/src/http/v2/ngx_http_v2.c
+++ b/src/http/v2/ngx_http_v2.c
@@ -61,6 +61,8 @@
 static void ngx_http_v2_read_handler(ngx_event_t *rev);
 static void ngx_http_v2_write_handler(ngx_event_t *wev);
 static void ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c);
+static void ngx_http_v2_lingering_close(ngx_http_v2_connection_t *h2c);
+static void ngx_http_v2_lingering_close_handler(ngx_event_t *rev);
 
 static u_char *ngx_http_v2_state_proxy_protocol(ngx_http_v2_connection_t *h2c,
     u_char *pos, u_char *end);
@@ -662,7 +664,7 @@
     }
 
     if (h2c->goaway) {
-        ngx_http_close_connection(c);
+        ngx_http_v2_lingering_close(h2c);
         return;
     }
 
@@ -700,6 +702,113 @@
 }
 
 
+static void
+ngx_http_v2_lingering_close(ngx_http_v2_connection_t *h2c)
+{
+    ngx_event_t               *rev, *wev;
+    ngx_connection_t          *c;
+    ngx_http_core_loc_conf_t  *clcf;
+
+    c = h2c->connection;
+
+    clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx,
+                                        ngx_http_core_module);
+
+    if (clcf->lingering_close == NGX_HTTP_LINGERING_OFF) {
+        ngx_http_close_connection(c);
+        return;
+    }
+
+    rev = c->read;
+    rev->handler = ngx_http_v2_lingering_close_handler;
+
+    h2c->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000);
+    ngx_add_timer(rev, clcf->lingering_timeout);
+
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+        ngx_http_close_connection(c);
+        return;
+    }
+
+    wev = c->write;
+    wev->handler = ngx_http_empty_handler;
+
+    if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {
+        if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) {
+            ngx_http_close_connection(c);
+            return;
+        }
+    }
+
+    if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) {
+        ngx_connection_error(c, ngx_socket_errno,
+                             ngx_shutdown_socket_n " failed");
+        ngx_http_close_connection(c);
+        return;
+    }
+
+    if (rev->ready) {
+        ngx_http_v2_lingering_close_handler(rev);
+    }
+}
+
+
+static void
+ngx_http_v2_lingering_close_handler(ngx_event_t *rev)
+{
+    ssize_t                    n;
+    ngx_msec_t                 timer;
+    ngx_connection_t          *c;
+    ngx_http_core_loc_conf_t  *clcf;
+    ngx_http_v2_connection_t  *h2c;
+    u_char                     buffer[NGX_HTTP_LINGERING_BUFFER_SIZE];
+
+    c = rev->data;
+    h2c = c->data;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http2 lingering close handler");
+
+    if (rev->timedout) {
+        ngx_http_close_connection(c);
+        return;
+    }
+
+    timer = (ngx_msec_t) h2c->lingering_time - (ngx_msec_t) ngx_time();
+    if ((ngx_msec_int_t) timer <= 0) {
+        ngx_http_close_connection(c);
+        return;
+    }
+
+    do {
+        n = c->recv(c, buffer, NGX_HTTP_LINGERING_BUFFER_SIZE);
+
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "lingering read: %z", n);
+
+        if (n == NGX_ERROR || n == 0) {
+            ngx_http_close_connection(c);
+            return;
+        }
+
+    } while (rev->ready);
+
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+        ngx_http_close_connection(c);
+        return;
+    }
+
+    clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx,
+                                        ngx_http_core_module);
+    timer *= 1000;
+
+    if (timer > clcf->lingering_timeout) {
+        timer = clcf->lingering_timeout;
+    }
+
+    ngx_add_timer(rev, timer);
+}
+
+
 static u_char *
 ngx_http_v2_state_proxy_protocol(ngx_http_v2_connection_t *h2c, u_char *pos,
     u_char *end)
@@ -4590,16 +4699,15 @@
     h2c->blocked = 1;
 
     if (!c->error && !h2c->goaway) {
+        h2c->goaway = 1;
+
         if (ngx_http_v2_send_goaway(h2c, status) != NGX_ERROR) {
             (void) ngx_http_v2_send_output_queue(h2c);
         }
     }
 
-    c->error = 1;
-
     if (!h2c->processing && !h2c->pushing) {
-        ngx_http_close_connection(c);
-        return;
+        goto done;
     }
 
     c->read->handler = ngx_http_empty_handler;
@@ -4647,10 +4755,18 @@
     h2c->blocked = 0;
 
     if (h2c->processing || h2c->pushing) {
+        c->error = 1;
         return;
     }
 
-    ngx_http_close_connection(c);
+done:
+
+    if (c->error) {
+        ngx_http_close_connection(c);
+        return;
+    }
+
+    ngx_http_v2_lingering_close(h2c);
 }
 
 
diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h
index 59ddf54..3492297 100644
--- a/src/http/v2/ngx_http_v2.h
+++ b/src/http/v2/ngx_http_v2.h
@@ -157,6 +157,8 @@
     ngx_uint_t                       last_sid;
     ngx_uint_t                       last_push;
 
+    time_t                           lingering_time;
+
     unsigned                         closed_nodes:8;
     unsigned                         settings_ack:1;
     unsigned                         table_update:1;
diff --git a/src/os/unix/ngx_files.c b/src/os/unix/ngx_files.c
index 482d327..1c82a8e 100644
--- a/src/os/unix/ngx_files.c
+++ b/src/os/unix/ngx_files.c
@@ -875,9 +875,28 @@
         return 512;
     }
 
+#if (NGX_LINUX)
+    if ((size_t) fs.f_bsize > ngx_pagesize) {
+        return 512;
+    }
+#endif
+
     return (size_t) fs.f_bsize;
 }
 
+
+off_t
+ngx_fs_available(u_char *name)
+{
+    struct statfs  fs;
+
+    if (statfs((char *) name, &fs) == -1) {
+        return NGX_MAX_OFF_T_VALUE;
+    }
+
+    return (off_t) fs.f_bavail * fs.f_bsize;
+}
+
 #elif (NGX_HAVE_STATVFS)
 
 size_t
@@ -893,9 +912,28 @@
         return 512;
     }
 
+#if (NGX_LINUX)
+    if ((size_t) fs.f_frsize > ngx_pagesize) {
+        return 512;
+    }
+#endif
+
     return (size_t) fs.f_frsize;
 }
 
+
+off_t
+ngx_fs_available(u_char *name)
+{
+    struct statvfs  fs;
+
+    if (statvfs((char *) name, &fs) == -1) {
+        return NGX_MAX_OFF_T_VALUE;
+    }
+
+    return (off_t) fs.f_bavail * fs.f_frsize;
+}
+
 #else
 
 size_t
@@ -904,4 +942,11 @@
     return 512;
 }
 
+
+off_t
+ngx_fs_available(u_char *name)
+{
+    return NGX_MAX_OFF_T_VALUE;
+}
+
 #endif
diff --git a/src/os/unix/ngx_files.h b/src/os/unix/ngx_files.h
index 383e38e..d084713 100644
--- a/src/os/unix/ngx_files.h
+++ b/src/os/unix/ngx_files.h
@@ -185,7 +185,10 @@
 #define ngx_is_exec(sb)          (((sb)->st_mode & S_IXUSR) == S_IXUSR)
 #define ngx_file_access(sb)      ((sb)->st_mode & 0777)
 #define ngx_file_size(sb)        (sb)->st_size
-#define ngx_file_fs_size(sb)     ngx_max((sb)->st_size, (sb)->st_blocks * 512)
+#define ngx_file_fs_size(sb)                                                 \
+    (((sb)->st_blocks * 512 > (sb)->st_size                                  \
+     && (sb)->st_blocks * 512 < (sb)->st_size + 8 * (sb)->st_blksize)        \
+     ? (sb)->st_blocks * 512 : (sb)->st_size)
 #define ngx_file_mtime(sb)       (sb)->st_mtime
 #define ngx_file_uniq(sb)        (sb)->st_ino
 
@@ -346,6 +349,7 @@
 #endif
 
 size_t ngx_fs_bsize(u_char *name);
+off_t ngx_fs_available(u_char *name);
 
 
 #if (NGX_HAVE_OPENAT)
diff --git a/src/os/unix/ngx_process_cycle.c b/src/os/unix/ngx_process_cycle.c
index 5817a2c..f87e009 100644
--- a/src/os/unix/ngx_process_cycle.c
+++ b/src/os/unix/ngx_process_cycle.c
@@ -77,12 +77,11 @@
     u_char            *p;
     size_t             size;
     ngx_int_t          i;
-    ngx_uint_t         n, sigio;
+    ngx_uint_t         sigio;
     sigset_t           set;
     struct itimerval   itv;
     ngx_uint_t         live;
     ngx_msec_t         delay;
-    ngx_listening_t   *ls;
     ngx_core_conf_t   *ccf;
 
     sigemptyset(&set);
@@ -204,16 +203,7 @@
         if (ngx_quit) {
             ngx_signal_worker_processes(cycle,
                                         ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
-
-            ls = cycle->listening.elts;
-            for (n = 0; n < cycle->listening.nelts; n++) {
-                if (ngx_close_socket(ls[n].fd) == -1) {
-                    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
-                                  ngx_close_socket_n " %V failed",
-                                  &ls[n].addr_text);
-                }
-            }
-            cycle->listening.nelts = 0;
+            ngx_close_listening_sockets(cycle);
 
             continue;
         }
diff --git a/src/os/unix/ngx_udp_sendmsg_chain.c b/src/os/unix/ngx_udp_sendmsg_chain.c
index 5399c79..3d1d6dd 100644
--- a/src/os/unix/ngx_udp_sendmsg_chain.c
+++ b/src/os/unix/ngx_udp_sendmsg_chain.c
@@ -189,6 +189,13 @@
         return cl;
     }
 
+    /* zero-sized datagram; pretend to have at least 1 iov */
+    if (n == 0) {
+        iov = &vec->iovs[n++];
+        iov->iov_base = NULL;
+        iov->iov_len = 0;
+    }
+
     vec->count = n;
     vec->size = total;
 
diff --git a/src/os/win32/ngx_files.c b/src/os/win32/ngx_files.c
index 0b131b5..3017b45 100644
--- a/src/os/win32/ngx_files.c
+++ b/src/os/win32/ngx_files.c
@@ -658,6 +658,19 @@
 }
 
 
+off_t
+ngx_fs_available(u_char *name)
+{
+    ULARGE_INTEGER  navail;
+
+    if (GetDiskFreeSpaceEx((const char *) name, &navail, NULL, NULL) == 0) {
+        return NGX_MAX_OFF_T_VALUE;
+    }
+
+    return (off_t) navail.QuadPart;
+}
+
+
 static ngx_int_t
 ngx_win32_check_filename(u_char *name, u_short *u, size_t len)
 {
diff --git a/src/os/win32/ngx_files.h b/src/os/win32/ngx_files.h
index 6eb720e..a10839b 100644
--- a/src/os/win32/ngx_files.h
+++ b/src/os/win32/ngx_files.h
@@ -259,6 +259,7 @@
 #define ngx_directio_off_n          "ngx_directio_off_n"
 
 size_t ngx_fs_bsize(u_char *name);
+off_t ngx_fs_available(u_char *name);
 
 
 #define ngx_stdout               GetStdHandle(STD_OUTPUT_HANDLE)
diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c
index 7484a72..db11dd8 100644
--- a/src/stream/ngx_stream_proxy_module.c
+++ b/src/stream/ngx_stream_proxy_module.c
@@ -839,7 +839,7 @@
         u->upstream_buf.last = p;
     }
 
-    if (c->buffer && c->buffer->pos < c->buffer->last) {
+    if (c->buffer && c->buffer->pos <= c->buffer->last) {
         ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
                        "stream proxy add preread buffer: %uz",
                        c->buffer->last - c->buffer->pos);
@@ -853,6 +853,7 @@
         *cl->buf = *c->buffer;
 
         cl->buf->tag = (ngx_buf_tag_t) &ngx_stream_proxy_module;
+        cl->buf->temporary = (cl->buf->pos == cl->buf->last) ? 0 : 1;
         cl->buf->flush = 1;
 
         cl->next = u->upstream_out;
diff --git a/src/stream/ngx_stream_write_filter_module.c b/src/stream/ngx_stream_write_filter_module.c
index 24326c6..156a61c 100644
--- a/src/stream/ngx_stream_write_filter_module.c
+++ b/src/stream/ngx_stream_write_filter_module.c
@@ -234,7 +234,8 @@
 
     if (size == 0
         && !(c->buffered & NGX_LOWLEVEL_BUFFERED)
-        && !(last && c->need_last_buf))
+        && !(last && c->need_last_buf)
+        && !(c->type == SOCK_DGRAM && flush))
     {
         if (last || flush || sync) {
             for (cl = *out; cl; /* void */) {