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