]> granicus.if.org Git - php/commitdiff
Fixed bug #70470
authorBouke van der Bijl <boukevanderbijl@gmail.com>
Mon, 4 Sep 2017 14:28:29 +0000 (16:28 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Tue, 5 Sep 2017 14:23:32 +0000 (16:23 +0200)
NEWS
sapi/cli/php_cli_server.c
sapi/cli/tests/bug70470.phpt
sapi/cli/tests/emptyheader.phpt [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 09b0307aba90b44f6d9ac9d1d09304901df1457d..3bb2a0791a57c6f90d50a36772935f8789e62218 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,10 @@ PHP                                                                        NEWS
 - Core:
   . Fixed #75042 run-tests.php issues with EXTENSION block). (John Boehr)
 
+- CLI server:
+  . Fixed bug #70470 (Built-in server truncates headers spanning over TCP
+    packets). (bouk)
+
 - CURL:
   . Fixed bug #75093 (OpenSSL support not detected). (Remi)
 
index 5d820743b6cdab06136e7651a8622785fb5baab6..4d5324f36c0897e192f338f3ba694fecf9b4934d 100644 (file)
@@ -171,6 +171,9 @@ typedef struct php_cli_server_client {
        char *current_header_name;
        size_t current_header_name_len;
        unsigned int current_header_name_allocated:1;
+       char *current_header_value;
+       size_t current_header_value_len;
+       enum { HEADER_NONE=0, HEADER_FIELD, HEADER_VALUE } last_header_element;
        size_t post_read_offset;
        php_cli_server_request request;
        unsigned int content_sender_initialized:1;
@@ -1469,7 +1472,7 @@ static void normalize_vpath(char **retval, size_t *retval_len, const char *vpath
 #ifdef PHP_WIN32
        {
                char *p = decoded_vpath;
-               
+
                do {
                        if (*p == '\\') {
                                *p = '/';
@@ -1572,50 +1575,100 @@ static int php_cli_server_client_read_request_on_fragment(php_http_parser *parse
        return 0;
 }
 
-static int php_cli_server_client_read_request_on_header_field(php_http_parser *parser, const char *at, size_t length)
+static void php_cli_server_client_save_header(php_cli_server_client *client)
 {
-       php_cli_server_client *client = parser->data;
+       /* strip off the colon */
+       zend_string *orig_header_name = zend_string_init(client->current_header_name, client->current_header_name_len, 1);
+       char *lc_header_name = zend_str_tolower_dup(client->current_header_name, client->current_header_name_len);
+       zend_hash_str_add_ptr(&client->request.headers, lc_header_name, client->current_header_name_len, client->current_header_value);
+       zend_hash_add_ptr(&client->request.headers_original_case, orig_header_name, client->current_header_value);
+       efree(lc_header_name);
+       zend_string_release(orig_header_name);
+
        if (client->current_header_name_allocated) {
                pefree(client->current_header_name, 1);
                client->current_header_name_allocated = 0;
        }
-       client->current_header_name = (char *)at;
-       client->current_header_name_len = length;
-       return 0;
+       client->current_header_name = NULL;
+       client->current_header_name_len = 0;
+       client->current_header_value = NULL;
+       client->current_header_value_len = 0;
 }
 
-static int php_cli_server_client_read_request_on_header_value(php_http_parser *parser, const char *at, size_t length)
+static int php_cli_server_client_read_request_on_header_field(php_http_parser *parser, const char *at, size_t length)
 {
        php_cli_server_client *client = parser->data;
-       char *value = pestrndup(at, length, 1);
-       if (!value) {
-               return 1;
-       }
-       {
-               /* strip off the colon */
-               zend_string *orig_header_name = zend_string_init(client->current_header_name, client->current_header_name_len, 1);
-               char *lc_header_name = zend_str_tolower_dup(client->current_header_name, client->current_header_name_len);
-               zend_hash_str_add_ptr(&client->request.headers, lc_header_name, client->current_header_name_len, value);
-               zend_hash_add_ptr(&client->request.headers_original_case, orig_header_name, value);
-               efree(lc_header_name);
-               zend_string_release(orig_header_name);
+       switch (client->last_header_element) {
+       case HEADER_VALUE:
+               php_cli_server_client_save_header(client);
+               /* break missing intentionally */
+       case HEADER_NONE:
+               client->current_header_name = (char *)at;
+               client->current_header_name_len = length;
+               break;
+       case HEADER_FIELD:
+               if (client->current_header_name_allocated) {
+                       size_t new_length = client->current_header_name_len + length;
+                       client->current_header_name = perealloc(client->current_header_name, new_length + 1, 1);
+                       memcpy(client->current_header_name + client->current_header_name_len, at, length);
+                       client->current_header_name_len = new_length;
+               } else {
+                       size_t new_length = client->current_header_name_len + length;
+                       char* field = pemalloc(new_length + 1, 1);
+                       memcpy(field, client->current_header_name, client->current_header_name_len);
+                       memcpy(field + client->current_header_name_len, at, length);
+                       client->current_header_name = field;
+                       client->current_header_name_len = new_length;
+                       client->current_header_name_allocated = 1;
+               }
+               break;
        }
 
-       if (client->current_header_name_allocated) {
-               pefree(client->current_header_name, 1);
-               client->current_header_name_allocated = 0;
+       client->last_header_element = HEADER_FIELD;
+       return 0;
+}
+
+static int php_cli_server_client_read_request_on_header_value(php_http_parser *parser, const char *at, size_t length)
+{
+       php_cli_server_client *client = parser->data;
+       switch (client->last_header_element) {
+       case HEADER_FIELD:
+               client->current_header_value = pestrndup(at, length, 1);
+               client->current_header_value_len = length;
+               break;
+       case HEADER_VALUE:
+               {
+                       size_t new_length = client->current_header_value_len + length;
+                       client->current_header_value = perealloc(client->current_header_value, new_length + 1, 1);
+                       memcpy(client->current_header_value + client->current_header_value_len, at, length);
+                       client->current_header_value_len = new_length;
+               }
+               break;
+       case HEADER_NONE:
+               // can't happen
+               assert(0);
+               break;
        }
+       client->last_header_element = HEADER_VALUE;
        return 0;
 }
 
 static int php_cli_server_client_read_request_on_headers_complete(php_http_parser *parser)
 {
        php_cli_server_client *client = parser->data;
-       if (client->current_header_name_allocated) {
-               pefree(client->current_header_name, 1);
-               client->current_header_name_allocated = 0;
+       switch (client->last_header_element) {
+       case HEADER_NONE:
+               break;
+       case HEADER_FIELD:
+               client->current_header_value = pemalloc(1, 1);
+               *client->current_header_value = '\0';
+               client->current_header_value_len = 0;
+               /* break missing intentionally */
+       case HEADER_VALUE:
+               php_cli_server_client_save_header(client);
+               break;
        }
-       client->current_header_name = NULL;
+       client->last_header_element = HEADER_NONE;
        return 0;
 }
 
@@ -1791,9 +1844,14 @@ static int php_cli_server_client_ctor(php_cli_server_client *client, php_cli_ser
        }
        php_http_parser_init(&client->parser, PHP_HTTP_REQUEST);
        client->request_read = 0;
+
+       client->last_header_element = HEADER_NONE;
        client->current_header_name = NULL;
        client->current_header_name_len = 0;
        client->current_header_name_allocated = 0;
+       client->current_header_value = NULL;
+       client->current_header_value_len = 0;
+
        client->post_read_offset = 0;
        if (FAILURE == php_cli_server_request_ctor(&client->request)) {
                return FAILURE;
index acd8c98f05a69b340afdcfa9b17b3ab5f77d38ca..1e05f184b40bedbe653a69914d0c40d600883333 100644 (file)
@@ -4,8 +4,6 @@ Bug #70470 (Built-in server truncates headers spanning over TCP packets)
 <?php
 include "skipif.inc";
 ?>
---XFAIL--
-bug is not fixed yet
 --FILE--
 <?php
 include "php_cli_server.inc";
diff --git a/sapi/cli/tests/emptyheader.phpt b/sapi/cli/tests/emptyheader.phpt
new file mode 100644 (file)
index 0000000..9529730
--- /dev/null
@@ -0,0 +1,35 @@
+--TEST--
+Correctly handle split and empty header
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start("var_dump(getAllheaders());");
+
+$fp = fsockopen(PHP_CLI_SERVER_HOSTNAME, PHP_CLI_SERVER_PORT, $errno, $errmsg, 0.5);
+
+if (!$fp) {
+       die("connect failed: " . $errmsg);
+}
+
+fwrite($fp, "GET / HTTP/1.1\r\nUser-Agent\r\nAccept: */*\r\nReferer:\r\nHi\r\n\r\n");
+fflush($fp);
+while (!feof($fp)) {
+       echo fgets($fp);
+}
+fclose($fp);
+?>
+--EXPECTF--
+HTTP/1.1 200 OK
+%a
+array(3) {
+  ["User-AgentAccept"]=>
+  string(3) "*/*"
+  ["Referer"]=>
+  string(0) ""
+  ["Hi"]=>
+  string(0) ""
+}