fix connection: header to honor all headers that need to be present

This commit is contained in:
Stefan Eissing 2025-09-01 17:32:25 +02:00
parent 3e1ca39c70
commit 2367508f6c
No known key found for this signature in database
22 changed files with 98 additions and 91 deletions

View file

@ -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)

View file

@ -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);

View file

@ -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 */

View file

@ -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
};
/*

View file

@ -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;
}

View file

@ -66,8 +66,8 @@ GET /%TESTNUMBER HTTP/1.1
Host: %HOSTIP:%HTTPPORT
User-Agent: curl/%VERSION
Accept: */*
Connection: TE
TE: gzip
Connection: TE
</protocol>
</verify>

View file

@ -176,8 +176,8 @@ GET /%TESTNUMBER HTTP/1.1
Host: %HOSTIP:%HTTPPORT
User-Agent: curl/%VERSION
Accept: */*
Connection: TE
TE: gzip
Connection: TE
</protocol>
</verify>

View file

@ -67,8 +67,8 @@ GET /%TESTNUMBER HTTP/1.1
Host: %HOSTIP:%HTTPPORT
User-Agent: curl/%VERSION
Accept: */*
Connection: TE
TE: gzip
Connection: TE
</protocol>
</verify>

View file

@ -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
</protocol>
</verify>

View file

@ -66,8 +66,8 @@ GET /%TESTNUMBER HTTP/1.1
Host: %HOSTIP:%HTTPPORT
User-Agent: curl/%VERSION
Accept: */*
Connection: TE
TE: gzip
Connection: TE
</protocol>
</verify>

View file

@ -66,8 +66,8 @@ GET /%TESTNUMBER HTTP/1.1
Host: %HOSTIP:%HTTPPORT
User-Agent: curl/%VERSION
Accept: */*
Connection: TE
TE: gzip
Connection: TE
</protocol>
</verify>

View file

@ -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
</protocol>
</verify>

View file

@ -52,8 +52,8 @@ GET /%TESTNUMBER HTTP/1.1
Host: %HOSTIP:%HTTPPORT
User-Agent: curl/%VERSION
Accept: */*
Connection: TE
TE: gzip
Connection: TE
</protocol>
<errorcode>

View file

@ -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
</protocol>

View file

@ -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
</protocol>
</verify>

View file

@ -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
</protocol>
<errorcode>

View file

@ -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%
</protocol>

View file

@ -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%
</protocol>

View file

@ -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
</protocol>
# 22 == CURLE_HTTP_RETURNED_ERROR

View file

@ -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
</protocol>

View file

@ -43,8 +43,8 @@ GET /%TESTNUMBER HTTP/1.1
Host: %HOSTIP:%HTTPPORT
User-Agent: curl/%VERSION
Accept: */*
Connection: TE
TE: gzip
Connection: TE
</protocol>

View file

@ -51,8 +51,8 @@ GET /%TESTNUMBER HTTP/1.1
Host: %HOSTIP:%HTTPPORT
User-Agent: curl/%VERSION
Accept: */*
Connection: TE
TE: gzip
Connection: TE
</protocol>