diff --git a/.hgtags b/.hgtags
index cf161eecc7d9a681809798398cb3197f7169c088..da0a246d2b4ba5cc3e73786d64e7556f81c9ab99 100644
--- a/.hgtags
+++ b/.hgtags
@@ -450,3 +450,7 @@ fdacd273711ddf20f778c1fb91529ab53979a454 release-1.17.8
 5e8d52bca714d4b85284ddb649d1ba4a3ca978a8 release-1.17.9
 c44970de01474f6f3e01b0adea85ec1d03e3a5f2 release-1.17.10
 cbe6ba650211541310618849168631ce0b788f35 release-1.19.0
+062920e2f3bf871ef7a3d8496edec1b3065faf80 release-1.19.1
+a7b46539f507e6c64efa0efda69ad60b6f4ffbce release-1.19.2
+3cbc2602325f0ac08917a4397d76f5155c34b7b1 release-1.19.3
+dc0cc425fa63a80315f6efb68697cadb6626cdf2 release-1.19.4
diff --git a/auto/modules b/auto/modules
index d78e2823ae8176184cb463c6b6ee7790530409f1..f1c63f3d5ad98e6c2531e44795493a92f06d02e8 100644
--- a/auto/modules
+++ b/auto/modules
@@ -1119,6 +1119,16 @@ if [ $STREAM != NO ]; then
         . auto/module
     fi
 
+    if [ $STREAM_SET = YES ]; then
+        ngx_module_name=ngx_stream_set_module
+        ngx_module_deps=
+        ngx_module_srcs=src/stream/ngx_stream_set_module.c
+        ngx_module_libs=
+        ngx_module_link=$STREAM_SET
+
+        . auto/module
+    fi
+
     if [ $STREAM_UPSTREAM_HASH = YES ]; then
         ngx_module_name=ngx_stream_upstream_hash_module
         ngx_module_deps=
diff --git a/auto/options b/auto/options
index 521c9768d8b37e2e219773da2ec98c04f1dad862..0b21def27268ab04466e37818dfd04bccfc435d0 100644
--- a/auto/options
+++ b/auto/options
@@ -124,6 +124,7 @@ STREAM_GEOIP=NO
 STREAM_MAP=YES
 STREAM_SPLIT_CLIENTS=YES
 STREAM_RETURN=YES
+STREAM_SET=YES
 STREAM_UPSTREAM_HASH=YES
 STREAM_UPSTREAM_LEAST_CONN=YES
 STREAM_UPSTREAM_RANDOM=YES
@@ -324,6 +325,7 @@ use the \"--with-mail_ssl_module\" option instead"
         --without-stream_split_clients_module)
                                          STREAM_SPLIT_CLIENTS=NO    ;;
         --without-stream_return_module)  STREAM_RETURN=NO           ;;
+        --without-stream_set_module)     STREAM_SET=NO              ;;
         --without-stream_upstream_hash_module)
                                          STREAM_UPSTREAM_HASH=NO    ;;
         --without-stream_upstream_least_conn_module)
@@ -538,6 +540,7 @@ cat << END
   --without-stream_split_clients_module
                                      disable ngx_stream_split_clients_module
   --without-stream_return_module     disable ngx_stream_return_module
+  --without-stream_set_module        disable ngx_stream_set_module
   --without-stream_upstream_hash_module
                                      disable ngx_stream_upstream_hash_module
   --without-stream_upstream_least_conn_module
diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml
index 423ccace8c204684a7a9e0dd8324ab27bed1888c..a7417f59c4a36e6313fd07e22f3b34afda7af786 100644
--- a/docs/xml/nginx/changes.xml
+++ b/docs/xml/nginx/changes.xml
@@ -5,6 +5,372 @@
 <change_log title="nginx">
 
 
+<changes ver="1.19.4" date="2020-10-27">
+
+<change type="feature">
+<para lang="ru">
+Š´ŠøŃ€ŠµŠŗŃ‚ŠøŠ²Ń‹ ssl_conf_command, proxy_ssl_conf_command, grpc_ssl_conf_command
+Šø uwsgi_ssl_conf_command.
+</para>
+<para lang="en">
+the "ssl_conf_command", "proxy_ssl_conf_command", "grpc_ssl_conf_command",
+and "uwsgi_ssl_conf_command" directives.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+Š´ŠøŃ€ŠµŠŗŃ‚ŠøŠ²Š° ssl_reject_handshake.
+</para>
+<para lang="en">
+the "ssl_reject_handshake" directive.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+Š´ŠøŃ€ŠµŠŗŃ‚ŠøŠ²Š° proxy_smtp_auth Š² ŠæŠ¾Ń‡Ń‚Š¾Š²Š¾Š¼ ŠæŃ€Š¾ŠŗсŠø-сŠµŃ€Š²ŠµŃ€Šµ.
+</para>
+<para lang="en">
+the "proxy_smtp_auth" directive in mail proxy.
+</para>
+</change>
+
+</changes>
+
+
+<changes ver="1.19.3" date="2020-09-29">
+
+<change type="feature">
+<para lang="ru">
+Š¼Š¾Š´ŃƒŠ»ŃŒ ngx_stream_set_module.
+</para>
+<para lang="en">
+the ngx_stream_set_module.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+Š´ŠøŃ€ŠµŠŗŃ‚ŠøŠ²Š° proxy_cookie_flags.
+</para>
+<para lang="en">
+the "proxy_cookie_flags" directive.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+Š´ŠøŃ€ŠµŠŗŃ‚ŠøŠ²Š° userid_flags.
+</para>
+<para lang="en">
+the "userid_flags" directive.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+Ń€Š°ŃŃˆŠøŃ€ŠµŠ½ŠøŠµ уŠæŃ€Š°Š²Š»ŠµŠ½ŠøѸ ŠŗѨшŠøŃ€Š¾Š²Š°Š½ŠøŠµŠ¼ stale-if-error
+Š¾ŃˆŠøŠ±Š¾Ń‡Š½Š¾ ŠæŃ€ŠøŠ¼ŠµŠ½Ń¸Š»Š¾ŃŃŒ, ŠµŃŠ»Šø Š±Ń¨ŠŗŠµŠ½Š´ Š²Š¾Š·Š²Ń€Š°Ń‰Š°Š» Š¾Ń‚Š²ŠµŃ‚
+с ŠŗŠ¾Š´Š¾Š¼ 500, 502, 503, 504, 403, 404 ŠøŠ»Šø 429.
+</para>
+<para lang="en">
+the "stale-if-error" cache control extension
+was erroneously applied if backend returned a response
+with status code 500, 502, 503, 504, 403, 404, or 429.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+ŠµŃŠ»Šø ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š»Š¾ŃŃŒ ŠŗѨшŠøŃ€Š¾Š²Š°Š½ŠøŠµ
+Šø Š±Ń¨ŠŗŠµŠ½Š´ Š²Š¾Š·Š²Ń€Š°Ń‰Š°Š» Š¾Ń‚Š²ŠµŃ‚Ń‹ с стрŠ¾ŠŗŠ¾Š¹ Š·Š°Š³Š¾Š»Š¾Š²ŠŗŠ° Vary,
+Š² Š»Š¾Š³Š°Ń… Š¼Š¾Š³Š»Šø ŠæŠ¾Ń¸Š²Š»Ń¸Ń‚ьсѸ сŠ¾Š¾Š±Ń‰ŠµŠ½ŠøѸ "[crit] cache file ... has too long header".
+</para>
+<para lang="en">
+"[crit] cache file ... has too long header" messages might appear in logs
+if caching was used
+and the backend returned responses with the "Vary" header line.
+</para>
+</change>
+
+<change type="workaround">
+<para lang="ru">
+ŠæŃ€Šø ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½ŠøŠø OpenSSL 1.1.1
+Š² Š»Š¾Š³Š°Ń… Š¼Š¾Š³Š»Šø ŠæŠ¾Ń¸Š²Š»Ń¸Ń‚ьсѸ сŠ¾Š¾Š±Ń‰ŠµŠ½ŠøѸ "[crit] SSL_write() failed".
+</para>
+<para lang="en">
+"[crit] SSL_write() failed" messages might appear in logs
+when using OpenSSL 1.1.1.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+Š² Š»Š¾Š³Š°Ń… Š¼Š¾Š³Š»Šø ŠæŠ¾Ń¸Š²Š»Ń¸Ń‚ьсѸ сŠ¾Š¾Š±Ń‰ŠµŠ½ŠøѸ
+"SSL_shutdown() failed (SSL: ... bad write retry)";
+Š¾ŃˆŠøŠ±ŠŗŠ° ŠæŠ¾Ń¸Š²ŠøŠ»Š°ŃŃŒ Š² 1.19.2.
+</para>
+<para lang="en">
+"SSL_shutdown() failed (SSL: ... bad write retry)"
+messages might appear in logs;
+the bug had appeared in 1.19.2.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+ŠæŃ€Šø ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½ŠøŠø HTTP/2
+Š² Ń€Š°Š±Š¾Ń‡ŠµŠ¼ ŠæŃ€Š¾Ń†ŠµŃŃŠµ Š¼Š¾Š³ ŠæŃ€Š¾ŠøŠ·Š¾Š¹Ń‚Šø segmentation fault,
+ŠµŃŠ»Šø Š¾ŃˆŠøŠ±ŠŗŠø с ŠŗŠ¾Š´Š¾Š¼ 400 с ŠæŠ¾Š¼Š¾Ń‰ŃŒŃˇ Š´ŠøŃ€ŠµŠŗŃ‚ŠøŠ²Ń‹ error_page
+ŠæŠµŃ€ŠµŠ½Š°ŠæŃ€Š°Š²Š»Ń¸Š»Šøсь Š² ŠæŃ€Š¾ŠŗсŠøруŠµŠ¼Ń‹Š¹ location.
+</para>
+<para lang="en">
+a segmentation fault might occur in a worker process
+when using HTTP/2
+if errors with code 400 were redirected to a proxied location
+using the "error_page" directive.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+утŠµŃ‡ŠŗŠø сŠ¾ŠŗŠµŃ‚Š¾Š² ŠæŃ€Šø ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½ŠøŠø HTTP/2 Šø ŠæŠ¾Š´Š·Š°ŠæŃ€Š¾ŃŠ¾Š² Š² Š¼Š¾Š´ŃƒŠ»Šµ njs.
+</para>
+<para lang="en">
+socket leak when using HTTP/2 and subrequests in the njs module.
+</para>
+</change>
+
+</changes>
+
+
+<changes ver="1.19.2" date="2020-08-11">
+
+<change type="change">
+<para lang="ru">
+Ń‚ŠµŠæŠµŃ€ŃŒ nginx Š½Š°Ń‡ŠøŠ½Š°ŠµŃ‚ Š·Š°ŠŗрыŠ²Š°Ń‚ь keepalive-сŠ¾ŠµŠ´ŠøŠ½ŠµŠ½ŠøѸ,
+Š½Šµ Š´Š¾Š¶ŠøŠ´Š°Ń¸ŃŃŒ ŠøсчŠµŃ€ŠæŠ°Š½ŠøѸ Š²ŃŠµŃ… сŠ²Š¾Š±Š¾Š´Š½Ń‹Ń… сŠ¾ŠµŠ´ŠøŠ½ŠµŠ½ŠøŠ¹,
+Š° Ń‚Š°ŠŗŠ¶Šµ ŠæŠøшŠµŃ‚ Š¾Š± ѨтŠ¾Š¼ ŠæŃ€ŠµŠ´ŃƒŠæŃ€ŠµŠ¶Š´ŠµŠ½ŠøŠµ Š² Š»Š¾Š³ Š¾ŃˆŠøŠ±Š¾Šŗ.
+</para>
+<para lang="en">
+now nginx starts closing keepalive connections
+before all free worker connections are exhausted,
+and logs a warning about this to the error log.
+</para>
+</change>
+
+<change type="change">
+<para lang="ru">
+Š¾ŠæŃ‚ŠøŠ¼ŠøŠ·Š°Ń†ŠøѸ чтŠµŠ½ŠøѸ Ń‚ŠµŠ»Š° Š·Š°ŠæŃ€Š¾ŃŠ°
+ŠæŃ€Šø ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½ŠøŠø chunked transfer encoding.
+</para>
+<para lang="en">
+optimization of client request body reading
+when using chunked transfer encoding.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+утŠµŃ‡ŠŗŠø ŠæŠ°Š¼Ń¸Ń‚Šø ŠæŃ€Šø ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½ŠøŠø Š´ŠøŃ€ŠµŠŗŃ‚ŠøŠ²Ń‹ ssl_ocsp.
+</para>
+<para lang="en">
+memory leak if the "ssl_ocsp" directive was used.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+Š² Š»Š¾Š³Š°Ń… Š¼Š¾Š³Š»Šø ŠæŠ¾Ń¸Š²Š»Ń¸Ń‚ьсѸ сŠ¾Š¾Š±Ń‰ŠµŠ½ŠøѸ "zero size buf in output",
+ŠµŃŠ»Šø FastCGI-сŠµŃ€Š²ŠµŃ€ Š²Š¾Š·Š²Ń€Š°Ń‰Š°Š» Š½ŠµŠŗŠ¾Ń€Ń€ŠµŠŗŃ‚Š½Ń‹Š¹ Š¾Ń‚Š²ŠµŃ‚;
+Š¾ŃˆŠøŠ±ŠŗŠ° ŠæŠ¾Ń¸Š²ŠøŠ»Š°ŃŃŒ Š² 1.19.1.
+</para>
+<para lang="en">
+"zero size buf in output" alerts might appear in logs
+if a FastCGI server returned an incorrect response;
+the bug had appeared in 1.19.1.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+Š² Ń€Š°Š±Š¾Ń‡ŠµŠ¼ ŠæŃ€Š¾Ń†ŠµŃŃŠµ Š¼Š¾Š³ ŠæŃ€Š¾ŠøŠ·Š¾Š¹Ń‚Šø segmentation fault,
+ŠµŃŠ»Šø Ń€Š°Š·Š¼ŠµŃ€Ń‹ large_client_header_buffers Š¾Ń‚Š»ŠøчŠ°Š»Šøсь
+Š² Ń€Š°Š·Š½Ń‹Ń… Š²ŠøртуŠ°Š»ŃŒŠ½Ń‹Ń… сŠµŃ€Š²ŠµŃ€Š°Ń….
+</para>
+<para lang="en">
+a segmentation fault might occur in a worker process
+if different large_client_header_buffers sizes were used
+in different virtual servers.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+SSL shutdown Š¼Š¾Š³ Š½Šµ Ń€Š°Š±Š¾Ń‚Š°Ń‚ь.
+</para>
+<para lang="en">
+SSL shutdown might not work.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+Š² Š»Š¾Š³Š°Ń… Š¼Š¾Š³Š»Šø ŠæŠ¾Ń¸Š²Š»Ń¸Ń‚ьсѸ сŠ¾Š¾Š±Ń‰ŠµŠ½ŠøѸ
+"SSL_shutdown() failed (SSL: ... bad write retry)".
+</para>
+<para lang="en">
+"SSL_shutdown() failed (SSL: ... bad write retry)"
+messages might appear in logs.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+Š² Š¼Š¾Š´ŃƒŠ»Šµ ngx_http_slice_module.
+</para>
+<para lang="en">
+in the ngx_http_slice_module.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+Š² Š¼Š¾Š´ŃƒŠ»Šµ ngx_http_xslt_filter_module.
+</para>
+<para lang="en">
+in the ngx_http_xslt_filter_module.
+</para>
+</change>
+
+</changes>
+
+
+<changes ver="1.19.1" date="2020-07-07">
+
+<change type="change">
+<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/misc/GNUmakefile b/misc/GNUmakefile
index 4582e44606173a34e5bf884f8ab43bec6d4450f5..80eb9b929a6418b0a27f734119d08fedab8ce678 100644
--- a/misc/GNUmakefile
+++ b/misc/GNUmakefile
@@ -6,7 +6,7 @@ TEMP =		tmp
 
 CC =		cl
 OBJS =		objs.msvc8
-OPENSSL =	openssl-1.1.1g
+OPENSSL =	openssl-1.1.1h
 ZLIB =		zlib-1.2.11
 PCRE =		pcre-8.44
 
diff --git a/src/core/nginx.h b/src/core/nginx.h
index 49d35c2b98e32d47ecd3546c55933ad8896cbab3..fe064bb4f706fd36378f0f5cbc9ee82698969ca8 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -9,8 +9,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version      1019001
-#define NGINX_VERSION      "1.19.1"
+#define nginx_version      1019005
+#define NGINX_VERSION      "1.19.5"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #ifdef NGX_BUILD
diff --git a/src/core/ngx_buf.h b/src/core/ngx_buf.h
index 12781a7821988331e7093633273bb3814e606850..4b665629c7f3ce9c911147e76d38e8e90861dded 100644
--- a/src/core/ngx_buf.h
+++ b/src/core/ngx_buf.h
@@ -125,20 +125,20 @@ typedef struct {
 #define NGX_CHAIN_ERROR     (ngx_chain_t *) NGX_ERROR
 
 
-#define ngx_buf_in_memory(b)        (b->temporary || b->memory || b->mmap)
-#define ngx_buf_in_memory_only(b)   (ngx_buf_in_memory(b) && !b->in_file)
+#define ngx_buf_in_memory(b)       ((b)->temporary || (b)->memory || (b)->mmap)
+#define ngx_buf_in_memory_only(b)  (ngx_buf_in_memory(b) && !(b)->in_file)
 
 #define ngx_buf_special(b)                                                   \
-    ((b->flush || b->last_buf || b->sync)                                    \
-     && !ngx_buf_in_memory(b) && !b->in_file)
+    (((b)->flush || (b)->last_buf || (b)->sync)                              \
+     && !ngx_buf_in_memory(b) && !(b)->in_file)
 
 #define ngx_buf_sync_only(b)                                                 \
-    (b->sync                                                                 \
-     && !ngx_buf_in_memory(b) && !b->in_file && !b->flush && !b->last_buf)
+    ((b)->sync && !ngx_buf_in_memory(b)                                      \
+     && !(b)->in_file && !(b)->flush && !(b)->last_buf)
 
 #define ngx_buf_size(b)                                                      \
-    (ngx_buf_in_memory(b) ? (off_t) (b->last - b->pos):                      \
-                            (b->file_last - b->file_pos))
+    (ngx_buf_in_memory(b) ? (off_t) ((b)->last - (b)->pos):                  \
+                            ((b)->file_last - (b)->file_pos))
 
 ngx_buf_t *ngx_create_temp_buf(ngx_pool_t *pool, size_t size);
 ngx_chain_t *ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs);
@@ -149,8 +149,8 @@ ngx_chain_t *ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs);
 
 ngx_chain_t *ngx_alloc_chain_link(ngx_pool_t *pool);
 #define ngx_free_chain(pool, cl)                                             \
-    cl->next = pool->chain;                                                  \
-    pool->chain = cl
+    (cl)->next = (pool)->chain;                                              \
+    (pool)->chain = (cl)
 
 
 
diff --git a/src/core/ngx_conf_file.c b/src/core/ngx_conf_file.c
index 6d1629e9b41d9c86770c12e0927a16183a86f0b6..fec7bb83ab259091ec870f05323aa5d72e093efe 100644
--- a/src/core/ngx_conf_file.c
+++ b/src/core/ngx_conf_file.c
@@ -1137,7 +1137,7 @@ ngx_conf_set_keyval_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 
     a = (ngx_array_t **) (p + cmd->offset);
 
-    if (*a == NULL) {
+    if (*a == NGX_CONF_UNSET_PTR || *a == NULL) {
         *a = ngx_array_create(cf->pool, 4, sizeof(ngx_keyval_t));
         if (*a == NULL) {
             return NGX_CONF_ERROR;
diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c
index 88fefcea269fdfc50c0ac5a2ea625a8c703651b8..c082d0dac08acce4cd79eb950275d2a23697c753 100644
--- a/src/core/ngx_connection.c
+++ b/src/core/ngx_connection.c
@@ -1107,12 +1107,9 @@ ngx_get_connection(ngx_socket_t s, ngx_log_t *log)
         return NULL;
     }
 
-    c = ngx_cycle->free_connections;
+    ngx_drain_connections((ngx_cycle_t *) ngx_cycle);
 
-    if (c == NULL) {
-        ngx_drain_connections((ngx_cycle_t *) ngx_cycle);
-        c = ngx_cycle->free_connections;
-    }
+    c = ngx_cycle->free_connections;
 
     if (c == NULL) {
         ngx_log_error(NGX_LOG_ALERT, log, 0,
@@ -1298,6 +1295,21 @@ ngx_drain_connections(ngx_cycle_t *cycle)
     ngx_queue_t       *q;
     ngx_connection_t  *c;
 
+    if (cycle->free_connection_n > cycle->connection_n / 16
+        || cycle->reusable_connections_n == 0)
+    {
+        return;
+    }
+
+    if (cycle->connections_reuse_time != ngx_time()) {
+        cycle->connections_reuse_time = ngx_time();
+
+        ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
+                      "%ui worker_connections are not enough, "
+                      "reusing connections",
+                      cycle->connection_n);
+    }
+
     n = ngx_max(ngx_min(32, cycle->reusable_connections_n / 8), 1);
 
     for (i = 0; i < n; i++) {
diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c
index 764cf46ba6a04931f81b0231e775ecf87117c615..d7479fa418784367018660d53d8474ab075e0fa4 100644
--- a/src/core/ngx_cycle.c
+++ b/src/core/ngx_cycle.c
@@ -1009,6 +1009,7 @@ ngx_int_t
 ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log)
 {
     size_t      len;
+    ngx_int_t   rc;
     ngx_uint_t  create;
     ngx_file_t  file;
     u_char      pid[NGX_INT64_LEN + 2];
@@ -1033,11 +1034,13 @@ ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log)
         return NGX_ERROR;
     }
 
+    rc = NGX_OK;
+
     if (!ngx_test_config) {
         len = ngx_snprintf(pid, NGX_INT64_LEN + 2, "%P%N", ngx_pid) - pid;
 
         if (ngx_write_file(&file, pid, len, 0) == NGX_ERROR) {
-            return NGX_ERROR;
+            rc = NGX_ERROR;
         }
     }
 
@@ -1046,7 +1049,7 @@ ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log)
                       ngx_close_file_n " \"%s\" failed", file.name.data);
     }
 
