git: 117d256c411a - main - www/nginx-devel: update HTTPv3/QUIC patch to the recent commit

From: Sergey A. Osokin <osa_at_FreeBSD.org>
Date: Tue, 01 Feb 2022 14:57:34 UTC
The branch main has been updated by osa:

URL: https://cgit.FreeBSD.org/ports/commit/?id=117d256c411ae24dc0e1c0868a1825a59300ca4e

commit 117d256c411ae24dc0e1c0868a1825a59300ca4e
Author:     Sergey A. Osokin <osa@FreeBSD.org>
AuthorDate: 2022-02-01 14:57:06 +0000
Commit:     Sergey A. Osokin <osa@FreeBSD.org>
CommitDate: 2022-02-01 14:57:26 +0000

    www/nginx-devel: update HTTPv3/QUIC patch to the recent commit
---
 www/nginx-devel/Makefile                 |   2 +-
 www/nginx-devel/files/extra-patch-httpv3 | 785 +++++++++++++++----------------
 2 files changed, 393 insertions(+), 394 deletions(-)

diff --git a/www/nginx-devel/Makefile b/www/nginx-devel/Makefile
index 332014908b34..381dee0d872e 100644
--- a/www/nginx-devel/Makefile
+++ b/www/nginx-devel/Makefile
@@ -2,7 +2,7 @@
 
 PORTNAME?=	nginx
 PORTVERSION=	1.21.6
-PORTREVISION=	3
+PORTREVISION=	4
 CATEGORIES=	www
 MASTER_SITES=	https://nginx.org/download/ \
 		LOCAL/osa
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;
*** 947 LINES SKIPPED ***