From 18b04b480ebc41841b2004cc11797eda40fb3958 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Tue, 1 Oct 2013 11:07:55 +0200 Subject: [PATCH] Fixed bug #61548 --- NEWS | 4 + ext/standard/http_fopen_wrapper.c | 61 ++++++------- ext/standard/tests/http/bug61548.phpt | 118 ++++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 30 deletions(-) create mode 100644 ext/standard/tests/http/bug61548.phpt diff --git a/NEWS b/NEWS index 4dc7ef7c18..9bb60327fa 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,10 @@ PHP NEWS . Fixed bug #65721 (configure script broken in 5.5.4 and 5.4.20 when enabling imap). (ryotakatsuki at gmail dot com) +- Standard: + . Fixed bug #61548 (content-type must appear at the end of headers for 201 + Location to work in http). (Mike) + 19 Sep 2013, PHP 5.4.20 - Core: diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index b8676bbba4..4605e7494f 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -84,6 +84,30 @@ #define HTTP_WRAPPER_HEADER_INIT 1 #define HTTP_WRAPPER_REDIRECTED 2 +static inline void strip_header(char *header_bag, char *lc_header_bag, + const char *lc_header_name) +{ + char *lc_header_start = strstr(lc_header_bag, lc_header_name); + char *header_start = header_bag + (lc_header_start - lc_header_bag); + + if (lc_header_start + && (lc_header_start == lc_header_bag || *(lc_header_start-1) == '\n') + ) { + char *lc_eol = strchr(lc_header_start, '\n'); + char *eol = header_start + (lc_eol - lc_header_start); + + if (lc_eol) { + size_t eollen = strlen(lc_eol); + + memmove(lc_header_start, lc_eol+1, eollen); + memmove(header_start, eol+1, eollen); + } else { + *lc_header_start = '\0'; + *header_start = '\0'; + } + } +} + php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context, int redirect_max, int flags STREAMS_DC TSRMLS_DC) /* {{{ */ { php_stream *stream = NULL; @@ -425,40 +449,17 @@ finish: if (tmp && strlen(tmp) > 0) { char *s; - if (!header_init) { /* Remove post headers for redirects */ - int l = strlen(tmp); - char *s2, *tmp_c = estrdup(tmp); - - php_strtolower(tmp_c, l); - if ((s = strstr(tmp_c, "content-length:"))) { - if ((s2 = memchr(s, '\n', tmp_c + l - s))) { - int b = tmp_c + l - 1 - s2; - memmove(tmp, tmp + (s2 + 1 - tmp_c), b); - memmove(tmp_c, s2 + 1, b); - - } else { - tmp[s - tmp_c] = *s = '\0'; - } - l = strlen(tmp_c); - } - if ((s = strstr(tmp_c, "content-type:"))) { - if ((s2 = memchr(s, '\n', tmp_c + l - s))) { - memmove(tmp, tmp + (s2 + 1 - tmp_c), tmp_c + l - 1 - s2); - } else { - tmp[s - tmp_c] = '\0'; - } - } - - efree(tmp_c); - tmp_c = php_trim(tmp, strlen(tmp), NULL, 0, NULL, 3 TSRMLS_CC); - efree(tmp); - tmp = tmp_c; - } - user_headers = estrdup(tmp); /* Make lowercase for easy comparison against 'standard' headers */ php_strtolower(tmp, strlen(tmp)); + + if (!header_init) { + /* strip POST headers on redirect */ + strip_header(user_headers, tmp, "content-length:"); + strip_header(user_headers, tmp, "content-type:"); + } + if ((s = strstr(tmp, "user-agent:")) && (s == tmp || *(s-1) == '\r' || *(s-1) == '\n' || *(s-1) == '\t' || *(s-1) == ' ')) { diff --git a/ext/standard/tests/http/bug61548.phpt b/ext/standard/tests/http/bug61548.phpt new file mode 100644 index 0000000000..138b15a338 --- /dev/null +++ b/ext/standard/tests/http/bug61548.phpt @@ -0,0 +1,118 @@ +--TEST-- +Bug #61548 (content-type must appear at the end of headers) +--INI-- +allow_url_fopen=1 +--SKIPIF-- + +--FILE-- + [ + 'method' => 'POST', + 'header' => $header, + 'follow_location' => true, + ], + ]; + + $ctx = stream_context_create($options); + + $responses = [ + "data://text/plain,HTTP/1.1 201\r\nLocation: /foo\r\n\r\n", + "data://text/plain,HTTP/1.1 200\r\nConnection: close\r\n\r\n", + ]; + $pid = http_server('tcp://127.0.0.1:12342', $responses, $output); + + $fd = fopen('http://127.0.0.1:12342/', 'rb', false, $ctx); + fseek($output, 0, SEEK_SET); + echo stream_get_contents($output); + + http_server_kill($pid); +} + +do_test("First:1\nSecond:2\nContent-type: text/plain"); +do_test("First:1\nSecond:2\nContent-type: text/plain\n"); +do_test("First:1\nSecond:2\nContent-type: text/plain\nThird:"); +do_test("First:1\nContent-type:text/plain\nSecond:2"); +do_test("First:1\nContent-type:text/plain\nSecond:2\n"); +do_test("First:1\nContent-type:text/plain\nSecond:2\nThird:"); + +?> +Done +--EXPECT-- +POST / HTTP/1.0 +Host: 127.0.0.1:12342 +First:1 +Second:2 +Content-type: text/plain + +GET /foo HTTP/1.0 +Host: 127.0.0.1:12342 +First:1 +Second:2 + + +POST / HTTP/1.0 +Host: 127.0.0.1:12342 +First:1 +Second:2 +Content-type: text/plain + +GET /foo HTTP/1.0 +Host: 127.0.0.1:12342 +First:1 +Second:2 + + +POST / HTTP/1.0 +Host: 127.0.0.1:12342 +First:1 +Second:2 +Content-type: text/plain +Third: + +GET /foo HTTP/1.0 +Host: 127.0.0.1:12342 +First:1 +Second:2 +Third: + +POST / HTTP/1.0 +Host: 127.0.0.1:12342 +First:1 +Content-type:text/plain +Second:2 + +GET /foo HTTP/1.0 +Host: 127.0.0.1:12342 +First:1 +Second:2 + +POST / HTTP/1.0 +Host: 127.0.0.1:12342 +First:1 +Content-type:text/plain +Second:2 + +GET /foo HTTP/1.0 +Host: 127.0.0.1:12342 +First:1 +Second:2 + +POST / HTTP/1.0 +Host: 127.0.0.1:12342 +First:1 +Content-type:text/plain +Second:2 +Third: + +GET /foo HTTP/1.0 +Host: 127.0.0.1:12342 +First:1 +Second:2 +Third: + +Done + -- 2.40.0