-    return NGX_OK;
+    return rc;
 }
 
 
diff --git a/src/core/ngx_cycle.h b/src/core/ngx_cycle.h
index 54fa2e6bfcf8cae93602c35c680b3dd36bc14f1b..0f7d9bc741ad4762742f7890be52911026d886db 100644
--- a/src/core/ngx_cycle.h
+++ b/src/core/ngx_cycle.h
@@ -55,6 +55,7 @@ struct ngx_cycle_s {
 
     ngx_queue_t               reusable_connections_queue;
     ngx_uint_t                reusable_connections_n;
+    time_t                    connections_reuse_time;
 
     ngx_array_t               listening;
     ngx_array_t               paths;
diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c
index e51712c0a2830bd910d1ccde54bfcae7e1d95483..5b716ee75c31372e2e40c0480097bc48d811e12b 100644
--- a/src/core/ngx_resolver.c
+++ b/src/core/ngx_resolver.c
@@ -1918,7 +1918,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n,
 
     if (rn == NULL) {
         ngx_log_error(r->log_level, r->log, 0,
-                      "unexpected response for %V", &name);
+                      "unexpected DNS response for %V", &name);
         ngx_resolver_free(r, name.data);
         goto failed;
     }
@@ -1930,7 +1930,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n,
 
         if (rn->query6 == NULL || rn->naddrs6 != (u_short) -1) {
             ngx_log_error(r->log_level, r->log, 0,
-                          "unexpected response for %V", &name);
+                          "unexpected DNS response for %V", &name);
             ngx_resolver_free(r, name.data);
             goto failed;
         }
@@ -1949,7 +1949,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n,
 
         if (rn->query == NULL || rn->naddrs != (u_short) -1) {
             ngx_log_error(r->log_level, r->log, 0,
-                          "unexpected response for %V", &name);
+                          "unexpected DNS response for %V", &name);
             ngx_resolver_free(r, name.data);
             goto failed;
         }
@@ -1964,7 +1964,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n,
 
     if (ident != qident) {
         ngx_log_error(r->log_level, r->log, 0,
-                      "wrong ident %ui response for %V, expect %ui",
+                      "wrong ident %ui in DNS response for %V, expect %ui",
                       ident, &name, qident);
         ngx_resolver_free(r, name.data);
         goto failed;
@@ -2149,7 +2149,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n,
 
         if (class != 1) {
             ngx_log_error(r->log_level, r->log, 0,
-                          "unexpected RR class %ui", class);
+                          "unexpected RR class %ui in DNS response", class);
             goto failed;
         }
 
@@ -2218,7 +2218,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n,
         default:
 
             ngx_log_error(r->log_level, r->log, 0,
-                          "unexpected RR type %ui", type);
+                          "unexpected RR type %ui in DNS response", type);
         }
 
         i += len;
@@ -2567,7 +2567,7 @@ ngx_resolver_process_srv(ngx_resolver_t *r, u_char *buf, size_t n,
 
     if (rn == NULL || rn->query == NULL) {
         ngx_log_error(r->log_level, r->log, 0,
-                      "unexpected response for %V", &name);
+                      "unexpected DNS response for %V", &name);
         ngx_resolver_free(r, name.data);
         goto failed;
     }
@@ -2581,7 +2581,7 @@ ngx_resolver_process_srv(ngx_resolver_t *r, u_char *buf, size_t n,
 
     if (ident != qident) {
         ngx_log_error(r->log_level, r->log, 0,
-                      "wrong ident %ui response for %V, expect %ui",
+                      "wrong ident %ui in DNS response for %V, expect %ui",
                       ident, &name, qident);
         ngx_resolver_free(r, name.data);
         goto failed;
@@ -2691,7 +2691,7 @@ ngx_resolver_process_srv(ngx_resolver_t *r, u_char *buf, size_t n,
 
         if (class != 1) {
             ngx_log_error(r->log_level, r->log, 0,
-                          "unexpected RR class %ui", class);
+                          "unexpected RR class %ui in DNS response", class);
             goto failed;
         }
 
@@ -2734,7 +2734,7 @@ ngx_resolver_process_srv(ngx_resolver_t *r, u_char *buf, size_t n,
         default:
 
             ngx_log_error(r->log_level, r->log, 0,
-                          "unexpected RR type %ui", type);
+                          "unexpected RR type %ui in DNS response", type);
         }
 
         i += len;
@@ -3165,7 +3165,7 @@ valid:
 
     if (rn == NULL || rn->query == NULL) {
         ngx_log_error(r->log_level, r->log, 0,
-                      "unexpected response for %V", &name);
+                      "unexpected DNS response for %V", &name);
         ngx_resolver_free(r, name.data);
         goto failed;
     }
@@ -3174,7 +3174,7 @@ valid:
 
     if (ident != qident) {
         ngx_log_error(r->log_level, r->log, 0,
-                      "wrong ident %ui response for %V, expect %ui",
+                      "wrong ident %ui in DNS response for %V, expect %ui",
                       ident, &name, qident);
         ngx_resolver_free(r, name.data);
         goto failed;
@@ -3256,7 +3256,7 @@ valid:
 
         if (class != 1) {
             ngx_log_error(r->log_level, r->log, 0,
-                          "unexpected RR class %ui", class);
+                          "unexpected RR class %ui in DNS response", class);
             goto failed;
         }
 
@@ -3283,7 +3283,7 @@ valid:
         default:
 
             ngx_log_error(r->log_level, r->log, 0,
-                          "unexpected RR type %ui", type);
+                          "unexpected RR type %ui in DNS response", type);
         }
 
         i += len;
@@ -3952,12 +3952,12 @@ ngx_resolver_copy(ngx_resolver_t *r, ngx_str_t *name, u_char *buf, u_char *src,
         }
 
         if (p >= last) {
-            err = "name is out of response";
+            err = "name is out of DNS response";
             goto invalid;
         }
     }
 
-    err = "compression pointers loop";
+    err = "compression pointers loop in DNS response";
 
 invalid:
 
diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c
index 468e9600c752fa6f2797ce579a9a6664f3befc46..93f32ea0c7191d4266a3f2e5da0f0de9637f9dc8 100644
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -11,6 +11,8 @@
 
 static u_char *ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64,
     u_char zero, ngx_uint_t hexadecimal, ngx_uint_t width);
+static u_char *ngx_sprintf_str(u_char *buf, u_char *last, u_char *src,
+    size_t len, ngx_uint_t hexadecimal);
 static void ngx_encode_base64_internal(ngx_str_t *dst, ngx_str_t *src,
     const u_char *basis, ngx_uint_t padding);
 static ngx_int_t ngx_decode_base64_internal(ngx_str_t *dst, ngx_str_t *src,
@@ -101,10 +103,10 @@ ngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src)
  *    %M                        ngx_msec_t
  *    %r                        rlim_t
  *    %p                        void *
- *    %V                        ngx_str_t *
- *    %v                        ngx_variable_value_t *
- *    %s                        null-terminated string
- *    %*s                       length and string
+ *    %[x|X]V                   ngx_str_t *
+ *    %[x|X]v                   ngx_variable_value_t *
+ *    %[x|X]s                   null-terminated string
+ *    %*[x|X]s                  length and string
  *    %Z                        '\0'
  *    %N                        '\n'
  *    %c                        char
@@ -165,7 +167,7 @@ ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args)
     u_char                *p, zero;
     int                    d;
     double                 f;
-    size_t                 len, slen;
+    size_t                 slen;
     int64_t                i64;
     uint64_t               ui64, frac;
     ngx_msec_t             ms;
@@ -250,8 +252,7 @@ ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args)
             case 'V':
                 v = va_arg(args, ngx_str_t *);
 
-                len = ngx_min(((size_t) (last - buf)), v->len);
-                buf = ngx_cpymem(buf, v->data, len);
+                buf = ngx_sprintf_str(buf, last, v->data, v->len, hex);
                 fmt++;
 
                 continue;
@@ -259,8 +260,7 @@ ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args)
             case 'v':
                 vv = va_arg(args, ngx_variable_value_t *);
 
-                len = ngx_min(((size_t) (last - buf)), vv->len);
-                buf = ngx_cpymem(buf, vv->data, len);
+                buf = ngx_sprintf_str(buf, last, vv->data, vv->len, hex);
                 fmt++;
 
                 continue;
@@ -268,16 +268,7 @@ ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args)
             case 's':
                 p = va_arg(args, u_char *);
 
-                if (slen == (size_t) -1) {
-                    while (*p && buf < last) {
-                        *buf++ = *p++;
-                    }
-
-                } else {
-                    len = ngx_min(((size_t) (last - buf)), slen);
-                    buf = ngx_cpymem(buf, p, len);
-                }
-
+                buf = ngx_sprintf_str(buf, last, p, slen, hex);
                 fmt++;
 
                 continue;
@@ -576,6 +567,64 @@ ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, u_char zero,
 }
 
 
+static u_char *
+ngx_sprintf_str(u_char *buf, u_char *last, u_char *src, size_t len,
+    ngx_uint_t hexadecimal)
+{
+    static u_char   hex[] = "0123456789abcdef";
+    static u_char   HEX[] = "0123456789ABCDEF";
+
+    if (hexadecimal == 0) {
+
+        if (len == (size_t) -1) {
+            while (*src && buf < last) {
+                *buf++ = *src++;
+            }
+
+        } else {
+            len = ngx_min((size_t) (last - buf), len);
+            buf = ngx_cpymem(buf, src, len);
+        }
+
+    } else if (hexadecimal == 1) {
+
+        if (len == (size_t) -1) {
+
+            while (*src && buf < last - 1) {
+                *buf++ = hex[*src >> 4];
+                *buf++ = hex[*src++ & 0xf];
+            }
+
+        } else {
+
+            while (len-- && buf < last - 1) {
+                *buf++ = hex[*src >> 4];
+                *buf++ = hex[*src++ & 0xf];
+            }
+        }
+
+    } else { /* hexadecimal == 2 */
+
+        if (len == (size_t) -1) {
+
+            while (*src && buf < last - 1) {
+                *buf++ = HEX[*src >> 4];
+                *buf++ = HEX[*src++ & 0xf];
+            }
+
+        } else {
+
+            while (len-- && buf < last - 1) {
+                *buf++ = HEX[*src >> 4];
+                *buf++ = HEX[*src++ & 0xf];
+            }
+        }
+    }
+
+    return buf;
+}
+
+
 /*
  * We use ngx_strcasecmp()/ngx_strncasecmp() for 7-bit ASCII strings only,
  * and implement our own ngx_strcasecmp()/ngx_strncasecmp()
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index c1d5d6a43b35dc8f9856380b1c2d20b3a05a4fce..fd2b92ffc06dc3dd768c7917da4f08e946386042 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -920,7 +920,8 @@ ngx_int_t
 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_VERIFY_PEER, ngx_ssl_verify_callback);
+    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);
 
@@ -1469,6 +1470,78 @@ ngx_ssl_early_data(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_uint_t enable)
 }
 
 
+ngx_int_t
+ngx_ssl_conf_commands(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *commands)
+{
+    if (commands == NULL) {
+        return NGX_OK;
+    }
+
+#ifdef SSL_CONF_FLAG_FILE
+    {
+    int            type;
+    u_char        *key, *value;
+    ngx_uint_t     i;
+    ngx_keyval_t  *cmd;
+    SSL_CONF_CTX  *cctx;
+
+    cctx = SSL_CONF_CTX_new();
+    if (cctx == NULL) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "SSL_CONF_CTX_new() failed");
+        return NGX_ERROR;
+    }
+
+    SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE);
+    SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SERVER);
+    SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT);
+    SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CERTIFICATE);
+    SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SHOW_ERRORS);
+
+    SSL_CONF_CTX_set_ssl_ctx(cctx, ssl->ctx);
+
+    cmd = commands->elts;
+    for (i = 0; i < commands->nelts; i++) {
+
+        key = cmd[i].key.data;
+        type = SSL_CONF_cmd_value_type(cctx, (char *) key);
+
+        if (type == SSL_CONF_TYPE_FILE || type == SSL_CONF_TYPE_DIR) {
+            if (ngx_conf_full_name(cf->cycle, &cmd[i].value, 1) != NGX_OK) {
+                SSL_CONF_CTX_free(cctx);
+                return NGX_ERROR;
+            }
+        }
+
+        value = cmd[i].value.data;
+
+        if (SSL_CONF_cmd(cctx, (char *) key, (char *) value) <= 0) {
+            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                          "SSL_CONF_cmd(\"%s\", \"%s\") failed", key, value);
+            SSL_CONF_CTX_free(cctx);
+            return NGX_ERROR;
+        }
+    }
+
+    if (SSL_CONF_CTX_finish(cctx) != 1) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "SSL_CONF_finish() failed");
+        SSL_CONF_CTX_free(cctx);
+        return NGX_ERROR;
+    }
+
+    SSL_CONF_CTX_free(cctx);
+
+    return NGX_OK;
+    }
+#else
+    ngx_log_error(NGX_LOG_EMERG, ssl->log, 0,
+                  "SSL_CONF_cmd() is not available on this platform");
+    return NGX_ERROR;
+#endif
+}
+
+
 ngx_int_t
 ngx_ssl_client_session_cache(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_uint_t enable)
 {
@@ -1720,6 +1793,13 @@ ngx_ssl_handshake(ngx_connection_t *c)
         return NGX_ERROR;
     }
 
+    if (c->ssl->handshake_rejected) {
+        ngx_connection_error(c, err, "handshake rejected");
+        ERR_clear_error();
+
+        return NGX_ERROR;
+    }
+
     c->read->error = 1;
 
     ngx_ssl_connection_error(c, sslerr, err, "SSL_do_handshake() failed");
@@ -2572,6 +2652,18 @@ ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size)
 
     sslerr = SSL_get_error(c->ssl->connection, n);
 
+    if (sslerr == SSL_ERROR_ZERO_RETURN) {
+
+        /*
+         * OpenSSL 1.1.1 fails to return SSL_ERROR_SYSCALL if an error
+         * happens during SSL_write() after close_notify alert from the
+         * peer, and returns SSL_ERROR_ZERO_RETURN instead,
+         * https://git.openssl.org/?p=openssl.git;a=commitdiff;h=8051ab2
+         */
+
+        sslerr = SSL_ERROR_SYSCALL;
+    }
+
     err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
 
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);
@@ -2773,8 +2865,9 @@ ngx_ssl_free_buffer(ngx_connection_t *c)
 ngx_int_t
 ngx_ssl_shutdown(ngx_connection_t *c)
 {
-    int        n, sslerr, mode;
-    ngx_err_t  err;
+    int         n, sslerr, mode;
+    ngx_err_t   err;
+    ngx_uint_t  tries;
 
     ngx_ssl_ocsp_cleanup(c);
 
@@ -2791,7 +2884,7 @@ ngx_ssl_shutdown(ngx_connection_t *c)
         return NGX_OK;
     }
 
-    if (c->timedout) {
+    if (c->timedout || c->error || c->buffered) {
         mode = SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN;
         SSL_set_quiet_shutdown(c->ssl->connection, 1);
 
@@ -2815,55 +2908,78 @@ ngx_ssl_shutdown(ngx_connection_t *c)
 
     ngx_ssl_clear_error(c->log);
 
-    n = SSL_shutdown(c->ssl->connection);
+    tries = 2;
+
+    for ( ;; ) {
+
+        /*
+         * For bidirectional shutdown, SSL_shutdown() needs to be called
+         * twice: first call sends the "close notify" alert and returns 0,
+         * second call waits for the peer's "close notify" alert.
+         */
+
+        n = SSL_shutdown(c->ssl->connection);
+
+        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n);
+
+        if (n == 1) {
+            SSL_free(c->ssl->connection);
+            c->ssl = NULL;
 
-    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n);
+            return NGX_OK;
+        }
 
-    sslerr = 0;
+        if (n == 0 && tries-- > 1) {
+            continue;
+        }
 
-    /* before 0.9.8m SSL_shutdown() returned 0 instead of -1 on errors */
+        /* before 0.9.8m SSL_shutdown() returned 0 instead of -1 on errors */
 
-    if (n != 1 && ERR_peek_error()) {
         sslerr = SSL_get_error(c->ssl->connection, n);
 
         ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
                        "SSL_get_error: %d", sslerr);
-    }
 
-    if (n == 1 || sslerr == 0 || sslerr == SSL_ERROR_ZERO_RETURN) {
-        SSL_free(c->ssl->connection);
-        c->ssl = NULL;
+        if (sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE) {
+            c->read->handler = ngx_ssl_shutdown_handler;
+            c->write->handler = ngx_ssl_shutdown_handler;
 
-        return NGX_OK;
-    }
+            if (sslerr == SSL_ERROR_WANT_READ) {
+                c->read->ready = 0;
 
-    if (sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE) {
-        c->read->handler = ngx_ssl_shutdown_handler;
-        c->write->handler = ngx_ssl_shutdown_handler;
+            } else {
+                c->write->ready = 0;
+            }
 
-        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
-            return NGX_ERROR;
-        }
+            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+                return NGX_ERROR;
+            }
 
-        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
-            return NGX_ERROR;
-        }
+            if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+                return NGX_ERROR;
+            }
+
+            ngx_add_timer(c->read, 3000);
 
-        if (sslerr == SSL_ERROR_WANT_READ) {
-            ngx_add_timer(c->read, 30000);
+            return NGX_AGAIN;
         }
 
-        return NGX_AGAIN;
-    }
+        if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {
+            SSL_free(c->ssl->connection);
+            c->ssl = NULL;
 
-    err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
+            return NGX_OK;
+        }
 
-    ngx_ssl_connection_error(c, sslerr, err, "SSL_shutdown() failed");
+        err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
 
-    SSL_free(c->ssl->connection);
-    c->ssl = NULL;
+        ngx_ssl_connection_error(c, sslerr, err, "SSL_shutdown() failed");
 
-    return NGX_ERROR;
+        SSL_free(c->ssl->connection);
+        c->ssl = NULL;
+
+        return NGX_ERROR;
+    }
 }
 
 
@@ -3245,8 +3361,9 @@ ngx_ssl_session_id_context(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
         }
     }
 
-    if (SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index) == NULL) {
-
+    if (SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index) == NULL
+        && certificates != NULL)
+    {
         /*
          * If certificates are loaded dynamically, we use certificate
          * names as specified in the configuration (with variables).
@@ -3940,9 +4057,6 @@ ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
     ngx_ssl_session_ticket_key_t  *key;
     const EVP_MD                  *digest;
     const EVP_CIPHER              *cipher;
-#if (NGX_DEBUG)
-    u_char                         buf[32];
-#endif
 
     c = ngx_ssl_get_connection(ssl_conn);
     ssl_ctx = c->ssl->session_ctx;
@@ -3964,8 +4078,8 @@ ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
         /* encrypt session ticket */
 
         ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                       "ssl session ticket encrypt, key: \"%*s\" (%s session)",
-                       ngx_hex_dump(buf, key[0].name, 16) - buf, buf,
+                       "ssl session ticket encrypt, key: \"%*xs\" (%s session)",
+                       (size_t) 16, key[0].name,
                        SSL_session_reused(ssl_conn) ? "reused" : "new");
 
         if (key[0].size == 48) {
@@ -4011,17 +4125,16 @@ ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
         }
 
         ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                       "ssl session ticket decrypt, key: \"%*s\" not found",
-                       ngx_hex_dump(buf, name, 16) - buf, buf);
+                       "ssl session ticket decrypt, key: \"%*xs\" not found",
+                       (size_t) 16, name);
 
         return 0;
 
     found:
 
         ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                       "ssl session ticket decrypt, key: \"%*s\"%s",
