ngtcp2: share common functionality

Share common functions/structs between ngtcp2 HTTP/3 and the proxy
version.

Fix bugs in proxy implementation when it comes to stream and pollset
handling and transfer lifetimes.

Curl_multi_xfer_sockbuf_borrow: work without multi

When a connection gets shutdown by a share, the easy handle used is
share->admin and it does not have a multi handle. In that case let
Curl_multi_xfer_sockbuf_borrow() allocate a buffer to be freed on
release.

This happens when a TLS filter sends its last notify through a HTTP/3
proxy tunnel.

Closes #21871
This commit is contained in:
Stefan Eissing 2026-06-05 12:55:50 +02:00 committed by Daniel Stenberg
parent 4fcf9c8f59
commit f924489b25
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
43 changed files with 3254 additions and 4970 deletions

View file

@ -214,3 +214,16 @@ class TestEyeballs:
r.check_exit_code(0)
r.check_response(count=1, http_status=200)
assert r.stats[0]['http_version'] == '2'
# h3 download using --connect-to IPv6 address
@pytest.mark.skipif(condition=not Env.have_h3(), reason="missing HTTP/3 support")
@pytest.mark.skipif(condition=not Env.curl_has_feature('IPv6'), reason="no IPv6")
def test_06_25_h3_connect_to(self, env: Env, httpd, nghttpx):
curl = CurlClient(env=env, force_resolv=False)
urln = f'https://{env.authority_for(env.domain1, "h3")}/data.json'
r = curl.http_download(urls=[urln], extra_args=[
'--http3-only', '--connect-to',
f'{env.authority_for(env.domain1, "h3")}:[::1]:{env.https_port}'
])
r.check_response(count=1, http_status=200)
assert r.stats[0]['http_version'] == '3'

View file

@ -113,8 +113,7 @@ static int test_idx;
struct cf_test_ctx {
int idx;
int ai_family;
uint8_t transport_in;
uint8_t transport_out;
uint8_t transport_peer;
char id[16];
struct curltime started;
timediff_t fail_delay_ms;
@ -166,10 +165,13 @@ static CURLcode cf_test_adjust_pollset(struct Curl_cfilter *cf,
static CURLcode cf_test_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
uint8_t transport_peer,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport_in,
uint8_t transport_out)
struct Curl_peer *tunnel_peer,
uint8_t transport_above)
{
static const struct Curl_cftype cft_test = {
"TEST",
@ -194,7 +196,11 @@ static CURLcode cf_test_create(struct Curl_cfilter **pcf,
CURLcode result;
(void)data;
(void)origin;
(void)peer;
(void)conn;
(void)tunnel_peer;
(void)transport_above;
ctx = curlx_calloc(1, sizeof(*ctx));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
@ -202,8 +208,7 @@ static CURLcode cf_test_create(struct Curl_cfilter **pcf,
}
ctx->idx = test_idx++;
ctx->ai_family = addr->family;
ctx->transport_in = transport_in;
ctx->transport_out = transport_out;
ctx->transport_peer = transport_peer;
ctx->started = curlx_now();
current_tr->ongoing++;
if(current_tr->ongoing > current_tr->max_concurrent)

View file

@ -43,7 +43,7 @@ static CURLcode test_unit3304(const char *arg)
UNITTEST_BEGIN_SIMPLE
#ifdef USE_SSL
struct Curl_peer dest;
struct Curl_peer origin;
struct ssl_peer peer;
struct ssl_primary_config ssl;
char *key1 = NULL;
@ -60,12 +60,12 @@ static CURLcode test_unit3304(const char *arg)
static char lc_ctype[] = "pem";
static char lc_ktype[] = "pem";
memset(&dest, 0, sizeof(dest));
dest.hostname = base_hostname;
dest.port = 443;
memset(&origin, 0, sizeof(origin));
origin.hostname = base_hostname;
origin.port = 443;
memset(&peer, 0, sizeof(peer));
peer.dest = &dest;
peer.origin = &origin;
peer.transport = TRNSPRT_TCP;
memset(&ssl, 0, sizeof(ssl));
@ -78,9 +78,9 @@ static CURLcode test_unit3304(const char *arg)
ssl.key_type = base_ktype;
/* Baseline: same config produces same key. */
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1),
"peer key build failed");
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2),
"peer key build failed");
fail_unless(key1 && key2 && !strcmp(key1, key2),
"identical config should produce identical peer key");
@ -89,10 +89,10 @@ static CURLcode test_unit3304(const char *arg)
/* key_passwd is NOT in the peer key: lookup uses timing-safe comparison
* via cf_ssl_scache_match_auth(), same as SRP credentials. */
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1),
"peer key build failed");
ssl.key_passwd = NULL;
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2),
"peer key build failed");
fail_unless(key1 && key2 && !strcmp(key1, key2),
"key_passwd must not affect the peer key");
@ -101,10 +101,10 @@ static CURLcode test_unit3304(const char *arg)
ssl.key_passwd = base_passwd;
/* Different key path must produce a different peer key. */
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1),
"peer key build failed");
ssl.key = alt_key;
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2),
"peer key build failed");
fail_unless(key1 && key2 && strcmp(key1, key2),
"different key must produce different peer key");
@ -113,10 +113,10 @@ static CURLcode test_unit3304(const char *arg)
ssl.key = base_key;
/* Different key_type must produce a different peer key. */
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1),
"peer key build failed");
ssl.key_type = alt_ktype;
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2),
"peer key build failed");
fail_unless(key1 && key2 && strcmp(key1, key2),
"different key_type must produce different peer key");
@ -125,10 +125,10 @@ static CURLcode test_unit3304(const char *arg)
ssl.key_type = base_ktype;
/* Different cert_type must produce a different peer key. */
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1),
"peer key build failed");
ssl.cert_type = alt_ctype;
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2),
"peer key build failed");
fail_unless(key1 && key2 && strcmp(key1, key2),
"different cert_type must produce different peer key");
@ -138,10 +138,10 @@ static CURLcode test_unit3304(const char *arg)
/* cert_type is case-insensitive: "PEM" and "pem" must produce the
* same peer key, consistent with the conn-reuse comparison. */
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1),
"peer key build failed");
ssl.cert_type = lc_ctype;
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2),
"peer key build failed");
fail_unless(key1 && key2 && !strcmp(key1, key2),
"cert_type case must not affect peer key");
@ -151,10 +151,10 @@ static CURLcode test_unit3304(const char *arg)
/* key_type is case-insensitive: "PEM" and "pem" must produce the
* same peer key. */
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1),
"peer key build failed");
ssl.key_type = lc_ktype;
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2),
"peer key build failed");
fail_unless(key1 && key2 && !strcmp(key1, key2),
"key_type case must not affect peer key");