summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2019-02-18 08:14:52 +0100
committerDaniel Stenberg <daniel@haxx.se>2019-02-18 11:09:30 +0100
commit52dfba161d08a93728f4d15453a6cb30ab40b55a (patch)
treebdb773e26bff79e272d8e4ff309331c335444244
parente6522522f96ad96b459e608c6cdcd46a32099b5b (diff)
downloadcurl-bagder/empty-header-threadsafe.tar.gz
http: make adding a blank header thread-safebagder/empty-header-threadsafe
Previously the function would edit the provided header in-place when a semicolon is used to signify an empty header. This made it impossible to use the same set of custom headers in multiple threads simultaneously. This approach now makes a local copy when it needs to edit the string. Reported-by: d912e3 on github Fixes #3578 Closes #3579
-rw-r--r--lib/http.c34
1 files changed, 21 insertions, 13 deletions
diff --git a/lib/http.c b/lib/http.c
index f59e90a23..3ad677f5f 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -1790,9 +1790,16 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
}
else {
if(*(--ptr) == ';') {
- /* send no-value custom header if terminated by semicolon */
- *ptr = ':';
- semicolonp = ptr;
+ /* copy the source */
+ semicolonp = strdup(headers->data);
+ if(!semicolonp) {
+ Curl_add_buffer_free(&req_buffer);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ /* put a colon where the semicolon is */
+ semicolonp[ptr - headers->data] = ':';
+ /* point at the colon */
+ optr = &semicolonp [ptr - headers->data];
}
}
ptr = optr;
@@ -1808,36 +1815,37 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
if(*ptr || semicolonp) {
/* only send this if the contents was non-blank or done special */
CURLcode result = CURLE_OK;
+ char *compare = semicolonp ? semicolonp : headers->data;
if(conn->allocptr.host &&
/* a Host: header was sent already, don't pass on any custom Host:
header as that will produce *two* in the same request! */
- checkprefix("Host:", headers->data))
+ checkprefix("Host:", compare))
;
else if(data->set.httpreq == HTTPREQ_POST_FORM &&
/* this header (extended by formdata.c) is sent later */
- checkprefix("Content-Type:", headers->data))
+ checkprefix("Content-Type:", compare))
;
else if(data->set.httpreq == HTTPREQ_POST_MIME &&
/* this header is sent later */
- checkprefix("Content-Type:", headers->data))
+ checkprefix("Content-Type:", compare))
;
else if(conn->bits.authneg &&
/* while doing auth neg, don't allow the custom length since
we will force length zero then */
- checkprefix("Content-Length:", headers->data))
+ checkprefix("Content-Length:", compare))
;
else if(conn->allocptr.te &&
/* when asking for Transfer-Encoding, don't pass on a custom
Connection: */
- checkprefix("Connection:", headers->data))
+ checkprefix("Connection:", compare))
;
else if((conn->httpversion == 20) &&
- checkprefix("Transfer-Encoding:", headers->data))
+ checkprefix("Transfer-Encoding:", compare))
/* HTTP/2 doesn't support chunked requests */
;
- else if((checkprefix("Authorization:", headers->data) ||
- checkprefix("Cookie:", headers->data)) &&
+ else if((checkprefix("Authorization:", compare) ||
+ checkprefix("Cookie:", compare)) &&
/* be careful of sending this potentially sensitive header to
other hosts */
(data->state.this_is_a_follow &&
@@ -1846,10 +1854,10 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
!strcasecompare(data->state.first_host, conn->host.name)))
;
else {
- result = Curl_add_bufferf(&req_buffer, "%s\r\n", headers->data);
+ result = Curl_add_bufferf(&req_buffer, "%s\r\n", compare);
}
if(semicolonp)
- *semicolonp = ';'; /* put back the semicolon */
+ free(semicolonp);
if(result)
return result;
}