-                       ngx_hex_dump(buf, key[i].name, 16) - buf, buf,
-                       (i == 0) ? " (default)" : "");
+                       "ssl session ticket decrypt, key: \"%*xs\"%s",
+                       (size_t) 16, key[i].name, (i == 0) ? " (default)" : "");
 
         if (key[i].size == 48) {
             cipher = EVP_aes_128_cbc();
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
index 4909f021e40a53ae612b02a5af36f91d099d29b8..329760d0937350b92075499c2fb16d86772c0f39 100644
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -95,6 +95,7 @@ struct ngx_ssl_connection_s {
     u_char                      early_buf;
 
     unsigned                    handshaked:1;
+    unsigned                    handshake_rejected:1;
     unsigned                    renegotiation:1;
     unsigned                    buffer:1;
     unsigned                    no_wait_shutdown:1;
@@ -203,6 +204,9 @@ ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file);
 ngx_int_t ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name);
 ngx_int_t ngx_ssl_early_data(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_uint_t enable);
+ngx_int_t ngx_ssl_conf_commands(ngx_conf_t *cf, ngx_ssl_t *ssl,
+    ngx_array_t *commands);
+
 ngx_int_t ngx_ssl_client_session_cache(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_uint_t enable);
 ngx_int_t ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
@@ -211,6 +215,7 @@ ngx_int_t ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
 ngx_int_t ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_array_t *paths);
 ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data);
+
 ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c,
     ngx_uint_t flags);
 
diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c
index a0a63c16516ff5a18559e0af159452c2e4c998b5..e3fa8c4e2be18f96910b9f5dfdce6ef3a8a70726 100644
--- a/src/event/ngx_event_openssl_stapling.c
+++ b/src/event/ngx_event_openssl_stapling.c
@@ -883,6 +883,7 @@ ngx_ssl_ocsp_validate(ngx_connection_t *c)
 
     ocsp = ngx_pcalloc(c->pool, sizeof(ngx_ssl_ocsp_t));
     if (ocsp == NULL) {
+        X509_free(cert);
         return NGX_ERROR;
     }
 
@@ -899,6 +900,7 @@ ngx_ssl_ocsp_validate(ngx_connection_t *c)
     if (ocsp->certs) {
         ocsp->certs = X509_chain_up_ref(ocsp->certs);
         if (ocsp->certs == NULL) {
+            X509_free(cert);
             return NGX_ERROR;
         }
     }
@@ -910,6 +912,7 @@ ngx_ssl_ocsp_validate(ngx_connection_t *c)
         if (store == NULL) {
             ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
                           "SSL_CTX_get_cert_store() failed");
+            X509_free(cert);
             return NGX_ERROR;
         }
 
@@ -917,6 +920,7 @@ ngx_ssl_ocsp_validate(ngx_connection_t *c)
         if (store_ctx == NULL) {
             ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
                           "X509_STORE_CTX_new() failed");
+            X509_free(cert);
             return NGX_ERROR;
         }
 
@@ -926,6 +930,7 @@ ngx_ssl_ocsp_validate(ngx_connection_t *c)
             ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
                           "X509_STORE_CTX_init() failed");
             X509_STORE_CTX_free(store_ctx);
+            X509_free(cert);
             return NGX_ERROR;
         }
 
@@ -933,6 +938,7 @@ ngx_ssl_ocsp_validate(ngx_connection_t *c)
         if (rc <= 0) {
             ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "X509_verify_cert() failed");
             X509_STORE_CTX_free(store_ctx);
+            X509_free(cert);
             return NGX_ERROR;
         }
 
@@ -941,12 +947,15 @@ ngx_ssl_ocsp_validate(ngx_connection_t *c)
             ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
                           "X509_STORE_CTX_get1_chain() failed");
             X509_STORE_CTX_free(store_ctx);
+            X509_free(cert);
             return NGX_ERROR;
         }
 
         X509_STORE_CTX_free(store_ctx);
     }
 
+    X509_free(cert);
+
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
                    "ssl ocsp validate, certs:%d", sk_X509_num(ocsp->certs));
 
@@ -980,6 +989,7 @@ ngx_ssl_ocsp_validate_next(ngx_connection_t *c)
         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 +998,8 @@ ngx_ssl_ocsp_validate_next(ngx_connection_t *c)
 
         ctx = ngx_ssl_ocsp_start(c->log);
         if (ctx == NULL) {
-            goto failed;
+            rc = NGX_ERROR;
+            goto done;
         }
 
         ocsp->ctx = ctx;
@@ -1012,8 +1023,9 @@ ngx_ssl_ocsp_validate_next(ngx_connection_t *c)
         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 +1037,7 @@ ngx_ssl_ocsp_validate_next(ngx_connection_t *c)
         rc = ngx_ssl_ocsp_cache_lookup(ctx);
 
         if (rc == NGX_ERROR) {
-            goto failed;
+            goto done;
         }
 
         if (rc == NGX_DECLINED) {
@@ -1051,12 +1063,12 @@ ngx_ssl_ocsp_validate_next(ngx_connection_t *c)
 
 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 +1085,16 @@ ngx_ssl_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)
 
     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 +1102,17 @@ ngx_ssl_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)
 
     ngx_ssl_ocsp_validate_next(c);
 
-done:
+    return;
 
-    if (ocsp->status == NGX_AGAIN || !c->ssl->in_ocsp) {
-        return;
-    }
+done:
 
-    c->ssl->handshaked = 1;
+    ocsp->status = rc;
+    ngx_ssl_ocsp_done(ctx);
 
-    c->ssl->handler(c);
+    if (c->ssl->in_ocsp) {
+        c->ssl->handshaked = 1;
+        c->ssl->handler(c);
+    }
 }
 
 
@@ -2654,16 +2662,8 @@ ngx_ssl_ocsp_create_key(ngx_ssl_ocsp_ctx_t *ctx)
     p = ngx_cpymem(p, serial->data, serial->length);
     ngx_memzero(p, 20 - serial->length);
 
-#if (NGX_DEBUG)
-    {
-        u_char  buf[120];
-
-        ngx_hex_dump(buf, ctx->key.data, ctx->key.len);
-
-        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
-                       "ssl ocsp key %*s", sizeof(buf), buf);
-    }
-#endif
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+                   "ssl ocsp key %xV", &ctx->key);
 
     return NGX_OK;
 }
diff --git a/src/event/ngx_event_pipe.c b/src/event/ngx_event_pipe.c
index 531b13aade0349a2752521752f3cfa23ea4c97b7..54412e130f6e7036696c482a57d839171bcda0ab 100644
--- a/src/event/ngx_event_pipe.c
+++ b/src/event/ngx_event_pipe.c
@@ -960,6 +960,22 @@ ngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
         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 @@ ngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
         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 2be0672148e1cdad64121b93ffd91f0dbd7005d4..5191880e3d16567f1f25108d785b50f2395808c8 100644
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -81,12 +81,15 @@ typedef struct {
     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 @@ ngx_http_fastcgi_process_header(ngx_http_request_t *r)
 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 @@ ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
     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 @@ ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
             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_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
                 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;
@@ -2252,6 +2306,18 @@ ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
             break;
         }
 
+        if (f->rest == -2) {
+            f->rest = r->upstream->headers_in.content_length_n;
+        }
+
+        if (f->rest == 0) {
+            ngx_log_error(NGX_LOG_WARN, p->log, 0,
+                          "upstream sent more data than specified in "
+                          "\"Content-Length\" header");
+            p->upstream_done = 1;
+            break;
+        }
+
         cl = ngx_chain_get_free_buf(p->pool, &p->free);
         if (cl == NULL) {
             return NGX_ERROR;
@@ -2289,15 +2355,27 @@ ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
             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 > 0) {
 
-        b->last = f->last;
+            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");
 
-        break;
+                b->last = b->pos + f->rest;
+                p->upstream_done = 1;
 
+                break;
+            }
+
+            f->rest -= b->last - b->pos;
+        }
     }
 
     if (flcf->keep_conn) {
@@ -2391,6 +2469,14 @@ ngx_http_fastcgi_non_buffered_filter(void *data, ssize_t bytes)
 
             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;
@@ -2486,6 +2572,14 @@ ngx_http_fastcgi_non_buffered_filter(void *data, ssize_t bytes)
             break;
         }
 
+        if (f->rest == 0) {
+            ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
+                          "upstream sent more data than specified in "
+                          "\"Content-Length\" header");
+            u->length = 0;
+            break;
+        }
+
         cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);
         if (cl == NULL) {
             return NGX_ERROR;
@@ -2510,13 +2604,27 @@ ngx_http_fastcgi_non_buffered_filter(void *data, ssize_t bytes)
             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) {
+
+            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");
 
-        break;
+                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 992211e73584eafa370588c96af263f217be953d..0b8bb5281dc971cbceca31c478023359ca152fbf 100644
--- a/src/http/modules/ngx_http_grpc_module.c
+++ b/src/http/modules/ngx_http_grpc_module.c
@@ -40,6 +40,7 @@ typedef struct {
     ngx_str_t                  ssl_certificate;
     ngx_str_t                  ssl_certificate_key;
     ngx_array_t               *ssl_passwords;
+    ngx_array_t               *ssl_conf_commands;
 #endif
 } ngx_http_grpc_loc_conf_t;
 
@@ -84,6 +85,8 @@ typedef struct {
     ngx_uint_t                 pings;
     ngx_uint_t                 settings;
 
+    off_t                      length;
+
     ssize_t                    send_window;
     size_t                     recv_window;
 
@@ -206,6 +209,8 @@ static char *ngx_http_grpc_pass(ngx_conf_t *cf, ngx_command_t *cmd,
 #if (NGX_HTTP_SSL)
 static char *ngx_http_grpc_ssl_password_file(ngx_conf_t *cf,
     ngx_command_t *cmd, void *conf);
+static char *ngx_http_grpc_ssl_conf_command_check(ngx_conf_t *cf, void *post,
+    void *data);
 static ngx_int_t ngx_http_grpc_set_ssl(ngx_conf_t *cf,
     ngx_http_grpc_loc_conf_t *glcf);
 #endif
@@ -240,6 +245,9 @@ static ngx_conf_bitmask_t  ngx_http_grpc_ssl_protocols[] = {
     { ngx_null_string, 0 }
 };
 
+static ngx_conf_post_t  ngx_http_grpc_ssl_conf_command_post =
+    { ngx_http_grpc_ssl_conf_command_check };
+
 #endif
 
 
@@ -436,6 +444,13 @@ static ngx_command_t  ngx_http_grpc_commands[] = {
       0,
       NULL },
 
+    { ngx_string("grpc_ssl_conf_command"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
+      ngx_conf_set_keyval_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_grpc_loc_conf_t, ssl_conf_commands),
+      &ngx_http_grpc_ssl_conf_command_post },
+
 #endif
 
       ngx_null_command
@@ -1126,20 +1141,11 @@ ngx_http_grpc_create_request(ngx_http_request_t *r)
 
     f->flags |= NGX_HTTP_V2_END_HEADERS_FLAG;
 
-#if (NGX_DEBUG)
-    if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) {
-        u_char  buf[512];
-        size_t  n, m;
-
-        n = ngx_min(b->last - b->pos, 256);
-        m = ngx_hex_dump(buf, b->pos, n) - buf;
-
-        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "grpc header: %*s%s, len: %uz",
-                       m, buf, b->last - b->pos > 256 ? "..." : "",
-                       b->last - b->pos);
-    }
-#endif
+    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "grpc header: %*xs%s, len: %uz",
+                   (size_t) ngx_min(b->last - b->pos, 256), b->pos,
+                   b->last - b->pos > 256 ? "..." : "",
+                   b->last - b->pos);
 
     if (r->request_body_no_buffering) {
 
@@ -1589,20 +1595,11 @@ ngx_http_grpc_process_header(ngx_http_request_t *r)
     u = r->upstream;
     b = &u->buffer;
 
-#if (NGX_DEBUG)
-    if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) {
-        u_char  buf[512];
-        size_t  n, m;
-
-        n = ngx_min(b->last - b->pos, 256);
-        m = ngx_hex_dump(buf, b->pos, n) - buf;
-
-        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "grpc response: %*s%s, len: %uz",
-                       m, buf, b->last - b->pos > 256 ? "..." : "",
-                       b->last - b->pos);
-    }
-#endif
+    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "grpc response: %*xs%s, len: %uz",
+                   (size_t) ngx_min(b->last - b->pos, 256),
+                   b->pos, b->last - b->pos > 256 ? "..." : "",
+                   b->last - b->pos);
 
     ctx = ngx_http_grpc_get_ctx(r);
 
@@ -1953,10 +1950,28 @@ ngx_http_grpc_filter_init(void *data)
     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 +2014,12 @@ ngx_http_grpc_filter(void *data, ssize_t bytes)
 
                 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 +2073,17 @@ ngx_http_grpc_filter(void *data, ssize_t bytes)
                     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, "
@@ -4287,7 +4319,6 @@ ngx_http_grpc_create_loc_conf(ngx_conf_t *cf)
      *     conf->upstream.hide_headers_hash = { NULL, 0 };
      *     conf->upstream.ssl_name = NULL;
      *
-     *     conf->headers_source = NULL;
      *     conf->headers.lengths = NULL;
      *     conf->headers.values = NULL;
      *     conf->headers.hash = { NULL, 0 };
@@ -4323,6 +4354,7 @@ ngx_http_grpc_create_loc_conf(ngx_conf_t *cf)
     conf->upstream.ssl_verify = NGX_CONF_UNSET;
     conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;
     conf->ssl_passwords = NGX_CONF_UNSET_PTR;
+    conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;
 #endif
 
     /* the hardcoded values */
@@ -4340,6 +4372,8 @@ ngx_http_grpc_create_loc_conf(ngx_conf_t *cf)
     conf->upstream.pass_trailers = 1;
     conf->upstream.preserve_output = 1;
 
+    conf->headers_source = NGX_CONF_UNSET_PTR;
+
     ngx_str_set(&conf->upstream.module, "grpc");
 
     return conf;
@@ -4431,6 +4465,9 @@ ngx_http_grpc_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
                               prev->ssl_certificate_key, "");
     ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL);
 
+    ngx_conf_merge_ptr_value(conf->ssl_conf_commands,
+                              prev->ssl_conf_commands, NULL);
+
     if (conf->ssl && ngx_http_grpc_set_ssl(cf, conf) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
@@ -4470,9 +4507,10 @@ ngx_http_grpc_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
         clcf->handler = ngx_http_grpc_handler;
     }
 
-    if (conf->headers_source == NULL) {
+    ngx_conf_merge_ptr_value(conf->headers_source, prev->headers_source, NULL);
+
+    if (conf->headers_source == prev->headers_source) {
         conf->headers = prev->headers;
-        conf->headers_source = prev->headers_source;
         conf->host_set = prev->host_set;
     }
 
@@ -4797,6 +4835,17 @@ ngx_http_grpc_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 }
 
 
+static char *
+ngx_http_grpc_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data)
+{
+#ifndef SSL_CONF_FLAG_FILE
+    return "is not supported on this platform";
+#endif
+
+    return NGX_CONF_OK;
+}
+
+
 static ngx_int_t
 ngx_http_grpc_set_ssl(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *glcf)
 {
@@ -4887,6 +4936,12 @@ ngx_http_grpc_set_ssl(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *glcf)
 
 #endif
 
+    if (ngx_ssl_conf_commands(cf, glcf->upstream.ssl, glcf->ssl_conf_commands)
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
     return NGX_OK;
 }
 
diff --git a/src/http/modules/ngx_http_limit_req_module.c b/src/http/modules/ngx_http_limit_req_module.c
index 6bd3e6a3bf6749764e9ea1c7e70935d45809039b..dad5edb934c3b21bb21a25bebf657a97195c9b5f 100644
--- a/src/http/modules/ngx_http_limit_req_module.c
+++ b/src/http/modules/ngx_http_limit_req_module.c
@@ -69,6 +69,8 @@ static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit,
     ngx_uint_t hash, ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t account);
 static ngx_msec_t ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits,
     ngx_uint_t n, ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit);
+static void ngx_http_limit_req_unlock(ngx_http_limit_req_limit_t *limits,
+    ngx_uint_t n);
 static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx,
     ngx_uint_t n);
 
@@ -223,6 +225,7 @@ ngx_http_limit_req_handler(ngx_http_request_t *r)
         ctx = limit->shm_zone->data;
 
         if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) {
+            ngx_http_limit_req_unlock(limits, n);
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
 
@@ -270,21 +273,7 @@ ngx_http_limit_req_handler(ngx_http_request_t *r)
                         &limit->shm_zone->shm.name);
         }
 
-        while (n--) {
-            ctx = limits[n].shm_zone->data;
-
-            if (ctx->node == NULL) {
-                continue;
-            }
-
-            ngx_shmtx_lock(&ctx->shpool->mutex);
-
-            ctx->node->count--;
-
-            ngx_shmtx_unlock(&ctx->shpool->mutex);
-
-            ctx->node = NULL;
-        }
+        ngx_http_limit_req_unlock(limits, n);
 
         if (lrcf->dry_run) {
             r->main->limit_req_status = NGX_HTTP_LIMIT_REQ_REJECTED_DRY_RUN;
@@ -612,6 +601,29 @@ ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits, ngx_uint_t n,
 }
 
 
+static void
+ngx_http_limit_req_unlock(ngx_http_limit_req_limit_t *limits, ngx_uint_t n)
+{
+    ngx_http_limit_req_ctx_t  *ctx;
+
+    while (n--) {
+        ctx = limits[n].shm_zone->data;
+
+        if (ctx->node == NULL) {
+            continue;
+        }
+
+        ngx_shmtx_lock(&ctx->shpool->mutex);
+
+        ctx->node->count--;
+
+        ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+        ctx->node = NULL;
+    }
+}
+
+
 static void
 ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)
 {
diff --git a/src/http/modules/ngx_http_memcached_module.c b/src/http/modules/ngx_http_memcached_module.c
index 775bd7e81104ae85f7b5a0db905f7c1ee98ed5a3..c82df6e33738bbd6ac1dd97a8012d195fea7fa30 100644
--- a/src/http/modules/ngx_http_memcached_module.c
+++ b/src/http/modules/ngx_http_memcached_module.c
@@ -485,10 +485,11 @@ ngx_http_memcached_filter(void *data, ssize_t bytes)
 
     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 @@ ngx_http_memcached_filter(void *data, ssize_t bytes)
 
     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 3aafb9996131eaf2f4e45434e94f6e268436ccc9..3d8768af6739855730e4e11f8afd9f8090feea98 100644
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -10,6 +10,19 @@
 #include <ngx_http.h>
 
 
+#define  NGX_HTTP_PROXY_COOKIE_SECURE           0x0001
+#define  NGX_HTTP_PROXY_COOKIE_SECURE_ON        0x0002
+#define  NGX_HTTP_PROXY_COOKIE_SECURE_OFF       0x0004
+#define  NGX_HTTP_PROXY_COOKIE_HTTPONLY         0x0008
+#define  NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON      0x0010
+#define  NGX_HTTP_PROXY_COOKIE_HTTPONLY_OFF     0x0020
+#define  NGX_HTTP_PROXY_COOKIE_SAMESITE         0x0040
+#define  NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT  0x0080
+#define  NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX     0x0100
+#define  NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE    0x0200
+#define  NGX_HTTP_PROXY_COOKIE_SAMESITE_OFF     0x0400
+
+
 typedef struct {
     ngx_array_t                    caches;  /* ngx_http_file_cache_t * */
 } ngx_http_proxy_main_conf_t;
@@ -18,7 +31,7 @@ typedef struct {
 typedef struct ngx_http_proxy_rewrite_s  ngx_http_proxy_rewrite_t;
 
 typedef ngx_int_t (*ngx_http_proxy_rewrite_pt)(ngx_http_request_t *r,
-    ngx_table_elt_t *h, size_t prefix, size_t len,
+    ngx_str_t *value, size_t prefix, size_t len,
     ngx_http_proxy_rewrite_t *pr);
 
 struct ngx_http_proxy_rewrite_s {
@@ -35,6 +48,19 @@ struct ngx_http_proxy_rewrite_s {
 };
 
 
+typedef struct {
+    union {
+        ngx_http_complex_value_t   complex;
+#if (NGX_PCRE)
+        ngx_http_regex_t          *regex;
+#endif
+    } cookie;
+
+    ngx_uint_t                     flags;
+    ngx_uint_t                     regex;
+} ngx_http_proxy_cookie_flags_t;
+
+
 typedef struct {
     ngx_str_t                      key_start;
     ngx_str_t                      schema;
@@ -72,6 +98,7 @@ typedef struct {
     ngx_array_t                   *redirects;
     ngx_array_t                   *cookie_domains;
     ngx_array_t                   *cookie_paths;
+    ngx_array_t                   *cookie_flags;
 
     ngx_http_complex_value_t      *method;
     ngx_str_t                      location;
@@ -100,6 +127,7 @@ typedef struct {
     ngx_str_t                      ssl_certificate;
     ngx_str_t                      ssl_certificate_key;
     ngx_array_t                   *ssl_passwords;
+    ngx_array_t                   *ssl_conf_commands;
 #endif
 } ngx_http_proxy_loc_conf_t;
 
@@ -158,10 +186,16 @@ static ngx_int_t ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r,
     ngx_table_elt_t *h, size_t prefix);
 static ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r,
     ngx_table_elt_t *h);
+static ngx_int_t ngx_http_proxy_parse_cookie(ngx_str_t *value,
+    ngx_array_t *attrs);
 static ngx_int_t ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r,
-    ngx_table_elt_t *h, u_char *value, ngx_array_t *rewrites);
+    ngx_str_t *value, ngx_array_t *rewrites);
+static ngx_int_t ngx_http_proxy_rewrite_cookie_flags(ngx_http_request_t *r,
+    ngx_array_t *attrs, ngx_array_t *flags);
+static ngx_int_t ngx_http_proxy_edit_cookie_flags(ngx_http_request_t *r,
+    ngx_array_t *attrs, ngx_uint_t flags);
 static ngx_int_t ngx_http_proxy_rewrite(ngx_http_request_t *r,
-    ngx_table_elt_t *h, size_t prefix, size_t len, ngx_str_t *replacement);
+    ngx_str_t *value, size_t prefix, size_t len, ngx_str_t *replacement);
 
 static ngx_int_t ngx_http_proxy_add_variables(ngx_conf_t *cf);
 static void *ngx_http_proxy_create_main_conf(ngx_conf_t *cf);
