diff options
author | Sergey A. Osokin <osa@FreeBSD.org> | 2022-02-01 09:57:06 -0500 |
---|---|---|
committer | Sergey A. Osokin <osa@FreeBSD.org> | 2022-02-01 09:57:26 -0500 |
commit | 117d256c411ae24dc0e1c0868a1825a59300ca4e (patch) | |
tree | 014c56233ef4a3989e9a0cac1f5f89fe91b9a3c1 /www/nginx-devel/files | |
parent | deskutils/recoll: update to 1.31.4 and set X11MON as default option (diff) |
www/nginx-devel: update HTTPv3/QUIC patch to the recent commit
Diffstat (limited to 'www/nginx-devel/files')
-rw-r--r-- | www/nginx-devel/files/extra-patch-httpv3 | 785 |
1 files changed, 392 insertions, 393 deletions
diff --git a/www/nginx-devel/files/extra-patch-httpv3 b/www/nginx-devel/files/extra-patch-httpv3 index 9f0ab11e7c7c..84104bfbf152 100644 --- a/www/nginx-devel/files/extra-patch-httpv3 +++ b/www/nginx-devel/files/extra-patch-httpv3 @@ -2,7 +2,7 @@ diff --git a/README b/README new file mode 100644 --- /dev/null +++ b/README -@@ -0,0 +1,261 @@ +@@ -0,0 +1,233 @@ +Experimental QUIC support for nginx +----------------------------------- + @@ -39,8 +39,7 @@ new file mode 100644 + + What works now: + -+ Currently we support IETF-QUIC draft-29 through final RFC documents. -+ Earlier drafts are NOT supported as they have incompatible wire format. ++ We support IETF QUIC version 1. Internet drafts are no longer supported. + + nginx should be able to respond to HTTP/3 requests over QUIC and + it should be possible to upload and download big files without errors. @@ -58,21 +57,6 @@ new file mode 100644 + + Lost packets are detected and retransmitted properly + + Clients may migrate to new address + -+ Not (yet) supported features: -+ -+ - Explicit Congestion Notification (ECN) as specified in quic-recovery [5] -+ - A connection with the spin bit succeeds and the bit is spinning -+ - Structured Logging -+ -+ Since the code is experimental and still under development, -+ a lot of things may not work as expected, for example: -+ -+ - Flow control mechanism is basic and intended to avoid CPU hog and make -+ simple interactions possible -+ -+ - Not all protocol requirements are strictly followed; some of checks are -+ omitted for the sake of simplicity of initial implementation -+ +2. Installing + + You will need a BoringSSL [4] library that provides QUIC support @@ -183,21 +167,12 @@ new file mode 100644 + + * Browsers + -+ Known to work: Firefox 80+ and Chrome 85+ (QUIC draft 29+) ++ Known to work: Firefox 90+ and Chrome 92+ (QUIC version 1) + + Beware of strange issues: sometimes browser may decide to ignore QUIC + Cache clearing/restart might help. Always check access.log and + error.log to make sure you are using HTTP/3 and not TCP https. + -+ + to enable QUIC in Firefox, set the following in 'about:config': -+ network.http.http3.enabled = true -+ -+ + to enable QUIC in Chrome, enable it on command line and force it -+ on your site: -+ -+ $ ./chrome --enable-quic --quic-version=h3-29 \ -+ --origin-to-force-quic-on=example.com:8443 -+ + * Console clients + + Known to work: ngtcp2, firefox's neqo and chromium's console clients: @@ -206,10 +181,7 @@ new file mode 100644 + + $ ./neqo-client https://127.0.0.1:8443/ + -+ $ chromium-build/out/my_build/quic_client http://example.com:8443 \ -+ --quic_version=h3-29 \ -+ --allow_unknown_root_cert \ -+ --disable_certificate_verification ++ $ chromium-build/out/my_build/quic_client http://example.com:8443 + + + If you've got it right, in the access log you should see something like: @@ -227,7 +199,7 @@ new file mode 100644 + + Ensure you are using the proper SSL library in runtime + (`nginx -V` will show you what you are using) + -+ + Ensure your client is actually sending QUIC requests ++ + Ensure your client is actually sending requests over QUIC + (see "Clients" section about browsers and cache) + + We recommend to start with simple console client like ngtcp2 @@ -257,7 +229,7 @@ new file mode 100644 + + [1] https://datatracker.ietf.org/doc/html/rfc9000 + [2] https://datatracker.ietf.org/doc/html/draft-ietf-quic-http -+ [3] https://mailman.nginx.org/mailman/listinfo/nginx-devel ++ [3] https://mailman.nginx.org/mailman3/lists/nginx-devel.nginx.org/ + [4] https://boringssl.googlesource.com/boringssl/ + [5] https://datatracker.ietf.org/doc/html/rfc9002 + [6] https://nginx.org/en/docs/http/ngx_http_core_module.html#listen @@ -962,7 +934,7 @@ diff --git a/src/core/ngx_core.h b/src/core/ngx_core.h diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c --- a/src/event/ngx_event.c +++ b/src/event/ngx_event.c -@@ -266,6 +266,18 @@ ngx_process_events_and_timers(ngx_cycle_ +@@ -267,6 +267,18 @@ ngx_process_events_and_timers(ngx_cycle_ ngx_int_t ngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags) { @@ -981,7 +953,7 @@ diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c if (ngx_event_flags & NGX_USE_CLEAR_EVENT) { /* kqueue, epoll */ -@@ -336,9 +348,15 @@ ngx_handle_write_event(ngx_event_t *wev, +@@ -337,9 +349,15 @@ ngx_handle_write_event(ngx_event_t *wev, { ngx_connection_t *c; @@ -1002,7 +974,7 @@ diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h --- a/src/event/ngx_event.h +++ b/src/event/ngx_event.h -@@ -493,12 +493,6 @@ extern ngx_module_t ngx_event_ +@@ -494,12 +494,6 @@ extern ngx_module_t ngx_event_ void ngx_event_accept(ngx_event_t *ev); @@ -1015,7 +987,7 @@ diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h ngx_int_t ngx_trylock_accept_mutex(ngx_cycle_t *cycle); ngx_int_t ngx_enable_accept_events(ngx_cycle_t *cycle); u_char *ngx_accept_log_error(ngx_log_t *log, u_char *buf, size_t len); -@@ -528,6 +522,7 @@ ngx_int_t ngx_send_lowat(ngx_connection_ +@@ -529,6 +523,7 @@ ngx_int_t ngx_send_lowat(ngx_connection_ #include <ngx_event_timer.h> #include <ngx_event_posted.h> @@ -1026,7 +998,7 @@ diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c -@@ -3146,6 +3146,13 @@ ngx_ssl_shutdown(ngx_connection_t *c) +@@ -3149,6 +3149,13 @@ ngx_ssl_shutdown(ngx_connection_t *c) ngx_err_t err; ngx_uint_t tries; @@ -1929,7 +1901,7 @@ diff --git a/src/event/quic/ngx_event_quic.c b/src/event/quic/ngx_event_quic.c new file mode 100644 --- /dev/null +++ b/src/event/quic/ngx_event_quic.c -@@ -0,0 +1,1491 @@ +@@ -0,0 +1,1457 @@ + +/* + * Copyright (C) Nginx, Inc. @@ -1948,7 +1920,6 @@ new file mode 100644 + ngx_quic_header_t *pkt); +static void ngx_quic_input_handler(ngx_event_t *rev); + -+static ngx_int_t ngx_quic_close_quic(ngx_connection_t *c, ngx_int_t rc); +static void ngx_quic_close_timer_handler(ngx_event_t *ev); + +static ngx_int_t ngx_quic_handle_datagram(ngx_connection_t *c, ngx_buf_t *b, @@ -2142,17 +2113,13 @@ new file mode 100644 + + rc = ngx_quic_handle_datagram(c, c->buffer, conf); + if (rc != NGX_OK) { -+ ngx_quic_close_connection(c, rc == NGX_DECLINED ? NGX_DONE : NGX_ERROR); ++ ngx_quic_close_connection(c, rc); + return; + } + ++ /* quic connection is now created */ + qc = ngx_quic_get_connection(c); + -+ if (qc == NULL) { -+ ngx_quic_close_connection(c, NGX_DONE); -+ return; -+ } -+ + ngx_add_timer(c->read, qc->tp.max_idle_timeout); + ngx_quic_connstate_dbg(c); + @@ -2261,8 +2228,7 @@ new file mode 100644 + } + } + -+ if (ngx_quic_keys_set_initial_secret(c->pool, qc->keys, &pkt->dcid, -+ qc->version) ++ if (ngx_quic_keys_set_initial_secret(c->pool, qc->keys, &pkt->dcid) + != NGX_OK) + { + return NULL; @@ -2376,7 +2342,7 @@ new file mode 100644 + return; + } + -+ if (rc == NGX_DECLINED) { ++ if (rc == NGX_DONE) { + return; + } + @@ -2392,54 +2358,22 @@ new file mode 100644 +void +ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc) +{ ++ ngx_uint_t i; + ngx_pool_t *pool; ++ ngx_quic_send_ctx_t *ctx; + ngx_quic_connection_t *qc; + -+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic ngx_quic_close_connection rc:%i", rc); -+ + qc = ngx_quic_get_connection(c); + + if (qc == NULL) { -+ if (rc == NGX_ERROR) { -+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic close connection early error"); -+ } -+ -+ } else if (ngx_quic_close_quic(c, rc) == NGX_AGAIN) { -+ return; -+ } -+ -+ if (c->ssl) { -+ (void) ngx_ssl_shutdown(c); -+ } -+ -+ if (c->read->timer_set) { -+ ngx_del_timer(c->read); ++ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, ++ "quic packet rejected rc:%i, cleanup connection", rc); ++ goto quic_done; + } + -+#if (NGX_STAT_STUB) -+ (void) ngx_atomic_fetch_add(ngx_stat_active, -1); -+#endif -+ -+ c->destroyed = 1; -+ -+ pool = c->pool; -+ -+ ngx_close_connection(c); -+ -+ ngx_destroy_pool(pool); -+} -+ -+ -+static ngx_int_t -+ngx_quic_close_quic(ngx_connection_t *c, ngx_int_t rc) -+{ -+ ngx_uint_t i; -+ ngx_quic_send_ctx_t *ctx; -+ ngx_quic_connection_t *qc; -+ -+ qc = ngx_quic_get_connection(c); ++ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, ++ "quic close %s rc:%i", ++ qc->closing ? "resumed": "initiated", rc); + + if (!qc->closing) { + @@ -2458,10 +2392,11 @@ new file mode 100644 + * closed and its state is discarded when it remains idle + */ + -+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic closing %s connection", -+ qc->draining ? "drained" : "idle"); ++ /* this case also handles some errors from ngx_quic_run() */ + ++ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, ++ "quic close silent drain:%d timedout:%d", ++ qc->draining, c->read->timedout); + } else { + + /* @@ -2476,7 +2411,7 @@ new file mode 100644 + + if (rc == NGX_OK) { + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic immediate close drain:%d", ++ "quic close immediate drain:%d", + qc->draining); + + qc->close.log = c->log; @@ -2496,7 +2431,7 @@ new file mode 100644 + } + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic immediate close due to %s error: %ui %s", ++ "quic close immediate due to %serror: %ui %s", + qc->error_app ? "app " : "", qc->error, + qc->error_reason ? qc->error_reason : ""); + } @@ -2519,7 +2454,7 @@ new file mode 100644 + } + + if (ngx_quic_close_streams(c, qc) == NGX_AGAIN) { -+ return NGX_AGAIN; ++ return; + } + + if (qc->push.timer_set) { @@ -2539,18 +2474,37 @@ new file mode 100644 + } + + if (qc->close.timer_set) { -+ return NGX_AGAIN; ++ return; + } + + ngx_quic_close_sockets(c); + -+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic part of connection is terminated"); ++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic close completed"); + + /* may be tested from SSL callback during SSL shutdown */ + c->udp = NULL; + -+ return NGX_OK; ++quic_done: ++ ++ if (c->ssl) { ++ (void) ngx_ssl_shutdown(c); ++ } ++ ++ if (c->read->timer_set) { ++ ngx_del_timer(c->read); ++ } ++ ++#if (NGX_STAT_STUB) ++ (void) ngx_atomic_fetch_add(ngx_stat_active, -1); ++#endif ++ ++ c->destroyed = 1; ++ ++ pool = c->pool; ++ ++ ngx_close_connection(c); ++ ++ ngx_destroy_pool(pool); +} + + @@ -2633,22 +2587,18 @@ new file mode 100644 +#if (NGX_DEBUG) + if (pkt.parsed) { + ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic packet %s done decr:%d pn:%L perr:%ui rc:%i", -+ ngx_quic_level_name(pkt.level), pkt.decrypted, -+ pkt.pn, pkt.error, rc); ++ "quic packet done rc:%i level:%s" ++ " decr:%d pn:%L perr:%ui", ++ rc, ngx_quic_level_name(pkt.level), ++ pkt.decrypted, pkt.pn, pkt.error); + } else { + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic packet done parse failed rc:%i", rc); ++ "quic packet done rc:%i parse failed", rc); + } +#endif + -+ if (rc == NGX_ERROR) { -+ return NGX_ERROR; -+ } -+ -+ if (rc == NGX_DONE) { -+ /* stop further processing */ -+ return NGX_DECLINED; ++ if (rc == NGX_ERROR || rc == NGX_DONE) { ++ return rc; + } + + if (rc == NGX_OK) { @@ -2683,7 +2633,7 @@ new file mode 100644 + } + + if (!good) { -+ return NGX_DECLINED; ++ return NGX_DONE; + } + + qc = ngx_quic_get_connection(c); @@ -2717,13 +2667,13 @@ new file mode 100644 + + rc = ngx_quic_parse_packet(pkt); + -+ if (rc == NGX_DECLINED || rc == NGX_ERROR) { -+ return rc; ++ if (rc == NGX_ERROR) { ++ return NGX_DECLINED; + } + + pkt->parsed = 1; + -+ c->log->action = "processing quic packet"; ++ c->log->action = "handling quic packet"; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic packet rx dcid len:%uz %xV", @@ -2808,10 +2758,12 @@ new file mode 100644 + } + + if (pkt->level != ssl_encryption_initial) { ++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, ++ "quic expected initial, got handshake"); + return NGX_ERROR; + } + -+ c->log->action = "processing initial packet"; ++ c->log->action = "handling initial packet"; + + if (pkt->dcid.len < NGX_QUIC_CID_LEN_MIN) { + /* RFC 9000, 7.2. Negotiating Connection IDs */ @@ -3363,7 +3315,7 @@ new file mode 100644 +{ + ngx_connection_t *c; + -+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "quic push timer"); ++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "quic push handler"); + + c = ev->data; + @@ -3407,25 +3359,11 @@ new file mode 100644 + + ngx_quic_finalize_connection(c, qc->shutdown_code, qc->shutdown_reason); +} -+ -+ -+uint32_t -+ngx_quic_version(ngx_connection_t *c) -+{ -+ uint32_t version; -+ ngx_quic_connection_t *qc; -+ -+ qc = ngx_quic_get_connection(c); -+ -+ version = qc->version; -+ -+ return (version & 0xff000000) == 0xff000000 ? version & 0xff : version; -+} diff --git a/src/event/quic/ngx_event_quic.h b/src/event/quic/ngx_event_quic.h new file mode 100644 --- /dev/null +++ b/src/event/quic/ngx_event_quic.h -@@ -0,0 +1,88 @@ +@@ -0,0 +1,109 @@ + +/* + * Copyright (C) Nginx, Inc. @@ -3456,44 +3394,66 @@ new file mode 100644 +#define NGX_QUIC_STREAM_UNIDIRECTIONAL 0x02 + + -+typedef struct { -+ ngx_ssl_t *ssl; ++typedef enum { ++ NGX_QUIC_STREAM_SEND_READY = 0, ++ NGX_QUIC_STREAM_SEND_SEND, ++ NGX_QUIC_STREAM_SEND_DATA_SENT, ++ NGX_QUIC_STREAM_SEND_DATA_RECVD, ++ NGX_QUIC_STREAM_SEND_RESET_SENT, ++ NGX_QUIC_STREAM_SEND_RESET_RECVD ++} ngx_quic_stream_send_state_e; ++ ++ ++typedef enum { ++ NGX_QUIC_STREAM_RECV_RECV = 0, ++ NGX_QUIC_STREAM_RECV_SIZE_KNOWN, ++ NGX_QUIC_STREAM_RECV_DATA_RECVD, ++ NGX_QUIC_STREAM_RECV_DATA_READ, ++ NGX_QUIC_STREAM_RECV_RESET_RECVD, ++ NGX_QUIC_STREAM_RECV_RESET_READ ++} ngx_quic_stream_recv_state_e; + -+ ngx_flag_t retry; -+ ngx_flag_t gso_enabled; -+ ngx_flag_t disable_active_migration; -+ ngx_msec_t timeout; -+ ngx_str_t host_key; -+ size_t mtu; -+ size_t stream_buffer_size; -+ ngx_uint_t max_concurrent_streams_bidi; -+ ngx_uint_t max_concurrent_streams_uni; -+ ngx_uint_t active_connection_id_limit; -+ ngx_int_t stream_close_code; -+ ngx_int_t stream_reject_code_uni; -+ ngx_int_t stream_reject_code_bidi; + -+ u_char av_token_key[NGX_QUIC_AV_KEY_LEN]; -+ u_char sr_token_key[NGX_QUIC_SR_KEY_LEN]; ++typedef struct { ++ ngx_ssl_t *ssl; ++ ++ ngx_flag_t retry; ++ ngx_flag_t gso_enabled; ++ ngx_flag_t disable_active_migration; ++ ngx_msec_t timeout; ++ ngx_str_t host_key; ++ size_t mtu; ++ size_t stream_buffer_size; ++ ngx_uint_t max_concurrent_streams_bidi; ++ ngx_uint_t max_concurrent_streams_uni; ++ ngx_uint_t active_connection_id_limit; ++ ngx_int_t stream_close_code; ++ ngx_int_t stream_reject_code_uni; ++ ngx_int_t stream_reject_code_bidi; ++ ++ u_char av_token_key[NGX_QUIC_AV_KEY_LEN]; ++ u_char sr_token_key[NGX_QUIC_SR_KEY_LEN]; +} ngx_quic_conf_t; + + +struct ngx_quic_stream_s { -+ ngx_rbtree_node_t node; -+ ngx_queue_t queue; -+ ngx_connection_t *parent; -+ ngx_connection_t *connection; -+ uint64_t id; -+ uint64_t acked; -+ uint64_t send_max_data; -+ uint64_t recv_max_data; -+ uint64_t recv_offset; -+ uint64_t recv_window; -+ uint64_t recv_last; -+ uint64_t final_size; -+ ngx_chain_t *in; -+ ngx_chain_t *out; -+ ngx_uint_t cancelable; /* unsigned cancelable:1; */ ++ ngx_rbtree_node_t node; ++ ngx_queue_t queue; ++ ngx_connection_t *parent; ++ ngx_connection_t *connection; ++ uint64_t id; ++ uint64_t acked; ++ uint64_t send_max_data; ++ uint64_t recv_max_data; ++ uint64_t recv_offset; ++ uint64_t recv_window; ++ uint64_t recv_last; ++ uint64_t final_size; ++ ngx_chain_t *in; ++ ngx_chain_t *out; ++ ngx_uint_t cancelable; /* unsigned cancelable:1; */ ++ ngx_quic_stream_send_state_e send_state; ++ ngx_quic_stream_recv_state_e recv_state; +}; + + @@ -3505,7 +3465,6 @@ new file mode 100644 + const char *reason); +ngx_int_t ngx_quic_reset_stream(ngx_connection_t *c, ngx_uint_t err); +ngx_int_t ngx_quic_shutdown_stream(ngx_connection_t *c, int how); -+uint32_t ngx_quic_version(ngx_connection_t *c); +ngx_int_t ngx_quic_handle_read_event(ngx_event_t *rev, ngx_uint_t flags); +ngx_int_t ngx_quic_handle_write_event(ngx_event_t *wev, size_t lowat); +ngx_int_t ngx_quic_get_packet_dcid(ngx_log_t *log, u_char *data, size_t len, @@ -3518,7 +3477,7 @@ diff --git a/src/event/quic/ngx_event_quic_ack.c b/src/event/quic/ngx_event_quic new file mode 100644 --- /dev/null +++ b/src/event/quic/ngx_event_quic_ack.c -@@ -0,0 +1,1190 @@ +@@ -0,0 +1,1193 @@ + +/* + * Copyright (C) Nginx, Inc. @@ -4138,10 +4097,13 @@ new file mode 100644 + case NGX_QUIC_FT_STREAM: + qs = ngx_quic_find_stream(&qc->streams.tree, f->u.stream.stream_id); + -+ if (qs && qs->connection->write->error) { -+ /* RESET_STREAM was sent */ -+ ngx_quic_free_frame(c, f); -+ break; ++ if (qs) { ++ if (qs->send_state == NGX_QUIC_STREAM_SEND_RESET_SENT ++ || qs->send_state == NGX_QUIC_STREAM_SEND_RESET_RECVD) ++ { ++ ngx_quic_free_frame(c, f); ++ break; ++ } + } + + /* fall through */ @@ -6091,7 +6053,7 @@ new file mode 100644 + } + + ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic cid #%uL received id:%uz:%xV:%*xs", ++ "quic cid seq:%uL received id:%uz:%xV:%*xs", + cid->seqnum, id->len, id, + (size_t) NGX_QUIC_SR_TOKEN_LEN, cid->sr_token); + @@ -6171,7 +6133,7 @@ new file mode 100644 + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic socket #%uL is retired", qsock->sid.seqnum); ++ "quic socket seq:%uL is retired", qsock->sid.seqnum); + + ngx_quic_close_socket(c, qsock); + @@ -7349,7 +7311,7 @@ new file mode 100644 + } + + ngx_log_error(NGX_LOG_INFO, c->log, 0, -+ "quic path #%uL addr:%V successfully validated", ++ "quic path seq:%uL addr:%V successfully validated", + path->seqnum, &path->addr_text); + + ngx_quic_path_dbg(c, "is validated", path); @@ -7407,7 +7369,7 @@ new file mode 100644 + NGX_SOCKADDR_STRLEN, 1); + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic path #%uL created addr:%V", ++ "quic path seq:%uL created addr:%V", + path->seqnum, &path->addr_text); + return path; +} @@ -7535,8 +7497,8 @@ new file mode 100644 + path->received += len; + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic packet len:%O via sock#%uL path#%uL", -+ len, qsock->sid.seqnum, path->seqnum); ++ "quic packet len:%O via sock seq:%L path seq:%uL", ++ len, (int64_t) qsock->sid.seqnum, path->seqnum); + ngx_quic_path_dbg(c, "status", path); + + return NGX_OK; @@ -7564,7 +7526,7 @@ new file mode 100644 + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic path #%uL addr:%V retired", ++ "quic path seq:%uL addr:%V retired", + path->seqnum, &path->addr_text); + + return NGX_OK; @@ -7587,7 +7549,7 @@ new file mode 100644 + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic send path set to #%uL addr:%V", ++ "quic send path set to seq:%uL addr:%V", + path->seqnum, &path->addr_text); +} + @@ -7664,7 +7626,7 @@ new file mode 100644 + } + + ngx_log_error(NGX_LOG_INFO, c->log, 0, -+ "quic migrated to path#%uL addr:%V", ++ "quic migrated to path seq:%uL addr:%V", + qc->path->seqnum, &qc->path->addr_text); + + ngx_quic_path_dbg(c, "is now active", qc->path); @@ -7683,7 +7645,7 @@ new file mode 100644 + qc = ngx_quic_get_connection(c); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic initiated validation of path #%uL", path->seqnum); ++ "quic initiated validation of path seq:%uL", path->seqnum); + + path->validating = 1; + @@ -7719,7 +7681,7 @@ new file mode 100644 + ngx_quic_frame_t frame; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic path #%uL send path_challenge tries:%ui", ++ "quic path seq:%uL send path_challenge tries:%ui", + path->seqnum, path->tries); + + ngx_memzero(&frame, sizeof(ngx_quic_frame_t)); @@ -7809,7 +7771,7 @@ new file mode 100644 + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, -+ "quic path #%uL validation failed", path->seqnum); ++ "quic path seq:%uL validation failed", path->seqnum); + + /* found expired path */ + @@ -7843,7 +7805,7 @@ new file mode 100644 + ngx_quic_set_connection_path(c, qc->path); + + ngx_log_error(NGX_LOG_INFO, c->log, 0, -+ "quic path #%uL addr:%V is restored from backup", ++ "quic path seq:%uL addr:%V is restored from backup", + qc->path->seqnum, &qc->path->addr_text); + + ngx_quic_path_dbg(c, "is active", qc->path); @@ -7885,7 +7847,7 @@ new file mode 100644 + +#define ngx_quic_path_dbg(c, msg, path) \ + ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, \ -+ "quic path#%uL %s sent:%O recvd:%O state:%s%s%s", \ ++ "quic path seq:%uL %s sent:%O recvd:%O state:%s%s%s", \ + path->seqnum, msg, path->sent, path->received, \ + path->limited ? "L" : "", path->validated ? "V": "N", \ + path->validating ? "R": ""); @@ -7910,7 +7872,7 @@ diff --git a/src/event/quic/ngx_event_quic_output.c b/src/event/quic/ngx_event_q new file mode 100644 --- /dev/null +++ b/src/event/quic/ngx_event_quic_output.c -@@ -0,0 +1,1273 @@ +@@ -0,0 +1,1268 @@ + +/* + * Copyright (C) Nginx, Inc. @@ -8318,7 +8280,7 @@ new file mode 100644 + struct msghdr msg; + struct cmsghdr *cmsg; + -+#if defined(NGX_HAVE_ADDRINFO_CMSG) ++#if (NGX_HAVE_ADDRINFO_CMSG) + char msg_control[CMSG_SPACE(sizeof(uint16_t)) + + CMSG_SPACE(sizeof(ngx_addrinfo_t))]; +#else @@ -8351,7 +8313,7 @@ new file mode 100644 + valp = (void *) CMSG_DATA(cmsg); + *valp = segment; + -+#if defined(NGX_HAVE_ADDRINFO_CMSG) ++#if (NGX_HAVE_ADDRINFO_CMSG) + if (c->listening && c->listening->wildcard && c->local_sockaddr) { + cmsg = CMSG_NXTHDR(&msg, cmsg); + clen += ngx_set_srcaddr_cmsg(cmsg, c->local_sockaddr); @@ -8586,13 +8548,10 @@ new file mode 100644 +ngx_quic_init_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx, + ngx_quic_header_t *pkt, ngx_quic_path_t *path) +{ -+ ngx_quic_socket_t *qsock; + ngx_quic_connection_t *qc; + + qc = ngx_quic_get_connection(c); + -+ qsock = ngx_quic_get_socket(c); -+ + ngx_memzero(pkt, sizeof(ngx_quic_header_t)); + + pkt->flags = NGX_QUIC_PKT_FIXED_BIT; @@ -8612,8 +8571,7 @@ new file mode 100644 + pkt->dcid.data = path->cid->id; + pkt->dcid.len = path->cid->len; + -+ pkt->scid.data = qsock->sid.id; -+ pkt->scid.len = qsock->sid.len; ++ pkt->scid = qc->tp.initial_scid; + + pkt->version = qc->version; + pkt->log = c->log; @@ -8632,7 +8590,7 @@ new file mode 100644 + ssize_t n; + struct iovec iov; + struct msghdr msg; -+#if defined(NGX_HAVE_ADDRINFO_CMSG) ++#if (NGX_HAVE_ADDRINFO_CMSG) + struct cmsghdr *cmsg; + char msg_control[CMSG_SPACE(sizeof(ngx_addrinfo_t))]; +#endif @@ -8648,7 +8606,7 @@ new file mode 100644 + msg.msg_name = sockaddr; + msg.msg_namelen = socklen; + -+#if defined(NGX_HAVE_ADDRINFO_CMSG) ++#if (NGX_HAVE_ADDRINFO_CMSG) + if (c->listening && c->listening->wildcard && c->local_sockaddr) { + + msg.msg_control = msg_control; @@ -8863,8 +8821,7 @@ new file mode 100644 + return NGX_ERROR; + } + -+ if (ngx_quic_keys_set_initial_secret(c->pool, pkt.keys, &inpkt->dcid, -+ inpkt->version) ++ if (ngx_quic_keys_set_initial_secret(c->pool, pkt.keys, &inpkt->dcid) + != NGX_OK) + { + return NGX_ERROR; @@ -8897,7 +8854,7 @@ new file mode 100644 + return NGX_ERROR; + } + -+ return NGX_OK; ++ return NGX_DONE; +} + + @@ -9233,7 +9190,7 @@ diff --git a/src/event/quic/ngx_event_quic_protection.c b/src/event/quic/ngx_eve new file mode 100644 --- /dev/null +++ b/src/event/quic/ngx_event_quic_protection.c -@@ -0,0 +1,1186 @@ +@@ -0,0 +1,1177 @@ + +/* + * Copyright (C) Nginx, Inc. @@ -9382,7 +9339,7 @@ new file mode 100644 + +ngx_int_t +ngx_quic_keys_set_initial_secret(ngx_pool_t *pool, ngx_quic_keys_t *keys, -+ ngx_str_t *secret, uint32_t version) ++ ngx_str_t *secret) +{ + size_t is_len; + uint8_t is[SHA256_DIGEST_LENGTH]; @@ -9393,9 +9350,6 @@ new file mode 100644 + static const uint8_t salt[20] = + "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17" + "\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb\x7f\x0a"; -+ static const uint8_t salt29[20] = -+ "\xaf\xbf\xec\x28\x99\x93\xd2\x4c\x9e\x97" -+ "\x86\xf1\x9c\x61\x11\xe0\x43\x90\xa8\x99"; + + client = &keys->secrets[ssl_encryption_initial].client; + server = &keys->secrets[ssl_encryption_initial].server; @@ -9411,7 +9365,7 @@ new file mode 100644 + is_len = SHA256_DIGEST_LENGTH; + + if (ngx_hkdf_extract(is, &is_len, digest, secret->data, secret->len, -+ (version & 0xff000000) ? salt29 : salt, sizeof(salt)) ++ salt, sizeof(salt)) + != NGX_OK) + { + return NGX_ERROR; @@ -10128,12 +10082,8 @@ new file mode 100644 + /* 5.8. Retry Packet Integrity */ + static u_char key[16] = + "\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e"; -+ static u_char key29[16] = -+ "\xcc\xce\x18\x7e\xd0\x9a\x09\xd0\x57\x28\x15\x5a\x6c\xb9\x6b\xe1"; + static u_char nonce[NGX_QUIC_IV_LEN] = + "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb"; -+ static u_char nonce29[NGX_QUIC_IV_LEN] = -+ "\xe5\x49\x30\xf9\x7f\x21\x36\xf0\x53\x0a\x8c\x1c"; + static ngx_str_t in = ngx_string(""); + + ad.data = res->data; @@ -10152,12 +10102,10 @@ new file mode 100644 + } + + secret.key.len = sizeof(key); -+ secret.key.data = (pkt->version & 0xff000000) ? key29 : key; ++ secret.key.data = key; + secret.iv.len = NGX_QUIC_IV_LEN; + -+ if (ngx_quic_tls_seal(ciphers.c, &secret, &itag, -+ (pkt->version & 0xff000000) ? nonce29 : nonce, -+ &in, &ad, pkt->log) ++ if (ngx_quic_tls_seal(ciphers.c, &secret, &itag, nonce, &in, &ad, pkt->log) + != NGX_OK) + { + return NGX_ERROR; @@ -10446,7 +10394,7 @@ new file mode 100644 + +ngx_quic_keys_t *ngx_quic_keys_new(ngx_pool_t *pool); +ngx_int_t ngx_quic_keys_set_initial_secret(ngx_pool_t *pool, -+ ngx_quic_keys_t *keys, ngx_str_t *secret, uint32_t version); ++ ngx_quic_keys_t *keys, ngx_str_t *secret); +ngx_int_t ngx_quic_keys_set_encryption_secret(ngx_pool_t *pool, + ngx_uint_t is_write, ngx_quic_keys_t *keys, + enum ssl_encryption_level_t level, const SSL_CIPHER *cipher, @@ -10629,7 +10577,7 @@ new file mode 100644 + qc->nsockets--; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic socket #%L closed nsock:%ui", ++ "quic socket seq:%L closed nsock:%ui", + (int64_t) qsock->sid.seqnum, qc->nsockets); +} + @@ -10654,7 +10602,7 @@ new file mode 100644 + qsock->quic = qc; + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic socket #%L listening at sid:%xV nsock:%ui", ++ "quic socket seq:%L listening at sid:%xV nsock:%ui", + (int64_t) sid->seqnum, &id, qc->nsockets); + + return NGX_OK; @@ -11307,8 +11255,8 @@ new file mode 100644 + } +#endif + -+#if BORINGSSL_API_VERSION >= 13 -+ SSL_set_quic_use_legacy_codepoint(ssl_conn, qc->version != 1); ++#if (BORINGSSL_API_VERSION >= 13 && BORINGSSL_API_VERSION < 15) ++ SSL_set_quic_use_legacy_codepoint(ssl_conn, 0); +#endif + + qsock = ngx_quic_get_socket(c); @@ -11384,7 +11332,7 @@ diff --git a/src/event/quic/ngx_event_quic_streams.c b/src/event/quic/ngx_event_ new file mode 100644 --- /dev/null +++ b/src/event/quic/ngx_event_quic_streams.c -@@ -0,0 +1,1599 @@ +@@ -0,0 +1,1586 @@ + +/* + * Copyright (C) Nginx, Inc. @@ -11421,17 +11369,19 @@ new file mode 100644 +static ngx_int_t ngx_quic_update_flow(ngx_connection_t *c, uint64_t last); +static ngx_int_t ngx_quic_update_max_stream_data(ngx_connection_t *c); +static ngx_int_t ngx_quic_update_max_data(ngx_connection_t *c); ++static void ngx_quic_set_event(ngx_event_t *ev); + + +ngx_connection_t * +ngx_quic_open_stream(ngx_connection_t *c, ngx_uint_t bidi) +{ + uint64_t id; -+ ngx_quic_stream_t *qs, *nqs; ++ ngx_connection_t *pc; ++ ngx_quic_stream_t *nqs; + ngx_quic_connection_t *qc; + -+ qs = c->quic; -+ qc = ngx_quic_get_connection(qs->parent); ++ pc = c->quic ? c->quic->parent : c; ++ qc = ngx_quic_get_connection(pc); + + if (bidi) { + if (qc->streams.server_streams_bidi @@ -11477,7 +11427,7 @@ new file mode 100644 + qc->streams.server_streams_uni++; + } + -+ nqs = ngx_quic_create_stream(qs->parent, id); ++ nqs = ngx_quic_create_stream(pc, id); + if (nqs == NULL) { + return NULL; + } @@ -11542,7 +11492,6 @@ new file mode 100644 +{ + ngx_pool_t *pool; + ngx_queue_t *q; -+ ngx_event_t *rev, *wev; + ngx_rbtree_t *tree; + ngx_rbtree_node_t *node; + ngx_quic_stream_t *qs; @@ -11578,19 +11527,11 @@ new file mode 100644 + { + qs = (ngx_quic_stream_t *) node; + -+ rev = qs->connection->read; -+ rev->error = 1; -+ rev->ready = 1; -+ -+ wev = qs->connection->write; -+ wev->error = 1; -+ wev->ready = 1; ++ qs->recv_state = NGX_QUIC_STREAM_RECV_RESET_RECVD; ++ qs->send_state = NGX_QUIC_STREAM_SEND_RESET_SENT; + -+ ngx_post_event(rev, &ngx_posted_events); -+ -+ if (rev->timer_set) { -+ ngx_del_timer(rev); -+ } ++ ngx_quic_set_event(qs->connection->read); ++ ngx_quic_set_event(qs->connection->write); + +#if (NGX_DEBUG) + ns++; @@ -11607,19 +11548,22 @@ new file mode 100644 +ngx_int_t +ngx_quic_reset_stream(ngx_connection_t *c, ngx_uint_t err) +{ -+ ngx_event_t *wev; + ngx_connection_t *pc; + ngx_quic_frame_t *frame; + ngx_quic_stream_t *qs; + ngx_quic_connection_t *qc; + -+ wev = c->write; ++ qs = c->quic; + -+ if (wev->error) { ++ if (qs->send_state == NGX_QUIC_STREAM_SEND_DATA_RECVD ++ || qs->send_state == NGX_QUIC_STREAM_SEND_RESET_SENT ++ || qs->send_state == NGX_QUIC_STREAM_SEND_RESET_RECVD) ++ { + return NGX_OK; + } + -+ qs = c->quic; ++ qs->send_state = NGX_QUIC_STREAM_SEND_RESET_SENT; ++ + pc = qs->parent; + qc = ngx_quic_get_connection(pc); + @@ -11636,9 +11580,6 @@ new file mode 100644 + + ngx_quic_queue_frame(qc, frame); + -+ wev->error = 1; -+ wev->ready = 1; -+ + return NGX_OK; +} + @@ -11646,27 +11587,15 @@ new file mode 100644 +ngx_int_t +ngx_quic_shutdown_stream(ngx_connection_t *c, int how) +{ -+ ngx_quic_stream_t *qs; -+ -+ qs = c->quic; -+ + if (how == NGX_RDWR_SHUTDOWN || how == NGX_WRITE_SHUTDOWN) { -+ if ((qs->id & NGX_QUIC_STREAM_SERVER_INITIATED) -+ || (qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) -+ { -+ if (ngx_quic_shutdown_stream_send(c) != NGX_OK) { -+ return NGX_ERROR; -+ } ++ if (ngx_quic_shutdown_stream_send(c) != NGX_OK) { ++ return NGX_ERROR; + } + } + + if (how == NGX_RDWR_SHUTDOWN || how == NGX_READ_SHUTDOWN) { -+ if ((qs->id & NGX_QUIC_STREAM_SERVER_INITIATED) == 0 -+ || (qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) -+ { -+ if (ngx_quic_shutdown_stream_recv(c) != NGX_OK) { -+ return NGX_ERROR; -+ } ++ if (ngx_quic_shutdown_stream_recv(c) != NGX_OK) { ++ return NGX_ERROR; + } + } + @@ -11677,19 +11606,21 @@ new file mode 100644 +static ngx_int_t +ngx_quic_shutdown_stream_send(ngx_connection_t *c) +{ -+ ngx_event_t *wev; + ngx_connection_t *pc; + ngx_quic_frame_t *frame; + ngx_quic_stream_t *qs; + ngx_quic_connection_t *qc; + -+ wev = c->write; ++ qs = c->quic; + -+ if (wev->error) { ++ if (qs->send_state != NGX_QUIC_STREAM_SEND_READY ++ && qs->send_state != NGX_QUIC_STREAM_SEND_SEND) ++ { + return NGX_OK; + } + -+ qs = c->quic; ++ qs->send_state = NGX_QUIC_STREAM_SEND_DATA_SENT; ++ + pc = qs->parent; + qc = ngx_quic_get_connection(pc); + @@ -11713,8 +11644,6 @@ new file mode 100644 + + ngx_quic_queue_frame(qc, frame); + -+ wev->error = 1; -+ + return NGX_OK; +} + @@ -11722,19 +11651,19 @@ new file mode 100644 +static ngx_int_t +ngx_quic_shutdown_stream_recv(ngx_connection_t *c) +{ -+ ngx_event_t *rev; + ngx_connection_t *pc; + ngx_quic_frame_t *frame; + ngx_quic_stream_t *qs; + ngx_quic_connection_t *qc; + -+ rev = c->read; ++ qs = c->quic; + -+ if (rev->pending_eof || rev->error) { ++ if (qs->recv_state != NGX_QUIC_STREAM_RECV_RECV ++ && qs->recv_state != NGX_QUIC_STREAM_RECV_SIZE_KNOWN) ++ { + return NGX_OK; + } + -+ qs = c->quic; + pc = qs->parent; + qc = ngx_quic_get_connection(pc); + @@ -11757,8 +11686,6 @@ new file mode 100644 + + ngx_quic_queue_frame(qc, frame); + -+ rev->error = 1; -+ + return NGX_OK; +} + @@ -12076,9 +12003,13 @@ new file mode 100644 + if (id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { + if (id & NGX_QUIC_STREAM_SERVER_INITIATED) { + qs->send_max_data = qc->ctp.initial_max_stream_data_uni; ++ qs->recv_state = NGX_QUIC_STREAM_RECV_DATA_READ; ++ qs->send_state = NGX_QUIC_STREAM_SEND_READY; + + } else { + qs->recv_max_data = qc->tp.initial_max_stream_data_uni; ++ qs->recv_state = NGX_QUIC_STREAM_RECV_RECV; ++ qs->send_state = NGX_QUIC_STREAM_SEND_DATA_RECVD; + } + + } else { @@ -12090,6 +12021,9 @@ new file mode 100644 + qs->send_max_data = qc->ctp.initial_max_stream_data_bidi_local; + qs->recv_max_data = qc->tp.initial_max_stream_data_bidi_remote; + } ++ ++ qs->recv_state = NGX_QUIC_STREAM_RECV_RECV; ++ qs->send_state = NGX_QUIC_STREAM_SEND_READY; + } + + qs->recv_window = qs->recv_max_data; @@ -12130,25 +12064,19 @@ new file mode 100644 + pc = qs->parent; + rev = c->read; + -+ if (rev->error) { ++ if (qs->recv_state == NGX_QUIC_STREAM_RECV_RESET_RECVD ++ || qs->recv_state == NGX_QUIC_STREAM_RECV_RESET_READ) ++ { ++ qs->recv_state = NGX_QUIC_STREAM_RECV_RESET_READ; ++ rev->error = 1; + return NGX_ERROR; + } + -+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic stream id:0x%xL recv eof:%d buf:%uz", -+ qs->id, rev->pending_eof, size); -+ -+ if (qs->in == NULL || qs->in->buf->sync) { -+ rev->ready = 0; -+ -+ if (qs->recv_offset == qs->final_size) { -+ rev->eof = 1; -+ return 0; -+ } ++ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, ++ "quic stream id:0x%xL recv buf:%uz", qs->id, size); + -+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic stream id:0x%xL recv() not ready", qs->id); -+ return NGX_AGAIN; ++ if (size == 0) { ++ return 0; + } + + in = ngx_quic_read_chain(pc, &qs->in, size); @@ -12166,8 +12094,23 @@ new file mode 100644 + + ngx_quic_free_chain(pc, in); + -+ if (qs->in == NULL) { -+ rev->ready = rev->pending_eof; ++ if (len == 0) { ++ rev->ready = 0; ++ ++ if (qs->recv_state == NGX_QUIC_STREAM_RECV_SIZE_KNOWN ++ && qs->recv_offset == qs->final_size) ++ { ++ qs->recv_state = NGX_QUIC_STREAM_RECV_DATA_READ; ++ } ++ ++ if (qs->recv_state == NGX_QUIC_STREAM_RECV_DATA_READ) { ++ rev->eof = 1; ++ return 0; ++ } ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, ++ "quic stream id:0x%xL recv() not ready", qs->id); ++ return NGX_AGAIN; + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, @@ -12225,10 +12168,15 @@ new file mode 100644 + qc = ngx_quic_get_connection(pc); + wev = c->write; + -+ if (wev->error) { ++ if (qs->send_state != NGX_QUIC_STREAM_SEND_READY ++ && qs->send_state != NGX_QUIC_STREAM_SEND_SEND) ++ { ++ wev->error = 1; + return NGX_CHAIN_ERROR; + } + ++ qs->send_state = NGX_QUIC_STREAM_SEND_SEND; ++ + flow = ngx_quic_max_stream_flow(c); + if (flow == 0) { + wev->ready = 0; @@ -12406,7 +12354,6 @@ new file mode 100644 + ngx_quic_frame_t *frame) +{ + uint64_t last; -+ ngx_event_t *rev; + ngx_connection_t *sc; + ngx_quic_stream_t *qs; + ngx_quic_connection_t *qc; @@ -12437,9 +12384,9 @@ new file mode 100644 + + sc = qs->connection; + -+ rev = sc->read; -+ -+ if (rev->error) { ++ if (qs->recv_state != NGX_QUIC_STREAM_RECV_RECV ++ && qs->recv_state != NGX_QUIC_STREAM_RECV_SIZE_KNOWN) ++ { + return NGX_OK; + } + @@ -12472,8 +12419,8 @@ new file mode 100644 + return NGX_ERROR; + } + -+ rev->pending_eof = 1; + qs->final_size = last; ++ qs->recv_state = NGX_QUIC_STREAM_RECV_SIZE_KNOWN; + } + + if (ngx_quic_write_chain(c, &qs->in, frame->data, f->length, @@ -12484,11 +12431,7 @@ new file mode 100644 + } + + if (f->offset == qs->recv_offset) { -+ rev->ready = 1; -+ -+ if (rev->active) { -+ ngx_post_event(rev, &ngx_posted_events); -+ } ++ ngx_quic_set_event(sc->read); + } + + return NGX_OK; @@ -12499,7 +12442,6 @@ new file mode 100644 +ngx_quic_handle_max_data_frame(ngx_connection_t *c, + ngx_quic_max_data_frame_t *f) +{ -+ ngx_event_t *wev; + ngx_rbtree_t *tree; + ngx_rbtree_node_t *node; + ngx_quic_stream_t *qs; @@ -12521,12 +12463,7 @@ new file mode 100644 + node = ngx_rbtree_next(tree, node)) + { + qs = (ngx_quic_stream_t *) node; -+ wev = qs->connection->write; -+ -+ if (wev->active) { -+ wev->ready = 1; -+ ngx_post_event(wev, &ngx_posted_events); -+ } ++ ngx_quic_set_event(qs->connection->write); + } + } + @@ -12587,7 +12524,6 @@ new file mode 100644 + ngx_quic_header_t *pkt, ngx_quic_max_stream_data_frame_t *f) +{ + uint64_t sent; -+ ngx_event_t *wev; + ngx_quic_stream_t *qs; + ngx_quic_connection_t *qc; + @@ -12617,12 +12553,7 @@ new file mode 100644 + sent = qs->connection->sent; + + if (sent >= qs->send_max_data) { -+ wev = qs->connection->write; -+ -+ if (wev->active) { -+ wev->ready = 1; -+ ngx_post_event(wev, &ngx_posted_events); -+ } ++ ngx_quic_set_event(qs->connection->write); + } + + qs->send_max_data = f->limit; @@ -12635,7 +12566,6 @@ new file mode 100644 +ngx_quic_handle_reset_stream_frame(ngx_connection_t *c, + ngx_quic_header_t *pkt, ngx_quic_reset_stream_frame_t *f) +{ -+ ngx_event_t *rev; + ngx_connection_t *sc; + ngx_quic_stream_t *qs; + ngx_quic_connection_t *qc; @@ -12659,11 +12589,15 @@ new file mode 100644 + return NGX_OK; + } + -+ sc = qs->connection; ++ if (qs->recv_state == NGX_QUIC_STREAM_RECV_RESET_RECVD ++ || qs->recv_state == NGX_QUIC_STREAM_RECV_RESET_READ) ++ { ++ return NGX_OK; ++ } ++ ++ qs->recv_state = NGX_QUIC_STREAM_RECV_RESET_RECVD; + -+ rev = sc->read; -+ rev->error = 1; -+ rev->ready = 1; ++ sc = qs->connection; + + if (ngx_quic_control_flow(sc, f->final_size) != NGX_OK) { + return NGX_ERROR; @@ -12685,9 +12619,7 @@ new file mode 100644 + return NGX_ERROR; + } + -+ if (rev->active) { -+ ngx_post_event(rev, &ngx_posted_events); -+ } ++ ngx_quic_set_event(qs->connection->read); + + return NGX_OK; +} @@ -12697,7 +12629,6 @@ new file mode 100644 +ngx_quic_handle_stop_sending_frame(ngx_connection_t *c, + ngx_quic_header_t *pkt, ngx_quic_stop_sending_frame_t *f) +{ -+ ngx_event_t *wev; + ngx_quic_stream_t *qs; + ngx_quic_connection_t *qc; + @@ -12724,11 +12655,7 @@ new file mode 100644 + return NGX_ERROR; + } + -+ wev = qs->connection->write; -+ -+ if (wev->active) { -+ ngx_post_event(wev, &ngx_posted_events); -+ } ++ ngx_quic_set_event(qs->connection->write); + + return NGX_OK; +} @@ -12767,7 +12694,6 @@ new file mode 100644 +ngx_quic_handle_stream_ack(ngx_connection_t *c, ngx_quic_frame_t *f) +{ + uint64_t sent, unacked; -+ ngx_event_t *wev; + ngx_quic_stream_t *qs; + ngx_quic_connection_t *qc; + @@ -12778,13 +12704,11 @@ new file mode 100644 + return; + } + -+ wev = qs->connection->write; + sent = qs->connection->sent; + unacked = sent - qs->acked; + -+ if (unacked >= qc->conf->stream_buffer_size && wev->active) { -+ wev->ready = 1; -+ ngx_post_event(wev, &ngx_posted_events); ++ if (unacked >= qc->conf->stream_buffer_size) { ++ ngx_quic_set_event(qs->connection->write); + } + + qs->acked += f->u.stream.length; @@ -12799,11 +12723,9 @@ new file mode 100644 +ngx_quic_control_flow(ngx_connection_t *c, uint64_t last) +{ + uint64_t len; -+ ngx_event_t *rev; + ngx_quic_stream_t *qs; + ngx_quic_connection_t *qc; + -+ rev = c->read; + qs = c->quic; + qc = ngx_quic_get_connection(qs->parent); + @@ -12820,7 +12742,9 @@ new file mode 100644 + + qs->recv_last += len; + -+ if (!rev->error && qs->recv_last > qs->recv_max_data) { ++ if (qs->recv_state == NGX_QUIC_STREAM_RECV_RECV ++ && qs->recv_last > qs->recv_max_data) ++ { + qc->error = NGX_QUIC_ERR_FLOW_CONTROL_ERROR; + return NGX_ERROR; + } @@ -12840,12 +12764,10 @@ new file mode 100644 +ngx_quic_update_flow(ngx_connection_t *c, uint64_t last) +{ + uint64_t len; -+ ngx_event_t *rev; + ngx_connection_t *pc; + ngx_quic_stream_t *qs; + ngx_quic_connection_t *qc; + -+ rev = c->read; + qs = c->quic; + pc = qs->parent; + qc = ngx_quic_get_connection(pc); @@ -12861,9 +12783,7 @@ new file mode 100644 + + qs->recv_offset += len; + -+ if (!rev->pending_eof && !rev->error -+ && qs->recv_max_data <= qs->recv_offset + qs->recv_window / 2) -+ { ++ if (qs->recv_max_data <= qs->recv_offset + qs->recv_window / 2) { + if (ngx_quic_update_max_stream_data(c) != NGX_OK) { + return NGX_ERROR; + } @@ -12896,6 +12816,10 @@ new file mode 100644 + pc = qs->parent; + qc = ngx_quic_get_connection(pc); + ++ if (qs->recv_state != NGX_QUIC_STREAM_RECV_RECV) { ++ return NGX_OK; ++ } ++ + recv_max_data = qs->recv_offset + qs->recv_window; + + if (qs->recv_max_data == recv_max_data) { @@ -12958,6 +12882,17 @@ new file mode 100644 +} + + ++static void ++ngx_quic_set_event(ngx_event_t *ev) ++{ ++ ev->ready = 1; ++ ++ if (ev->active) { ++ ngx_post_event(ev, &ngx_posted_events); ++ } ++} ++ ++ +ngx_int_t +ngx_quic_handle_read_event(ngx_event_t *rev, ngx_uint_t flags) +{ @@ -13365,7 +13300,7 @@ diff --git a/src/event/quic/ngx_event_quic_transport.c b/src/event/quic/ngx_even new file mode 100644 --- /dev/null +++ b/src/event/quic/ngx_event_quic_transport.c -@@ -0,0 +1,2170 @@ +@@ -0,0 +1,2164 @@ + +/* + * Copyright (C) Nginx, Inc. @@ -13441,8 +13376,6 @@ new file mode 100644 +#define ngx_quic_build_int_set(p, value, len, bits) \ + (*(p)++ = ((value >> ((len) * 8)) & 0xff) | ((bits) << 6)) + -+#define NGX_QUIC_VERSION(c) (0xff000000 + (c)) -+ + +static u_char *ngx_quic_parse_int(u_char *pos, u_char *end, uint64_t *out); +static ngx_uint_t ngx_quic_varint_len(uint64_t value); @@ -13505,10 +13438,6 @@ new file mode 100644 +uint32_t ngx_quic_versions[] = { + /* QUICv1 */ + 0x00000001, -+ NGX_QUIC_VERSION(29), -+ NGX_QUIC_VERSION(30), -+ NGX_QUIC_VERSION(31), -+ NGX_QUIC_VERSION(32), +}; + +#define NGX_QUIC_NVERSIONS \ @@ -13659,14 +13588,14 @@ new file mode 100644 + + if (ngx_quic_parse_short_header(pkt, NGX_QUIC_SERVER_CID_LEN) != NGX_OK) + { -+ return NGX_DECLINED; ++ return NGX_ERROR; + } + + return NGX_OK; + } + + if (ngx_quic_parse_long_header(pkt) != NGX_OK) { -+ return NGX_DECLINED; ++ return NGX_ERROR; + } + + if (!ngx_quic_supported_version(pkt->version)) { @@ -13674,7 +13603,7 @@ new file mode 100644 + } + + if (ngx_quic_parse_long_header_v1(pkt) != NGX_OK) { -+ return NGX_DECLINED; ++ return NGX_ERROR; + } + + return NGX_OK; @@ -15941,15 +15870,12 @@ new file mode 100644 diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c -@@ -419,16 +419,22 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t +@@ -419,16 +419,19 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { - unsigned int srvlen; - unsigned char *srv; -+#if (NGX_HTTP_V3) -+ const char *fmt; -+#endif + unsigned int srvlen; + unsigned char *srv; #if (NGX_DEBUG) @@ -15971,7 +15897,7 @@ diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ c = ngx_ssl_get_connection(ssl_conn); #endif -@@ -441,14 +447,46 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t +@@ -441,14 +444,34 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t } #endif @@ -15995,23 +15921,11 @@ diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ + if (h3scf->hq) { + srv = (unsigned char *) NGX_HTTP_V3_HQ_ALPN_PROTO; + srvlen = sizeof(NGX_HTTP_V3_HQ_ALPN_PROTO) - 1; -+ fmt = NGX_HTTP_V3_HQ_ALPN_DRAFT_FMT; + } else +#endif + { + srv = (unsigned char *) NGX_HTTP_V3_ALPN_PROTO; + srvlen = sizeof(NGX_HTTP_V3_ALPN_PROTO) - 1; -+ fmt = NGX_HTTP_V3_ALPN_DRAFT_FMT; -+ } -+ -+ /* QUIC draft */ -+ -+ if (ngx_quic_version(c) > 1) { -+ srv = ngx_pnalloc(c->pool, sizeof("\x05h3-xx") - 1); -+ if (srv == NULL) { -+ return SSL_TLSEXT_ERR_NOACK; -+ } -+ srvlen = ngx_sprintf(srv, fmt, ngx_quic_version(c)) - srv; + } + + } else @@ -16019,7 +15933,7 @@ diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ { srv = (unsigned char *) NGX_HTTP_ALPN_PROTOS; srvlen = sizeof(NGX_HTTP_ALPN_PROTOS) - 1; -@@ -1240,6 +1278,7 @@ static ngx_int_t +@@ -1240,6 +1263,7 @@ static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf) { ngx_uint_t a, p, s; @@ -16027,7 +15941,7 @@ diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ ngx_http_conf_addr_t *addr; ngx_http_conf_port_t *port; ngx_http_ssl_srv_conf_t *sscf; -@@ -1289,22 +1328,38 @@ ngx_http_ssl_init(ngx_conf_t *cf) +@@ -1289,22 +1313,38 @@ ngx_http_ssl_init(ngx_conf_t *cf) addr = port[p].addrs.elts; for (a = 0; a < port[p].addrs.nelts; a++) { @@ -16069,7 +15983,7 @@ diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ return NGX_ERROR; } -@@ -1325,8 +1380,8 @@ ngx_http_ssl_init(ngx_conf_t *cf) +@@ -1325,8 +1365,8 @@ ngx_http_ssl_init(ngx_conf_t *cf) ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "no \"ssl_certificate\" is defined for " @@ -16599,7 +16513,7 @@ diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c new file mode 100644 --- /dev/null +++ b/src/http/v3/ngx_http_v3.c -@@ -0,0 +1,115 @@ +@@ -0,0 +1,123 @@ + +/* + * Copyright (C) Roman Arutyunyan @@ -16649,6 +16563,10 @@ new file mode 100644 + h3c->keepalive.handler = ngx_http_v3_keepalive_handler; + h3c->keepalive.cancelable = 1; + ++ h3c->table.send_insert_count.log = pc->log; ++ h3c->table.send_insert_count.data = pc; ++ h3c->table.send_insert_count.handler = ngx_http_v3_inc_insert_count_handler; ++ + cln = ngx_pool_cleanup_add(pc->pool, 0); + if (cln == NULL) { + goto failed; @@ -16680,8 +16598,8 @@ new file mode 100644 + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 keepalive handler"); + -+ ngx_quic_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, -+ "keepalive timeout"); ++ ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, ++ "keepalive timeout"); +} + + @@ -16695,6 +16613,10 @@ new file mode 100644 + if (h3c->keepalive.timer_set) { + ngx_del_timer(&h3c->keepalive); + } ++ ++ if (h3c->table.send_insert_count.posted) { ++ ngx_delete_posted_event(&h3c->table.send_insert_count); ++ } +} + + @@ -16742,10 +16664,7 @@ new file mode 100644 + + +#define NGX_HTTP_V3_ALPN_PROTO "\x02h3" -+#define NGX_HTTP_V3_ALPN_DRAFT_FMT "\x05h3-%02uD" -+ +#define NGX_HTTP_V3_HQ_ALPN_PROTO "\x0Ahq-interop" -+#define NGX_HTTP_V3_HQ_ALPN_DRAFT_FMT "\x05hq-%02uD" + +#define NGX_HTTP_V3_VARLEN_INT_LEN 4 +#define NGX_HTTP_V3_PREFIX_INT_LEN 11 @@ -16803,7 +16722,8 @@ new file mode 100644 + + +#define ngx_http_quic_get_connection(c) \ -+ ((ngx_http_connection_t *) (c)->quic->parent->data) ++ ((ngx_http_connection_t *) ((c)->quic ? (c)->quic->parent->data \ ++ : (c)->data)) + +#define ngx_http_v3_get_session(c) ngx_http_quic_get_connection(c)->v3_session + @@ -16816,10 +16736,12 @@ new file mode 100644 + module) + +#define ngx_http_v3_finalize_connection(c, code, reason) \ -+ ngx_quic_finalize_connection(c->quic->parent, code, reason) ++ ngx_quic_finalize_connection((c)->quic ? (c)->quic->parent : (c), \ ++ code, reason) + +#define ngx_http_v3_shutdown_connection(c, code, reason) \ -+ ngx_quic_shutdown_connection(c->quic->parent, code, reason) ++ ngx_quic_shutdown_connection((c)->quic ? (c)->quic->parent : (c), \ ++ code, reason) + + +typedef struct { @@ -19337,7 +19259,7 @@ diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c new file mode 100644 --- /dev/null +++ b/src/http/v3/ngx_http_v3_parse.c -@@ -0,0 +1,2005 @@ +@@ -0,0 +1,2007 @@ + +/* + * Copyright (C) Roman Arutyunyan @@ -19735,6 +19657,8 @@ new file mode 100644 + if (ngx_http_v3_send_ack_section(c, c->quic->id) != NGX_OK) { + return NGX_ERROR; + } ++ ++ ngx_http_v3_ack_insert_count(c, st->prefix.insert_count); + } + + st->state = sw_start; @@ -23190,7 +23114,7 @@ diff --git a/src/http/v3/ngx_http_v3_table.c b/src/http/v3/ngx_http_v3_table.c new file mode 100644 --- /dev/null +++ b/src/http/v3/ngx_http_v3_table.c -@@ -0,0 +1,678 @@ +@@ -0,0 +1,719 @@ + +/* + * Copyright (C) Roman Arutyunyan @@ -23425,11 +23349,9 @@ new file mode 100644 + dt->elts[dt->nelts++] = field; + dt->size += size; + -+ /* TODO increment can be sent less often */ ++ dt->insert_count++; + -+ if (ngx_http_v3_send_inc_insert_count(c, 1) != NGX_OK) { -+ return NGX_ERROR; -+ } ++ ngx_post_event(&dt->send_insert_count, &ngx_posted_events); + + if (ngx_http_v3_new_entry(c) != NGX_OK) { + return NGX_ERROR; @@ -23439,6 +23361,34 @@ new file mode 100644 +} + + ++void ++ngx_http_v3_inc_insert_count_handler(ngx_event_t *ev) ++{ ++ ngx_connection_t *c; ++ ngx_http_v3_session_t *h3c; ++ ngx_http_v3_dynamic_table_t *dt; ++ ++ c = ev->data; ++ ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, ++ "http3 inc insert count handler"); ++ ++ h3c = ngx_http_v3_get_session(c); ++ dt = &h3c->table; ++ ++ if (dt->insert_count > dt->ack_insert_count) { ++ if (ngx_http_v3_send_inc_insert_count(c, ++ dt->insert_count - dt->ack_insert_count) ++ != NGX_OK) ++ { ++ return; ++ } ++ ++ dt->ack_insert_count = dt->insert_count; ++ } ++} ++ ++ +ngx_int_t +ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) +{ @@ -23800,6 +23750,21 @@ new file mode 100644 +} + + ++void ++ngx_http_v3_ack_insert_count(ngx_connection_t *c, uint64_t insert_count) ++{ ++ ngx_http_v3_session_t *h3c; ++ ngx_http_v3_dynamic_table_t *dt; ++ ++ h3c = ngx_http_v3_get_session(c); ++ dt = &h3c->table; ++ ++ if (dt->ack_insert_count < insert_count) { ++ dt->ack_insert_count = insert_count; ++ } ++} ++ ++ +static void +ngx_http_v3_unblock(void *data) +{ @@ -23873,7 +23838,7 @@ diff --git a/src/http/v3/ngx_http_v3_table.h b/src/http/v3/ngx_http_v3_table.h new file mode 100644 --- /dev/null +++ b/src/http/v3/ngx_http_v3_table.h -@@ -0,0 +1,53 @@ +@@ -0,0 +1,58 @@ + +/* + * Copyright (C) Roman Arutyunyan @@ -23902,9 +23867,13 @@ new file mode 100644 + ngx_uint_t base; + size_t size; + size_t capacity; ++ uint64_t insert_count; ++ uint64_t ack_insert_count; ++ ngx_event_t send_insert_count; +} ngx_http_v3_dynamic_table_t; + + ++void ngx_http_v3_inc_insert_count_handler(ngx_event_t *ev); +void ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c); +ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, + ngx_uint_t index, ngx_str_t *value); @@ -23922,6 +23891,7 @@ new file mode 100644 + ngx_uint_t *insert_count); +ngx_int_t ngx_http_v3_check_insert_count(ngx_connection_t *c, + ngx_uint_t insert_count); ++void ngx_http_v3_ack_insert_count(ngx_connection_t *c, uint64_t insert_count); +ngx_int_t ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, + uint64_t value); + @@ -23931,7 +23901,7 @@ diff --git a/src/http/v3/ngx_http_v3_uni.c b/src/http/v3/ngx_http_v3_uni.c new file mode 100644 --- /dev/null +++ b/src/http/v3/ngx_http_v3_uni.c -@@ -0,0 +1,733 @@ +@@ -0,0 +1,762 @@ + +/* + * Copyright (C) Roman Arutyunyan @@ -23960,7 +23930,8 @@ new file mode 100644 + +static void ngx_http_v3_close_uni_stream(ngx_connection_t *c); +static void ngx_http_v3_uni_read_handler(ngx_event_t *rev); -+static void ngx_http_v3_dummy_write_handler(ngx_event_t *wev); ++static void ngx_http_v3_uni_dummy_read_handler(ngx_event_t *wev); ++static void ngx_http_v3_uni_dummy_write_handler(ngx_event_t *wev); +static void ngx_http_v3_push_cleanup(void *data); +static ngx_connection_t *ngx_http_v3_get_uni_stream(ngx_connection_t *c, + ngx_uint_t type); @@ -24002,7 +23973,7 @@ new file mode 100644 + c->data = us; + + c->read->handler = ngx_http_v3_uni_read_handler; -+ c->write->handler = ngx_http_v3_dummy_write_handler; ++ c->write->handler = ngx_http_v3_uni_dummy_write_handler; + + ngx_http_v3_uni_read_handler(c->read); +} @@ -24186,7 +24157,33 @@ new file mode 100644 + + +static void -+ngx_http_v3_dummy_write_handler(ngx_event_t *wev) ++ngx_http_v3_uni_dummy_read_handler(ngx_event_t *rev) ++{ ++ u_char ch; ++ ngx_connection_t *c; ++ ++ c = rev->data; ++ ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 dummy read handler"); ++ ++ if (rev->ready) { ++ if (c->recv(c, &ch, 1) != 0) { ++ ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, NULL); ++ ngx_http_v3_close_uni_stream(c); ++ return; ++ } ++ } ++ ++ if (ngx_handle_read_event(rev, 0) != NGX_OK) { ++ ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, ++ NULL); ++ ngx_http_v3_close_uni_stream(c); ++ } ++} ++ ++ ++static void ++ngx_http_v3_uni_dummy_write_handler(ngx_event_t *wev) +{ + ngx_connection_t *c; + @@ -24327,8 +24324,8 @@ new file mode 100644 + + sc->data = us; + -+ sc->read->handler = ngx_http_v3_uni_read_handler; -+ sc->write->handler = ngx_http_v3_dummy_write_handler; ++ sc->read->handler = ngx_http_v3_uni_dummy_read_handler; ++ sc->write->handler = ngx_http_v3_uni_dummy_write_handler; + + if (index >= 0) { + h3c->known_streams[index] = sc; @@ -24343,6 +24340,8 @@ new file mode 100644 + goto failed; + } + ++ ngx_post_event(sc->read, &ngx_posted_events); ++ + return sc; + +failed: @@ -24779,7 +24778,7 @@ diff --git a/src/os/unix/ngx_udp_sendmsg_chain.c b/src/os/unix/ngx_udp_sendmsg_c - u_char msg_control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; -#endif - -+#if defined(NGX_HAVE_ADDRINFO_CMSG) ++#if (NGX_HAVE_ADDRINFO_CMSG) + struct cmsghdr *cmsg; + u_char msg_control[CMSG_SPACE(sizeof(ngx_addrinfo_t))]; #endif @@ -24790,7 +24789,7 @@ diff --git a/src/os/unix/ngx_udp_sendmsg_chain.c b/src/os/unix/ngx_udp_sendmsg_c msg.msg_iovlen = vec->count; -#if (NGX_HAVE_MSGHDR_MSG_CONTROL) -+#if defined(NGX_HAVE_ADDRINFO_CMSG) ++#if (NGX_HAVE_ADDRINFO_CMSG) + if (c->listening && c->listening->wildcard && c->local_sockaddr) { + + msg.msg_control = msg_control; @@ -24807,7 +24806,7 @@ diff --git a/src/os/unix/ngx_udp_sendmsg_chain.c b/src/os/unix/ngx_udp_sendmsg_c +} + + -+#if defined(NGX_HAVE_ADDRINFO_CMSG) ++#if (NGX_HAVE_ADDRINFO_CMSG) - if (c->listening && c->listening->wildcard && c->local_sockaddr) { +size_t |