[PATCH 1 of 3] HTTP/3: "quic" parameter of "listen" directive

Roman Arutyunyan arut at nginx.com
Thu Jan 26 11:50:45 UTC 2023


# HG changeset patch
# User Roman Arutyunyan <arut at nginx.com>
# Date 1674653899 -14400
#      Wed Jan 25 17:38:19 2023 +0400
# Branch quic
# Node ID 250539ea3e05d0ac2ead4784a1b44f28ff4d1ef6
# Parent  a954b551dc3f8f059140e5843c49341cae55b98b
HTTP/3: "quic" parameter of "listen" directive.

Now "listen" directve has a new "quic" parameter which enables QUIC protocol
for the address.  Further, to enable HTTP/3, a new directive "http3" is
introduced.  The hq-interop protocol is enabled by "http3_hq" as before.
Now application protocol is chosen by ALPN.

Previously used "http3" parameter of "listen" is eliminated.

diff --git a/README b/README
--- a/README
+++ b/README
@@ -93,13 +93,13 @@ 2. Installing
 
 3. Configuration
 
-    The HTTP "listen" directive got a new option "http3" which enables
-    HTTP/3 over QUIC on the specified port.
+    The HTTP "listen" directive got a new option "quic" which enables
+    QUIC as client transport protocol instead of TCP.
 
     The Stream "listen" directive got a new option "quic" which enables
     QUIC as client transport protocol instead of TCP or plain UDP.
 
-    Along with "http3" or "quic", it's also possible to specify "reuseport"
+    Along with "quic", it's also possible to specify "reuseport"
     option [8] to make it work properly with multiple workers.
 
     To enable address validation:
@@ -133,12 +133,13 @@ 3. Configuration
 
     A number of directives were added that configure HTTP/3:
 
+        http3
+        http3_hq
         http3_stream_buffer_size
         http3_max_concurrent_pushes
         http3_max_concurrent_streams
         http3_push
         http3_push_preload
-        http3_hq (requires NGX_HTTP_V3_HQ macro)
 
     In http, an additional variable is available: $http3.
     The value of $http3 is "h3" for HTTP/3 connections,
