HTTP/3: add proxy CONNECT and MASQUE CONNECT-UDP support (ngtcp2 QUIC)

This patch adds two major proxy capabilities to curl (ngtcp2 QUIC):
- HTTP/3 Proxy CONNECT: Tunnel HTTP/1.1 or HTTP/2 traffic through an
  HTTPS proxy that speaks HTTP/3 (QUIC) using the standard CONNECT
  method over an HTTP/3 connection.
- MASQUE CONNECT-UDP: Tunnel HTTP/3 (QUIC) traffic through an HTTP
  proxy (speaking HTTP/1.1, HTTP/2, or HTTP/3) using the extended
  CONNECT method with the CONNECT-UDP protocol (RFC9297 & RFC9298).

Public API additions:
- `CURLPROXY_HTTPS3`: new proxy type constant for HTTP/3 proxy
- `--proxy-http3`: new CLI flag to negotiate HTTP/3 with HTTPS proxy

The implementation adds two new filters:
- `H3-PROXY` - enables negotiating HTTP/3 (QUIC) to the proxy and
  running CONNECT/CONNECT-UDP through that proxy transport.
- `CAPSULE` - dedicated filter inserted between QUIC transport and
  HTTP-PROXY to handle datagram capsule encapsulation/decapsulation.

Here is how the curl filter chaining looks in different scenarios:
- HTTP/3 Proxy CONNECT (tunneling TCP protocols over QUIC proxy):
  conn -> HTTP/1.1 or HTTP/2  -> SSL -> HTTP-PROXY ->
                                 H3-PROXY -> HAPPY-EYEBALLS -> UDP
- MASQUE CONNECT-UDP (tunneling QUIC over any proxy):
  conn -> HTTP/3 -> CAPSULE -> HTTP-PROXY -> H3-PROXY ->
                               HAPPY-EYEBALLS -> UDP
  conn -> HTTP/3 -> CAPSULE -> HTTP-PROXY -> H1-PROXY or H2-PROXY ->
                               SSL -> HAPPY-EYEBALLS -> TCP

- Both features currently require the ngtcp2 QUIC backend.
- Both features are experimental (disabled by default). Enable with
  `--enable-proxy-http3`(autotools) or `-DUSE_PROXY_HTTP3=ON`(CMake).

Tests:
- tests/unit/unit3400.c: Unit tests for capsule protocol encode/decode
- tests/http/test_60_h3_proxy.py: Comprehensive pytest integration suite
- tests/http/testenv/h2o.py: Managing h2o instances with HTTP/1.1, HTTP/2,
  and HTTP/3 (QUIC) listeners, proxy.connect and proxy.connect-udp enabled.

References:
  RFC 9297 - HTTP Datagrams and the Capsule Protocol
  RFC 9298 - Proxying UDP in HTTP
  RFC 9000 §16 — Variable-Length Integer Encoding

Signed-off-by: Aritra Basu <aritrbas+gh@cisco.com>

Closes #21153
This commit is contained in:
Aritra Basu 2026-04-27 19:35:38 -04:00 committed by Daniel Stenberg
parent efc3f2309e
commit e78b1b3ecc
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
66 changed files with 7401 additions and 473 deletions

View file

@ -250,6 +250,7 @@ static const struct LongShort aliases[]= {
{"proxy-digest", ARG_BOOL, ' ', C_PROXY_DIGEST},
{"proxy-header", ARG_STRG, ' ', C_PROXY_HEADER},
{"proxy-http2", ARG_BOOL, ' ', C_PROXY_HTTP2},
{"proxy-http3", ARG_BOOL, ' ', C_PROXY_HTTP3},
{"proxy-insecure", ARG_BOOL, ' ', C_PROXY_INSECURE},
{"proxy-key", ARG_FILE|ARG_TLS, ' ', C_PROXY_KEY},
{"proxy-key-type", ARG_STRG|ARG_TLS, ' ', C_PROXY_KEY_TYPE},
@ -2024,6 +2025,18 @@ static ParameterError opt_bool(struct OperationConfig *config,
config->proxyver = toggle ? CURLPROXY_HTTPS2 : CURLPROXY_HTTPS;
break;
case C_PROXY_HTTP3: /* --proxy-http3 */
#ifndef USE_PROXY_HTTP3
if(toggle)
return PARAM_LIBCURL_DOESNT_SUPPORT;
config->proxyver = CURLPROXY_HTTPS;
#else
if(!feature_httpsproxy || !feature_http3)
return PARAM_LIBCURL_DOESNT_SUPPORT;
config->proxyver = toggle ? CURLPROXY_HTTPS3 : CURLPROXY_HTTPS;
#endif
break;
case C_APPEND: /* --append */
config->ftp_append = toggle;
break;
@ -2895,7 +2908,8 @@ static ParameterError opt_string(struct OperationConfig *config,
case C_PROXY: /* --proxy */
/* --proxy */
err = getstr(&config->proxy, nextarg, ALLOW_BLANK);
if(config->proxyver != CURLPROXY_HTTPS2)
if(config->proxyver != CURLPROXY_HTTPS2 &&
config->proxyver != CURLPROXY_HTTPS3)
config->proxyver = CURLPROXY_HTTP;
break;
case C_REQUEST: /* --request */

View file

@ -201,6 +201,7 @@ typedef enum {
C_PROXY_DIGEST,
C_PROXY_HEADER,
C_PROXY_HTTP2,
C_PROXY_HTTP3,
C_PROXY_INSECURE,
C_PROXY_KEY,
C_PROXY_KEY_TYPE,

View file

@ -542,6 +542,9 @@ const struct helptxt helptext[] = {
{ " --proxy-http2",
"Use HTTP/2 with HTTPS proxy",
CURLHELP_HTTP | CURLHELP_PROXY },
{ " --proxy-http3",
"Use HTTP/3 with HTTPS proxy",
CURLHELP_HTTP | CURLHELP_PROXY },
{ " --proxy-insecure",
"Skip HTTPS proxy cert verification",
CURLHELP_PROXY | CURLHELP_TLS },