From mdounin at mdounin.ru Thu May 1 15:35:09 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Thu, 01 May 2025 18:35:09 +0300 Subject: [PATCH] Compatibility with -Wunterminated-string-initialization in GCC 15 Message-ID: <6d64b685bc7fbee88e31.1746113709@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1746113698 -10800 # Thu May 01 18:34:58 2025 +0300 # Node ID 6d64b685bc7fbee88e31ce54a151de0deeaabdde # Parent b325ee44215f5e1ff102d7dbfc15ffe5b285bb9d Compatibility with -Wunterminated-string-initialization in GCC 15. Builds with recently released GCC 15 currently fail due to -Wunterminated-string-initialization (introduced in -Wextra), which warns about attempts to initialize a character array with a string literal if the trailing NUL character doesn't fit (perfectly valid in C, but might be unintentional). Fix is to use arrays for initialization instead of string literals, or switch to using character arrays of unknown size where appropriate. diff --git a/src/event/quic/ngx_event_quic_protection.c b/src/event/quic/ngx_event_quic_protection.c --- a/src/event/quic/ngx_event_quic_protection.c +++ b/src/event/quic/ngx_event_quic_protection.c @@ -122,9 +122,10 @@ ngx_quic_keys_set_initial_secret(ngx_qui ngx_quic_secret_t *client, *server; ngx_quic_ciphers_t ciphers; - static const uint8_t salt[20] = - "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17" - "\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb\x7f\x0a"; + static const uint8_t salt[20] = { + 0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, + 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a + }; client = &keys->secrets[ssl_encryption_initial].client; server = &keys->secrets[ssl_encryption_initial].server; @@ -936,10 +937,14 @@ ngx_quic_create_retry_packet(ngx_quic_he ngx_quic_ciphers_t ciphers; /* 5.8. Retry Packet Integrity */ - static u_char key_data[16] = - "\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e"; - static u_char nonce[NGX_QUIC_IV_LEN] = - "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb"; + static u_char key_data[16] = { + 0xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66, 0x57, 0x5a, 0x1d, 0x76, + 0x6b, 0x54, 0xe3, 0x68, 0xc8, 0x4e + }; + static u_char nonce[NGX_QUIC_IV_LEN] = { + 0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, + 0x25, 0xbb + }; static ngx_str_t in = ngx_string(""); ad.data = res->data; diff --git a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c --- a/src/http/v2/ngx_http_v2_filter_module.c +++ b/src/http/v2/ngx_http_v2_filter_module.c @@ -116,7 +116,7 @@ ngx_http_v2_header_filter(ngx_http_reque u_char addr[NGX_SOCKADDR_STRLEN]; #if (NGX_HTTP_GZIP) - static const u_char accept_encoding[12] = + static const u_char accept_encoding[] = "\x8b\x84\x84\x2d\x69\x5b\x05\x44\x3c\x86\xaa\x6f"; #endif @@ -341,7 +341,7 @@ ngx_http_v2_header_filter(ngx_http_reque #if (NGX_HTTP_GZIP) if (r->gzip_vary) { if (clcf->gzip_vary) { - len += 1 + sizeof(accept_encoding); + len += 1 + sizeof(accept_encoding) - 1; } else { r->gzip_vary = 0; @@ -568,7 +568,7 @@ ngx_http_v2_header_filter(ngx_http_reque "http2 output header: \"vary: Accept-Encoding\""); *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX); - pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding)); + pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding) - 1); } #endif From mdounin at mdounin.ru Thu May 1 15:54:29 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Thu, 01 May 2025 18:54:29 +0300 Subject: [PATCH] Stream: fixed passwords usage for certificates with variables Message-ID: <756db4ac033c5dea1a3c.1746114869@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1746113755 -10800 # Thu May 01 18:35:55 2025 +0300 # Node ID 756db4ac033c5dea1a3c6478e0701115164a8c34 # Parent 6d64b685bc7fbee88e31ce54a151de0deeaabdde Stream: fixed passwords usage for certificates with variables. Missed in 9343:4f20c52c5f1b. Passwords not preserved for run time could happen to be used at run time if there are multiple server{} blocks all using the same SSL configuration inherited from the stream{} block. 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 @@ -2261,6 +2261,19 @@ ngx_stream_proxy_set_ssl(ngx_conf_t *cf, ngx_pool_cleanup_t *cln; if (pscf->ssl->ctx) { + + if (pscf->ssl_certificate + && pscf->ssl_certificate->value.len + && (pscf->ssl_certificate->lengths + || pscf->ssl_certificate_key->lengths)) + { + pscf->ssl_passwords = + ngx_ssl_preserve_passwords(cf, pscf->ssl_passwords); + if (pscf->ssl_passwords == NULL) { + return NGX_ERROR; + } + } + return NGX_OK; } From mdounin at mdounin.ru Fri May 2 00:43:03 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Fri, 02 May 2025 03:43:03 +0300 Subject: [PATCH 0 of 6] conditional rearm of write timeouts Message-ID: Hello! The following patch series introduces conditional rearm of write timeouts (and also does some related cleanup). Notably, this might be beneficial on Linux under memory pressure, when requested send buffers cannot be allocated and writev() returns EAGAIN without any progress, yet write events are reported by kernel, resulting in a busy loop. And, more importantly, making existing write timeout handling ineffective. Suggested change is to only rearm write timeouts if some progress was made, in most cases detected by comparing c->sent with the original value as saved at the start of the event handler. Comments are welcome. -- Maxim Dounin From mdounin at mdounin.ru Fri May 2 00:43:04 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Fri, 02 May 2025 03:43:04 +0300 Subject: [PATCH 1 of 6] Stream: style In-Reply-To: References: Message-ID: <0705780fc64d7088c91a.1746146584@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1746114905 -10800 # Thu May 01 18:55:05 2025 +0300 # Node ID 0705780fc64d7088c91a98650c82bdda56820ad5 # Parent 756db4ac033c5dea1a3c6478e0701115164a8c34 Stream: style. Moved SSL configuration parsing functions to the end of file, where configuration parsing should be. 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 @@ -94,10 +94,6 @@ static char *ngx_stream_proxy_bind(ngx_c #if (NGX_STREAM_SSL) 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); @@ -107,6 +103,10 @@ static ngx_int_t ngx_stream_proxy_merge_ ngx_stream_proxy_srv_conf_t *conf, ngx_stream_proxy_srv_conf_t *prev); static ngx_int_t ngx_stream_proxy_set_ssl(ngx_conf_t *cf, ngx_stream_proxy_srv_conf_t *pscf); +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 ngx_conf_bitmask_t ngx_stream_proxy_ssl_protocols[] = { @@ -1010,41 +1010,6 @@ ngx_stream_proxy_send_proxy_protocol(ngx } -static char * -ngx_stream_proxy_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf) -{ - ngx_stream_proxy_srv_conf_t *pscf = conf; - - ngx_str_t *value; - - if (pscf->ssl_passwords != NGX_CONF_UNSET_PTR) { - return "is duplicate"; - } - - value = cf->args->elts; - - pscf->ssl_passwords = ngx_ssl_read_password_file(cf, &value[1]); - - if (pscf->ssl_passwords == NULL) { - return NGX_CONF_ERROR; - } - - return NGX_CONF_OK; -} - - -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"; -#else - return NGX_CONF_OK; -#endif -} - - static void ngx_stream_proxy_ssl_init_connection(ngx_stream_session_t *s) { @@ -2518,3 +2483,42 @@ ngx_stream_proxy_bind(ngx_conf_t *cf, ng return NGX_CONF_OK; } + + +#if (NGX_STREAM_SSL) + +static char * +ngx_stream_proxy_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + ngx_stream_proxy_srv_conf_t *pscf = conf; + + ngx_str_t *value; + + if (pscf->ssl_passwords != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + value = cf->args->elts; + + pscf->ssl_passwords = ngx_ssl_read_password_file(cf, &value[1]); + + if (pscf->ssl_passwords == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +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"; +#else + return NGX_CONF_OK; +#endif +} + +#endif From mdounin at mdounin.ru Fri May 2 00:43:05 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Fri, 02 May 2025 03:43:05 +0300 Subject: [PATCH 2 of 6] Stream: style In-Reply-To: References: Message-ID: <01f2cc76e1a892ff2a9c.1746146585@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1746114908 -10800 # Thu May 01 18:55:08 2025 +0300 # Node ID 01f2cc76e1a892ff2a9c848db91a0de1b3eb23a2 # Parent 0705780fc64d7088c91a98650c82bdda56820ad5 Stream: style. Unless actually required for some reason, line continuation should be in column 79, not 80. diff --git a/src/stream/ngx_stream.h b/src/stream/ngx_stream.h --- a/src/stream/ngx_stream.h +++ b/src/stream/ngx_stream.h @@ -264,20 +264,20 @@ typedef struct { #define ngx_stream_delete_ctx(s, module) s->ctx[module.ctx_index] = NULL; -#define ngx_stream_get_module_main_conf(s, module) \ +#define ngx_stream_get_module_main_conf(s, module) \ (s)->main_conf[module.ctx_index] -#define ngx_stream_get_module_srv_conf(s, module) \ +#define ngx_stream_get_module_srv_conf(s, module) \ (s)->srv_conf[module.ctx_index] -#define ngx_stream_conf_get_module_main_conf(cf, module) \ +#define ngx_stream_conf_get_module_main_conf(cf, module) \ ((ngx_stream_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index] -#define ngx_stream_conf_get_module_srv_conf(cf, module) \ +#define ngx_stream_conf_get_module_srv_conf(cf, module) \ ((ngx_stream_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index] -#define ngx_stream_cycle_get_module_main_conf(cycle, module) \ - (cycle->conf_ctx[ngx_stream_module.index] ? \ - ((ngx_stream_conf_ctx_t *) cycle->conf_ctx[ngx_stream_module.index]) \ - ->main_conf[module.ctx_index]: \ +#define ngx_stream_cycle_get_module_main_conf(cycle, module) \ + (cycle->conf_ctx[ngx_stream_module.index] ? \ + ((ngx_stream_conf_ctx_t *) cycle->conf_ctx[ngx_stream_module.index]) \ + ->main_conf[module.ctx_index]: \ NULL) From mdounin at mdounin.ru Fri May 2 00:43:06 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Fri, 02 May 2025 03:43:06 +0300 Subject: [PATCH 3 of 6] Stream: fixed timeout usage for proxy_protocol with SSL proxying In-Reply-To: References: Message-ID: <4053f6d9b5c0caca6736.1746146586@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1746114911 -10800 # Thu May 01 18:55:11 2025 +0300 # Node ID 4053f6d9b5c0caca67365a320488154b84ebfba8 # Parent 01f2cc76e1a892ff2a9c848db91a0de1b3eb23a2 Stream: fixed timeout usage for proxy_protocol with SSL proxying. Connection establishment with proxied server includes SSL handshake, and relevant timeout is set with the proxy_connect_timeout directive. Since proxy_protocol sending happens before SSL handshake, it should use proxy_connect_timeout as well. Further, the timeout should not be rearmed if it's already set. Additionally, read handler should be set as long as we are waiting for events, since it can be triggered. 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 @@ -977,8 +977,11 @@ ngx_stream_proxy_send_proxy_protocol(ngx pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); - ngx_add_timer(pc->write, pscf->timeout); - + if (!pc->write->timer_set) { + ngx_add_timer(pc->write, pscf->connect_timeout); + } + + pc->read->handler = ngx_stream_proxy_connect_handler; pc->write->handler = ngx_stream_proxy_connect_handler; return NGX_AGAIN; From mdounin at mdounin.ru Fri May 2 00:43:07 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Fri, 02 May 2025 03:43:07 +0300 Subject: [PATCH 4 of 6] Stream: fixed proxy_connect_timeout with SSL proxying In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1746114916 -10800 # Thu May 01 18:55:16 2025 +0300 # Node ID ea2fca0e094ffa78db62ec8655a42965441cd8df # Parent 4053f6d9b5c0caca67365a320488154b84ebfba8 Stream: fixed proxy_connect_timeout with SSL proxying. Connection establishment, including SSL handshake, is expected to complete within the time set with the proxy_connect_timeout directive. However, previously corresponding timer was removed after TCP connect, and then again added for SSL handshaking, resulting in 2x longer time allowed in the worst case. Fix is to remove the timer in ngx_stream_proxy_init_upstream() instead of doing this in ngx_stream_proxy_connect_handler(). 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 @@ -929,6 +929,10 @@ ngx_stream_proxy_init_upstream(ngx_strea pc->read->handler = ngx_stream_proxy_upstream_handler; pc->write->handler = ngx_stream_proxy_upstream_handler; + if (pc->write->timer_set) { + ngx_del_timer(pc->write); + } + if (pc->read->ready) { ngx_post_event(pc->read, &ngx_posted_events); } @@ -1113,10 +1117,6 @@ ngx_stream_proxy_ssl_handshake(ngx_conne } } - if (pc->write->timer_set) { - ngx_del_timer(pc->write); - } - ngx_stream_proxy_init_upstream(s); return; @@ -1494,8 +1494,6 @@ ngx_stream_proxy_connect_handler(ngx_eve return; } - ngx_del_timer(c->write); - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, "stream proxy connect upstream"); From mdounin at mdounin.ru Fri May 2 00:43:08 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Fri, 02 May 2025 03:43:08 +0300 Subject: [PATCH 5 of 6] Mail: style In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1746114919 -10800 # Thu May 01 18:55:19 2025 +0300 # Node ID af60c59087e6721c8f24fae557a98617e7457823 # Parent ea2fca0e094ffa78db62ec8655a42965441cd8df Mail: style. There is no reason to use c->write as we already have wev, and it is used elsewhere in ngx_mail_send(). diff --git a/src/mail/ngx_mail_handler.c b/src/mail/ngx_mail_handler.c --- a/src/mail/ngx_mail_handler.c +++ b/src/mail/ngx_mail_handler.c @@ -1042,7 +1042,7 @@ ngx_mail_send(ngx_event_t *wev) } if (s->out.len == 0) { - if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + if (ngx_handle_write_event(wev, 0) != NGX_OK) { ngx_mail_close_connection(c); } @@ -1086,9 +1086,9 @@ again: cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - ngx_add_timer(c->write, cscf->timeout); + ngx_add_timer(wev, cscf->timeout); - if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + if (ngx_handle_write_event(wev, 0) != NGX_OK) { ngx_mail_close_connection(c); return; } From mdounin at mdounin.ru Fri May 2 00:43:09 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Fri, 02 May 2025 03:43:09 +0300 Subject: [PATCH 6 of 6] Conditional rearm of write timeouts In-Reply-To: References: Message-ID: <0aedbaa3cce2f488f376.1746146589@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1746135925 -10800 # Fri May 02 00:45:25 2025 +0300 # Node ID 0aedbaa3cce2f488f376475d344de571af12ab7d # Parent af60c59087e6721c8f24fae557a98617e7457823 Conditional rearm of write timeouts. When the network memory limit is hit on Linux, it is possible that connections are reported as writable, yet writev() / sendfile() returns no progress and EAGAIN. This results in write timeouts being ineffective, since they are rearmed on each write event. In particular, such behaviour can be easily reproduced with SO_SNDBUF explicitly set (via "listen ... sndbuf=...") and with poll or select event methods. Before kernel 6.0 and 5.19.2, this also can be easily reproduced with epoll (849b425cd091e "tcp: fix possible freeze in tx path under memory pressure"). With this change, write timeouts are only rearmed if some progress is made, ensuring that timeouts work properly in such situations. 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 @@ -22,10 +22,13 @@ static ngx_int_t ngx_event_pipe_drain_ch ngx_int_t ngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write) { + off_t sent; ngx_int_t rc; ngx_uint_t flags; ngx_event_t *rev, *wev; + sent = p->downstream->sent; + for ( ;; ) { if (do_write) { p->log->action = "sending to client"; @@ -88,7 +91,9 @@ ngx_event_pipe(ngx_event_pipe_t *p, ngx_ if (!wev->delayed) { if (wev->active && !wev->ready) { - ngx_add_timer(wev, p->send_timeout); + if (p->downstream->sent != sent || !wev->timer_set) { + ngx_add_timer(wev, p->send_timeout); + } } else if (wev->timer_set) { ngx_del_timer(wev); diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -2855,6 +2855,7 @@ ngx_http_set_write_handler(ngx_http_requ static void ngx_http_writer(ngx_http_request_t *r) { + off_t sent; ngx_int_t rc; ngx_event_t *wev; ngx_connection_t *c; @@ -2892,6 +2893,8 @@ ngx_http_writer(ngx_http_request_t *r) return; } + sent = c->sent; + rc = ngx_http_output_filter(r, NULL); ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, @@ -2905,7 +2908,7 @@ ngx_http_writer(ngx_http_request_t *r) if (r->buffered || r->postponed || (r == r->main && c->buffered)) { - if (!wev->delayed) { + if (!wev->delayed && (c->sent != sent || !wev->timer_set)) { ngx_add_timer(wev, clcf->send_timeout); } 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 @@ -2103,6 +2103,7 @@ static void ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_uint_t do_write) { + off_t sent; ngx_int_t rc; ngx_connection_t *c; @@ -2120,6 +2121,12 @@ ngx_http_upstream_send_request(ngx_http_ return; } + sent = c->sent; + + if (!u->request_sent) { + sent = -1; + } + c->log->action = "sending request to upstream"; rc = ngx_http_upstream_send_request_body(r, u, do_write); @@ -2136,7 +2143,9 @@ ngx_http_upstream_send_request(ngx_http_ if (rc == NGX_AGAIN) { if (!c->write->ready || u->request_body_blocked) { - ngx_add_timer(c->write, u->conf->send_timeout); + if (c->sent != sent || !c->write->timer_set) { + ngx_add_timer(c->write, u->conf->send_timeout); + } } else if (c->write->timer_set) { ngx_del_timer(c->write); @@ -3512,6 +3521,7 @@ static void ngx_http_upstream_process_upgraded(ngx_http_request_t *r, ngx_uint_t from_upstream, ngx_uint_t do_write) { + off_t dsent, usent; size_t size; ssize_t n; ngx_buf_t *b; @@ -3529,6 +3539,9 @@ ngx_http_upstream_process_upgraded(ngx_h downstream = c; upstream = u->peer.connection; + dsent = downstream->sent; + usent = upstream->sent; + if (downstream->write->timedout) { c->timedout = 1; ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); @@ -3648,7 +3661,9 @@ ngx_http_upstream_process_upgraded(ngx_h } if (upstream->write->active && !upstream->write->ready) { - ngx_add_timer(upstream->write, u->conf->send_timeout); + if (upstream->sent != usent || !upstream->write->timer_set) { + ngx_add_timer(upstream->write, u->conf->send_timeout); + } } else if (upstream->write->timer_set) { ngx_del_timer(upstream->write); @@ -3693,7 +3708,9 @@ ngx_http_upstream_process_upgraded(ngx_h } if (downstream->write->active && !downstream->write->ready) { - ngx_add_timer(downstream->write, clcf->send_timeout); + if (downstream->sent != dsent || !downstream->write->timer_set) { + ngx_add_timer(downstream->write, clcf->send_timeout); + } } else if (downstream->write->timer_set) { ngx_del_timer(downstream->write); @@ -3755,6 +3772,7 @@ static void ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r, ngx_uint_t do_write) { + off_t sent; size_t size; ssize_t n; ngx_buf_t *b; @@ -3768,6 +3786,8 @@ ngx_http_upstream_process_non_buffered_r downstream = r->connection; upstream = u->peer.connection; + sent = downstream->sent; + b = &u->buffer; do_write = do_write || u->length == 0; @@ -3857,7 +3877,9 @@ ngx_http_upstream_process_non_buffered_r } if (downstream->write->active && !downstream->write->ready) { - ngx_add_timer(downstream->write, clcf->send_timeout); + if (downstream->sent != sent || !downstream->write->timer_set) { + ngx_add_timer(downstream->write, clcf->send_timeout); + } } else if (downstream->write->timer_set) { ngx_del_timer(downstream->write); 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 @@ -503,6 +503,7 @@ ngx_int_t ngx_http_v2_send_output_queue(ngx_http_v2_connection_t *h2c) { int tcp_nodelay; + off_t sent; ngx_chain_t *cl; ngx_event_t *wev; ngx_connection_t *c; @@ -537,6 +538,8 @@ ngx_http_v2_send_output_queue(ngx_http_v out->blocked, out->length); } + sent = c->sent; + cl = c->send_chain(c, cl, 0); if (cl == NGX_CHAIN_ERROR) { @@ -592,7 +595,10 @@ ngx_http_v2_send_output_queue(ngx_http_v h2c->last_out = frame; if (!wev->ready) { - ngx_add_timer(wev, clcf->send_timeout); + if (c->sent != sent || !wev->timer_set) { + ngx_add_timer(wev, clcf->send_timeout); + } + return NGX_AGAIN; } diff --git a/src/mail/ngx_mail_handler.c b/src/mail/ngx_mail_handler.c --- a/src/mail/ngx_mail_handler.c +++ b/src/mail/ngx_mail_handler.c @@ -1084,9 +1084,10 @@ ngx_mail_send(ngx_event_t *wev) again: - cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - - ngx_add_timer(wev, cscf->timeout); + if (n > 0 || !wev->timer_set) { + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + ngx_add_timer(wev, cscf->timeout); + } if (ngx_handle_write_event(wev, 0) != NGX_OK) { ngx_mail_close_connection(c); diff --git a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c --- a/src/mail/ngx_mail_proxy_module.c +++ b/src/mail/ngx_mail_proxy_module.c @@ -1116,6 +1116,7 @@ static void ngx_mail_proxy_handler(ngx_event_t *ev) { char *action, *recv_action, *send_action; + off_t sent; size_t size; ssize_t n; ngx_buf_t *b; @@ -1181,6 +1182,7 @@ ngx_mail_proxy_handler(ngx_event_t *ev) } do_write = ev->write ? 1 : 0; + sent = dst->sent; ngx_log_debug3(NGX_LOG_DEBUG_MAIL, ev->log, 0, "mail proxy handler: %ui, #%d > #%d", @@ -1276,7 +1278,7 @@ ngx_mail_proxy_handler(ngx_event_t *ev) return; } - if (c == s->connection) { + if (c == s->connection && (dst->sent != sent || !ev->write)) { pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); ngx_add_timer(c->read, pcf->timeout); } 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 @@ -1555,7 +1555,7 @@ ngx_stream_proxy_process(ngx_stream_sess ngx_uint_t do_write) { char *recv_action, *send_action; - off_t *received, limit; + off_t *received, limit, sent; size_t size, limit_rate; ssize_t n; ngx_buf_t *b; @@ -1615,6 +1615,14 @@ ngx_stream_proxy_process(ngx_stream_sess send_action = "proxying and sending to upstream"; } +#if (NGX_SUPPRESS_WARN) + sent = 0; +#endif + + if (dst) { + sent = dst->sent; + } + for ( ;; ) { if (do_write && dst) { @@ -1758,7 +1766,9 @@ ngx_stream_proxy_process(ngx_stream_sess } if (!c->read->delayed && !pc->read->delayed) { - ngx_add_timer(c->write, pscf->timeout); + if (dst->sent != sent || !c->write->timer_set) { + ngx_add_timer(c->write, pscf->timeout); + } } else if (c->write->timer_set) { ngx_del_timer(c->write); diff --git a/src/stream/ngx_stream_return_module.c b/src/stream/ngx_stream_return_module.c --- a/src/stream/ngx_stream_return_module.c +++ b/src/stream/ngx_stream_return_module.c @@ -133,6 +133,7 @@ ngx_stream_return_handler(ngx_stream_ses static void ngx_stream_return_write_handler(ngx_event_t *ev) { + off_t sent; ngx_connection_t *c; ngx_stream_session_t *s; ngx_stream_return_ctx_t *ctx; @@ -146,6 +147,8 @@ ngx_stream_return_write_handler(ngx_even return; } + sent = c->sent; + ctx = ngx_stream_get_module_ctx(s, ngx_stream_return_module); if (ngx_stream_top_filter(s, ctx->out, 1) == NGX_ERROR) { @@ -167,7 +170,9 @@ ngx_stream_return_write_handler(ngx_even return; } - ngx_add_timer(ev, 5000); + if (c->sent != sent || !ev->timer_set) { + ngx_add_timer(ev, 5000); + } } From mdounin at mdounin.ru Thu May 8 14:44:03 2025 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 8 May 2025 17:44:03 +0300 Subject: [PATCH 0 of 6] conditional rearm of write timeouts In-Reply-To: References: Message-ID: Hello! On Fri, May 02, 2025 at 03:43:03AM +0300, Maxim Dounin wrote: > Hello! > > The following patch series introduces conditional rearm of write > timeouts (and also does some related cleanup). > > Notably, this might be beneficial on Linux under memory pressure, when > requested send buffers cannot be allocated and writev() returns EAGAIN > without any progress, yet write events are reported by kernel, resulting > in a busy loop. And, more importantly, making existing write timeout > handling ineffective. > > Suggested change is to only rearm write timeouts if some progress was > made, in most cases detected by comparing c->sent with the original > value as saved at the start of the event handler. > > Comments are welcome. It looks like I've accidentally posted this patch series and a couple of previous patches into the nginx@ mailing list instead of nginx-devel at freenginx.org, sorry. -- Maxim Dounin http://mdounin.ru/