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
This commit is contained in:
parent
7e814c8717
commit
e83818cae1
@ -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;
|
||||
|
||||
@ -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.
|
||||
|
||||
175
lib/http.c
175
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)
|
||||
{
|
||||
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";
|
||||
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;
|
||||
}
|
||||
|
||||
return "1.0";
|
||||
static const char *get_http_string(int httpversion)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
52
lib/http2.c
52
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)
|
||||
bool Curl_http2_may_switch(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)
|
||||
{
|
||||
(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;
|
||||
}
|
||||
|
||||
16
lib/http2.h
16
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
|
||||
|
||||
@ -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.
|
||||
*/
|
||||
|
||||
@ -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)) {
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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])) :
|
||||
|
||||
17
lib/url.c
17
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;
|
||||
|
||||
@ -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 */
|
||||
};
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -68,7 +68,7 @@ Accept: */*
|
||||
</protocol>
|
||||
# curl: (1) Version mismatch (from HTTP/1 to HTTP/2)
|
||||
<errorcode>
|
||||
1
|
||||
8
|
||||
</errorcode>
|
||||
</verify>
|
||||
</testcase>
|
||||
|
||||
@ -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}'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user