@@ -180,6 +214,8 @@ static char *ngx_http_proxy_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 static char *ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+static char *ngx_http_proxy_cookie_flags(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 static char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 #if (NGX_HTTP_CACHE)
@@ -194,6 +230,10 @@ static char *ngx_http_proxy_ssl_password_file(ngx_conf_t *cf,
 #endif
 
 static char *ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data);
+#if (NGX_HTTP_SSL)
+static char *ngx_http_proxy_ssl_conf_command_check(ngx_conf_t *cf, void *post,
+    void *data);
+#endif
 
 static ngx_int_t ngx_http_proxy_rewrite_regex(ngx_conf_t *cf,
     ngx_http_proxy_rewrite_t *pr, ngx_str_t *regex, ngx_uint_t caseless);
@@ -239,6 +279,9 @@ static ngx_conf_bitmask_t  ngx_http_proxy_ssl_protocols[] = {
     { ngx_null_string, 0 }
 };
 
+static ngx_conf_post_t  ngx_http_proxy_ssl_conf_command_post =
+    { ngx_http_proxy_ssl_conf_command_check };
+
 #endif
 
 
@@ -282,6 +325,13 @@ static ngx_command_t  ngx_http_proxy_commands[] = {
       0,
       NULL },
 
+    { ngx_string("proxy_cookie_flags"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
+      ngx_http_proxy_cookie_flags,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
     { ngx_string("proxy_store"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_http_proxy_store,
@@ -722,6 +772,13 @@ static ngx_command_t  ngx_http_proxy_commands[] = {
       0,
       NULL },
 
+    { ngx_string("proxy_ssl_conf_command"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
+      ngx_conf_set_keyval_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_proxy_loc_conf_t, ssl_conf_commands),
+      &ngx_http_proxy_ssl_conf_command_post },
+
 #endif
 
       ngx_null_command
@@ -845,6 +902,36 @@ static ngx_path_init_t  ngx_http_proxy_temp_path = {
 };
 
 
+static ngx_conf_bitmask_t  ngx_http_proxy_cookie_flags_masks[] = {
+
+    { ngx_string("secure"),
+      NGX_HTTP_PROXY_COOKIE_SECURE|NGX_HTTP_PROXY_COOKIE_SECURE_ON },
+
+    { ngx_string("nosecure"),
+      NGX_HTTP_PROXY_COOKIE_SECURE|NGX_HTTP_PROXY_COOKIE_SECURE_OFF },
+
+    { ngx_string("httponly"),
+      NGX_HTTP_PROXY_COOKIE_HTTPONLY|NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON },
+
+    { ngx_string("nohttponly"),
+      NGX_HTTP_PROXY_COOKIE_HTTPONLY|NGX_HTTP_PROXY_COOKIE_HTTPONLY_OFF },
+
+    { ngx_string("samesite=strict"),
+      NGX_HTTP_PROXY_COOKIE_SAMESITE|NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT },
+
+    { ngx_string("samesite=lax"),
+      NGX_HTTP_PROXY_COOKIE_SAMESITE|NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX },
+
+    { ngx_string("samesite=none"),
+      NGX_HTTP_PROXY_COOKIE_SAMESITE|NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE },
+
+    { ngx_string("nosamesite"),
+      NGX_HTTP_PROXY_COOKIE_SAMESITE|NGX_HTTP_PROXY_COOKIE_SAMESITE_OFF },
+
+    { ngx_null_string, 0 }
+};
+
+
 static ngx_int_t
 ngx_http_proxy_handler(ngx_http_request_t *r)
 {
@@ -906,7 +993,7 @@ ngx_http_proxy_handler(ngx_http_request_t *r)
         u->rewrite_redirect = ngx_http_proxy_rewrite_redirect;
     }
 
-    if (plcf->cookie_domains || plcf->cookie_paths) {
+    if (plcf->cookie_domains || plcf->cookie_paths || plcf->cookie_flags) {
         u->rewrite_cookie = ngx_http_proxy_rewrite_cookie;
     }
 
@@ -2015,6 +2102,25 @@ ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
         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;
@@ -2042,20 +2148,23 @@ ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
         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;
@@ -2082,6 +2191,23 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
         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;
 
@@ -2144,9 +2270,15 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
 
             /* 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;
         }
 
@@ -2161,13 +2293,13 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
 
         /* 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);
 
@@ -2227,6 +2359,18 @@ ngx_http_proxy_non_buffered_copy_filter(void *data, ssize_t bytes)
         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) {
@@ -2313,6 +2457,12 @@ ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
             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;
         }
 
@@ -2521,7 +2671,7 @@ ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h,
     len = h->value.len - prefix;
 
     for (i = 0; i < plcf->redirects->nelts; i++) {
-        rc = pr[i].handler(r, h, prefix, len, &pr[i]);
+        rc = pr[i].handler(r, &h->value, prefix, len, &pr[i]);
 
         if (rc != NGX_DECLINED) {
             return rc;
@@ -2535,27 +2685,43 @@ ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h,
 static ngx_int_t
 ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h)
 {
-    size_t                      prefix;
     u_char                     *p;
+    size_t                      len;
     ngx_int_t                   rc, rv;
+    ngx_str_t                  *key, *value;
+    ngx_uint_t                  i;
+    ngx_array_t                 attrs;
+    ngx_keyval_t               *attr;
     ngx_http_proxy_loc_conf_t  *plcf;
 
-    p = (u_char *) ngx_strchr(h->value.data, ';');
-    if (p == NULL) {
-        return NGX_DECLINED;
+    if (ngx_array_init(&attrs, r->pool, 2, sizeof(ngx_keyval_t)) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    if (ngx_http_proxy_parse_cookie(&h->value, &attrs) != NGX_OK) {
+        return NGX_ERROR;
     }
 
-    prefix = p + 1 - h->value.data;
+    attr = attrs.elts;
+
+    if (attr[0].value.data == NULL) {
+        return NGX_DECLINED;
+    }
 
     rv = NGX_DECLINED;
 
     plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
 
-    if (plcf->cookie_domains) {
-        p = ngx_strcasestrn(h->value.data + prefix, "domain=", 7 - 1);
+    for (i = 1; i < attrs.nelts; i++) {
 
-        if (p) {
-            rc = ngx_http_proxy_rewrite_cookie_value(r, h, p + 7,
+        key = &attr[i].key;
+        value = &attr[i].value;
+
+        if (plcf->cookie_domains && key->len == 6
+            && ngx_strncasecmp(key->data, (u_char *) "domain", 6) == 0
+            && value->data)
+        {
+            rc = ngx_http_proxy_rewrite_cookie_value(r, value,
                                                      plcf->cookie_domains);
             if (rc == NGX_ERROR) {
                 return NGX_ERROR;
@@ -2565,13 +2731,12 @@ ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h)
                 rv = rc;
             }
         }
-    }
-
-    if (plcf->cookie_paths) {
-        p = ngx_strcasestrn(h->value.data + prefix, "path=", 5 - 1);
 
-        if (p) {
-            rc = ngx_http_proxy_rewrite_cookie_value(r, h, p + 5,
+        if (plcf->cookie_paths && key->len == 4
+            && ngx_strncasecmp(key->data, (u_char *) "path", 4) == 0
+            && value->data)
+        {
+            rc = ngx_http_proxy_rewrite_cookie_value(r, value,
                                                      plcf->cookie_paths);
             if (rc == NGX_ERROR) {
                 return NGX_ERROR;
@@ -2583,30 +2748,153 @@ ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h)
         }
     }
 
-    return rv;
+    if (plcf->cookie_flags) {
+        rc = ngx_http_proxy_rewrite_cookie_flags(r, &attrs,
+                                                 plcf->cookie_flags);
+        if (rc == NGX_ERROR) {
+            return NGX_ERROR;
+        }
+
+        if (rc != NGX_DECLINED) {
+            rv = rc;
+        }
+
+        attr = attrs.elts;
+    }
+
+    if (rv != NGX_OK) {
+        return rv;
+    }
+
+    len = 0;
+
+    for (i = 0; i < attrs.nelts; i++) {
+
+        if (attr[i].key.data == NULL) {
+            continue;
+        }
+
+        if (i > 0) {
+            len += 2;
+        }
+
+        len += attr[i].key.len;
+
+        if (attr[i].value.data) {
+            len += 1 + attr[i].value.len;
+        }
+    }
+
+    p = ngx_pnalloc(r->pool, len + 1);
+    if (p == NULL) {
+        return NGX_ERROR;
+    }
+
+    h->value.data = p;
+    h->value.len = len;
+
+    for (i = 0; i < attrs.nelts; i++) {
+
+        if (attr[i].key.data == NULL) {
+            continue;
+        }
+
+        if (i > 0) {
+            *p++ = ';';
+            *p++ = ' ';
+        }
+
+        p = ngx_cpymem(p, attr[i].key.data, attr[i].key.len);
+
+        if (attr[i].value.data) {
+            *p++ = '=';
+            p = ngx_cpymem(p, attr[i].value.data, attr[i].value.len);
+        }
+    }
+
+    *p = '\0';
+
+    return NGX_OK;
 }
 
 
 static ngx_int_t
-ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, ngx_table_elt_t *h,
-    u_char *value, ngx_array_t *rewrites)
+ngx_http_proxy_parse_cookie(ngx_str_t *value, ngx_array_t *attrs)
 {
-    size_t                     len, prefix;
-    u_char                    *p;
-    ngx_int_t                  rc;
-    ngx_uint_t                 i;
-    ngx_http_proxy_rewrite_t  *pr;
+    u_char        *start, *end, *p, *last;
+    ngx_str_t      name, val;
+    ngx_keyval_t  *attr;
+
+    start = value->data;
+    end = value->data + value->len;
+
+    for ( ;; ) {
 
-    prefix = value - h->value.data;
+        last = (u_char *) ngx_strchr(start, ';');
+
+        if (last == NULL) {
+            last = end;
+        }
+
+        while (start < last && *start == ' ') { start++; }
+
+        for (p = start; p < last && *p != '='; p++) { /* void */ }
+
+        name.data = start;
+        name.len = p - start;
+
+        while (name.len && name.data[name.len - 1] == ' ') {
+            name.len--;
+        }
+
+        if (p < last) {
+
+            p++;
+
+            while (p < last && *p == ' ') { p++; }
+
+            val.data = p;
+            val.len = last - val.data;
+
+            while (val.len && val.data[val.len - 1] == ' ') {
+                val.len--;
+            }
+
+        } else {
+            ngx_str_null(&val);
+        }
+
+        attr = ngx_array_push(attrs);
+        if (attr == NULL) {
+            return NGX_ERROR;
+        }
+
+        attr->key = name;
+        attr->value = val;
+
+        if (last == end) {
+            break;
+        }
+
+        start = last + 1;
+    }
+
+    return NGX_OK;
+}
 
-    p = (u_char *) ngx_strchr(value, ';');
 
-    len = p ? (size_t) (p - value) : (h->value.len - prefix);
+static ngx_int_t
+ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, ngx_str_t *value,
+    ngx_array_t *rewrites)
+{
+    ngx_int_t                  rc;
+    ngx_uint_t                 i;
+    ngx_http_proxy_rewrite_t  *pr;
 
     pr = rewrites->elts;
 
     for (i = 0; i < rewrites->nelts; i++) {
-        rc = pr[i].handler(r, h, prefix, len, &pr[i]);
+        rc = pr[i].handler(r, value, 0, value->len, &pr[i]);
 
         if (rc != NGX_DECLINED) {
             return rc;
@@ -2618,8 +2906,194 @@ ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, ngx_table_elt_t *h,
 
 
 static ngx_int_t
-ngx_http_proxy_rewrite_complex_handler(ngx_http_request_t *r,
-    ngx_table_elt_t *h, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)
+ngx_http_proxy_rewrite_cookie_flags(ngx_http_request_t *r, ngx_array_t *attrs,
+    ngx_array_t *flags)
+{
+    ngx_str_t                       pattern;
+#if (NGX_PCRE)
+    ngx_int_t                       rc;
+#endif
+    ngx_uint_t                      i;
+    ngx_keyval_t                   *attr;
+    ngx_http_proxy_cookie_flags_t  *pcf;
+
+    attr = attrs->elts;
+    pcf = flags->elts;
+
+    for (i = 0; i < flags->nelts; i++) {
+
+#if (NGX_PCRE)
+        if (pcf[i].regex) {
+            rc = ngx_http_regex_exec(r, pcf[i].cookie.regex, &attr[0].key);
+
+            if (rc == NGX_ERROR) {
+                return NGX_ERROR;
+            }
+
+            if (rc == NGX_OK) {
+                break;
+            }
+
+            /* NGX_DECLINED */
+
+            continue;
+        }
+#endif
+
+        if (ngx_http_complex_value(r, &pcf[i].cookie.complex, &pattern)
+            != NGX_OK)
+        {
+            return NGX_ERROR;
+        }
+
+        if (pattern.len == attr[0].key.len
+            && ngx_strncasecmp(attr[0].key.data, pattern.data, pattern.len)
+               == 0)
+        {
+            break;
+        }
+    }
+
+    if (i == flags->nelts) {
+        return NGX_DECLINED;
+    }
+
+    return ngx_http_proxy_edit_cookie_flags(r, attrs, pcf[i].flags);
+}
+
+
+static ngx_int_t
+ngx_http_proxy_edit_cookie_flags(ngx_http_request_t *r, ngx_array_t *attrs,
+    ngx_uint_t flags)
+{
+    ngx_str_t     *key, *value;
+    ngx_uint_t     i;
+    ngx_keyval_t  *attr;
+
+    attr = attrs->elts;
+
+    for (i = 1; i < attrs->nelts; i++) {
+        key = &attr[i].key;
+
+        if (key->len == 6
+            && ngx_strncasecmp(key->data, (u_char *) "secure", 6) == 0)
+        {
+            if (flags & NGX_HTTP_PROXY_COOKIE_SECURE_ON) {
+                flags &= ~NGX_HTTP_PROXY_COOKIE_SECURE_ON;
+
+            } else if (flags & NGX_HTTP_PROXY_COOKIE_SECURE_OFF) {
+                key->data = NULL;
+            }
+
+            continue;
+        }
+
+        if (key->len == 8
+            && ngx_strncasecmp(key->data, (u_char *) "httponly", 8) == 0)
+        {
+            if (flags & NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON) {
+                flags &= ~NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON;
+
+            } else if (flags & NGX_HTTP_PROXY_COOKIE_HTTPONLY_OFF) {
+                key->data = NULL;
+            }
+
+            continue;
+        }
+
+        if (key->len == 8
+            && ngx_strncasecmp(key->data, (u_char *) "samesite", 8) == 0)
+        {
+            value = &attr[i].value;
+
+            if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT) {
+                flags &= ~NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT;
+
+                if (value->len != 6
+                    || ngx_strncasecmp(value->data, (u_char *) "strict", 6)
+                       != 0)
+                {
+                    ngx_str_set(key, "SameSite");
+                    ngx_str_set(value, "Strict");
+                }
+
+            } else if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX) {
+                flags &= ~NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX;
+
+                if (value->len != 3
+                    || ngx_strncasecmp(value->data, (u_char *) "lax", 3) != 0)
+                {
+                    ngx_str_set(key, "SameSite");
+                    ngx_str_set(value, "Lax");
+                }
+
+            } else if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE) {
+                flags &= ~NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE;
+
+                if (value->len != 4
+                    || ngx_strncasecmp(value->data, (u_char *) "none", 4) != 0)
+                {
+                    ngx_str_set(key, "SameSite");
+                    ngx_str_set(value, "None");
+                }
+
+            } else if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_OFF) {
+                key->data = NULL;
+            }
+
+            continue;
+        }
+    }
+
+    if (flags & NGX_HTTP_PROXY_COOKIE_SECURE_ON) {
+        attr = ngx_array_push(attrs);
+        if (attr == NULL) {
+            return NGX_ERROR;
+        }
+
+        ngx_str_set(&attr->key, "Secure");
+        ngx_str_null(&attr->value);
+    }
+
+    if (flags & NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON) {
+        attr = ngx_array_push(attrs);
+        if (attr == NULL) {
+            return NGX_ERROR;
+        }
+
+        ngx_str_set(&attr->key, "HttpOnly");
+        ngx_str_null(&attr->value);
+    }
+
+    if (flags & (NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT
+                 |NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX
+                 |NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE))
+    {
+        attr = ngx_array_push(attrs);
+        if (attr == NULL) {
+            return NGX_ERROR;
+        }
+
+        ngx_str_set(&attr->key, "SameSite");
+
+        if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT) {
+            ngx_str_set(&attr->value, "Strict");
+
+        } else if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX) {
+            ngx_str_set(&attr->value, "Lax");
+
+        } else {
+            ngx_str_set(&attr->value, "None");
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_rewrite_complex_handler(ngx_http_request_t *r, ngx_str_t *value,
+    size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)
 {
     ngx_str_t  pattern, replacement;
 
@@ -2628,8 +3102,7 @@ ngx_http_proxy_rewrite_complex_handler(ngx_http_request_t *r,
     }
 
     if (pattern.len > len
-        || ngx_rstrncmp(h->value.data + prefix, pattern.data,
-                        pattern.len) != 0)
+        || ngx_rstrncmp(value->data + prefix, pattern.data, pattern.len) != 0)
     {
         return NGX_DECLINED;
     }
@@ -2638,20 +3111,20 @@ ngx_http_proxy_rewrite_complex_handler(ngx_http_request_t *r,
         return NGX_ERROR;
     }
 
-    return ngx_http_proxy_rewrite(r, h, prefix, pattern.len, &replacement);
+    return ngx_http_proxy_rewrite(r, value, prefix, pattern.len, &replacement);
 }
 
 
 #if (NGX_PCRE)
 
 static ngx_int_t
-ngx_http_proxy_rewrite_regex_handler(ngx_http_request_t *r, ngx_table_elt_t *h,
+ngx_http_proxy_rewrite_regex_handler(ngx_http_request_t *r, ngx_str_t *value,
     size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)
 {
     ngx_str_t  pattern, replacement;
 
     pattern.len = len;
-    pattern.data = h->value.data + prefix;
+    pattern.data = value->data + prefix;
 
     if (ngx_http_regex_exec(r, pr->pattern.regex, &pattern) != NGX_OK) {
         return NGX_DECLINED;
@@ -2661,20 +3134,15 @@ ngx_http_proxy_rewrite_regex_handler(ngx_http_request_t *r, ngx_table_elt_t *h,
         return NGX_ERROR;
     }
 
-    if (prefix == 0 && h->value.len == len) {
-        h->value = replacement;
-        return NGX_OK;
-    }
-
-    return ngx_http_proxy_rewrite(r, h, prefix, len, &replacement);
+    return ngx_http_proxy_rewrite(r, value, prefix, len, &replacement);
 }
 
 #endif
 
 
 static ngx_int_t
-ngx_http_proxy_rewrite_domain_handler(ngx_http_request_t *r,
-    ngx_table_elt_t *h, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)
+ngx_http_proxy_rewrite_domain_handler(ngx_http_request_t *r, ngx_str_t *value,
+    size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)
 {
     u_char     *p;
     ngx_str_t   pattern, replacement;
@@ -2683,9 +3151,9 @@ ngx_http_proxy_rewrite_domain_handler(ngx_http_request_t *r,
         return NGX_ERROR;
     }
 
-    p = h->value.data + prefix;
+    p = value->data + prefix;
 
-    if (p[0] == '.') {
+    if (len && p[0] == '.') {
         p++;
         prefix++;
         len--;
@@ -2699,18 +3167,23 @@ ngx_http_proxy_rewrite_domain_handler(ngx_http_request_t *r,
         return NGX_ERROR;
     }
 
-    return ngx_http_proxy_rewrite(r, h, prefix, len, &replacement);
+    return ngx_http_proxy_rewrite(r, value, prefix, len, &replacement);
 }
 
 
 static ngx_int_t
-ngx_http_proxy_rewrite(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix,
+ngx_http_proxy_rewrite(ngx_http_request_t *r, ngx_str_t *value, size_t prefix,
     size_t len, ngx_str_t *replacement)
 {
     u_char  *p, *data;
     size_t   new_len;
 
-    new_len = replacement->len + h->value.len - len;
+    if (len == value->len) {
+        *value = *replacement;
+        return NGX_OK;
+    }
+
+    new_len = replacement->len + value->len - len;
 
     if (replacement->len > len) {
 
@@ -2719,23 +3192,22 @@ ngx_http_proxy_rewrite(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix,
             return NGX_ERROR;
         }
 
-        p = ngx_copy(data, h->value.data, prefix);
+        p = ngx_copy(data, value->data, prefix);
         p = ngx_copy(p, replacement->data, replacement->len);
 
-        ngx_memcpy(p, h->value.data + prefix + len,
-                   h->value.len - len - prefix + 1);
+        ngx_memcpy(p, value->data + prefix + len,
+                   value->len - len - prefix + 1);
 
-        h->value.data = data;
+        value->data = data;
 
     } else {
-        p = ngx_copy(h->value.data + prefix, replacement->data,
-                     replacement->len);
+        p = ngx_copy(value->data + prefix, replacement->data, replacement->len);
 
-        ngx_memmove(p, h->value.data + prefix + len,
-                    h->value.len - len - prefix + 1);
+        ngx_memmove(p, value->data + prefix + len,
+                    value->len - len - prefix + 1);
     }
 
-    h->value.len = new_len;
+    value->len = new_len;
 
     return NGX_OK;
 }
@@ -2811,7 +3283,6 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
      *     conf->method = NULL;
      *     conf->location = NULL;
      *     conf->url = { 0, NULL };
-     *     conf->headers_source = NULL;
      *     conf->headers.lengths = NULL;
      *     conf->headers.values = NULL;
      *     conf->headers.hash = { NULL, 0 };
@@ -2884,16 +3355,20 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
     conf->upstream.ssl_verify = NGX_CONF_UNSET;
     conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;
     conf->ssl_passwords = NGX_CONF_UNSET_PTR;
+    conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;
 #endif
 
     /* "proxy_cyclic_temp_file" is disabled */
     conf->upstream.cyclic_temp_file = 0;
 
+    conf->headers_source = NGX_CONF_UNSET_PTR;
+
     conf->redirect = NGX_CONF_UNSET;
     conf->upstream.change_buffering = 1;
 
     conf->cookie_domains = NGX_CONF_UNSET_PTR;
     conf->cookie_paths = NGX_CONF_UNSET_PTR;
+    conf->cookie_flags = NGX_CONF_UNSET_PTR;
 
     conf->http_version = NGX_CONF_UNSET_UINT;
 
@@ -3228,6 +3703,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
                               prev->ssl_certificate_key, "");
     ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL);
 
+    ngx_conf_merge_ptr_value(conf->ssl_conf_commands,
+                              prev->ssl_conf_commands, NULL);
+
     if (conf->ssl && ngx_http_proxy_set_ssl(cf, conf) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
@@ -3289,6 +3767,8 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
 
     ngx_conf_merge_ptr_value(conf->cookie_paths, prev->cookie_paths, NULL);
 
+    ngx_conf_merge_ptr_value(conf->cookie_flags, prev->cookie_flags, NULL);
+
     ngx_conf_merge_uint_value(conf->http_version, prev->http_version,
                               NGX_HTTP_VERSION_10);
 
@@ -3359,12 +3839,13 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
         }
     }
 
-    if (conf->headers_source == NULL) {
+    ngx_conf_merge_ptr_value(conf->headers_source, prev->headers_source, NULL);
+
+    if (conf->headers_source == prev->headers_source) {
         conf->headers = prev->headers;
 #if (NGX_HTTP_CACHE)
         conf->headers_cache = prev->headers_cache;
 #endif
-        conf->headers_source = prev->headers_source;
     }
 
     rc = ngx_http_proxy_init_headers(cf, conf, &conf->headers,
@@ -3703,7 +4184,7 @@ ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
     ngx_http_compile_complex_value_t   ccv;
 
     if (plcf->redirect == 0) {
-        return NGX_CONF_OK;
+        return "is duplicate";
     }
 
     plcf->redirect = 1;
@@ -3712,16 +4193,12 @@ ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 
     if (cf->args->nelts == 2) {
         if (ngx_strcmp(value[1].data, "off") == 0) {
-            plcf->redirect = 0;
-            plcf->redirects = NULL;
-            return NGX_CONF_OK;
-        }
 
-        if (ngx_strcmp(value[1].data, "false") == 0) {
-            ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
-                           "invalid parameter \"false\", use \"off\" instead");
+            if (plcf->redirects) {
+                return "is duplicate";
+            }
+
             plcf->redirect = 0;
-            plcf->redirects = NULL;
             return NGX_CONF_OK;
         }
 
@@ -3745,7 +4222,9 @@ ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
         return NGX_CONF_ERROR;
     }
 
-    if (ngx_strcmp(value[1].data, "default") == 0) {
+    if (cf->args->nelts == 2
+        && ngx_strcmp(value[1].data, "default") == 0)
+    {
         if (plcf->proxy_lengths) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "\"proxy_redirect default\" cannot be used "
@@ -3848,7 +4327,7 @@ ngx_http_proxy_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
     ngx_http_compile_complex_value_t   ccv;
 
     if (plcf->cookie_domains == NULL) {
-        return NGX_CONF_OK;
+        return "is duplicate";
     }
 
     value = cf->args->elts;
@@ -3856,6 +4335,11 @@ ngx_http_proxy_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
     if (cf->args->nelts == 2) {
 
         if (ngx_strcmp(value[1].data, "off") == 0) {
+
+            if (plcf->cookie_domains != NGX_CONF_UNSET_PTR) {
+                return "is duplicate";
+            }
+
             plcf->cookie_domains = NULL;
             return NGX_CONF_OK;
         }
@@ -3935,7 +4419,7 @@ ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
     ngx_http_compile_complex_value_t   ccv;
 
     if (plcf->cookie_paths == NULL) {
-        return NGX_CONF_OK;
+        return "is duplicate";
     }
 
     value = cf->args->elts;
@@ -3943,6 +4427,11 @@ ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
     if (cf->args->nelts == 2) {
 
         if (ngx_strcmp(value[1].data, "off") == 0) {
+
+            if (plcf->cookie_paths != NGX_CONF_UNSET_PTR) {
+                return "is duplicate";
+            }
+
             plcf->cookie_paths = NULL;
             return NGX_CONF_OK;
         }
@@ -4012,6 +4501,131 @@ ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 }
 
 
+static char *
+ngx_http_proxy_cookie_flags(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_proxy_loc_conf_t *plcf = conf;
+
+    ngx_str_t                         *value;
+    ngx_uint_t                         i, m;
+    ngx_conf_bitmask_t                *mask;
+    ngx_http_proxy_cookie_flags_t     *pcf;
+    ngx_http_compile_complex_value_t   ccv;
+#if (NGX_PCRE)
+    ngx_regex_compile_t                rc;
+    u_char                             errstr[NGX_MAX_CONF_ERRSTR];
+#endif
+
+    if (plcf->cookie_flags == NULL) {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    if (cf->args->nelts == 2) {
+
+        if (ngx_strcmp(value[1].data, "off") == 0) {
+
+            if (plcf->cookie_flags != NGX_CONF_UNSET_PTR) {
+                return "is duplicate";
+            }
+
+            plcf->cookie_flags = NULL;
+            return NGX_CONF_OK;
+        }
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid parameter \"%V\"", &value[1]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (plcf->cookie_flags == NGX_CONF_UNSET_PTR) {
+        plcf->cookie_flags = ngx_array_create(cf->pool, 1,
+                                        sizeof(ngx_http_proxy_cookie_flags_t));
+        if (plcf->cookie_flags == NULL) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    pcf = ngx_array_push(plcf->cookie_flags);
+    if (pcf == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    pcf->regex = 0;
+
+    if (value[1].data[0] == '~') {
+        value[1].len--;
+        value[1].data++;
+
+#if (NGX_PCRE)
+        ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
+
+        rc.pattern = value[1];
+        rc.err.len = NGX_MAX_CONF_ERRSTR;
+        rc.err.data = errstr;
+        rc.options = NGX_REGEX_CASELESS;
+
+        pcf->cookie.regex = ngx_http_regex_compile(cf, &rc);
+        if (pcf->cookie.regex == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        pcf->regex = 1;
+#else
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "using regex \"%V\" requires PCRE library",
+                           &value[1]);
+        return NGX_CONF_ERROR;
+#endif
+
+    } else {
+
+        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+        ccv.cf = cf;
+        ccv.value = &value[1];
+        ccv.complex_value = &pcf->cookie.complex;
+
+        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    mask = ngx_http_proxy_cookie_flags_masks;
+    pcf->flags = 0;
+
+    for (i = 2; i < cf->args->nelts; i++) {
+        for (m = 0; mask[m].name.len != 0; m++) {
+
+            if (mask[m].name.len != value[i].len
+                || ngx_strcasecmp(mask[m].name.data, value[i].data) != 0)
+            {
+                continue;
+            }
+
+            if (pcf->flags & mask[m].mask) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "duplicate parameter \"%V\"", &value[i]);
+                return NGX_CONF_ERROR;
+            }
+
+            pcf->flags |= mask[m].mask;
+
+            break;
+        }
+
+        if (mask[m].name.len == 0) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "invalid parameter \"%V\"", &value[i]);
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    return NGX_CONF_OK;
+}
+
+
 static ngx_int_t
 ngx_http_proxy_rewrite_regex(ngx_conf_t *cf, ngx_http_proxy_rewrite_t *pr,
     ngx_str_t *regex, ngx_uint_t caseless)
@@ -4250,6 +4864,17 @@ ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data)
 
 #if (NGX_HTTP_SSL)
 
+static char *
+ngx_http_proxy_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data)
+{
+#ifndef SSL_CONF_FLAG_FILE
+    return "is not supported on this platform";
+#endif
+
+    return NGX_CONF_OK;
+}
+
+
 static ngx_int_t
 ngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf)
 {
@@ -4327,6 +4952,12 @@ ngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf)
         return NGX_ERROR;
     }
 
+    if (ngx_ssl_conf_commands(cf, plcf->upstream.ssl, plcf->ssl_conf_commands)
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
     return NGX_OK;
 }
 
diff --git a/src/http/modules/ngx_http_scgi_module.c b/src/http/modules/ngx_http_scgi_module.c
index 7216f781d3fc76204765ed60e128aad7a7f9c32f..600999c88ef0a46decc11777aa20f7e312c4a440 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_create_request(ngx_http_request_t *r);
 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 @@ ngx_http_scgi_handler(ngx_http_request_t *r)
     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 @@ ngx_http_scgi_process_header(ngx_http_request_t *r)
 }
 
 
+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_slice_filter_module.c b/src/http/modules/ngx_http_slice_filter_module.c
index c1edbca2b6bad560b2214511557e35f8ab39281f..186380a2f3cb29e452e52522f62f7a88944ba2fd 100644
--- a/src/http/modules/ngx_http_slice_filter_module.c
+++ b/src/http/modules/ngx_http_slice_filter_module.c
@@ -180,6 +180,11 @@ ngx_http_slice_header_filter(ngx_http_request_t *r)
     r->headers_out.content_range->hash = 0;
     r->headers_out.content_range = NULL;
 
+    if (r->headers_out.accept_ranges) {
+        r->headers_out.accept_ranges->hash = 0;
+        r->headers_out.accept_ranges = NULL;
+    }
+
     r->allow_ranges = 1;
     r->subrequest_ranges = 1;
     r->single_range = 1;
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
index d7072a6266ff003ed64a5bc11afdac0110514ea0..e062b03a116cecfb72f0941c827681d4121ace9f 100644
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -53,6 +53,9 @@ static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
 static char *ngx_http_ssl_ocsp_cache(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 
+static char *ngx_http_ssl_conf_command_check(ngx_conf_t *cf, void *post,
+    void *data);
+
 static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf);
 
 
@@ -89,6 +92,10 @@ static ngx_conf_deprecated_t  ngx_http_ssl_deprecated = {
 };
 
 
+static ngx_conf_post_t  ngx_http_ssl_conf_command_post =
+    { ngx_http_ssl_conf_command_check };
+
+
 static ngx_command_t  ngx_http_ssl_commands[] = {
 
     { ngx_string("ssl"),
@@ -280,6 +287,20 @@ static ngx_command_t  ngx_http_ssl_commands[] = {
       offsetof(ngx_http_ssl_srv_conf_t, early_data),
       NULL },
 
+    { ngx_string("ssl_conf_command"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE2,
+      ngx_conf_set_keyval_slot,
+      NGX_HTTP_SRV_CONF_OFFSET,
+      offsetof(ngx_http_ssl_srv_conf_t, conf_commands),
+      &ngx_http_ssl_conf_command_post },
+
+    { ngx_string("ssl_reject_handshake"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_SRV_CONF_OFFSET,
+      offsetof(ngx_http_ssl_srv_conf_t, reject_handshake),
+      NULL },
+
       ngx_null_command
 };
 
@@ -600,12 +621,14 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
     sscf->enable = NGX_CONF_UNSET;
     sscf->prefer_server_ciphers = NGX_CONF_UNSET;
     sscf->early_data = NGX_CONF_UNSET;
+    sscf->reject_handshake = NGX_CONF_UNSET;
     sscf->buffer_size = NGX_CONF_UNSET_SIZE;
     sscf->verify = NGX_CONF_UNSET_UINT;
     sscf->verify_depth = NGX_CONF_UNSET_UINT;
     sscf->certificates = NGX_CONF_UNSET_PTR;
     sscf->certificate_keys = NGX_CONF_UNSET_PTR;
     sscf->passwords = NGX_CONF_UNSET_PTR;
+    sscf->conf_commands = NGX_CONF_UNSET_PTR;
     sscf->builtin_session_cache = NGX_CONF_UNSET;
     sscf->session_timeout = NGX_CONF_UNSET;
     sscf->session_tickets = NGX_CONF_UNSET;
@@ -645,6 +668,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
                          prev->prefer_server_ciphers, 0);
 
     ngx_conf_merge_value(conf->early_data, prev->early_data, 0);
+    ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0);
 
     ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
                          (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1
@@ -675,6 +699,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
 
     ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
 
+    ngx_conf_merge_ptr_value(conf->conf_commands, prev->conf_commands, NULL);
+
     ngx_conf_merge_uint_value(conf->ocsp, prev->ocsp, 0);
     ngx_conf_merge_str_value(conf->ocsp_responder, prev->ocsp_responder, "");
     ngx_conf_merge_ptr_value(conf->ocsp_cache_zone,
@@ -690,38 +716,35 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
 
     if (conf->enable) {
 
-        if (conf->certificates == NULL) {
-            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
-                          "no \"ssl_certificate\" is defined for "
-                          "the \"ssl\" directive in %s:%ui",
-                          conf->file, conf->line);
-            return NGX_CONF_ERROR;
-        }
+        if (conf->certificates) {
+            if (conf->certificate_keys == NULL) {
+                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                              "no \"ssl_certificate_key\" is defined for "
+                              "the \"ssl\" directive in %s:%ui",
+                              conf->file, conf->line);
+                return NGX_CONF_ERROR;
+            }
 
-        if (conf->certificate_keys == NULL) {
-            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
-                          "no \"ssl_certificate_key\" is defined for "
-                          "the \"ssl\" directive in %s:%ui",
-                          conf->file, conf->line);
-            return NGX_CONF_ERROR;
-        }
+            if (conf->certificate_keys->nelts < conf->certificates->nelts) {
+                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                              "no \"ssl_certificate_key\" is defined "
+                              "for certificate \"%V\" and "
+                              "the \"ssl\" directive in %s:%ui",
+                              ((ngx_str_t *) conf->certificates->elts)
+                              + conf->certificates->nelts - 1,
+                              conf->file, conf->line);
+                return NGX_CONF_ERROR;
+            }
 
-        if (conf->certificate_keys->nelts < conf->certificates->nelts) {
+        } else if (!conf->reject_handshake) {
             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
-                          "no \"ssl_certificate_key\" is defined "
-                          "for certificate \"%V\" and "
+                          "no \"ssl_certificate\" is defined for "
                           "the \"ssl\" directive in %s:%ui",
-                          ((ngx_str_t *) conf->certificates->elts)
-                          + conf->certificates->nelts - 1,
                           conf->file, conf->line);
             return NGX_CONF_ERROR;
         }
 
-    } else {
-
-        if (conf->certificates == NULL) {
-            return NGX_CONF_OK;
-        }
+    } else if (conf->certificates) {
 
         if (conf->certificate_keys == NULL
             || conf->certificate_keys->nelts < conf->certificates->nelts)
@@ -733,6 +756,9 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
                           + conf->certificates->nelts - 1);
             return NGX_CONF_ERROR;
         }
+
+    } else if (!conf->reject_handshake) {
+        return NGX_CONF_OK;
     }
 
     if (ngx_ssl_create(&conf->ssl, conf->protocols, conf) != NGX_OK) {
@@ -791,7 +817,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
         return NGX_CONF_ERROR;
 #endif
 
-    } else {
+    } else if (conf->certificates) {
 
         /* configure certificates */
 
@@ -913,6 +939,10 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
         return NGX_CONF_ERROR;
     }
 
+    if (ngx_ssl_conf_commands(cf, &conf->ssl, conf->conf_commands) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
     return NGX_CONF_OK;
 }
 
@@ -926,6 +956,10 @@ ngx_http_ssl_compile_certificates(ngx_conf_t *cf,
     ngx_http_complex_value_t          *cv;
     ngx_http_compile_complex_value_t   ccv;
 
+    if (conf->certificates == NULL) {
+        return NGX_OK;
+    }
+
     cert = conf->certificates->elts;
     key = conf->certificate_keys->elts;
     nelts = conf->certificates->nelts;
@@ -1235,6 +1269,17 @@ invalid:
 }
 
 
+static char *
+ngx_http_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data)
+{
+#ifndef SSL_CONF_FLAG_FILE
+    return "is not supported on this platform";
+#endif
+
+    return NGX_CONF_OK;
+}
+
+
 static ngx_int_t
 ngx_http_ssl_init(ngx_conf_t *cf)
 {
@@ -1295,7 +1340,33 @@ ngx_http_ssl_init(ngx_conf_t *cf)
             cscf = addr[a].default_server;
             sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index];
 
-            if (sscf->certificates == NULL) {
+            if (sscf->certificates) {
+                continue;
+            }
+
+            if (!sscf->reject_handshake) {
+                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                              "no \"ssl_certificate\" is defined for "
+                              "the \"listen ... ssl\" directive in %s:%ui",
+                              cscf->file_name, cscf->line);
+                return NGX_ERROR;
+            }
+
+            /*
+             * if no certificates are defined in the default server,
+             * check all non-default server blocks
+             */
+
+            cscfp = addr[a].servers.elts;
+            for (s = 0; s < addr[a].servers.nelts; s++) {
+
+                cscf = cscfp[s];
+                sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index];
+
+                if (sscf->certificates || sscf->reject_handshake) {
+                    continue;
+                }
+
                 ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                               "no \"ssl_certificate\" is defined for "
                               "the \"listen ... ssl\" directive in %s:%ui",
diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h
index 98aa1be4055c9ab85de68449a8654559f42b4451..7ab0f7eae28f7cd9eba52106a38ae8c811c39995 100644
--- a/src/http/modules/ngx_http_ssl_module.h
+++ b/src/http/modules/ngx_http_ssl_module.h
@@ -21,6 +21,7 @@ typedef struct {
 
     ngx_flag_t                      prefer_server_ciphers;
     ngx_flag_t                      early_data;
+    ngx_flag_t                      reject_handshake;
 
     ngx_uint_t                      protocols;
 
@@ -48,6 +49,7 @@ typedef struct {
     ngx_str_t                       ciphers;
 
     ngx_array_t                    *passwords;
+    ngx_array_t                    *conf_commands;
 
     ngx_shm_zone_t                 *shm_zone;
 
diff --git a/src/http/modules/ngx_http_userid_filter_module.c b/src/http/modules/ngx_http_userid_filter_module.c
index 31cf402f4b1fd3d80f9f734564df59869626d7e0..1e33c5c96cfa6c4e47166e1e016b9b70b69558ef 100644
--- a/src/http/modules/ngx_http_userid_filter_module.c
+++ b/src/http/modules/ngx_http_userid_filter_module.c
@@ -15,12 +15,21 @@
 #define NGX_HTTP_USERID_V1    2
 #define NGX_HTTP_USERID_ON    3
 
+#define NGX_HTTP_USERID_COOKIE_OFF              0x0002
+#define NGX_HTTP_USERID_COOKIE_SECURE           0x0004
+#define NGX_HTTP_USERID_COOKIE_HTTPONLY         0x0008
+#define NGX_HTTP_USERID_COOKIE_SAMESITE         0x0010
+#define NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT  0x0020
+#define NGX_HTTP_USERID_COOKIE_SAMESITE_LAX     0x0040
+#define NGX_HTTP_USERID_COOKIE_SAMESITE_NONE    0x0080
+
 /* 31 Dec 2037 23:55:55 GMT */
 #define NGX_HTTP_USERID_MAX_EXPIRES  2145916555
 
 
 typedef struct {
     ngx_uint_t  enable;
+    ngx_uint_t  flags;
 
     ngx_int_t   service;
 
@@ -88,6 +97,20 @@ static ngx_conf_enum_t  ngx_http_userid_state[] = {
 };
 
 
+static ngx_conf_bitmask_t  ngx_http_userid_flags[] = {
+    { ngx_string("off"), NGX_HTTP_USERID_COOKIE_OFF },
+    { ngx_string("secure"), NGX_HTTP_USERID_COOKIE_SECURE },
+    { ngx_string("httponly"), NGX_HTTP_USERID_COOKIE_HTTPONLY },
+    { ngx_string("samesite=strict"),
+      NGX_HTTP_USERID_COOKIE_SAMESITE|NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT },
+    { ngx_string("samesite=lax"),
+      NGX_HTTP_USERID_COOKIE_SAMESITE|NGX_HTTP_USERID_COOKIE_SAMESITE_LAX },
+    { ngx_string("samesite=none"),
+      NGX_HTTP_USERID_COOKIE_SAMESITE|NGX_HTTP_USERID_COOKIE_SAMESITE_NONE },
+    { ngx_null_string, 0 }
+};
+
+
 static ngx_conf_post_handler_pt  ngx_http_userid_domain_p =
     ngx_http_userid_domain;
 static ngx_conf_post_handler_pt  ngx_http_userid_path_p = ngx_http_userid_path;
@@ -138,6 +161,13 @@ static ngx_command_t  ngx_http_userid_commands[] = {
       0,
       NULL },
 
+    { ngx_string("userid_flags"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
+      ngx_conf_set_bitmask_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_userid_conf_t, flags),
+      &ngx_http_userid_flags },
+
     { ngx_string("userid_p3p"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_str_slot,
@@ -383,6 +413,26 @@ ngx_http_userid_set_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx,
         len += conf->domain.len;
     }
 
+    if (conf->flags & NGX_HTTP_USERID_COOKIE_SECURE) {
+        len += sizeof("; secure") - 1;
+    }
+
+    if (conf->flags & NGX_HTTP_USERID_COOKIE_HTTPONLY) {
+        len += sizeof("; httponly") - 1;
+    }
+
+    if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT) {
+        len += sizeof("; samesite=strict") - 1;
+    }
+
+    if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_LAX) {
+        len += sizeof("; samesite=lax") - 1;
+    }
+
+    if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_NONE) {
+        len += sizeof("; samesite=none") - 1;
+    }
+
     cookie = ngx_pnalloc(r->pool, len);
     if (cookie == NULL) {
         return NGX_ERROR;
@@ -422,6 +472,26 @@ ngx_http_userid_set_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx,
 
     p = ngx_copy(p, conf->path.data, conf->path.len);
 
+    if (conf->flags & NGX_HTTP_USERID_COOKIE_SECURE) {
+        p = ngx_cpymem(p, "; secure", sizeof("; secure") - 1);
+    }
+
+    if (conf->flags & NGX_HTTP_USERID_COOKIE_HTTPONLY) {
+        p = ngx_cpymem(p, "; httponly", sizeof("; httponly") - 1);
+    }
+
+    if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT) {
+        p = ngx_cpymem(p, "; samesite=strict", sizeof("; samesite=strict") - 1);
+    }
+
+    if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_LAX) {
+        p = ngx_cpymem(p, "; samesite=lax", sizeof("; samesite=lax") - 1);
+    }
+
+    if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_NONE) {
+        p = ngx_cpymem(p, "; samesite=none", sizeof("; samesite=none") - 1);
+    }
+
     set_cookie = ngx_list_push(&r->headers_out.headers);
     if (set_cookie == NULL) {
         return NGX_ERROR;
@@ -658,6 +728,7 @@ ngx_http_userid_create_conf(ngx_conf_t *cf)
     /*
      * set by ngx_pcalloc():
      *
+     *     conf->flags = 0;
      *     conf->name = { 0, NULL };
      *     conf->domain = { 0, NULL };
      *     conf->path = { 0, NULL };
@@ -682,6 +753,9 @@ ngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent, void *child)
     ngx_conf_merge_uint_value(conf->enable, prev->enable,
                               NGX_HTTP_USERID_OFF);
 
+    ngx_conf_merge_bitmask_value(conf->flags, prev->flags,
+                            (NGX_CONF_BITMASK_SET|NGX_HTTP_USERID_COOKIE_OFF));
+
     ngx_conf_merge_str_value(conf->name, prev->name, "uid");
     ngx_conf_merge_str_value(conf->domain, prev->domain, "");
     ngx_conf_merge_str_value(conf->path, prev->path, "; path=/");
diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c
index 56dc236ef181ba7b95275ba4a9c345736679fbe4..bf2732675e851f1d904f6a53ffb68e3c5886293f 100644
--- a/src/http/modules/ngx_http_uwsgi_module.c
+++ b/src/http/modules/ngx_http_uwsgi_module.c
@@ -57,6 +57,7 @@ typedef struct {
     ngx_str_t                  ssl_certificate;
     ngx_str_t                  ssl_certificate_key;
     ngx_array_t               *ssl_passwords;
+    ngx_array_t               *ssl_conf_commands;
 #endif
 } ngx_http_uwsgi_loc_conf_t;
 
@@ -67,6 +68,7 @@ static ngx_int_t ngx_http_uwsgi_create_request(ngx_http_request_t *r);
 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);
@@ -95,6 +97,8 @@ static char *ngx_http_uwsgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,
 #if (NGX_HTTP_SSL)
 static char *ngx_http_uwsgi_ssl_password_file(ngx_conf_t *cf,
     ngx_command_t *cmd, void *conf);
+static char *ngx_http_uwsgi_ssl_conf_command_check(ngx_conf_t *cf, void *post,
+    void *data);
 static ngx_int_t ngx_http_uwsgi_set_ssl(ngx_conf_t *cf,
     ngx_http_uwsgi_loc_conf_t *uwcf);
 #endif
@@ -133,6 +137,9 @@ static ngx_conf_bitmask_t  ngx_http_uwsgi_ssl_protocols[] = {
     { ngx_null_string, 0 }
 };
 
+static ngx_conf_post_t  ngx_http_uwsgi_ssl_conf_command_post =
+    { ngx_http_uwsgi_ssl_conf_command_check };
+
 #endif
 
 
@@ -560,6 +567,13 @@ static ngx_command_t ngx_http_uwsgi_commands[] = {
       0,
       NULL },
 
+    { ngx_string("uwsgi_ssl_conf_command"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
+      ngx_conf_set_keyval_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_uwsgi_loc_conf_t, ssl_conf_commands),
+      &ngx_http_uwsgi_ssl_conf_command_post },
+
 #endif
 
       ngx_null_command
@@ -703,6 +717,10 @@ ngx_http_uwsgi_handler(ngx_http_request_t *r)
     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 +1159,7 @@ ngx_http_uwsgi_create_request(ngx_http_request_t *r)
         r->upstream->request_bufs = cl;
     }
 
+    b->flush = 1;
     cl->next = NULL;
 
     return NGX_OK;
@@ -1355,6 +1374,37 @@ ngx_http_uwsgi_process_header(ngx_http_request_t *r)
 }
 
 
+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)
 {
@@ -1463,6 +1513,7 @@ ngx_http_uwsgi_create_loc_conf(ngx_conf_t *cf)
     conf->upstream.ssl_verify = NGX_CONF_UNSET;
     conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;
     conf->ssl_passwords = NGX_CONF_UNSET_PTR;
+    conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;
 #endif
 
     /* "uwsgi_cyclic_temp_file" is disabled */
@@ -1793,6 +1844,9 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
                               prev->ssl_certificate_key, "");
     ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL);
 
