From vl at inspert.ru Fri Aug 15 07:56:40 2025 From: vl at inspert.ru (Vladimir Homutov) Date: Fri, 15 Aug 2025 10:56:40 +0300 Subject: stale SSL errors in quic Message-ID: Hello, Commits 7468a10b62276be4adee0fcd6aaf6244270984ab "QUIC: adjusted handling of callback errors." and 47f96993f669543c6cb4979dd3f680ad01314ee5 "QUIC: logging of SSL library errors." lead to the situation when you may get spurious "ignoring stale global SSL error" errors in unrelated connections. This happens due to the fact that openssl error queue is (thread) global, and quic handshake handler does not read out the error after the failed handshake and relies on qc->error set by callback. So the error stays in queue and may show itself in unrelated quic connection, typically on SSL shutdown. config below may be used to reproduce the issue: >>> daemon off; error_log logs/error.log debug; events { } http { ssl_certificate_key localhost.key; ssl_certificate localhost.crt; server { error_log logs/good.log debug; listen 127.0.0.1:8080 quic; location / { return 200 OK; } } server { error_log logs/reject.log debug; listen 127.0.0.1:8081 quic; ssl_reject_handshake on; location / { return 200 OK; } } } <<< start the server and run: $ curl -k --http3 https://127.0.0.1:8080/ $ curl -k --http3 https://127.0.0.1:8081/ The result is alert in good.log: 2025/08/15 09:58:19 [alert] 1154786#1154786: *1 ignoring stale global SSL error (SSL: error:10000084:SSL routines:OPENSSL_internal:CLIENTHELLO_TLSEXT error:100000be:SSL routines:OPENSSL_internal:PARSE_TLSEXT) while preparing ack, client: 127.0.0.1, server: 127.0.0.1:8080 caused by failed handshake in server 2; when the ngx_http_ssl_servername() callback returns error, the ngx_quic_send_alert() sets qc->error, and the ngx_quic_ssl_handshake() checks qc->error after SSL_do_handshake() and closes connection. But the error stays, and manifests itself later in another connection in the same process. The quic fix is probably something like this: diff --git a/src/event/quic/ngx_event_quic_ssl.c b/src/event/quic/ngx_event_quic_ssl.c index e961c80cd..dc0a030ff 100644 --- a/src/event/quic/ngx_event_quic_ssl.c +++ b/src/event/quic/ngx_event_quic_ssl.c @@ -696,6 +696,7 @@ ngx_quic_handshake(ngx_connection_t *c) ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); if (qc->error) { + ERR_clear_error(); return NGX_ERROR; } Or may be it makes sense to clear error in the moment of setting qc->error; and do not clear everything, but try to log using ngx_ssl_error() with some non-alert level. From stephen.farrell at cs.tcd.ie Fri Aug 15 15:27:12 2025 From: stephen.farrell at cs.tcd.ie (Stephen Farrell) Date: Fri, 15 Aug 2025 16:27:12 +0100 Subject: PR for encrypted client hello (ECH) Message-ID: <45662a50-ee16-46f6-9f10-e12bb838ff61@cs.tcd.ie> Hiya, In preparation for whenever the OpenSSL library has ECH support (soonish:-) I've created a PR [1] for a way to ECH-enable NGINX. Be great to get any comments on that so that we can try land in a place where different web servers handle ECH in sorta-compatible ways. Thanks, Stephen. PS: Feedback here or on the PR are both fine from my POV. [1] https://github.com/nginx/nginx/pull/840 -------------- next part -------------- A non-text attachment was scrubbed... Name: OpenPGP_signature.asc Type: application/pgp-signature Size: 236 bytes Desc: OpenPGP digital signature URL: From pluknet at nginx.com Thu Aug 21 14:25:25 2025 From: pluknet at nginx.com (Sergey Kandaurov) Date: Thu, 21 Aug 2025 18:25:25 +0400 Subject: stale SSL errors in quic In-Reply-To: References: Message-ID: On Fri, Aug 15, 2025 at 10:56:40AM +0300, Vladimir Homutov wrote: > Hello, > > Commits 7468a10b62276be4adee0fcd6aaf6244270984ab > "QUIC: adjusted handling of callback errors." > > and 47f96993f669543c6cb4979dd3f680ad01314ee5 > "QUIC: logging of SSL library errors." > > lead to the situation when you may get spurious > "ignoring stale global SSL error" errors in unrelated connections. > > This happens due to the fact that openssl error queue is (thread) global, > and quic handshake handler does not read out the error after the failed > handshake and relies on qc->error set by callback. > > So the error stays in queue and may show itself in unrelated quic connection, > typically on SSL shutdown. > > config below may be used to reproduce the issue: > > >>> > daemon off; > error_log logs/error.log debug; > events { > } > > http { > ssl_certificate_key localhost.key; > ssl_certificate localhost.crt; > server { > error_log logs/good.log debug; > listen 127.0.0.1:8080 quic; > location / { return 200 OK; } > } > server { > error_log logs/reject.log debug; > listen 127.0.0.1:8081 quic; > ssl_reject_handshake on; > location / { return 200 OK; } > } > } > <<< > > start the server and run: > > $ curl -k --http3 https://127.0.0.1:8080/ > $ curl -k --http3 https://127.0.0.1:8081/ > > The result is alert in good.log: > > 2025/08/15 09:58:19 [alert] 1154786#1154786: *1 ignoring stale global SSL error (SSL: error:10000084:SSL routines:OPENSSL_internal:CLIENTHELLO_TLSEXT error:100000be:SSL routines:OPENSSL_internal:PARSE_TLSEXT) while preparing ack, client: 127.0.0.1, server: 127.0.0.1:8080 > > caused by failed handshake in server 2; Thanks for reporting. I was able to reproduce this with Test::Nginx reliably: my $s = Test::Nginx::HTTP3->new(8980); my $s2 = Test::Nginx::HTTP3->new(8981, probe => 1); select undef, undef, undef, 0.1; $s = undef; select undef, undef, undef, 0.2; $t->stop(); So the error queue remains non-empty until the 1st connection SSL shutdown. The following happens as seen in debug (filtered): 2025/08/21 15:52:02 [debug] 73543#1490328: *1 quic run 2025/08/21 15:52:02 [debug] 73543#1490328: *1 SSL_do_handshake: -1 2025/08/21 15:52:02 [debug] 73543#1490328: *1 SSL_do_handshake: 1 2025/08/21 15:52:02 [debug] 73543#1490328: *6 quic run 2025/08/21 15:52:02 [debug] 73543#1490328: *6 quic ngx_quic_send_alert() level:0 alert:112 2025/08/21 15:52:02 [debug] 73543#1490328: *6 SSL_do_handshake: -1 2025/08/21 15:52:02 [alert] 73543#1490328: *1 ignoring stale global SSL error (SSL: error:10000084:SSL routines:OPENSSL_internal:CLIENTHELLO_TLSEXT error:100000be:SSL routines:OPENSSL_internal:PARSE_TLSEXT) while preparing ack, client: 127.0.0.1, server: 127.0.0.1:8980 2025/08/21 15:52:02 [debug] 73543#1490328: *1 quic ngx_quic_send_alert() level:3 alert:0 2025/08/21 15:52:02 [debug] 73543#1490328: *1 SSL_shutdown: 1 The above is using BoringSSL to match the report. This is equivalently broken with all other libraries with a slightly different error message: 2025/08/21 16:03:40 [alert] 78666#1507465: *1 ignoring stale global SSL error (SSL: error:0A0000EA:SSL routines::callback failed) while preparing ack, client: 127.0.0.1, server: 127.0.0.1:8980 I don't think 47f96993f669543c6cb4979dd3f680ad01314ee5 is related. Although it formally changes reproduction surface by ngx_ssl_error(), this touches errors, which should not normally happen in practice. 7468a10b62276be4adee0fcd6aaf6244270984ab is a real culprit. > > when the ngx_http_ssl_servername() callback returns error, > the ngx_quic_send_alert() sets qc->error, and the ngx_quic_ssl_handshake() > checks qc->error after SSL_do_handshake() and closes connection. > But the error stays, and manifests itself later in another connection > in the same process. > > The quic fix is probably something like this: > > diff --git a/src/event/quic/ngx_event_quic_ssl.c b/src/event/quic/ngx_event_quic_ssl.c > index e961c80cd..dc0a030ff 100644 > --- a/src/event/quic/ngx_event_quic_ssl.c > +++ b/src/event/quic/ngx_event_quic_ssl.c > @@ -696,6 +696,7 @@ ngx_quic_handshake(ngx_connection_t *c) > ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); > > if (qc->error) { > + ERR_clear_error(); > return NGX_ERROR; > } > > Or may be it makes sense to clear error in the moment of setting qc->error; > and do not clear everything, but try to log using ngx_ssl_error() with some > non-alert level. What happens here is the added early check for qc->error broke the check for handshake_rejected, resulting in a missing diagnostics for the SNI-rejected handshake, and in a missing ERR_clear_error(). This can be fixed by moving the qc->error check to later such that handshake_rejected is checked first to make the error queue cleared. Although not practicably visible as needed, this can be also accompanied by clearing the error queue under the qc->error case as well, to be safe. Fixing this is complicated by different codes returned from SSL_get_error() for handshake errors happened in QUIC callbacks in various SSL libraries: # define SSL_ERROR_SSL 1 # define SSL_ERROR_WANT_READ 2 # define SSL_ERROR_WANT_WRITE 3 compat 3.5 bssl qtls lssl tp 2 3* 2 2 2 sni 1 1 1 1 1 In this regard, checking c->ssl->handshake_rejected is moved out of "sslerr != SSL_ERROR_WANT_READ". It is a set only flag, so this should be safe to do. This also somewhat simplifies and streamlines code. Patch also reconstructs a missing "SSL_do_handshake() failed" diagnostics for the qc->error case. It is made this way to avoid logging at the crit log level because qc->error is expected to have an empty error queue. Please test if it works for you. diff --git a/src/event/quic/ngx_event_quic_ssl.c b/src/event/quic/ngx_event_quic_ssl.c index e961c80cd..1de896291 100644 --- a/src/event/quic/ngx_event_quic_ssl.c +++ b/src/event/quic/ngx_event_quic_ssl.c @@ -695,25 +695,25 @@ ngx_quic_handshake(ngx_connection_t *c) ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); - if (qc->error) { - return NGX_ERROR; - } - if (n <= 0) { sslerr = SSL_get_error(ssl_conn, n); ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr); - if (sslerr != SSL_ERROR_WANT_READ) { - - if (c->ssl->handshake_rejected) { - ngx_connection_error(c, 0, "handshake rejected"); - ERR_clear_error(); + if (c->ssl->handshake_rejected) { + ngx_connection_error(c, 0, "handshake rejected"); + ERR_clear_error(); + return NGX_ERROR; + } - return NGX_ERROR; - } + if (qc->error) { + ngx_connection_error(c, 0, "SSL_do_handshake() failed"); + ERR_clear_error(); + return NGX_ERROR; + } + if (sslerr != SSL_ERROR_WANT_READ) { ngx_ssl_connection_error(c, sslerr, 0, "SSL_do_handshake() failed"); return NGX_ERROR; } From noreply at nginx.com Fri Aug 22 17:02:11 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:11 +0000 (UTC) Subject: [nginx] SSL: disabled certificate compression by default with OpenSSL. Message-ID: <20250822170211.5D88C3F6A7@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/ed99269eed283e474590bbe951bad1d74b721955 branches: master commit: ed99269eed283e474590bbe951bad1d74b721955 user: Sergey Kandaurov date: Tue, 15 Jul 2025 15:55:26 +0400 description: SSL: disabled certificate compression by default with OpenSSL. Certificate compression is supported since OpenSSL 3.2, it is enabled automatically as negotiated in a TLSv1.3 handshake. Using certificate compression and decompression in runtime may be suboptimal in terms of CPU and memory consumption in certain typical scenarios, hence it is disabled by default on both server and client sides. It can be enabled with ssl_conf_command and similar directives in upstream as appropriate, for example: ssl_conf_command Options RxCertificateCompression; ssl_conf_command Options TxCertificateCompression; Compressing server certificates requires additional support, this is addressed separately. --- src/event/ngx_event_openssl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index ff604c562..0c23c3f2f 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -387,6 +387,11 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data) SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_COMPRESSION); #endif +#ifdef SSL_OP_NO_TX_CERTIFICATE_COMPRESSION + SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TX_CERTIFICATE_COMPRESSION); + SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_RX_CERTIFICATE_COMPRESSION); +#endif + #ifdef SSL_OP_NO_ANTI_REPLAY SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_ANTI_REPLAY); #endif From noreply at nginx.com Fri Aug 22 17:02:11 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:11 +0000 (UTC) Subject: [nginx] SSL: support for compressed server certificates with OpenSSL. Message-ID: <20250822170211.662123F6A8@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/251444fcf4434bfddbe3394a568c51d4f7bd857f branches: master commit: 251444fcf4434bfddbe3394a568c51d4f7bd857f user: Sergey Kandaurov date: Wed, 9 Jul 2025 19:02:09 +0400 description: SSL: support for compressed server certificates with OpenSSL. The ssl_certificate_compression directive allows to send compressed server certificates. In OpenSSL, they are pre-compressed on startup. To simplify configuration, the SSL_OP_NO_TX_CERTIFICATE_COMPRESSION option is automatically cleared if certificates were pre-compressed. SSL_CTX_compress_certs() may return an error in legitimate cases, e.g., when none of compression algorithms is available or if the resulting compressed size is larger than the original one, thus it is silently ignored. Certificate compression is supported in Chrome with brotli only, in Safari with zlib only, and in Firefox with all listed algorithms. It is supported since Ubuntu 24.10, which has OpenSSL with enabled zlib and zstd support. The actual list of algorithms supported in OpenSSL depends on how the library was configured; it can be brotli, zlib, zstd as listed in RFC 8879. --- src/event/ngx_event_openssl.c | 30 ++++++++++++++++++++++++++++++ src/event/ngx_event_openssl.h | 2 ++ src/http/modules/ngx_http_ssl_module.c | 18 ++++++++++++++++++ src/http/modules/ngx_http_ssl_module.h | 1 + src/mail/ngx_mail_ssl_module.c | 18 ++++++++++++++++++ src/mail/ngx_mail_ssl_module.h | 1 + src/stream/ngx_stream_ssl_module.c | 18 ++++++++++++++++++ src/stream/ngx_stream_ssl_module.h | 1 + 8 files changed, 89 insertions(+) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 0c23c3f2f..e36f30c74 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -664,6 +664,36 @@ retry: } +ngx_int_t +ngx_ssl_certificate_compression(ngx_conf_t *cf, ngx_ssl_t *ssl, + ngx_uint_t enable) +{ + if (!enable) { + return NGX_OK; + } + +#ifdef SSL_OP_NO_TX_CERTIFICATE_COMPRESSION + + if (SSL_CTX_compress_certs(ssl->ctx, 0) == 0) { + ngx_ssl_error(NGX_LOG_WARN, ssl->log, 0, + "SSL_CTX_compress_certs() failed, ignored"); + return NGX_OK; + } + + SSL_CTX_clear_options(ssl->ctx, SSL_OP_NO_TX_CERTIFICATE_COMPRESSION); + +#else + + ngx_log_error(NGX_LOG_WARN, ssl->log, 0, + "\"ssl_certificate_compression\" is not supported " + "on this platform, ignored"); + +#endif + + return NGX_OK; +} + + ngx_int_t ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers, ngx_uint_t prefer_server_ciphers) diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index 0c9e9e840..e7ccd51e8 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -236,6 +236,8 @@ ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_int_t ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *cert, ngx_str_t *key, ngx_ssl_cache_t *cache, ngx_array_t *passwords); +ngx_int_t ngx_ssl_certificate_compression(ngx_conf_t *cf, ngx_ssl_t *ssl, + ngx_uint_t enable); ngx_int_t ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers, ngx_uint_t prefer_server_ciphers); diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index dbfe5c08b..fbf4ab871 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -124,6 +124,13 @@ static ngx_command_t ngx_http_ssl_commands[] = { 0, NULL }, + { ngx_string("ssl_certificate_compression"), + 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, certificate_compression), + NULL }, + { ngx_string("ssl_dhparam"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, @@ -621,6 +628,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf) */ sscf->prefer_server_ciphers = NGX_CONF_UNSET; + sscf->certificate_compression = NGX_CONF_UNSET; sscf->early_data = NGX_CONF_UNSET; sscf->reject_handshake = NGX_CONF_UNSET; sscf->buffer_size = NGX_CONF_UNSET_SIZE; @@ -658,6 +666,9 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->prefer_server_ciphers, prev->prefer_server_ciphers, 0); + ngx_conf_merge_value(conf->certificate_compression, + prev->certificate_compression, 0); + ngx_conf_merge_value(conf->early_data, prev->early_data, 0); ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0); @@ -792,6 +803,13 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) { return NGX_CONF_ERROR; } + + if (ngx_ssl_certificate_compression(cf, &conf->ssl, + conf->certificate_compression) + != NGX_OK) + { + return NGX_CONF_ERROR; + } } conf->ssl.buffer_size = conf->buffer_size; diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h index 8650fab93..9b26529fa 100644 --- a/src/http/modules/ngx_http_ssl_module.h +++ b/src/http/modules/ngx_http_ssl_module.h @@ -18,6 +18,7 @@ typedef struct { ngx_ssl_t ssl; ngx_flag_t prefer_server_ciphers; + ngx_flag_t certificate_compression; ngx_flag_t early_data; ngx_flag_t reject_handshake; diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c index 176e9c624..079d0e773 100644 --- a/src/mail/ngx_mail_ssl_module.c +++ b/src/mail/ngx_mail_ssl_module.c @@ -97,6 +97,13 @@ static ngx_command_t ngx_mail_ssl_commands[] = { 0, NULL }, + { ngx_string("ssl_certificate_compression"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, certificate_compression), + NULL }, + { ngx_string("ssl_dhparam"), NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, @@ -314,6 +321,7 @@ ngx_mail_ssl_create_conf(ngx_conf_t *cf) scf->passwords = NGX_CONF_UNSET_PTR; scf->conf_commands = NGX_CONF_UNSET_PTR; scf->prefer_server_ciphers = NGX_CONF_UNSET; + scf->certificate_compression = NGX_CONF_UNSET; scf->verify = NGX_CONF_UNSET_UINT; scf->verify_depth = NGX_CONF_UNSET_UINT; scf->builtin_session_cache = NGX_CONF_UNSET; @@ -343,6 +351,9 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->prefer_server_ciphers, prev->prefer_server_ciphers, 0); + ngx_conf_merge_value(conf->certificate_compression, + prev->certificate_compression, 0); + ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, (NGX_CONF_BITMASK_SET|NGX_SSL_DEFAULT_PROTOCOLS)); @@ -446,6 +457,13 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } + if (ngx_ssl_certificate_compression(cf, &conf->ssl, + conf->certificate_compression) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + if (conf->verify) { if (conf->verify != 3 diff --git a/src/mail/ngx_mail_ssl_module.h b/src/mail/ngx_mail_ssl_module.h index c0eb6a38f..a0e9a173a 100644 --- a/src/mail/ngx_mail_ssl_module.h +++ b/src/mail/ngx_mail_ssl_module.h @@ -21,6 +21,7 @@ typedef struct { ngx_flag_t prefer_server_ciphers; + ngx_flag_t certificate_compression; ngx_ssl_t ssl; diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c index 2f1b99624..7ce1175f1 100644 --- a/src/stream/ngx_stream_ssl_module.c +++ b/src/stream/ngx_stream_ssl_module.c @@ -133,6 +133,13 @@ static ngx_command_t ngx_stream_ssl_commands[] = { 0, NULL }, + { ngx_string("ssl_certificate_compression"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, certificate_compression), + NULL }, + { ngx_string("ssl_dhparam"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, @@ -881,6 +888,7 @@ ngx_stream_ssl_create_srv_conf(ngx_conf_t *cf) sscf->passwords = NGX_CONF_UNSET_PTR; sscf->conf_commands = NGX_CONF_UNSET_PTR; sscf->prefer_server_ciphers = NGX_CONF_UNSET; + sscf->certificate_compression = NGX_CONF_UNSET; sscf->reject_handshake = NGX_CONF_UNSET; sscf->verify = NGX_CONF_UNSET_UINT; sscf->verify_depth = NGX_CONF_UNSET_UINT; @@ -914,6 +922,9 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->prefer_server_ciphers, prev->prefer_server_ciphers, 0); + ngx_conf_merge_value(conf->certificate_compression, + prev->certificate_compression, 0); + ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0); ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, @@ -1039,6 +1050,13 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) { return NGX_CONF_ERROR; } + + if (ngx_ssl_certificate_compression(cf, &conf->ssl, + conf->certificate_compression) + != NGX_OK) + { + return NGX_CONF_ERROR; + } } if (conf->verify) { diff --git a/src/stream/ngx_stream_ssl_module.h b/src/stream/ngx_stream_ssl_module.h index ffa03a6f3..31f138cfd 100644 --- a/src/stream/ngx_stream_ssl_module.h +++ b/src/stream/ngx_stream_ssl_module.h @@ -18,6 +18,7 @@ typedef struct { ngx_msec_t handshake_timeout; ngx_flag_t prefer_server_ciphers; + ngx_flag_t certificate_compression; ngx_flag_t reject_handshake; ngx_ssl_t ssl; From noreply at nginx.com Fri Aug 22 17:02:21 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:21 +0000 (UTC) Subject: [nginx] nginx-1.29.1-RELEASE Message-ID: <20250822170221.609083F6A8@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/0024724f2f77ac4fa0d7394e859608d6844a5914 branches: master commit: 0024724f2f77ac4fa0d7394e859608d6844a5914 user: Sergey Kandaurov date: Tue, 12 Aug 2025 18:46:04 +0400 description: nginx-1.29.1-RELEASE --- docs/xml/nginx/changes.xml | 85 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml index 45b8d4efe..132a8d835 100644 --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -5,6 +5,91 @@ + + + + +теперь сжатие сертификатов в протоколе TLSv1.3 по умолчанию запрещено. + + +now TLSv1.3 certificate compression is disabled by default. + + + + + +директива ssl_certificate_compression. + + +the "ssl_certificate_compression" directive. + + + + + +поддержка 0-RTT в QUIC при использовании OpenSSL 3.5.1 и новее. + + +support for 0-RTT in QUIC when using OpenSSL 3.5.1 or newer. + + + + + +при использовании HTTP/2 и директивы early_hints +ответ 103 мог буферизироваться. + + +the 103 response might be buffered +when using HTTP/2 and the "early_hints" directive. + + + + + +в обработке заголовков запроса "Host" и ":authority" +с одинаковыми значениями при использовании HTTP/2; +ошибка появилась в 1.17.9. + + +in handling "Host" and ":authority" header lines +with equal values when using HTTP/2; +the bug had appeared in 1.17.9. + + + + + +в обработке заголовка запроса "Host" с портом +при использовании HTTP/3. + + +in handling "Host" header lines with a port +when using HTTP/3. + + + + + +nginx не собирался под NetBSD 10.0. + + +nginx could not be built on NetBSD 10.0. + + + + + +в работе параметра none директивы smtp_auth. + + +in the "none" parameter of the "smtp_auth" directive. + + + + + + From noreply at nginx.com Fri Aug 22 17:02:10 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:10 +0000 (UTC) Subject: [nginx] HTTP/3: improved invalid ":authority" error message. Message-ID: <20250822170210.82A1C3F6AB@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/372659114ed9b7a406093890ec2bdf437925ce64 branches: master commit: 372659114ed9b7a406093890ec2bdf437925ce64 user: Sergey Kandaurov date: Wed, 30 Jul 2025 17:43:44 +0400 description: HTTP/3: improved invalid ":authority" error message. --- src/http/v3/ngx_http_v3_request.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 32b11b598..a52910860 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -965,7 +965,7 @@ ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r) if (rc == NGX_DECLINED) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent invalid host in request line"); + "client sent invalid \":authority\" header"); goto failed; } From noreply at nginx.com Fri Aug 22 17:02:20 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:20 +0000 (UTC) Subject: [njs] Modules: added configure time check when "js_import" is not defined. Message-ID: <20250822170220.7CB553F6A8@pubserv1.nginx> details: https://github.com/nginx/njs/commit/d157f564d665d806d4b5072c59d3e4760eddb966 branches: master commit: d157f564d665d806d4b5072c59d3e4760eddb966 user: Dmitry Volyntsev date: Mon, 11 Aug 2025 23:26:12 -0700 description: Modules: added configure time check when "js_import" is not defined. --- nginx/ngx_http_js_module.c | 91 ++++++++++++++++++++++++++++++++++++++++++- nginx/ngx_js.h | 6 ++- nginx/ngx_stream_js_module.c | 93 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 184 insertions(+), 6 deletions(-) diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c index 45ddf17e..578aec42 100644 --- a/nginx/ngx_http_js_module.c +++ b/nginx/ngx_http_js_module.c @@ -40,6 +40,9 @@ typedef struct { ngx_log_t log; ngx_http_log_ctx_t log_ctx; ngx_event_t event; + + u_char *file_name; + ngx_uint_t line; } ngx_js_periodic_t; @@ -7688,12 +7691,61 @@ ngx_http_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf) static ngx_int_t ngx_http_js_init(ngx_conf_t *cf) { + ngx_uint_t i, found_issue; + ngx_js_set_t *data; + ngx_hash_key_t *key; + ngx_http_variable_t *v; + ngx_js_periodic_t *periodic; + ngx_js_loc_conf_t *jlcf; + ngx_js_main_conf_t *jmcf; + ngx_http_core_main_conf_t *cmcf; + ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_js_header_filter; ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_js_body_filter; + jmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_js_module); + jlcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_js_module); + + if (jlcf->engine == NULL) { + found_issue = 0; + + if (jmcf->periodics != NULL) { + periodic = jmcf->periodics->elts; + + for (i = 0; i < jmcf->periodics->nelts; i++) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"js_import\" directives found for " + "\"js_periodic\" \"%V\" in %s:%ui", + &periodic[i].method, periodic[i].file_name, + periodic[i].line); + } + + found_issue = 1; + } + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + key = cmcf->variables_keys->keys.elts; + + for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) { + v = key[i].value; + if (v->get_handler == ngx_http_js_variable_set) { + data = (ngx_js_set_t *) v->data; + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"js_import\" directives found for " + "\"js_set\" \"$%V\" \"%V\" in %s:%ui", &v->name, + &data->fname, data->file_name, data->line); + found_issue = 1; + } + } + + if (found_issue) { + return NGX_ERROR; + } + } + return NGX_OK; } @@ -7892,6 +7944,8 @@ invalid: periodic->jitter = jitter; periodic->worker_affinity = mask; periodic->conf_ctx = cf->ctx; + periodic->file_name = cf->conf_file->file.name.data; + periodic->line = cf->conf_file->line; return NGX_CONF_OK; } @@ -7927,6 +7981,8 @@ ngx_http_js_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) data->fname = value[2]; data->flags = 0; + data->file_name = cf->conf_file->file.name.data; + data->line = cf->conf_file->line; if (v->get_handler == ngx_http_js_variable_set) { prev = (ngx_js_set_t *) v->data; @@ -8138,7 +8194,40 @@ ngx_http_js_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_uint_value(conf->buffer_type, prev->buffer_type, NGX_JS_STRING); - return ngx_js_merge_conf(cf, parent, child, ngx_http_js_init_conf_vm); + if (ngx_js_merge_conf(cf, parent, child, ngx_http_js_init_conf_vm) + != NGX_CONF_OK) + { + return NGX_CONF_ERROR; + } + + if (conf->content.len != 0) { + if (conf->imports == NGX_CONF_UNSET_PTR) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no imports defined for \"js_content\" \"%V\", " + "use \"js_import\" directive", &conf->content); + return NGX_CONF_ERROR; + } + } + + if (conf->header_filter.len != 0) { + if (conf->imports == NGX_CONF_UNSET_PTR) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no imports defined for \"js_header_filter\" \"%V\", " + "use \"js_import\" directive", &conf->header_filter); + return NGX_CONF_ERROR; + } + } + + if (conf->body_filter.len != 0) { + if (conf->imports == NGX_CONF_UNSET_PTR) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no imports defined for \"js_body_filter\" \"%V\", " + "use \"js_import\" directive", &conf->body_filter); + return NGX_CONF_ERROR; + } + } + + return NGX_CONF_OK; } diff --git a/nginx/ngx_js.h b/nginx/ngx_js.h index 99330f88..5a5a79b3 100644 --- a/nginx/ngx_js.h +++ b/nginx/ngx_js.h @@ -186,8 +186,10 @@ struct ngx_js_loc_conf_s { typedef struct { - ngx_str_t fname; - unsigned flags; + ngx_str_t fname; + unsigned flags; + u_char *file_name; + ngx_uint_t line; } ngx_js_set_t; diff --git a/nginx/ngx_stream_js_module.c b/nginx/ngx_stream_js_module.c index 328ce581..396a71e2 100644 --- a/nginx/ngx_stream_js_module.c +++ b/nginx/ngx_stream_js_module.c @@ -46,6 +46,9 @@ typedef struct { ngx_log_t log; ngx_event_t event; + + u_char *file_name; + ngx_uint_t line; } ngx_js_periodic_t; @@ -3392,6 +3395,8 @@ invalid: periodic->jitter = jitter; periodic->worker_affinity = mask; periodic->conf_ctx = cf->ctx; + periodic->file_name = cf->conf_file->file.name.data; + periodic->line = cf->conf_file->line; return NGX_CONF_OK; } @@ -3425,6 +3430,8 @@ ngx_stream_js_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } data->fname = value[2]; + data->file_name = cf->conf_file->file.name.data; + data->line = cf->conf_file->line; if (v->get_handler == ngx_stream_js_variable_set) { prev = (ngx_js_set_t *) v->data; @@ -3562,7 +3569,40 @@ ngx_stream_js_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_str_value(conf->preread, prev->preread, ""); ngx_conf_merge_str_value(conf->filter, prev->filter, ""); - return ngx_js_merge_conf(cf, parent, child, ngx_stream_js_init_conf_vm); + if (ngx_js_merge_conf(cf, parent, child, ngx_stream_js_init_conf_vm) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + if (conf->access.len != 0) { + if (conf->imports == NGX_CONF_UNSET_PTR) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no imports defined for \"js_access\" \"%V\", " + "use \"js_import\" directive", &conf->access); + return NGX_CONF_ERROR; + } + } + + if (conf->preread.len != 0) { + if (conf->imports == NGX_CONF_UNSET_PTR) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no imports defined for \"js_preread\" \"%V\", " + "use \"js_import\" directive", &conf->preread); + return NGX_CONF_ERROR; + } + } + + if (conf->filter.len != 0) { + if (conf->imports == NGX_CONF_UNSET_PTR) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no imports defined for \"js_filter\" \"%V\", " + "use \"js_import\" directive", &conf->filter); + return NGX_CONF_ERROR; + } + } + + return NGX_CONF_OK; } @@ -3577,8 +3617,16 @@ ngx_stream_js_shared_dict_zone(ngx_conf_t *cf, ngx_command_t *cmd, static ngx_int_t ngx_stream_js_init(ngx_conf_t *cf) { - ngx_stream_handler_pt *h; - ngx_stream_core_main_conf_t *cmcf; + ngx_uint_t i; + ngx_flag_t found_issue; + ngx_js_set_t *data; + ngx_hash_key_t *key; + ngx_stream_variable_t *v; + ngx_js_periodic_t *periodic; + ngx_js_loc_conf_t *jlcf; + ngx_js_main_conf_t *jmcf; + ngx_stream_handler_pt *h; + ngx_stream_core_main_conf_t *cmcf; ngx_stream_next_filter = ngx_stream_top_filter; ngx_stream_top_filter = ngx_stream_js_body_filter; @@ -3599,6 +3647,45 @@ ngx_stream_js_init(ngx_conf_t *cf) *h = ngx_stream_js_preread_handler; + jmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_js_module); + jlcf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_js_module); + + if (jlcf->engine == NULL) { + found_issue = 0; + + if (jmcf->periodics != NULL) { + periodic = jmcf->periodics->elts; + + for (i = 0; i < jmcf->periodics->nelts; i++) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"js_import\" directives found for " + "\"js_periodic\" \"%V\" in %s:%ui", + &periodic[i].method, periodic[i].file_name, + periodic[i].line); + } + + found_issue = 1; + } + + key = cmcf->variables_keys->keys.elts; + + for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) { + v = key[i].value; + if (v->get_handler == ngx_stream_js_variable_set) { + data = (ngx_js_set_t *) v->data; + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"js_import\" directives found for " + "\"js_set\" \"$%V\" \"%V\" in %s:%ui", &v->name, + &data->fname, data->file_name, data->line); + found_issue = 1; + } + } + + if (found_issue) { + return NGX_ERROR; + } + } + return NGX_OK; } From noreply at nginx.com Fri Aug 22 17:02:10 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:10 +0000 (UTC) Subject: [nginx] Made ngx_http_process_request_header() static again. Message-ID: <20250822170210.7DEE33F6AA@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/4d857aaf435975d3c34e41d7a9cb50c0f87879ec branches: master commit: 4d857aaf435975d3c34e41d7a9cb50c0f87879ec user: Sergey Kandaurov date: Wed, 23 Jul 2025 15:56:37 +0400 description: Made ngx_http_process_request_header() static again. The function contains mostly HTTP/1.x specific request processing, which has no use in other protocols. After the previous change in HTTP/2, it can now be hidden. This is an API change. --- src/http/ngx_http.h | 1 - src/http/ngx_http_request.c | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h index 7d98f5cd7..922d3f4f9 100644 --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -122,7 +122,6 @@ ngx_int_t ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b, ngx_http_request_t *ngx_http_create_request(ngx_connection_t *c); ngx_int_t ngx_http_process_request_uri(ngx_http_request_t *r); -ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r); void ngx_http_process_request(ngx_http_request_t *r); void ngx_http_update_location_config(ngx_http_request_t *r); void ngx_http_handler(ngx_http_request_t *r); diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index ceac8d307..95cb1a133 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -29,6 +29,7 @@ static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r, static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r); static ngx_int_t ngx_http_find_virtual_server(ngx_connection_t *c, ngx_http_virtual_names_t *virtual_names, ngx_str_t *host, ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp); @@ -1984,7 +1985,7 @@ ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h, } -ngx_int_t +static ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r) { if (r->headers_in.server.len == 0 From vl at inspert.ru Fri Aug 22 07:49:57 2025 From: vl at inspert.ru (Vladimir Homutov) Date: Fri, 22 Aug 2025 10:49:57 +0300 Subject: stale SSL errors in quic In-Reply-To: References: Message-ID: On Thu, Aug 21, 2025 at 06:25:25PM +0400, Sergey Kandaurov wrote: > On Fri, Aug 15, 2025 at 10:56:40AM +0300, Vladimir Homutov wrote: > > Hello, > > > > Commits 7468a10b62276be4adee0fcd6aaf6244270984ab > > "QUIC: adjusted handling of callback errors." > > > > and 47f96993f669543c6cb4979dd3f680ad01314ee5 > > "QUIC: logging of SSL library errors." > > > > lead to the situation when you may get spurious > > "ignoring stale global SSL error" errors in unrelated connections. > > > > This happens due to the fact that openssl error queue is (thread) global, > > and quic handshake handler does not read out the error after the failed > > handshake and relies on qc->error set by callback. > > > > So the error stays in queue and may show itself in unrelated quic connection, > > typically on SSL shutdown. > > > > config below may be used to reproduce the issue: > > > > >>> > > daemon off; > > error_log logs/error.log debug; > > events { > > } > > > > http { > > ssl_certificate_key localhost.key; > > ssl_certificate localhost.crt; > > server { > > error_log logs/good.log debug; > > listen 127.0.0.1:8080 quic; > > location / { return 200 OK; } > > } > > server { > > error_log logs/reject.log debug; > > listen 127.0.0.1:8081 quic; > > ssl_reject_handshake on; > > location / { return 200 OK; } > > } > > } > > <<< > > > > start the server and run: > > > > $ curl -k --http3 https://127.0.0.1:8080/ > > $ curl -k --http3 https://127.0.0.1:8081/ > > > > The result is alert in good.log: > > > > 2025/08/15 09:58:19 [alert] 1154786#1154786: *1 ignoring stale global SSL error (SSL: error:10000084:SSL routines:OPENSSL_internal:CLIENTHELLO_TLSEXT error:100000be:SSL routines:OPENSSL_internal:PARSE_TLSEXT) while preparing ack, client: 127.0.0.1, server: 127.0.0.1:8080 > > > > caused by failed handshake in server 2; > > Thanks for reporting. > I was able to reproduce this with Test::Nginx reliably: > > my $s = Test::Nginx::HTTP3->new(8980); > my $s2 = Test::Nginx::HTTP3->new(8981, probe => 1); > > select undef, undef, undef, 0.1; > $s = undef; > > select undef, undef, undef, 0.2; > > $t->stop(); > > So the error queue remains non-empty until the 1st connection SSL shutdown. > The following happens as seen in debug (filtered): > > 2025/08/21 15:52:02 [debug] 73543#1490328: *1 quic run > 2025/08/21 15:52:02 [debug] 73543#1490328: *1 SSL_do_handshake: -1 > 2025/08/21 15:52:02 [debug] 73543#1490328: *1 SSL_do_handshake: 1 > 2025/08/21 15:52:02 [debug] 73543#1490328: *6 quic run > 2025/08/21 15:52:02 [debug] 73543#1490328: *6 quic ngx_quic_send_alert() level:0 alert:112 > 2025/08/21 15:52:02 [debug] 73543#1490328: *6 SSL_do_handshake: -1 > 2025/08/21 15:52:02 [alert] 73543#1490328: *1 ignoring stale global SSL error (SSL: error:10000084:SSL routines:OPENSSL_internal:CLIENTHELLO_TLSEXT error:100000be:SSL routines:OPENSSL_internal:PARSE_TLSEXT) while preparing ack, client: 127.0.0.1, server: 127.0.0.1:8980 > 2025/08/21 15:52:02 [debug] 73543#1490328: *1 quic ngx_quic_send_alert() level:3 alert:0 > 2025/08/21 15:52:02 [debug] 73543#1490328: *1 SSL_shutdown: 1 > > The above is using BoringSSL to match the report. > This is equivalently broken with all other libraries > with a slightly different error message: > > 2025/08/21 16:03:40 [alert] 78666#1507465: *1 ignoring stale global SSL error (SSL: error:0A0000EA:SSL routines::callback failed) while preparing ack, client: 127.0.0.1, server: 127.0.0.1:8980 > > I don't think 47f96993f669543c6cb4979dd3f680ad01314ee5 is related. > Although it formally changes reproduction surface by ngx_ssl_error(), > this touches errors, which should not normally happen in practice. > 7468a10b62276be4adee0fcd6aaf6244270984ab is a real culprit. > > > > > when the ngx_http_ssl_servername() callback returns error, > > the ngx_quic_send_alert() sets qc->error, and the ngx_quic_ssl_handshake() > > checks qc->error after SSL_do_handshake() and closes connection. > > But the error stays, and manifests itself later in another connection > > in the same process. > > > > The quic fix is probably something like this: > > > > diff --git a/src/event/quic/ngx_event_quic_ssl.c b/src/event/quic/ngx_event_quic_ssl.c > > index e961c80cd..dc0a030ff 100644 > > --- a/src/event/quic/ngx_event_quic_ssl.c > > +++ b/src/event/quic/ngx_event_quic_ssl.c > > @@ -696,6 +696,7 @@ ngx_quic_handshake(ngx_connection_t *c) > > ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); > > > > if (qc->error) { > > + ERR_clear_error(); > > return NGX_ERROR; > > } > > > > Or may be it makes sense to clear error in the moment of setting qc->error; > > and do not clear everything, but try to log using ngx_ssl_error() with some > > non-alert level. > > What happens here is the added early check for qc->error broke the > check for handshake_rejected, resulting in a missing diagnostics > for the SNI-rejected handshake, and in a missing ERR_clear_error(). > > This can be fixed by moving the qc->error check to later such that > handshake_rejected is checked first to make the error queue cleared. > Although not practicably visible as needed, this can be also accompanied > by clearing the error queue under the qc->error case as well, to be safe. > > Fixing this is complicated by different codes returned from SSL_get_error() > for handshake errors happened in QUIC callbacks in various SSL libraries: > > # define SSL_ERROR_SSL 1 > # define SSL_ERROR_WANT_READ 2 > # define SSL_ERROR_WANT_WRITE 3 > > compat 3.5 bssl qtls lssl > tp 2 3* 2 2 2 > sni 1 1 1 1 1 > > In this regard, checking c->ssl->handshake_rejected is moved out of > "sslerr != SSL_ERROR_WANT_READ". It is a set only flag, so this should > be safe to do. This also somewhat simplifies and streamlines code. > > Patch also reconstructs a missing "SSL_do_handshake() failed" diagnostics > for the qc->error case. It is made this way to avoid logging at the crit > log level because qc->error is expected to have an empty error queue. > > Please test if it works for you. > > diff --git a/src/event/quic/ngx_event_quic_ssl.c b/src/event/quic/ngx_event_quic_ssl.c > index e961c80cd..1de896291 100644 > --- a/src/event/quic/ngx_event_quic_ssl.c > +++ b/src/event/quic/ngx_event_quic_ssl.c > @@ -695,25 +695,25 @@ ngx_quic_handshake(ngx_connection_t *c) > > ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); > > - if (qc->error) { > - return NGX_ERROR; > - } > - > if (n <= 0) { > sslerr = SSL_get_error(ssl_conn, n); > > ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", > sslerr); > > - if (sslerr != SSL_ERROR_WANT_READ) { > - > - if (c->ssl->handshake_rejected) { > - ngx_connection_error(c, 0, "handshake rejected"); > - ERR_clear_error(); > + if (c->ssl->handshake_rejected) { > + ngx_connection_error(c, 0, "handshake rejected"); > + ERR_clear_error(); > + return NGX_ERROR; > + } > > - return NGX_ERROR; > - } > + if (qc->error) { > + ngx_connection_error(c, 0, "SSL_do_handshake() failed"); > + ERR_clear_error(); > + return NGX_ERROR; > + } > > + if (sslerr != SSL_ERROR_WANT_READ) { > ngx_ssl_connection_error(c, sslerr, 0, "SSL_do_handshake() failed"); > return NGX_ERROR; > } yes, this works and totally makes sense. thank you! From noreply at nginx.com Fri Aug 22 17:02:21 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:21 +0000 (UTC) Subject: [nginx] Updated OpenSSL used for win32 builds. Message-ID: <20250822170221.5249D3F6A7@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/cc1c07ca33865bb632ae9b48b13c51dbb5389483 branches: master commit: cc1c07ca33865bb632ae9b48b13c51dbb5389483 user: Sergey Kandaurov date: Wed, 13 Aug 2025 17:55:56 +0400 description: Updated OpenSSL used for win32 builds. --- misc/GNUmakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/GNUmakefile b/misc/GNUmakefile index 22eed135f..81b192156 100644 --- a/misc/GNUmakefile +++ b/misc/GNUmakefile @@ -6,7 +6,7 @@ TEMP = tmp CC = cl OBJS = objs.msvc8 -OPENSSL = openssl-3.5.0 +OPENSSL = openssl-3.5.2 ZLIB = zlib-1.3.1 PCRE = pcre2-10.45 From noreply at nginx.com Fri Aug 22 17:02:20 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:20 +0000 (UTC) Subject: [njs] Modules: fixed merging of js_path directives. Message-ID: <20250822170220.737423F6A7@pubserv1.nginx> details: https://github.com/nginx/njs/commit/75be83e267147dce17bbea4f59b198b04b1ebad3 branches: master commit: 75be83e267147dce17bbea4f59b198b04b1ebad3 user: Dmitry Volyntsev date: Mon, 11 Aug 2025 18:55:18 -0700 description: Modules: fixed merging of js_path directives. The issue initially appeared in 1b54abb (0.7.7). --- nginx/ngx_js.c | 4 ++-- nginx/t/js_paths.t | 23 ++++++++++++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c index 01d4bb2a..0678c3e6 100644 --- a/nginx/ngx_js.c +++ b/nginx/ngx_js.c @@ -3483,7 +3483,7 @@ ngx_js_merge_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, return NGX_ERROR; } - s = prev->imports->elts; + s = prev->paths->elts; for (i = 0; i < prev->paths->nelts; i++) { path = ngx_array_push(paths); @@ -3494,7 +3494,7 @@ ngx_js_merge_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, *path = s[i]; } - s = conf->imports->elts; + s = conf->paths->elts; for (i = 0; i < conf->paths->nelts; i++) { path = ngx_array_push(paths); diff --git a/nginx/t/js_paths.t b/nginx/t/js_paths.t index 98d87512..95fe07fc 100644 --- a/nginx/t/js_paths.t +++ b/nginx/t/js_paths.t @@ -51,6 +51,11 @@ http { location /test2 { js_content test.test2; } + + location /merge { + js_path "lib3"; + js_content test.test3; + } } } @@ -60,6 +65,7 @@ $t->write_file('test.js', <write_file('test.js', <testdir(); mkdir("$d/lib1"); mkdir("$d/lib2"); +mkdir("$d/lib3"); $t->write_file('lib1/module1.js', <write_file('lib2/module2.js', <write_file('lib3/module3.js', <try_run('no njs available')->plan(4); +$t->try_run('no njs available')->plan(5); ############################################################################### @@ -106,5 +122,6 @@ like(http_get('/test?fun=sum&a=3&b=4'), qr/7/s, 'test sum'); like(http_get('/test?fun=prod&a=3&b=4'), qr/12/s, 'test prod'); like(http_get('/test2?a=3&b=4'), qr/34/s, 'test2'); like(http_get('/test2?a=A&b=B'), qr/AB/s, 'test2 relative'); +like(http_get('/merge'), qr/Hello from lib3/s, 'test merge'); ############################################################################### From noreply at nginx.com Fri Aug 22 17:02:10 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:10 +0000 (UTC) Subject: [nginx] HTTP/2: factored out constructing the Host header. Message-ID: <20250822170210.71FD73F6A7@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/a238bb3d22c251647d04cf07b35c218994ab1ff5 branches: master commit: a238bb3d22c251647d04cf07b35c218994ab1ff5 user: Sergey Kandaurov date: Wed, 23 Jul 2025 14:32:34 +0400 description: HTTP/2: factored out constructing the Host header. No functional changes. --- src/http/v2/ngx_http_v2.c | 87 ++++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index 91a28b228..c9ef6d7ee 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -158,6 +158,8 @@ static ngx_int_t ngx_http_v2_construct_request_line(ngx_http_request_t *r); static ngx_int_t ngx_http_v2_cookie(ngx_http_request_t *r, ngx_http_v2_header_t *header); static ngx_int_t ngx_http_v2_construct_cookie_header(ngx_http_request_t *r); +static ngx_int_t ngx_http_v2_construct_host_header(ngx_http_request_t *r, + ngx_str_t *value); static void ngx_http_v2_run_request(ngx_http_request_t *r); static ngx_int_t ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos, size_t size, ngx_uint_t last, ngx_uint_t flush); @@ -3517,45 +3519,7 @@ ngx_http_v2_parse_scheme(ngx_http_request_t *r, ngx_str_t *value) static ngx_int_t ngx_http_v2_parse_authority(ngx_http_request_t *r, ngx_str_t *value) { - ngx_table_elt_t *h; - ngx_http_header_t *hh; - ngx_http_core_main_conf_t *cmcf; - - static ngx_str_t host = ngx_string("host"); - - h = ngx_list_push(&r->headers_in.headers); - if (h == NULL) { - return NGX_ERROR; - } - - h->hash = ngx_hash(ngx_hash(ngx_hash('h', 'o'), 's'), 't'); - - h->key.len = host.len; - h->key.data = host.data; - - h->value.len = value->len; - h->value.data = value->data; - - h->lowcase_key = host.data; - - cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); - - hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, - h->lowcase_key, h->key.len); - - if (hh == NULL) { - return NGX_ERROR; - } - - if (hh->handler(r, h, hh->offset) != NGX_OK) { - /* - * request has been finalized already - * in ngx_http_process_host() - */ - return NGX_ABORT; - } - - return NGX_OK; + return ngx_http_v2_construct_host_header(r, value); } @@ -3734,6 +3698,51 @@ ngx_http_v2_construct_cookie_header(ngx_http_request_t *r) } +static ngx_int_t +ngx_http_v2_construct_host_header(ngx_http_request_t *r, ngx_str_t *value) +{ + ngx_table_elt_t *h; + ngx_http_header_t *hh; + ngx_http_core_main_conf_t *cmcf; + + static ngx_str_t host = ngx_string("host"); + + h = ngx_list_push(&r->headers_in.headers); + if (h == NULL) { + return NGX_ERROR; + } + + h->hash = ngx_hash(ngx_hash(ngx_hash('h', 'o'), 's'), 't'); + + h->key.len = host.len; + h->key.data = host.data; + + h->value.len = value->len; + h->value.data = value->data; + + h->lowcase_key = host.data; + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, + h->lowcase_key, h->key.len); + + if (hh == NULL) { + return NGX_ERROR; + } + + if (hh->handler(r, h, hh->offset) != NGX_OK) { + /* + * request has been finalized already + * in ngx_http_process_host() + */ + return NGX_ABORT; + } + + return NGX_OK; +} + + static void ngx_http_v2_run_request(ngx_http_request_t *r) { From noreply at nginx.com Fri Aug 22 17:02:10 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:10 +0000 (UTC) Subject: [nginx] HTTP/2: fixed handling of the ":authority" header. Message-ID: <20250822170210.780383F6A8@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/ede5623b1529131fcc3f994e6a6f0692954fa26b branches: master commit: ede5623b1529131fcc3f994e6a6f0692954fa26b user: Sergey Kandaurov date: Wed, 23 Jul 2025 14:54:07 +0400 description: HTTP/2: fixed handling of the ":authority" header. Previously, it misused the Host header processing resulting in 400 (Bad Request) errors for a valid request that contains both ":authority" and Host headers with the same value, treating it after 37984f0be as if client sent more than one Host header. Such an overly strict handling violates RFC 9113. The fix is to process ":authority" as a distinct header, similarly to processing an authority component in the HTTP/1.x request line. This allows to disambiguate and compare Host and ":authority" values after all headers were processed. With this change, the ngx_http_process_request_header() function can no longer be used here, certain parts were inlined similar to the HTTP/3 module. To provide compatibility for misconfigurations that use $http_host to return the value of the ":authority" header, the Host header, if missing, is now reconstructed from ":authority". --- src/http/v2/ngx_http_v2.c | 120 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 105 insertions(+), 15 deletions(-) diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index c9ef6d7ee..dba4477d5 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -158,8 +158,7 @@ static ngx_int_t ngx_http_v2_construct_request_line(ngx_http_request_t *r); static ngx_int_t ngx_http_v2_cookie(ngx_http_request_t *r, ngx_http_v2_header_t *header); static ngx_int_t ngx_http_v2_construct_cookie_header(ngx_http_request_t *r); -static ngx_int_t ngx_http_v2_construct_host_header(ngx_http_request_t *r, - ngx_str_t *value); +static ngx_int_t ngx_http_v2_construct_host_header(ngx_http_request_t *r); static void ngx_http_v2_run_request(ngx_http_request_t *r); static ngx_int_t ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos, size_t size, ngx_uint_t last, ngx_uint_t flush); @@ -3519,7 +3518,41 @@ ngx_http_v2_parse_scheme(ngx_http_request_t *r, ngx_str_t *value) static ngx_int_t ngx_http_v2_parse_authority(ngx_http_request_t *r, ngx_str_t *value) { - return ngx_http_v2_construct_host_header(r, value); + ngx_int_t rc; + + if (r->host_start) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent duplicate \":authority\" header"); + return NGX_DECLINED; + } + + r->host_start = value->data; + r->host_end = value->data + value->len; + + rc = ngx_http_validate_host(value, r->pool, 0); + + if (rc == NGX_DECLINED) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid \":authority\" header"); + return NGX_DECLINED; + } + + if (rc == NGX_ERROR) { + ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ABORT; + } + + if (ngx_http_set_virtual_server(r, value) == NGX_ERROR) { + /* + * request has been finalized already + * in ngx_http_set_virtual_server() + */ + return NGX_ABORT; + } + + r->headers_in.server = *value; + + return NGX_OK; } @@ -3699,7 +3732,7 @@ ngx_http_v2_construct_cookie_header(ngx_http_request_t *r) static ngx_int_t -ngx_http_v2_construct_host_header(ngx_http_request_t *r, ngx_str_t *value) +ngx_http_v2_construct_host_header(ngx_http_request_t *r) { ngx_table_elt_t *h; ngx_http_header_t *hh; @@ -3709,6 +3742,7 @@ ngx_http_v2_construct_host_header(ngx_http_request_t *r, ngx_str_t *value) h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { + ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_ERROR; } @@ -3717,8 +3751,8 @@ ngx_http_v2_construct_host_header(ngx_http_request_t *r, ngx_str_t *value) h->key.len = host.len; h->key.data = host.data; - h->value.len = value->len; - h->value.data = value->data; + h->value.len = r->host_end - r->host_start; + h->value.data = r->host_start; h->lowcase_key = host.data; @@ -3728,6 +3762,7 @@ ngx_http_v2_construct_host_header(ngx_http_request_t *r, ngx_str_t *value) h->lowcase_key, h->key.len); if (hh == NULL) { + ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_ERROR; } @@ -3736,7 +3771,7 @@ ngx_http_v2_construct_host_header(ngx_http_request_t *r, ngx_str_t *value) * request has been finalized already * in ngx_http_process_host() */ - return NGX_ABORT; + return NGX_ERROR; } return NGX_OK; @@ -3746,6 +3781,7 @@ ngx_http_v2_construct_host_header(ngx_http_request_t *r, ngx_str_t *value) static void ngx_http_v2_run_request(ngx_http_request_t *r) { + ngx_str_t host; ngx_connection_t *fc; ngx_http_v2_srv_conf_t *h2scf; ngx_http_v2_connection_t *h2c; @@ -3773,24 +3809,78 @@ ngx_http_v2_run_request(ngx_http_request_t *r) r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE; - if (ngx_http_process_request_header(r) != NGX_OK) { + if (r->headers_in.server.len == 0) { + ngx_log_error(NGX_LOG_INFO, fc->log, 0, + "client sent neither \":authority\" nor \"Host\" header"); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); goto failed; } - if (r->headers_in.content_length_n > 0 && r->stream->in_closed) { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client prematurely closed stream"); + if (r->host_end) { - r->stream->skip_data = 1; + host.len = r->host_end - r->host_start; + host.data = r->host_start; - ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); - goto failed; + if (r->headers_in.host) { + if (r->headers_in.host->value.len != host.len + || ngx_memcmp(r->headers_in.host->value.data, host.data, + host.len) + != 0) + { + ngx_log_error(NGX_LOG_INFO, fc->log, 0, + "client sent \":authority\" and \"Host\" headers " + "with different values"); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + goto failed; + } + + } else { + /* compatibility for $http_host */ + + if (ngx_http_v2_construct_host_header(r) != NGX_OK) { + goto failed; + } + } } - if (r->headers_in.content_length_n == -1 && !r->stream->in_closed) { + if (r->headers_in.content_length) { + r->headers_in.content_length_n = + ngx_atoof(r->headers_in.content_length->value.data, + r->headers_in.content_length->value.len); + + if (r->headers_in.content_length_n == NGX_ERROR) { + ngx_log_error(NGX_LOG_INFO, fc->log, 0, + "client sent invalid \"Content-Length\" header"); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + goto failed; + } + + if (r->headers_in.content_length_n > 0 && r->stream->in_closed) { + ngx_log_error(NGX_LOG_INFO, fc->log, 0, + "client prematurely closed stream"); + + r->stream->skip_data = 1; + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + goto failed; + } + + } else if (!r->stream->in_closed) { r->headers_in.chunked = 1; } + if (r->method == NGX_HTTP_CONNECT) { + ngx_log_error(NGX_LOG_INFO, fc->log, 0, "client sent CONNECT method"); + ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED); + goto failed; + } + + if (r->method == NGX_HTTP_TRACE) { + ngx_log_error(NGX_LOG_INFO, fc->log, 0, "client sent TRACE method"); + ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED); + goto failed; + } + h2c = r->stream->connection; h2c->payload_bytes += r->request_length; From noreply at nginx.com Fri Aug 22 17:02:19 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:19 +0000 (UTC) Subject: [nginx] Mail: improved error handling in plain/login/cram-md5 auth methods. Message-ID: <20250822170219.9A0523F6A7@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/765642b86e0df1b5ef37f42522be7d08d95909c9 branches: master commit: 765642b86e0df1b5ef37f42522be7d08d95909c9 user: Sergey Kandaurov date: Tue, 12 Aug 2025 15:55:02 +0400 description: Mail: improved error handling in plain/login/cram-md5 auth methods. Previously, login and password storage could be left in inconsistent state in a session after decoding errors. --- src/mail/ngx_mail_handler.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/mail/ngx_mail_handler.c b/src/mail/ngx_mail_handler.c index 1167df3fb..d3be7f3b3 100644 --- a/src/mail/ngx_mail_handler.c +++ b/src/mail/ngx_mail_handler.c @@ -523,7 +523,7 @@ ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c) ngx_int_t ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n) { - u_char *p, *last; + u_char *p, *pos, *last; ngx_str_t *arg, plain; arg = s->args.elts; @@ -555,7 +555,7 @@ ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n) return NGX_MAIL_PARSE_INVALID_COMMAND; } - s->login.data = p; + pos = p; while (p < last && *p) { p++; } @@ -565,7 +565,8 @@ ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n) return NGX_MAIL_PARSE_INVALID_COMMAND; } - s->login.len = p++ - s->login.data; + s->login.len = p++ - pos; + s->login.data = pos; s->passwd.len = last - p; s->passwd.data = p; @@ -583,24 +584,26 @@ ngx_int_t ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n) { - ngx_str_t *arg; + ngx_str_t *arg, login; arg = s->args.elts; ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "mail auth login username: \"%V\"", &arg[n]); - s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len)); - if (s->login.data == NULL) { + login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len)); + if (login.data == NULL) { return NGX_ERROR; } - if (ngx_decode_base64(&s->login, &arg[n]) != NGX_OK) { + if (ngx_decode_base64(&login, &arg[n]) != NGX_OK) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid base64 encoding in AUTH LOGIN command"); return NGX_MAIL_PARSE_INVALID_COMMAND; } + s->login = login; + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "mail auth login username: \"%V\"", &s->login); @@ -611,7 +614,7 @@ ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c, ngx_int_t ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c) { - ngx_str_t *arg; + ngx_str_t *arg, passwd; arg = s->args.elts; @@ -620,18 +623,19 @@ ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c) "mail auth login password: \"%V\"", &arg[0]); #endif - s->passwd.data = ngx_pnalloc(c->pool, - ngx_base64_decoded_length(arg[0].len)); - if (s->passwd.data == NULL) { + passwd.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len)); + if (passwd.data == NULL) { return NGX_ERROR; } - if (ngx_decode_base64(&s->passwd, &arg[0]) != NGX_OK) { + if (ngx_decode_base64(&passwd, &arg[0]) != NGX_OK) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid base64 encoding in AUTH LOGIN command"); return NGX_MAIL_PARSE_INVALID_COMMAND; } + s->passwd = passwd; + #if (NGX_DEBUG_MAIL_PASSWD) ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "mail auth login password: \"%V\"", &s->passwd); @@ -674,24 +678,26 @@ ngx_int_t ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c) { u_char *p, *last; - ngx_str_t *arg; + ngx_str_t *arg, login; arg = s->args.elts; ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "mail auth cram-md5: \"%V\"", &arg[0]); - s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len)); - if (s->login.data == NULL) { + login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len)); + if (login.data == NULL) { return NGX_ERROR; } - if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) { + if (ngx_decode_base64(&login, &arg[0]) != NGX_OK) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid base64 encoding in AUTH CRAM-MD5 command"); return NGX_MAIL_PARSE_INVALID_COMMAND; } + s->login = login; + p = s->login.data; last = p + s->login.len; From noreply at nginx.com Fri Aug 22 17:02:19 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:19 +0000 (UTC) Subject: [nginx] Mail: reset stale auth credentials with "smtp_auth none;". Message-ID: <20250822170219.9EFE93F6A8@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/9c02c84a7443f3d736a1a5eb3f596de9af8a0c9c branches: master commit: 9c02c84a7443f3d736a1a5eb3f596de9af8a0c9c user: Sergey Kandaurov date: Mon, 7 Jul 2025 23:48:44 +0400 description: Mail: reset stale auth credentials with "smtp_auth none;". They might be reused in a session if an SMTP client proceeded unauthenticated after previous invalid authentication attempts. This could confuse an authentication server when passing stale credentials along with "Auth-Method: none". The condition to send the "Auth-Salt" header is similarly refined. --- src/mail/ngx_mail_auth_http_module.c | 5 ++++- src/mail/ngx_mail_smtp_handler.c | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/mail/ngx_mail_auth_http_module.c b/src/mail/ngx_mail_auth_http_module.c index 27f64b92e..4ca6d6e24 100644 --- a/src/mail/ngx_mail_auth_http_module.c +++ b/src/mail/ngx_mail_auth_http_module.c @@ -1321,7 +1321,10 @@ ngx_mail_auth_http_create_request(ngx_mail_session_t *s, ngx_pool_t *pool, b->last = ngx_copy(b->last, passwd.data, passwd.len); *b->last++ = CR; *b->last++ = LF; - if (s->auth_method != NGX_MAIL_AUTH_PLAIN && s->salt.len) { + if ((s->auth_method == NGX_MAIL_AUTH_APOP + || s->auth_method == NGX_MAIL_AUTH_CRAM_MD5) + && s->salt.len) + { b->last = ngx_cpymem(b->last, "Auth-Salt: ", sizeof("Auth-Salt: ") - 1); b->last = ngx_copy(b->last, s->salt.data, s->salt.len); diff --git a/src/mail/ngx_mail_smtp_handler.c b/src/mail/ngx_mail_smtp_handler.c index e68ceedfd..1e26c2c8d 100644 --- a/src/mail/ngx_mail_smtp_handler.c +++ b/src/mail/ngx_mail_smtp_handler.c @@ -782,6 +782,9 @@ ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c) ngx_str_set(&s->out, smtp_ok); + ngx_str_null(&s->login); + ngx_str_null(&s->passwd); + return NGX_OK; } From noreply at nginx.com Fri Aug 22 17:02:15 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:15 +0000 (UTC) Subject: [njs] Change: increasing the default stack size to 160k. Message-ID: <20250822170215.903F03F6A7@pubserv1.nginx> details: https://github.com/nginx/njs/commit/972951a4286101776c3a4cee2a33ac3dd4be4ec9 branches: master commit: 972951a4286101776c3a4cee2a33ac3dd4be4ec9 user: Dmitry Volyntsev date: Mon, 11 Aug 2025 16:25:47 -0700 description: Change: increasing the default stack size to 160k. This change allows EarleyBoyer benchmark from arewefastyet/benchmarks/v8-v7 to pass with default settings. Previous commit 5e9a6d5 (v0.7.9) reduced the stack size to prevent stack overflow when compiling with -O0 in computed goto mode, where native stack frames for njs_vmcode_interpreter() consume ~80KB each. To address this, we now set a lower maximum stack size specifically for unit tests. This fixes #939 issue on Github. --- src/njs_vm.h | 2 +- src/test/njs_unit_test.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/njs_vm.h b/src/njs_vm.h index 3351dee4..46f566e3 100644 --- a/src/njs_vm.h +++ b/src/njs_vm.h @@ -8,7 +8,7 @@ #define _NJS_VM_H_INCLUDED_ -#define NJS_MAX_STACK_SIZE (64 * 1024) +#define NJS_MAX_STACK_SIZE (160 * 1024) typedef struct njs_frame_s njs_frame_t; diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 78e12197..67c57d19 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -21856,6 +21856,7 @@ njs_unit_test(njs_unit_test_t tests[], size_t num, njs_str_t *name, options.module = opts->module; options.unsafe = opts->unsafe; options.backtrace = opts->backtrace; + options.max_stack_size = 64 * 1024; options.addons = opts->externals ? njs_unit_test_addon_external_modules : njs_unit_test_addon_modules; @@ -22008,6 +22009,7 @@ njs_interactive_test(njs_unit_test_t tests[], size_t num, njs_str_t *name, options.init = 1; options.interactive = 1; options.backtrace = 1; + options.max_stack_size = 64 * 1024; options.addons = opts->externals ? njs_unit_test_addon_external_modules : njs_unit_test_addon_modules; From noreply at nginx.com Fri Aug 22 17:02:19 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:19 +0000 (UTC) Subject: [nginx] Mail: logging upstream to the error log with "smtp_auth none; ". Message-ID: <20250822170219.A38DB3F6AA@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/239e10793adb1e32847095ba6c1d14249bf19a5c branches: master commit: 239e10793adb1e32847095ba6c1d14249bf19a5c user: Sergey Kandaurov date: Mon, 21 Jul 2025 17:44:28 +0400 description: Mail: logging upstream to the error log with "smtp_auth none;". Previously, it was never logged because of missing login. --- src/mail/ngx_mail_handler.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/mail/ngx_mail_handler.c b/src/mail/ngx_mail_handler.c index d3be7f3b3..a88e6c287 100644 --- a/src/mail/ngx_mail_handler.c +++ b/src/mail/ngx_mail_handler.c @@ -1006,14 +1006,12 @@ ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len) len -= p - buf; buf = p; - if (s->login.len == 0) { - return p; + if (s->login.len) { + p = ngx_snprintf(buf, len, ", login: \"%V\"", &s->login); + len -= p - buf; + buf = p; } - p = ngx_snprintf(buf, len, ", login: \"%V\"", &s->login); - len -= p - buf; - buf = p; - if (s->proxy == NULL) { return p; } From noreply at nginx.com Fri Aug 22 17:02:14 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:14 +0000 (UTC) Subject: [nginx] Auth basic: fixed file descriptor leak on memory allocation error. Message-ID: <20250822170214.C26F03F6A7@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/034f15bbc251ed72018d8396e7eeb3bf30fd789b branches: master commit: 034f15bbc251ed72018d8396e7eeb3bf30fd789b user: Sergey Kandaurov date: Fri, 8 Aug 2025 19:44:27 +0400 description: Auth basic: fixed file descriptor leak on memory allocation error. Found by Coverity (CID 1662016). --- src/http/modules/ngx_http_auth_basic_module.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/http/modules/ngx_http_auth_basic_module.c b/src/http/modules/ngx_http_auth_basic_module.c index 02d41e88a..69e8d2161 100644 --- a/src/http/modules/ngx_http_auth_basic_module.c +++ b/src/http/modules/ngx_http_auth_basic_module.c @@ -253,7 +253,8 @@ ngx_http_auth_basic_handler(ngx_http_request_t *r) pwd.len = i - passwd; pwd.data = ngx_pnalloc(r->pool, pwd.len + 1); if (pwd.data == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; + rc = NGX_HTTP_INTERNAL_SERVER_ERROR; + goto cleanup; } ngx_cpystrn(pwd.data, &buf[passwd], pwd.len + 1); From noreply at nginx.com Fri Aug 22 17:02:23 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:23 +0000 (UTC) Subject: [nginx] Version bump. Message-ID: <20250822170223.C31523F6A7@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/36d40e5610ff07b422db62bec687c06608a0bd84 branches: master commit: 36d40e5610ff07b422db62bec687c06608a0bd84 user: Sergey Kandaurov date: Wed, 13 Aug 2025 20:07:38 +0400 description: Version bump. --- src/core/nginx.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/nginx.h b/src/core/nginx.h index cf230af64..2db393e62 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1029001 -#define NGINX_VERSION "1.29.1" +#define nginx_version 1029002 +#define NGINX_VERSION "1.29.2" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD From noreply at nginx.com Fri Aug 22 17:02:23 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:23 +0000 (UTC) Subject: [nginx] Added a previously missed changes entry in 1.29.1 relnotes. Message-ID: <20250822170223.CFBD63F6A8@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/1a82df8cca80458fc3da0968f64624f40cafdf37 branches: master commit: 1a82df8cca80458fc3da0968f64624f40cafdf37 user: Sergey Kandaurov date: Wed, 13 Aug 2025 20:26:56 +0400 description: Added a previously missed changes entry in 1.29.1 relnotes. --- docs/xml/nginx/changes.xml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml index 132a8d835..c07de09b7 100644 --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -7,6 +7,21 @@ + + +обработка специально созданного логина/пароля при использовании +метода аутентификации "none" в модуле ngx_mail_smtp_module +могла приводить к отправке серверу аутентификации +части содержимого памяти рабочего процесса (CVE-2025-53859). + + +processing of a specially crafted login/password when using +the "none" authentication method in the ngx_mail_smtp_module +might cause worker process memory disclosure +to the authentication server (CVE-2025-53859). + + + теперь сжатие сертификатов в протоколе TLSv1.3 по умолчанию запрещено. From noreply at nginx.com Fri Aug 22 17:02:13 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:13 +0000 (UTC) Subject: [njs] Added CLA workflow. Message-ID: <20250822170214.013963F6A7@pubserv1.nginx> details: https://github.com/nginx/njs/commit/d93680e26b6f60226a0cc89fcc48db72d68a226c branches: master commit: d93680e26b6f60226a0cc89fcc48db72d68a226c user: Vadim Zhestikov date: Thu, 7 Aug 2025 17:41:09 -0700 description: Added CLA workflow. --- .github/workflows/f5_cla.yml | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/.github/workflows/f5_cla.yml b/.github/workflows/f5_cla.yml new file mode 100644 index 00000000..43e473ea --- /dev/null +++ b/.github/workflows/f5_cla.yml @@ -0,0 +1,41 @@ +--- +name: F5 CLA +on: + issue_comment: + types: [created] + pull_request_target: + types: [opened, closed, synchronize] +permissions: read-all +jobs: + f5-cla: + name: F5 CLA + runs-on: ubuntu-24.04 + permissions: + actions: write + pull-requests: write + statuses: write + steps: + - name: Run F5 Contributor License Agreement (CLA) assistant + if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have hereby read the F5 CLA and agree to its terms') || github.event_name == 'pull_request_target' + uses: contributor-assistant/github-action at ca4a40a7d1004f18d9960b404b97e5f30a505a08 # v2.6.1 + with: + # Path to the CLA document. + path-to-document: https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md + # Custom CLA messages. + custom-notsigned-prcomment: '🎉 Thank you for your contribution! It appears you have not yet signed the [F5 Contributor License Agreement (CLA)](https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md), which is required for your changes to be incorporated into an F5 Open Source Software (OSS) project. Please kindly read the [F5 CLA](https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md) and reply on a new comment with the following text to agree:' + custom-pr-sign-comment: 'I have hereby read the F5 CLA and agree to its terms' + custom-allsigned-prcomment: '✅ All required contributors have signed the F5 CLA for this PR. Thank you!' + # Remote repository storing CLA signatures. + remote-organization-name: f5 + remote-repository-name: f5-cla-data + # Branch where CLA signatures are stored. + branch: main + path-to-signatures: signatures/signatures.json + # Comma separated list of usernames for maintainers or any other individuals who should not be prompted for a CLA. + # NOTE: You will want to edit the usernames to suit your project needs. + allowlist: bot* + # Do not lock PRs after a merge. + lock-pullrequest-aftermerge: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PERSONAL_ACCESS_TOKEN: ${{ secrets.F5_CLA_TOKEN }} From noreply at nginx.com Fri Aug 22 17:02:10 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:10 +0000 (UTC) Subject: [nginx] Updated ngx_http_process_multi_header_lines() comments. Message-ID: <20250822170210.8890F3F6AC@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/f4005126d78d19f1efd4f8fb4cad916d8976d97a branches: master commit: f4005126d78d19f1efd4f8fb4cad916d8976d97a user: Sergey Kandaurov date: Thu, 31 Jul 2025 21:31:27 +0400 description: Updated ngx_http_process_multi_header_lines() comments. Missed in fcf4331a0. --- src/http/v2/ngx_http_v2.c | 2 +- src/http/v3/ngx_http_v3_request.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index dba4477d5..13856583f 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -3722,7 +3722,7 @@ ngx_http_v2_construct_cookie_header(ngx_http_request_t *r) if (hh->handler(r, h, hh->offset) != NGX_OK) { /* * request has been finalized already - * in ngx_http_process_multi_header_lines() + * in ngx_http_process_header_line() */ return NGX_ERROR; } diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index a52910860..844a4000a 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -1216,7 +1216,7 @@ ngx_http_v3_construct_cookie_header(ngx_http_request_t *r) if (hh->handler(r, h, hh->offset) != NGX_OK) { /* * request has been finalized already - * in ngx_http_process_multi_header_lines() + * in ngx_http_process_header_line() */ return NGX_ERROR; } From noreply at nginx.com Fri Aug 22 17:02:22 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:22 +0000 (UTC) Subject: [nginx] Annotated tag created: release-1.29.1 Message-ID: <20250822170222.2F9473F6A7@pubserv1.nginx> details: https://github.com/nginx/nginx/releases/tag/release-1.29.1 branches: commit: 0024724f2f77ac4fa0d7394e859608d6844a5914 user: Sergey Kandaurov date: Wed Aug 13 18:35:13 2025 +0400 description: release-1.29.1 tag From noreply at nginx.com Fri Aug 22 17:02:04 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Aug 2025 17:02:04 +0000 (UTC) Subject: [nginx] HTTP/2: fixed flushing early hints over SSL. Message-ID: <20250822170204.D39913F6A7@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/50932c3c6c1d2c442ef08241443162c7a3ed58b3 branches: master commit: 50932c3c6c1d2c442ef08241443162c7a3ed58b3 user: Roman Arutyunyan date: Thu, 24 Jul 2025 18:29:21 +0400 description: HTTP/2: fixed flushing early hints over SSL. Previously, when using HTTP/2 over SSL, an early hints HEADERS frame was queued in SSL buffer, and might not be immediately flushed. This resulted in a delay of early hints delivery until the main response was sent. The fix is to set the flush flag for the early hints HEADERS frame buffer. --- src/http/v2/ngx_http_v2_filter_module.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c index 907906a88..6b73b1e68 100644 --- a/src/http/v2/ngx_http_v2_filter_module.c +++ b/src/http/v2/ngx_http_v2_filter_module.c @@ -32,7 +32,8 @@ static ngx_int_t ngx_http_v2_early_hints_filter(ngx_http_request_t *r); static ngx_int_t ngx_http_v2_init_stream(ngx_http_request_t *r); static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame( - ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin); + ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin, + ngx_uint_t flush); static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame( ngx_http_request_t *r); @@ -609,7 +610,7 @@ ngx_http_v2_header_filter(ngx_http_request_t *r) fin = r->header_only || (r->headers_out.content_length_n == 0 && !r->expect_trailers); - frame = ngx_http_v2_create_headers_frame(r, start, pos, fin); + frame = ngx_http_v2_create_headers_frame(r, start, pos, fin, 0); if (frame == NULL) { return NGX_ERROR; } @@ -774,7 +775,7 @@ ngx_http_v2_early_hints_filter(ngx_http_request_t *r) header[i].value.len, tmp); } - frame = ngx_http_v2_create_headers_frame(r, start, pos, 0); + frame = ngx_http_v2_create_headers_frame(r, start, pos, 0, 1); if (frame == NULL) { return NGX_ERROR; } @@ -825,7 +826,7 @@ ngx_http_v2_init_stream(ngx_http_request_t *r) static ngx_http_v2_out_frame_t * ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos, - u_char *end, ngx_uint_t fin) + u_char *end, ngx_uint_t fin, ngx_uint_t flush) { u_char type, flags; size_t rest, frame_size; @@ -916,6 +917,7 @@ ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos, } b->last_buf = fin; + b->flush = flush; cl->next = NULL; frame->last = cl; @@ -1038,7 +1040,7 @@ ngx_http_v2_create_trailers_frame(ngx_http_request_t *r) header[i].value.len, tmp); } - return ngx_http_v2_create_headers_frame(r, start, pos, 1); + return ngx_http_v2_create_headers_frame(r, start, pos, 1, 0); } From noreply at nginx.com Wed Aug 27 01:59:29 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 27 Aug 2025 01:59:29 +0000 (UTC) Subject: [nginx] Removed legacy charset directive from default config example. Message-ID: <20250827015929.AC7CD3F91A@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/446ce033e5b9e192e228638e826f2a39328d879c branches: master commit: 446ce033e5b9e192e228638e826f2a39328d879c user: Mohamed Karrab date: Mon, 18 Aug 2025 20:28:06 +0100 description: Removed legacy charset directive from default config example. The example configuration previously specified 'charset koi8-r', which is a legacy Cyrillic encoding. As koi8-r is rarely used today and modern browsers handle UTF-8 by default, specifying the charset explicitly is unnecessary. Removing the directive keeps the example configuration concise and aligned with current best practices. --- conf/nginx.conf | 2 -- 1 file changed, 2 deletions(-) diff --git a/conf/nginx.conf b/conf/nginx.conf index 29bc085f2..2315a8663 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -36,8 +36,6 @@ http { listen 80; server_name localhost; - #charset koi8-r; - #access_log logs/host.access.log main; location / { From noreply at nginx.com Wed Aug 27 01:59:39 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 27 Aug 2025 01:59:39 +0000 (UTC) Subject: [njs] Modules: fixed incorrect config rejections introduced in d157f56. Message-ID: <20250827015939.6B29F3F91A@pubserv1.nginx> details: https://github.com/nginx/njs/commit/2c93ca079f497b68ea1680d282f705c926cf7210 branches: master commit: 2c93ca079f497b68ea1680d282f705c926cf7210 user: Dmitry Volyntsev date: Fri, 15 Aug 2025 17:28:47 -0700 description: Modules: fixed incorrect config rejections introduced in d157f56. d157f56 introduced configure time checks for js_set and js_periodic directives. It turned out to be too strict. The fix is to remove them completely as there is no way to track variable usage context in configure time. --- nginx/ngx_http_js_module.c | 66 +++++------------------- nginx/ngx_stream_js_module.c | 64 +++++------------------ nginx/t/js_variables_location.t | 96 +++++++++++++++++++++++++++++++++++ nginx/t/stream_js_variables_server.t | 98 ++++++++++++++++++++++++++++++++++++ 4 files changed, 218 insertions(+), 106 deletions(-) diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c index 578aec42..402c7382 100644 --- a/nginx/ngx_http_js_module.c +++ b/nginx/ngx_http_js_module.c @@ -40,9 +40,6 @@ typedef struct { ngx_log_t log; ngx_http_log_ctx_t log_ctx; ngx_event_t event; - - u_char *file_name; - ngx_uint_t line; } ngx_js_periodic_t; @@ -1540,6 +1537,9 @@ ngx_http_js_variable_set(ngx_http_request_t *r, ngx_http_variable_value_t *v, } if (rc == NGX_DECLINED) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no \"js_import\" directives found for \"js_set\" handler" + " \"%V\" in the current scope", fname); v->not_found = 1; return NGX_OK; } @@ -4485,6 +4485,15 @@ ngx_http_js_periodic_handler(ngx_event_t *ev) rc = ngx_http_js_init_vm(r, ngx_http_js_periodic_session_proto_id); + if (rc == NGX_DECLINED) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no \"js_import\" directives found for \"js_periodic\"" + " handler \"%V\" in the current scope", + &periodic->method); + ngx_http_js_periodic_destroy(r, periodic); + return; + } + if (rc != NGX_OK) { ngx_http_js_periodic_destroy(r, periodic); return; @@ -7691,61 +7700,12 @@ ngx_http_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf) static ngx_int_t ngx_http_js_init(ngx_conf_t *cf) { - ngx_uint_t i, found_issue; - ngx_js_set_t *data; - ngx_hash_key_t *key; - ngx_http_variable_t *v; - ngx_js_periodic_t *periodic; - ngx_js_loc_conf_t *jlcf; - ngx_js_main_conf_t *jmcf; - ngx_http_core_main_conf_t *cmcf; - ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_js_header_filter; ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_js_body_filter; - jmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_js_module); - jlcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_js_module); - - if (jlcf->engine == NULL) { - found_issue = 0; - - if (jmcf->periodics != NULL) { - periodic = jmcf->periodics->elts; - - for (i = 0; i < jmcf->periodics->nelts; i++) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "no \"js_import\" directives found for " - "\"js_periodic\" \"%V\" in %s:%ui", - &periodic[i].method, periodic[i].file_name, - periodic[i].line); - } - - found_issue = 1; - } - - cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); - key = cmcf->variables_keys->keys.elts; - - for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) { - v = key[i].value; - if (v->get_handler == ngx_http_js_variable_set) { - data = (ngx_js_set_t *) v->data; - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "no \"js_import\" directives found for " - "\"js_set\" \"$%V\" \"%V\" in %s:%ui", &v->name, - &data->fname, data->file_name, data->line); - found_issue = 1; - } - } - - if (found_issue) { - return NGX_ERROR; - } - } - return NGX_OK; } @@ -7944,8 +7904,6 @@ invalid: periodic->jitter = jitter; periodic->worker_affinity = mask; periodic->conf_ctx = cf->ctx; - periodic->file_name = cf->conf_file->file.name.data; - periodic->line = cf->conf_file->line; return NGX_CONF_OK; } diff --git a/nginx/ngx_stream_js_module.c b/nginx/ngx_stream_js_module.c index 396a71e2..a3a85662 100644 --- a/nginx/ngx_stream_js_module.c +++ b/nginx/ngx_stream_js_module.c @@ -46,9 +46,6 @@ typedef struct { ngx_log_t log; ngx_event_t event; - - u_char *file_name; - ngx_uint_t line; } ngx_js_periodic_t; @@ -1068,6 +1065,9 @@ ngx_stream_js_variable_set(ngx_stream_session_t *s, } if (rc == NGX_DECLINED) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "no \"js_import\" directives found for \"js_set\" handler" + " \"%V\" in the current scope", fname); v->not_found = 1; return NGX_OK; } @@ -3061,6 +3061,15 @@ ngx_stream_js_periodic_handler(ngx_event_t *ev) rc = ngx_stream_js_init_vm(s, ngx_stream_js_periodic_session_proto_id); + if (rc == NGX_DECLINED) { + ngx_log_error(NGX_LOG_ERR, &periodic->log, 0, + "no \"js_import\" directives found for \"js_periodic\"" + " handler \"%V\" in the current scope", + &periodic->method); + ngx_stream_js_periodic_destroy(s, periodic); + return; + } + if (rc != NGX_OK) { ngx_stream_js_periodic_destroy(s, periodic); return; @@ -3395,8 +3404,6 @@ invalid: periodic->jitter = jitter; periodic->worker_affinity = mask; periodic->conf_ctx = cf->ctx; - periodic->file_name = cf->conf_file->file.name.data; - periodic->line = cf->conf_file->line; return NGX_CONF_OK; } @@ -3617,14 +3624,6 @@ ngx_stream_js_shared_dict_zone(ngx_conf_t *cf, ngx_command_t *cmd, static ngx_int_t ngx_stream_js_init(ngx_conf_t *cf) { - ngx_uint_t i; - ngx_flag_t found_issue; - ngx_js_set_t *data; - ngx_hash_key_t *key; - ngx_stream_variable_t *v; - ngx_js_periodic_t *periodic; - ngx_js_loc_conf_t *jlcf; - ngx_js_main_conf_t *jmcf; ngx_stream_handler_pt *h; ngx_stream_core_main_conf_t *cmcf; @@ -3647,45 +3646,6 @@ ngx_stream_js_init(ngx_conf_t *cf) *h = ngx_stream_js_preread_handler; - jmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_js_module); - jlcf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_js_module); - - if (jlcf->engine == NULL) { - found_issue = 0; - - if (jmcf->periodics != NULL) { - periodic = jmcf->periodics->elts; - - for (i = 0; i < jmcf->periodics->nelts; i++) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "no \"js_import\" directives found for " - "\"js_periodic\" \"%V\" in %s:%ui", - &periodic[i].method, periodic[i].file_name, - periodic[i].line); - } - - found_issue = 1; - } - - key = cmcf->variables_keys->keys.elts; - - for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) { - v = key[i].value; - if (v->get_handler == ngx_stream_js_variable_set) { - data = (ngx_js_set_t *) v->data; - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "no \"js_import\" directives found for " - "\"js_set\" \"$%V\" \"%V\" in %s:%ui", &v->name, - &data->fname, data->file_name, data->line); - found_issue = 1; - } - } - - if (found_issue) { - return NGX_ERROR; - } - } - return NGX_OK; } diff --git a/nginx/t/js_variables_location.t b/nginx/t/js_variables_location.t new file mode 100644 index 00000000..02bd85ef --- /dev/null +++ b/nginx/t/js_variables_location.t @@ -0,0 +1,96 @@ +#!/usr/bin/perl + +# (C) Dmitry Volyntsev +# (C) Nginx, Inc. + +# Tests for http njs module, setting nginx variables, location js_import. + +############################################################################### + +use warnings; +use strict; + +use Test::More; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new()->has(qw/http rewrite/) + ->write_file_expand('nginx.conf', <<'EOF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +http { + %%TEST_GLOBALS_HTTP%% + + server { + listen 127.0.0.1:8080; + server_name localhost; + + location /foo { + js_import main from foo.js; + js_set $test_var main.variable; + + return 200 $test_var; + } + + location /bar { + js_import main from bar.js; + js_set $test_var main.variable; + + return 200 $test_var; + } + + location /not_found { + return 200 "NOT_FOUND:$test_var"; + } + } +} + +EOF + +$t->write_file('foo.js', <write_file('bar.js', <try_run('no njs')->plan(4); + +############################################################################### + +like(http_get('/foo'), qr/foo_var/, 'foo var'); +like(http_get('/bar'), qr/bar_var/, 'bar var'); +like(http_get('/not_found'), qr/NOT_FOUND:$/, 'not found is empty'); + +$t->stop(); + +ok(index($t->read_file('error.log'), + 'no "js_import" directives found for "js_set" handler "main.variable" ' + . 'in the current scope') > 0, 'log error for js_set without js_import'); + +############################################################################### diff --git a/nginx/t/stream_js_variables_server.t b/nginx/t/stream_js_variables_server.t new file mode 100644 index 00000000..a7d53718 --- /dev/null +++ b/nginx/t/stream_js_variables_server.t @@ -0,0 +1,98 @@ +#!/usr/bin/perl + +# (C) Dmitry Volyntsev +# (C) Nginx, Inc. + +# Tests for stream njs module, setting nginx variables, server js_import. + +############################################################################### + +use warnings; +use strict; + +use Test::More; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; +use Test::Nginx::Stream qw/ stream /; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new()->has(qw/stream stream_return/) + ->write_file_expand('nginx.conf', <<'EOF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +stream { + %%TEST_GLOBALS_STREAM%% + + server { + listen 127.0.0.1:8081; + + js_import main from foo.js; + js_set $test_var main.variable; + + return $test_var; + } + + server { + listen 127.0.0.1:8082; + + js_import main from bar.js; + js_set $test_var main.variable; + + return $test_var; + } + + server { + listen 127.0.0.1:8083; + + return "NOT_FOUND:$test_var"; + } +} + +EOF + +$t->write_file('foo.js', <write_file('bar.js', <try_run('no stream njs available')->plan(4); + +############################################################################### + +is(stream('127.0.0.1:' . port(8081))->read(), 'foo_var', 'foo var'); +is(stream('127.0.0.1:' . port(8082))->read(), 'bar_var', 'bar var'); +is(stream('127.0.0.1:' . port(8083))->read(), 'NOT_FOUND:', 'not found var'); + +$t->stop(); + +ok(index($t->read_file('error.log'), + 'no "js_import" directives found for "js_set" handler "main.variable" ' + . 'in the current scope') > 0, 'log error for js_set without js_import'); + +############################################################################### From noreply at nginx.com Wed Aug 27 01:59:41 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 27 Aug 2025 01:59:41 +0000 (UTC) Subject: [njs] Fixed building QuickJS support with clang 19. Message-ID: <20250827015941.023F93F91A@pubserv1.nginx> details: https://github.com/nginx/njs/commit/8259f9a2ee8d5c0f7ef45ccf563984a9ad95a9db branches: master commit: 8259f9a2ee8d5c0f7ef45ccf563984a9ad95a9db user: Dmitry Volyntsev date: Mon, 25 Aug 2025 16:47:43 -0700 description: Fixed building QuickJS support with clang 19. checking for QuickJS library -lquickjs In file included from build/autotest.c:6: /home/xeioex/workspace/nginx/nginScript/quickjs/quickjs.h:1052:34: error: cast from 'JSCFunctionMagic *' (aka 'struct JSValue (*)(struct JSContext *, struct JSValue, int, struct JSValue *, int)') to 'JSCFunction *' (aka 'struct JSValue (*)(struct JSContext *, struct JSValue, int, struct JSValue *)') converts to incompatible function type [-Werror,-Wcast-function-type-mismatch] 1052 | return JS_NewCFunction2(ctx, (JSCFunction *)func, name, length, cproto, magic); -Wcast-function-type-mismatch become enabled by -Werror since clang 19. --- auto/quickjs | 61 ++++++++++++++---------------------------------------------- nginx/config | 16 ++++++---------- src/qjs.h | 10 ++++++++-- 3 files changed, 28 insertions(+), 59 deletions(-) diff --git a/auto/quickjs b/auto/quickjs index 60c0888e..cbf860a2 100644 --- a/auto/quickjs +++ b/auto/quickjs @@ -5,6 +5,7 @@ NJS_QUICKJS_LIB= NJS_HAVE_QUICKJS=NO +NJS_QUICKJS_DEFAULT_INCS="src $NJS_BUILD_DIR" if [ $NJS_TRY_QUICKJS = YES ]; then njs_found=no @@ -12,14 +13,9 @@ if [ $NJS_TRY_QUICKJS = YES ]; then njs_feature="QuickJS library -lquickjs.lto" njs_feature_name=NJS_HAVE_QUICKJS njs_feature_run=yes - njs_feature_incs= + njs_feature_incs="$NJS_QUICKJS_DEFAULT_INCS" njs_feature_libs="-lquickjs.lto -lm -ldl -lpthread" - njs_feature_test="#if defined(__GNUC__) && (__GNUC__ >= 8) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored \"-Wcast-function-type\" - #endif - - #include + njs_feature_test="#include int main() { JSRuntime *rt; @@ -39,7 +35,7 @@ if [ $NJS_TRY_QUICKJS = YES ]; then if [ $njs_found = no ]; then njs_feature="QuickJS library -I/usr/include/quickjs/ -L/usr/lib/quickjs/ -lquickjs.lto" - njs_feature_incs="/usr/include/quickjs/" + njs_feature_incs="$NJS_QUICKJS_DEFAULT_INCS /usr/include/quickjs/" njs_feature_libs="-L/usr/lib/quickjs/ -lquickjs.lto -lm -ldl -lpthread" . auto/feature @@ -47,7 +43,7 @@ if [ $NJS_TRY_QUICKJS = YES ]; then if [ $njs_found = no ]; then njs_feature="QuickJS library -I/usr/include/quickjs/ -L/usr/lib/quickjs/ -lquickjs" - njs_feature_incs="/usr/include/quickjs/" + njs_feature_incs="$NJS_QUICKJS_DEFAULT_INCS /usr/include/quickjs/" njs_feature_libs="-L/usr/lib/quickjs/ -lquickjs -lm -ldl -lpthread" . auto/feature @@ -55,7 +51,7 @@ if [ $NJS_TRY_QUICKJS = YES ]; then if [ $njs_found = no ]; then njs_feature="QuickJS-NG library -lqjs" - njs_feature_incs="" + njs_feature_incs="$NJS_QUICKJS_DEFAULT_INCS" njs_feature_libs="-lqjs -lm -ldl -lpthread" . auto/feature @@ -66,12 +62,7 @@ if [ $NJS_TRY_QUICKJS = YES ]; then njs_feature="QuickJS JS_GetClassID()" njs_feature_name=NJS_HAVE_QUICKJS_GET_CLASS_ID - njs_feature_test="#if defined(__GNUC__) && (__GNUC__ >= 8) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored \"-Wcast-function-type\" - #endif - - #include + njs_feature_test="#include int main() { (void) JS_GetClassID(JS_UNDEFINED); @@ -89,12 +80,7 @@ if [ $NJS_TRY_QUICKJS = YES ]; then njs_feature="QuickJS JS_NewTypedArray()" njs_feature_name=NJS_HAVE_QUICKJS_NEW_TYPED_ARRAY - njs_feature_test="#if defined(__GNUC__) && (__GNUC__ >= 8) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored \"-Wcast-function-type\" - #endif - - #include + njs_feature_test="#include int main() { JSValue ta, argv; @@ -116,12 +102,7 @@ if [ $NJS_TRY_QUICKJS = YES ]; then njs_feature="QuickJS JS_IsSameValue()" njs_feature_name=NJS_HAVE_QUICKJS_IS_SAME_VALUE - njs_feature_test="#if defined(__GNUC__) && (__GNUC__ >= 8) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored \"-Wcast-function-type\" - #endif - - #include + njs_feature_test="#include int main() { JSRuntime *rt; @@ -139,12 +120,7 @@ if [ $NJS_TRY_QUICKJS = YES ]; then njs_feature="QuickJS JS_IsArray()" njs_feature_name=NJS_HAVE_QUICKJS_IS_ARRAY_SINGLE_ARG - njs_feature_test="#if defined(__GNUC__) && (__GNUC__ >= 8) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored \"-Wcast-function-type\" - #endif - - #include + njs_feature_test="#include int main() { JSRuntime *rt; @@ -162,12 +138,7 @@ if [ $NJS_TRY_QUICKJS = YES ]; then njs_feature="QuickJS JS_AddIntrinsicBigInt()" njs_feature_name=NJS_HAVE_QUICKJS_ADD_INTRINSIC_BIG_INT - njs_feature_test="#if defined(__GNUC__) && (__GNUC__ >= 8) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored \"-Wcast-function-type\" - #endif - - #include + njs_feature_test="#include int main() { JSRuntime *rt; @@ -186,12 +157,7 @@ if [ $NJS_TRY_QUICKJS = YES ]; then njs_feature="QuickJS version" njs_feature_name=NJS_QUICKJS_VERSION njs_feature_run=value - njs_feature_test="#if defined(__GNUC__) && (__GNUC__ >= 8) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored \"-Wcast-function-type\" - #endif - - #include + njs_feature_test="#include int main() { #if defined(QJS_VERSION_MAJOR) @@ -206,7 +172,8 @@ if [ $NJS_TRY_QUICKJS = YES ]; then NJS_HAVE_QUICKJS=YES NJS_QUICKJS_LIB="$njs_feature_libs" - NJS_LIB_INCS="$NJS_LIB_INCS $njs_feature_incs" + NJS_QUICKJS_INCS=`echo "$njs_feature_incs" | sed -e "s|^$NJS_QUICKJS_DEFAULT_INCS||"` + NJS_LIB_INCS="$NJS_LIB_INCS $NJS_QUICKJS_INCS" NJS_LIB_AUX_LIBS="$NJS_LIB_AUX_LIBS $njs_feature_libs" fi diff --git a/nginx/config b/nginx/config index 1c303d9c..474be8f3 100644 --- a/nginx/config +++ b/nginx/config @@ -23,6 +23,7 @@ NJS_XSLT_LIB= NJS_ZLIB_LIB= NJS_QUICKJS_LIB= NJS_QUICKJS_INC= +NJS_QUICKJS_DEFAULT_INCS="$ngx_addon_dir/../src $ngx_addon_dir/../build" NJS_HAVE_QUICKJS= if [ $NJS_QUICKJS != NO ]; then @@ -30,13 +31,8 @@ if [ $NJS_QUICKJS != NO ]; then ngx_feature="QuickJS library -lquickjs.lto" ngx_feature_name=NJS_HAVE_QUICKJS ngx_feature_run=yes - ngx_feature_incs="#if defined(__GNUC__) && (__GNUC__ >= 8) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored \"-Wcast-function-type\" - #endif - - #include " - ngx_feature_path="" + ngx_feature_incs="#include " + ngx_feature_path="$NJS_QUICKJS_DEFAULT_INCS" ngx_feature_libs="-lquickjs.lto -lm -ldl -lpthread" ngx_feature_test="JSRuntime *rt; @@ -54,7 +50,7 @@ if [ $NJS_QUICKJS != NO ]; then if [ $ngx_found = no ]; then ngx_feature="QuickJS library -I/usr/include/quickjs/ -L/usr/lib/quickjs/ -lquickjs.lto" - ngx_feature_path="/usr/include/quickjs/" + ngx_feature_path="$NJS_QUICKJS_DEFAULT_INCS /usr/include/quickjs/" ngx_feature_libs="-L/usr/lib/quickjs/ -lquickjs.lto -lm -ldl -lpthread" . auto/feature @@ -69,7 +65,7 @@ if [ $NJS_QUICKJS != NO ]; then if [ $ngx_found = no ]; then ngx_feature="QuickJS-NG library -lqjs" - ngx_feature_path="" + ngx_feature_path="$NJS_QUICKJS_DEFAULT_INCS" ngx_feature_libs="-lqjs -lm -ldl -lpthread" . auto/feature @@ -108,7 +104,7 @@ if [ $NJS_QUICKJS != NO ]; then NJS_HAVE_QUICKJS=YES NJS_QUICKJS_LIB="$ngx_feature_libs" - NJS_QUICKJS_INC="$ngx_feature_path" + NJS_QUICKJS_INC=`echo "$ngx_feature_path" | sed -e "s|^$NJS_QUICKJS_DEFAULT_INCS||"` echo " enabled QuickJS engine" fi diff --git a/src/qjs.h b/src/qjs.h index e920453e..954cc23c 100644 --- a/src/qjs.h +++ b/src/qjs.h @@ -20,7 +20,12 @@ #include #include -#if defined(__GNUC__) && (__GNUC__ >= 8) +#ifndef __has_warning +# define __has_warning(x) 0 +#endif + +#if (defined(__GNUC__) && (__GNUC__ >= 8)) \ + || (defined(__clang__) && __has_warning("-Wcast-function-type")) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-function-type" #endif @@ -31,7 +36,8 @@ #define JS_BOOL bool #endif -#if defined(__GNUC__) && (__GNUC__ >= 8) +#if (defined(__GNUC__) && (__GNUC__ >= 8)) \ + || (defined(__clang__) && __has_warning("-Wcast-function-type")) #pragma GCC diagnostic pop #endif #include From noreply at nginx.com Wed Aug 27 01:59:41 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 27 Aug 2025 01:59:41 +0000 (UTC) Subject: [njs] Modules: removing extra copy while streaming in qjs. Message-ID: <20250827015941.BFB363F91B@pubserv1.nginx> details: https://github.com/nginx/njs/commit/6842e1b8f2cf73243afdcb88b27d08fdaa861ad6 branches: master commit: 6842e1b8f2cf73243afdcb88b27d08fdaa861ad6 user: Dmitry Volyntsev date: Wed, 13 Aug 2025 23:19:46 -0700 description: Modules: removing extra copy while streaming in qjs. --- nginx/ngx_http_js_module.c | 12 +----------- nginx/ngx_stream_js_module.c | 13 +------------ 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c index 402c7382..487c8115 100644 --- a/nginx/ngx_http_js_module.c +++ b/nginx/ngx_http_js_module.c @@ -7398,7 +7398,6 @@ ngx_http_qjs_body_filter(ngx_http_request_t *r, ngx_http_js_loc_conf_t *jlcf, ngx_http_js_ctx_t *ctx, ngx_chain_t *in) { size_t len; - u_char *p; JSAtom last_key; JSValue arguments[3], last; ngx_int_t rc; @@ -7425,16 +7424,7 @@ ngx_http_qjs_body_filter(ngx_http_request_t *r, ngx_http_js_loc_conf_t *jlcf, if (!ctx->done) { len = b->last - b->pos; - p = ngx_pnalloc(r->pool, len); - if (p == NULL) { - return NJS_ERROR; - } - - if (len) { - ngx_memcpy(p, b->pos, len); - } - - arguments[1] = ngx_qjs_prop(cx, jlcf->buffer_type, p, len); + arguments[1] = ngx_qjs_prop(cx, jlcf->buffer_type, b->pos, len); if (JS_IsException(arguments[1])) { JS_FreeAtom(cx, last_key); return NGX_ERROR; diff --git a/nginx/ngx_stream_js_module.c b/nginx/ngx_stream_js_module.c index a3a85662..a7446c49 100644 --- a/nginx/ngx_stream_js_module.c +++ b/nginx/ngx_stream_js_module.c @@ -2638,7 +2638,6 @@ ngx_stream_qjs_run_event(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, ngx_stream_js_ev_t *event, ngx_uint_t from_upstream) { size_t len; - u_char *p; JSContext *cx; ngx_int_t rc; ngx_str_t exception; @@ -2658,17 +2657,7 @@ ngx_stream_qjs_run_event(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, len = b ? b->last - b->pos : 0; - p = ngx_pnalloc(c->pool, len); - if (p == NULL) { - (void) JS_ThrowOutOfMemory(cx); - goto error; - } - - if (len) { - ngx_memcpy(p, b->pos, len); - } - - argv[0] = ngx_qjs_prop(cx, event->data_type, p, len); + argv[0] = ngx_qjs_prop(cx, event->data_type, b ? b->pos : NULL, len); if (JS_IsException(argv[0])) { goto error; } From noreply at nginx.com Wed Aug 27 01:59:41 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 27 Aug 2025 01:59:41 +0000 (UTC) Subject: [njs] Tests: added TypedArray test for js_filter in stream. Message-ID: <20250827015941.B7AF23F91A@pubserv1.nginx> details: https://github.com/nginx/njs/commit/b6fd8ebc8b5bdc36ba1147cfd51bfedc72fc6a3b branches: master commit: b6fd8ebc8b5bdc36ba1147cfd51bfedc72fc6a3b user: Dmitry Volyntsev date: Tue, 19 Aug 2025 17:16:22 -0700 description: Tests: added TypedArray test for js_filter in stream. --- nginx/t/stream_js_buffer.t | 77 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/nginx/t/stream_js_buffer.t b/nginx/t/stream_js_buffer.t index cc960136..be6569f2 100644 --- a/nginx/t/stream_js_buffer.t +++ b/nginx/t/stream_js_buffer.t @@ -91,6 +91,12 @@ stream { js_filter test.header_inject; proxy_pass 127.0.0.1:8080; } + + server { + listen 127.0.0.1:8086; + js_filter test.typed_array_send; + proxy_pass 127.0.0.1:8090; + } } EOF @@ -150,12 +156,26 @@ $t->write_file('test.js', <try_run('no njs ngx')->plan(5); +$t->run_daemon(\&stream_daemon, port(8090)); +$t->try_run('no njs ngx')->plan(6); +$t->waitforsocket('127.0.0.1:' . port(8090)); ############################################################################### @@ -167,6 +187,9 @@ stream('127.0.0.1:' . port(8084))->io('x'); like(http_get('/p/return'), qr/RETURN:foo/, 'injected header'); +is(stream('127.0.0.1:' . port(8086))->io('x', length => 6), 'ellllo', + 'typed array send'); + $t->stop(); ok(index($t->read_file('error.log'), 'cb_mismatch:mixing string and buffer') @@ -175,3 +198,53 @@ ok(index($t->read_file('error.log'), 'cb_mismatch2:mixing string and buffer') > 0, 'cb mismatch'); ############################################################################### + +sub stream_daemon { + my $server = IO::Socket::INET->new( + Proto => 'tcp', + LocalAddr => '127.0.0.1:' . port(8090), + Listen => 5, + Reuse => 1 + ) + or die "Can't create listening socket: $!\n"; + + local $SIG{PIPE} = 'IGNORE'; + + while (my $client = $server->accept()) { + $client->autoflush(1); + + log2c("(new connection $client)"); + + while (1) { + my $bytes_read = $client->sysread(my $buffer, 65536); + + if (!defined $bytes_read) { + log2c("Read error from $client: $!"); + last; + + } elsif ($bytes_read == 0) { + log2c("Client $client disconnected"); + last; + } + + log2i("$client $buffer"); + + my $bytes_written = $client->syswrite($buffer); + + if (!defined $bytes_written || $bytes_written != length($buffer)) { + log2c("Write error to $client: $!"); + last; + } + + log2o("$client $buffer"); + } + + close $client; + } +} + +sub log2i { Test::Nginx::log_core('|| <<', @_); } +sub log2o { Test::Nginx::log_core('|| >>', @_); } +sub log2c { Test::Nginx::log_core('||', @_); } + +############################################################################### From noreply at nginx.com Wed Aug 27 01:59:41 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 27 Aug 2025 01:59:41 +0000 (UTC) Subject: [njs] Modules: optimized memory consumption while streaming in qjs. Message-ID: <20250827015941.C872D3F91C@pubserv1.nginx> details: https://github.com/nginx/njs/commit/44744d957b9958f35f18805a5acfa564e66af35f branches: master commit: 44744d957b9958f35f18805a5acfa564e66af35f user: Dmitry Volyntsev date: Mon, 18 Aug 2025 21:21:09 -0700 description: Modules: optimized memory consumption while streaming in qjs. This allows to stream long tcp streams or large http response bodies with low memory consumption. This works only for qjs engine, because njs has no GC. This fixes #943 issue on Github. --- nginx/ngx_http_js_module.c | 115 +++++++++++++++++++++++++++++++------- nginx/ngx_stream_js_module.c | 130 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 200 insertions(+), 45 deletions(-) diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c index 487c8115..3f38f86f 100644 --- a/nginx/ngx_http_js_module.c +++ b/nginx/ngx_http_js_module.c @@ -5601,10 +5601,12 @@ static JSValue ngx_http_qjs_ext_send_buffer(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { + size_t byte_offset, byte_length, len; unsigned last_buf, flush; - JSValue flags, value; + JSValue flags, value, val, buf; ngx_str_t buffer; ngx_buf_t *b; + const char *str; ngx_chain_t *cl; ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; @@ -5620,10 +5622,6 @@ ngx_http_qjs_ext_send_buffer(JSContext *cx, JSValueConst this_val, return JS_ThrowTypeError(cx, "cannot send buffer while not filtering"); } - if (ngx_qjs_string(cx, argv[0], &buffer) != NGX_OK) { - return JS_ThrowTypeError(cx, "failed get buffer arg"); - } - flush = ctx->buf->flush; last_buf = ctx->buf->last_buf; @@ -5647,29 +5645,106 @@ ngx_http_qjs_ext_send_buffer(JSContext *cx, JSValueConst this_val, JS_FreeValue(cx, value); } - cl = ngx_chain_get_free_buf(r->pool, &ctx->free); - if (cl == NULL) { - return JS_ThrowOutOfMemory(cx); + val = argv[0]; + + if (JS_IsNullOrUndefined(val)) { + buffer.len = 0; + buffer.data = NULL; } - b = cl->buf; + str = NULL; + buf = JS_UNDEFINED; - b->flush = flush; - b->last_buf = last_buf; + if (JS_IsString(val)) { + goto string; + } - b->memory = (buffer.len ? 1 : 0); - b->sync = (buffer.len ? 0 : 1); - b->tag = (ngx_buf_tag_t) &ngx_http_js_module; + buf = JS_GetTypedArrayBuffer(cx, val, &byte_offset, &byte_length, NULL); + if (!JS_IsException(buf)) { + buffer.data = JS_GetArrayBuffer(cx, &buffer.len, buf); + if (buffer.data == NULL) { + JS_FreeValue(cx, buf); + return JS_EXCEPTION; + } - b->start = buffer.data; - b->end = buffer.data + buffer.len; - b->pos = b->start; - b->last = b->end; + buffer.data += byte_offset; + buffer.len = byte_length; - *ctx->last_out = cl; - ctx->last_out = &cl->next; + } else { +string: + + str = JS_ToCStringLen(cx, &buffer.len, val); + if (str == NULL) { + return JS_EXCEPTION; + } + + buffer.data = (u_char *) str; + } + + do { + cl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (cl == NULL) { + goto out_of_memory; + } + + b = cl->buf; + + if (b->start == NULL) { + b->start = ngx_pnalloc(r->pool, buffer.len); + if (b->start == NULL) { + goto out_of_memory; + } + + len = buffer.len; + b->end = b->start + len; + + } else { + len = ngx_min(buffer.len, (size_t) (b->end - b->start)); + } + + memcpy(b->start, buffer.data, len); + + b->pos = b->start; + b->last = b->start + len; + + if (buffer.len == len) { + b->last_buf = last_buf; + b->flush = flush; + + } else { + b->last_buf = 0; + b->flush = 0; + } + + b->memory = (len ? 1 : 0); + b->sync = (len ? 0 : 1); + b->tag = (ngx_buf_tag_t) &ngx_http_js_module; + + buffer.data += len; + buffer.len -= len; + + *ctx->last_out = cl; + ctx->last_out = &cl->next; + + } while (buffer.len != 0); + + if (str != NULL) { + JS_FreeCString(cx, str); + } + + JS_FreeValue(cx, buf); return JS_UNDEFINED; + +out_of_memory: + + if (str != NULL) { + JS_FreeCString(cx, str); + } + + JS_FreeValue(cx, buf); + + return JS_ThrowOutOfMemory(cx); } diff --git a/nginx/ngx_stream_js_module.c b/nginx/ngx_stream_js_module.c index a7446c49..13b685e5 100644 --- a/nginx/ngx_stream_js_module.c +++ b/nginx/ngx_stream_js_module.c @@ -2273,10 +2273,12 @@ static JSValue ngx_stream_qjs_ext_send(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int from_upstream) { - JSValue val; + size_t byte_offset, byte_length, len; + JSValue val, buf; unsigned last_buf, flush; ngx_str_t buffer; ngx_buf_t *b; + const char *str; ngx_chain_t *cl; ngx_connection_t *c; ngx_stream_js_ctx_t *ctx; @@ -2295,10 +2297,6 @@ ngx_stream_qjs_ext_send(JSContext *cx, JSValueConst this_val, int argc, return JS_ThrowInternalError(cx, "cannot send buffer in this handler"); } - if (ngx_qjs_string(cx, argv[0], &buffer) != NGX_OK) { - return JS_EXCEPTION; - } - /* * ctx->buf != NULL when s.send() is called while processing incoming * data chunks, otherwise s.send() is called asynchronously @@ -2353,39 +2351,121 @@ ngx_stream_qjs_ext_send(JSContext *cx, JSValueConst this_val, int argc, } } - cl = ngx_chain_get_free_buf(c->pool, &ctx->free); - if (cl == NULL) { - return JS_ThrowInternalError(cx, "memory error"); + val = argv[0]; + + if (JS_IsNullOrUndefined(val)) { + buffer.len = 0; + buffer.data = NULL; } - b = cl->buf; + str = NULL; + buf = JS_UNDEFINED; - b->flush = flush; - b->last_buf = last_buf; + if (JS_IsString(val)) { + goto string; + } - b->memory = (buffer.len ? 1 : 0); - b->sync = (buffer.len ? 0 : 1); - b->tag = (ngx_buf_tag_t) &ngx_stream_js_module; + buf = JS_GetTypedArrayBuffer(cx, val, &byte_offset, &byte_length, NULL); + if (!JS_IsException(buf)) { + buffer.data = JS_GetArrayBuffer(cx, &buffer.len, buf); + if (buffer.data == NULL) { + JS_FreeValue(cx, buf); + return JS_EXCEPTION; + } - b->start = buffer.data; - b->end = buffer.data + buffer.len; + buffer.data += byte_offset; + buffer.len = byte_length; - b->pos = b->start; - b->last = b->end; + } else { +string: - if (from_upstream == NGX_JS_BOOL_UNSET) { - *ctx->last_out = cl; - ctx->last_out = &cl->next; + str = JS_ToCStringLen(cx, &buffer.len, val); + if (str == NULL) { + return JS_EXCEPTION; + } - } else { + buffer.data = (u_char *) str; + } - if (ngx_stream_js_next_filter(s, ctx, cl, from_upstream) == NGX_ERROR) { - return JS_ThrowInternalError(cx, "ngx_stream_js_next_filter() " - "failed"); + do { + cl = ngx_chain_get_free_buf(c->pool, &ctx->free); + if (cl == NULL) { + goto out_of_memory; + } + + b = cl->buf; + + if (b->start == NULL) { + b->start = ngx_pnalloc(c->pool, buffer.len); + if (b->start == NULL) { + goto out_of_memory; + } + + len = buffer.len; + b->end = b->start + len; + + } else { + len = ngx_min(buffer.len, (size_t) (b->end - b->start)); + } + + memcpy(b->start, buffer.data, len); + + b->pos = b->start; + b->last = b->start + len; + + if (buffer.len == len) { + b->last_buf = last_buf; + b->flush = flush; + + } else { + b->last_buf = 0; + b->flush = 0; } + + b->memory = (len ? 1 : 0); + b->sync = (len ? 0 : 1); + b->tag = (ngx_buf_tag_t) &ngx_stream_js_module; + + buffer.data += len; + buffer.len -= len; + + if (from_upstream == NGX_JS_BOOL_UNSET) { + *ctx->last_out = cl; + ctx->last_out = &cl->next; + + } else { + + if (ngx_stream_js_next_filter(s, ctx, cl, from_upstream) + == NGX_ERROR) + { + if (str != NULL) { + JS_FreeCString(cx, str); + } + + return JS_ThrowInternalError(cx, "ngx_stream_js_next_filter() " + "failed"); + } + } + + } while (buffer.len != 0); + + if (str != NULL) { + JS_FreeCString(cx, str); } + JS_FreeValue(cx, buf); + return JS_UNDEFINED; + +out_of_memory: + + if (str != NULL) { + JS_FreeCString(cx, str); + } + + JS_FreeValue(cx, buf); + + return JS_ThrowInternalError(cx, "memory error"); } From pikolasikolatest5 at gmail.com Fri Aug 29 20:34:31 2025 From: pikolasikolatest5 at gmail.com (Chihab chger) Date: Fri, 29 Aug 2025 21:34:31 +0100 Subject: =?UTF-8?B?2LTZh9in2K/YqSDYp9mE2KPYqNmI2Kkg2YHZiiDYp9mE2YXYutix2Kg=?= Message-ID: شهادة الأبوة في المغرب -------------- next part -------------- An HTML attachment was scrubbed... URL: From pikolasikolatest5 at gmail.com Fri Aug 29 20:56:55 2025 From: pikolasikolatest5 at gmail.com (Chihab chger) Date: Fri, 29 Aug 2025 21:56:55 +0100 Subject: =?UTF-8?B?2LTZh9in2K/YqSDYp9mE2KPYqNmI2Kkg2YHZiiDYp9mE2YXYutix2Kg=?= Message-ID: شهادة الأبوة في المغرب -------------- next part -------------- An HTML attachment was scrubbed... URL: