# HG changeset patch # User Sergey Kandaurov # Date 1594643662 -10800 # Node ID 001ec7fce5678ad3fff1d8088dac04291541896d # Parent e02250b55b17bb04e2b5cf2c41ab4707afbb92e0# Parent 2ab0ecfe5a5f02f7214b02c8a1f9d6c5843d310e Merged with the default branch. diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -450,3 +450,4 @@ fdacd273711ddf20f778c1fb91529ab53979a454 5e8d52bca714d4b85284ddb649d1ba4a3ca978a8 release-1.17.9 c44970de01474f6f3e01b0adea85ec1d03e3a5f2 release-1.17.10 cbe6ba650211541310618849168631ce0b788f35 release-1.19.0 +062920e2f3bf871ef7a3d8496edec1b3065faf80 release-1.19.1 diff --git a/contrib/vim/syntax/nginx.vim b/contrib/vim/syntax/nginx.vim --- a/contrib/vim/syntax/nginx.vim +++ b/contrib/vim/syntax/nginx.vim @@ -141,6 +141,7 @@ syn keyword ngxDirective contained ancie syn keyword ngxDirective contained api syn keyword ngxDirective contained auth_basic syn keyword ngxDirective contained auth_basic_user_file +syn keyword ngxDirective contained auth_delay syn keyword ngxDirective contained auth_http syn keyword ngxDirective contained auth_http_header syn keyword ngxDirective contained auth_http_pass_client_cert @@ -332,6 +333,7 @@ syn keyword ngxDirective contained ip_ha syn keyword ngxDirective contained js_access syn keyword ngxDirective contained js_content syn keyword ngxDirective contained js_filter +syn keyword ngxDirective contained js_import syn keyword ngxDirective contained js_include syn keyword ngxDirective contained js_path syn keyword ngxDirective contained js_preread @@ -348,6 +350,7 @@ syn keyword ngxDirective contained large syn keyword ngxDirective contained least_conn syn keyword ngxDirective contained least_time syn keyword ngxDirective contained limit_conn +syn keyword ngxDirective contained limit_conn_dry_run syn keyword ngxDirective contained limit_conn_log_level syn keyword ngxDirective contained limit_conn_status syn keyword ngxDirective contained limit_conn_zone @@ -595,6 +598,9 @@ syn keyword ngxDirective contained ssl_e syn keyword ngxDirective contained ssl_ecdh_curve syn keyword ngxDirective contained ssl_engine syn keyword ngxDirective contained ssl_handshake_timeout +syn keyword ngxDirective contained ssl_ocsp +syn keyword ngxDirective contained ssl_ocsp_cache +syn keyword ngxDirective contained ssl_ocsp_responder syn keyword ngxDirective contained ssl_password_file syn keyword ngxDirective contained ssl_prefer_server_ciphers syn keyword ngxDirective contained ssl_preread @@ -770,6 +776,7 @@ syn keyword ngxDirectiveThirdParty conta syn keyword ngxDirectiveThirdParty contained auth_gss_force_realm syn keyword ngxDirectiveThirdParty contained auth_gss_format_full syn keyword ngxDirectiveThirdParty contained auth_gss_keytab +syn keyword ngxDirectiveThirdParty contained auth_gss_map_to_local syn keyword ngxDirectiveThirdParty contained auth_gss_realm syn keyword ngxDirectiveThirdParty contained auth_gss_service_name @@ -791,8 +798,8 @@ syn keyword ngxDirectiveThirdParty conta " AJP protocol proxy " https://github.com/yaoweibin/nginx_ajp_module +syn keyword ngxDirectiveThirdParty contained ajp_buffers syn keyword ngxDirectiveThirdParty contained ajp_buffer_size -syn keyword ngxDirectiveThirdParty contained ajp_buffers syn keyword ngxDirectiveThirdParty contained ajp_busy_buffers_size syn keyword ngxDirectiveThirdParty contained ajp_cache syn keyword ngxDirectiveThirdParty contained ajp_cache_key @@ -818,6 +825,7 @@ syn keyword ngxDirectiveThirdParty conta syn keyword ngxDirectiveThirdParty contained ajp_pass_request_body syn keyword ngxDirectiveThirdParty contained ajp_pass_request_headers syn keyword ngxDirectiveThirdParty contained ajp_read_timeout +syn keyword ngxDirectiveThirdParty contained ajp_secret syn keyword ngxDirectiveThirdParty contained ajp_send_lowat syn keyword ngxDirectiveThirdParty contained ajp_send_timeout syn keyword ngxDirectiveThirdParty contained ajp_store @@ -854,8 +862,8 @@ syn keyword ngxDirectiveThirdParty conta syn keyword ngxDirectiveThirdParty contained content_handler_type syn keyword ngxDirectiveThirdParty contained handler_code syn keyword ngxDirectiveThirdParty contained handler_name +syn keyword ngxDirectiveThirdParty contained handlers_lazy_init syn keyword ngxDirectiveThirdParty contained handler_type -syn keyword ngxDirectiveThirdParty contained handlers_lazy_init syn keyword ngxDirectiveThirdParty contained header_filter_code syn keyword ngxDirectiveThirdParty contained header_filter_name syn keyword ngxDirectiveThirdParty contained header_filter_property @@ -871,6 +879,10 @@ syn keyword ngxDirectiveThirdParty conta syn keyword ngxDirectiveThirdParty contained jvm_path syn keyword ngxDirectiveThirdParty contained jvm_var syn keyword ngxDirectiveThirdParty contained jvm_workers +syn keyword ngxDirectiveThirdParty contained log_handler_code +syn keyword ngxDirectiveThirdParty contained log_handler_name +syn keyword ngxDirectiveThirdParty contained log_handler_property +syn keyword ngxDirectiveThirdParty contained log_handler_type syn keyword ngxDirectiveThirdParty contained max_balanced_tcp_connections syn keyword ngxDirectiveThirdParty contained rewrite_handler_code syn keyword ngxDirectiveThirdParty contained rewrite_handler_name @@ -879,6 +891,7 @@ syn keyword ngxDirectiveThirdParty conta syn keyword ngxDirectiveThirdParty contained shared_map syn keyword ngxDirectiveThirdParty contained write_page_size + " Certificate Transparency " https://github.com/grahamedgecombe/nginx-ct syn keyword ngxDirectiveThirdParty contained ssl_ct @@ -942,6 +955,7 @@ syn keyword ngxDirectiveThirdParty conta syn keyword ngxDirectiveThirdParty contained fancyindex_ignore syn keyword ngxDirectiveThirdParty contained fancyindex_localtime syn keyword ngxDirectiveThirdParty contained fancyindex_name_length +syn keyword ngxDirectiveThirdParty contained fancyindex_show_dotfiles syn keyword ngxDirectiveThirdParty contained fancyindex_show_path syn keyword ngxDirectiveThirdParty contained fancyindex_time_format @@ -991,8 +1005,8 @@ syn keyword ngxDirectiveThirdParty conta syn keyword ngxDirectiveThirdParty contained nchan_benchmark_subscriber_distribution syn keyword ngxDirectiveThirdParty contained nchan_benchmark_subscribers_per_channel syn keyword ngxDirectiveThirdParty contained nchan_benchmark_time +syn keyword ngxDirectiveThirdParty contained nchan_channel_events_channel_id syn keyword ngxDirectiveThirdParty contained nchan_channel_event_string -syn keyword ngxDirectiveThirdParty contained nchan_channel_events_channel_id syn keyword ngxDirectiveThirdParty contained nchan_channel_group syn keyword ngxDirectiveThirdParty contained nchan_channel_group_accounting syn keyword ngxDirectiveThirdParty contained nchan_channel_id @@ -1000,6 +1014,10 @@ syn keyword ngxDirectiveThirdParty conta syn keyword ngxDirectiveThirdParty contained nchan_channel_timeout syn keyword ngxDirectiveThirdParty contained nchan_deflate_message_for_websocket syn keyword ngxDirectiveThirdParty contained nchan_eventsource_event +syn keyword ngxDirectiveThirdParty contained nchan_eventsource_ping_comment +syn keyword ngxDirectiveThirdParty contained nchan_eventsource_ping_data +syn keyword ngxDirectiveThirdParty contained nchan_eventsource_ping_event +syn keyword ngxDirectiveThirdParty contained nchan_eventsource_ping_interval syn keyword ngxDirectiveThirdParty contained nchan_group_location syn keyword ngxDirectiveThirdParty contained nchan_group_max_channels syn keyword ngxDirectiveThirdParty contained nchan_group_max_messages @@ -1047,10 +1065,10 @@ syn keyword ngxDirectiveThirdParty conta syn keyword ngxDirectiveThirdParty contained nchan_stub_status syn keyword ngxDirectiveThirdParty contained nchan_sub_channel_id syn keyword ngxDirectiveThirdParty contained nchan_subscribe_existing_channels_only -syn keyword ngxDirectiveThirdParty contained nchan_subscribe_request syn keyword ngxDirectiveThirdParty contained nchan_subscriber syn keyword ngxDirectiveThirdParty contained nchan_subscriber_channel_id syn keyword ngxDirectiveThirdParty contained nchan_subscriber_compound_etag_message_id +syn keyword ngxDirectiveThirdParty contained nchan_subscribe_request syn keyword ngxDirectiveThirdParty contained nchan_subscriber_first_message syn keyword ngxDirectiveThirdParty contained nchan_subscriber_http_raw_stream_separator syn keyword ngxDirectiveThirdParty contained nchan_subscriber_last_message_id @@ -1987,11 +2005,7 @@ syn keyword ngxDirectiveThirdParty conta " update upstreams' config by restful interface " https://github.com/yzprofile/ngx_http_dyups_module syn keyword ngxDirectiveThirdParty contained dyups_interface -syn keyword ngxDirectiveThirdParty contained dyups_read_msg_log -syn keyword ngxDirectiveThirdParty contained dyups_read_msg_timeout syn keyword ngxDirectiveThirdParty contained dyups_shm_zone_size -syn keyword ngxDirectiveThirdParty contained dyups_trylock -syn keyword ngxDirectiveThirdParty contained dyups_upstream_conf " add given content to the end of the response according to the condition specified " https://github.com/flygoast/ngx_http_footer_if_filter @@ -2308,6 +2322,62 @@ syn keyword ngxDirectiveThirdParty conta " https://github.com/flygoast/ngx_http_upstream_ketama_chash syn keyword ngxDirectiveThirdParty contained ketama_chash +" nginx-sticky-module-ng +" https://github.com/ayty-adrianomartins/nginx-sticky-module-ng +syn keyword ngxDirectiveThirdParty contained sticky_no_fallback + +" dynamic linking and call the function of your application +" https://github.com/Taymindis/nginx-link-function +syn keyword ngxDirectiveThirdParty contained ngx_link_func_add_prop +syn keyword ngxDirectiveThirdParty contained ngx_link_func_add_req_header +syn keyword ngxDirectiveThirdParty contained ngx_link_func_ca_cert +syn keyword ngxDirectiveThirdParty contained ngx_link_func_call +syn keyword ngxDirectiveThirdParty contained ngx_link_func_download_link_lib +syn keyword ngxDirectiveThirdParty contained ngx_link_func_lib +syn keyword ngxDirectiveThirdParty contained ngx_link_func_shm_size +syn keyword ngxDirectiveThirdParty contained ngx_link_func_subrequest + +" purge content from FastCGI, proxy, SCGI and uWSGI caches +" https://github.com/torden/ngx_cache_purge +syn keyword ngxDirectiveThirdParty contained cache_purge_response_type + +" set the flags "HttpOnly", "secure" and "SameSite" for cookies +" https://github.com/AirisX/nginx_cookie_flag_module +syn keyword ngxDirectiveThirdParty contained set_cookie_flag + +" Embed websockify into Nginx (convert any tcp connection into websocket) +" https://github.com/tg123/websockify-nginx-module +syn keyword ngxDirectiveThirdParty contained websockify_buffer_size +syn keyword ngxDirectiveThirdParty contained websockify_connect_timeout +syn keyword ngxDirectiveThirdParty contained websockify_pass +syn keyword ngxDirectiveThirdParty contained websockify_read_timeout +syn keyword ngxDirectiveThirdParty contained websockify_send_timeout + +" IP2Location Nginx +" https://github.com/ip2location/ip2location-nginx +syn keyword ngxDirectiveThirdParty contained ip2location +syn keyword ngxDirectiveThirdParty contained ip2location_access_type +syn keyword ngxDirectiveThirdParty contained ip2location_proxy +syn keyword ngxDirectiveThirdParty contained ip2location_proxy_recursive + +" IP2Proxy module for Nginx +" https://github.com/ip2location/ip2proxy-nginx +syn keyword ngxDirectiveThirdParty contained ip2proxy +syn keyword ngxDirectiveThirdParty contained ip2proxy_access_type +syn keyword ngxDirectiveThirdParty contained ip2proxy_as +syn keyword ngxDirectiveThirdParty contained ip2proxy_asn +syn keyword ngxDirectiveThirdParty contained ip2proxy_city +syn keyword ngxDirectiveThirdParty contained ip2proxy_country_long +syn keyword ngxDirectiveThirdParty contained ip2proxy_country_short +syn keyword ngxDirectiveThirdParty contained ip2proxy_database +syn keyword ngxDirectiveThirdParty contained ip2proxy_domain +syn keyword ngxDirectiveThirdParty contained ip2proxy_is_proxy +syn keyword ngxDirectiveThirdParty contained ip2proxy_isp +syn keyword ngxDirectiveThirdParty contained ip2proxy_last_seen +syn keyword ngxDirectiveThirdParty contained ip2proxy_proxy_type +syn keyword ngxDirectiveThirdParty contained ip2proxy_region +syn keyword ngxDirectiveThirdParty contained ip2proxy_reverse_proxy +syn keyword ngxDirectiveThirdParty contained ip2proxy_usage_type diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -5,6 +5,130 @@ + + + + +директивы lingering_close, lingering_time и lingering_timeout +теперь работают при использовании HTTP/2. + + +the "lingering_close", "lingering_time", and "lingering_timeout" directives +now work when using HTTP/2. + + + + + +теперь лишние данные, присланные бэкендом, всегда отбрасываются. + + +now extra data sent by a backend are always discarded. + + + + + +теперь при получении слишком короткого ответа от FastCGI-сервера +nginx пытается отправить клиенту доступную часть ответа, +после чего закрывает соединение с клиентом. + + +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. + + + + + +теперь при получении ответа некорректной длины от gRPC-бэкенда +nginx прекращает обработку ответа с ошибкой. + + +now after receiving a response with incorrect length from a gRPC backend +nginx stops response processing with an error. + + + + + +параметр min_free в директивах proxy_cache_path, fastcgi_cache_path, +scgi_cache_path и uwsgi_cache_path.
+Спасибо Adam Bambuch. +
+ +the "min_free" parameter of the "proxy_cache_path", "fastcgi_cache_path", +"scgi_cache_path", and "uwsgi_cache_path" directives.
+Thanks to Adam Bambuch. +
+
+ + + +nginx не удалял unix domain listen-сокеты +при плавном завершении по сигналу SIGQUIT. + + +nginx did not delete unix domain listen sockets +during graceful shutdown on the SIGQUIT signal. + + + + + +UDP-пакеты нулевого размера не проксировались. + + +zero length UDP datagrams were not proxied. + + + + + +проксирование на uwsgi-бэкенды с использованием SSL могло не работать.
+Спасибо Guanzhong Chen. +
+ +proxying to uwsgi backends using SSL might not work.
+Thanks to Guanzhong Chen. +
+
+ + + +в обработке ошибок при использовании директивы ssl_ocsp. + + +in error handling when using the "ssl_ocsp" directive. + + + + + +при использовании файловых систем XFS и NFS +размер кэша на диске мог считаться некорректно. + + +on XFS and NFS file systems +disk cache size might be calculated incorrectly. + + + + + +если сервер memcached возвращал некорректный ответ, +в логах могли появляться сообщения "negative size buf in writer". + + +"negative size buf in writer" alerts might appear in logs +if a memcached server returned a malformed response. + + + +
+ + diff --git a/src/core/nginx.c b/src/core/nginx.c --- a/src/core/nginx.c +++ b/src/core/nginx.c @@ -492,6 +492,7 @@ ngx_add_inherited_sockets(ngx_cycle_t *c ngx_memzero(ls, sizeof(ngx_listening_t)); ls->fd = (ngx_socket_t) s; + ls->inherited = 1; } } diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1019000 -#define NGINX_VERSION "1.19.0" +#define nginx_version 1019001 +#define NGINX_VERSION "1.19.1" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c --- a/src/core/ngx_connection.c +++ b/src/core/ngx_connection.c @@ -1074,7 +1074,8 @@ ngx_close_listening_sockets(ngx_cycle_t if (ls[i].sockaddr->sa_family == AF_UNIX && ngx_process <= NGX_PROCESS_MASTER - && ngx_new_binary == 0) + && ngx_new_binary == 0 + && (!ls[i].inherited || ngx_getppid() != ngx_parent)) { u_char *name = ls[i].addr_text.data + sizeof("unix:") - 1; diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c --- a/src/core/ngx_cycle.c +++ b/src/core/ngx_cycle.c @@ -520,6 +520,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) == NGX_OK) { nls[n].fd = ls[i].fd; + nls[n].inherited = ls[i].inherited; nls[n].previous = &ls[i]; ls[i].remain = 1; diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -920,6 +920,9 @@ 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_CTX_get_verify_mode(ssl->ctx), + ngx_ssl_verify_callback); + SSL_CTX_set_verify_depth(ssl->ctx, depth); if (cert->len == 0) { diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c --- a/src/event/ngx_event_openssl_stapling.c +++ b/src/event/ngx_event_openssl_stapling.c @@ -980,6 +980,7 @@ ngx_ssl_ocsp_validate_next(ngx_connectio if (ocsp->ncert == n - 1 || (ocf->depth == 2 && ocsp->ncert == 1)) { ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "ssl ocsp validated, certs:%ui", ocsp->ncert); + rc = NGX_OK; goto done; } @@ -988,7 +989,8 @@ ngx_ssl_ocsp_validate_next(ngx_connectio ctx = ngx_ssl_ocsp_start(c->log); if (ctx == NULL) { - goto failed; + rc = NGX_ERROR; + goto done; } ocsp->ctx = ctx; @@ -1012,8 +1014,9 @@ ngx_ssl_ocsp_validate_next(ngx_connectio ctx->uri = ocf->uri; ctx->port = ocf->port; - if (ngx_ssl_ocsp_responder(c, ctx) != NGX_OK) { - goto failed; + rc = ngx_ssl_ocsp_responder(c, ctx); + if (rc != NGX_OK) { + goto done; } if (ctx->uri.len == 0) { @@ -1025,7 +1028,7 @@ ngx_ssl_ocsp_validate_next(ngx_connectio rc = ngx_ssl_ocsp_cache_lookup(ctx); if (rc == NGX_ERROR) { - goto failed; + goto done; } if (rc == NGX_DECLINED) { @@ -1051,12 +1054,12 @@ ngx_ssl_ocsp_validate_next(ngx_connectio done: - ocsp->status = NGX_OK; - return; - -failed: - - ocsp->status = NGX_ERROR; + ocsp->status = rc; + + if (c->ssl->in_ocsp) { + c->ssl->handshaked = 1; + c->ssl->handler(c); + } } @@ -1073,22 +1076,16 @@ ngx_ssl_ocsp_handler(ngx_ssl_ocsp_ctx_t rc = ngx_ssl_ocsp_verify(ctx); if (rc != NGX_OK) { - ocsp->status = rc; - ngx_ssl_ocsp_done(ctx); goto done; } rc = ngx_ssl_ocsp_cache_store(ctx); if (rc != NGX_OK) { - ocsp->status = rc; - ngx_ssl_ocsp_done(ctx); goto done; } if (ctx->status != V_OCSP_CERTSTATUS_GOOD) { ocsp->cert_status = ctx->status; - ocsp->status = NGX_OK; - ngx_ssl_ocsp_done(ctx); goto done; } @@ -1096,15 +1093,17 @@ ngx_ssl_ocsp_handler(ngx_ssl_ocsp_ctx_t ngx_ssl_ocsp_validate_next(c); + return; + done: - if (ocsp->status == NGX_AGAIN || !c->ssl->in_ocsp) { - return; + ocsp->status = rc; + ngx_ssl_ocsp_done(ctx); + + if (c->ssl->in_ocsp) { + c->ssl->handshaked = 1; + c->ssl->handler(c); } - - c->ssl->handshaked = 1; - - c->ssl->handler(c); } diff --git a/src/event/ngx_event_pipe.c b/src/event/ngx_event_pipe.c --- 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_eve 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_eve 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 --- 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 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_ 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_ 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_ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0, "http fastcgi sent end request"); + if (f->rest > 0) { + ngx_log_error(NGX_LOG_ERR, p->log, 0, + "upstream prematurely closed " + "FastCGI request"); + + p->upstream_error = 1; + p->upstream_eof = 0; + f->closed = 1; + + break; + } + if (!flcf->keep_conn) { p->upstream_done = 1; break; @@ -2289,15 +2343,31 @@ ngx_http_fastcgi_input_filter(ngx_event_ f->pos += f->length; b->last = f->pos; - continue; + } else { + f->length -= f->last - f->pos; + f->pos = f->last; + b->last = f->last; + } + + if (f->rest == -2) { + f->rest = r->upstream->headers_in.content_length_n; } - f->length -= f->last - f->pos; - - b->last = f->last; - - break; - + if (f->rest >= 0) { + + if (b->last - b->pos > f->rest) { + ngx_log_error(NGX_LOG_WARN, p->log, 0, + "upstream sent more data than specified in " + "\"Content-Length\" header"); + + b->last = b->pos + f->rest; + p->upstream_done = 1; + + break; + } + + f->rest -= b->last - b->pos; + } } if (flcf->keep_conn) { @@ -2391,6 +2461,14 @@ ngx_http_fastcgi_non_buffered_filter(voi if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) { + if (f->rest > 0) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream prematurely closed " + "FastCGI request"); + u->error = 1; + break; + } + if (f->pos + f->padding < f->last) { u->length = 0; break; @@ -2510,13 +2588,27 @@ ngx_http_fastcgi_non_buffered_filter(voi 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; - - break; + 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"); + + 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 --- a/src/http/modules/ngx_http_grpc_module.c +++ b/src/http/modules/ngx_http_grpc_module.c @@ -84,6 +84,8 @@ typedef struct { ngx_uint_t pings; ngx_uint_t settings; + off_t length; + ssize_t send_window; size_t recv_window; @@ -1953,10 +1955,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 +2019,12 @@ ngx_http_grpc_filter(void *data, ssize_t if (ctx->done) { + if (ctx->length > 0) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream prematurely closed stream"); + return NGX_ERROR; + } + /* * We have finished parsing the response and the * remaining control frames. If there are unsent @@ -2052,6 +2078,17 @@ ngx_http_grpc_filter(void *data, ssize_t return NGX_ERROR; } + if (ctx->length != -1) { + if ((off_t) ctx->rest > ctx->length) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent response body larger " + "than indicated content length"); + return NGX_ERROR; + } + + ctx->length -= ctx->rest; + } + if (ctx->rest > ctx->recv_window) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "upstream violated stream flow control, " diff --git a/src/http/modules/ngx_http_memcached_module.c b/src/http/modules/ngx_http_memcached_module.c --- 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, ss 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, ss 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 --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -2015,6 +2015,25 @@ ngx_http_proxy_copy_filter(ngx_event_pip 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 +2061,23 @@ ngx_http_proxy_copy_filter(ngx_event_pip 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 +2104,23 @@ ngx_http_proxy_chunked_filter(ngx_event_ 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 +2183,15 @@ ngx_http_proxy_chunked_filter(ngx_event_ /* 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 +2206,13 @@ ngx_http_proxy_chunked_filter(ngx_event_ /* 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 +2272,18 @@ ngx_http_proxy_non_buffered_copy_filter( 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 +2370,12 @@ ngx_http_proxy_non_buffered_chunked_filt u->keepalive = !u->headers_in.connection_close; u->length = 0; + if (buf->pos != buf->last) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "upstream sent data after final chunk"); + u->keepalive = 0; + } + break; } diff --git a/src/http/modules/ngx_http_scgi_module.c b/src/http/modules/ngx_http_scgi_module.c --- 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_re 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 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_re } +static ngx_int_t +ngx_http_scgi_input_filter_init(void *data) +{ + ngx_http_request_t *r = data; + ngx_http_upstream_t *u; + + u = r->upstream; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http scgi filter init s:%ui l:%O", + u->headers_in.status_n, u->headers_in.content_length_n); + + if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT + || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED) + { + u->pipe->length = 0; + u->length = 0; + + } else if (r->method == NGX_HTTP_HEAD) { + u->pipe->length = -1; + u->length = -1; + + } else { + u->pipe->length = u->headers_in.content_length_n; + u->length = u->headers_in.content_length_n; + } + + return NGX_OK; +} + + static void ngx_http_scgi_abort_request(ngx_http_request_t *r) { diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c --- a/src/http/modules/ngx_http_uwsgi_module.c +++ b/src/http/modules/ngx_http_uwsgi_module.c @@ -67,6 +67,7 @@ static ngx_int_t ngx_http_uwsgi_create_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); @@ -703,6 +704,10 @@ ngx_http_uwsgi_handler(ngx_http_request_ u->pipe->input_filter = ngx_event_pipe_copy_input_filter; u->pipe->input_ctx = r; + u->input_filter_init = ngx_http_uwsgi_input_filter_init; + u->input_filter = ngx_http_upstream_non_buffered_filter; + u->input_filter_ctx = r; + if (!uwcf->upstream.request_buffering && uwcf->upstream.pass_request_body && !r->headers_in.chunked) @@ -1141,6 +1146,7 @@ ngx_http_uwsgi_create_request(ngx_http_r r->upstream->request_bufs = cl; } + b->flush = 1; cl->next = NULL; return NGX_OK; @@ -1355,6 +1361,37 @@ ngx_http_uwsgi_process_header(ngx_http_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) { diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1493,14 +1493,14 @@ ngx_http_server_names(ngx_conf_t *cf, ng 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 --- a/src/http/ngx_http_cache.h +++ b/src/http/ngx_http_cache.h @@ -160,6 +160,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 --- a/src/http/ngx_http_file_cache.c +++ b/src/http/ngx_http_file_cache.c @@ -1959,7 +1959,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 +1988,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 +2316,7 @@ ngx_http_file_cache_set_slot(ngx_conf_t { char *confp = conf; - off_t max_size; + off_t max_size, min_free; u_char *last, *p; time_t inactive; ssize_t size; @@ -2341,6 +2353,7 @@ ngx_http_file_cache_set_slot(ngx_conf_t name.len = 0; size = 0; max_size = NGX_MAX_OFF_T_VALUE; + min_free = 0; value = cf->args->elts; @@ -2476,6 +2489,29 @@ ngx_http_file_cache_set_slot(ngx_conf_t continue; } + if (ngx_strncmp(value[i].data, "min_free=", 9) == 0) { + +#if (NGX_WIN32 || NGX_HAVE_STATFS || NGX_HAVE_STATVFS) + + s.len = value[i].len - 9; + s.data = value[i].data + 9; + + min_free = ngx_parse_offset(&s); + if (min_free < 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid min_free value \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + +#else + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "min_free is not supported " + "on this platform, ignored"); +#endif + + continue; + } + if (ngx_strncmp(value[i].data, "loader_files=", 13) == 0) { loader_files = ngx_atoi(value[i].data + 13, value[i].len - 13); @@ -2607,6 +2643,7 @@ ngx_http_file_cache_set_slot(ngx_conf_t cache->inactive = inactive; cache->max_size = max_size; + cache->min_free = min_free; caches = (ngx_array_t *) (confp + cmd->offset); diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c --- 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_reques 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; @@ -3627,7 +3625,7 @@ ngx_http_upstream_process_non_buffered_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_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(vo 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 --- 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 --- 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_http_v2_connection_t *h2c); +static void ngx_http_v2_lingering_close_handler(ngx_event_t *rev); static u_char *ngx_http_v2_state_proxy_protocol(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end); @@ -661,7 +663,7 @@ ngx_http_v2_handle_connection(ngx_http_v } if (h2c->goaway) { - ngx_http_close_connection(c); + ngx_http_v2_lingering_close(h2c); return; } @@ -699,6 +701,113 @@ ngx_http_v2_handle_connection(ngx_http_v } +static void +ngx_http_v2_lingering_close(ngx_http_v2_connection_t *h2c) +{ + ngx_event_t *rev, *wev; + ngx_connection_t *c; + ngx_http_core_loc_conf_t *clcf; + + c = h2c->connection; + + clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx, + ngx_http_core_module); + + if (clcf->lingering_close == NGX_HTTP_LINGERING_OFF) { + ngx_http_close_connection(c); + return; + } + + rev = c->read; + rev->handler = ngx_http_v2_lingering_close_handler; + + h2c->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000); + ngx_add_timer(rev, clcf->lingering_timeout); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + + wev = c->write; + wev->handler = ngx_http_empty_handler; + + if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) { + if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + } + + if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) { + ngx_connection_error(c, ngx_socket_errno, + ngx_shutdown_socket_n " failed"); + ngx_http_close_connection(c); + return; + } + + if (rev->ready) { + ngx_http_v2_lingering_close_handler(rev); + } +} + + +static void +ngx_http_v2_lingering_close_handler(ngx_event_t *rev) +{ + ssize_t n; + ngx_msec_t timer; + ngx_connection_t *c; + ngx_http_core_loc_conf_t *clcf; + ngx_http_v2_connection_t *h2c; + u_char buffer[NGX_HTTP_LINGERING_BUFFER_SIZE]; + + c = rev->data; + h2c = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http2 lingering close handler"); + + if (rev->timedout) { + ngx_http_close_connection(c); + return; + } + + timer = (ngx_msec_t) h2c->lingering_time - (ngx_msec_t) ngx_time(); + if ((ngx_msec_int_t) timer <= 0) { + ngx_http_close_connection(c); + return; + } + + do { + n = c->recv(c, buffer, NGX_HTTP_LINGERING_BUFFER_SIZE); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "lingering read: %z", n); + + if (n == NGX_ERROR || n == 0) { + ngx_http_close_connection(c); + return; + } + + } while (rev->ready); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + + clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx, + ngx_http_core_module); + timer *= 1000; + + if (timer > clcf->lingering_timeout) { + timer = clcf->lingering_timeout; + } + + ngx_add_timer(rev, timer); +} + + static u_char * ngx_http_v2_state_proxy_protocol(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) @@ -4541,16 +4650,15 @@ ngx_http_v2_finalize_connection(ngx_http 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 +4706,18 @@ ngx_http_v2_finalize_connection(ngx_http h2c->blocked = 0; if (h2c->processing || h2c->pushing) { + c->error = 1; return; } - ngx_http_close_connection(c); +done: + + if (c->error) { + ngx_http_close_connection(c); + return; + } + + ngx_http_v2_lingering_close(h2c); } diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h --- 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/os/unix/ngx_files.c b/src/os/unix/ngx_files.c --- 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 --- 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 #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_process_cycle.c b/src/os/unix/ngx_process_cycle.c --- a/src/os/unix/ngx_process_cycle.c +++ b/src/os/unix/ngx_process_cycle.c @@ -77,12 +77,11 @@ ngx_master_process_cycle(ngx_cycle_t *cy u_char *p; size_t size; ngx_int_t i; - ngx_uint_t n, sigio; + ngx_uint_t sigio; sigset_t set; struct itimerval itv; ngx_uint_t live; ngx_msec_t delay; - ngx_listening_t *ls; ngx_core_conf_t *ccf; sigemptyset(&set); @@ -204,16 +203,7 @@ ngx_master_process_cycle(ngx_cycle_t *cy if (ngx_quit) { ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); - - ls = cycle->listening.elts; - for (n = 0; n < cycle->listening.nelts; n++) { - if (ngx_close_socket(ls[n].fd) == -1) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, - ngx_close_socket_n " %V failed", - &ls[n].addr_text); - } - } - cycle->listening.nelts = 0; + ngx_close_listening_sockets(cycle); continue; } diff --git a/src/os/unix/ngx_udp_sendmsg_chain.c b/src/os/unix/ngx_udp_sendmsg_chain.c --- 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_ 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 --- 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 --- 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 --- a/src/stream/ngx_stream_proxy_module.c +++ b/src/stream/ngx_stream_proxy_module.c @@ -839,7 +839,7 @@ ngx_stream_proxy_init_upstream(ngx_strea u->upstream_buf.last = p; } - if (c->buffer && c->buffer->pos < c->buffer->last) { + if (c->buffer && c->buffer->pos <= c->buffer->last) { ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, "stream proxy add preread buffer: %uz", c->buffer->last - c->buffer->pos); @@ -853,6 +853,7 @@ ngx_stream_proxy_init_upstream(ngx_strea *cl->buf = *c->buffer; cl->buf->tag = (ngx_buf_tag_t) &ngx_stream_proxy_module; + cl->buf->temporary = (cl->buf->pos == cl->buf->last) ? 0 : 1; cl->buf->flush = 1; cl->next = u->upstream_out; diff --git a/src/stream/ngx_stream_write_filter_module.c b/src/stream/ngx_stream_write_filter_module.c --- 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_sessi 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 */) {