+    ngx_conf_merge_ptr_value(conf->ssl_conf_commands,
+                              prev->ssl_conf_commands, NULL);
+
     if (conf->ssl && ngx_http_uwsgi_set_ssl(cf, conf) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
@@ -2339,6 +2393,17 @@ ngx_http_uwsgi_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 }
 
 
+static char *
+ngx_http_uwsgi_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data)
+{
+#ifndef SSL_CONF_FLAG_FILE
+    return "is not supported on this platform";
+#endif
+
+    return NGX_CONF_OK;
+}
+
+
 static ngx_int_t
 ngx_http_uwsgi_set_ssl(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *uwcf)
 {
@@ -2416,6 +2481,12 @@ ngx_http_uwsgi_set_ssl(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *uwcf)
         return NGX_ERROR;
     }
 
+    if (ngx_ssl_conf_commands(cf, uwcf->upstream.ssl, uwcf->ssl_conf_commands)
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
     return NGX_OK;
 }
 
diff --git a/src/http/modules/ngx_http_xslt_filter_module.c b/src/http/modules/ngx_http_xslt_filter_module.c
index b2f107dc0ac89e44cc7a75bbfede43f07b3aebc4..8afd656af444f2a5840abbad31c7670e53c2bfc6 100644
--- a/src/http/modules/ngx_http_xslt_filter_module.c
+++ b/src/http/modules/ngx_http_xslt_filter_module.c
@@ -233,6 +233,7 @@ ngx_http_xslt_header_filter(ngx_http_request_t *r)
     ngx_http_set_ctx(r, ctx, ngx_http_xslt_filter_module);
 
     r->main_filter_need_in_memory = 1;
