diff --git a/docs/libcurl/curl_easy_header.3 b/docs/libcurl/curl_easy_header.3 index e4ce0dd31f..9aaa9d767c 100644 --- a/docs/libcurl/curl_easy_header.3 +++ b/docs/libcurl/curl_easy_header.3 @@ -94,7 +94,9 @@ but it might have a different case. The data \fBvalue\fP field points to, comes exactly as delivered over the network but with leading and trailing whitespace and newlines stripped -off. The `value` data is nul-terminated. +off. The `value` data is nul-terminated. For legacy HTTP/1 "folded headers", +this API provides the full single value in an unfolded manner with a single +whitespace between the lines. \fBamount\fP is how many headers using this name that exist, within the origin and request scope asked for. diff --git a/lib/headers.c b/lib/headers.c index 226c696be6..b83557d77c 100644 --- a/lib/headers.c +++ b/lib/headers.c @@ -216,6 +216,54 @@ static CURLcode namevalue(char *header, size_t hlen, unsigned int type, return CURLE_OK; } +static CURLcode append_value(struct Curl_easy *data, const char *value, + size_t vlen) /* length of the incoming header */ +{ + struct Curl_header_store *hs; + struct Curl_header_store *newhs; + size_t olen; /* length of the old value */ + size_t offset; + DEBUGASSERT(data->state.prevhead); + hs = data->state.prevhead; + olen = strlen(hs->value); + offset = hs->value - hs->buffer; + + /* skip all trailing space letters */ + while(vlen && ISSPACE(value[vlen - 1])) + vlen--; + + /* save only one leading space */ + while((vlen > 1) && ISSPACE(value[0]) && ISSPACE(value[1])) { + vlen--; + value++; + } + + /* since this header block might move in the realloc below, it needs to + first be unlinked from the list and then re-added again after the + realloc */ + Curl_llist_remove(&data->state.httphdrs, &hs->node, NULL); + + newhs = Curl_saferealloc(hs, sizeof(*hs) + vlen + olen + 1); + if(!newhs) + return CURLE_OUT_OF_MEMORY; + /* ->name' and ->value point into ->buffer (to keep the header allocation + in a single memory block), which now potentially have moved. Adjust + them. */ + newhs->name = newhs->buffer; + newhs->value = &newhs->buffer[offset]; + + /* put the data at the end of the previous data, not the newline */ + memcpy(&newhs->value[olen], value, vlen); + newhs->value[olen + vlen] = 0; /* zero terminate at newline */ + + /* insert this node into the list of headers */ + Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail, + newhs, &newhs->node); + data->state.prevhead = newhs; + return CURLE_OK; +} + + /* * Curl_headers_push() gets passed a full HTTP header to store. It gets called * immediately before the header callback. The header is CRLF terminated. @@ -242,6 +290,10 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, } hlen = end - header + 1; + if((header[0] == ' ') || (header[0] == '\t')) + /* line folding, append value to the previous header's value */ + return append_value(data, header, hlen); + hs = calloc(1, sizeof(*hs) + hlen); if(!hs) return CURLE_OUT_OF_MEMORY; @@ -260,7 +312,7 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, /* insert this node into the list of headers */ Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail, hs, &hs->node); - + data->state.prevhead = hs; return CURLE_OK; fail: free(hs); diff --git a/lib/http.c b/lib/http.c index b215307dca..765fe0bd31 100644 --- a/lib/http.c +++ b/lib/http.c @@ -3799,11 +3799,16 @@ static CURLcode verify_header(struct Curl_easy *data) if(k->headerline < 2) /* the first "header" is the status-line and it has no colon */ return CURLE_OK; - ptr = memchr(header, ':', hlen); - if(!ptr) { - /* this is bad, bail out */ - failf(data, "Header without colon"); - return CURLE_WEIRD_SERVER_REPLY; + if(((header[0] == ' ') || (header[0] == '\t')) && k->headerline > 2) + /* line folding, can't happen on line 2 */ + ; + else { + ptr = memchr(header, ':', hlen); + if(!ptr) { + /* this is bad, bail out */ + failf(data, "Header without colon"); + return CURLE_WEIRD_SERVER_REPLY; + } } return CURLE_OK; } diff --git a/lib/urldata.h b/lib/urldata.h index 14770574d8..145836f57c 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1423,6 +1423,7 @@ struct UrlState { headers */ struct Curl_llist httphdrs; /* received headers */ struct curl_header headerout; /* for external purposes */ + struct Curl_header_store *prevhead; /* the latest added header */ #endif trailers_state trailers_state; /* whether we are sending trailers and what stage are we at */ diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc index cc486a5c9e..9718bb978e 100644 --- a/tests/data/Makefile.inc +++ b/tests/data/Makefile.inc @@ -160,7 +160,7 @@ test1240 test1241 test1242 test1243 test1244 test1245 test1246 test1247 \ test1248 test1249 test1250 test1251 test1252 test1253 test1254 test1255 \ test1256 test1257 test1258 test1259 test1260 test1261 test1262 test1263 \ test1264 test1265 test1266 test1267 test1268 test1269 test1270 test1271 \ -test1272 test1273 \ +test1272 test1273 test1274 \ \ test1280 test1281 test1282 test1283 test1284 test1285 test1286 test1287 \ test1288 test1289 test1290 test1291 test1292 test1293 test1294 test1295 \ diff --git a/tests/data/test1274 b/tests/data/test1274 new file mode 100644 index 0000000000..ae29f4899e --- /dev/null +++ b/tests/data/test1274 @@ -0,0 +1,65 @@ + + + +HTTP +HTTP GET +header line folding + + + +# +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/ + fake + folded +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Content-Length: 6 +Connection: close + +-foo- + + + +# +# Client-side + + +http + + +HTTP header line folding + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER -D log/out%TESTNUMBER + + + +# +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/ + fake + folded +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Content-Length: 6 +Connection: close + + + + diff --git a/tests/data/test1940 b/tests/data/test1940 index 682916b709..8e05538b5c 100644 --- a/tests/data/test1940 +++ b/tests/data/test1940 @@ -12,6 +12,9 @@ HTTP/1.1 200 OK Date: Thu, 09 Nov 2010 14:49:00 GMT Server: test with trailing space Content-Type: text/html +Fold: is + folding a + line Content-Length: 0 Set-Cookie: onecookie=data; Set-Cookie: secondcookie=2data; @@ -44,7 +47,7 @@ http://%HOSTIP:%HTTPPORT/%TESTNUMBER # Verify data after the test has been "shot" - + Date == Thu, 09 Nov 2010 14:49:00 GMT Server == test with trailing space Content-Type == text/html @@ -53,6 +56,7 @@ http://%HOSTIP:%HTTPPORT/%TESTNUMBER - Set-Cookie == onecookie=data; (0/3) - Set-Cookie == secondcookie=2data; (1/3) - Set-Cookie == cookie3=data3; (2/3) + Fold == is folding a line diff --git a/tests/libtest/lib1940.c b/tests/libtest/lib1940.c index 922cd425a0..93bf0a2da7 100644 --- a/tests/libtest/lib1940.c +++ b/tests/libtest/lib1940.c @@ -32,6 +32,7 @@ static const char *show[]={ "location", "set-cookie", "silly-thing", + "fold", NULL };