diff --git a/lib/http.c b/lib/http.c index cd656cc8b5..0ce3b25bc3 100644 --- a/lib/http.c +++ b/lib/http.c @@ -107,9 +107,6 @@ static CURLcode http_header(struct Curl_easy *data, const char *hd, size_t hdlen); 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_TE(struct Curl_easy *data, struct dynbuf *req, int httpversion); @@ -1704,9 +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.http_connection_hd_added && - /* when Connection: header has already been added */ - 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")) @@ -2320,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; @@ -2390,19 +2387,11 @@ 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; } @@ -2645,6 +2634,38 @@ static CURLcode http_check_new_conn(struct Curl_easy *data) return CURLE_OK; } +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); + + if(custom && !custom_val) + return CURLE_OUT_OF_MEMORY; + + 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")); + + free(custom_val); + return result; +} + /* Header identifier in order we send them by default */ typedef enum { H1_HD_REQUEST, @@ -2668,19 +2689,19 @@ typedef enum { #endif H1_HD_UPGRADE, H1_HD_COOKIES, -#ifndef CURL_DISABLE_WEBSOCKETS - H1_HD_WEBSOCKET, -#endif H1_HD_CONDITIONALS, H1_HD_CUSTOM, - H1_HD_LAST /* not a header, just the last enum value for iterating */ + 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) + const char *method, + Curl_HttpReq httpreq) { CURLcode result = CURLE_OK; switch(id) { @@ -2733,26 +2754,8 @@ static CURLcode http_add_hd(struct Curl_easy *data, #ifdef HAVE_LIBZ 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")); - if(cptr) { - cptr = Curl_copy_header_value(cptr); - if(!cptr) - return CURLE_OUT_OF_MEMORY; - } - - data->state.http_connection_hd_added = TRUE; - if(cptr && *cptr) - result = curlx_dyn_addf(req, "Connection: %s, TE\r\nTE: gzip\r\n", - cptr); - else - result = curlx_dyn_add(req, "Connection: TE\r\nTE: gzip\r\n"); - - free(cptr); + data->state.http_hd_te = TRUE; + result = curlx_dyn_add(req, "TE: gzip\r\n"); } #endif break; @@ -2803,19 +2806,16 @@ static CURLcode http_add_hd(struct Curl_easy *data, 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; -#ifndef CURL_DISABLE_WEBSOCKETS - case H1_HD_WEBSOCKET: - if(data->conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS)) - result = Curl_ws_request(data, req); - break; -#endif - case H1_HD_CONDITIONALS: result = Curl_add_timecondition(data, req); break; @@ -2824,8 +2824,17 @@ static CURLcode http_add_hd(struct Curl_easy *data, result = Curl_add_custom_headers(data, FALSE, httpversion, req); break; - default: - DEBUGASSERT(0); + 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; @@ -2867,7 +2876,10 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) result = Curl_headers_init(data); if(result) goto out; - data->state.http_connection_hd_added = FALSE; + + data->state.http_hd_te = FALSE; + data->state.http_hd_upgrade = FALSE; + data->state.http_hd_h2_settings = FALSE; /* what kind of request do we need to send? */ Curl_http_method(data, &method, &httpreq); @@ -2904,17 +2916,15 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) 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); + 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; } - /* Add any final headers, including the last, empty line */ - result = http_req_complete(data, &req, httpversion, httpreq); - if(result) - goto out; - + /* 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) 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_proxy.c b/lib/http_proxy.c index 5b1d118584..2d742856ce 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -162,10 +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.http_connection_hd_added && - /* when Connection: header has already been added */ - 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/urldata.h b/lib/urldata.h index b5b1209d3f..cf181af641 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1177,7 +1177,11 @@ struct UrlState { internal use and the user does not have ownership of the handle. */ BIT(http_ignorecustom); /* ignore custom method from now */ - BIT(http_connection_hd_added); /* 'Connection: ' header already added */ +#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