diff --git a/auto/unix b/auto/unix index 0dd66cf..6087e04 100644 --- a/auto/unix +++ b/auto/unix @@ -129,26 +129,6 @@ if test -z "$NGX_KQUEUE_CHECKED"; then fi -if [ "$NGX_SYSTEM" = "NetBSD" ]; then - - # NetBSD 2.0 incompatibly defines kevent.udata as "intptr_t" - - cat << END >> $NGX_AUTO_CONFIG_H - -#define NGX_KQUEUE_UDATA_T - -END - -else - cat << END >> $NGX_AUTO_CONFIG_H - -#define NGX_KQUEUE_UDATA_T (void *) - -END - -fi - - ngx_feature="crypt()" ngx_feature_name="NGX_HAVE_CRYPT" ngx_feature_run=no diff --git a/conf/nginx.conf b/conf/nginx.conf index 29bc085..2315a86 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 / { diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml index ecb063d..c07de09 100644 --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -5,6 +5,199 @@ + + + + +обработка специально созданного логина/пароля при использовании +метода аутентификации "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 по умолчанию запрещено. + + +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. + + + + + + + + + + +поддержка ответа с кодом 103 от proxy- и gRPC-бэкендов; +директива early_hints. + + +support for response code 103 from proxy and gRPC backends; +the "early_hints" directive. + + + + + +возможность загрузки секретных ключей с аппаратных устройств +с помощью OpenSSL provider. + + +loading of secret keys from hardware tokens +with OpenSSL provider. + + + + + +поддержка параметра so_keepalive директивы listen на macOS. + + +support for the "so_keepalive" parameter of the "listen" directive on macOS. + + + + + +уровень логгирования ошибок SSL в QUIC handshake +изменён с уровня error на crit для критических ошибок +и на info для всех остальных; +уровень логгирования неподдерживаемых транспортных параметров QUIC +понижен с уровня info до debug. + + +the logging level of SSL errors in a QUIC handshake +has been changed from "error" to "crit" for critical errors, +and to "info" for the rest; +the logging level of unsupported QUIC transport parameters +has been lowered from "info" to "debug". + + + + + +бинарная версия nginx/Windows теперь использует для сборки Windows SDK 10. + + +the native nginx/Windows binary release is now built using Windows SDK 10. + + + + + +nginx не собирался gcc 15, +если использовались модули ngx_http_v2_module и ngx_http_v3_module. + + +nginx could not be built by gcc 15 +if ngx_http_v2_module or ngx_http_v3_module modules were used. + + + + + +nginx мог не собираться gcc 14 и новее с оптимизацией -O3 -flto, +если использовался модуль ngx_http_v3_module. + + +nginx might not be built by gcc 14 or newer with -O3 -flto optimization +if ngx_http_v3_module was used. + + + + + +Исправления и улучшения в HTTP/3. + + +Bugfixes and improvements in HTTP/3. + + + + + + diff --git a/misc/GNUmakefile b/misc/GNUmakefile index b7e76b9..81b1921 100644 --- a/misc/GNUmakefile +++ b/misc/GNUmakefile @@ -6,9 +6,9 @@ TEMP = tmp CC = cl OBJS = objs.msvc8 -OPENSSL = openssl-3.0.15 +OPENSSL = openssl-3.5.2 ZLIB = zlib-1.3.1 -PCRE = pcre2-10.39 +PCRE = pcre2-10.45 release: export @@ -76,7 +76,8 @@ win32: --with-stream_realip_module \ --with-stream_ssl_preread_module \ --with-openssl=$(OBJS)/lib/$(OPENSSL) \ - --with-openssl-opt="no-asm no-tests -D_WIN32_WINNT=0x0501" \ + --with-openssl-opt="no-asm no-tests no-makedepend \ + -D_WIN32_WINNT=0x0501" \ --with-http_ssl_module \ --with-mail_ssl_module \ --with-stream_ssl_module @@ -109,7 +110,7 @@ zip: export cp -p $(OBJS)/lib/$(OPENSSL)/LICENSE.txt \ $(TEMP)/$(NGINX)/docs/OpenSSL.LICENSE - cp -p $(OBJS)/lib/$(PCRE)/LICENCE \ + cp -p $(OBJS)/lib/$(PCRE)/LICENCE.md \ $(TEMP)/$(NGINX)/docs/PCRE.LICENCE sed -ne '/^ (C) 1995-20/,/^ jloup@gzip\.org/p' \ diff --git a/src/core/nginx.h b/src/core/nginx.h index cd09635..1fdb800 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1027006 -#define NGINX_VERSION "1.27.6" +#define nginx_version 1029002 +#define NGINX_VERSION "1.29.2" #define NGINX_VER "nginx/" NGINX_VERSION " by Hakase" #ifndef NGINX_SERVER diff --git a/src/core/ngx_thread_pool.c b/src/core/ngx_thread_pool.c index 7fb0f7f..7bf90e9 100644 --- a/src/core/ngx_thread_pool.c +++ b/src/core/ngx_thread_pool.c @@ -207,7 +207,7 @@ ngx_thread_pool_exit_handler(void *data, ngx_log_t *log) *lock = 0; - pthread_exit(0); + pthread_exit(NULL); } diff --git a/src/event/modules/ngx_kqueue_module.c b/src/event/modules/ngx_kqueue_module.c index 9c7244c..0c905ef 100644 --- a/src/event/modules/ngx_kqueue_module.c +++ b/src/event/modules/ngx_kqueue_module.c @@ -10,6 +10,15 @@ #include +/* NetBSD up to 10.0 incompatibly defines kevent.udata as "intptr_t" */ + +#if (__NetBSD__ && __NetBSD_Version__ < 1000000000) +#define NGX_KQUEUE_UDATA_T +#else +#define NGX_KQUEUE_UDATA_T (void *) +#endif + + typedef struct { ngx_uint_t changes; ngx_uint_t events; @@ -191,7 +200,7 @@ ngx_kqueue_init(ngx_cycle_t *cycle, ngx_msec_t timer) kev.flags = EV_ADD|EV_ENABLE; kev.fflags = 0; kev.data = timer; - kev.udata = 0; + kev.udata = NGX_KQUEUE_UDATA_T (uintptr_t) 0; ts.tv_sec = 0; ts.tv_nsec = 0; @@ -237,7 +246,7 @@ ngx_kqueue_notify_init(ngx_log_t *log) notify_kev.data = 0; notify_kev.flags = EV_ADD|EV_CLEAR; notify_kev.fflags = 0; - notify_kev.udata = 0; + notify_kev.udata = NGX_KQUEUE_UDATA_T (uintptr_t) 0; if (kevent(ngx_kqueue, ¬ify_kev, 1, NULL, 0, NULL) == -1) { ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index b89b31f..9636384 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 @@ -659,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) @@ -1374,7 +1409,7 @@ ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file) if (SSL_CTX_set0_tmp_dh_pkey(ssl->ctx, dh) != 1) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "SSL_CTX_set0_tmp_dh_pkey(\"%s\") failed", file->data); -#if (OPENSSL_VERSION_NUMBER >= 0x3000001fL) +#if (OPENSSL_VERSION_NUMBER >= 0x30000010L) EVP_PKEY_free(dh); #endif BIO_free(bio); @@ -5094,11 +5129,7 @@ ngx_ssl_get_curve(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) return NGX_OK; } -#if (OPENSSL_VERSION_NUMBER >= 0x3000000fL) name = SSL_group_to_name(c->ssl->connection, nid); -#else - name = NULL; -#endif s->len = name ? ngx_strlen(name) : sizeof("0x0000") - 1; s->data = ngx_pnalloc(pool, s->len); @@ -5152,11 +5183,7 @@ ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) nid = curves[i]; if (nid & TLSEXT_nid_unknown) { -#if (OPENSSL_VERSION_NUMBER >= 0x3000000fL) name = SSL_group_to_name(c->ssl->connection, nid); -#else - name = NULL; -#endif len += name ? ngx_strlen(name) : sizeof("0x0000") - 1; @@ -5178,11 +5205,7 @@ ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) nid = curves[i]; if (nid & TLSEXT_nid_unknown) { -#if (OPENSSL_VERSION_NUMBER >= 0x3000000fL) name = SSL_group_to_name(c->ssl->connection, nid); -#else - name = NULL; -#endif p = name ? ngx_cpymem(p, name, ngx_strlen(name)) : ngx_sprintf(p, "0x%04xd", nid & 0xffff); diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index 3d25ebc..fccb677 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -96,6 +96,11 @@ #endif +#if (OPENSSL_VERSION_NUMBER < 0x30000000L) +#define SSL_group_to_name(s, nid) NULL +#endif + + typedef struct ngx_ssl_ocsp_s ngx_ssl_ocsp_t; @@ -245,6 +250,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/event/ngx_event_openssl_cache.c b/src/event/ngx_event_openssl_cache.c index 18efc73..42f5e1c 100644 --- a/src/event/ngx_event_openssl_cache.c +++ b/src/event/ngx_event_openssl_cache.c @@ -707,7 +707,7 @@ ngx_ssl_cache_pkey_create(ngx_ssl_cache_key_t *id, char **err, void *data) return NULL; } - pkey = ENGINE_load_private_key(engine, (char *) last, 0, 0); + pkey = ENGINE_load_private_key(engine, (char *) last, NULL, NULL); if (pkey == NULL) { *err = "ENGINE_load_private_key() failed"; diff --git a/src/event/quic/ngx_event_quic.c b/src/event/quic/ngx_event_quic.c index 8df4877..6a59aaf 100644 --- a/src/event/quic/ngx_event_quic.c +++ b/src/event/quic/ngx_event_quic.c @@ -964,7 +964,7 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt) qc = ngx_quic_get_connection(c); qc->error = 0; - qc->error_reason = 0; + qc->error_reason = NULL; c->log->action = "decrypting packet"; diff --git a/src/event/quic/ngx_event_quic.h b/src/event/quic/ngx_event_quic.h index d95d3d8..bab085f 100644 --- a/src/event/quic/ngx_event_quic.h +++ b/src/event/quic/ngx_event_quic.h @@ -12,7 +12,7 @@ #include -#ifdef OSSL_RECORD_PROTECTION_LEVEL_NONE +#if (OPENSSL_VERSION_NUMBER >= 0x30500010L) #define NGX_QUIC_OPENSSL_API 1 #elif (defined SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION) diff --git a/src/http/modules/ngx_http_addition_filter_module.c b/src/http/modules/ngx_http_addition_filter_module.c index e546f0d..040244c 100644 --- a/src/http/modules/ngx_http_addition_filter_module.c +++ b/src/http/modules/ngx_http_addition_filter_module.c @@ -245,7 +245,7 @@ ngx_http_addition_merge_conf(ngx_conf_t *cf, void *parent, void *child) if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types, &prev->types_keys, &prev->types, ngx_http_html_default_types) - != NGX_OK) + != NGX_CONF_OK) { return NGX_CONF_ERROR; } diff --git a/src/http/modules/ngx_http_auth_basic_module.c b/src/http/modules/ngx_http_auth_basic_module.c index 02d41e8..69e8d21 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); diff --git a/src/http/modules/ngx_http_charset_filter_module.c b/src/http/modules/ngx_http_charset_filter_module.c index d44da62..4829600 100644 --- a/src/http/modules/ngx_http_charset_filter_module.c +++ b/src/http/modules/ngx_http_charset_filter_module.c @@ -1564,7 +1564,7 @@ ngx_http_charset_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types, &prev->types_keys, &prev->types, ngx_http_charset_default_types) - != NGX_OK) + != NGX_CONF_OK) { return NGX_CONF_ERROR; } diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c index a41b496..6b19773 100644 --- a/src/http/modules/ngx_http_fastcgi_module.c +++ b/src/http/modules/ngx_http_fastcgi_module.c @@ -3130,7 +3130,7 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path, prev->upstream.temp_path, &ngx_http_fastcgi_temp_path) - != NGX_OK) + != NGX_CONF_OK) { return NGX_CONF_ERROR; } diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c index 80046d6..4c95525 100644 --- a/src/http/modules/ngx_http_grpc_module.c +++ b/src/http/modules/ngx_http_grpc_module.c @@ -1869,7 +1869,8 @@ ngx_http_grpc_process_header(ngx_http_request_t *r) return NGX_HTTP_UPSTREAM_INVALID_HEADER; } - if (status < NGX_HTTP_OK) { + if (status < NGX_HTTP_OK && status != NGX_HTTP_EARLY_HINTS) + { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "upstream sent unexpected :status \"%V\"", status_line); @@ -1902,6 +1903,10 @@ ngx_http_grpc_process_header(ngx_http_request_t *r) h->lowcase_key = h->key.data; h->hash = ngx_hash_key(h->key.data, h->key.len); + if (u->headers_in.status_n == NGX_HTTP_EARLY_HINTS) { + continue; + } + hh = ngx_hash_find(&umcf->headers_in_hash, h->hash, h->lowcase_key, h->key.len); @@ -1923,6 +1928,17 @@ ngx_http_grpc_process_header(ngx_http_request_t *r) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "grpc header done"); + if (u->headers_in.status_n == NGX_HTTP_EARLY_HINTS) { + if (ctx->end_stream) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream prematurely closed stream"); + return NGX_HTTP_UPSTREAM_INVALID_HEADER; + } + + ctx->status = 0; + return NGX_HTTP_UPSTREAM_EARLY_HINTS; + } + if (ctx->end_stream) { u->headers_in.content_length_n = 0; @@ -4413,6 +4429,7 @@ ngx_http_grpc_create_loc_conf(ngx_conf_t *cf) conf->upstream.pass_request_body = 1; conf->upstream.force_ranges = 0; conf->upstream.pass_trailers = 1; + conf->upstream.pass_early_hints = 1; conf->upstream.preserve_output = 1; conf->headers_source = NGX_CONF_UNSET_PTR; diff --git a/src/http/modules/ngx_http_gunzip_filter_module.c b/src/http/modules/ngx_http_gunzip_filter_module.c index 5d170a1..22e75e3 100644 --- a/src/http/modules/ngx_http_gunzip_filter_module.c +++ b/src/http/modules/ngx_http_gunzip_filter_module.c @@ -304,7 +304,7 @@ ngx_http_gunzip_filter_inflate_start(ngx_http_request_t *r, { int rc; - ctx->zstream.next_in = Z_NULL; + ctx->zstream.next_in = NULL; ctx->zstream.avail_in = 0; ctx->zstream.zalloc = ngx_http_gunzip_filter_alloc; diff --git a/src/http/modules/ngx_http_gzip_filter_module.c b/src/http/modules/ngx_http_gzip_filter_module.c index 7113df6..dac21bb 100644 --- a/src/http/modules/ngx_http_gzip_filter_module.c +++ b/src/http/modules/ngx_http_gzip_filter_module.c @@ -1115,7 +1115,7 @@ ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child) if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types, &prev->types_keys, &prev->types, ngx_http_html_default_types) - != NGX_OK) + != NGX_CONF_OK) { return NGX_CONF_ERROR; } diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c index 6f5f197..ecb1956 100644 --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -1877,6 +1877,13 @@ ngx_http_proxy_process_status_line(ngx_http_request_t *r) u->headers_in.status_n, &u->headers_in.status_line); if (ctx->status.http_version < NGX_HTTP_VERSION_11) { + + if (ctx->status.code == NGX_HTTP_EARLY_HINTS) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent HTTP/1.0 response with early hints"); + return NGX_HTTP_UPSTREAM_INVALID_HEADER; + } + u->headers_in.connection_close = 1; } @@ -1938,6 +1945,14 @@ ngx_http_proxy_process_header(ngx_http_request_t *r) ngx_strlow(h->lowcase_key, h->key.data, h->key.len); } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy header: \"%V: %V\"", + &h->key, &h->value); + + if (r->upstream->headers_in.status_n == NGX_HTTP_EARLY_HINTS) { + continue; + } + hh = ngx_hash_find(&umcf->headers_in_hash, h->hash, h->lowcase_key, h->key.len); @@ -1949,10 +1964,6 @@ ngx_http_proxy_process_header(ngx_http_request_t *r) } } - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http proxy header: \"%V: %V\"", - &h->key, &h->value); - continue; } @@ -1963,6 +1974,20 @@ ngx_http_proxy_process_header(ngx_http_request_t *r) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http proxy header done"); + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + + if (r->upstream->headers_in.status_n == NGX_HTTP_EARLY_HINTS) { + ctx->status.code = 0; + ctx->status.count = 0; + ctx->status.start = NULL; + ctx->status.end = NULL; + + r->upstream->process_header = + ngx_http_proxy_process_status_line; + r->state = 0; + return NGX_HTTP_UPSTREAM_EARLY_HINTS; + } + /* * if no "Server" and "Date" in header line, * then add the special empty headers @@ -2010,8 +2035,6 @@ ngx_http_proxy_process_header(ngx_http_request_t *r) * connections alive in case of r->header_only or X-Accel-Redirect */ - ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); - if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED || ctx->head @@ -3422,10 +3445,10 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf) conf->ssl_conf_commands = NGX_CONF_UNSET_PTR; #endif - /* "proxy_cyclic_temp_file" is disabled */ + /* the hardcoded values */ conf->upstream.cyclic_temp_file = 0; - conf->upstream.change_buffering = 1; + conf->upstream.pass_early_hints = 1; conf->headers_source = NGX_CONF_UNSET_PTR; @@ -3638,7 +3661,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path, prev->upstream.temp_path, &ngx_http_proxy_temp_path) - != NGX_OK) + != NGX_CONF_OK) { return NGX_CONF_ERROR; } diff --git a/src/http/modules/ngx_http_scgi_module.c b/src/http/modules/ngx_http_scgi_module.c index 9023a36..49977b0 100644 --- a/src/http/modules/ngx_http_scgi_module.c +++ b/src/http/modules/ngx_http_scgi_module.c @@ -1543,7 +1543,7 @@ ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path, prev->upstream.temp_path, &ngx_http_scgi_temp_path) - != NGX_OK) + != NGX_CONF_OK) { return NGX_CONF_ERROR; } diff --git a/src/http/modules/ngx_http_ssi_filter_module.c b/src/http/modules/ngx_http_ssi_filter_module.c index 47068f7..65ca034 100644 --- a/src/http/modules/ngx_http_ssi_filter_module.c +++ b/src/http/modules/ngx_http_ssi_filter_module.c @@ -820,7 +820,7 @@ ngx_http_ssi_body_filter(ngx_http_request_t *r, ngx_chain_t *in) } for (prm = cmd->params; prm->name.len; prm++) { - if (prm->mandatory && params[prm->index] == 0) { + if (prm->mandatory && params[prm->index] == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "mandatory \"%V\" parameter is absent " "in \"%V\" SSI command", @@ -2942,7 +2942,7 @@ ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types, &prev->types_keys, &prev->types, ngx_http_html_default_types) - != NGX_OK) + != NGX_CONF_OK) { return NGX_CONF_ERROR; } diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 14e9b07..6af252f 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, @@ -656,6 +663,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; @@ -698,6 +706,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, 1); + ngx_conf_merge_value(conf->certificate_compression, + prev->certificate_compression, 0); + ngx_conf_merge_value(conf->early_data, prev->early_data, 1); ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0); @@ -846,6 +857,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 08b0e11..e50bdc9 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/http/modules/ngx_http_sub_filter_module.c b/src/http/modules/ngx_http_sub_filter_module.c index 456bb27..b50a252 100644 --- a/src/http/modules/ngx_http_sub_filter_module.c +++ b/src/http/modules/ngx_http_sub_filter_module.c @@ -901,7 +901,7 @@ ngx_http_sub_merge_conf(ngx_conf_t *cf, void *parent, void *child) if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types, &prev->types_keys, &prev->types, ngx_http_html_default_types) - != NGX_OK) + != NGX_CONF_OK) { return NGX_CONF_ERROR; } diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c index 51a861d..c1d0035 100644 --- a/src/http/modules/ngx_http_uwsgi_module.c +++ b/src/http/modules/ngx_http_uwsgi_module.c @@ -1803,7 +1803,7 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path, prev->upstream.temp_path, &ngx_http_uwsgi_temp_path) - != NGX_OK) + != NGX_CONF_OK) { return NGX_CONF_ERROR; } diff --git a/src/http/modules/ngx_http_xslt_filter_module.c b/src/http/modules/ngx_http_xslt_filter_module.c index 8afd656..4e6e1b9 100644 --- a/src/http/modules/ngx_http_xslt_filter_module.c +++ b/src/http/modules/ngx_http_xslt_filter_module.c @@ -1112,7 +1112,7 @@ ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child) if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types, &prev->types_keys, &prev->types, ngx_http_xslt_default_types) - != NGX_OK) + != NGX_CONF_OK) { return NGX_CONF_ERROR; } diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c index d835f89..7f2b422 100644 --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -72,6 +72,7 @@ ngx_uint_t ngx_http_max_module; ngx_http_output_header_filter_pt ngx_http_top_header_filter; +ngx_http_output_header_filter_pt ngx_http_top_early_hints_filter; ngx_http_output_body_filter_pt ngx_http_top_body_filter; ngx_http_request_body_filter_pt ngx_http_top_request_body_filter; diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h index e06464e..c89dc29 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); @@ -152,6 +151,7 @@ ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r, ngx_int_t ngx_http_read_unbuffered_request_body(ngx_http_request_t *r); ngx_int_t ngx_http_send_header(ngx_http_request_t *r); +ngx_int_t ngx_http_send_early_hints(ngx_http_request_t *r); ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error); ngx_int_t ngx_http_filter_finalize_request(ngx_http_request_t *r, @@ -191,6 +191,7 @@ extern ngx_str_t ngx_http_html_default_types[]; extern ngx_http_output_header_filter_pt ngx_http_top_header_filter; +extern ngx_http_output_header_filter_pt ngx_http_top_early_hints_filter; extern ngx_http_output_body_filter_pt ngx_http_top_body_filter; extern ngx_http_request_body_filter_pt ngx_http_top_request_body_filter; diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index 92c3eae..c75ddb8 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -670,6 +670,13 @@ static ngx_command_t ngx_http_core_commands[] = { offsetof(ngx_http_core_loc_conf_t, etag), NULL }, + { ngx_string("early_hints"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_http_set_predicate_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, early_hints), + NULL }, + { ngx_string("error_page"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_2MORE, @@ -1857,6 +1864,37 @@ ngx_http_send_header(ngx_http_request_t *r) } +ngx_int_t +ngx_http_send_early_hints(ngx_http_request_t *r) +{ + ngx_int_t rc; + ngx_http_core_loc_conf_t *clcf; + + if (r->post_action) { + return NGX_OK; + } + + if (r->header_sent) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "header already sent"); + return NGX_ERROR; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + rc = ngx_http_test_predicates(r, clcf->early_hints); + + if (rc != NGX_DECLINED) { + return rc; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http send early hints \"%V?%V\"", &r->uri, &r->args); + + return ngx_http_top_early_hints_filter(r); +} + + ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in) { @@ -3637,6 +3675,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t *cf) clcf->chunked_transfer_encoding = NGX_CONF_UNSET; clcf->etag = NGX_CONF_UNSET; clcf->server_tokens = NGX_CONF_UNSET_UINT; + clcf->early_hints = NGX_CONF_UNSET_PTR; clcf->types_hash_max_size = NGX_CONF_UNSET_UINT; clcf->types_hash_bucket_size = NGX_CONF_UNSET_UINT; @@ -3892,7 +3931,7 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (ngx_conf_merge_path_value(cf, &conf->client_body_temp_path, prev->client_body_temp_path, &ngx_http_client_temp_path) - != NGX_OK) + != NGX_CONF_OK) { return NGX_CONF_ERROR; } @@ -3917,6 +3956,8 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_uint_value(conf->server_tokens, prev->server_tokens, NGX_HTTP_SERVER_TOKENS_ON); + ngx_conf_merge_ptr_value(conf->early_hints, prev->early_hints, NULL); + ngx_conf_merge_ptr_value(conf->open_file_cache, prev->open_file_cache, NULL); diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h index e7e266b..a794144 100644 --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -430,6 +430,8 @@ struct ngx_http_core_loc_conf_s { ngx_http_complex_value_t *disable_symlinks_from; #endif + ngx_array_t *early_hints; /* early_hints */ + ngx_array_t *error_pages; /* error_page */ ngx_path_t *client_body_temp_path; /* client_body_temp_path */ diff --git a/src/http/ngx_http_header_filter_module.c b/src/http/ngx_http_header_filter_module.c index 495f2d6..17b8249 100644 --- a/src/http/ngx_http_header_filter_module.c +++ b/src/http/ngx_http_header_filter_module.c @@ -13,6 +13,7 @@ static ngx_int_t ngx_http_header_filter_init(ngx_conf_t *cf); static ngx_int_t ngx_http_header_filter(ngx_http_request_t *r); +static ngx_int_t ngx_http_early_hints_filter(ngx_http_request_t *r); static ngx_http_module_t ngx_http_header_filter_module_ctx = { @@ -50,6 +51,9 @@ static u_char ngx_http_server_string[] = "Server: " NGINX_SERVER CRLF; static u_char ngx_http_server_full_string[] = "Server: " NGINX_SERVER_FULL CRLF; static u_char ngx_http_server_build_string[] = "Server: " NGINX_SERVER_FULL_BUILD CRLF; +static ngx_str_t ngx_http_early_hints_status_line = + ngx_string("HTTP/1.1 103 Early Hints" CRLF); + static ngx_str_t ngx_http_status_lines[] = { @@ -625,10 +629,113 @@ ngx_http_header_filter(ngx_http_request_t *r) } +static ngx_int_t +ngx_http_early_hints_filter(ngx_http_request_t *r) +{ + size_t len; + ngx_buf_t *b; + ngx_uint_t i; + ngx_chain_t out; + ngx_list_part_t *part; + ngx_table_elt_t *header; + + if (r != r->main) { + return NGX_OK; + } + + if (r->http_version < NGX_HTTP_VERSION_11) { + return NGX_OK; + } + + len = 0; + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + len += header[i].key.len + sizeof(": ") - 1 + header[i].value.len + + sizeof(CRLF) - 1; + } + + if (len == 0) { + return NGX_OK; + } + + len += ngx_http_early_hints_status_line.len + /* the end of the early hints */ + + sizeof(CRLF) - 1; + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NGX_ERROR; + } + + b->last = ngx_copy(b->last, ngx_http_early_hints_status_line.data, + ngx_http_early_hints_status_line.len); + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len); + *b->last++ = ':'; *b->last++ = ' '; + + b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len); + *b->last++ = CR; *b->last++ = LF; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "%*s", (size_t) (b->last - b->pos), b->pos); + + /* the end of HTTP early hints */ + *b->last++ = CR; *b->last++ = LF; + + r->header_size = b->last - b->pos; + + b->flush = 1; + + out.buf = b; + out.next = NULL; + + return ngx_http_write_filter(r, &out); +} + + static ngx_int_t ngx_http_header_filter_init(ngx_conf_t *cf) { ngx_http_top_header_filter = ngx_http_header_filter; + ngx_http_top_early_hints_filter = ngx_http_early_hints_filter; return NGX_OK; } diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index ceac8d3..95cb1a1 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 diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 9407f46..ad11f14 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -74,6 +74,7 @@ #define NGX_HTTP_CONTINUE 100 #define NGX_HTTP_SWITCHING_PROTOCOLS 101 #define NGX_HTTP_PROCESSING 102 +#define NGX_HTTP_EARLY_HINTS 103 #define NGX_HTTP_OK 200 #define NGX_HTTP_CREATED 201 diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index d4cf1b7..de0f92a 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -48,6 +48,9 @@ static void ngx_http_upstream_send_request_handler(ngx_http_request_t *r, static void ngx_http_upstream_read_request_handler(ngx_http_request_t *r); static void ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u); +static ngx_int_t ngx_http_upstream_process_early_hints(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void ngx_http_upstream_early_hints_writer(ngx_http_request_t *r); static ngx_int_t ngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u); static ngx_int_t ngx_http_upstream_intercept_errors(ngx_http_request_t *r, @@ -1120,7 +1123,7 @@ ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u) return NGX_ERROR; } - if (rc == NGX_AGAIN) { + if (rc == NGX_AGAIN || rc == NGX_HTTP_UPSTREAM_EARLY_HINTS) { rc = NGX_HTTP_UPSTREAM_INVALID_HEADER; } @@ -2530,6 +2533,18 @@ ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u) continue; } + if (rc == NGX_HTTP_UPSTREAM_EARLY_HINTS) { + rc = ngx_http_upstream_process_early_hints(r, u); + + if (rc == NGX_OK) { + rc = u->process_header(r); + + if (rc == NGX_AGAIN) { + continue; + } + } + } + break; } @@ -2567,6 +2582,148 @@ ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u) } +static ngx_int_t +ngx_http_upstream_process_early_hints(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + u_char *p; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *h, *ho; + ngx_connection_t *c; + + c = r->connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http upstream early hints"); + + if (u->conf->pass_early_hints) { + + u->early_hints_length += u->buffer.pos - u->buffer.start; + + if (u->early_hints_length <= (off_t) u->conf->buffer_size) { + + part = &u->headers_in.headers.part; + h = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + h = part->elts; + i = 0; + } + + if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash, + h[i].lowcase_key, h[i].key.len)) + { + continue; + } + + ho = ngx_list_push(&r->headers_out.headers); + if (ho == NULL) { + return NGX_ERROR; + } + + *ho = h[i]; + } + + if (ngx_http_send_early_hints(r) == NGX_ERROR) { + return NGX_ERROR; + } + + if (c->buffered) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + return NGX_ERROR; + } + + r->write_event_handler = ngx_http_upstream_early_hints_writer; + } + + } else { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "upstream sent too big early hints"); + } + } + + ngx_http_clean_header(r); + + ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t)); + u->headers_in.content_length_n = -1; + u->headers_in.last_modified_time = -1; + + if (ngx_list_init(&u->headers_in.headers, r->pool, 8, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_list_init(&u->headers_in.trailers, r->pool, 2, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + p = u->buffer.pos; + + u->buffer.pos = u->buffer.start; + +#if (NGX_HTTP_CACHE) + + if (r->cache) { + u->buffer.pos += r->cache->header_start; + } + +#endif + + u->buffer.last = ngx_movemem(u->buffer.pos, p, u->buffer.last - p); + + return NGX_OK; +} + + +static void +ngx_http_upstream_early_hints_writer(ngx_http_request_t *r) +{ + ngx_connection_t *c; + ngx_http_upstream_t *u; + + c = r->connection; + u = r->upstream; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream early hints writer"); + + c->log->action = "sending early hints to client"; + + if (ngx_http_write_filter(r, NULL) == NGX_ERROR) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + if (!c->buffered) { + if (!u->store && !r->post_action && !u->conf->ignore_client_abort) { + r->write_event_handler = + ngx_http_upstream_wr_check_broken_connection; + + } else { + r->write_event_handler = ngx_http_request_empty_handler; + } + } + + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + } +} + + static ngx_int_t ngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u) { diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h index f906f53..f3e9f79 100644 --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -43,6 +43,7 @@ |NGX_HTTP_UPSTREAM_FT_HTTP_429) #define NGX_HTTP_UPSTREAM_INVALID_HEADER 40 +#define NGX_HTTP_UPSTREAM_EARLY_HINTS 41 #define NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT 0x00000002 @@ -184,6 +185,8 @@ typedef struct { ngx_flag_t request_buffering; ngx_flag_t pass_request_headers; ngx_flag_t pass_request_body; + ngx_flag_t pass_trailers; + ngx_flag_t pass_early_hints; ngx_flag_t ignore_client_abort; ngx_flag_t intercept_errors; @@ -232,7 +235,6 @@ typedef struct { signed store:2; unsigned intercept_404:1; unsigned change_buffering:1; - unsigned pass_trailers:1; unsigned preserve_output:1; #if (NGX_HTTP_SSL || NGX_COMPAT) @@ -354,6 +356,7 @@ struct ngx_http_upstream_s { ngx_buf_t buffer; off_t length; + off_t early_hints_length; ngx_chain_t *out_bufs; ngx_chain_t *busy_bufs; diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index 0f832dd..23fb7b2 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -158,6 +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); 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); @@ -3526,44 +3527,40 @@ 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; + ngx_int_t rc; - static ngx_str_t host = ngx_string("host"); - - h = ngx_list_push(&r->headers_in.headers); - if (h == NULL) { - return NGX_ERROR; + if (r->host_start) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent duplicate \":authority\" header"); + return NGX_DECLINED; } - h->hash = ngx_hash(ngx_hash(ngx_hash('h', 'o'), 's'), 't'); + r->host_start = value->data; + r->host_end = value->data + value->len; - h->key.len = host.len; - h->key.data = host.data; + rc = ngx_http_validate_host(value, r->pool, 0); - 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 (rc == NGX_DECLINED) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid \":authority\" header"); + return NGX_DECLINED; } - if (hh->handler(r, h, hh->offset) != NGX_OK) { + 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_process_host() + * in ngx_http_set_virtual_server() */ return NGX_ABORT; } + r->headers_in.server = *value; + return NGX_OK; } @@ -3734,7 +3731,54 @@ 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; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v2_construct_host_header(ngx_http_request_t *r) +{ + 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) { + ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR); + 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 = r->host_end - r->host_start; + h->value.data = r->host_start; + + 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) { + ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + if (hh->handler(r, h, hh->offset) != NGX_OK) { + /* + * request has been finalized already + * in ngx_http_process_host() */ return NGX_ERROR; } @@ -3746,6 +3790,7 @@ ngx_http_v2_construct_cookie_header(ngx_http_request_t *r) 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 +3818,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) { - 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"); - - r->stream->skip_data = 1; - + 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 == -1 && !r->stream->in_closed) { + if (r->host_end) { + + host.len = r->host_end - r->host_start; + host.data = r->host_start; + + 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) { + 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; diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h index 18b8f48..20cc536 100644 --- a/src/http/v2/ngx_http_v2.h +++ b/src/http/v2/ngx_http_v2.h @@ -270,6 +270,7 @@ struct ngx_http_v2_stream_s { ngx_pool_t *pool; + unsigned initialized:1; unsigned waiting:1; unsigned blocked:1; unsigned exhausted:1; diff --git a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c index 7d46ab8..899996f 100644 --- a/src/http/v2/ngx_http_v2_filter_module.c +++ b/src/http/v2/ngx_http_v2_filter_module.c @@ -59,8 +59,13 @@ #define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1 +static ngx_int_t ngx_http_v2_header_filter(ngx_http_request_t *r); +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); @@ -127,6 +132,7 @@ ngx_module_t ngx_http_v2_filter_module = { static ngx_http_output_header_filter_pt ngx_http_next_header_filter; +static ngx_http_output_header_filter_pt ngx_http_next_early_hints_filter; static ngx_int_t @@ -139,7 +145,6 @@ ngx_http_v2_header_filter(ngx_http_request_t *r) ngx_list_part_t *part; ngx_table_elt_t *header; ngx_connection_t *fc; - ngx_http_cleanup_t *cln; ngx_http_v2_stream_t *stream; ngx_http_v2_out_frame_t *frame; ngx_http_v2_connection_t *h2c; @@ -582,14 +587,203 @@ 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; } ngx_http_v2_queue_blocked_frame(h2c, frame); - stream->queued = 1; + stream->queued++; + + if (ngx_http_v2_init_stream(r) != NGX_OK) { + return NGX_ERROR; + } + + return ngx_http_v2_filter_send(fc, stream); +} + + +static ngx_int_t +ngx_http_v2_early_hints_filter(ngx_http_request_t *r) +{ + u_char *pos, *start, *tmp; + size_t len, tmp_len; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_connection_t *fc; + ngx_http_v2_stream_t *stream; + ngx_http_v2_out_frame_t *frame; + ngx_http_v2_connection_t *h2c; + + stream = r->stream; + + if (!stream) { + return ngx_http_next_early_hints_filter(r); + } + + if (r != r->main) { + return NGX_OK; + } + + fc = r->connection; + + if (fc->error) { + return NGX_ERROR; + } + + len = 0; + tmp_len = 0; + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_CRIT, fc->log, 0, + "too long response header name: \"%V\"", + &header[i].key); + return NGX_ERROR; + } + + if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_CRIT, fc->log, 0, + "too long response header value: \"%V: %V\"", + &header[i].key, &header[i].value); + return NGX_ERROR; + } + + len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len + + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; + + if (header[i].key.len > tmp_len) { + tmp_len = header[i].key.len; + } + + if (header[i].value.len > tmp_len) { + tmp_len = header[i].value.len; + } + } + + if (len == 0) { + return NGX_OK; + } + + h2c = stream->connection; + + len += h2c->table_update ? 1 : 0; + len += 1 + ngx_http_v2_literal_size("418"); + + tmp = ngx_palloc(r->pool, tmp_len); + pos = ngx_pnalloc(r->pool, len); + + if (pos == NULL || tmp == NULL) { + return NGX_ERROR; + } + + start = pos; + + if (h2c->table_update) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 table size update: 0"); + *pos++ = (1 << 5) | 0; + h2c->table_update = 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 output header: \":status: %03ui\"", + (ngx_uint_t) NGX_HTTP_EARLY_HINTS); + + *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX); + *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3; + pos = ngx_sprintf(pos, "%03ui", (ngx_uint_t) NGX_HTTP_EARLY_HINTS); + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + +#if (NGX_DEBUG) + if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) { + ngx_strlow(tmp, header[i].key.data, header[i].key.len); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 output header: \"%*s: %V\"", + header[i].key.len, tmp, &header[i].value); + } +#endif + + *pos++ = 0; + + pos = ngx_http_v2_write_name(pos, header[i].key.data, + header[i].key.len, tmp); + + pos = ngx_http_v2_write_value(pos, header[i].value.data, + header[i].value.len, tmp); + } + + frame = ngx_http_v2_create_headers_frame(r, start, pos, 0, 1); + if (frame == NULL) { + return NGX_ERROR; + } + + ngx_http_v2_queue_blocked_frame(h2c, frame); + + stream->queued++; + + if (ngx_http_v2_init_stream(r) != NGX_OK) { + return NGX_ERROR; + } + + return ngx_http_v2_filter_send(fc, stream); +} + + +static ngx_int_t +ngx_http_v2_init_stream(ngx_http_request_t *r) +{ + ngx_connection_t *fc; + ngx_http_cleanup_t *cln; + ngx_http_v2_stream_t *stream; + + stream = r->stream; + fc = r->connection; + + if (stream->initialized) { + return NGX_OK; + } + + stream->initialized = 1; cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { @@ -603,13 +797,13 @@ ngx_http_v2_header_filter(ngx_http_request_t *r) fc->need_last_buf = 1; fc->need_flush_buf = 1; - return ngx_http_v2_filter_send(fc, stream); + return NGX_OK; } 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; @@ -700,6 +894,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; @@ -822,7 +1017,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); } @@ -1544,5 +1739,8 @@ ngx_http_v2_filter_init(ngx_conf_t *cf) ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_v2_header_filter; + ngx_http_next_early_hints_filter = ngx_http_top_early_hints_filter; + ngx_http_top_early_hints_filter = ngx_http_v2_early_hints_filter; + return NGX_OK; } diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c index 46ab128..8603024 100644 --- a/src/http/v3/ngx_http_v3_filter_module.c +++ b/src/http/v3/ngx_http_v3_filter_module.c @@ -20,6 +20,7 @@ #define NGX_HTTP_V3_HEADER_METHOD_GET 17 #define NGX_HTTP_V3_HEADER_SCHEME_HTTP 22 #define NGX_HTTP_V3_HEADER_SCHEME_HTTPS 23 +#define NGX_HTTP_V3_HEADER_STATUS_103 24 #define NGX_HTTP_V3_HEADER_STATUS_200 25 #define NGX_HTTP_V3_HEADER_ACCEPT_ENCODING 31 #define NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN 53 @@ -36,6 +37,7 @@ typedef struct { static ngx_int_t ngx_http_v3_header_filter(ngx_http_request_t *r); +static ngx_int_t ngx_http_v3_early_hints_filter(ngx_http_request_t *r); static ngx_int_t ngx_http_v3_body_filter(ngx_http_request_t *r, ngx_chain_t *in); static ngx_chain_t *ngx_http_v3_create_trailers(ngx_http_request_t *r, @@ -75,6 +77,7 @@ ngx_module_t ngx_http_v3_filter_module = { static ngx_http_output_header_filter_pt ngx_http_next_header_filter; +static ngx_http_output_header_filter_pt ngx_http_next_early_hints_filter; static ngx_http_output_body_filter_pt ngx_http_next_body_filter; @@ -588,6 +591,150 @@ ngx_http_v3_header_filter(ngx_http_request_t *r) } +static ngx_int_t +ngx_http_v3_early_hints_filter(ngx_http_request_t *r) +{ + size_t len, n; + ngx_buf_t *b; + ngx_uint_t i; + ngx_chain_t *out, *hl, *cl; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_http_v3_session_t *h3c; + + if (r->http_version != NGX_HTTP_VERSION_30) { + return ngx_http_next_early_hints_filter(r); + } + + if (r != r->main) { + return NGX_OK; + } + + len = 0; + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + len += ngx_http_v3_encode_field_l(NULL, &header[i].key, + &header[i].value); + } + + if (len == 0) { + return NGX_OK; + } + + len += ngx_http_v3_encode_field_section_prefix(NULL, 0, 0, 0); + + len += ngx_http_v3_encode_field_ri(NULL, 0, NGX_HTTP_V3_HEADER_STATUS_103); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 header len:%uz", len); + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NGX_ERROR; + } + + b->last = (u_char *) ngx_http_v3_encode_field_section_prefix(b->last, + 0, 0, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 output header: \":status: %03ui\"", + (ngx_uint_t) NGX_HTTP_EARLY_HINTS); + + b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, + NGX_HTTP_V3_HEADER_STATUS_103); + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 output header: \"%V: %V\"", + &header[i].key, &header[i].value); + + b->last = (u_char *) ngx_http_v3_encode_field_l(b->last, + &header[i].key, + &header[i].value); + } + + b->flush = 1; + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = NULL; + + n = b->last - b->pos; + + h3c = ngx_http_v3_get_session(r->connection); + h3c->payload_bytes += n; + + len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_HEADERS) + + ngx_http_v3_encode_varlen_int(NULL, n); + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NGX_ERROR; + } + + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, + NGX_HTTP_V3_FRAME_HEADERS); + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n); + + hl = ngx_alloc_chain_link(r->pool); + if (hl == NULL) { + return NGX_ERROR; + } + + hl->buf = b; + hl->next = cl; + + out = hl; + + for (cl = out; cl; cl = cl->next) { + h3c->total_bytes += cl->buf->last - cl->buf->pos; + r->header_size += cl->buf->last - cl->buf->pos; + } + + return ngx_http_write_filter(r, out); +} + + static ngx_int_t ngx_http_v3_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { @@ -845,6 +992,9 @@ ngx_http_v3_filter_init(ngx_conf_t *cf) ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_v3_header_filter; + ngx_http_next_early_hints_filter = ngx_http_top_early_hints_filter; + ngx_http_top_early_hints_filter = ngx_http_v3_early_hints_filter; + ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_v3_body_filter; diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index 436765c..bcbf0db 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -623,6 +623,12 @@ ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, } if (st->huffman) { + if (n > NGX_MAX_INT_T_VALUE / 8) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent too large field line"); + return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; + } + n = n * 8 / 5; st->huffstate = 0; } diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index e41ad50..844a400 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; } @@ -1003,6 +1003,7 @@ ngx_http_v3_process_request_header(ngx_http_request_t *r) { ssize_t n; ngx_buf_t *b; + ngx_str_t host; ngx_connection_t *c; ngx_http_v3_session_t *h3c; ngx_http_v3_srv_conf_t *h3scf; @@ -1034,11 +1035,13 @@ ngx_http_v3_process_request_header(ngx_http_request_t *r) goto failed; } - if (r->headers_in.host) { - if (r->headers_in.host->value.len != r->headers_in.server.len - || ngx_memcmp(r->headers_in.host->value.data, - r->headers_in.server.data, - r->headers_in.server.len) + if (r->headers_in.host && r->host_end) { + + host.len = r->host_end - r->host_start; + host.data = r->host_start; + + 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, c->log, 0, @@ -1213,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; } diff --git a/src/mail/ngx_mail_auth_http_module.c b/src/mail/ngx_mail_auth_http_module.c index 27f64b9..4ca6d6e 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_handler.c b/src/mail/ngx_mail_handler.c index 1167df3..a88e6c2 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; @@ -1000,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; } diff --git a/src/mail/ngx_mail_smtp_handler.c b/src/mail/ngx_mail_smtp_handler.c index e68ceed..1e26c2c 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; } diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c index 050557f..ea707b4 100644 --- a/src/mail/ngx_mail_ssl_module.c +++ b/src/mail/ngx_mail_ssl_module.c @@ -98,6 +98,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, @@ -315,6 +322,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; @@ -344,6 +352,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, 1); + 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)); @@ -447,6 +458,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->client_certificate.len == 0 && conf->verify != 3) { diff --git a/src/mail/ngx_mail_ssl_module.h b/src/mail/ngx_mail_ssl_module.h index c0eb6a3..a0e9a17 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/os/unix/ngx_time.c b/src/os/unix/ngx_time.c index cc760b2..c97bae2 100644 --- a/src/os/unix/ngx_time.c +++ b/src/os/unix/ngx_time.c @@ -43,7 +43,7 @@ ngx_timezone_update(void) struct tm *t; char buf[4]; - s = time(0); + s = time(NULL); t = localtime(&s); diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c index f732712..7207e3d 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, 1); + 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 ffa03a6..31f138c 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;