From: Stefan Eissing Date: Tue, 5 Aug 2025 11:37:12 +0000 (+0200) Subject: vtls: set seen http version on successful ALPN X-Git-Tag: curl-8_16_0~241 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=41fe621ae13616893512df1226025c60f3b148b9;p=thirdparty%2Fcurl.git 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 --- diff --git a/lib/http.c b/lib/http.c index d41aefdfaa..8eb1378851 100644 --- a/lib/http.c +++ b/lib/http.c @@ -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"; diff --git a/lib/urldata.h b/lib/urldata.h index 438f11218c..b9cc25b9b0 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -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; diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index 1cb99aaea8..6467025137 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -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, diff --git a/lib/vquic/curl_osslq.c b/lib/vquic/curl_osslq.c index c18801d811..fa2c0a2899 100644 --- a/lib/vquic/curl_osslq.c +++ b/lib/vquic/curl_osslq.c @@ -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, diff --git a/lib/vquic/curl_quiche.c b/lib/vquic/curl_quiche.c index b981f4d780..f225667811 100644 --- a/lib/vquic/curl_quiche.c +++ b/lib/vquic/curl_quiche.c @@ -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, diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 96d64dd606..f17f9142be 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -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,