From: Stefan Eissing Date: Mon, 1 Sep 2025 13:52:30 +0000 (+0200) Subject: Curl_http(), decomplexify X-Git-Tag: curl-8_16_0~49 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4d040c71d7e611a0e5e9114c81eb6469a02090ea;p=thirdparty%2Fcurl.git Curl_http(), decomplexify Split out adding of individual request headers into a switch. Check the connection http version only on fresh connections, use separate methods. Add TE: header directly without allocation. Add bit for indicating Connection: header has been added and custom headers should not do that again. Closes #18444 --- diff --git a/lib/http.c b/lib/http.c index 3f8c69e492..0ce3b25bc3 100644 --- a/lib/http.c +++ b/lib/http.c @@ -105,24 +105,16 @@ static void http_exp100_got100(struct Curl_easy *data); static CURLcode http_firstwrite(struct Curl_easy *data); static CURLcode http_header(struct Curl_easy *data, const char *hd, size_t hdlen); -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, int httpversion, - Curl_HttpReq httpreq); -static CURLcode http_req_set_reader(struct Curl_easy *data, - Curl_HttpReq httpreq, int httpversion, - const char **tep); +static CURLcode http_req_set_TE(struct Curl_easy *data, + struct dynbuf *req, + int httpversion); static CURLcode http_size(struct Curl_easy *data); static CURLcode http_statusline(struct Curl_easy *data, struct connectdata *conn); -static CURLcode http_target(struct Curl_easy *data, struct connectdata *conn, - struct dynbuf *req); +static CURLcode http_target(struct Curl_easy *data, struct dynbuf *req); static CURLcode http_useragent(struct Curl_easy *data); -#ifdef HAVE_LIBZ -static CURLcode http_transferencode(struct Curl_easy *data); -#endif /* @@ -1709,10 +1701,8 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, we will force length zero then */ curlx_str_casecompare(&name, "Content-Length")) ; - else if(data->state.aptr.te && - /* when asking for Transfer-Encoding, do not pass on a custom - Connection: */ - curlx_str_casecompare(&name, "Connection")) + else if(curlx_str_casecompare(&name, "Connection")) + /* Normal Connection: header generation takes care of this */ ; else if((httpversion >= 20) && curlx_str_casecompare(&name, "Transfer-Encoding")) @@ -1817,14 +1807,14 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data, } #endif -void Curl_http_method(struct Curl_easy *data, struct connectdata *conn, +void Curl_http_method(struct Curl_easy *data, const char **method, Curl_HttpReq *reqp) { Curl_HttpReq httpreq = (Curl_HttpReq)data->state.httpreq; const char *request; - if(conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS)) + if(data->conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS)) httpreq = HTTPREQ_GET; - else if((conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) && + else if((data->conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) && data->state.upload) httpreq = HTTPREQ_PUT; @@ -1875,10 +1865,12 @@ static CURLcode http_useragent(struct Curl_easy *data) } -static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn) +static CURLcode http_set_aptr_host(struct Curl_easy *data) { - const char *ptr; + struct connectdata *conn = data->conn; struct dynamically_allocated_data *aptr = &data->state.aptr; + const char *ptr; + if(!data->state.this_is_a_follow) { /* Free to avoid leaking memory on multiple requests */ free(data->state.first_host); @@ -1966,7 +1958,6 @@ static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn) * Append the request-target to the HTTP request */ static CURLcode http_target(struct Curl_easy *data, - struct connectdata *conn, struct dynbuf *r) { CURLcode result = CURLE_OK; @@ -1979,7 +1970,7 @@ static CURLcode http_target(struct Curl_easy *data, } #ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { + if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) { /* Using a proxy but does not tunnel through it */ /* The path sent to the proxy is in fact the entire URL. But if the remote @@ -1993,8 +1984,8 @@ static CURLcode http_target(struct Curl_easy *data, if(!h) return CURLE_OUT_OF_MEMORY; - if(conn->host.dispname != conn->host.name) { - uc = curl_url_set(h, CURLUPART_HOST, conn->host.name, 0); + if(data->conn->host.dispname != data->conn->host.name) { + uc = curl_url_set(h, CURLUPART_HOST, data->conn->host.name, 0); if(uc) { curl_url_cleanup(h); return CURLE_OUT_OF_MEMORY; @@ -2060,8 +2051,6 @@ static CURLcode http_target(struct Curl_easy *data, } else -#else - (void)conn; /* not used in disabled-proxy builds */ #endif { result = curlx_dyn_add(r, path); @@ -2247,21 +2236,13 @@ static CURLcode http_resume(struct Curl_easy *data, Curl_HttpReq httpreq) return CURLE_OK; } -static CURLcode http_req_set_reader(struct Curl_easy *data, - Curl_HttpReq httpreq, int httpversion, - const char **tep) +static CURLcode http_req_set_TE(struct Curl_easy *data, + struct dynbuf *req, + int httpversion) { CURLcode result = CURLE_OK; const char *ptr; - result = set_reader(data, httpreq); - if(result) - return result; - - result = http_resume(data, httpreq); - if(result) - return result; - ptr = Curl_checkheaders(data, STRCONST("Transfer-Encoding")); if(ptr) { /* Some kind of TE is requested, check if 'chunked' is chosen */ @@ -2295,7 +2276,7 @@ static CURLcode http_req_set_reader(struct Curl_easy *data, } if(data->req.upload_chunky) - *tep = "Transfer-Encoding: chunked\r\n"; + result = curlx_dyn_add(req, "Transfer-Encoding: chunked\r\n"); } return result; } @@ -2335,9 +2316,10 @@ static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r, return CURLE_OK; } -static CURLcode http_req_complete(struct Curl_easy *data, - struct dynbuf *r, int httpversion, - Curl_HttpReq httpreq) +static CURLcode http_add_content_hds(struct Curl_easy *data, + struct dynbuf *r, + int httpversion, + Curl_HttpReq httpreq) { CURLcode result = CURLE_OK; curl_off_t req_clen; @@ -2405,26 +2387,17 @@ static CURLcode http_req_complete(struct Curl_easy *data, break; } - /* end of headers */ - result = curlx_dyn_addn(r, STRCONST("\r\n")); - if(!result) { - Curl_pgrsSetUploadSize(data, req_clen); - if(announced_exp100) - result = http_exp100_add_reader(data); - } + Curl_pgrsSetUploadSize(data, req_clen); + if(announced_exp100) + result = http_exp100_add_reader(data); out: - if(!result) { - /* setup variables for the upcoming transfer */ - Curl_xfer_setup_sendrecv(data, FIRSTSOCKET, -1); - } return result; } #ifndef CURL_DISABLE_COOKIES static CURLcode http_cookies(struct Curl_easy *data, - struct connectdata *conn, struct dynbuf *r) { CURLcode result = CURLE_OK; @@ -2441,9 +2414,9 @@ static CURLcode http_cookies(struct Curl_easy *data, if(data->cookies && data->state.cookie_engine) { const char *host = data->state.aptr.cookiehost ? - data->state.aptr.cookiehost : conn->host.name; + data->state.aptr.cookiehost : data->conn->host.name; Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - rc = Curl_cookie_getlist(data, conn, host, &list); + rc = Curl_cookie_getlist(data, data->conn, host, &list); Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } if(!rc) { @@ -2494,7 +2467,7 @@ static CURLcode http_cookies(struct Curl_easy *data, return result; } #else -#define http_cookies(a,b,c) CURLE_OK +#define http_cookies(a,b) CURLE_OK #endif static CURLcode http_range(struct Curl_easy *data, @@ -2616,63 +2589,13 @@ static CURLcode http_firstwrite(struct Curl_easy *data) return CURLE_OK; } -#ifdef HAVE_LIBZ -static CURLcode http_transferencode(struct Curl_easy *data) -{ - if(!Curl_checkheaders(data, STRCONST("TE")) && - data->set.http_transfer_encoding) { - /* When we are to insert a TE: header in the request, we must also insert - TE in a Connection: header, so we need to merge the custom provided - Connection: header and prevent the original to get sent. Note that if - the user has inserted his/her own TE: header we do not do this magic - but then assume that the user will handle it all! */ - char *cptr = Curl_checkheaders(data, STRCONST("Connection")); -#define TE_HEADER "TE: gzip\r\n" - - Curl_safefree(data->state.aptr.te); - - if(cptr) { - cptr = Curl_copy_header_value(cptr); - if(!cptr) - return CURLE_OUT_OF_MEMORY; - } - - /* Create the (updated) Connection: header */ - data->state.aptr.te = aprintf("Connection: %s%sTE\r\n" TE_HEADER, - cptr ? cptr : "", (cptr && *cptr) ? ", ":""); - - free(cptr); - if(!data->state.aptr.te) - return CURLE_OUT_OF_MEMORY; - } - return CURLE_OK; -} -#endif - -/* - * Curl_http() gets called from the generic multi_do() function when an HTTP - * request is to be performed. This creates and sends a properly constructed - * HTTP request. - */ -CURLcode Curl_http(struct Curl_easy *data, bool *done) +static CURLcode http_check_new_conn(struct Curl_easy *data) { struct connectdata *conn = data->conn; - CURLcode result = CURLE_OK; - Curl_HttpReq httpreq; - const char *te = ""; /* transfer-encoding */ - const char *request; - const char *httpstring; - struct dynbuf req; - char *altused = NULL; - const char *p_accept; /* Accept: string */ - unsigned char httpversion; - const char *alpn; const char *info_version = NULL; + const char *alpn; + CURLcode result; - /* 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 - the rest of the request in the PERFORM phase. */ - *done = TRUE; alpn = Curl_conn_get_alpn_negotiated(data, conn); if(alpn && !strcmp("h3", alpn)) { DEBUGASSERT(Curl_conn_http_version(data, conn) == 30); @@ -2684,7 +2607,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) conn->bits.proxy && !conn->bits.tunnel_proxy) { result = Curl_http2_switch(data); if(result) - goto fail; + return result; } else #endif @@ -2697,7 +2620,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) DEBUGF(infof(data, "HTTP/2 over clean TCP")); result = Curl_http2_switch(data); if(result) - goto fail; + return result; info_version = "HTTP/2"; /* There is no ALPN here, but the connection is now definitely h2 */ conn->httpversion_seen = 20; @@ -2706,213 +2629,321 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) info_version = "HTTP/1.x"; } - if(info_version && !data->conn->bits.reuse) + if(info_version) infof(data, "using %s", info_version); + return CURLE_OK; +} - /* Add collecting of headers written to client. For a new connection, - * we might have done that already, but reuse - * or multiplex needs it here as well. */ - result = Curl_headers_init(data); - if(result) - goto fail; +static CURLcode http_add_connection_hd(struct Curl_easy *data, + struct dynbuf *req) +{ + char *custom = Curl_checkheaders(data, STRCONST("Connection")); + char *custom_val = custom ? Curl_copy_header_value(custom) : NULL; + const char *sep = (custom_val && *custom_val) ? ", " : "Connection: "; + CURLcode result = CURLE_OK; + size_t rlen = curlx_dyn_len(req); - result = http_host(data, conn); - if(result) - goto fail; + if(custom && !custom_val) + return CURLE_OUT_OF_MEMORY; - result = http_useragent(data); - if(result) - goto fail; + if(custom_val && *custom_val) + result = curlx_dyn_addf(req, "Connection: %s", custom_val); + if(!result && data->state.http_hd_te) { + result = curlx_dyn_addf(req, "%s%s", sep, "TE"); + sep = ", "; + } + if(!result && data->state.http_hd_upgrade) { + result = curlx_dyn_addf(req, "%s%s", sep, "Upgrade"); + sep = ", "; + } + if(!result && data->state.http_hd_h2_settings) { + result = curlx_dyn_addf(req, "%s%s", sep, "HTTP2-Settings"); + } + if(rlen < curlx_dyn_len(req)) + result = curlx_dyn_addn(req, STRCONST("\r\n")); - Curl_http_method(data, conn, &request, &httpreq); + free(custom_val); + return result; +} - /* setup the authentication headers */ - { - char *pq = NULL; - if(data->state.up.query) { - pq = aprintf("%s?%s", data->state.up.path, data->state.up.query); - if(!pq) - return CURLE_OUT_OF_MEMORY; - } - result = Curl_http_output_auth(data, conn, request, httpreq, - (pq ? pq : data->state.up.path), FALSE); - free(pq); - if(result) - goto fail; - } +/* Header identifier in order we send them by default */ +typedef enum { + H1_HD_REQUEST, + H1_HD_HOST, +#ifndef CURL_DISABLE_PROXY + H1_HD_PROXY_AUTH, +#endif + H1_HD_USER_AUTH, + H1_HD_RANGE, + H1_HD_USER_AGENT, + H1_HD_ACCEPT, + H1_HD_TE, + H1_HD_ACCEPT_ENCODING, + H1_HD_REFERER, +#ifndef CURL_DISABLE_PROXY + H1_HD_PROXY_CONNECTION, +#endif + H1_HD_TRANSFER_ENCODING, +#ifndef CURL_DISABLE_ALTSVC + H1_HD_ALT_USED, +#endif + H1_HD_UPGRADE, + H1_HD_COOKIES, + H1_HD_CONDITIONALS, + H1_HD_CUSTOM, + H1_HD_CONTENT, + H1_HD_CONNECTION, + H1_HD_LAST /* the last, empty header line */ +} http_hd_t; + +static CURLcode http_add_hd(struct Curl_easy *data, + struct dynbuf *req, + http_hd_t id, + unsigned char httpversion, + const char *method, + Curl_HttpReq httpreq) +{ + CURLcode result = CURLE_OK; + switch(id) { + case H1_HD_REQUEST: + /* add the main request stuff */ + /* GET/HEAD/POST/PUT */ + result = curlx_dyn_addf(req, "%s ", method); + if(!result) + result = http_target(data, req); + if(!result) + result = curlx_dyn_addf(req, " HTTP/%s\r\n", + get_http_string(httpversion)); + break; - Curl_safefree(data->state.aptr.ref); - if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) { - data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer); - if(!data->state.aptr.ref) - return CURLE_OUT_OF_MEMORY; - } + case H1_HD_HOST: + if(data->state.aptr.host) + result = curlx_dyn_add(req, data->state.aptr.host); + break; - if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && - data->set.str[STRING_ENCODING]) { - free(data->state.aptr.accept_encoding); - data->state.aptr.accept_encoding = - aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]); - if(!data->state.aptr.accept_encoding) - return CURLE_OUT_OF_MEMORY; - } - else - Curl_safefree(data->state.aptr.accept_encoding); +#ifndef CURL_DISABLE_PROXY + case H1_HD_PROXY_AUTH: + if(data->state.aptr.proxyuserpwd) + result = curlx_dyn_add(req, data->state.aptr.proxyuserpwd); + break; +#endif + + case H1_HD_USER_AUTH: + if(data->state.aptr.userpwd) + result = curlx_dyn_add(req, data->state.aptr.userpwd); + break; + case H1_HD_RANGE: + if(data->state.use_range && data->state.aptr.rangeline) + result = curlx_dyn_add(req, data->state.aptr.rangeline); + break; + + case H1_HD_USER_AGENT: + if(data->set.str[STRING_USERAGENT] && /* User-Agent: */ + *data->set.str[STRING_USERAGENT] && + data->state.aptr.uagent) + result = curlx_dyn_add(req, data->state.aptr.uagent); + break; + + case H1_HD_ACCEPT: + if(!Curl_checkheaders(data, STRCONST("Accept"))) + result = curlx_dyn_add(req, "Accept: */*\r\n"); + break; + + case H1_HD_TE: #ifdef HAVE_LIBZ - /* we only consider transfer-encoding magic if libz support is built-in */ - result = http_transferencode(data); - if(result) - goto fail; + if(!Curl_checkheaders(data, STRCONST("TE")) && + data->set.http_transfer_encoding) { + data->state.http_hd_te = TRUE; + result = curlx_dyn_add(req, "TE: gzip\r\n"); + } #endif + break; - httpversion = http_request_version(data); - httpstring = get_http_string(httpversion); + case H1_HD_ACCEPT_ENCODING: + Curl_safefree(data->state.aptr.accept_encoding); + if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && + data->set.str[STRING_ENCODING]) + result = curlx_dyn_addf(req, "Accept-Encoding: %s\r\n", + data->set.str[STRING_ENCODING]); + break; - result = http_req_set_reader(data, httpreq, httpversion, &te); - if(result) - goto fail; + case H1_HD_REFERER: + Curl_safefree(data->state.aptr.ref); + if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) + result = curlx_dyn_addf(req, "Referer: %s\r\n", data->state.referer); + break; + +#ifndef CURL_DISABLE_PROXY + case H1_HD_PROXY_CONNECTION: + if(data->conn->bits.httpproxy && + !data->conn->bits.tunnel_proxy && + !Curl_checkheaders(data, STRCONST("Proxy-Connection")) && + !Curl_checkProxyheaders(data, data->conn, STRCONST("Proxy-Connection"))) + result = curlx_dyn_add(req, "Proxy-Connection: Keep-Alive\r\n"); + break; +#endif - p_accept = Curl_checkheaders(data, - STRCONST("Accept")) ? NULL : "Accept: */*\r\n"; + case H1_HD_TRANSFER_ENCODING: + result = http_req_set_TE(data, req, httpversion); + break; - result = http_range(data, httpreq); - if(result) - goto fail; +#ifndef CURL_DISABLE_ALTSVC + case H1_HD_ALT_USED: + if(data->conn->bits.altused && + !Curl_checkheaders(data, STRCONST("Alt-Used"))) + result = curlx_dyn_addf(req, "Alt-Used: %s:%d\r\n", + data->conn->conn_to_host.name, + data->conn->conn_to_port); + break; +#endif + + case H1_HD_UPGRADE: + if(!Curl_conn_is_ssl(data->conn, FIRSTSOCKET) && (httpversion < 20) && + (data->state.http_neg.wanted & CURL_HTTP_V2x) && + data->state.http_neg.h2_upgrade) { + /* append HTTP2 upgrade magic stuff to the HTTP request if it is not done + over SSL */ + result = Curl_http2_request_upgrade(req, data); + } +#ifndef CURL_DISABLE_WEBSOCKETS + if(!result && data->conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS)) + result = Curl_ws_request(data, req); +#endif + break; + case H1_HD_COOKIES: + result = http_cookies(data, req); + break; + + case H1_HD_CONDITIONALS: + result = Curl_add_timecondition(data, req); + break; + + case H1_HD_CUSTOM: + result = Curl_add_custom_headers(data, FALSE, httpversion, req); + break; + + case H1_HD_CONTENT: + result = http_add_content_hds(data, req, httpversion, httpreq); + break; + + case H1_HD_CONNECTION: { + result = http_add_connection_hd(data, req); + break; + } + + case H1_HD_LAST: + result = curlx_dyn_addn(req, STRCONST("\r\n")); + break; + } + return result; +} + +/* + * Curl_http() gets called from the generic multi_do() function when an HTTP + * request is to be performed. This creates and sends a properly constructed + * HTTP request. + */ +CURLcode Curl_http(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + Curl_HttpReq httpreq; + const char *method; + struct dynbuf req; + unsigned char httpversion; + size_t hd_id; + + /* 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 + the rest of the request in the PERFORM phase. */ + *done = TRUE; /* initialize a dynamic send-buffer */ curlx_dyn_init(&req, DYN_HTTP_REQUEST); - /* make sure the header buffer is reset - if there are leftovers from a previous transfer */ curlx_dyn_reset(&data->state.headerb); - /* add the main request stuff */ - /* GET/HEAD/POST/PUT */ - result = curlx_dyn_addf(&req, "%s ", request); - if(!result) - result = http_target(data, conn, &req); - if(result) { - curlx_dyn_free(&req); - goto fail; + if(!data->conn->bits.reuse) { + result = http_check_new_conn(data); + if(result) + goto out; } -#ifndef CURL_DISABLE_ALTSVC - if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) { - altused = aprintf("Alt-Used: %s:%d\r\n", - conn->conn_to_host.name, conn->conn_to_port); - if(!altused) { - curlx_dyn_free(&req); - return CURLE_OUT_OF_MEMORY; - } - } -#endif - result = - curlx_dyn_addf(&req, - " HTTP/%s\r\n" /* HTTP version */ - "%s" /* host */ - "%s" /* proxyuserpwd */ - "%s" /* userpwd */ - "%s" /* range */ - "%s" /* user agent */ - "%s" /* accept */ - "%s" /* TE: */ - "%s" /* accept-encoding */ - "%s" /* referer */ - "%s" /* Proxy-Connection */ - "%s" /* transfer-encoding */ - "%s",/* Alt-Used */ - - httpstring, - (data->state.aptr.host ? data->state.aptr.host : ""), -#ifndef CURL_DISABLE_PROXY - data->state.aptr.proxyuserpwd ? - data->state.aptr.proxyuserpwd : "", -#else - "", -#endif - data->state.aptr.userpwd ? data->state.aptr.userpwd : "", - (data->state.use_range && data->state.aptr.rangeline) ? - data->state.aptr.rangeline : "", - (data->set.str[STRING_USERAGENT] && - *data->set.str[STRING_USERAGENT] && - data->state.aptr.uagent) ? - data->state.aptr.uagent : "", - p_accept ? p_accept : "", - data->state.aptr.te ? data->state.aptr.te : "", - (data->set.str[STRING_ENCODING] && - *data->set.str[STRING_ENCODING] && - data->state.aptr.accept_encoding) ? - data->state.aptr.accept_encoding : "", - (data->state.referer && data->state.aptr.ref) ? - data->state.aptr.ref : "" /* Referer: */, -#ifndef CURL_DISABLE_PROXY - (conn->bits.httpproxy && - !conn->bits.tunnel_proxy && - !Curl_checkheaders(data, STRCONST("Proxy-Connection")) && - !Curl_checkProxyheaders(data, conn, - STRCONST("Proxy-Connection"))) ? - "Proxy-Connection: Keep-Alive\r\n":"", -#else - "", -#endif - te, - altused ? altused : "" - ); + /* Add collecting of headers written to client. For a new connection, + * we might have done that already, but reuse + * or multiplex needs it here as well. */ + result = Curl_headers_init(data); + if(result) + goto out; - /* clear userpwd and proxyuserpwd to avoid reusing old credentials - * from reused connections */ - Curl_safefree(data->state.aptr.userpwd); -#ifndef CURL_DISABLE_PROXY - Curl_safefree(data->state.aptr.proxyuserpwd); -#endif - free(altused); + data->state.http_hd_te = FALSE; + data->state.http_hd_upgrade = FALSE; + data->state.http_hd_h2_settings = FALSE; - if(result) { - curlx_dyn_free(&req); - goto fail; - } + /* what kind of request do we need to send? */ + Curl_http_method(data, &method, &httpreq); - if(!Curl_conn_is_ssl(conn, FIRSTSOCKET) && (httpversion < 20) && - (data->state.http_neg.wanted & CURL_HTTP_V2x) && - data->state.http_neg.h2_upgrade) { - /* append HTTP2 upgrade magic stuff to the HTTP request if it is not done - over SSL */ - result = Curl_http2_request_upgrade(&req, data); - if(result) { - curlx_dyn_free(&req); - return result; + /* select host to send */ + result = http_set_aptr_host(data); + if(!result) { + /* setup the authentication headers, how that method and host are known */ + char *pq = NULL; + if(data->state.up.query) { + pq = aprintf("%s?%s", data->state.up.path, data->state.up.query); + if(!pq) + return CURLE_OUT_OF_MEMORY; } + result = Curl_http_output_auth(data, data->conn, method, httpreq, + (pq ? pq : data->state.up.path), FALSE); + free(pq); } + if(result) + goto out; - result = http_cookies(data, conn, &req); -#ifndef CURL_DISABLE_WEBSOCKETS - if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS)) - result = Curl_ws_request(data, &req); -#endif + result = http_useragent(data); + if(result) + goto out; + + /* Setup input reader, resume information and ranges */ + result = set_reader(data, httpreq); if(!result) - result = Curl_add_timecondition(data, &req); + result = http_resume(data, httpreq); if(!result) - result = Curl_add_custom_headers(data, FALSE, httpversion, &req); + result = http_range(data, httpreq); + if(result) + goto out; - if(!result) { - /* req_send takes ownership of the 'req' memory on success */ - result = http_req_complete(data, &req, httpversion, httpreq); - if(!result) - result = Curl_req_send(data, &req, httpversion); + httpversion = http_request_version(data); + /* Add request line and all headers to `req` */ + for(hd_id = 0; hd_id <= H1_HD_LAST; ++hd_id) { + result = http_add_hd(data, &req, (http_hd_t)hd_id, + httpversion, method, httpreq); + if(result) + goto out; } - curlx_dyn_free(&req); - if(result) - goto fail; + + /* setup variables for the upcoming transfer and send */ + Curl_xfer_setup_sendrecv(data, FIRSTSOCKET, -1); + result = Curl_req_send(data, &req, httpversion); 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 */ data->req.upload_chunky = FALSE; -fail: + +out: if(CURLE_TOO_LARGE == result) failf(data, "HTTP request too large"); + + /* clear userpwd and proxyuserpwd to avoid reusing old credentials + * from reused connections */ + Curl_safefree(data->state.aptr.userpwd); +#ifndef CURL_DISABLE_PROXY + Curl_safefree(data->state.aptr.proxyuserpwd); +#endif + curlx_dyn_free(&req); return result; } diff --git a/lib/http.h b/lib/http.h index 50279aedfa..67ef17f5b9 100644 --- a/lib/http.h +++ b/lib/http.h @@ -106,7 +106,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, bool is_connect, CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, bool is_connect, struct dynhds *hds); -void Curl_http_method(struct Curl_easy *data, struct connectdata *conn, +void Curl_http_method(struct Curl_easy *data, const char **method, Curl_HttpReq *); /* protocol-specific functions set up to be called by the main engine */ diff --git a/lib/http2.c b/lib/http2.c index 7c95c2ff7e..e2cdc9181f 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -1820,8 +1820,9 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req, return result; } + data->state.http_hd_upgrade = TRUE; + data->state.http_hd_h2_settings = TRUE; result = curlx_dyn_addf(req, - "Connection: Upgrade, HTTP2-Settings\r\n" "Upgrade: %s\r\n" "HTTP2-Settings: %s\r\n", NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64); diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index 850b7ae112..c62db499ca 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -775,7 +775,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) } } - Curl_http_method(data, conn, &method, &httpreq); + Curl_http_method(data, &method, &httpreq); payload_hash = parse_content_sha_hdr(data, curlx_str(&provider1), diff --git a/lib/http_proxy.c b/lib/http_proxy.c index df51484eb9..2d742856ce 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -162,11 +162,6 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data, we will force length zero then */ hd_name_eq(name, namelen, STRCONST("Content-Length:"))) ; - else if(data->state.aptr.te && - /* when asking for Transfer-Encoding, do not pass on a custom - Connection: */ - hd_name_eq(name, namelen, STRCONST("Connection:"))) - ; else if((httpversion >= 20) && hd_name_eq(name, namelen, STRCONST("Transfer-Encoding:"))) /* HTTP/2 and HTTP/3 do not support chunked requests */ diff --git a/lib/url.c b/lib/url.c index 20161cac44..b52267cb67 100644 --- a/lib/url.c +++ b/lib/url.c @@ -320,7 +320,6 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_safefree(data->state.aptr.uagent); Curl_safefree(data->state.aptr.userpwd); Curl_safefree(data->state.aptr.accept_encoding); - Curl_safefree(data->state.aptr.te); Curl_safefree(data->state.aptr.rangeline); Curl_safefree(data->state.aptr.ref); Curl_safefree(data->state.aptr.host); diff --git a/lib/urldata.h b/lib/urldata.h index a35512cf7a..cf181af641 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1122,7 +1122,6 @@ struct UrlState { #ifndef CURL_DISABLE_RTSP char *rtsp_transport; #endif - char *te; /* TE: request header */ /* transfer credentials */ char *user; @@ -1178,6 +1177,11 @@ struct UrlState { internal use and the user does not have ownership of the handle. */ BIT(http_ignorecustom); /* ignore custom method from now */ +#ifndef CURL_DISABLE_HTTP + BIT(http_hd_te); /* Added HTTP header TE: */ + BIT(http_hd_upgrade); /* Added HTTP header Upgrade: */ + BIT(http_hd_h2_settings); /* Added HTTP header H2Settings: */ +#endif }; /* diff --git a/lib/ws.c b/lib/ws.c index b6434b0b80..6070cd6d0d 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -1023,11 +1023,6 @@ CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req) MUST include the "websocket" keyword. */ "Upgrade", "websocket" }, - { - /* The request MUST contain a |Connection| header field whose value - MUST include the "Upgrade" token. */ - "Connection", "Upgrade", - }, { /* The request MUST include a header field with the name |Sec-WebSocket-Version|. The value of this header field MUST be @@ -1043,7 +1038,7 @@ CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req) "Sec-WebSocket-Key", NULL, } }; - heads[3].val = &keyval[0]; + heads[2].val = &keyval[0]; /* 16 bytes random */ result = Curl_rand(data, (unsigned char *)rand, sizeof(rand)); @@ -1065,6 +1060,7 @@ CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req) heads[i].val); } } + data->state.http_hd_upgrade = TRUE; k->upgr101 = UPGR101_WS; return result; } diff --git a/tests/data/test1122 b/tests/data/test1122 index 073c59acbf..d0ce3ffaf0 100644 --- a/tests/data/test1122 +++ b/tests/data/test1122 @@ -66,8 +66,8 @@ GET /%TESTNUMBER HTTP/1.1 Host: %HOSTIP:%HTTPPORT User-Agent: curl/%VERSION Accept: */* -Connection: TE TE: gzip +Connection: TE diff --git a/tests/data/test1123 b/tests/data/test1123 index 08320aa599..a01aae69be 100644 --- a/tests/data/test1123 +++ b/tests/data/test1123 @@ -176,8 +176,8 @@ GET /%TESTNUMBER HTTP/1.1 Host: %HOSTIP:%HTTPPORT User-Agent: curl/%VERSION Accept: */* -Connection: TE TE: gzip +Connection: TE diff --git a/tests/data/test1124 b/tests/data/test1124 index ee969cda0a..fdc172398f 100644 --- a/tests/data/test1124 +++ b/tests/data/test1124 @@ -67,8 +67,8 @@ GET /%TESTNUMBER HTTP/1.1 Host: %HOSTIP:%HTTPPORT User-Agent: curl/%VERSION Accept: */* -Connection: TE TE: gzip +Connection: TE diff --git a/tests/data/test1125 b/tests/data/test1125 index 248432f3ce..3b4d58b2e6 100644 --- a/tests/data/test1125 +++ b/tests/data/test1125 @@ -66,8 +66,8 @@ GET /%TESTNUMBER HTTP/1.1 Host: %HOSTIP:%HTTPPORT User-Agent: curl/%VERSION Accept: */* -Connection: close, TE TE: gzip +Connection: close, TE diff --git a/tests/data/test1170 b/tests/data/test1170 index f3ade56b7e..d6271714bd 100644 --- a/tests/data/test1170 +++ b/tests/data/test1170 @@ -66,8 +66,8 @@ GET /%TESTNUMBER HTTP/1.1 Host: %HOSTIP:%HTTPPORT User-Agent: curl/%VERSION Accept: */* -Connection: TE TE: gzip +Connection: TE diff --git a/tests/data/test1171 b/tests/data/test1171 index 14d660176b..486aadb94b 100644 --- a/tests/data/test1171 +++ b/tests/data/test1171 @@ -66,8 +66,8 @@ GET /%TESTNUMBER HTTP/1.1 Host: %HOSTIP:%HTTPPORT User-Agent: curl/%VERSION Accept: */* -Connection: TE TE: gzip +Connection: TE diff --git a/tests/data/test1277 b/tests/data/test1277 index 7b163a47e7..52a0f675a2 100644 --- a/tests/data/test1277 +++ b/tests/data/test1277 @@ -183,9 +183,9 @@ GET /%TESTNUMBER HTTP/1.1 Host: %HOSTIP:%HTTPPORT User-Agent: curl/%VERSION Accept: */* -Connection: TE TE: gzip Accept-Encoding: xxx +Connection: TE diff --git a/tests/data/test1546 b/tests/data/test1546 index 0ec7093f71..b83b7ee95f 100644 --- a/tests/data/test1546 +++ b/tests/data/test1546 @@ -52,8 +52,8 @@ GET /%TESTNUMBER HTTP/1.1 Host: %HOSTIP:%HTTPPORT User-Agent: curl/%VERSION Accept: */* -Connection: TE TE: gzip +Connection: TE diff --git a/tests/data/test1704 b/tests/data/test1704 index 0401f796b4..324551a068 100644 --- a/tests/data/test1704 +++ b/tests/data/test1704 @@ -52,9 +52,9 @@ GET /%TESTNUMBER HTTP/1.1 Host: %HOSTIP:%HTTPPORT User-Agent: curl/%VERSION Accept: */* -Connection: Upgrade, HTTP2-Settings Upgrade: h2c HTTP2-Settings: AAMAAABkAAQAAQAAAAIAAAAA +Connection: Upgrade, HTTP2-Settings diff --git a/tests/data/test1800 b/tests/data/test1800 index b1fd8021a6..222437e8e1 100644 --- a/tests/data/test1800 +++ b/tests/data/test1800 @@ -47,9 +47,9 @@ GET /%TESTNUMBER HTTP/1.1 Host: %HOSTIP:%HTTPPORT User-Agent: curl/%VERSION Accept: */* -Connection: Upgrade, HTTP2-Settings Upgrade: %H2CVER HTTP2-Settings: AAMAAABkAAQAAQAAAAIAAAAA +Connection: Upgrade, HTTP2-Settings diff --git a/tests/data/test2300 b/tests/data/test2300 index 18a38e04d0..8b28774686 100644 --- a/tests/data/test2300 +++ b/tests/data/test2300 @@ -53,9 +53,9 @@ Host: %HOSTIP:%HTTPPORT User-Agent: curl/%VERSION Accept: */* Upgrade: websocket -Connection: Upgrade Sec-WebSocket-Version: 13 Sec-WebSocket-Key: NDMyMTUzMjE2MzIxNzMyMQ== +Connection: Upgrade diff --git a/tests/data/test2301 b/tests/data/test2301 index e3cab99f15..1b968c7672 100644 --- a/tests/data/test2301 +++ b/tests/data/test2301 @@ -58,9 +58,9 @@ Host: %HOSTIP:%HTTPPORT User-Agent: webbie-sox/3 Accept: */* Upgrade: websocket -Connection: Upgrade Sec-WebSocket-Version: 13 Sec-WebSocket-Key: NDMyMTUzMjE2MzIxNzMyMQ== +Connection: Upgrade %hex[%8a%00]hex% diff --git a/tests/data/test2302 b/tests/data/test2302 index b1d11fcf6e..8e2d34877f 100644 --- a/tests/data/test2302 +++ b/tests/data/test2302 @@ -59,9 +59,9 @@ Host: %HOSTIP:%HTTPPORT User-Agent: webbie-sox/3 Accept: */* Upgrade: websocket -Connection: Upgrade Sec-WebSocket-Version: 13 Sec-WebSocket-Key: NDMyMTUzMjE2MzIxNzMyMQ== +Connection: Upgrade %hex[%8a%808321]hex% diff --git a/tests/data/test2303 b/tests/data/test2303 index d90d23dc9e..8ad45cbeca 100644 --- a/tests/data/test2303 +++ b/tests/data/test2303 @@ -49,9 +49,9 @@ Host: %HOSTIP:%HTTPPORT User-Agent: webbie-sox/3 Accept: */* Upgrade: websocket -Connection: Upgrade Sec-WebSocket-Version: 13 Sec-WebSocket-Key: NDMyMTUzMjE2MzIxNzMyMQ== +Connection: Upgrade # 22 == CURLE_HTTP_RETURNED_ERROR diff --git a/tests/data/test2304 b/tests/data/test2304 index 21fac7a0f0..8ed75d96a9 100644 --- a/tests/data/test2304 +++ b/tests/data/test2304 @@ -58,9 +58,9 @@ Host: %HOSTIP:%HTTPPORT User-Agent: websocket/%TESTNUMBER Accept: */* Upgrade: websocket -Connection: Upgrade Sec-WebSocket-Version: 13 Sec-WebSocket-Key: NDMyMTUzMjE2MzIxNzMyMQ== +Connection: Upgrade diff --git a/tests/data/test387 b/tests/data/test387 index d0e5c95ed4..a6d2ab03fc 100644 --- a/tests/data/test387 +++ b/tests/data/test387 @@ -43,8 +43,8 @@ GET /%TESTNUMBER HTTP/1.1 Host: %HOSTIP:%HTTPPORT User-Agent: curl/%VERSION Accept: */* -Connection: TE TE: gzip +Connection: TE diff --git a/tests/data/test418 b/tests/data/test418 index 6031b867b8..fa502f1b56 100644 --- a/tests/data/test418 +++ b/tests/data/test418 @@ -51,8 +51,8 @@ GET /%TESTNUMBER HTTP/1.1 Host: %HOSTIP:%HTTPPORT User-Agent: curl/%VERSION Accept: */* -Connection: TE TE: gzip +Connection: TE