diff options
author | Rowan Collins <rowan.collins@gmail.com> | 2016-03-30 22:12:03 +0000 |
---|---|---|
committer | Nikita Popov <nikic@php.net> | 2017-01-07 23:54:57 +0100 |
commit | 5146d9f8ac170d8ba7109370d732d56dc0777578 (patch) | |
tree | 2b7b7095f00f985a4d4aa65a1b9ae23a97f16f28 /ext/standard/http_fopen_wrapper.c | |
parent | a46bbdda2e082070bd67ecdc500d9671bf1ab823 (diff) | |
download | php-git-5146d9f8ac170d8ba7109370d732d56dc0777578.tar.gz |
http_fopen_wrapper.c - Handle HTTP headers with varying white space
The stream handler assumed all HTTP headers contained exactly one space,
but the standard says there may be zero or more. Should fix Bug #47021,
and any other edge cases caused by a web server sending unusual spacing,
e.g. the MIME type discovered from Content-Type: can no longer contain
leading whitespace.
We strip trailing whitespace from the headers added into
$http_response_header as well.
Diffstat (limited to 'ext/standard/http_fopen_wrapper.c')
-rw-r--r-- | ext/standard/http_fopen_wrapper.c | 56 |
1 files changed, 41 insertions, 15 deletions
diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index 0c996d8eae..1ed7dc2b80 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -755,8 +755,10 @@ finish: while (!body && !php_stream_eof(stream)) { size_t http_header_line_length; + if (php_stream_get_line(stream, http_header_line, HTTP_HEADER_BLOCK_SIZE, &http_header_line_length) && *http_header_line != '\n' && *http_header_line != '\r') { char *e = http_header_line + http_header_line_length - 1; + char *http_header_value; if (*e != '\n') { do { /* partial header */ if (php_stream_get_line(stream, http_header_line, HTTP_HEADER_BLOCK_SIZE, &http_header_line_length) == NULL) { @@ -770,26 +772,54 @@ finish: while (*e == '\n' || *e == '\r') { e--; } - http_header_line_length = e - http_header_line + 1; - http_header_line[http_header_line_length] = '\0'; - if (!strncasecmp(http_header_line, "Location: ", 10)) { + /* The primary definition of an HTTP header in RFC 7230 states: + * > Each header field consists of a case-insensitive field name followed + * > by a colon (":"), optional leading whitespace, the field value, and + * > optional trailing whitespace. */ + + /* Strip trailing whitespace */ + while (*e == ' ' || *e == '\t') { + e--; + } + + /* Terminate header line */ + e++; + *e = '\0'; + http_header_line_length = e - http_header_line; + + http_header_value = memchr(http_header_line, ':', http_header_line_length); + if (http_header_value) { + http_header_value++; /* Skip ':' */ + + /* Strip leading whitespace */ + while (http_header_value < e + && (*http_header_value == ' ' || *http_header_value == '\t')) { + http_header_value++; + } + } + + if (!strncasecmp(http_header_line, "Location:", sizeof("Location:")-1)) { if (context && (tmpzval = php_stream_context_get_option(context, "http", "follow_location")) != NULL) { follow_location = zval_is_true(tmpzval); - } else if (!((response_code >= 300 && response_code < 304) || 307 == response_code || 308 == response_code)) { + } else if (!((response_code >= 300 && response_code < 304) + || 307 == response_code || 308 == response_code)) { /* we shouldn't redirect automatically if follow_location isn't set and response_code not in (300, 301, 302, 303 and 307) see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.1 RFC 7238 defines 308: http://tools.ietf.org/html/rfc7238 */ follow_location = 0; } - strlcpy(location, http_header_line + 10, sizeof(location)); - } else if (!strncasecmp(http_header_line, "Content-Type: ", 14)) { - php_stream_notify_info(context, PHP_STREAM_NOTIFY_MIME_TYPE_IS, http_header_line + 14, 0); - } else if (!strncasecmp(http_header_line, "Content-Length: ", 16)) { - file_size = atoi(http_header_line + 16); + strlcpy(location, http_header_value, sizeof(location)); + } else if (!strncasecmp(http_header_line, "Content-Type:", sizeof("Content-Type:")-1)) { + php_stream_notify_info(context, PHP_STREAM_NOTIFY_MIME_TYPE_IS, http_header_value, 0); + } else if (!strncasecmp(http_header_line, "Content-Length:", sizeof("Content-Length")-1)) { + file_size = atoi(http_header_value); php_stream_notify_file_size(context, file_size, http_header_line, 0); - } else if (!strncasecmp(http_header_line, "Transfer-Encoding: chunked", sizeof("Transfer-Encoding: chunked"))) { + } else if ( + !strncasecmp(http_header_line, "Transfer-Encoding:", sizeof("Transfer-Encoding")-1) + && !strncasecmp(http_header_value, "Chunked", sizeof("Chunked")-1) + ) { /* create filter to decode response body */ if (!(options & STREAM_ONLY_GET_HEADERS)) { @@ -808,13 +838,9 @@ finish: } } - if (http_header_line[0] == '\0') { - body = 1; - } else { + { zval http_header; - ZVAL_STRINGL(&http_header, http_header_line, http_header_line_length); - zend_hash_next_index_insert(Z_ARRVAL(response_header), &http_header); } } else { |