From: Stefan Eissing Date: Wed, 22 Jan 2025 13:45:30 +0000 (+0100) Subject: lib: clarify 'conn->httpversion' X-Git-Tag: curl-8_12_0~64 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e83818cae1da495939aee5def1172ca1d20cc1e4;p=thirdparty%2Fcurl.git lib: clarify 'conn->httpversion' The variable `conn->httpversion` was used for several purposes and it was unclear at which time the value represents what. - rename `conn->httpversion` to `conn->httpversion_seen` This makes clear that the variable only records the last HTTP version seen on the connection - if any. And that it no longer is an indication of what version to use. - Change Alt-Svc handling to no longer modify `conn->httpversion` but set `data->state.httpwant` for influencing the HTTP version to use on a transfer. - Add `data->req.httpversion_sent` to have a record of what HTTP version was sent in a request - Add connection filter type CF_TYPE_HTTP - Add filter query `CF_QUERY_HTTP_VERSION` to ask what HTTP filter version is in place - Lookup filters HTTP version instead of using `conn->httpversion` Test test_12_05 now switches to HTTP/1.1 correctly and the expectations have been fixed. Removed the connection fitler "is_httpN()" checks and using the version query instead. Closes #16073 --- diff --git a/lib/cfilters.c b/lib/cfilters.c index 3aecaff543..8b05356151 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -494,13 +494,35 @@ bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex) for(; cf; cf = cf->next) { if(cf->cft->flags & CF_TYPE_MULTIPLEX) return TRUE; - if(cf->cft->flags & CF_TYPE_IP_CONNECT - || cf->cft->flags & CF_TYPE_SSL) + if(cf->cft->flags & (CF_TYPE_IP_CONNECT|CF_TYPE_SSL)) return FALSE; } return FALSE; } +unsigned char Curl_conn_http_version(struct Curl_easy *data) +{ + struct Curl_cfilter *cf; + CURLcode result = CURLE_UNKNOWN_OPTION; + unsigned char v = 0; + + cf = data->conn ? data->conn->cfilter[FIRSTSOCKET] : NULL; + for(; cf; cf = cf->next) { + if(cf->cft->flags & CF_TYPE_HTTP) { + int value = 0; + result = cf->cft->query(cf, data, CF_QUERY_HTTP_VERSION, &value, NULL); + if(!result && ((value < 0) || (value > 255))) + result = CURLE_FAILED_INIT; + else + v = (unsigned char)value; + break; + } + if(cf->cft->flags & (CF_TYPE_IP_CONNECT|CF_TYPE_SSL)) + break; + } + return result ? 0 : v; +} + bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex) { struct Curl_cfilter *cf; diff --git a/lib/cfilters.h b/lib/cfilters.h index f6d8221a1c..2d5599a90a 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -176,6 +176,7 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf, #define CF_QUERY_STREAM_ERROR 6 /* error code - */ #define CF_QUERY_NEED_FLUSH 7 /* TRUE/FALSE - */ #define CF_QUERY_IP_INFO 8 /* TRUE/FALSE struct ip_quadruple */ +#define CF_QUERY_HTTP_VERSION 9 /* number (10/11/20/30) - */ /** * Query the cfilter for properties. Filters ignorant of a query will @@ -195,11 +196,13 @@ typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf, * CF_TYPE_SSL: provide SSL/TLS * CF_TYPE_MULTIPLEX: provides multiplexing of easy handles * CF_TYPE_PROXY provides proxying + * CF_TYPE_HTTP implement a version of the HTTP protocol */ #define CF_TYPE_IP_CONNECT (1 << 0) #define CF_TYPE_SSL (1 << 1) #define CF_TYPE_MULTIPLEX (1 << 2) #define CF_TYPE_PROXY (1 << 3) +#define CF_TYPE_HTTP (1 << 4) /* A connection filter type, e.g. specific implementation. */ struct Curl_cftype { @@ -392,6 +395,12 @@ bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex); */ bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex); +/** + * Return the HTTP version used on the FIRSTSOCKET connection filters + * or 0 if unknown. Value otherwise is 09, 10, 11, etc. + */ +unsigned char Curl_conn_http_version(struct Curl_easy *data); + /** * Close the filter chain at `sockindex` for connection `data->conn`. * Filters remain in place and may be connected again afterwards. diff --git a/lib/http.c b/lib/http.c index f3bacd290a..cef73a5781 100644 --- a/lib/http.c +++ b/lib/http.c @@ -108,9 +108,10 @@ static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn); static CURLcode http_range(struct Curl_easy *data, Curl_HttpReq httpreq); static CURLcode http_req_complete(struct Curl_easy *data, - struct dynbuf *r, Curl_HttpReq httpreq); + struct dynbuf *r, int httpversion, + Curl_HttpReq httpreq); static CURLcode http_req_set_reader(struct Curl_easy *data, - Curl_HttpReq httpreq, + Curl_HttpReq httpreq, int httpversion, const char **tep); static CURLcode http_size(struct Curl_easy *data); static CURLcode http_statusline(struct Curl_easy *data, @@ -121,8 +122,6 @@ static CURLcode http_useragent(struct Curl_easy *data); #ifdef HAVE_LIBZ static CURLcode http_transferencode(struct Curl_easy *data); #endif -static bool use_http_1_1plus(const struct Curl_easy *data, - const struct connectdata *conn); /* @@ -533,7 +532,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) else data->info.httpauthpicked = data->state.authhost.picked; if(data->state.authhost.picked == CURLAUTH_NTLM && - conn->httpversion > 11) { + (data->req.httpversion_sent > 11)) { infof(data, "Forcing HTTP/1.1 for NTLM"); connclose(conn, "Force HTTP/1.1 connection"); data->state.httpwant = CURL_HTTP_VERSION_1_1; @@ -1217,45 +1216,55 @@ CURLcode Curl_http_done(struct Curl_easy *data, return CURLE_OK; } -/* - * Determine if we should use HTTP 1.1 (OR BETTER) for this request. Reasons - * to avoid it include: - * - * - if the user specifically requested HTTP 1.0 - * - if the server we are connected to only supports 1.0 - * - if any server previously contacted to handle this request only supports - * 1.0. - */ -static bool use_http_1_1plus(const struct Curl_easy *data, - const struct connectdata *conn) +/* Determine if we may use HTTP 1.1 for this request. */ +static bool http_may_use_1_1(const struct Curl_easy *data) { - if((data->state.httpversion == 10) || (conn->httpversion == 10)) + const struct connectdata *conn = data->conn; + /* We have seen a previous response for *this* transfer with 1.0, + * on another connection or the same one. */ + if(data->state.httpversion == 10) + return FALSE; + /* We have seen a previous response on *this* connection with 1.0. */ + if(conn->httpversion_seen == 10) return FALSE; + /* We want 1.0 and have seen no previous response on *this* connection + with a higher version (maybe no response at all yet). */ if((data->state.httpwant == CURL_HTTP_VERSION_1_0) && - (conn->httpversion <= 10)) + (conn->httpversion_seen <= 10)) return FALSE; + /* We want something newer than 1.0 or have no preferences. */ return (data->state.httpwant == CURL_HTTP_VERSION_NONE) || (data->state.httpwant >= CURL_HTTP_VERSION_1_1); } -static const char *get_http_string(const struct Curl_easy *data, - const struct connectdata *conn) +static unsigned char http_request_version(struct Curl_easy *data) +{ + unsigned char httpversion = Curl_conn_http_version(data); + if(!httpversion) { + /* No specific HTTP connection filter installed. */ + httpversion = http_may_use_1_1(data) ? 11 : 10; + } + return httpversion; +} + +static const char *get_http_string(int httpversion) { - if(Curl_conn_is_http3(data, conn, FIRSTSOCKET)) - return "3"; - if(Curl_conn_is_http2(data, conn, FIRSTSOCKET)) - return "2"; - if(use_http_1_1plus(data, conn)) - return "1.1"; - - return "1.0"; + switch(httpversion) { + case 30: + return "3"; + case 20: + return "2"; + case 11: + return "1.1"; + default: + return "1.0"; + } } CURLcode Curl_add_custom_headers(struct Curl_easy *data, - bool is_connect, + bool is_connect, int httpversion, struct dynbuf *req) { - struct connectdata *conn = data->conn; char *ptr; struct curl_slist *h[2]; struct curl_slist *headers; @@ -1268,7 +1277,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, if(is_connect) proxy = HEADER_CONNECT; else - proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy ? + proxy = data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy ? HEADER_PROXY : HEADER_SERVER; switch(proxy) { @@ -1368,7 +1377,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, Connection: */ checkprefix("Connection:", compare)) ; - else if((conn->httpversion >= 20) && + else if((httpversion >= 20) && checkprefix("Transfer-Encoding:", compare)) /* HTTP/2 does not support chunked requests */ ; @@ -1900,7 +1909,7 @@ static CURLcode http_resume(struct Curl_easy *data, Curl_HttpReq httpreq) } static CURLcode http_req_set_reader(struct Curl_easy *data, - Curl_HttpReq httpreq, + Curl_HttpReq httpreq, int httpversion, const char **tep) { CURLcode result = CURLE_OK; @@ -1920,12 +1929,10 @@ static CURLcode http_req_set_reader(struct Curl_easy *data, data->req.upload_chunky = Curl_compareheader(ptr, STRCONST("Transfer-Encoding:"), STRCONST("chunked")); - if(data->req.upload_chunky && - use_http_1_1plus(data, data->conn) && - (data->conn->httpversion >= 20)) { - infof(data, "suppressing chunked transfer encoding on connection " - "using HTTP version 2 or higher"); - data->req.upload_chunky = FALSE; + if(data->req.upload_chunky && (httpversion >= 20)) { + infof(data, "suppressing chunked transfer encoding on connection " + "using HTTP version 2 or higher"); + data->req.upload_chunky = FALSE; } } else { @@ -1933,10 +1940,10 @@ static CURLcode http_req_set_reader(struct Curl_easy *data, if(req_clen < 0) { /* indeterminate request content length */ - if(use_http_1_1plus(data, data->conn)) { + if(httpversion > 10) { /* On HTTP/1.1, enable chunked, on HTTP/2 and later we do not * need it */ - data->req.upload_chunky = (data->conn->httpversion < 20); + data->req.upload_chunky = (httpversion < 20); } else { failf(data, "Chunky upload is not supported by HTTP 1.0"); @@ -1955,7 +1962,7 @@ static CURLcode http_req_set_reader(struct Curl_easy *data, } static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r, - bool *announced_exp100) + int httpversion, bool *announced_exp100) { CURLcode result; char *ptr; @@ -1974,9 +1981,7 @@ static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r, *announced_exp100 = Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue")); } - else if(!data->state.disableexpect && - use_http_1_1plus(data, data->conn) && - (data->conn->httpversion < 20)) { + else if(!data->state.disableexpect && (httpversion == 11)) { /* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an Expect: 100-continue to the headers which actually speeds up post operations (as there is one packet coming back from the web server) */ @@ -1992,7 +1997,8 @@ static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r, } static CURLcode http_req_complete(struct Curl_easy *data, - struct dynbuf *r, Curl_HttpReq httpreq) + struct dynbuf *r, int httpversion, + Curl_HttpReq httpreq) { CURLcode result = CURLE_OK; curl_off_t req_clen; @@ -2052,7 +2058,7 @@ static CURLcode http_req_complete(struct Curl_easy *data, goto out; } } - result = addexpect(data, r, &announced_exp100); + result = addexpect(data, r, httpversion, &announced_exp100); if(result) goto out; break; @@ -2326,6 +2332,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) struct dynbuf req; char *altused = NULL; const char *p_accept; /* Accept: string */ + unsigned char httpversion; /* Always consider the DO phase done after this function call, even if there may be parts of the request that are not yet sent, since we can deal with @@ -2334,29 +2341,29 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) switch(conn->alpn) { case CURL_HTTP_VERSION_3: - DEBUGASSERT(Curl_conn_is_http3(data, conn, FIRSTSOCKET)); + DEBUGASSERT(Curl_conn_http_version(data) == 30); break; case CURL_HTTP_VERSION_2: #ifndef CURL_DISABLE_PROXY - if(!Curl_conn_is_http2(data, conn, FIRSTSOCKET) && + if((Curl_conn_http_version(data) != 20) && conn->bits.proxy && !conn->bits.tunnel_proxy ) { - result = Curl_http2_switch(data, conn, FIRSTSOCKET); + result = Curl_http2_switch(data); if(result) goto fail; } else #endif - DEBUGASSERT(Curl_conn_is_http2(data, conn, FIRSTSOCKET)); + DEBUGASSERT(Curl_conn_http_version(data) == 20); break; case CURL_HTTP_VERSION_1_1: /* continue with HTTP/1.x when explicitly requested */ break; default: /* Check if user wants to use HTTP/2 with clear TCP */ - if(Curl_http2_may_switch(data, conn, FIRSTSOCKET)) { + if(Curl_http2_may_switch(data)) { DEBUGF(infof(data, "HTTP/2 over clean TCP")); - result = Curl_http2_switch(data, conn, FIRSTSOCKET); + result = Curl_http2_switch(data); if(result) goto fail; } @@ -2420,7 +2427,10 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) goto fail; #endif - result = http_req_set_reader(data, httpreq, &te); + httpversion = http_request_version(data); + httpstring = get_http_string(httpversion); + + result = http_req_set_reader(data, httpreq, httpversion, &te); if(result) goto fail; @@ -2431,8 +2441,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(result) goto fail; - httpstring = get_http_string(data, conn); - /* initialize a dynamic send-buffer */ Curl_dyn_init(&req, DYN_HTTP_REQUEST); @@ -2526,8 +2534,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) goto fail; } - if(!Curl_conn_is_ssl(conn, FIRSTSOCKET) && - conn->httpversion < 20 && + if(!Curl_conn_is_ssl(conn, FIRSTSOCKET) && (httpversion < 20) && (data->state.httpwant == CURL_HTTP_VERSION_2)) { /* append HTTP2 upgrade magic stuff to the HTTP request if it is not done over SSL */ @@ -2546,19 +2553,19 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(!result) result = Curl_add_timecondition(data, &req); if(!result) - result = Curl_add_custom_headers(data, FALSE, &req); + result = Curl_add_custom_headers(data, FALSE, httpversion, &req); if(!result) { /* req_send takes ownership of the 'req' memory on success */ - result = http_req_complete(data, &req, httpreq); + result = http_req_complete(data, &req, httpversion, httpreq); if(!result) - result = Curl_req_send(data, &req); + result = Curl_req_send(data, &req, httpversion); } Curl_dyn_free(&req); if(result) goto fail; - if((conn->httpversion >= 20) && data->req.upload_chunky) + if((httpversion >= 20) && data->req.upload_chunky) /* upload_chunky was set above to set up the request in a chunky fashion, but is disabled here again to avoid that the chunked encoded version is actually used when sending the request body over h2 */ @@ -2680,8 +2687,8 @@ static CURLcode http_header(struct Curl_easy *data, )) ? HD_VAL(hd, hdlen, "Alt-Svc:") : NULL; if(v) { /* the ALPN of the current request */ - enum alpnid id = (conn->httpversion == 30) ? ALPN_h3 : - (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1; + enum alpnid id = (k->httpversion == 30) ? ALPN_h3 : + (k->httpversion == 20) ? ALPN_h2 : ALPN_h1; return Curl_altsvc_parse(data, data->asi, v, id, conn->host.name, curlx_uitous((unsigned int)conn->remote_port)); } @@ -2753,7 +2760,7 @@ static CURLcode http_header(struct Curl_easy *data, streamclose(conn, "Connection: close used"); return CURLE_OK; } - if((conn->httpversion == 10) && + if((k->httpversion == 10) && HD_IS_AND_SAYS(hd, hdlen, "Connection:", "keep-alive")) { /* * An HTTP/1.0 reply with the 'Connection: keep-alive' line @@ -2843,7 +2850,7 @@ static CURLcode http_header(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY v = HD_VAL(hd, hdlen, "Proxy-Connection:"); if(v) { - if((conn->httpversion == 10) && conn->bits.httpproxy && + if((k->httpversion == 10) && conn->bits.httpproxy && HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "keep-alive")) { /* * When an HTTP/1.0 reply comes when using a proxy, the @@ -2854,7 +2861,7 @@ static CURLcode http_header(struct Curl_easy *data, connkeep(conn, "Proxy-Connection keep-alive"); /* do not close */ infof(data, "HTTP/1.0 proxy connection set to keep alive"); } - else if((conn->httpversion == 11) && conn->bits.httpproxy && + else if((k->httpversion == 11) && conn->bits.httpproxy && HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) { /* * We get an HTTP/1.1 response from a proxy and it says it will @@ -3043,11 +3050,11 @@ static CURLcode http_statusline(struct Curl_easy *data, case 30: #endif /* no major version switch mid-connection */ - if(conn->httpversion && - (k->httpversion/10 != conn->httpversion/10)) { + if(k->httpversion_sent && + (k->httpversion/10 != k->httpversion_sent/10)) { failf(data, "Version mismatch (from HTTP/%u to HTTP/%u)", - conn->httpversion/10, k->httpversion/10); - return CURLE_UNSUPPORTED_PROTOCOL; + k->httpversion_sent/10, k->httpversion/10); + return CURLE_WEIRD_SERVER_REPLY; } break; default: @@ -3058,7 +3065,7 @@ static CURLcode http_statusline(struct Curl_easy *data, data->info.httpcode = k->httpcode; data->info.httpversion = k->httpversion; - conn->httpversion = (unsigned char)k->httpversion; + conn->httpversion_seen = (unsigned char)k->httpversion; if(!data->state.httpversion || data->state.httpversion > k->httpversion) /* store the lowest server version we encounter */ @@ -3238,7 +3245,7 @@ static CURLcode http_on_response(struct Curl_easy *data, if(k->upgr101 == UPGR101_RECEIVED) { /* supposedly upgraded to http2 now */ - if(conn->httpversion != 20) + if(data->req.httpversion != 20) infof(data, "Lying server, not serving HTTP/2"); } @@ -3274,8 +3281,7 @@ static CURLcode http_on_response(struct Curl_easy *data, break; case 101: /* Switching Protocols only allowed from HTTP/1.1 */ - - if(conn->httpversion != 11) { + if(k->httpversion_sent != 11) { /* invalid for other HTTP versions */ failf(data, "unexpected 101 response code"); result = CURLE_WEIRD_SERVER_REPLY; @@ -3289,6 +3295,7 @@ static CURLcode http_on_response(struct Curl_easy *data, /* We expect more response from HTTP/2 later */ k->header = TRUE; k->headerline = 0; /* restart the header line counter */ + k->httpversion_sent = 20; /* It's a HTTP/2 request now */ /* Any remaining `buf` bytes are already HTTP/2 and passed to * be processed. */ result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen); @@ -3337,7 +3344,7 @@ static CURLcode http_on_response(struct Curl_easy *data, } if((k->size == -1) && !k->chunk && !conn->bits.close && - (conn->httpversion == 11) && + (k->httpversion == 11) && !(conn->handler->protocol & CURLPROTO_RTSP) && data->state.httpreq != HTTPREQ_HEAD) { /* On HTTP 1.1, when connection is not to get closed, but no @@ -3486,9 +3493,7 @@ static CURLcode http_on_response(struct Curl_easy *data, like to call http2_handle_stream_close to properly close a stream. In order to do this, we keep reading until we close the stream. */ - if(0 == k->maxdownload - && !Curl_conn_is_http2(data, conn, FIRSTSOCKET) - && !Curl_conn_is_http3(data, conn, FIRSTSOCKET)) + if((0 == k->maxdownload) && (k->httpversion_sent < 20)) k->download_done = TRUE; /* final response without error, prepare to receive the body */ @@ -3573,7 +3578,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data, p++; if((p[0] == '.') && (p[1] == '0' || p[1] == '1')) { if(ISBLANK(p[2])) { - k->httpversion = 10 + (p[1] - '0'); + k->httpversion = (unsigned char)(10 + (p[1] - '0')); p += 3; if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + @@ -3593,7 +3598,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data, case '3': if(!ISBLANK(p[1])) break; - k->httpversion = (*p - '0') * 10; + k->httpversion = (unsigned char)((*p - '0') * 10); p += 2; if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + @@ -3723,10 +3728,11 @@ static CURLcode http_parse_headers(struct Curl_easy *data, Curl_dyn_len(&data->state.headerb)); if(st == STATUS_BAD) { - /* this is not the beginning of a protocol first header line */ + /* this is not the beginning of a protocol first header line. + * Cannot be 0.9 if version was detected or connection was reused. */ k->header = FALSE; streamclose(conn, "bad HTTP: No end-of-message indicator"); - if(conn->httpversion >= 10) { + if((k->httpversion >= 10) || conn->bits.reuse) { failf(data, "Invalid status line"); return CURLE_WEIRD_SERVER_REPLY; } @@ -3761,8 +3767,9 @@ static CURLcode http_parse_headers(struct Curl_easy *data, Curl_dyn_len(&data->state.headerb)); if(st == STATUS_BAD) { streamclose(conn, "bad HTTP: No end-of-message indicator"); - /* this is not the beginning of a protocol first header line */ - if(conn->httpversion >= 10) { + /* this is not the beginning of a protocol first header line. + * Cannot be 0.9 if version was detected or connection was reused. */ + if((k->httpversion >= 10) || conn->bits.reuse) { failf(data, "Invalid status line"); return CURLE_WEIRD_SERVER_REPLY; } diff --git a/lib/http.h b/lib/http.h index 691c98e9c5..d12813a6ff 100644 --- a/lib/http.h +++ b/lib/http.h @@ -76,7 +76,7 @@ char *Curl_checkProxyheaders(struct Curl_easy *data, CURLcode Curl_add_timecondition(struct Curl_easy *data, struct dynbuf *req); CURLcode Curl_add_custom_headers(struct Curl_easy *data, bool is_connect, - struct dynbuf *req); + int httpversion, struct dynbuf *req); CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, bool is_connect, struct dynhds *hds); diff --git a/lib/http2.c b/lib/http2.c index 840e579d72..7797197819 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -2723,6 +2723,9 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf, } break; } + case CF_QUERY_HTTP_VERSION: + *pres1 = 20; + return CURLE_OK; default: break; } @@ -2733,7 +2736,7 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_nghttp2 = { "HTTP/2", - CF_TYPE_MULTIPLEX, + CF_TYPE_MULTIPLEX | CF_TYPE_HTTP, CURL_LOG_LVL_NONE, cf_h2_destroy, cf_h2_connect, @@ -2807,35 +2810,12 @@ out: return result; } -static bool cf_is_http2(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - (void)data; - for(; cf; cf = cf->next) { - if(cf->cft == &Curl_cft_nghttp2) - return TRUE; - if(cf->cft->flags & CF_TYPE_IP_CONNECT) - return FALSE; - } - return FALSE; -} - -bool Curl_conn_is_http2(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex) -{ - return conn ? cf_is_http2(conn->cfilter[sockindex], data) : FALSE; -} - -bool Curl_http2_may_switch(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) +bool Curl_http2_may_switch(struct Curl_easy *data) { - (void)sockindex; - if(!Curl_conn_is_http2(data, conn, sockindex) && + if(Curl_conn_http_version(data) < 20 && data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) { #ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { + if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) { /* We do not support HTTP/2 proxies yet. Also it is debatable whether or not this setting should apply to HTTP/2 proxies. */ infof(data, "Ignoring HTTP/2 prior knowledge due to proxy"); @@ -2847,21 +2827,19 @@ bool Curl_http2_may_switch(struct Curl_easy *data, return FALSE; } -CURLcode Curl_http2_switch(struct Curl_easy *data, - struct connectdata *conn, int sockindex) +CURLcode Curl_http2_switch(struct Curl_easy *data) { struct Curl_cfilter *cf; CURLcode result; - DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex)); + DEBUGASSERT(Curl_conn_http_version(data) < 20); - result = http2_cfilter_add(&cf, data, conn, sockindex, FALSE); + result = http2_cfilter_add(&cf, data, data->conn, FIRSTSOCKET, FALSE); if(result) return result; CURL_TRC_CF(data, cf, "switching connection to HTTP/2"); - conn->httpversion = 20; /* we know we are on HTTP/2 now */ - conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + data->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ Curl_multi_connchanged(data->multi); if(cf->next) { @@ -2876,14 +2854,13 @@ CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data) struct Curl_cfilter *cf_h2; CURLcode result; - DEBUGASSERT(!cf_is_http2(cf, data)); + DEBUGASSERT(Curl_conn_http_version(data) < 20); result = http2_cfilter_insert_after(cf, data, FALSE); if(result) return result; cf_h2 = cf->next; - cf->conn->httpversion = 20; /* we know we are on HTTP/2 now */ cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ Curl_multi_connchanged(data->multi); @@ -2902,7 +2879,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, struct cf_h2_ctx *ctx; CURLcode result; - DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex)); + DEBUGASSERT(Curl_conn_http_version(data) < 20); DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED); result = http2_cfilter_add(&cf, data, conn, sockindex, TRUE); @@ -2935,7 +2912,6 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, " after upgrade: len=%zu", nread); } - conn->httpversion = 20; /* we know we are on HTTP/2 now */ conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ Curl_multi_connchanged(data->multi); @@ -2950,7 +2926,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, CURLE_HTTP2_STREAM error! */ bool Curl_h2_http_1_1_error(struct Curl_easy *data) { - if(Curl_conn_is_http2(data, data->conn, FIRSTSOCKET)) { + if(Curl_conn_http_version(data) == 20) { int err = Curl_conn_get_stream_error(data, data->conn, FIRSTSOCKET); return err == NGHTTP2_HTTP_1_1_REQUIRED; } diff --git a/lib/http2.h b/lib/http2.h index dbb1784661..93cc2d44f2 100644 --- a/lib/http2.h +++ b/lib/http2.h @@ -44,15 +44,9 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req, /* returns true if the HTTP/2 stream error was HTTP_1_1_REQUIRED */ bool Curl_h2_http_1_1_error(struct Curl_easy *data); -bool Curl_conn_is_http2(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex); -bool Curl_http2_may_switch(struct Curl_easy *data, - struct connectdata *conn, - int sockindex); +bool Curl_http2_may_switch(struct Curl_easy *data); -CURLcode Curl_http2_switch(struct Curl_easy *data, - struct connectdata *conn, int sockindex); +CURLcode Curl_http2_switch(struct Curl_easy *data); CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data); @@ -69,12 +63,10 @@ extern struct Curl_cftype Curl_cft_nghttp2; #else /* USE_NGHTTP2 */ -#define Curl_cf_is_http2(a,b) FALSE -#define Curl_conn_is_http2(a,b,c) FALSE -#define Curl_http2_may_switch(a,b,c) FALSE +#define Curl_http2_may_switch(a) FALSE #define Curl_http2_request_upgrade(x,y) CURLE_UNSUPPORTED_PROTOCOL -#define Curl_http2_switch(a,b,c) CURLE_UNSUPPORTED_PROTOCOL +#define Curl_http2_switch(a) CURLE_UNSUPPORTED_PROTOCOL #define Curl_http2_upgrade(a,b,c,d,e) CURLE_UNSUPPORTED_PROTOCOL #define Curl_h2_http_1_1_error(x) 0 #endif diff --git a/lib/http_proxy.c b/lib/http_proxy.c index 154dc31963..3a2e9f2b05 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -56,7 +56,7 @@ static bool hd_name_eq(const char *n1, size_t n1len, } static CURLcode dynhds_add_custom(struct Curl_easy *data, - bool is_connect, + bool is_connect, int httpversion, struct dynhds *hds) { struct connectdata *conn = data->conn; @@ -169,9 +169,9 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data, Connection: */ hd_name_eq(name, namelen, STRCONST("Connection:"))) ; - else if((conn->httpversion >= 20) && + else if((httpversion >= 20) && hd_name_eq(name, namelen, STRCONST("Transfer-Encoding:"))) - /* HTTP/2 does not support chunked requests */ + /* HTTP/2 and HTTP/3 do not support chunked requests */ ; else if((hd_name_eq(name, namelen, STRCONST("Authorization:")) || hd_name_eq(name, namelen, STRCONST("Cookie:"))) && @@ -221,11 +221,18 @@ CURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf, return CURLE_OK; } +struct cf_proxy_ctx { + /* the protocol specific sub-filter we install during connect */ + struct Curl_cfilter *cf_protocol; + int httpversion; /* HTTP version used to CONNECT */ +}; + CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, struct Curl_cfilter *cf, struct Curl_easy *data, int http_version_major) { + struct cf_proxy_ctx *ctx = cf->ctx; const char *hostname = NULL; char *authority = NULL; int port; @@ -286,7 +293,7 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, goto out; } - result = dynhds_add_custom(data, TRUE, &req->headers); + result = dynhds_add_custom(data, TRUE, ctx->httpversion, &req->headers); out: if(result && req) { @@ -298,12 +305,6 @@ out: return result; } - -struct cf_proxy_ctx { - /* the protocol specific sub-filter we install during connect */ - struct Curl_cfilter *cf_protocol; -}; - static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool blocking, bool *done) @@ -325,6 +326,7 @@ connect_sub: *done = FALSE; if(!ctx->cf_protocol) { struct Curl_cfilter *cf_protocol = NULL; + int httpversion = 0; int alpn = Curl_conn_cf_is_ssl(cf->next) ? cf->conn->proxy_alpn : CURL_HTTP_VERSION_1_1; @@ -340,6 +342,7 @@ connect_sub: if(result) goto out; cf_protocol = cf->next; + httpversion = (alpn == CURL_HTTP_VERSION_1_0) ? 10 : 11; break; #ifdef USE_NGHTTP2 case CURL_HTTP_VERSION_2: @@ -349,6 +352,7 @@ connect_sub: if(result) goto out; cf_protocol = cf->next; + httpversion = 20; break; #endif default: @@ -358,6 +362,7 @@ connect_sub: } ctx->cf_protocol = cf_protocol; + ctx->httpversion = httpversion; /* after we installed the filter "below" us, we call connect * on out sub-chain again. */ diff --git a/lib/request.c b/lib/request.c index b1298a0387..d5f04e9f1d 100644 --- a/lib/request.c +++ b/lib/request.c @@ -66,7 +66,8 @@ CURLcode Curl_req_soft_reset(struct SingleRequest *req, req->headerbytecount = 0; req->allheadercount = 0; req->deductheadercount = 0; - + req->httpversion_sent = 0; + req->httpversion = 0; result = Curl_client_start(data); if(result) return result; @@ -373,7 +374,8 @@ static CURLcode req_send_buffer_add(struct Curl_easy *data, return CURLE_OK; } -CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req) +CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req, + unsigned char httpversion) { CURLcode result; const char *buf; @@ -382,6 +384,7 @@ CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req) if(!data || !data->conn) return CURLE_FAILED_INIT; + data->req.httpversion_sent = httpversion; buf = Curl_dyn_ptr(req); blen = Curl_dyn_len(req); if(!Curl_creader_total_length(data)) { diff --git a/lib/request.h b/lib/request.h index b760c2634d..4c77be962f 100644 --- a/lib/request.h +++ b/lib/request.h @@ -81,10 +81,11 @@ struct SingleRequest { first one */ curl_off_t offset; /* possible resume offset read from the Content-Range: header */ - int httpversion; /* Version in response (09, 10, 11, etc.) */ int httpcode; /* error code from the 'HTTP/1.? XXX' or 'RTSP/1.? XXX' line */ int keepon; + unsigned char httpversion_sent; /* Version in request (09, 10, 11, etc.) */ + unsigned char httpversion; /* Version in response (09, 10, 11, etc.) */ enum upgrade101 upgr101; /* 101 upgrade state */ /* Client Writer stack, handles transfer- and content-encodings, protocol @@ -199,9 +200,11 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data); * bytes are really send. * @param data the transfer making the request * @param buf the complete header bytes, no body + * @param httpversion version used in request (09, 10, 11, etc.) * @return CURLE_OK (on blocking with *pnwritten == 0) or error. */ -CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf); +CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf, + unsigned char httpversion); /** * TRUE iff the request has sent all request headers and data. diff --git a/lib/rtsp.c b/lib/rtsp.c index f6f4a33f1c..e26586cace 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -230,6 +230,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) Curl_RtspReq rtspreq = data->set.rtspreq; struct RTSP *rtsp = data->req.p.rtsp; struct dynbuf req_buffer; + unsigned char httpversion = 11; /* RTSP is close to HTTP/1.1, sort of... */ const char *p_request = NULL; const char *p_session_id = NULL; @@ -499,7 +500,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) goto out; } - result = Curl_add_custom_headers(data, FALSE, &req_buffer); + result = Curl_add_custom_headers(data, FALSE, httpversion, &req_buffer); if(result) goto out; @@ -585,7 +586,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) Curl_xfer_setup1(data, CURL_XFER_SENDRECV, -1, TRUE); /* issue the request */ - result = Curl_req_send(data, &req_buffer); + result = Curl_req_send(data, &req_buffer, httpversion); if(result) { failf(data, "Failed sending RTSP request"); goto out; diff --git a/lib/transfer.c b/lib/transfer.c index 4e0454fe83..83e894750f 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -782,7 +782,7 @@ static void xfer_setup( DEBUGASSERT((writesockindex <= 1) && (writesockindex >= -1)); DEBUGASSERT(!shutdown || (sockindex == -1) || (writesockindex == -1)); - if(conn->bits.multiplex || conn->httpversion >= 20 || want_send) { + if(Curl_conn_is_multiplex(conn, FIRSTSOCKET) || want_send) { /* when multiplexing, the read/write sockets need to be the same! */ conn->sockfd = sockindex == -1 ? ((writesockindex == -1 ? CURL_SOCKET_BAD : conn->sock[writesockindex])) : diff --git a/lib/url.c b/lib/url.c index 1461674fa7..d6747c0e74 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1002,7 +1002,7 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) if(match->may_multiplex && (data->state.httpwant == CURL_HTTP_VERSION_2_0) && (needle->handler->protocol & CURLPROTO_HTTP) && - !conn->httpversion) { + !conn->httpversion_seen) { if(data->set.pipewait) { infof(data, "Server upgrade does not support multiplex yet, wait"); match->found = NULL; @@ -1036,17 +1036,18 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) * so we take any existing connection. */ if((needle->handler->protocol & PROTO_FAMILY_HTTP) && (data->state.httpwant != CURL_HTTP_VERSION_2TLS)) { - if((conn->httpversion >= 20) && + unsigned char httpversion = Curl_conn_http_version(data); + if((httpversion >= 20) && (data->state.httpwant < CURL_HTTP_VERSION_2_0)) { DEBUGF(infof(data, "nor reusing conn #%" CURL_FORMAT_CURL_OFF_T " with httpversion=%d, we want a version less than h2", - conn->connection_id, conn->httpversion)); + conn->connection_id, httpversion)); } - if((conn->httpversion >= 30) && + if((httpversion >= 30) && (data->state.httpwant < CURL_HTTP_VERSION_3)) { DEBUGF(infof(data, "nor reusing conn #%" CURL_FORMAT_CURL_OFF_T " with httpversion=%d, we want a version less than h3", - conn->connection_id, conn->httpversion)); + conn->connection_id, httpversion)); return FALSE; } } @@ -3135,14 +3136,14 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, /* protocol version switch */ switch(as->dst.alpnid) { case ALPN_h1: - conn->httpversion = 11; + data->state.httpwant = CURL_HTTP_VERSION_1_1; break; case ALPN_h2: - conn->httpversion = 20; + data->state.httpwant = CURL_HTTP_VERSION_2_0; break; case ALPN_h3: conn->transport = TRNSPRT_QUIC; - conn->httpversion = 30; + data->state.httpwant = CURL_HTTP_VERSION_3; break; default: /* should not be possible */ break; diff --git a/lib/urldata.h b/lib/urldata.h index 224167f7f0..b18594d137 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -950,7 +950,9 @@ struct connectdata { #endif unsigned char transport; /* one of the TRNSPRT_* defines */ unsigned char ip_version; /* copied from the Curl_easy at creation time */ - unsigned char httpversion; /* the HTTP version*10 reported by the server */ + /* HTTP version last responded with by the server. + * 0 at start, then one of 09, 10, 11, etc. */ + unsigned char httpversion_seen; unsigned char connect_only; unsigned char gssapi_delegation; /* inherited from set.gssapi_delegation */ }; diff --git a/lib/vquic/curl_msh3.c b/lib/vquic/curl_msh3.c index dc43766a81..9e7cfbea4f 100644 --- a/lib/vquic/curl_msh3.c +++ b/lib/vquic/curl_msh3.c @@ -918,7 +918,6 @@ static CURLcode cf_msh3_connect(struct Curl_cfilter *cf, if(ctx->handshake_succeeded) { CURL_TRC_CF(data, cf, "handshake succeeded"); cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - cf->conn->httpversion = 30; cf->connected = TRUE; cf->conn->alpn = CURL_HTTP_VERSION_3; *done = TRUE; @@ -1025,6 +1024,9 @@ static CURLcode cf_msh3_query(struct Curl_cfilter *cf, *when = ctx->handshake_at; return CURLE_OK; } + case CF_QUERY_HTTP_VERSION: + *pres1 = 30; + return CURLE_OK; default: break; } @@ -1047,7 +1049,7 @@ static bool cf_msh3_conn_is_alive(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_http3 = { "HTTP/3", - CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, + CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP, 0, cf_msh3_destroy, cf_msh3_connect, diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index 2f0a5c0ec5..afe3b9d6b4 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -470,7 +470,6 @@ static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) ctx->handshake_at = Curl_now(); ctx->tls_handshake_complete = TRUE; cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - cf->conn->httpversion = 30; ctx->tls_vrfy_result = Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); @@ -2594,6 +2593,9 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, *when = ctx->handshake_at; return CURLE_OK; } + case CF_QUERY_HTTP_VERSION: + *pres1 = 30; + return CURLE_OK; default: break; } @@ -2656,7 +2658,7 @@ out: struct Curl_cftype Curl_cft_http3 = { "HTTP/3", - CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, + CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP, 0, cf_ngtcp2_destroy, cf_ngtcp2_connect, diff --git a/lib/vquic/curl_osslq.c b/lib/vquic/curl_osslq.c index bd9d21f09f..1b78497f16 100644 --- a/lib/vquic/curl_osslq.c +++ b/lib/vquic/curl_osslq.c @@ -570,7 +570,6 @@ static CURLcode cf_osslq_verify_peer(struct Curl_cfilter *cf, struct cf_osslq_ctx *ctx = cf->ctx; cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - cf->conn->httpversion = 30; return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); } @@ -2359,6 +2358,9 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf, *when = ctx->handshake_at; return CURLE_OK; } + case CF_QUERY_HTTP_VERSION: + *pres1 = 30; + return CURLE_OK; default: break; } @@ -2369,7 +2371,7 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_http3 = { "HTTP/3", - CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, + CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP, 0, cf_osslq_destroy, cf_osslq_connect, diff --git a/lib/vquic/curl_quiche.c b/lib/vquic/curl_quiche.c index 59be1c9ec3..ec0dccecfd 100644 --- a/lib/vquic/curl_quiche.c +++ b/lib/vquic/curl_quiche.c @@ -1372,7 +1372,6 @@ static CURLcode cf_quiche_verify_peer(struct Curl_cfilter *cf, struct cf_quiche_ctx *ctx = cf->ctx; cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - cf->conn->httpversion = 30; return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); } @@ -1566,6 +1565,9 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf, *when = ctx->handshake_at; return CURLE_OK; } + case CF_QUERY_HTTP_VERSION: + *pres1 = 30; + return CURLE_OK; default: break; } @@ -1613,7 +1615,7 @@ static bool cf_quiche_conn_is_alive(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_http3 = { "HTTP/3", - CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, + CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP, 0, cf_quiche_destroy, cf_quiche_connect, diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index c0d6e2f3eb..9ac3518612 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -689,24 +689,6 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, #endif } -bool Curl_conn_is_http3(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex) -{ -#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) - return Curl_conn_is_ngtcp2(data, conn, sockindex); -#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3) - return Curl_conn_is_osslq(data, conn, sockindex); -#elif defined(USE_QUICHE) - return Curl_conn_is_quiche(data, conn, sockindex); -#elif defined(USE_MSH3) - return Curl_conn_is_msh3(data, conn, sockindex); -#else - return (conn->handler->protocol & PROTO_FAMILY_HTTP) && - (conn->httpversion == 30); -#endif -} - CURLcode Curl_conn_may_http3(struct Curl_easy *data, const struct connectdata *conn) { diff --git a/lib/vquic/vquic.h b/lib/vquic/vquic.h index c1ca1df6aa..1cd3e258f4 100644 --- a/lib/vquic/vquic.h +++ b/lib/vquic/vquic.h @@ -46,16 +46,8 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, const struct Curl_addrinfo *ai, int transport); -bool Curl_conn_is_http3(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex); - extern struct Curl_cftype Curl_cft_http3; -#else /* USE_HTTP3 */ - -#define Curl_conn_is_http3(a,b,c) FALSE - #endif /* !USE_HTTP3 */ CURLcode Curl_conn_may_http3(struct Curl_easy *data, diff --git a/tests/data/test471 b/tests/data/test471 index 69ecb5e210..4a08fed278 100644 --- a/tests/data/test471 +++ b/tests/data/test471 @@ -68,7 +68,7 @@ Accept: */* # curl: (1) Version mismatch (from HTTP/1 to HTTP/2) -1 +8 diff --git a/tests/http/test_12_reuse.py b/tests/http/test_12_reuse.py index 6e6e1912c3..18d88596b1 100644 --- a/tests/http/test_12_reuse.py +++ b/tests/http/test_12_reuse.py @@ -98,6 +98,7 @@ class TestReuse: for s in r.stats: assert s['http_version'] == '3', f'{s}' + @pytest.mark.skipif(condition=not Env.have_h3(), reason="h3 not supported") def test_12_04_alt_svc_h3h2(self, env: Env, httpd, nghttpx): httpd.clear_extra_configs() httpd.reload() @@ -115,11 +116,12 @@ class TestReuse: '--alt-svc', f'{asfile}', ]) r.check_response(count=count, http_status=200) - # We expect the connection to be reused + # We expect the connection to be reused and use HTTP/2 assert r.total_connects == 1 for s in r.stats: assert s['http_version'] == '2', f'{s}' + @pytest.mark.skipif(condition=not Env.have_h3(), reason="h3 not supported") def test_12_05_alt_svc_h3h1(self, env: Env, httpd, nghttpx): httpd.clear_extra_configs() httpd.reload() @@ -137,9 +139,7 @@ class TestReuse: '--alt-svc', f'{asfile}', ]) r.check_response(count=count, http_status=200) - # We expect the connection to be reused + # We expect the connection to be reused and use HTTP/1.1 assert r.total_connects == 1 - # When using http/1.1 from alt-svc, we ALPN-negotiate 'h2,http/1.1' anyway - # which means our server gives us h2 for s in r.stats: - assert s['http_version'] == '2', f'{s}' + assert s['http_version'] == '1.1', f'{s}'