diff options
Diffstat (limited to 'src')
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, ¬ify_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(¤t->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); |