+    r->allow_ranges = 0;
 
     return NGX_OK;
 }
diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c
index 79ef9c644c83a7cd05eb07442994cfe9d48a669d..a35e9bb8a233e90165dc4dcef6caa0004fcf4d8b 100644
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -1469,14 +1469,14 @@ ngx_http_server_names(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,
                                   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 f9e96640998c62a6b4a02f1249df8b73f271b234..bb936c5fe0c590da1fca89e2c8c8d27d2cdf08ab 100644
--- a/src/http/ngx_http_cache.h
+++ b/src/http/ngx_http_cache.h
@@ -80,6 +80,7 @@ struct ngx_http_cache_s {
     ngx_str_t                        vary;
     u_char                           variant[NGX_HTTP_CACHE_KEY_LEN];
 
+    size_t                           buffer_size;
     size_t                           header_start;
     size_t                           body_start;
     off_t                            length;
@@ -116,6 +117,7 @@ struct ngx_http_cache_s {
     unsigned                         purged:1;
     unsigned                         reading:1;
     unsigned                         secondary:1;
+    unsigned                         update_variant:1;
     unsigned                         background:1;
 
     unsigned                         stale_updating:1;
@@ -160,6 +162,7 @@ struct ngx_http_file_cache_s {
 
     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 ecdf11e28797cad8b12e841cae325ac853ef31d8..c40093bca61cbd199c0c06e85640614f85241c8b 100644
--- a/src/http/ngx_http_file_cache.c
+++ b/src/http/ngx_http_file_cache.c
@@ -294,6 +294,8 @@ ngx_http_file_cache_open(ngx_http_request_t *r)
         cln->data = c;
     }
 
+    c->buffer_size = c->body_start;
+
     rc = ngx_http_file_cache_exists(cache, c);
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -852,7 +854,7 @@ ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
         if (fcn->exists || fcn->uses >= c->min_uses) {
 
             c->exists = fcn->exists;
-            if (fcn->body_start) {
+            if (fcn->body_start && !c->update_variant) {
                 c->body_start = fcn->body_start;
             }
 
@@ -1230,7 +1232,7 @@ ngx_http_file_cache_reopen(ngx_http_request_t *r, ngx_http_cache_t *c)
 
     c->secondary = 1;
     c->file.name.len = 0;
-    c->body_start = c->buf->end - c->buf->start;
+    c->body_start = c->buffer_size;
 
     ngx_memcpy(c->key, c->variant, NGX_HTTP_CACHE_KEY_LEN);
 
@@ -1337,6 +1339,7 @@ ngx_http_file_cache_update_variant(ngx_http_request_t *r, ngx_http_cache_t *c)
     ngx_shmtx_unlock(&cache->shpool->mutex);
 
     c->file.name.len = 0;
+    c->update_variant = 1;
 
     ngx_memcpy(c->key, c->main, NGX_HTTP_CACHE_KEY_LEN);
 
@@ -1959,7 +1962,7 @@ ngx_http_file_cache_manager(void *data)
 {
     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 +1991,19 @@ ngx_http_file_cache_manager(void *data)
                        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 +2319,7 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     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 +2356,7 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
     name.len = 0;
     size = 0;
     max_size = NGX_MAX_OFF_T_VALUE;
+    min_free = 0;
 
     value = cf->args->elts;
 
@@ -2476,6 +2492,29 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
             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 +2646,7 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 
     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_request.c b/src/http/ngx_http_request.c
index 6feb6cc31c30a4c91c0f4bd4290529980092702f..12a68a961d97e6e6ed2d2fc644efbe29edd5b798 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -49,7 +49,7 @@ static void ngx_http_request_finalizer(ngx_http_request_t *r);
 
 static void ngx_http_set_keepalive(ngx_http_request_t *r);
 static void ngx_http_keepalive_handler(ngx_event_t *ev);
-static void ngx_http_set_lingering_close(ngx_http_request_t *r);
+static void ngx_http_set_lingering_close(ngx_connection_t *c);
 static void ngx_http_lingering_close_handler(ngx_event_t *ev);
 static ngx_int_t ngx_http_post_action(ngx_http_request_t *r);
 static void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t error);
@@ -871,10 +871,14 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
         return SSL_TLSEXT_ERR_ALERT_FATAL;
     }
 
+    hc = c->data;
+
     servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);
 
     if (servername == NULL) {
-        return SSL_TLSEXT_ERR_OK;
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                       "SSL server name: null");
+        goto done;
     }
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
@@ -883,7 +887,7 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
     host.len = ngx_strlen(servername);
 
     if (host.len == 0) {
-        return SSL_TLSEXT_ERR_OK;
+        goto done;
     }
 
     host.data = (u_char *) servername;
@@ -891,32 +895,27 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
     rc = ngx_http_validate_host(&host, c->pool, 1);
 
     if (rc == NGX_ERROR) {
-        *ad = SSL_AD_INTERNAL_ERROR;
-        return SSL_TLSEXT_ERR_ALERT_FATAL;
+        goto error;
     }
 
     if (rc == NGX_DECLINED) {
-        return SSL_TLSEXT_ERR_OK;
+        goto done;
     }
 
-    hc = c->data;
-
     rc = ngx_http_find_virtual_server(c, hc->addr_conf->virtual_names, &host,
                                       NULL, &cscf);
 
     if (rc == NGX_ERROR) {
-        *ad = SSL_AD_INTERNAL_ERROR;
-        return SSL_TLSEXT_ERR_ALERT_FATAL;
+        goto error;
     }
 
     if (rc == NGX_DECLINED) {
-        return SSL_TLSEXT_ERR_OK;
+        goto done;
     }
 
     hc->ssl_servername = ngx_palloc(c->pool, sizeof(ngx_str_t));
     if (hc->ssl_servername == NULL) {
-        *ad = SSL_AD_INTERNAL_ERROR;
-        return SSL_TLSEXT_ERR_ALERT_FATAL;
+        goto error;
     }
 
     *hc->ssl_servername = host;
@@ -932,7 +931,9 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
     c->ssl->buffer_size = sscf->buffer_size;
 
     if (sscf->ssl.ctx) {
-        SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx);
+        if (SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx) == NULL) {
+            goto error;
+        }
 
         /*
          * SSL_set_SSL_CTX() only changes certs as of 1.0.0d
@@ -957,7 +958,22 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
 #endif
     }
 
+done:
+
+    sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
+
+    if (sscf->reject_handshake) {
+        c->ssl->handshake_rejected = 1;
+        *ad = SSL_AD_UNRECOGNIZED_NAME;
+        return SSL_TLSEXT_ERR_ALERT_FATAL;
+    }
+
     return SSL_TLSEXT_ERR_OK;
+
+error:
+
+    *ad = SSL_AD_INTERNAL_ERROR;
+    return SSL_TLSEXT_ERR_ALERT_FATAL;
 }
 
 #endif
@@ -1647,6 +1663,12 @@ ngx_http_alloc_large_header_buffer(ngx_http_request_t *r,
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http large header copy: %uz", r->header_in->pos - old);
 
+    if (r->header_in->pos - old > b->end - b->start) {
+        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                      "too large header to copy");
+        return NGX_ERROR;
+    }
+
     new = b->start;
 
     ngx_memcpy(new, old, r->header_in->pos - old);
@@ -2732,7 +2754,7 @@ ngx_http_finalize_connection(ngx_http_request_t *r)
                 || r->header_in->pos < r->header_in->last
                 || r->connection->read->ready)))
     {
-        ngx_http_set_lingering_close(r);
+        ngx_http_set_lingering_close(r->connection);
         return;
     }
 
@@ -2986,6 +3008,12 @@ closed:
         rev->error = 1;
     }
 
+#if (NGX_HTTP_SSL)
+    if (c->ssl) {
+        c->ssl->no_send_shutdown = 1;
+    }
+#endif
+
     ngx_log_error(NGX_LOG_INFO, c->log, err,
                   "client prematurely closed connection");
 
@@ -3011,13 +3039,6 @@ ngx_http_set_keepalive(ngx_http_request_t *r)
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "set http keepalive handler");
 
-    if (r->discard_body) {
-        r->write_event_handler = ngx_http_request_empty_handler;
-        r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000);
-        ngx_add_timer(rev, clcf->lingering_timeout);
-        return;
-    }
-
     c->log->action = "closing request";
 
     hc = r->http_connection;
@@ -3347,22 +3368,43 @@ ngx_http_keepalive_handler(ngx_event_t *rev)
 
 
 static void
-ngx_http_set_lingering_close(ngx_http_request_t *r)
+ngx_http_set_lingering_close(ngx_connection_t *c)
 {
     ngx_event_t               *rev, *wev;
-    ngx_connection_t          *c;
+    ngx_http_request_t        *r;
     ngx_http_core_loc_conf_t  *clcf;
 
-    c = r->connection;
+    r = c->data;
 
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
+    if (r->lingering_time == 0) {
+        r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000);
+    }
+
+#if (NGX_HTTP_SSL)
+    if (c->ssl) {
+        ngx_int_t  rc;
+
+        rc = ngx_ssl_shutdown(c);
+
+        if (rc == NGX_ERROR) {
+            ngx_http_close_request(r, 0);
+            return;
+        }
+
+        if (rc == NGX_AGAIN) {
+            c->ssl->handler = ngx_http_set_lingering_close;
+            return;
+        }
+
+        c->recv = ngx_recv;
+    }
+#endif
+
     rev = c->read;
     rev->handler = ngx_http_lingering_close_handler;
 
-    r->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_request(r, 0);
         return;
@@ -3385,6 +3427,8 @@ ngx_http_set_lingering_close(ngx_http_request_t *r)
         return;
     }
 
+    ngx_add_timer(rev, clcf->lingering_timeout);
+
     if (rev->ready) {
         ngx_http_lingering_close_handler(rev);
     }
diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c
index c4f092e59b1da71df1e50b44ea80eb36a3485852..f3b938382f84d04d2e51b730ee18ebba30829843 100644
--- a/src/http/ngx_http_request_body.c
+++ b/src/http/ngx_http_request_body.c
@@ -12,6 +12,8 @@
 
 static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r);
 static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r);
+static ngx_int_t ngx_http_copy_pipelined_header(ngx_http_request_t *r,
+    ngx_buf_t *buf);
 static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r);
 static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r);
 static ngx_int_t ngx_http_discard_request_body_filter(ngx_http_request_t *r,
@@ -282,28 +284,12 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r)
         for ( ;; ) {
             if (rb->buf->last == rb->buf->end) {
 
-                if (rb->buf->pos != rb->buf->last) {
+                /* update chains */
 
-                    /* pass buffer to request body filter chain */
+                rc = ngx_http_request_body_filter(r, NULL);
 
-                    out.buf = rb->buf;
-                    out.next = NULL;
-
-                    rc = ngx_http_request_body_filter(r, &out);
-
-                    if (rc != NGX_OK) {
-                        return rc;
-                    }
-
-                } else {
-
-                    /* update chains */
-
-                    rc = ngx_http_request_body_filter(r, NULL);
-
-                    if (rc != NGX_OK) {
-                        return rc;
-                    }
+                if (rc != NGX_OK) {
+                    return rc;
                 }
 
                 if (rb->busy != NULL) {
@@ -355,17 +341,15 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r)
             rb->buf->last += n;
             r->request_length += n;
 
-            if (n == rest) {
-                /* pass buffer to request body filter chain */
+            /* pass buffer to request body filter chain */
 
-                out.buf = rb->buf;
-                out.next = NULL;
+            out.buf = rb->buf;
+            out.next = NULL;
 
-                rc = ngx_http_request_body_filter(r, &out);
+            rc = ngx_http_request_body_filter(r, &out);
 
-                if (rc != NGX_OK) {
-                    return rc;
-                }
+            if (rc != NGX_OK) {
+                return rc;
             }
 
             if (rb->rest == 0) {
@@ -386,21 +370,6 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r)
 
         if (!c->read->ready) {
 
-            if (r->request_body_no_buffering
-                && rb->buf->pos != rb->buf->last)
-            {
-                /* pass buffer to request body filter chain */
-
-                out.buf = rb->buf;
-                out.next = NULL;
-
-                rc = ngx_http_request_body_filter(r, &out);
-
-                if (rc != NGX_OK) {
-                    return rc;
-                }
-            }
-
             clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
             ngx_add_timer(c->read, clcf->client_body_timeout);
 
@@ -412,6 +381,10 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r)
         }
     }
 
