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 */) {