aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/nginx.h4
-rw-r--r--src/core/ngx_connection.c4
-rw-r--r--src/core/ngx_thread_pool.c2
-rw-r--r--src/event/modules/ngx_kqueue_module.c13
-rw-r--r--src/event/ngx_event_accept.c17
-rw-r--r--src/event/ngx_event_openssl.c18
-rw-r--r--src/event/ngx_event_openssl.h7
-rw-r--r--src/event/ngx_event_openssl_cache.c109
-rw-r--r--src/event/quic/ngx_event_quic.c51
-rw-r--r--src/event/quic/ngx_event_quic.h15
-rw-r--r--src/event/quic/ngx_event_quic_ack.c16
-rw-r--r--src/event/quic/ngx_event_quic_connection.h29
-rw-r--r--src/event/quic/ngx_event_quic_connid.c6
-rw-r--r--src/event/quic/ngx_event_quic_migration.c22
-rw-r--r--src/event/quic/ngx_event_quic_openssl_compat.c22
-rw-r--r--src/event/quic/ngx_event_quic_openssl_compat.h8
-rw-r--r--src/event/quic/ngx_event_quic_output.c40
-rw-r--r--src/event/quic/ngx_event_quic_protection.c19
-rw-r--r--src/event/quic/ngx_event_quic_protection.h14
-rw-r--r--src/event/quic/ngx_event_quic_ssl.c619
-rw-r--r--src/event/quic/ngx_event_quic_streams.c16
-rw-r--r--src/event/quic/ngx_event_quic_transport.c14
-rw-r--r--src/event/quic/ngx_event_quic_transport.h10
-rw-r--r--src/http/modules/ngx_http_addition_filter_module.c2
-rw-r--r--src/http/modules/ngx_http_charset_filter_module.c2
-rw-r--r--src/http/modules/ngx_http_fastcgi_module.c2
-rw-r--r--src/http/modules/ngx_http_grpc_module.c19
-rw-r--r--src/http/modules/ngx_http_gunzip_filter_module.c2
-rw-r--r--src/http/modules/ngx_http_gzip_filter_module.c2
-rw-r--r--src/http/modules/ngx_http_proxy_module.c41
-rw-r--r--src/http/modules/ngx_http_scgi_module.c2
-rw-r--r--src/http/modules/ngx_http_ssi_filter_module.c4
-rw-r--r--src/http/modules/ngx_http_sub_filter_module.c2
-rw-r--r--src/http/modules/ngx_http_uwsgi_module.c2
-rw-r--r--src/http/modules/ngx_http_xslt_filter_module.c2
-rw-r--r--src/http/ngx_http.c1
-rw-r--r--src/http/ngx_http.h2
-rw-r--r--src/http/ngx_http_core_module.c43
-rw-r--r--src/http/ngx_http_core_module.h2
-rw-r--r--src/http/ngx_http_header_filter_module.c107
-rw-r--r--src/http/ngx_http_request.h1
-rw-r--r--src/http/ngx_http_upstream.c159
-rw-r--r--src/http/ngx_http_upstream.h3
-rw-r--r--src/http/v2/ngx_http_v2.h1
-rw-r--r--src/http/v2/ngx_http_v2_filter_module.c202
-rw-r--r--src/http/v3/ngx_http_v3_filter_module.c150
-rw-r--r--src/os/unix/ngx_time.c2
47 files changed, 1527 insertions, 303 deletions
diff --git a/src/core/nginx.h b/src/core/nginx.h
index 72664a531..cf230af64 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -9,8 +9,8 @@
#define _NGINX_H_INCLUDED_
-#define nginx_version 1029000
-#define NGINX_VERSION "1.29.0"
+#define nginx_version 1029001
+#define NGINX_VERSION "1.29.1"
#define NGINX_VER "nginx/" NGINX_VERSION
#ifdef NGX_BUILD
diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c
index 75809d9ad..7cae295eb 100644
--- a/src/core/ngx_connection.c
+++ b/src/core/ngx_connection.c
@@ -765,6 +765,8 @@ ngx_configure_listening_sockets(ngx_cycle_t *cycle)
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+#if !(NGX_DARWIN)
+
if (ls[i].keepidle) {
value = ls[i].keepidle;
@@ -782,6 +784,8 @@ ngx_configure_listening_sockets(ngx_cycle_t *cycle)
}
}
+#endif
+
if (ls[i].keepintvl) {
value = ls[i].keepintvl;
diff --git a/src/core/ngx_thread_pool.c b/src/core/ngx_thread_pool.c
index 7fb0f7f8d..7bf90e958 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 9c7244c45..0c905eff3 100644
--- a/src/event/modules/ngx_kqueue_module.c
+++ b/src/event/modules/ngx_kqueue_module.c
@@ -10,6 +10,15 @@
#include <ngx_event.h>
+/* 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, &notify_kev, 1, NULL, 0, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
diff --git a/src/event/ngx_event_accept.c b/src/event/ngx_event_accept.c
index 27038799d..033d7e021 100644
--- a/src/event/ngx_event_accept.c
+++ b/src/event/ngx_event_accept.c
@@ -203,6 +203,23 @@ ngx_event_accept(ngx_event_t *ev)
}
}
+#if (NGX_HAVE_KEEPALIVE_TUNABLE && NGX_DARWIN)
+
+ /* Darwin doesn't inherit TCP_KEEPALIVE from a listening socket */
+
+ if (ls->keepidle) {
+ if (setsockopt(s, IPPROTO_TCP, TCP_KEEPALIVE,
+ (const void *) &ls->keepidle, sizeof(int))
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
+ "setsockopt(TCP_KEEPALIVE, %d) failed, ignored",
+ ls->keepidle);
+ }
+ }
+
+#endif
+
*log = ls->log;
c->recv = ngx_recv;
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index 7eb05209d..ff604c562 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -45,8 +45,6 @@ static ssize_t ngx_ssl_sendfile(ngx_connection_t *c, ngx_buf_t *file,
size_t size);
static void ngx_ssl_read_handler(ngx_event_t *rev);
static void ngx_ssl_shutdown_handler(ngx_event_t *ev);
-static void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr,
- ngx_err_t err, char *text);
static void ngx_ssl_clear_error(ngx_log_t *log);
static ngx_int_t ngx_ssl_session_id_context(ngx_ssl_t *ssl,
@@ -1376,7 +1374,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);
@@ -3301,7 +3299,7 @@ ngx_ssl_shutdown_handler(ngx_event_t *ev)
}
-static void
+void
ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err,
char *text)
{
@@ -5057,11 +5055,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);
@@ -5115,11 +5109,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;
@@ -5141,11 +5131,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 d4a62b82a..0c9e9e840 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;
@@ -361,6 +366,8 @@ ngx_chain_t *ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in,
off_t limit);
void ngx_ssl_free_buffer(ngx_connection_t *c);
ngx_int_t ngx_ssl_shutdown(ngx_connection_t *c);
+void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err,
+ char *text);
void ngx_cdecl ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
char *fmt, ...);
void ngx_ssl_cleanup_ctx(void *data);
diff --git a/src/event/ngx_event_openssl_cache.c b/src/event/ngx_event_openssl_cache.c
index d62b4c430..42f5e1c9f 100644
--- a/src/event/ngx_event_openssl_cache.c
+++ b/src/event/ngx_event_openssl_cache.c
@@ -8,10 +8,16 @@
#include <ngx_core.h>
#include <ngx_event.h>
+#ifdef ERR_R_OSSL_STORE_LIB
+#include <openssl/store.h>
+#include <openssl/ui.h>
+#endif
+
#define NGX_SSL_CACHE_PATH 0
#define NGX_SSL_CACHE_DATA 1
#define NGX_SSL_CACHE_ENGINE 2
+#define NGX_SSL_CACHE_STORE 3
#define NGX_SSL_CACHE_DISABLED (ngx_array_t *) (uintptr_t) -1
@@ -116,6 +122,8 @@ static void ngx_ssl_cache_node_insert(ngx_rbtree_node_t *temp,
static void ngx_ssl_cache_node_free(ngx_rbtree_t *rbtree,
ngx_ssl_cache_node_t *cn);
+static ngx_int_t ngx_openssl_cache_init_worker(ngx_cycle_t *cycle);
+
static ngx_command_t ngx_openssl_cache_commands[] = {
@@ -144,7 +152,7 @@ ngx_module_t ngx_openssl_cache_module = {
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
- NULL, /* init process */
+ ngx_openssl_cache_init_worker, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
@@ -444,6 +452,11 @@ ngx_ssl_cache_init_key(ngx_pool_t *pool, ngx_uint_t index, ngx_str_t *path,
{
id->type = NGX_SSL_CACHE_ENGINE;
+ } else if (index == NGX_SSL_CACHE_PKEY
+ && ngx_strncmp(path->data, "store:", sizeof("store:") - 1) == 0)
+ {
+ id->type = NGX_SSL_CACHE_STORE;
+
} else {
if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, path)
!= NGX_OK)
@@ -694,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";
@@ -714,11 +727,6 @@ ngx_ssl_cache_pkey_create(ngx_ssl_cache_key_t *id, char **err, void *data)
#endif
}
- bio = ngx_ssl_cache_create_bio(id, err);
- if (bio == NULL) {
- return NULL;
- }
-
cb_data.encrypted = 0;
if (*passwords) {
@@ -734,6 +742,76 @@ ngx_ssl_cache_pkey_create(ngx_ssl_cache_key_t *id, char **err, void *data)
cb = NULL;
}
+ if (id->type == NGX_SSL_CACHE_STORE) {
+
+#ifdef ERR_R_OSSL_STORE_LIB
+
+ u_char *uri;
+ UI_METHOD *method;
+ OSSL_STORE_CTX *store;
+ OSSL_STORE_INFO *info;
+
+ method = (cb != NULL) ? UI_UTIL_wrap_read_pem_callback(cb, 0) : NULL;
+ uri = id->data + sizeof("store:") - 1;
+
+ store = OSSL_STORE_open((char *) uri, method, pwd, NULL, NULL);
+
+ if (store == NULL) {
+ *err = "OSSL_STORE_open() failed";
+
+ if (method != NULL) {
+ UI_destroy_method(method);
+ }
+
+ return NULL;
+ }
+
+ pkey = NULL;
+
+ while (pkey == NULL && !OSSL_STORE_eof(store)) {
+ info = OSSL_STORE_load(store);
+
+ if (info == NULL) {
+ continue;
+ }
+
+ if (OSSL_STORE_INFO_get_type(info) == OSSL_STORE_INFO_PKEY) {
+ pkey = OSSL_STORE_INFO_get1_PKEY(info);
+ }
+
+ OSSL_STORE_INFO_free(info);
+ }
+
+ OSSL_STORE_close(store);
+
+ if (method != NULL) {
+ UI_destroy_method(method);
+ }
+
+ if (pkey == NULL) {
+ *err = "OSSL_STORE_load() failed";
+ return NULL;
+ }
+
+ if (cb_data.encrypted) {
+ *passwords = NGX_SSL_CACHE_DISABLED;
+ }
+
+ return pkey;
+
+#else
+
+ *err = "loading \"store:...\" certificate keys is not supported";
+ return NULL;
+
+#endif
+ }
+
+ bio = ngx_ssl_cache_create_bio(id, err);
+ if (bio == NULL) {
+ return NULL;
+ }
+
for ( ;; ) {
pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, pwd);
@@ -1157,3 +1235,20 @@ ngx_ssl_cache_node_insert(ngx_rbtree_node_t *temp,
node->right = sentinel;
ngx_rbt_red(node);
}
+
+
+static ngx_int_t
+ngx_openssl_cache_init_worker(ngx_cycle_t *cycle)
+{
+#ifdef ERR_R_OSSL_STORE_LIB
+
+ if (ngx_process != NGX_PROCESS_WORKER) {
+ return NGX_OK;
+ }
+
+ UI_set_default_method(UI_null());
+
+#endif
+
+ return NGX_OK;
+}
diff --git a/src/event/quic/ngx_event_quic.c b/src/event/quic/ngx_event_quic.c
index 4682ecad9..6a59aaf93 100644
--- a/src/event/quic/ngx_event_quic.c
+++ b/src/event/quic/ngx_event_quic.c
@@ -72,7 +72,7 @@ ngx_quic_connstate_dbg(ngx_connection_t *c)
if (qc) {
- if (qc->error != (ngx_uint_t) -1) {
+ if (qc->error) {
p = ngx_slprintf(p, last, "%s", qc->error_app ? " app" : "");
p = ngx_slprintf(p, last, " error:%ui", qc->error);
@@ -135,6 +135,9 @@ ngx_quic_apply_transport_params(ngx_connection_t *c, ngx_quic_tp_t *ctp)
if (scid.len != ctp->initial_scid.len
|| ngx_memcmp(scid.data, ctp->initial_scid.data, scid.len) != 0)
{
+ qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;
+ qc->error_reason = "invalid initial_source_connection_id";
+
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic client initial_source_connection_id mismatch");
return NGX_ERROR;
@@ -257,9 +260,9 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf,
qc->send_ctx[i].pending_ack = NGX_QUIC_UNSET_PN;
}
- qc->send_ctx[0].level = ssl_encryption_initial;
- qc->send_ctx[1].level = ssl_encryption_handshake;
- qc->send_ctx[2].level = ssl_encryption_application;
+ qc->send_ctx[0].level = NGX_QUIC_ENCRYPTION_INITIAL;
+ qc->send_ctx[1].level = NGX_QUIC_ENCRYPTION_HANDSHAKE;
+ qc->send_ctx[2].level = NGX_QUIC_ENCRYPTION_APPLICATION;
ngx_queue_init(&qc->free_frames);
@@ -517,7 +520,7 @@ ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc)
* to terminate the connection immediately.
*/
- if (qc->error == (ngx_uint_t) -1) {
+ if (qc->error == 0 && rc == NGX_ERROR) {
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
qc->error_app = 0;
}
@@ -797,13 +800,13 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
pkt->dcid.len, &pkt->dcid);
#if (NGX_DEBUG)
- if (pkt->level != ssl_encryption_application) {
+ if (pkt->level != NGX_QUIC_ENCRYPTION_APPLICATION) {
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic packet rx scid len:%uz %xV",
pkt->scid.len, &pkt->scid);
}
- if (pkt->level == ssl_encryption_initial) {
+ if (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL) {
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic address validation token len:%uz %xV",
pkt->token.len, &pkt->token);
@@ -820,7 +823,7 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
return NGX_DECLINED;
}
- if (pkt->level != ssl_encryption_application) {
+ if (pkt->level != NGX_QUIC_ENCRYPTION_APPLICATION) {
if (pkt->version != qc->version) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
@@ -850,7 +853,9 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
rc = ngx_quic_handle_payload(c, pkt);
- if (rc == NGX_DECLINED && pkt->level == ssl_encryption_application) {
+ if (rc == NGX_DECLINED
+ && pkt->level == NGX_QUIC_ENCRYPTION_APPLICATION)
+ {
if (ngx_quic_handle_stateless_reset(c, pkt) == NGX_OK) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic stateless reset packet detected");
@@ -871,11 +876,11 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
return ngx_quic_negotiate_version(c, pkt);
}
- if (pkt->level == ssl_encryption_application) {
+ if (pkt->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
return ngx_quic_send_stateless_reset(c, conf, pkt);
}
- if (pkt->level != ssl_encryption_initial) {
+ if (pkt->level != NGX_QUIC_ENCRYPTION_INITIAL) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic expected initial, got handshake");
return NGX_ERROR;
@@ -958,8 +963,8 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
qc = ngx_quic_get_connection(c);
- qc->error = (ngx_uint_t) -1;
- qc->error_reason = 0;
+ qc->error = 0;
+ qc->error_reason = NULL;
c->log->action = "decrypting packet";
@@ -970,10 +975,10 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
return NGX_DECLINED;
}
-#if !defined (OPENSSL_IS_BORINGSSL)
- /* OpenSSL provides read keys for an application level before it's ready */
+#if (NGX_QUIC_QUICTLS_API)
+ /* QuicTLS provides app read keys before completing handshake */
- if (pkt->level == ssl_encryption_application && !c->ssl->handshaked) {
+ if (pkt->level == NGX_QUIC_ENCRYPTION_APPLICATION && !c->ssl->handshaked) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic no %s keys ready, ignoring packet",
ngx_quic_level_name(pkt->level));
@@ -1011,14 +1016,14 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
}
}
- if (pkt->level == ssl_encryption_handshake) {
+ if (pkt->level == NGX_QUIC_ENCRYPTION_HANDSHAKE) {
/*
* RFC 9001, 4.9.1. Discarding Initial Keys
*
* The successful use of Handshake packets indicates
* that no more Initial packets need to be exchanged
*/
- ngx_quic_discard_ctx(c, ssl_encryption_initial);
+ ngx_quic_discard_ctx(c, NGX_QUIC_ENCRYPTION_INITIAL);
if (!qc->path->validated) {
qc->path->validated = 1;
@@ -1027,14 +1032,14 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
}
}
- if (pkt->level == ssl_encryption_application) {
+ if (pkt->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
/*
* RFC 9001, 4.9.3. Discarding 0-RTT Keys
*
* After receiving a 1-RTT packet, servers MUST discard
* 0-RTT keys within a short time
*/
- ngx_quic_keys_discard(qc->keys, ssl_encryption_early_data);
+ ngx_quic_keys_discard(qc->keys, NGX_QUIC_ENCRYPTION_EARLY_DATA);
}
if (qc->closing) {
@@ -1061,7 +1066,7 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
c->log->action = "handling payload";
- if (pkt->level != ssl_encryption_application) {
+ if (pkt->level != NGX_QUIC_ENCRYPTION_APPLICATION) {
return ngx_quic_handle_frames(c, pkt);
}
@@ -1086,7 +1091,7 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
void
-ngx_quic_discard_ctx(ngx_connection_t *c, enum ssl_encryption_level_t level)
+ngx_quic_discard_ctx(ngx_connection_t *c, ngx_uint_t level)
{
ngx_queue_t *q;
ngx_quic_frame_t *f;
@@ -1127,7 +1132,7 @@ ngx_quic_discard_ctx(ngx_connection_t *c, enum ssl_encryption_level_t level)
ngx_quic_free_frame(c, f);
}
- if (level == ssl_encryption_initial) {
+ if (level == NGX_QUIC_ENCRYPTION_INITIAL) {
/* close temporary listener with initial dcid */
qsock = ngx_quic_find_socket(c, NGX_QUIC_UNSET_PN);
if (qsock) {
diff --git a/src/event/quic/ngx_event_quic.h b/src/event/quic/ngx_event_quic.h
index 15201671d..bab085f46 100644
--- a/src/event/quic/ngx_event_quic.h
+++ b/src/event/quic/ngx_event_quic.h
@@ -12,6 +12,21 @@
#include <ngx_core.h>
+#if (OPENSSL_VERSION_NUMBER >= 0x30500010L)
+#define NGX_QUIC_OPENSSL_API 1
+
+#elif (defined SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION)
+#define NGX_QUIC_QUICTLS_API 1
+
+#elif (defined OPENSSL_IS_BORINGSSL || defined LIBRESSL_VERSION_NUMBER)
+#define NGX_QUIC_BORINGSSL_API 1
+
+#else
+#define NGX_QUIC_BORINGSSL_API 1
+#define NGX_QUIC_OPENSSL_COMPAT 1
+#endif
+
+
#define NGX_QUIC_MAX_UDP_PAYLOAD_SIZE 65527
#define NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT 3
diff --git a/src/event/quic/ngx_event_quic_ack.c b/src/event/quic/ngx_event_quic_ack.c
index c7fd96c2c..abd3f7ade 100644
--- a/src/event/quic/ngx_event_quic_ack.c
+++ b/src/event/quic/ngx_event_quic_ack.c
@@ -36,7 +36,7 @@ typedef struct {
static ngx_inline ngx_msec_t ngx_quic_time_threshold(ngx_quic_connection_t *qc);
static uint64_t ngx_quic_packet_threshold(ngx_quic_send_ctx_t *ctx);
static void ngx_quic_rtt_sample(ngx_connection_t *c, ngx_quic_ack_frame_t *ack,
- enum ssl_encryption_level_t level, ngx_msec_t send_time);
+ ngx_uint_t level, ngx_msec_t send_time);
static ngx_int_t ngx_quic_handle_ack_frame_range(ngx_connection_t *c,
ngx_quic_send_ctx_t *ctx, uint64_t min, uint64_t max,
ngx_quic_ack_stat_t *st);
@@ -108,7 +108,7 @@ ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
ctx = ngx_quic_get_send_ctx(qc, pkt->level);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic ngx_quic_handle_ack_frame level:%d", pkt->level);
+ "quic ngx_quic_handle_ack_frame level:%ui", pkt->level);
ack = &f->u.ack;
@@ -207,7 +207,7 @@ ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
static void
ngx_quic_rtt_sample(ngx_connection_t *c, ngx_quic_ack_frame_t *ack,
- enum ssl_encryption_level_t level, ngx_msec_t send_time)
+ ngx_uint_t level, ngx_msec_t send_time)
{
ngx_msec_t latest_rtt, ack_delay, adjusted_rtt, rttvar_sample;
ngx_quic_connection_t *qc;
@@ -260,7 +260,7 @@ ngx_quic_handle_ack_frame_range(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
qc = ngx_quic_get_connection(c);
- if (ctx->level == ssl_encryption_application) {
+ if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
if (ngx_quic_handle_path_mtu(c, qc->path, min, max) != NGX_OK) {
return NGX_ERROR;
}
@@ -634,7 +634,7 @@ ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
wait = start->send_time + thr - now;
ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic detect_lost pnum:%uL thr:%M pthr:%uL wait:%i level:%d",
+ "quic detect_lost pnum:%uL thr:%M pthr:%uL wait:%i level:%ui",
start->pnum, thr, pkt_thr, (ngx_int_t) wait, start->level);
if ((ngx_msec_int_t) wait > 0
@@ -787,7 +787,7 @@ ngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
switch (f->type) {
case NGX_QUIC_FT_ACK:
case NGX_QUIC_FT_ACK_ECN:
- if (ctx->level == ssl_encryption_application) {
+ if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
/* force generation of most recent acknowledgment */
ctx->send_ack = NGX_QUIC_MAX_ACK_GAP;
}
@@ -1073,7 +1073,7 @@ ngx_quic_pto(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
duration = qc->avg_rtt;
duration += ngx_max(4 * qc->rttvar, NGX_QUIC_TIME_GRANULARITY);
- if (ctx->level == ssl_encryption_application && c->ssl->handshaked) {
+ if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION && c->ssl->handshaked) {
duration += qc->ctp.max_ack_delay;
}
@@ -1428,7 +1428,7 @@ ngx_quic_generate_ack(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
return NGX_OK;
}
- if (ctx->level == ssl_encryption_application) {
+ if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
delay = ngx_current_msec - ctx->ack_delay_start;
qc = ngx_quic_get_connection(c);
diff --git a/src/event/quic/ngx_event_quic_connection.h b/src/event/quic/ngx_event_quic_connection.h
index 04cda859e..33922cf80 100644
--- a/src/event/quic/ngx_event_quic_connection.h
+++ b/src/event/quic/ngx_event_quic_connection.h
@@ -17,6 +17,15 @@
/* #define NGX_QUIC_DEBUG_ALLOC */ /* log frames and bufs alloc */
/* #define NGX_QUIC_DEBUG_CRYPTO */
+#define NGX_QUIC_ENCRYPTION_INITIAL 0
+#define NGX_QUIC_ENCRYPTION_EARLY_DATA 1
+#define NGX_QUIC_ENCRYPTION_HANDSHAKE 2
+#define NGX_QUIC_ENCRYPTION_APPLICATION 3
+#define NGX_QUIC_ENCRYPTION_LAST 4
+
+#define NGX_QUIC_SEND_CTX_LAST (NGX_QUIC_ENCRYPTION_LAST - 1)
+
+
typedef struct ngx_quic_connection_s ngx_quic_connection_t;
typedef struct ngx_quic_server_id_s ngx_quic_server_id_t;
typedef struct ngx_quic_client_id_s ngx_quic_client_id_t;
@@ -46,8 +55,6 @@ typedef struct ngx_quic_keys_s ngx_quic_keys_t;
#define NGX_QUIC_UNSET_PN (uint64_t) -1
-#define NGX_QUIC_SEND_CTX_LAST (NGX_QUIC_ENCRYPTION_LAST - 1)
-
/* 0-RTT and 1-RTT data exist in the same packet number space,
* so we have 3 packet number spaces:
*
@@ -56,9 +63,9 @@ typedef struct ngx_quic_keys_s ngx_quic_keys_t;
* 2 - 0-RTT and 1-RTT
*/
#define ngx_quic_get_send_ctx(qc, level) \
- ((level) == ssl_encryption_initial) ? &((qc)->send_ctx[0]) \
- : (((level) == ssl_encryption_handshake) ? &((qc)->send_ctx[1]) \
- : &((qc)->send_ctx[2]))
+ ((level) == NGX_QUIC_ENCRYPTION_INITIAL) ? &((qc)->send_ctx[0]) \
+ : (((level) == NGX_QUIC_ENCRYPTION_HANDSHAKE) ? &((qc)->send_ctx[1]) \
+ : &((qc)->send_ctx[2]))
#define ngx_quic_get_connection(c) \
(((c)->udp) ? (((ngx_quic_socket_t *)((c)->udp))->quic) : NULL)
@@ -188,7 +195,7 @@ typedef struct {
* are also Initial packets.
*/
struct ngx_quic_send_ctx_s {
- enum ssl_encryption_level_t level;
+ ngx_uint_t level;
ngx_quic_buffer_t crypto;
uint64_t crypto_sent;
@@ -279,7 +286,7 @@ struct ngx_quic_connection_s {
off_t received;
ngx_uint_t error;
- enum ssl_encryption_level_t error_level;
+ ngx_uint_t error_level;
ngx_uint_t error_ftype;
const char *error_reason;
@@ -294,13 +301,17 @@ struct ngx_quic_connection_s {
unsigned key_phase:1;
unsigned validated:1;
unsigned client_tp_done:1;
+
+#if (NGX_QUIC_OPENSSL_API)
+ unsigned read_level:2;
+ unsigned write_level:2;
+#endif
};
ngx_int_t ngx_quic_apply_transport_params(ngx_connection_t *c,
ngx_quic_tp_t *ctp);
-void ngx_quic_discard_ctx(ngx_connection_t *c,
- enum ssl_encryption_level_t level);
+void ngx_quic_discard_ctx(ngx_connection_t *c, ngx_uint_t level);
void ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc);
void ngx_quic_shutdown_quic(ngx_connection_t *c);
diff --git a/src/event/quic/ngx_event_quic_connid.c b/src/event/quic/ngx_event_quic_connid.c
index f50868205..4e7b8dc22 100644
--- a/src/event/quic/ngx_event_quic_connid.c
+++ b/src/event/quic/ngx_event_quic_connid.c
@@ -99,7 +99,7 @@ ngx_quic_handle_new_connection_id_frame(ngx_connection_t *c,
return NGX_ERROR;
}
- frame->level = ssl_encryption_application;
+ frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID;
frame->u.retire_cid.sequence_number = f->seqnum;
@@ -452,7 +452,7 @@ ngx_quic_send_server_id(ngx_connection_t *c, ngx_quic_server_id_t *sid)
return NGX_ERROR;
}
- frame->level = ssl_encryption_application;
+ frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_NEW_CONNECTION_ID;
frame->u.ncid.seqnum = sid->seqnum;
frame->u.ncid.retire = 0;
@@ -485,7 +485,7 @@ ngx_quic_free_client_id(ngx_connection_t *c, ngx_quic_client_id_t *cid)
return NGX_ERROR;
}
- frame->level = ssl_encryption_application;
+ frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID;
frame->u.retire_cid.sequence_number = cid->seqnum;
diff --git a/src/event/quic/ngx_event_quic_migration.c b/src/event/quic/ngx_event_quic_migration.c
index 6befc3427..42354ca66 100644
--- a/src/event/quic/ngx_event_quic_migration.c
+++ b/src/event/quic/ngx_event_quic_migration.c
@@ -40,7 +40,7 @@ ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
ngx_quic_frame_t *fp;
ngx_quic_connection_t *qc;
- if (pkt->level != ssl_encryption_application || pkt->path_challenged) {
+ if (pkt->level != NGX_QUIC_ENCRYPTION_APPLICATION || pkt->path_challenged) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ignoring PATH_CHALLENGE");
return NGX_OK;
@@ -55,7 +55,7 @@ ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
return NGX_ERROR;
}
- fp->level = ssl_encryption_application;
+ fp->level = NGX_QUIC_ENCRYPTION_APPLICATION;
fp->type = NGX_QUIC_FT_PATH_RESPONSE;
fp->u.path_response = *f;
@@ -93,7 +93,7 @@ ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
return NGX_ERROR;
}
- fp->level = ssl_encryption_application;
+ fp->level = NGX_QUIC_ENCRYPTION_APPLICATION;
fp->type = NGX_QUIC_FT_PING;
ngx_quic_queue_frame(qc, fp);
@@ -177,7 +177,7 @@ valid:
if (rst) {
/* prevent old path packets contribution to congestion control */
- ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
+ ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
qc->rst_pnum = ctx->pnum;
ngx_memzero(&qc->congestion, sizeof(ngx_quic_congestion_t));
@@ -549,7 +549,7 @@ ngx_quic_validate_path(ngx_connection_t *c, ngx_quic_path_t *path)
(void) ngx_quic_send_path_challenge(c, path);
- ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
+ ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
pto = ngx_max(ngx_quic_pto(c, ctx), 1000);
path->expires = ngx_current_msec + pto;
@@ -579,7 +579,7 @@ ngx_quic_send_path_challenge(ngx_connection_t *c, ngx_quic_path_t *path)
return NGX_ERROR;
}
- frame->level = ssl_encryption_application;
+ frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_PATH_CHALLENGE;
ngx_memcpy(frame->u.path_challenge.data, path->challenge[n], 8);
@@ -767,7 +767,7 @@ ngx_quic_expire_path_validation(ngx_connection_t *c, ngx_quic_path_t *path)
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
- ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
+ ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
if (++path->tries < NGX_QUIC_PATH_RETRIES) {
pto = ngx_max(ngx_quic_pto(c, ctx), 1000) << path->tries;
@@ -830,7 +830,7 @@ ngx_quic_expire_path_mtu_delay(ngx_connection_t *c, ngx_quic_path_t *path)
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
- ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
+ ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
path->tries = 0;
@@ -876,7 +876,7 @@ ngx_quic_expire_path_mtu_discovery(ngx_connection_t *c, ngx_quic_path_t *path)
ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
- ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
+ ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
if (++path->tries < NGX_QUIC_PATH_RETRIES) {
rc = ngx_quic_send_path_mtu_probe(c, path);
@@ -922,13 +922,13 @@ ngx_quic_send_path_mtu_probe(ngx_connection_t *c, ngx_quic_path_t *path)
return NGX_ERROR;
}
- frame->level = ssl_encryption_application;
+ frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_PING;
frame->ignore_loss = 1;
frame->ignore_congestion = 1;
qc = ngx_quic_get_connection(c);
- ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
+ ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
pnum = ctx->pnum;
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
diff --git a/src/event/quic/ngx_event_quic_openssl_compat.c b/src/event/quic/ngx_event_quic_openssl_compat.c
index 6052bc683..58298dcb8 100644
--- a/src/event/quic/ngx_event_quic_openssl_compat.c
+++ b/src/event/quic/ngx_event_quic_openssl_compat.c
@@ -35,8 +35,6 @@ typedef struct {
ngx_str_t payload;
uint64_t number;
ngx_quic_compat_keys_t *keys;
-
- enum ssl_encryption_level_t level;
} ngx_quic_compat_record_t;
@@ -435,11 +433,10 @@ ngx_quic_compat_message_callback(int write_p, int version, int content_type,
case SSL3_RT_HANDSHAKE:
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic compat tx %s len:%uz ",
- ngx_quic_level_name(level), len);
+ "quic compat tx level:%d len:%uz", level, len);
if (com->method->add_handshake_data(ssl, level, buf, len) != 1) {
- goto failed;
+ return;
}
break;
@@ -449,11 +446,11 @@ ngx_quic_compat_message_callback(int write_p, int version, int content_type,
alert = ((u_char *) buf)[1];
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic compat %s alert:%ui len:%uz ",
- ngx_quic_level_name(level), alert, len);
+ "quic compat level:%d alert:%ui len:%uz",
+ level, alert, len);
if (com->method->send_alert(ssl, level, alert) != 1) {
- goto failed;
+ return;
}
}
@@ -461,10 +458,6 @@ ngx_quic_compat_message_callback(int write_p, int version, int content_type,
}
return;
-
-failed:
-
- ngx_post_event(&qc->close, &ngx_posted_events);
}
@@ -487,8 +480,8 @@ SSL_provide_quic_data(SSL *ssl, enum ssl_encryption_level_t level,
c = ngx_ssl_get_connection(ssl);
- ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic compat rx %s len:%uz",
- ngx_quic_level_name(level), len);
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic compat rx level:%d len:%uz", level, len);
qc = ngx_quic_get_connection(c);
com = qc->compat;
@@ -501,7 +494,6 @@ SSL_provide_quic_data(SSL *ssl, enum ssl_encryption_level_t level,
rec.log = c->log;
rec.number = com->read_record++;
rec.keys = &com->keys;
- rec.level = level;
if (level == ssl_encryption_initial) {
n = ngx_min(len, 65535);
diff --git a/src/event/quic/ngx_event_quic_openssl_compat.h b/src/event/quic/ngx_event_quic_openssl_compat.h
index 77cc3cb0d..89ee41e89 100644
--- a/src/event/quic/ngx_event_quic_openssl_compat.h
+++ b/src/event/quic/ngx_event_quic_openssl_compat.h
@@ -7,11 +7,6 @@
#ifndef _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_
#define _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_
-#if defined SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION \
- || defined LIBRESSL_VERSION_NUMBER
-#undef NGX_QUIC_OPENSSL_COMPAT
-#else
-
#include <ngx_config.h>
#include <ngx_core.h>
@@ -53,7 +48,4 @@ int SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params,
void SSL_get_peer_quic_transport_params(const SSL *ssl,
const uint8_t **out_params, size_t *out_params_len);
-
-#endif /* TLSEXT_TYPE_quic_transport_parameters */
-
#endif /* _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_ */
diff --git a/src/event/quic/ngx_event_quic_output.c b/src/event/quic/ngx_event_quic_output.c
index a92a539f3..8c3350504 100644
--- a/src/event/quic/ngx_event_quic_output.c
+++ b/src/event/quic/ngx_event_quic_output.c
@@ -55,7 +55,8 @@ static ssize_t ngx_quic_send_segments(ngx_connection_t *c, u_char *buf,
size_t len, struct sockaddr *sockaddr, socklen_t socklen, size_t segment);
#endif
static ssize_t ngx_quic_output_packet(ngx_connection_t *c,
- ngx_quic_send_ctx_t *ctx, u_char *data, size_t max, size_t min);
+ ngx_quic_send_ctx_t *ctx, u_char *data, size_t max, size_t min,
+ ngx_uint_t ack_only);
static void ngx_quic_init_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
ngx_quic_header_t *pkt, ngx_quic_path_t *path);
static ngx_uint_t ngx_quic_get_padding_level(ngx_connection_t *c);
@@ -131,8 +132,7 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
ngx_memzero(preserved_pnum, sizeof(preserved_pnum));
#endif
- while (cg->in_flight < cg->window) {
-
+ do {
p = dst;
len = ngx_quic_path_limit(c, path, path->mtu);
@@ -158,7 +158,8 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
return NGX_OK;
}
- n = ngx_quic_output_packet(c, ctx, p, len, min);
+ n = ngx_quic_output_packet(c, ctx, p, len, min,
+ cg->in_flight >= cg->window);
if (n == NGX_ERROR) {
return NGX_ERROR;
}
@@ -187,7 +188,8 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
ngx_quic_commit_send(c);
path->sent += len;
- }
+
+ } while (cg->in_flight < cg->window);
return NGX_OK;
}
@@ -292,17 +294,17 @@ ngx_quic_allow_segmentation(ngx_connection_t *c)
return 0;
}
- ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_initial);
+ ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_INITIAL);
if (!ngx_queue_empty(&ctx->frames)) {
return 0;
}
- ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_handshake);
+ ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_HANDSHAKE);
if (!ngx_queue_empty(&ctx->frames)) {
return 0;
}
- ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
+ ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
bytes = 0;
len = ngx_min(qc->path->mtu, NGX_QUIC_MAX_UDP_SEGMENT_BUF);
@@ -315,6 +317,10 @@ ngx_quic_allow_segmentation(ngx_connection_t *c)
bytes += f->len;
+ if (qc->congestion.in_flight + bytes >= qc->congestion.window) {
+ return 0;
+ }
+
if (bytes > len * 3) {
/* require at least ~3 full packets to batch */
return 1;
@@ -343,7 +349,7 @@ ngx_quic_create_segments(ngx_connection_t *c)
cg = &qc->congestion;
path = qc->path;
- ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
+ ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
if (ngx_quic_generate_ack(c, ctx) != NGX_OK) {
return NGX_ERROR;
@@ -364,7 +370,7 @@ ngx_quic_create_segments(ngx_connection_t *c)
if (len && cg->in_flight + (p - dst) < cg->window) {
- n = ngx_quic_output_packet(c, ctx, p, len, len);
+ n = ngx_quic_output_packet(c, ctx, p, len, len, 0);
if (n == NGX_ERROR) {
return NGX_ERROR;
}
@@ -494,7 +500,7 @@ ngx_quic_get_padding_level(ngx_connection_t *c)
*/
qc = ngx_quic_get_connection(c);
- ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_initial);
+ ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_INITIAL);
for (q = ngx_queue_head(&ctx->frames);
q != ngx_queue_sentinel(&ctx->frames);
@@ -521,7 +527,7 @@ ngx_quic_get_padding_level(ngx_connection_t *c)
static ssize_t
ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
- u_char *data, size_t max, size_t min)
+ u_char *data, size_t max, size_t min, ngx_uint_t ack_only)
{
size_t len, pad, min_payload, max_payload;
u_char *p;
@@ -585,6 +591,10 @@ ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
{
f = ngx_queue_data(q, ngx_quic_frame_t, queue);
+ if (ack_only && f->type != NGX_QUIC_FT_ACK) {
+ break;
+ }
+
if (len >= max_payload) {
break;
}
@@ -677,10 +687,10 @@ ngx_quic_init_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
pkt->flags = NGX_QUIC_PKT_FIXED_BIT;
- if (ctx->level == ssl_encryption_initial) {
+ if (ctx->level == NGX_QUIC_ENCRYPTION_INITIAL) {
pkt->flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_INITIAL;
- } else if (ctx->level == ssl_encryption_handshake) {
+ } else if (ctx->level == NGX_QUIC_ENCRYPTION_HANDSHAKE) {
pkt->flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_HANDSHAKE;
} else {
@@ -1093,7 +1103,7 @@ ngx_quic_send_new_token(ngx_connection_t *c, ngx_quic_path_t *path)
return NGX_ERROR;
}
- frame->level = ssl_encryption_application;
+ frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_NEW_TOKEN;
frame->data = out;
frame->u.token.length = token.len;
diff --git a/src/event/quic/ngx_event_quic_protection.c b/src/event/quic/ngx_event_quic_protection.c
index e5c0df7b4..885843d72 100644
--- a/src/event/quic/ngx_event_quic_protection.c
+++ b/src/event/quic/ngx_event_quic_protection.c
@@ -130,8 +130,8 @@ ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys, ngx_str_t *secret,
0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a
};
- client = &keys->secrets[ssl_encryption_initial].client;
- server = &keys->secrets[ssl_encryption_initial].server;
+ client = &keys->secrets[NGX_QUIC_ENCRYPTION_INITIAL].client;
+ server = &keys->secrets[NGX_QUIC_ENCRYPTION_INITIAL].server;
/*
* RFC 9001, section 5. Packet Protection
@@ -656,8 +656,8 @@ ngx_quic_crypto_hp_cleanup(ngx_quic_secret_t *s)
ngx_int_t
ngx_quic_keys_set_encryption_secret(ngx_log_t *log, ngx_uint_t is_write,
- ngx_quic_keys_t *keys, enum ssl_encryption_level_t level,
- const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len)
+ ngx_quic_keys_t *keys, ngx_uint_t level, const SSL_CIPHER *cipher,
+ const uint8_t *secret, size_t secret_len)
{
ngx_int_t key_len;
ngx_str_t secret_str;
@@ -722,8 +722,8 @@ ngx_quic_keys_set_encryption_secret(ngx_log_t *log, ngx_uint_t is_write,
ngx_uint_t
-ngx_quic_keys_available(ngx_quic_keys_t *keys,
- enum ssl_encryption_level_t level, ngx_uint_t is_write)
+ngx_quic_keys_available(ngx_quic_keys_t *keys, ngx_uint_t level,
+ ngx_uint_t is_write)
{
if (is_write == 0) {
return keys->secrets[level].client.ctx != NULL;
@@ -734,8 +734,7 @@ ngx_quic_keys_available(ngx_quic_keys_t *keys,
void
-ngx_quic_keys_discard(ngx_quic_keys_t *keys,
- enum ssl_encryption_level_t level)
+ngx_quic_keys_discard(ngx_quic_keys_t *keys, ngx_uint_t level)
{
ngx_quic_secret_t *client, *server;
@@ -765,7 +764,7 @@ ngx_quic_keys_switch(ngx_connection_t *c, ngx_quic_keys_t *keys)
{
ngx_quic_secrets_t *current, *next, tmp;
- current = &keys->secrets[ssl_encryption_application];
+ current = &keys->secrets[NGX_QUIC_ENCRYPTION_APPLICATION];
next = &keys->next_key;
ngx_quic_crypto_cleanup(&current->client);
@@ -794,7 +793,7 @@ ngx_quic_keys_update(ngx_event_t *ev)
qc = ngx_quic_get_connection(c);
keys = qc->keys;
- current = &keys->secrets[ssl_encryption_application];
+ current = &keys->secrets[NGX_QUIC_ENCRYPTION_APPLICATION];
next = &keys->next_key;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic key update");
diff --git a/src/event/quic/ngx_event_quic_protection.h b/src/event/quic/ngx_event_quic_protection.h
index c09456f53..fddc6083a 100644
--- a/src/event/quic/ngx_event_quic_protection.h
+++ b/src/event/quic/ngx_event_quic_protection.h
@@ -14,8 +14,6 @@
#include <ngx_event_quic_transport.h>
-#define NGX_QUIC_ENCRYPTION_LAST ((ssl_encryption_application) + 1)
-
/* RFC 5116, 5.1/5.3 and RFC 8439, 2.3/2.5 for all supported ciphers */
#define NGX_QUIC_IV_LEN 12
#define NGX_QUIC_TAG_LEN 16
@@ -94,13 +92,11 @@ typedef struct {
ngx_int_t ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys,
ngx_str_t *secret, ngx_log_t *log);
ngx_int_t ngx_quic_keys_set_encryption_secret(ngx_log_t *log,
- ngx_uint_t is_write, ngx_quic_keys_t *keys,
- enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
- const uint8_t *secret, size_t secret_len);
-ngx_uint_t ngx_quic_keys_available(ngx_quic_keys_t *keys,
- enum ssl_encryption_level_t level, ngx_uint_t is_write);
-void ngx_quic_keys_discard(ngx_quic_keys_t *keys,
- enum ssl_encryption_level_t level);
+ ngx_uint_t is_write, ngx_quic_keys_t *keys, ngx_uint_t level,
+ const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len);
+ngx_uint_t ngx_quic_keys_available(ngx_quic_keys_t *keys, ngx_uint_t level,
+ ngx_uint_t is_write);
+void ngx_quic_keys_discard(ngx_quic_keys_t *keys, ngx_uint_t level);
void ngx_quic_keys_switch(ngx_connection_t *c, ngx_quic_keys_t *keys);
void ngx_quic_keys_update(ngx_event_t *ev);
void ngx_quic_keys_cleanup(ngx_quic_keys_t *keys);
diff --git a/src/event/quic/ngx_event_quic_ssl.c b/src/event/quic/ngx_event_quic_ssl.c
index ba0b5929f..e961c80cd 100644
--- a/src/event/quic/ngx_event_quic_ssl.c
+++ b/src/event/quic/ngx_event_quic_ssl.c
@@ -10,13 +10,6 @@
#include <ngx_event_quic_connection.h>
-#if defined OPENSSL_IS_BORINGSSL \
- || defined LIBRESSL_VERSION_NUMBER \
- || NGX_QUIC_OPENSSL_COMPAT
-#define NGX_QUIC_BORINGSSL_API 1
-#endif
-
-
/*
* RFC 9000, 7.5. Cryptographic Message Buffering
*
@@ -25,43 +18,343 @@
#define NGX_QUIC_MAX_BUFFERED 65535
+#if (NGX_QUIC_OPENSSL_API)
+
+static int ngx_quic_cbs_send(ngx_ssl_conn_t *ssl_conn,
+ const unsigned char *data, size_t len, size_t *consumed, void *arg);
+static int ngx_quic_cbs_recv_rcd(ngx_ssl_conn_t *ssl_conn,
+ const unsigned char **data, size_t *bytes_read, void *arg);
+static int ngx_quic_cbs_release_rcd(ngx_ssl_conn_t *ssl_conn,
+ size_t bytes_read, void *arg);
+static int ngx_quic_cbs_yield_secret(ngx_ssl_conn_t *ssl_conn, uint32_t level,
+ int direction, const unsigned char *secret, size_t secret_len, void *arg);
+static int ngx_quic_cbs_got_transport_params(ngx_ssl_conn_t *ssl_conn,
+ const unsigned char *params, size_t params_len, void *arg);
+static int ngx_quic_cbs_alert(ngx_ssl_conn_t *ssl_conn, unsigned char alert,
+ void *arg);
+
+#else /* NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API */
+
+static ngx_inline ngx_uint_t ngx_quic_map_encryption_level(
+ enum ssl_encryption_level_t ssl_level);
+
#if (NGX_QUIC_BORINGSSL_API)
static int ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
- enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
+ enum ssl_encryption_level_t ssl_level, const SSL_CIPHER *cipher,
const uint8_t *secret, size_t secret_len);
static int ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,
- enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
+ enum ssl_encryption_level_t ssl_level, const SSL_CIPHER *cipher,
const uint8_t *secret, size_t secret_len);
-#else
+#else /* NGX_QUIC_QUICTLS_API */
static int ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
- enum ssl_encryption_level_t level, const uint8_t *read_secret,
+ enum ssl_encryption_level_t ssl_level, const uint8_t *read_secret,
const uint8_t *write_secret, size_t secret_len);
#endif
static int ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
- enum ssl_encryption_level_t level, const uint8_t *data, size_t len);
+ enum ssl_encryption_level_t ssl_level, const uint8_t *data, size_t len);
static int ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn);
static int ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn,
- enum ssl_encryption_level_t level, uint8_t alert);
-static ngx_int_t ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
- enum ssl_encryption_level_t level);
+ enum ssl_encryption_level_t ssl_level, uint8_t alert);
+
+#endif
+
+static ngx_int_t ngx_quic_handshake(ngx_connection_t *c);
+static ngx_int_t ngx_quic_crypto_provide(ngx_connection_t *c, ngx_uint_t level);
+
+
+#if (NGX_QUIC_OPENSSL_API)
+
+static int
+ngx_quic_cbs_send(ngx_ssl_conn_t *ssl_conn,
+ const unsigned char *data, size_t len, size_t *consumed, void *arg)
+{
+ ngx_connection_t *c = arg;
+
+ ngx_chain_t *out;
+ unsigned int alpn_len;
+ ngx_quic_frame_t *frame;
+ const unsigned char *alpn_data;
+ ngx_quic_send_ctx_t *ctx;
+ ngx_quic_connection_t *qc;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic ngx_quic_cbs_send len:%uz", len);
+
+ qc = ngx_quic_get_connection(c);
+
+ *consumed = 0;
+
+ SSL_get0_alpn_selected(ssl_conn, &alpn_data, &alpn_len);
+
+ if (alpn_len == 0) {
+ qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_NO_APPLICATION_PROTOCOL);
+ qc->error_reason = "missing ALPN extension";
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "quic missing ALPN extension");
+ return 1;
+ }
+
+ if (!qc->client_tp_done) {
+ /* RFC 9001, 8.2. QUIC Transport Parameters Extension */
+ qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_MISSING_EXTENSION);
+ qc->error_reason = "missing transport parameters";
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "missing transport parameters");
+ return 1;
+ }
+
+ ctx = ngx_quic_get_send_ctx(qc, qc->write_level);
+
+ out = ngx_quic_copy_buffer(c, (u_char *) data, len);
+ if (out == NGX_CHAIN_ERROR) {
+ qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
+ return 1;
+ }
+
+ frame = ngx_quic_alloc_frame(c);
+ if (frame == NULL) {
+ qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
+ return 1;
+ }
+
+ frame->data = out;
+ frame->level = qc->write_level;
+ frame->type = NGX_QUIC_FT_CRYPTO;
+ frame->u.crypto.offset = ctx->crypto_sent;
+ frame->u.crypto.length = len;
+
+ ctx->crypto_sent += len;
+ *consumed = len;
+
+ ngx_quic_queue_frame(qc, frame);
+
+ return 1;
+}
+
+
+static int
+ngx_quic_cbs_recv_rcd(ngx_ssl_conn_t *ssl_conn,
+ const unsigned char **data, size_t *bytes_read, void *arg)
+{
+ ngx_connection_t *c = arg;
+
+ ngx_buf_t *b;
+ ngx_chain_t *cl;
+ ngx_quic_send_ctx_t *ctx;
+ ngx_quic_connection_t *qc;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic ngx_quic_cbs_recv_rcd");
+
+ qc = ngx_quic_get_connection(c);
+ ctx = ngx_quic_get_send_ctx(qc, qc->read_level);
+
+ for (cl = ctx->crypto.chain; cl; cl = cl->next) {
+ b = cl->buf;
+
+ if (b->sync) {
+ /* hole */
+
+ *bytes_read = 0;
+
+ break;
+ }
+
+ *data = b->pos;
+ *bytes_read = b->last - b->pos;
+
+ break;
+ }
+
+ return 1;
+}
+
+
+static int
+ngx_quic_cbs_release_rcd(ngx_ssl_conn_t *ssl_conn, size_t bytes_read, void *arg)
+{
+ ngx_connection_t *c = arg;
+
+ ngx_chain_t *cl;
+ ngx_quic_send_ctx_t *ctx;
+ ngx_quic_connection_t *qc;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic ngx_quic_cbs_release_rcd len:%uz", bytes_read);
+
+ qc = ngx_quic_get_connection(c);
+ ctx = ngx_quic_get_send_ctx(qc, qc->read_level);
+
+ cl = ngx_quic_read_buffer(c, &ctx->crypto, bytes_read);
+ if (cl == NGX_CHAIN_ERROR) {
+ qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
+ return 1;
+ }
+
+ ngx_quic_free_chain(c, cl);
+
+ return 1;
+}
+
+
+static int
+ngx_quic_cbs_yield_secret(ngx_ssl_conn_t *ssl_conn, uint32_t ssl_level,
+ int direction, const unsigned char *secret, size_t secret_len, void *arg)
+{
+ ngx_connection_t *c = arg;
+
+ ngx_uint_t level;
+ const SSL_CIPHER *cipher;
+ ngx_quic_connection_t *qc;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic ngx_quic_cbs_yield_secret() level:%uD", ssl_level);
+#ifdef NGX_QUIC_DEBUG_CRYPTO
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic %s secret len:%uz %*xs",
+ direction ? "write" : "read", secret_len,
+ secret_len, secret);
+#endif
+
+ qc = ngx_quic_get_connection(c);
+ cipher = SSL_get_current_cipher(ssl_conn);
+
+ switch (ssl_level) {
+ case OSSL_RECORD_PROTECTION_LEVEL_NONE:
+ level = NGX_QUIC_ENCRYPTION_INITIAL;
+ break;
+ case OSSL_RECORD_PROTECTION_LEVEL_EARLY:
+ level = NGX_QUIC_ENCRYPTION_EARLY_DATA;
+ break;
+ case OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE:
+ level = NGX_QUIC_ENCRYPTION_HANDSHAKE;
+ break;
+ default: /* OSSL_RECORD_PROTECTION_LEVEL_APPLICATION */
+ level = NGX_QUIC_ENCRYPTION_APPLICATION;
+ break;
+ }
+
+ if (ngx_quic_keys_set_encryption_secret(c->log, direction, qc->keys, level,
+ cipher, secret, secret_len)
+ != NGX_OK)
+ {
+ qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
+ return 1;
+ }
+
+ if (direction) {
+ qc->write_level = level;
+
+ } else {
+ qc->read_level = level;
+ }
+
+ return 1;
+}
+
+
+static int
+ngx_quic_cbs_got_transport_params(ngx_ssl_conn_t *ssl_conn,
+ const unsigned char *params, size_t params_len, void *arg)
+{
+ ngx_connection_t *c = arg;
+
+ u_char *p, *end;
+ ngx_quic_tp_t ctp;
+ ngx_quic_connection_t *qc;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic ngx_quic_cbs_got_transport_params() len:%uz",
+ params_len);
+
+ qc = ngx_quic_get_connection(c);
+
+ /* defaults for parameters not sent by client */
+ ngx_memcpy(&ctp, &qc->ctp, sizeof(ngx_quic_tp_t));
+
+ p = (u_char *) params;
+ end = p + params_len;
+
+ if (ngx_quic_parse_transport_params(p, end, &ctp, c->log) != NGX_OK) {
+ qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;
+ qc->error_reason = "failed to process transport parameters";
+
+ return 1;
+ }
+
+ if (ngx_quic_apply_transport_params(c, &ctp) != NGX_OK) {
+ return 1;
+ }
+
+ qc->client_tp_done = 1;
+
+ return 1;
+}
+
+
+static int
+ngx_quic_cbs_alert(ngx_ssl_conn_t *ssl_conn, unsigned char alert, void *arg)
+{
+ ngx_connection_t *c = arg;
+
+ ngx_quic_connection_t *qc;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic ngx_quic_cbs_alert() alert:%d", (int) alert);
+
+ /* already closed on regular shutdown */
+
+ qc = ngx_quic_get_connection(c);
+ if (qc == NULL) {
+ return 1;
+ }
+
+ qc->error = NGX_QUIC_ERR_CRYPTO(alert);
+ qc->error_reason = "handshake failed";
+
+ return 1;
+}
+
+
+#else /* NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API */
+
+
+static ngx_inline ngx_uint_t
+ngx_quic_map_encryption_level(enum ssl_encryption_level_t ssl_level)
+{
+ switch (ssl_level) {
+ case ssl_encryption_initial:
+ return NGX_QUIC_ENCRYPTION_INITIAL;
+ case ssl_encryption_early_data:
+ return NGX_QUIC_ENCRYPTION_EARLY_DATA;
+ case ssl_encryption_handshake:
+ return NGX_QUIC_ENCRYPTION_HANDSHAKE;
+ default: /* ssl_encryption_application */
+ return NGX_QUIC_ENCRYPTION_APPLICATION;
+ }
+}
#if (NGX_QUIC_BORINGSSL_API)
static int
ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
- enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
+ enum ssl_encryption_level_t ssl_level, const SSL_CIPHER *cipher,
const uint8_t *rsecret, size_t secret_len)
{
+ ngx_uint_t level;
ngx_connection_t *c;
ngx_quic_connection_t *qc;
- c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+ c = ngx_ssl_get_connection(ssl_conn);
qc = ngx_quic_get_connection(c);
+ level = ngx_quic_map_encryption_level(ssl_level);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic ngx_quic_set_read_secret() level:%d", level);
+ "quic ngx_quic_set_read_secret() level:%d", ssl_level);
#ifdef NGX_QUIC_DEBUG_CRYPTO
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic read secret len:%uz %*xs", secret_len,
@@ -72,7 +365,7 @@ ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
cipher, rsecret, secret_len)
!= NGX_OK)
{
- return 0;
+ qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
}
return 1;
@@ -81,17 +374,19 @@ ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
static int
ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,
- enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
+ enum ssl_encryption_level_t ssl_level, const SSL_CIPHER *cipher,
const uint8_t *wsecret, size_t secret_len)
{
+ ngx_uint_t level;
ngx_connection_t *c;
ngx_quic_connection_t *qc;
- c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+ c = ngx_ssl_get_connection(ssl_conn);
qc = ngx_quic_get_connection(c);
+ level = ngx_quic_map_encryption_level(ssl_level);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic ngx_quic_set_write_secret() level:%d", level);
+ "quic ngx_quic_set_write_secret() level:%d", ssl_level);
#ifdef NGX_QUIC_DEBUG_CRYPTO
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic write secret len:%uz %*xs", secret_len,
@@ -102,28 +397,31 @@ ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,
cipher, wsecret, secret_len)
!= NGX_OK)
{
- return 0;
+ qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
}
return 1;
}
-#else
+#else /* NGX_QUIC_QUICTLS_API */
static int
ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
- enum ssl_encryption_level_t level, const uint8_t *rsecret,
+ enum ssl_encryption_level_t ssl_level, const uint8_t *rsecret,
const uint8_t *wsecret, size_t secret_len)
{
+ ngx_uint_t level;
ngx_connection_t *c;
const SSL_CIPHER *cipher;
ngx_quic_connection_t *qc;
- c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+ c = ngx_ssl_get_connection(ssl_conn);
qc = ngx_quic_get_connection(c);
+ level = ngx_quic_map_encryption_level(ssl_level);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic ngx_quic_set_encryption_secrets() level:%d", level);
+ "quic ngx_quic_set_encryption_secrets() level:%d",
+ ssl_level);
#ifdef NGX_QUIC_DEBUG_CRYPTO
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic read secret len:%uz %*xs", secret_len,
@@ -136,10 +434,11 @@ ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
cipher, rsecret, secret_len)
!= NGX_OK)
{
- return 0;
+ qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
+ return 1;
}
- if (level == ssl_encryption_early_data) {
+ if (level == NGX_QUIC_ENCRYPTION_EARLY_DATA) {
return 1;
}
@@ -153,7 +452,7 @@ ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
cipher, wsecret, secret_len)
!= NGX_OK)
{
- return 0;
+ qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
}
return 1;
@@ -164,24 +463,24 @@ ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
static int
ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
- enum ssl_encryption_level_t level, const uint8_t *data, size_t len)
+ enum ssl_encryption_level_t ssl_level, const uint8_t *data, size_t len)
{
u_char *p, *end;
size_t client_params_len;
+ ngx_uint_t level;
ngx_chain_t *out;
+ unsigned int alpn_len;
const uint8_t *client_params;
ngx_quic_tp_t ctp;
ngx_quic_frame_t *frame;
ngx_connection_t *c;
+ const unsigned char *alpn_data;
ngx_quic_send_ctx_t *ctx;
ngx_quic_connection_t *qc;
-#if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
- unsigned int alpn_len;
- const unsigned char *alpn_data;
-#endif
- c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+ c = ngx_ssl_get_connection(ssl_conn);
qc = ngx_quic_get_connection(c);
+ level = ngx_quic_map_encryption_level(ssl_level);
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_add_handshake_data");
@@ -193,20 +492,19 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
* here;
*/
-#if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
-
SSL_get0_alpn_selected(ssl_conn, &alpn_data, &alpn_len);
if (alpn_len == 0) {
- qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_NO_APPLICATION_PROTOCOL);
- qc->error_reason = "unsupported protocol in ALPN extension";
+ if (qc->error == 0) {
+ qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_NO_APPLICATION_PROTOCOL);
+ qc->error_reason = "missing ALPN extension";
- ngx_log_error(NGX_LOG_INFO, c->log, 0,
- "quic unsupported protocol in ALPN extension");
- return 0;
- }
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "quic missing ALPN extension");
+ }
-#endif
+ return 1;
+ }
SSL_get_peer_quic_transport_params(ssl_conn, &client_params,
&client_params_len);
@@ -217,12 +515,16 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
if (client_params_len == 0) {
/* RFC 9001, 8.2. QUIC Transport Parameters Extension */
- qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_MISSING_EXTENSION);
- qc->error_reason = "missing transport parameters";
- ngx_log_error(NGX_LOG_INFO, c->log, 0,
- "missing transport parameters");
- return 0;
+ if (qc->error == 0) {
+ qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_MISSING_EXTENSION);
+ qc->error_reason = "missing transport parameters";
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "missing transport parameters");
+ }
+
+ return 1;
}
p = (u_char *) client_params;
@@ -237,11 +539,11 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;
qc->error_reason = "failed to process transport parameters";
- return 0;
+ return 1;
}
if (ngx_quic_apply_transport_params(c, &ctp) != NGX_OK) {
- return 0;
+ return 1;
}
qc->client_tp_done = 1;
@@ -251,12 +553,14 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
out = ngx_quic_copy_buffer(c, (u_char *) data, len);
if (out == NGX_CHAIN_ERROR) {
- return 0;
+ qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
+ return 1;
}
frame = ngx_quic_alloc_frame(c);
if (frame == NULL) {
- return 0;
+ qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
+ return 1;
}
frame->data = out;
@@ -279,7 +583,7 @@ ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn)
#if (NGX_DEBUG)
ngx_connection_t *c;
- c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+ c = ngx_ssl_get_connection(ssl_conn);
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic ngx_quic_flush_flight()");
@@ -289,17 +593,17 @@ ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn)
static int
-ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,
- uint8_t alert)
+ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn,
+ enum ssl_encryption_level_t ssl_level, uint8_t alert)
{
ngx_connection_t *c;
ngx_quic_connection_t *qc;
- c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+ c = ngx_ssl_get_connection(ssl_conn);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic ngx_quic_send_alert() level:%s alert:%d",
- ngx_quic_level_name(level), (int) alert);
+ "quic ngx_quic_send_alert() level:%d alert:%d",
+ ssl_level, (int) alert);
/* already closed on regular shutdown */
@@ -314,13 +618,14 @@ ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,
return 1;
}
+#endif
+
ngx_int_t
ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
ngx_quic_frame_t *frame)
{
uint64_t last;
- ngx_chain_t *cl;
ngx_quic_send_ctx_t *ctx;
ngx_quic_connection_t *qc;
ngx_quic_crypto_frame_t *f;
@@ -343,13 +648,13 @@ ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
}
if (last <= ctx->crypto.offset) {
- if (pkt->level == ssl_encryption_initial) {
+ if (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL) {
/* speeding up handshake completion */
if (!ngx_queue_empty(&ctx->sent)) {
ngx_quic_resend_frames(c, ctx);
- ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_handshake);
+ ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_HANDSHAKE);
while (!ngx_queue_empty(&ctx->sent)) {
ngx_quic_resend_frames(c, ctx);
}
@@ -359,43 +664,25 @@ ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
return NGX_OK;
}
- if (f->offset == ctx->crypto.offset) {
- if (ngx_quic_crypto_input(c, frame->data, pkt->level) != NGX_OK) {
- return NGX_ERROR;
- }
-
- ngx_quic_skip_buffer(c, &ctx->crypto, last);
-
- } else {
- if (ngx_quic_write_buffer(c, &ctx->crypto, frame->data, f->length,
- f->offset)
- == NGX_CHAIN_ERROR)
- {
- return NGX_ERROR;
- }
+ if (ngx_quic_write_buffer(c, &ctx->crypto, frame->data, f->length,
+ f->offset)
+ == NGX_CHAIN_ERROR)
+ {
+ return NGX_ERROR;
}
- cl = ngx_quic_read_buffer(c, &ctx->crypto, (uint64_t) -1);
-
- if (cl) {
- if (ngx_quic_crypto_input(c, cl, pkt->level) != NGX_OK) {
- return NGX_ERROR;
- }
-
- ngx_quic_free_chain(c, cl);
+ if (ngx_quic_crypto_provide(c, pkt->level) != NGX_OK) {
+ return NGX_ERROR;
}
- return NGX_OK;
+ return ngx_quic_handshake(c);
}
static ngx_int_t
-ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
- enum ssl_encryption_level_t level)
+ngx_quic_handshake(ngx_connection_t *c)
{
int n, sslerr;
- ngx_buf_t *b;
- ngx_chain_t *cl;
ngx_ssl_conn_t *ssl_conn;
ngx_quic_frame_t *frame;
ngx_quic_connection_t *qc;
@@ -404,20 +691,14 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
ssl_conn = c->ssl->connection;
- for (cl = data; cl; cl = cl->next) {
- b = cl->buf;
-
- if (!SSL_provide_quic_data(ssl_conn, level, b->pos, b->last - b->pos)) {
- ngx_ssl_error(NGX_LOG_INFO, c->log, 0,
- "SSL_provide_quic_data() failed");
- return NGX_ERROR;
- }
- }
-
n = SSL_do_handshake(ssl_conn);
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);
@@ -433,13 +714,13 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
return NGX_ERROR;
}
- ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "SSL_do_handshake() failed");
+ ngx_ssl_connection_error(c, sslerr, 0, "SSL_do_handshake() failed");
return NGX_ERROR;
}
}
- if (n <= 0 || SSL_in_init(ssl_conn)) {
- if (ngx_quic_keys_available(qc->keys, ssl_encryption_early_data, 0)
+ if (!SSL_is_init_finished(ssl_conn)) {
+ if (ngx_quic_keys_available(qc->keys, NGX_QUIC_ENCRYPTION_EARLY_DATA, 0)
&& qc->client_tp_done)
{
if (ngx_quic_init_streams(c) != NGX_OK) {
@@ -461,7 +742,7 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
return NGX_ERROR;
}
- frame->level = ssl_encryption_application;
+ frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_HANDSHAKE_DONE;
ngx_quic_queue_frame(qc, frame);
@@ -485,7 +766,7 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
* An endpoint MUST discard its Handshake keys
* when the TLS handshake is confirmed.
*/
- ngx_quic_discard_ctx(c, ssl_encryption_handshake);
+ ngx_quic_discard_ctx(c, NGX_QUIC_ENCRYPTION_HANDSHAKE);
ngx_quic_discover_path_mtu(c, qc->path);
@@ -502,17 +783,97 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
}
+static ngx_int_t
+ngx_quic_crypto_provide(ngx_connection_t *c, ngx_uint_t level)
+{
+#if (NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API)
+
+ ngx_buf_t *b;
+ ngx_chain_t *out, *cl;
+ ngx_quic_send_ctx_t *ctx;
+ ngx_quic_connection_t *qc;
+ enum ssl_encryption_level_t ssl_level;
+
+ qc = ngx_quic_get_connection(c);
+ ctx = ngx_quic_get_send_ctx(qc, level);
+
+ out = ngx_quic_read_buffer(c, &ctx->crypto, (uint64_t) -1);
+ if (out == NGX_CHAIN_ERROR) {
+ return NGX_ERROR;
+ }
+
+ switch (level) {
+ case NGX_QUIC_ENCRYPTION_INITIAL:
+ ssl_level = ssl_encryption_initial;
+ break;
+ case NGX_QUIC_ENCRYPTION_EARLY_DATA:
+ ssl_level = ssl_encryption_early_data;
+ break;
+ case NGX_QUIC_ENCRYPTION_HANDSHAKE:
+ ssl_level = ssl_encryption_handshake;
+ break;
+ default: /* NGX_QUIC_ENCRYPTION_APPLICATION */
+ ssl_level = ssl_encryption_application;
+ break;
+ }
+
+ for (cl = out; cl; cl = cl->next) {
+ b = cl->buf;
+
+ if (!SSL_provide_quic_data(c->ssl->connection, ssl_level, b->pos,
+ b->last - b->pos))
+ {
+ ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
+ "SSL_provide_quic_data() failed");
+ return NGX_ERROR;
+ }
+ }
+
+ ngx_quic_free_chain(c, out);
+
+#endif
+
+ return NGX_OK;
+}
+
+
ngx_int_t
ngx_quic_init_connection(ngx_connection_t *c)
{
- u_char *p;
- size_t clen;
- ssize_t len;
- ngx_str_t dcid;
- ngx_ssl_conn_t *ssl_conn;
- ngx_quic_socket_t *qsock;
- ngx_quic_connection_t *qc;
- static SSL_QUIC_METHOD quic_method;
+ u_char *p;
+ size_t clen;
+ ssize_t len;
+ ngx_str_t dcid;
+ ngx_ssl_conn_t *ssl_conn;
+ ngx_quic_socket_t *qsock;
+ ngx_quic_connection_t *qc;
+
+#if (NGX_QUIC_OPENSSL_API)
+ static const OSSL_DISPATCH qtdis[] = {
+
+ { OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_SEND,
+ (void (*)(void)) ngx_quic_cbs_send },
+
+ { OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RECV_RCD,
+ (void (*)(void)) ngx_quic_cbs_recv_rcd },
+
+ { OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RELEASE_RCD,
+ (void (*)(void)) ngx_quic_cbs_release_rcd },
+
+ { OSSL_FUNC_SSL_QUIC_TLS_YIELD_SECRET,
+ (void (*)(void)) ngx_quic_cbs_yield_secret },
+
+ { OSSL_FUNC_SSL_QUIC_TLS_GOT_TRANSPORT_PARAMS,
+ (void (*)(void)) ngx_quic_cbs_got_transport_params },
+
+ { OSSL_FUNC_SSL_QUIC_TLS_ALERT,
+ (void (*)(void)) ngx_quic_cbs_alert },
+
+ { 0, NULL }
+ };
+#else /* NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API */
+ static SSL_QUIC_METHOD quic_method;
+#endif
qc = ngx_quic_get_connection(c);
@@ -524,6 +885,20 @@ ngx_quic_init_connection(ngx_connection_t *c)
ssl_conn = c->ssl->connection;
+#if (NGX_QUIC_OPENSSL_API)
+
+ if (SSL_set_quic_tls_cbs(ssl_conn, qtdis, c) == 0) {
+ ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
+ "quic SSL_set_quic_tls_cbs() failed");
+ return NGX_ERROR;
+ }
+
+ if (SSL_CTX_get_max_early_data(qc->conf->ssl->ctx)) {
+ SSL_set_quic_tls_early_data_enabled(ssl_conn, 1);
+ }
+
+#else /* NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API */
+
if (!quic_method.send_alert) {
#if (NGX_QUIC_BORINGSSL_API)
quic_method.set_read_secret = ngx_quic_set_read_secret;
@@ -537,17 +912,19 @@ ngx_quic_init_connection(ngx_connection_t *c)
}
if (SSL_set_quic_method(ssl_conn, &quic_method) == 0) {
- ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
"quic SSL_set_quic_method() failed");
return NGX_ERROR;
}
-#ifdef OPENSSL_INFO_QUIC
+#if (NGX_QUIC_QUICTLS_API)
if (SSL_CTX_get_max_early_data(qc->conf->ssl->ctx)) {
SSL_set_quic_early_data_enabled(ssl_conn, 1);
}
#endif
+#endif
+
qsock = ngx_quic_get_socket(c);
dcid.data = qsock->sid.id;
@@ -577,15 +954,23 @@ ngx_quic_init_connection(ngx_connection_t *c)
"quic transport parameters len:%uz %*xs", len, len, p);
#endif
+#if (NGX_QUIC_OPENSSL_API)
+ if (SSL_set_quic_tls_transport_params(ssl_conn, p, len) == 0) {
+ ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
+ "quic SSL_set_quic_tls_transport_params() failed");
+ return NGX_ERROR;
+ }
+#else
if (SSL_set_quic_transport_params(ssl_conn, p, len) == 0) {
- ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
"quic SSL_set_quic_transport_params() failed");
return NGX_ERROR;
}
+#endif
#ifdef OPENSSL_IS_BORINGSSL
if (SSL_set_quic_early_data_context(ssl_conn, p, clen) == 0) {
- ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
"quic SSL_set_quic_early_data_context() failed");
return NGX_ERROR;
}
diff --git a/src/event/quic/ngx_event_quic_streams.c b/src/event/quic/ngx_event_quic_streams.c
index a9a21f578..18fffeabe 100644
--- a/src/event/quic/ngx_event_quic_streams.c
+++ b/src/event/quic/ngx_event_quic_streams.c
@@ -280,7 +280,7 @@ ngx_quic_do_reset_stream(ngx_quic_stream_t *qs, ngx_uint_t err)
return NGX_ERROR;
}
- frame->level = ssl_encryption_application;
+ frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_RESET_STREAM;
frame->u.reset_stream.id = qs->id;
frame->u.reset_stream.error_code = err;
@@ -367,7 +367,7 @@ ngx_quic_shutdown_stream_recv(ngx_connection_t *c)
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pc->log, 0,
"quic stream id:0x%xL recv shutdown", qs->id);
- frame->level = ssl_encryption_application;
+ frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_STOP_SENDING;
frame->u.stop_sending.id = qs->id;
frame->u.stop_sending.error_code = qc->conf->stream_close_code;
@@ -527,7 +527,7 @@ ngx_quic_reject_stream(ngx_connection_t *c, uint64_t id)
return NGX_ERROR;
}
- frame->level = ssl_encryption_application;
+ frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_RESET_STREAM;
frame->u.reset_stream.id = id;
frame->u.reset_stream.error_code = code;
@@ -540,7 +540,7 @@ ngx_quic_reject_stream(ngx_connection_t *c, uint64_t id)
return NGX_ERROR;
}
- frame->level = ssl_encryption_application;
+ frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_STOP_SENDING;
frame->u.stop_sending.id = id;
frame->u.stop_sending.error_code = code;
@@ -1062,7 +1062,7 @@ ngx_quic_stream_flush(ngx_quic_stream_t *qs)
return NGX_ERROR;
}
- frame->level = ssl_encryption_application;
+ frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_STREAM;
frame->data = out;
@@ -1180,7 +1180,7 @@ ngx_quic_close_stream(ngx_quic_stream_t *qs)
return NGX_ERROR;
}
- frame->level = ssl_encryption_application;
+ frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_MAX_STREAMS;
if (qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) {
@@ -1771,7 +1771,7 @@ ngx_quic_update_max_stream_data(ngx_quic_stream_t *qs)
return NGX_ERROR;
}
- frame->level = ssl_encryption_application;
+ frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_MAX_STREAM_DATA;
frame->u.max_stream_data.id = qs->id;
frame->u.max_stream_data.limit = qs->recv_max_data;
@@ -1807,7 +1807,7 @@ ngx_quic_update_max_data(ngx_connection_t *c)
return NGX_ERROR;
}
- frame->level = ssl_encryption_application;
+ frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
frame->type = NGX_QUIC_FT_MAX_DATA;
frame->u.max_data.max_data = qc->streams.recv_max_data;
diff --git a/src/event/quic/ngx_event_quic_transport.c b/src/event/quic/ngx_event_quic_transport.c
index 0b3ef4b2e..ba6211c33 100644
--- a/src/event/quic/ngx_event_quic_transport.c
+++ b/src/event/quic/ngx_event_quic_transport.c
@@ -281,7 +281,7 @@ ngx_int_t
ngx_quic_parse_packet(ngx_quic_header_t *pkt)
{
if (!ngx_quic_long_pkt(pkt->flags)) {
- pkt->level = ssl_encryption_application;
+ pkt->level = NGX_QUIC_ENCRYPTION_APPLICATION;
if (ngx_quic_parse_short_header(pkt, NGX_QUIC_SERVER_CID_LEN) != NGX_OK)
{
@@ -468,13 +468,13 @@ ngx_quic_parse_long_header_v1(ngx_quic_header_t *pkt)
return NGX_ERROR;
}
- pkt->level = ssl_encryption_initial;
+ pkt->level = NGX_QUIC_ENCRYPTION_INITIAL;
} else if (ngx_quic_pkt_zrtt(pkt->flags)) {
- pkt->level = ssl_encryption_early_data;
+ pkt->level = NGX_QUIC_ENCRYPTION_EARLY_DATA;
} else if (ngx_quic_pkt_hs(pkt->flags)) {
- pkt->level = ssl_encryption_handshake;
+ pkt->level = NGX_QUIC_ENCRYPTION_HANDSHAKE;
} else {
ngx_log_error(NGX_LOG_INFO, pkt->log, 0,
@@ -593,7 +593,7 @@ ngx_quic_payload_size(ngx_quic_header_t *pkt, size_t pkt_len)
/* flags, version, dcid and scid with lengths and zero-length token */
len = 5 + 2 + pkt->dcid.len + pkt->scid.len
- + (pkt->level == ssl_encryption_initial ? 1 : 0);
+ + (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL ? 1 : 0);
if (len > pkt_len) {
return 0;
@@ -632,7 +632,7 @@ ngx_quic_create_long_header(ngx_quic_header_t *pkt, u_char *out,
if (out == NULL) {
return 5 + 2 + pkt->dcid.len + pkt->scid.len
+ ngx_quic_varint_len(rem_len) + pkt->num_len
- + (pkt->level == ssl_encryption_initial ? 1 : 0);
+ + (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL ? 1 : 0);
}
p = start = out;
@@ -647,7 +647,7 @@ ngx_quic_create_long_header(ngx_quic_header_t *pkt, u_char *out,
*p++ = pkt->scid.len;
p = ngx_cpymem(p, pkt->scid.data, pkt->scid.len);
- if (pkt->level == ssl_encryption_initial) {
+ if (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL) {
ngx_quic_build_int(&p, 0);
}
diff --git a/src/event/quic/ngx_event_quic_transport.h b/src/event/quic/ngx_event_quic_transport.h
index dcd763df1..656cb09fb 100644
--- a/src/event/quic/ngx_event_quic_transport.h
+++ b/src/event/quic/ngx_event_quic_transport.h
@@ -47,9 +47,9 @@
(ngx_quic_long_pkt(flags) ? 0x0F : 0x1F)
#define ngx_quic_level_name(lvl) \
- (lvl == ssl_encryption_application) ? "app" \
- : (lvl == ssl_encryption_initial) ? "init" \
- : (lvl == ssl_encryption_handshake) ? "hs" : "early"
+ (lvl == NGX_QUIC_ENCRYPTION_APPLICATION) ? "app" \
+ : (lvl == NGX_QUIC_ENCRYPTION_INITIAL) ? "init" \
+ : (lvl == NGX_QUIC_ENCRYPTION_HANDSHAKE) ? "hs" : "early"
#define NGX_QUIC_MAX_CID_LEN 20
#define NGX_QUIC_SERVER_CID_LEN NGX_QUIC_MAX_CID_LEN
@@ -262,7 +262,7 @@ typedef struct ngx_quic_frame_s ngx_quic_frame_t;
struct ngx_quic_frame_s {
ngx_uint_t type;
- enum ssl_encryption_level_t level;
+ ngx_uint_t level;
ngx_queue_t queue;
uint64_t pnum;
size_t plen;
@@ -310,7 +310,7 @@ typedef struct {
uint8_t flags;
uint32_t version;
ngx_str_t token;
- enum ssl_encryption_level_t level;
+ ngx_uint_t level;
ngx_uint_t error;
/* filled in by parser */
diff --git a/src/http/modules/ngx_http_addition_filter_module.c b/src/http/modules/ngx_http_addition_filter_module.c
index e546f0d60..040244c7e 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_charset_filter_module.c b/src/http/modules/ngx_http_charset_filter_module.c
index d44da6233..482960040 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 a41b496dc..6b1977340 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 80046d6a4..4c95525b4 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 5d170a1ba..22e75e300 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 7113df695..dac21bb18 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 d4c5abf62..8d5385c1d 100644
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -1888,6 +1888,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;
}
@@ -1949,6 +1956,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);
@@ -1960,10 +1975,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;
}
@@ -1974,6 +1985,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
@@ -2021,8 +2046,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
@@ -3628,10 +3651,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;
@@ -3844,7 +3867,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 9023a36e4..49977b07b 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 47068f755..65ca03440 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_sub_filter_module.c b/src/http/modules/ngx_http_sub_filter_module.c
index 456bb27e3..b50a25235 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 51a861d9a..c1d0035cc 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 8afd656af..4e6e1b99d 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 d835f896e..7f2b4225a 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 cb4a1e68a..7d98f5cd7 100644
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -152,6 +152,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 +192,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 92c3eae8a..c75ddb849 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,
@@ -1858,6 +1865,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)
{
ngx_int_t rc;
@@ -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 e7e266bf8..a794144aa 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 76f6e9629..789938329 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" CRLF;
static u_char ngx_http_server_full_string[] = "Server: " NGINX_VER CRLF;
static u_char ngx_http_server_build_string[] = "Server: " NGINX_VER_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[] = {
@@ -626,9 +630,112 @@ 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.h b/src/http/ngx_http_request.h
index 9407f46ae..ad11f147f 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 d4cf1b7fe..de0f92a4f 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;
}
@@ -2568,6 +2583,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)
{
ngx_msec_t timeout;
diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h
index e0a903669..f3e9f7979 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
@@ -185,6 +186,7 @@ typedef struct {
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;
@@ -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.h b/src/http/v2/ngx_http_v2.h
index 6751b3026..9605c8a23 100644
--- a/src/http/v2/ngx_http_v2.h
+++ b/src/http/v2/ngx_http_v2.h
@@ -213,6 +213,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 b63e343a0..907906a88 100644
--- a/src/http/v2/ngx_http_v2_filter_module.c
+++ b/src/http/v2/ngx_http_v2_filter_module.c
@@ -27,6 +27,10 @@
#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);
static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame(
@@ -95,6 +99,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
@@ -107,7 +112,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;
@@ -612,7 +616,196 @@ ngx_http_v2_header_filter(ngx_http_request_t *r)
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);
+ 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) {
@@ -626,7 +819,7 @@ 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;
}
@@ -1567,5 +1760,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 4d2276dc0..e3f15368f 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;
@@ -589,6 +592,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)
{
u_char *chunk;
@@ -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/os/unix/ngx_time.c b/src/os/unix/ngx_time.c
index cc760b2eb..c97bae2ed 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);