vtls: set seen http version on successful ALPN

When a HTTP version has been negotiated via ALPN, set the member
`conn->httpversion_seen` accordingly. This allows pending transfers to
reuse multiplexed http connections before the response to the first
transfer has arrived.

Fixes #18177
Reported-by: IoannisGS on github
Closes #18181
This commit is contained in:
Stefan Eissing 2025-08-05 13:37:12 +02:00 committed by Daniel Stenberg
parent 952117c032
commit 41fe621ae1
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
6 changed files with 52 additions and 14 deletions

View File

@ -2704,6 +2704,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
if(result)
goto fail;
info_version = "HTTP/2";
/* There is no ALPN here, but the connection is now definitely h2 */
conn->httpversion_seen = 20;
}
else
info_version = "HTTP/1.x";

View File

@ -768,7 +768,7 @@ struct connectdata {
and happy eyeballing. Use `Curl_conn_get_transport() for actual value
once the connection is set up. */
unsigned char ip_version; /* copied from the Curl_easy at creation time */
/* HTTP version last responded with by the server.
/* HTTP version last responded with by the server or negotiated via ALPN.
* 0 at start, then one of 09, 10, 11, etc. */
unsigned char httpversion_seen;
unsigned char connect_only;

View File

@ -1955,9 +1955,9 @@ static CURLcode h3_data_pause(struct Curl_cfilter *cf,
return CURLE_OK;
}
static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf,
struct Curl_easy *data,
int event, int arg1, void *arg2)
static CURLcode cf_ngtcp2_cntrl(struct Curl_cfilter *cf,
struct Curl_easy *data,
int event, int arg1, void *arg2)
{
struct cf_ngtcp2_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
@ -1995,6 +1995,10 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf,
}
break;
}
case CF_CTRL_CONN_INFO_UPDATE:
if(!cf->sockindex && cf->connected)
cf->conn->httpversion_seen = 30;
break;
default:
break;
}
@ -2733,7 +2737,7 @@ struct Curl_cftype Curl_cft_http3 = {
Curl_cf_def_data_pending,
cf_ngtcp2_send,
cf_ngtcp2_recv,
cf_ngtcp2_data_event,
cf_ngtcp2_cntrl,
cf_ngtcp2_conn_is_alive,
Curl_cf_def_conn_keep_alive,
cf_ngtcp2_query,

View File

@ -2168,9 +2168,9 @@ static bool cf_osslq_data_pending(struct Curl_cfilter *cf,
return stream && !Curl_bufq_is_empty(&stream->recvbuf);
}
static CURLcode cf_osslq_data_event(struct Curl_cfilter *cf,
struct Curl_easy *data,
int event, int arg1, void *arg2)
static CURLcode cf_osslq_cntrl(struct Curl_cfilter *cf,
struct Curl_easy *data,
int event, int arg1, void *arg2)
{
struct cf_osslq_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
@ -2206,6 +2206,10 @@ static CURLcode cf_osslq_data_event(struct Curl_cfilter *cf,
}
break;
}
case CF_CTRL_CONN_INFO_UPDATE:
if(!cf->sockindex && cf->connected)
cf->conn->httpversion_seen = 30;
break;
default:
break;
}
@ -2384,7 +2388,7 @@ struct Curl_cftype Curl_cft_http3 = {
cf_osslq_data_pending,
cf_osslq_send,
cf_osslq_recv,
cf_osslq_data_event,
cf_osslq_cntrl,
cf_osslq_conn_is_alive,
Curl_cf_def_conn_keep_alive,
cf_osslq_query,

View File

@ -1197,9 +1197,9 @@ static CURLcode h3_data_pause(struct Curl_cfilter *cf,
return CURLE_OK;
}
static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf,
struct Curl_easy *data,
int event, int arg1, void *arg2)
static CURLcode cf_quiche_cntrl(struct Curl_cfilter *cf,
struct Curl_easy *data,
int event, int arg1, void *arg2)
{
struct cf_quiche_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
@ -1238,6 +1238,10 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf,
}
break;
}
case CF_CTRL_CONN_INFO_UPDATE:
if(!cf->sockindex && cf->connected)
cf->conn->httpversion_seen = 30;
break;
default:
break;
}
@ -1621,7 +1625,7 @@ struct Curl_cftype Curl_cft_http3 = {
cf_quiche_data_pending,
cf_quiche_send,
cf_quiche_recv,
cf_quiche_data_event,
cf_quiche_cntrl,
cf_quiche_conn_is_alive,
Curl_cf_def_conn_keep_alive,
cf_quiche_query,

View File

@ -1605,6 +1605,30 @@ static CURLcode ssl_cf_query(struct Curl_cfilter *cf,
CURLE_UNKNOWN_OPTION;
}
static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf,
struct Curl_easy *data,
int event, int arg1, void *arg2)
{
struct ssl_connect_data *connssl = cf->ctx;
(void)arg1;
(void)arg2;
(void)data;
switch(event) {
case CF_CTRL_CONN_INFO_UPDATE:
if(connssl->negotiated.alpn && !cf->sockindex) {
if(!strcmp("http/1.1", connssl->negotiated.alpn))
cf->conn->httpversion_seen = 11;
else if(!strcmp("h2", connssl->negotiated.alpn))
cf->conn->httpversion_seen = 20;
else if(!strcmp("h3", connssl->negotiated.alpn))
cf->conn->httpversion_seen = 30;
}
break;
}
return CURLE_OK;
}
static bool cf_ssl_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data,
bool *input_pending)
{
@ -1628,7 +1652,7 @@ struct Curl_cftype Curl_cft_ssl = {
ssl_cf_data_pending,
ssl_cf_send,
ssl_cf_recv,
Curl_cf_def_cntrl,
ssl_cf_cntrl,
cf_ssl_is_alive,
Curl_cf_def_conn_keep_alive,
ssl_cf_query,