+    if (ngx_http_copy_pipelined_header(r, rb->buf) != NGX_OK) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
     if (c->read->timer_set) {
         ngx_del_timer(c->read);
     }
@@ -425,6 +398,88 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r)
 }
 
 
+static ngx_int_t
+ngx_http_copy_pipelined_header(ngx_http_request_t *r, ngx_buf_t *buf)
+{
+    size_t                     n;
+    ngx_buf_t                 *b;
+    ngx_chain_t               *cl;
+    ngx_http_connection_t     *hc;
+    ngx_http_core_srv_conf_t  *cscf;
+
+    b = r->header_in;
+    n = buf->last - buf->pos;
+
+    if (buf == b || n == 0) {
+        return NGX_OK;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http body pipelined header: %uz", n);
+
+    /*
+     * if there is a pipelined request in the client body buffer,
+     * copy it to the r->header_in buffer if there is enough room,
+     * or allocate a large client header buffer
+     */
+
+    if (n > (size_t) (b->end - b->last)) {
+
+        hc = r->http_connection;
+
+        if (hc->free) {
+            cl = hc->free;
+            hc->free = cl->next;
+
+            b = cl->buf;
+
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "http large header free: %p %uz",
+                           b->pos, b->end - b->last);
+
+        } else {
+            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+            b = ngx_create_temp_buf(r->connection->pool,
+                                    cscf->large_client_header_buffers.size);
+            if (b == NULL) {
+                return NGX_ERROR;
+            }
+
+            cl = ngx_alloc_chain_link(r->connection->pool);
+            if (cl == NULL) {
+                return NGX_ERROR;
+            }
+
+            cl->buf = b;
+
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "http large header alloc: %p %uz",
+                           b->pos, b->end - b->last);
+        }
+
+        cl->next = hc->busy;
+        hc->busy = cl;
+        hc->nbusy++;
+
+        r->header_in = b;
+
+        if (n > (size_t) (b->end - b->last)) {
+            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                          "too large pipelined header after reading body");
+            return NGX_ERROR;
+        }
+    }
+
+    ngx_memcpy(b->last, buf->pos, n);
+
+    b->last += n;
+    r->request_length -= n;
+
+    return NGX_OK;
+}
+
+
 static ngx_int_t
 ngx_http_write_request_body(ngx_http_request_t *r)
 {
@@ -619,6 +674,7 @@ ngx_http_discarded_request_body_handler(ngx_http_request_t *r)
     if (rc == NGX_OK) {
         r->discard_body = 0;
         r->lingering_close = 0;
+        r->lingering_time = 0;
         ngx_http_finalize_request(r, NGX_DONE);
         return;
     }
@@ -670,8 +726,7 @@ ngx_http_read_discarded_request_body(ngx_http_request_t *r)
 
     for ( ;; ) {
         if (r->headers_in.content_length_n == 0) {
-            r->read_event_handler = ngx_http_block_reading;
-            return NGX_OK;
+            break;
         }
 
         if (!r->connection->read->ready) {
@@ -705,15 +760,24 @@ ngx_http_read_discarded_request_body(ngx_http_request_t *r)
             return rc;
         }
     }
+
+    if (ngx_http_copy_pipelined_header(r, &b) != NGX_OK) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    r->read_event_handler = ngx_http_block_reading;
+
+    return NGX_OK;
 }
 
 
 static ngx_int_t
 ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b)
 {
-    size_t                    size;
-    ngx_int_t                 rc;
-    ngx_http_request_body_t  *rb;
+    size_t                     size;
+    ngx_int_t                  rc;
+    ngx_http_request_body_t   *rb;
+    ngx_http_core_srv_conf_t  *cscf;
 
     if (r->headers_in.chunked) {
 
@@ -768,7 +832,10 @@ ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b)
 
                 /* set amount of data we want to see next time */
 
-                r->headers_in.content_length_n = rb->chunked->length;
+                cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+                r->headers_in.content_length_n = ngx_max(rb->chunked->length,
+                               (off_t) cscf->large_client_header_buffers.size);
                 break;
             }
 
@@ -936,6 +1003,7 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)
     ngx_chain_t               *cl, *out, *tl, **ll;
     ngx_http_request_body_t   *rb;
     ngx_http_core_loc_conf_t  *clcf;
+    ngx_http_core_srv_conf_t  *cscf;
 
     rb = r->request_body;
 
@@ -949,8 +1017,10 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
 
+        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
         r->headers_in.content_length_n = 0;
-        rb->rest = 3;
+        rb->rest = cscf->large_client_header_buffers.size;
     }
 
     out = NULL;
@@ -958,6 +1028,8 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)
 
     for (cl = in; cl; cl = cl->next) {
 
+        b = NULL;
+
         for ( ;; ) {
 
             ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
@@ -992,6 +1064,29 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)
                     return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
                 }
 
+                if (b
+                    && rb->chunked->size <= 128
+                    && cl->buf->last - cl->buf->pos >= rb->chunked->size)
+                {
+                    r->headers_in.content_length_n += rb->chunked->size;
+
+                    if (rb->chunked->size < 8) {
+
+                        while (rb->chunked->size) {
+                            *b->last++ = *cl->buf->pos++;
+                            rb->chunked->size--;
+                        }
+
+                    } else {
+                        ngx_memmove(b->last, cl->buf->pos, rb->chunked->size);
+                        b->last += rb->chunked->size;
+                        cl->buf->pos += rb->chunked->size;
+                        rb->chunked->size = 0;
+                    }
+
+                    continue;
+                }
+
                 tl = ngx_chain_get_free_buf(r->pool, &rb->free);
                 if (tl == NULL) {
                     return NGX_HTTP_INTERNAL_SERVER_ERROR;
@@ -1057,7 +1152,10 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)
 
                 /* set rb->rest, amount of data we want to see next time */
 
-                rb->rest = rb->chunked->length;
+                cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+                rb->rest = ngx_max(rb->chunked->length,
+                               (off_t) cscf->large_client_header_buffers.size);
 
                 break;
             }
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index be96be47ad3c1ca244c7e3e215463c51284d9a2e..dda4046be8a116f7179f5af3cb5368949c8d3c5d 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -77,9 +77,6 @@ static void
 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 @@ ngx_http_upstream_reinit(ngx_http_request_t *r, ngx_http_upstream_t *u)
 
     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;
@@ -2475,7 +2473,7 @@ ngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u)
 #if (NGX_HTTP_CACHE)
 
         if (u->cache_status == NGX_HTTP_CACHE_EXPIRED
-            && ((u->conf->cache_use_stale & un->mask) || r->cache->stale_error))
+            && (u->conf->cache_use_stale & un->mask))
         {
             ngx_int_t  rc;
 
@@ -3627,7 +3625,7 @@ ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,
                     return;
                 }
 
-                if (upstream->read->error) {
+                if (upstream->read->error || u->error) {
                     ngx_http_upstream_finalize_request(r, u,
                                                        NGX_HTTP_BAD_GATEWAY);
                     return;
@@ -3705,14 +3703,14 @@ ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,
 }
 
 
-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;
@@ -3748,6 +3746,18 @@ ngx_http_upstream_non_buffered_filter(void *data, ssize_t bytes)
         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 6079d7236cbf62291797f17791d59b6a15506fc6..fd642c2d2a149ade0d4dc1a911b0cf8a654548b6 100644
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -391,6 +391,7 @@ struct ngx_http_upstream_s {
     unsigned                         buffering:1;
     unsigned                         keepalive:1;
     unsigned                         upgrade:1;
+    unsigned                         error:1;
 
     unsigned                         request_sent:1;
     unsigned                         request_body_sent:1;
@@ -414,6 +415,8 @@ typedef struct {
 
 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 08d66c97b3c6f2cc41c2052e5bd37eea70509998..58916a1845c2686bf6aafe27556757147dcbfd9a 100644
--- a/src/http/v2/ngx_http_v2.c
+++ b/src/http/v2/ngx_http_v2.c
@@ -60,6 +60,8 @@ typedef struct {
 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_connection_t *c);
+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);
@@ -473,6 +475,7 @@ ngx_http_v2_write_handler(ngx_event_t *wev)
         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
                        "http2 write event timed out");
         c->error = 1;
+        c->timedout = 1;
         ngx_http_v2_finalize_connection(h2c, 0);
         return;
     }
@@ -661,7 +664,7 @@ ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c)
     }
 
     if (h2c->goaway) {
-        ngx_http_close_connection(c);
+        ngx_http_v2_lingering_close(c);
         return;
     }
 
@@ -699,6 +702,137 @@ ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c)
 }
 
 
+static void
+ngx_http_v2_lingering_close(ngx_connection_t *c)
+{
+    ngx_event_t               *rev, *wev;
+    ngx_http_v2_connection_t  *h2c;
+    ngx_http_core_loc_conf_t  *clcf;
+
+    h2c = c->data;
+
+    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;
+    }
+
+    if (h2c->lingering_time == 0) {
+        h2c->lingering_time = ngx_time()
+                              + (time_t) (clcf->lingering_time / 1000);
+    }
+
+#if (NGX_HTTP_SSL)
+    if (c->ssl) {
+        ngx_int_t  rc;
+
+        rc = ngx_ssl_shutdown(c);
+
+        if (rc == NGX_ERROR) {
+            ngx_http_close_connection(c);
+            return;
+        }
+
+        if (rc == NGX_AGAIN) {
+            c->ssl->handler = ngx_http_v2_lingering_close;
+            return;
+        }
+
+        c->recv = ngx_recv;
+    }
+#endif
+
+    rev = c->read;
+    rev->handler = ngx_http_v2_lingering_close_handler;
+
+    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;
+    }
+
+    ngx_add_timer(rev, clcf->lingering_timeout);
+
+    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)
@@ -843,6 +977,13 @@ ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
                    "http2 DATA frame");
 
+    if (h2c->state.sid == 0) {
+        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+                      "client sent DATA frame with incorrect identifier");
+
+        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
+    }
+
     if (size > h2c->recv_window) {
         ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
                       "client violated connection flow control: "
@@ -941,6 +1082,7 @@ ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos,
     size_t                   size;
     ngx_buf_t               *buf;
     ngx_int_t                rc;
+    ngx_connection_t        *fc;
     ngx_http_request_t      *r;
     ngx_http_v2_stream_t    *stream;
     ngx_http_v2_srv_conf_t  *h2scf;
@@ -959,6 +1101,7 @@ ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos,
     }
 
     r = stream->request;
+    fc = r->connection;
 
     if (r->reading_body && !r->request_body_no_buffering) {
         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
@@ -967,6 +1110,13 @@ ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos,
         return ngx_http_v2_state_skip_padded(h2c, pos, end);
     }
 
+    if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) {
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+                       "skipping http2 DATA frame");
+
+        return ngx_http_v2_state_skip_padded(h2c, pos, end);
+    }
+
     size = end - pos;
 
     if (size >= h2c->state.length) {
@@ -984,6 +1134,8 @@ ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos,
             ngx_http_finalize_request(r, rc);
         }
 
+        ngx_http_run_posted_requests(fc);
+
     } else if (size) {
         buf = stream->preread;
 
@@ -1985,6 +2137,16 @@ static u_char *
 ngx_http_v2_state_settings(ngx_http_v2_connection_t *h2c, u_char *pos,
     u_char *end)
 {
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+                   "http2 SETTINGS frame");
+
+    if (h2c->state.sid) {
+        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+                      "client sent SETTINGS frame with incorrect identifier");
+
+        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
+    }
+
     if (h2c->state.flags == NGX_HTTP_V2_ACK_FLAG) {
 
         if (h2c->state.length != 0) {
@@ -2008,9 +2170,6 @@ ngx_http_v2_state_settings(ngx_http_v2_connection_t *h2c, u_char *pos,
         return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
     }
 
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
-                   "http2 SETTINGS frame");
-
     return ngx_http_v2_state_settings_params(h2c, pos, end);
 }
 
@@ -2159,6 +2318,13 @@ ngx_http_v2_state_ping(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
                    "http2 PING frame");
 
+    if (h2c->state.sid) {
+        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+                      "client sent PING frame with incorrect identifier");
+
+        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
+    }
+
     if (h2c->state.flags & NGX_HTTP_V2_ACK_FLAG) {
         return ngx_http_v2_state_skip(h2c, pos, end);
     }
@@ -2200,6 +2366,13 @@ ngx_http_v2_state_goaway(ngx_http_v2_connection_t *h2c, u_char *pos,
         return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_goaway);
     }
 
+    if (h2c->state.sid) {
+        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+                      "client sent GOAWAY frame with incorrect identifier");
+
+        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
+    }
+
 #if (NGX_DEBUG)
     h2c->state.length -= NGX_HTTP_V2_GOAWAY_SIZE;
 
@@ -4541,16 +4714,15 @@ ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c,
     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;
@@ -4598,10 +4770,18 @@ ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c,
     h2c->blocked = 0;
 
     if (h2c->processing || h2c->pushing) {
+        c->error = 1;
+        return;
+    }
+
+done:
+
+    if (c->error) {
+        ngx_http_close_connection(c);
         return;
     }
 
-    ngx_http_close_connection(c);
+    ngx_http_v2_lingering_close(c);
 }
 
 
diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h
index 59ddf54e2cb7c1cb345e9b5731ee9d526d01e9d0..349229711926390ac9f3805c2abf938e207b45e1 100644
--- a/src/http/v2/ngx_http_v2.h
+++ b/src/http/v2/ngx_http_v2.h
@@ -157,6 +157,8 @@ struct ngx_http_v2_connection_s {
     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/mail/ngx_mail.h b/src/mail/ngx_mail.h
index d904f25f1b75aadf4ae8603d9c25b761d4ebd488..25ac432b01591f5df7d9489461cbc4b0be072a26 100644
--- a/src/mail/ngx_mail.h
+++ b/src/mail/ngx_mail.h
@@ -162,10 +162,12 @@ typedef enum {
     ngx_smtp_auth_external,
     ngx_smtp_helo,
     ngx_smtp_helo_xclient,
+    ngx_smtp_helo_auth,
     ngx_smtp_helo_from,
     ngx_smtp_xclient,
     ngx_smtp_xclient_from,
     ngx_smtp_xclient_helo,
+    ngx_smtp_xclient_auth,
     ngx_smtp_from,
     ngx_smtp_to
 } ngx_smtp_state_e;
diff --git a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c
index 1c86e54cfe1dc66c52a0dd7e65920521f7a7d45a..610f5478030a258874178fd3babe9f4646818793 100644
--- a/src/mail/ngx_mail_proxy_module.c
+++ b/src/mail/ngx_mail_proxy_module.c
@@ -16,6 +16,7 @@ typedef struct {
     ngx_flag_t  enable;
     ngx_flag_t  pass_error_message;
     ngx_flag_t  xclient;
+    ngx_flag_t  smtp_auth;
     size_t      buffer_size;
     ngx_msec_t  timeout;
 } ngx_mail_proxy_conf_t;
@@ -74,6 +75,13 @@ static ngx_command_t  ngx_mail_proxy_commands[] = {
       offsetof(ngx_mail_proxy_conf_t, xclient),
       NULL },
 
+    { ngx_string("proxy_smtp_auth"),
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_MAIL_SRV_CONF_OFFSET,
+      offsetof(ngx_mail_proxy_conf_t, smtp_auth),
+      NULL },
+
       ngx_null_command
 };
 
@@ -450,7 +458,7 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
 {
     u_char                    *p;
     ngx_int_t                  rc;
-    ngx_str_t                  line;
+    ngx_str_t                  line, auth, encoded;
     ngx_buf_t                 *b;
     ngx_connection_t          *c;
     ngx_mail_session_t        *s;
@@ -513,6 +521,9 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
         } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {
             s->mail_state = ngx_smtp_helo_from;
 
+        } else if (pcf->smtp_auth) {
+            s->mail_state = ngx_smtp_helo_auth;
+
         } else {
             s->mail_state = ngx_smtp_helo;
         }
@@ -552,7 +563,9 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
         p = ngx_copy(p, s->connection->addr_text.data,
                      s->connection->addr_text.len);
 
-        if (s->login.len) {
+        pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
+
+        if (s->login.len && !pcf->smtp_auth) {
             p = ngx_cpymem(p, " LOGIN=", sizeof(" LOGIN=") - 1);
             p = ngx_copy(p, s->login.data, s->login.len);
         }
@@ -570,6 +583,9 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
         } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {
             s->mail_state = ngx_smtp_xclient_from;
 
+        } else if (pcf->smtp_auth) {
+            s->mail_state = ngx_smtp_xclient_auth;
+
         } else {
             s->mail_state = ngx_smtp_xclient;
         }
@@ -595,8 +611,62 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
                        &s->smtp_helo)
                    - line.data;
 
-        s->mail_state = (s->auth_method == NGX_MAIL_AUTH_NONE) ?
-                            ngx_smtp_helo_from : ngx_smtp_helo;
+        pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
+
+        if (s->auth_method == NGX_MAIL_AUTH_NONE) {
+            s->mail_state = ngx_smtp_helo_from;
+
+        } else if (pcf->smtp_auth) {
+            s->mail_state = ngx_smtp_helo_auth;
+
+        } else {
+            s->mail_state = ngx_smtp_helo;
+        }
+
+        break;
+
+    case ngx_smtp_helo_auth:
+    case ngx_smtp_xclient_auth:
+        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+                       "mail proxy send auth");
+
+        s->connection->log->action = "sending AUTH to upstream";
+
+        if (s->passwd.data == NULL) {
+            ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+                          "no password available");
+            ngx_mail_proxy_internal_server_error(s);
+            return;
+        }
+
+        auth.len = 1 + s->login.len + 1 + s->passwd.len;
+        auth.data = ngx_pnalloc(c->pool, auth.len);
+        if (auth.data == NULL) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
+        }
+
+        auth.len = ngx_sprintf(auth.data, "%Z%V%Z%V", &s->login, &s->passwd)
+                   - auth.data;
+
+        line.len = sizeof("AUTH PLAIN " CRLF) - 1
+                   + ngx_base64_encoded_length(auth.len);
+
+        line.data = ngx_pnalloc(c->pool, line.len);
+        if (line.data == NULL) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
+        }
+
+        encoded.data = ngx_cpymem(line.data, "AUTH PLAIN ",
+                                  sizeof("AUTH PLAIN ") - 1);
+
+        ngx_encode_base64(&encoded, &auth);
+
+        p = encoded.data + encoded.len;
+        *p++ = CR; *p = LF;
+
+        s->mail_state = ngx_smtp_auth_plain;
 
         break;
 
@@ -643,6 +713,7 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
 
     case ngx_smtp_helo:
     case ngx_smtp_xclient:
+    case ngx_smtp_auth_plain:
     case ngx_smtp_to:
 
         b = s->proxy->buffer;
@@ -824,6 +895,7 @@ ngx_mail_proxy_read_response(ngx_mail_session_t *s, ngx_uint_t state)
         case ngx_smtp_helo:
         case ngx_smtp_helo_xclient:
         case ngx_smtp_helo_from:
+        case ngx_smtp_helo_auth:
         case ngx_smtp_from:
             if (p[0] == '2' && p[1] == '5' && p[2] == '0') {
                 return NGX_OK;
@@ -833,11 +905,18 @@ ngx_mail_proxy_read_response(ngx_mail_session_t *s, ngx_uint_t state)
         case ngx_smtp_xclient:
         case ngx_smtp_xclient_from:
         case ngx_smtp_xclient_helo:
+        case ngx_smtp_xclient_auth:
             if (p[0] == '2' && (p[1] == '2' || p[1] == '5') && p[2] == '0') {
                 return NGX_OK;
             }
             break;
 
+        case ngx_smtp_auth_plain:
+            if (p[0] == '2' && p[1] == '3' && p[2] == '5') {
+                return NGX_OK;
+            }
+            break;
+
         case ngx_smtp_to:
             return NGX_OK;
         }