@@ -160,13 +161,15 @@ Example configuration:
         server {
             # for better compatibility it's recommended
             # to use the same port for quic and https
-            listen 8443 http3 reuseport;
+            listen 8443 quic reuseport;
             listen 8443 ssl;
 
             ssl_certificate     certs/example.com.crt;
             ssl_certificate_key certs/example.com.key;
             ssl_protocols       TLSv1.3;
 
+            http3 on;
+
             location / {
                 # required for browsers to direct them into quic port
                 add_header Alt-Svc 'h3=":8443"; ma=86400';
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
@@ -427,7 +427,7 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t 
 #if (NGX_HTTP_V2 || NGX_HTTP_V3)
     ngx_http_connection_t   *hc;
 #endif
-#if (NGX_HTTP_V3 && NGX_HTTP_V3_HQ)
+#if (NGX_HTTP_V3)
     ngx_http_v3_srv_conf_t  *h3scf;
 #endif
 #if (NGX_HTTP_V2 || NGX_HTTP_V3 || NGX_DEBUG)
@@ -455,19 +455,26 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t 
     } else
 #endif
 #if (NGX_HTTP_V3)
-    if (hc->addr_conf->http3) {
+    if (hc->addr_conf->quic) {
 
-#if (NGX_HTTP_V3_HQ)
         h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module);
 
-        if (h3scf->hq) {
+        if (h3scf->enable && h3scf->enable_hq) {
+            srv = (unsigned char *) NGX_HTTP_V3_ALPN_PROTO
+                                    NGX_HTTP_V3_HQ_ALPN_PROTO;
+            srvlen = sizeof(NGX_HTTP_V3_ALPN_PROTO NGX_HTTP_V3_HQ_ALPN_PROTO)
+                     - 1;
+
+        } else if (h3scf->enable) {
+            srv = (unsigned char *) NGX_HTTP_V3_ALPN_PROTO;
+            srvlen = sizeof(NGX_HTTP_V3_ALPN_PROTO) - 1;
+
+        } else if (h3scf->enable_hq) {
             srv = (unsigned char *) NGX_HTTP_V3_HQ_ALPN_PROTO;
             srvlen = sizeof(NGX_HTTP_V3_HQ_ALPN_PROTO) - 1;
-        } else
-#endif
-        {
-            srv = (unsigned char *) NGX_HTTP_V3_ALPN_PROTO;
-            srvlen = sizeof(NGX_HTTP_V3_ALPN_PROTO) - 1;
+
+        } else {
+            return SSL_TLSEXT_ERR_ALERT_FATAL;
         }
 
     } else
@@ -1313,12 +1320,12 @@ ngx_http_ssl_init(ngx_conf_t *cf)
         addr = port[p].addrs.elts;
         for (a = 0; a < port[p].addrs.nelts; a++) {
 
-            if (!addr[a].opt.ssl && !addr[a].opt.http3) {
+            if (!addr[a].opt.ssl && !addr[a].opt.quic) {
                 continue;
             }
 
-            if (addr[a].opt.http3) {
-                name = "http3";
+            if (addr[a].opt.quic) {
+                name = "quic";
 
             } else {
                 name = "ssl";
@@ -1329,7 +1336,7 @@ ngx_http_ssl_init(ngx_conf_t *cf)
 
             if (sscf->certificates) {
 
-                if (addr[a].opt.http3 && !(sscf->protocols & NGX_SSL_TLSv1_3)) {
+                if (addr[a].opt.quic && !(sscf->protocols & NGX_SSL_TLSv1_3)) {
                     ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                                   "\"ssl_protocols\" must enable TLSv1.3 for "
                                   "the \"listen ... %s\" directive in %s:%ui",
diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -1241,7 +1241,7 @@ ngx_http_add_addresses(ngx_conf_t *cf, n
     ngx_uint_t             http2;
 #endif
 #if (NGX_HTTP_V3)
-    ngx_uint_t             http3;
+    ngx_uint_t             quic;
 #endif
 
     /*
@@ -1279,7 +1279,7 @@ ngx_http_add_addresses(ngx_conf_t *cf, n
         http2 = lsopt->http2 || addr[i].opt.http2;
 #endif
 #if (NGX_HTTP_V3)
-        http3 = lsopt->http3 || addr[i].opt.http3;
+        quic = lsopt->quic || addr[i].opt.quic;
 #endif
 
         if (lsopt->set) {
@@ -1318,7 +1318,7 @@ ngx_http_add_addresses(ngx_conf_t *cf, n
         addr[i].opt.http2 = http2;
 #endif
 #if (NGX_HTTP_V3)
-        addr[i].opt.http3 = http3;
+        addr[i].opt.quic = quic;
 #endif
 
         return NGX_OK;
@@ -1823,7 +1823,7 @@ ngx_http_add_listening(ngx_conf_t *cf, n
 
 #if (NGX_HTTP_V3)
 
-    ls->quic = addr->opt.http3;
+    ls->quic = addr->opt.quic;
 
     if (ls->quic) {
         ngx_rbtree_init(&ls->rbtree, &ls->sentinel,
@@ -1865,7 +1865,7 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_h
         addrs[i].conf.http2 = addr[i].opt.http2;
 #endif
 #if (NGX_HTTP_V3)
-        addrs[i].conf.http3 = addr[i].opt.http3;
+        addrs[i].conf.quic = addr[i].opt.quic;
 #endif
         addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
 
@@ -1933,7 +1933,7 @@ ngx_http_add_addrs6(ngx_conf_t *cf, ngx_
         addrs6[i].conf.http2 = addr[i].opt.http2;
 #endif
 #if (NGX_HTTP_V3)
-        addrs6[i].conf.http3 = addr[i].opt.http3;
+        addrs6[i].conf.quic = addr[i].opt.quic;
 #endif
         addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
 
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -4189,14 +4189,14 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx
 #endif
         }
 
-        if (ngx_strcmp(value[n].data, "http3") == 0) {
+        if (ngx_strcmp(value[n].data, "quic") == 0) {
 #if (NGX_HTTP_V3)
-            lsopt.http3 = 1;
+            lsopt.quic = 1;
             lsopt.type = SOCK_DGRAM;
             continue;
 #else
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "the \"http3\" parameter requires "
+                               "the \"quic\" parameter requires "
                                "ngx_http_v3_module");
             return NGX_CONF_ERROR;
 #endif
@@ -4304,8 +4304,8 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx
     }
 
 #if (NGX_HTTP_SSL && NGX_HTTP_V3)
-    if (lsopt.ssl && lsopt.http3) {
-        return "\"ssl\" parameter is incompatible with \"http3\"";
+    if (lsopt.ssl && lsopt.quic) {
+        return "\"ssl\" parameter is incompatible with \"quic\"";
     }
 #endif
 
diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -75,7 +75,7 @@ typedef struct {
     unsigned                   wildcard:1;
     unsigned                   ssl:1;
     unsigned                   http2:1;
-    unsigned                   http3:1;
+    unsigned                   quic:1;
 #if (NGX_HAVE_INET6)
     unsigned                   ipv6only:1;
 #endif
@@ -239,7 +239,7 @@ struct ngx_http_addr_conf_s {
 
     unsigned                   ssl:1;
     unsigned                   http2:1;
-    unsigned                   http3:1;
+    unsigned                   quic:1;
     unsigned                   proxy_protocol:1;
 };
 
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -325,7 +325,7 @@ ngx_http_init_connection(ngx_connection_
 #endif
 
 #if (NGX_HTTP_V3)
-    if (hc->addr_conf->http3) {
+    if (hc->addr_conf->quic) {
         ngx_http_v3_init_stream(c);
         return;
     }
diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c
--- a/src/http/v3/ngx_http_v3.c
+++ b/src/http/v3/ngx_http_v3.c
@@ -17,12 +17,9 @@ static void ngx_http_v3_cleanup_session(
 ngx_int_t
 ngx_http_v3_init_session(ngx_connection_t *c)
 {
-    ngx_pool_cleanup_t      *cln;
-    ngx_http_connection_t   *hc;
-    ngx_http_v3_session_t   *h3c;
-#if (NGX_HTTP_V3_HQ)
-    ngx_http_v3_srv_conf_t  *h3scf;
-#endif
+    ngx_pool_cleanup_t     *cln;
+    ngx_http_connection_t  *hc;
+    ngx_http_v3_session_t  *h3c;
 
     hc = c->data;
 
@@ -36,13 +33,6 @@ ngx_http_v3_init_session(ngx_connection_
     h3c->max_push_id = (uint64_t) -1;
     h3c->goaway_push_id = (uint64_t) -1;
 
-#if (NGX_HTTP_V3_HQ)
-    h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module);
-    if (h3scf->hq) {
-        h3c->hq = 1;
-    }
-#endif
-
     ngx_queue_init(&h3c->blocked);
     ngx_queue_init(&h3c->pushing);
 
diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h
--- a/src/http/v3/ngx_http_v3.h
+++ b/src/http/v3/ngx_http_v3.h
@@ -21,6 +21,7 @@
 
 #define NGX_HTTP_V3_ALPN_PROTO                     "\x02h3"
 #define NGX_HTTP_V3_HQ_ALPN_PROTO                  "\x0Ahq-interop"
+#define NGX_HTTP_V3_HQ_PROTO                       "hq-interop"
 
 #define NGX_HTTP_V3_VARLEN_INT_LEN                 4
 #define NGX_HTTP_V3_PREFIX_INT_LEN                 11
@@ -101,13 +102,12 @@
 
 
 typedef struct {
+    ngx_flag_t                    enable;
+    ngx_flag_t                    enable_hq;
     size_t                        max_table_capacity;
     ngx_uint_t                    max_blocked_streams;
     ngx_uint_t                    max_concurrent_pushes;
     ngx_uint_t                    max_concurrent_streams;
-#if (NGX_HTTP_V3_HQ)
-    ngx_flag_t                    hq;
-#endif
     ngx_quic_conf_t               quic;
 } ngx_http_v3_srv_conf_t;
 
@@ -147,9 +147,7 @@ struct ngx_http_v3_session_s {
     off_t                         payload_bytes;
 
     unsigned                      goaway:1;
-#if (NGX_HTTP_V3_HQ)
     unsigned                      hq:1;
-#endif
 
     ngx_connection_t             *known_streams[NGX_HTTP_V3_MAX_KNOWN_STREAM];
 };
diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c
--- a/src/http/v3/ngx_http_v3_module.c
+++ b/src/http/v3/ngx_http_v3_module.c
@@ -32,6 +32,20 @@ static ngx_conf_post_t  ngx_http_quic_mt
 
 static ngx_command_t  ngx_http_v3_commands[] = {
 
+    { ngx_string("http3"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_SRV_CONF_OFFSET,
+      offsetof(ngx_http_v3_srv_conf_t, enable),
+      NULL },
+
+    { ngx_string("http3_hq"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_SRV_CONF_OFFSET,
+      offsetof(ngx_http_v3_srv_conf_t, enable_hq),
+      NULL },
+
     { ngx_string("http3_max_concurrent_pushes"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_num_slot,
@@ -46,15 +60,6 @@ static ngx_command_t  ngx_http_v3_comman
       offsetof(ngx_http_v3_srv_conf_t, max_concurrent_streams),
       NULL },
 
-#if (NGX_HTTP_V3_HQ)
-    { ngx_string("http3_hq"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
-      ngx_conf_set_flag_slot,
-      NGX_HTTP_SRV_CONF_OFFSET,
-      offsetof(ngx_http_v3_srv_conf_t, hq),
-      NULL },
-#endif
-
     { ngx_string("http3_push"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_http_v3_push,
@@ -160,14 +165,12 @@ static ngx_int_t
 ngx_http_v3_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
-    if (r->connection->quic) {
-#if (NGX_HTTP_V3_HQ)
+    ngx_http_v3_session_t  *h3c;
 
-        ngx_http_v3_srv_conf_t  *h3scf;
+    if (r->connection->quic) {
+        h3c = ngx_http_v3_get_session(r->connection);
 
-        h3scf = ngx_http_get_module_srv_conf(r, ngx_http_v3_module);
-
-        if (h3scf->hq) {
+        if (h3c->hq) {
             v->len = sizeof("hq") - 1;
             v->valid = 1;
             v->no_cacheable = 0;
@@ -177,8 +180,6 @@ ngx_http_v3_variable(ngx_http_request_t 
             return NGX_OK;
         }
 
-#endif
-
         v->len = sizeof("h3") - 1;
         v->valid = 1;
         v->no_cacheable = 0;
@@ -232,12 +233,12 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *
      *     h3scf->quic.timeout = 0;
      *     h3scf->max_blocked_streams = 0;
      */
+
+    h3scf->enable = NGX_CONF_UNSET;
+    h3scf->enable_hq = NGX_CONF_UNSET;
     h3scf->max_table_capacity = NGX_HTTP_V3_MAX_TABLE_CAPACITY;
     h3scf->max_concurrent_pushes = NGX_CONF_UNSET_UINT;
     h3scf->max_concurrent_streams = NGX_CONF_UNSET_UINT;
-#if (NGX_HTTP_V3_HQ)
-    h3scf->hq = NGX_CONF_UNSET;
-#endif
 
     h3scf->quic.mtu = NGX_CONF_UNSET_SIZE;
     h3scf->quic.stream_buffer_size = NGX_CONF_UNSET_SIZE;
@@ -264,6 +265,10 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *c
 
     ngx_http_ssl_srv_conf_t  *sscf;
 
+    ngx_conf_merge_value(conf->enable, prev->enable, 0);
+
+    ngx_conf_merge_value(conf->enable_hq, prev->enable_hq, 0);
+
     ngx_conf_merge_uint_value(conf->max_concurrent_pushes,
                               prev->max_concurrent_pushes, 10);
 
@@ -272,11 +277,6 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *c
 
     conf->max_blocked_streams = conf->max_concurrent_streams;
 
-#if (NGX_HTTP_V3_HQ)
-    ngx_conf_merge_value(conf->hq, prev->hq, 0);
-#endif
-
-
     ngx_conf_merge_size_value(conf->quic.mtu, prev->quic.mtu,
                               NGX_QUIC_MAX_UDP_PAYLOAD_SIZE);
 
diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c
--- a/src/http/v3/ngx_http_v3_request.c
+++ b/src/http/v3/ngx_http_v3_request.c
@@ -110,7 +110,10 @@ ngx_http_v3_init_stream(ngx_connection_t
 ngx_int_t
 ngx_http_v3_init(ngx_connection_t *c)
 {
+    unsigned int               len;
+    const unsigned char       *data;
     ngx_http_v3_session_t     *h3c;
+    ngx_http_v3_srv_conf_t    *h3scf;
     ngx_http_core_loc_conf_t  *clcf;
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init");
@@ -119,11 +122,23 @@ ngx_http_v3_init(ngx_connection_t *c)
     clcf = ngx_http_v3_get_module_loc_conf(c, ngx_http_core_module);
     ngx_add_timer(&h3c->keepalive, clcf->keepalive_timeout);
 
-#if (NGX_HTTP_V3_HQ)
-    if (h3c->hq) {
-        return NGX_OK;
+    h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module);
+
+    if (h3scf->enable_hq) {
+        if (!h3scf->enable) {
+            h3c->hq = 1;
+            return NGX_OK;
+        }
+
+        SSL_get0_alpn_selected(c->ssl->connection, &data, &len);
+
+        if (len == sizeof(NGX_HTTP_V3_HQ_PROTO) - 1
+            && ngx_strncmp(data, NGX_HTTP_V3_HQ_PROTO, len) == 0)
+        {
+            h3c->hq = 1;
+            return NGX_OK;
+        }
     }
-#endif
 
     return ngx_http_v3_send_settings(c);
 }
@@ -147,10 +162,7 @@ ngx_http_v3_shutdown(ngx_connection_t *c
     if (!h3c->goaway) {
         h3c->goaway = 1;
 
-#if (NGX_HTTP_V3_HQ)
-        if (!h3c->hq)
-#endif
-        {
+        if (!h3c->hq) {
             (void) ngx_http_v3_send_goaway(c, h3c->next_request_id);
         }
 
@@ -205,10 +217,7 @@ ngx_http_v3_init_request_stream(ngx_conn
     {
         h3c->goaway = 1;
 
-#if (NGX_HTTP_V3_HQ)
-        if (!h3c->hq)
-#endif
-        {
+        if (!h3c->hq) {
             if (ngx_http_v3_send_goaway(c, h3c->next_request_id) != NGX_OK) {
                 ngx_http_close_connection(c);
                 return;
@@ -236,10 +245,7 @@ ngx_http_v3_init_request_stream(ngx_conn
 
     rev = c->read;
 
-#if (NGX_HTTP_V3_HQ)
-    if (!h3c->hq)
-#endif
-    {
+    if (!h3c->hq) {
         rev->handler = ngx_http_v3_wait_request_handler;
         c->write->handler = ngx_http_empty_handler;
     }
@@ -398,14 +404,14 @@ ngx_http_v3_wait_request_handler(ngx_eve
 void
 ngx_http_v3_reset_stream(ngx_connection_t *c)
 {
+    ngx_http_v3_session_t   *h3c;
     ngx_http_v3_srv_conf_t  *h3scf;
 
     h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module);
 
-    if (h3scf->max_table_capacity > 0 && !c->read->eof
-#if (NGX_HTTP_V3_HQ)
-        && !h3scf->hq
-#endif
+    h3c = ngx_http_v3_get_session(c);
+
+    if (h3scf->max_table_capacity > 0 && !c->read->eof && !h3c->hq
         && (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0)
     {
         (void) ngx_http_v3_send_cancel_stream(c, c->quic->id);
diff --git a/src/http/v3/ngx_http_v3_uni.c b/src/http/v3/ngx_http_v3_uni.c
--- a/src/http/v3/ngx_http_v3_uni.c
+++ b/src/http/v3/ngx_http_v3_uni.c
@@ -37,12 +37,9 @@ void
 ngx_http_v3_init_uni_stream(ngx_connection_t *c)
 {
     uint64_t                   n;
-#if (NGX_HTTP_V3_HQ)
     ngx_http_v3_session_t     *h3c;
-#endif
     ngx_http_v3_uni_stream_t  *us;
 
-#if (NGX_HTTP_V3_HQ)
     h3c = ngx_http_v3_get_session(c);
     if (h3c->hq) {
         ngx_http_v3_finalize_connection(c,
@@ -52,7 +49,6 @@ ngx_http_v3_init_uni_stream(ngx_connecti
         ngx_http_v3_close_uni_stream(c);
         return;
     }
-#endif
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init uni stream");
 


More information about the nginx-devel mailing list