@@ -1102,6 +1181,7 @@ ngx_mail_proxy_create_conf(ngx_conf_t *cf)
     pcf->enable = NGX_CONF_UNSET;
     pcf->pass_error_message = NGX_CONF_UNSET;
     pcf->xclient = NGX_CONF_UNSET;
+    pcf->smtp_auth = NGX_CONF_UNSET;
     pcf->buffer_size = NGX_CONF_UNSET_SIZE;
     pcf->timeout = NGX_CONF_UNSET_MSEC;
 
@@ -1118,6 +1198,7 @@ ngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent, void *child)
     ngx_conf_merge_value(conf->enable, prev->enable, 0);
     ngx_conf_merge_value(conf->pass_error_message, prev->pass_error_message, 0);
     ngx_conf_merge_value(conf->xclient, prev->xclient, 1);
+    ngx_conf_merge_value(conf->smtp_auth, prev->smtp_auth, 0);
     ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
                               (size_t) ngx_pagesize);
     ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 24 * 60 * 60000);
diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c
index e193b298eaa85b8d1de95cd9c7b8395dcdef205c..d560bd60c7f5727f03734d1cebd400af52f58fa7 100644
--- a/src/mail/ngx_mail_ssl_module.c
+++ b/src/mail/ngx_mail_ssl_module.c
@@ -26,6 +26,9 @@ static char *ngx_mail_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
 static char *ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 
+static char *ngx_mail_ssl_conf_command_check(ngx_conf_t *cf, void *post,
+    void *data);
+
 
 static ngx_conf_enum_t  ngx_mail_starttls_state[] = {
     { ngx_string("off"), NGX_MAIL_STARTTLS_OFF },
@@ -61,6 +64,10 @@ static ngx_conf_deprecated_t  ngx_mail_ssl_deprecated = {
 };
 
 
+static ngx_conf_post_t  ngx_mail_ssl_conf_command_post =
+    { ngx_mail_ssl_conf_command_check };
+
+
 static ngx_command_t  ngx_mail_ssl_commands[] = {
 
     { ngx_string("ssl"),
@@ -196,6 +203,13 @@ static ngx_command_t  ngx_mail_ssl_commands[] = {
       offsetof(ngx_mail_ssl_conf_t, crl),
       NULL },
 
+    { ngx_string("ssl_conf_command"),
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE2,
+      ngx_conf_set_keyval_slot,
+      NGX_MAIL_SRV_CONF_OFFSET,
+      offsetof(ngx_mail_ssl_conf_t, conf_commands),
+      &ngx_mail_ssl_conf_command_post },
+
       ngx_null_command
 };
 
@@ -259,6 +273,7 @@ ngx_mail_ssl_create_conf(ngx_conf_t *cf)
     scf->certificates = NGX_CONF_UNSET_PTR;
     scf->certificate_keys = NGX_CONF_UNSET_PTR;
     scf->passwords = NGX_CONF_UNSET_PTR;
+    scf->conf_commands = NGX_CONF_UNSET_PTR;
     scf->prefer_server_ciphers = NGX_CONF_UNSET;
     scf->verify = NGX_CONF_UNSET_UINT;
     scf->verify_depth = NGX_CONF_UNSET_UINT;
@@ -316,6 +331,8 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
 
     ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
 
+    ngx_conf_merge_ptr_value(conf->conf_commands, prev->conf_commands, NULL);
+
 
     conf->ssl.log = cf->log;
 
@@ -461,6 +478,10 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
         return NGX_CONF_ERROR;
     }
 
+    if (ngx_ssl_conf_commands(cf, &conf->ssl, conf->conf_commands) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
     return NGX_CONF_OK;
 }
 
@@ -654,3 +675,14 @@ invalid:
 
     return NGX_CONF_ERROR;
 }
+
+
+static char *
+ngx_mail_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data)
+{
+#ifndef SSL_CONF_FLAG_FILE
+    return "is not supported on this platform";
+#endif
+
+    return NGX_CONF_OK;
+}
diff --git a/src/mail/ngx_mail_ssl_module.h b/src/mail/ngx_mail_ssl_module.h
index d6b0b8e0d294b80d245b250fb777ce23e990ed72..a0a611317356fb4d749c5d5ff4987aaf8f47ba9d 100644
--- a/src/mail/ngx_mail_ssl_module.h
+++ b/src/mail/ngx_mail_ssl_module.h
@@ -48,6 +48,7 @@ typedef struct {
     ngx_str_t        ciphers;
 
     ngx_array_t     *passwords;
+    ngx_array_t     *conf_commands;
 
     ngx_shm_zone_t  *shm_zone;
 
diff --git a/src/misc/ngx_cpp_test_module.cpp b/src/misc/ngx_cpp_test_module.cpp
index 5d2f08d3967ba0eca045c1cd3db2c73ef1cbb2b3..002640990f1a681cf9d23b617dadb8ab30677b1b 100644
--- a/src/misc/ngx_cpp_test_module.cpp
+++ b/src/misc/ngx_cpp_test_module.cpp
@@ -14,6 +14,8 @@ extern "C" {
   #include <ngx_mail_pop3_module.h>
   #include <ngx_mail_imap_module.h>
   #include <ngx_mail_smtp_module.h>
+
+  #include <ngx_stream.h>
 }
 
 // nginx header files should go before other, because they define 64-bit off_t
diff --git a/src/os/unix/ngx_files.c b/src/os/unix/ngx_files.c
index 482d327637e5af7485e05aace8755652ef1962f7..1c82a8ead0c43ce909c0ebc912ddd1d04ffc17d7 100644
--- a/src/os/unix/ngx_files.c
+++ b/src/os/unix/ngx_files.c
@@ -875,9 +875,28 @@ ngx_fs_bsize(u_char *name)
         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 @@ ngx_fs_bsize(u_char *name)
         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 @@ ngx_fs_bsize(u_char *name)
     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 383e38e65c7d48f61f527ee692561dca1a8b937f..d084713b6e7f95b369caa7248c42459fb8161a1d 100644
--- a/src/os/unix/ngx_files.h
+++ b/src/os/unix/ngx_files.h
@@ -185,7 +185,10 @@ ngx_int_t ngx_set_file_time(u_char *name, ngx_fd_t fd, time_t s);
 #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 @@ ngx_int_t ngx_directio_off(ngx_fd_t fd);
 #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_udp_sendmsg_chain.c b/src/os/unix/ngx_udp_sendmsg_chain.c
index 5399c7916a9e7806390659d7fca0ce7836cfd3cf..3d1d6dde4c8bc09dbb746a3671e2ed5d3a813d4a 100644
--- a/src/os/unix/ngx_udp_sendmsg_chain.c
+++ b/src/os/unix/ngx_udp_sendmsg_chain.c
@@ -189,6 +189,13 @@ ngx_udp_output_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *in, ngx_log_t *log)
         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 0b131b58af25763e7ad5b2396a5138351834ca98..3017b45fed10b3db65e40202680497e712b5573b 100644
--- a/src/os/win32/ngx_files.c
+++ b/src/os/win32/ngx_files.c
@@ -658,6 +658,19 @@ ngx_fs_bsize(u_char *name)
 }
 
 
+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 6eb720e78ed9090390712f77571aff6a68a2a96e..a10839ba470b32cacbba0fb17ba0db891fb26208 100644
--- a/src/os/win32/ngx_files.h
+++ b/src/os/win32/ngx_files.h
@@ -259,6 +259,7 @@ ngx_int_t ngx_directio_off(ngx_fd_t fd);
 #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 7484a728ad759c3ba041ec849771e7c1b166094f..0c86083c55b6992de2ea1931d3e5b0be5978aa19 100644
--- a/src/stream/ngx_stream_proxy_module.c
+++ b/src/stream/ngx_stream_proxy_module.c
@@ -49,6 +49,7 @@ typedef struct {
     ngx_str_t                        ssl_certificate;
     ngx_str_t                        ssl_certificate_key;
     ngx_array_t                     *ssl_passwords;
+    ngx_array_t                     *ssl_conf_commands;
 
     ngx_ssl_t                       *ssl;
 #endif
@@ -94,6 +95,8 @@ static char *ngx_stream_proxy_bind(ngx_conf_t *cf, ngx_command_t *cmd,
 static ngx_int_t ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s);
 static char *ngx_stream_proxy_ssl_password_file(ngx_conf_t *cf,
     ngx_command_t *cmd, void *conf);
+static char *ngx_stream_proxy_ssl_conf_command_check(ngx_conf_t *cf, void *post,
+    void *data);
 static void ngx_stream_proxy_ssl_init_connection(ngx_stream_session_t *s);
 static void ngx_stream_proxy_ssl_handshake(ngx_connection_t *pc);
 static void ngx_stream_proxy_ssl_save_session(ngx_connection_t *c);
@@ -112,6 +115,9 @@ static ngx_conf_bitmask_t  ngx_stream_proxy_ssl_protocols[] = {
     { ngx_null_string, 0 }
 };
 
+static ngx_conf_post_t  ngx_stream_proxy_ssl_conf_command_post =
+    { ngx_stream_proxy_ssl_conf_command_check };
+
 #endif
 
 
@@ -331,6 +337,13 @@ static ngx_command_t  ngx_stream_proxy_commands[] = {
       0,
       NULL },
 
+    { ngx_string("proxy_ssl_conf_command"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2,
+      ngx_conf_set_keyval_slot,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_proxy_srv_conf_t, ssl_conf_commands),
+      &ngx_stream_proxy_ssl_conf_command_post },
+
 #endif
 
       ngx_null_command
@@ -839,7 +852,7 @@ ngx_stream_proxy_init_upstream(ngx_stream_session_t *s)
         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 +866,7 @@ ngx_stream_proxy_init_upstream(ngx_stream_session_t *s)
         *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;
@@ -1007,6 +1021,17 @@ ngx_stream_proxy_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
 }
 
 
+static char *
+ngx_stream_proxy_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data)
+{
+#ifndef SSL_CONF_FLAG_FILE
+    return "is not supported on this platform";
+#endif
+
+    return NGX_CONF_OK;
+}
+
+
 static void
 ngx_stream_proxy_ssl_init_connection(ngx_stream_session_t *s)
 {
@@ -1984,6 +2009,7 @@ ngx_stream_proxy_create_srv_conf(ngx_conf_t *cf)
     conf->ssl_verify = NGX_CONF_UNSET;
     conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;
     conf->ssl_passwords = NGX_CONF_UNSET_PTR;
+    conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;
 #endif
 
     return conf;
@@ -2071,6 +2097,9 @@ ngx_stream_proxy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
 
     ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL);
 
+    ngx_conf_merge_ptr_value(conf->ssl_conf_commands,
+                              prev->ssl_conf_commands, NULL);
+
     if (conf->ssl_enable && ngx_stream_proxy_set_ssl(cf, conf) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
@@ -2155,6 +2184,12 @@ ngx_stream_proxy_set_ssl(ngx_conf_t *cf, ngx_stream_proxy_srv_conf_t *pscf)
         return NGX_ERROR;
     }
 
+    if (ngx_ssl_conf_commands(cf, pscf->ssl, pscf->ssl_conf_commands)
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
     return NGX_OK;
 }
 
diff --git a/src/stream/ngx_stream_set_module.c b/src/stream/ngx_stream_set_module.c
new file mode 100644
index 0000000000000000000000000000000000000000..9b34a6099f28e4fa4b43330e55afd172a7a6e6b8
--- /dev/null
+++ b/src/stream/ngx_stream_set_module.c
@@ -0,0 +1,226 @@
+
+/*
+ * Copyright (C) Pavel Pautov
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_stream.h>
+
+
+typedef struct {
+    ngx_int_t                   index;
+    ngx_stream_set_variable_pt  set_handler;
+    uintptr_t                   data;
+    ngx_stream_complex_value_t  value;
+} ngx_stream_set_cmd_t;
+
+
+typedef struct {
+    ngx_array_t                 commands;
+} ngx_stream_set_srv_conf_t;
+
+
+static ngx_int_t ngx_stream_set_handler(ngx_stream_session_t *s);
+static ngx_int_t ngx_stream_set_var(ngx_stream_session_t *s,
+    ngx_stream_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_stream_set_init(ngx_conf_t *cf);
+static void *ngx_stream_set_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_stream_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+
+
+static ngx_command_t  ngx_stream_set_commands[] = {
+
+    { ngx_string("set"),
+      NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2,
+      ngx_stream_set,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      0,
+      NULL },
+
+      ngx_null_command
+};
+
+
+static ngx_stream_module_t  ngx_stream_set_module_ctx = {
+    NULL,                                  /* preconfiguration */
+    ngx_stream_set_init,                   /* postconfiguration */
+
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    ngx_stream_set_create_srv_conf,        /* create server configuration */
+    NULL                                   /* merge server configuration */
+};
+
+
+ngx_module_t  ngx_stream_set_module = {
+    NGX_MODULE_V1,
+    &ngx_stream_set_module_ctx,            /* module context */
+    ngx_stream_set_commands,               /* module directives */
+    NGX_STREAM_MODULE,                     /* module type */
+    NULL,                                  /* init master */
+    NULL,                                  /* init module */
+    NULL,                                  /* init process */
+    NULL,                                  /* init thread */
+    NULL,                                  /* exit thread */
+    NULL,                                  /* exit process */
+    NULL,                                  /* exit master */
+    NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_stream_set_handler(ngx_stream_session_t *s)
+{
+    ngx_str_t                     str;
+    ngx_uint_t                    i;
+    ngx_stream_set_cmd_t         *cmds;
+    ngx_stream_set_srv_conf_t    *scf;
+    ngx_stream_variable_value_t   vv;
+
+    scf = ngx_stream_get_module_srv_conf(s, ngx_stream_set_module);
+    cmds = scf->commands.elts;
+    vv = ngx_stream_variable_null_value;
+
+    for (i = 0; i < scf->commands.nelts; i++) {
+        if (ngx_stream_complex_value(s, &cmds[i].value, &str) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        if (cmds[i].set_handler != NULL) {
+            vv.len = str.len;
+            vv.data = str.data;
+            cmds[i].set_handler(s, &vv, cmds[i].data);
+
+        } else {
+            s->variables[cmds[i].index].len = str.len;
+            s->variables[cmds[i].index].valid = 1;
+            s->variables[cmds[i].index].no_cacheable = 0;
+            s->variables[cmds[i].index].not_found = 0;
+            s->variables[cmds[i].index].data = str.data;
+        }
+    }
+
+    return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_stream_set_var(ngx_stream_session_t *s, ngx_stream_variable_value_t *v,
+    uintptr_t data)
+{
+    *v = ngx_stream_variable_null_value;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_set_init(ngx_conf_t *cf)
+{
+    ngx_stream_handler_pt        *h;
+    ngx_stream_core_main_conf_t  *cmcf;
+
+    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
+
+    h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREACCESS_PHASE].handlers);
+    if (h == NULL) {
+        return NGX_ERROR;
+    }
+
+    *h = ngx_stream_set_handler;
+
+    return NGX_OK;
+}
+
+
+static void *
+ngx_stream_set_create_srv_conf(ngx_conf_t *cf)
+{
+    ngx_stream_set_srv_conf_t  *conf;
+
+    conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_set_srv_conf_t));
+    if (conf == NULL) {
+        return NULL;
+    }
+
+    /*
+     * set by ngx_pcalloc():
+     *
+     *     conf->commands = { NULL };
+     */
+
+    return conf;
+}
+
+
+static char *
+ngx_stream_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_stream_set_srv_conf_t  *scf = conf;
+
+    ngx_str_t                           *args;
+    ngx_int_t                            index;
+    ngx_stream_set_cmd_t                *set_cmd;
+    ngx_stream_variable_t               *v;
+    ngx_stream_compile_complex_value_t   ccv;
+
+    args = cf->args->elts;
+
+    if (args[1].data[0] != '$') {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid variable name \"%V\"", &args[1]);
+        return NGX_CONF_ERROR;
+    }
+
+    args[1].len--;
+    args[1].data++;
+
+    v = ngx_stream_add_variable(cf, &args[1],
+                                NGX_STREAM_VAR_CHANGEABLE|NGX_STREAM_VAR_WEAK);
+    if (v == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    index = ngx_stream_get_variable_index(cf, &args[1]);
+    if (index == NGX_ERROR) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (v->get_handler == NULL) {
+        v->get_handler = ngx_stream_set_var;
+    }
+
+    if (scf->commands.elts == NULL) {
+        if (ngx_array_init(&scf->commands, cf->pool, 1,
+                           sizeof(ngx_stream_set_cmd_t))
+            != NGX_OK)
+        {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    set_cmd = ngx_array_push(&scf->commands);
+    if (set_cmd == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    set_cmd->index = index;
+    set_cmd->set_handler = v->set_handler;
+    set_cmd->data = v->data;
+
+    ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
+
+    ccv.cf = cf;
+    ccv.value = &args[2];
+    ccv.complex_value = &set_cmd->value;
+
+    if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c
index 79f30a863f604ab6b14a71b5f9bdfb7efe887bc2..ccd359f3b7011a01f8c7bd185e8a815ff1dee512 100644
--- a/src/stream/ngx_stream_ssl_module.c
+++ b/src/stream/ngx_stream_ssl_module.c
@@ -45,6 +45,10 @@ static char *ngx_stream_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 static char *ngx_stream_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+
+static char *ngx_stream_ssl_conf_command_check(ngx_conf_t *cf, void *post,
+    void *data);
+
 static ngx_int_t ngx_stream_ssl_init(ngx_conf_t *cf);
 
 
@@ -68,6 +72,10 @@ static ngx_conf_enum_t  ngx_stream_ssl_verify[] = {
 };
 
 
+static ngx_conf_post_t  ngx_stream_ssl_conf_command_post =
+    { ngx_stream_ssl_conf_command_check };
+
+
 static ngx_command_t  ngx_stream_ssl_commands[] = {
 
     { ngx_string("ssl_handshake_timeout"),
@@ -196,6 +204,13 @@ static ngx_command_t  ngx_stream_ssl_commands[] = {
       offsetof(ngx_stream_ssl_conf_t, crl),
       NULL },
 
+    { ngx_string("ssl_conf_command"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2,
+      ngx_conf_set_keyval_slot,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_ssl_conf_t, conf_commands),
+      &ngx_stream_ssl_conf_command_post },
+
       ngx_null_command
 };
 
@@ -595,6 +610,7 @@ ngx_stream_ssl_create_conf(ngx_conf_t *cf)
     scf->certificates = NGX_CONF_UNSET_PTR;
     scf->certificate_keys = NGX_CONF_UNSET_PTR;
     scf->passwords = NGX_CONF_UNSET_PTR;
+    scf->conf_commands = NGX_CONF_UNSET_PTR;
     scf->prefer_server_ciphers = NGX_CONF_UNSET;
     scf->verify = NGX_CONF_UNSET_UINT;
     scf->verify_depth = NGX_CONF_UNSET_UINT;
@@ -650,6 +666,8 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
 
     ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
 
+    ngx_conf_merge_ptr_value(conf->conf_commands, prev->conf_commands, NULL);
+
 
     conf->ssl.log = cf->log;
 
@@ -811,6 +829,10 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
         return NGX_CONF_ERROR;
     }
 
+    if (ngx_ssl_conf_commands(cf, &conf->ssl, conf->conf_commands) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
     return NGX_CONF_OK;
 }
 
@@ -1034,6 +1056,17 @@ invalid:
 }
 
 
+static char *
+ngx_stream_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data)
+{
+#ifndef SSL_CONF_FLAG_FILE
+    return "is not supported on this platform";
+#endif
+
+    return NGX_CONF_OK;
+}
+
+
 static ngx_int_t
 ngx_stream_ssl_init(ngx_conf_t *cf)
 {
diff --git a/src/stream/ngx_stream_ssl_module.h b/src/stream/ngx_stream_ssl_module.h
index 6cb4140a869396e0e32cdb715189e891f71268ce..c6e24bef366caa42a6669d0e9705aed24e2295bd 100644
--- a/src/stream/ngx_stream_ssl_module.h
+++ b/src/stream/ngx_stream_ssl_module.h
@@ -46,6 +46,7 @@ typedef struct {
     ngx_str_t        ciphers;
 
     ngx_array_t     *passwords;
+    ngx_array_t     *conf_commands;
 
     ngx_shm_zone_t  *shm_zone;
 
diff --git a/src/stream/ngx_stream_write_filter_module.c b/src/stream/ngx_stream_write_filter_module.c
index 24326c60e16645bdd251b89d2b33059be25c1a07..156a61c3d5ac65f658b548e3c02e89eb4c27e4a2 100644
--- a/src/stream/ngx_stream_write_filter_module.c
+++ b/src/stream/ngx_stream_write_filter_module.c
@@ -234,7 +234,8 @@ ngx_stream_write_filter(ngx_stream_session_t *s, ngx_chain_t *in,
 
     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 */) {