allow setting multiple headers with the same name
authorMichael Wallner <mike@php.net>
Thu, 31 Mar 2016 13:12:19 +0000 (15:12 +0200)
committerMichael Wallner <mike@php.net>
Thu, 31 Mar 2016 13:12:19 +0000 (15:12 +0200)
Only http\Message::addHeader will transform the headers into a single
one where the values will be concatenated by comma.

Closes github issue #34

src/php_http_header.c
src/php_http_header.h
src/php_http_message.c
tests/message005.phpt
tests/message011.phpt
tests/propertyproxy001.phpt

index 41601df..2f808c3 100644 (file)
@@ -38,39 +38,13 @@ ZEND_RESULT_CODE php_http_header_parse(const char *header, size_t length, HashTa
 
 void php_http_header_to_callback(HashTable *headers, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg TSRMLS_DC)
 {
-       HashPosition pos1, pos2;
+       HashPosition pos;
        php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-       zval **header, **single_header;
+       zval **header;
 
-       FOREACH_HASH_KEYVAL(pos1, headers, key, header) {
+       FOREACH_HASH_KEYVAL(pos, headers, key, header) {
                if (key.type == HASH_KEY_IS_STRING) {
-                       if (key.len == sizeof("Set-Cookie") && !strcasecmp(key.str, "Set-Cookie") && Z_TYPE_PP(header) == IS_ARRAY) {
-                               FOREACH_VAL(pos2, *header, single_header) {
-                                       if (Z_TYPE_PP(single_header) == IS_ARRAY) {
-                                               php_http_cookie_list_t *cookie = php_http_cookie_list_from_struct(NULL, *single_header TSRMLS_CC);
-
-                                               if (cookie) {
-                                                       char *buf;
-                                                       size_t len;
-
-                                                       php_http_cookie_list_to_string(cookie, &buf, &len);
-                                                       cb(cb_arg, crlf ? "Set-Cookie: %s" PHP_HTTP_CRLF : "Set-Cookie: %s", buf);
-                                                       php_http_cookie_list_free(&cookie);
-                                                       efree(buf);
-                                               }
-                                       } else {
-                                               zval *strval = php_http_header_value_to_string(*single_header TSRMLS_CC);
-
-                                               cb(cb_arg, crlf ? "Set-Cookie: %s" PHP_HTTP_CRLF : "Set-Cookie: %s", Z_STRVAL_P(strval));
-                                               zval_ptr_dtor(&strval);
-                                       }
-                               }
-                       } else {
-                               zval *strval = php_http_header_value_to_string(*header TSRMLS_CC);
-
-                               cb(cb_arg, crlf ? "%s: %s" PHP_HTTP_CRLF : "%s: %s", key.str, Z_STRVAL_P(strval));
-                               zval_ptr_dtor(&strval);
-                       }
+                       php_http_header_to_callback_ex(key.str, *header, crlf, cb, cb_arg TSRMLS_CC);
                }
        }
 }
@@ -80,6 +54,35 @@ void php_http_header_to_string(php_http_buffer_t *str, HashTable *headers TSRMLS
        php_http_header_to_callback(headers, 1, (php_http_pass_format_callback_t) php_http_buffer_appendf, str TSRMLS_CC);
 }
 
+void php_http_header_to_callback_ex(const char *key, zval *val, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg TSRMLS_DC)
+{
+       HashPosition pos;
+       zval **aval, *tmp;
+
+       switch (Z_TYPE_P(val)) {
+       case IS_ARRAY:
+               FOREACH_VAL(pos, val, aval) {
+                       php_http_header_to_callback_ex(key, *aval, crlf, cb, cb_arg TSRMLS_CC);
+               }
+               break;
+
+       case IS_BOOL:
+               cb(cb_arg, "%s: %s%s", key, Z_BVAL_P(val) ? "true" : "false", crlf ? PHP_HTTP_CRLF:"");
+               break;
+
+       default:
+               tmp = php_http_ztyp(IS_STRING, val);
+               cb(cb_arg, "%s: %s%s", key, Z_STRVAL_P(tmp), crlf ? PHP_HTTP_CRLF:"");
+               zval_ptr_dtor(&tmp);
+               break;
+       }
+}
+
+void php_http_header_to_string_ex(php_http_buffer_t *str, const char *key, zval *val TSRMLS_DC)
+{
+       php_http_header_to_callback_ex(key, val, 1, (php_http_pass_format_callback_t) php_http_buffer_appendf, str TSRMLS_CC);
+}
+
 zval *php_http_header_value_to_string(zval *header TSRMLS_DC)
 {
        zval *ret;
index a2baecb..9a57d8f 100644 (file)
@@ -18,7 +18,9 @@
 PHP_HTTP_API ZEND_RESULT_CODE php_http_header_parse(const char *header, size_t length, HashTable *headers, php_http_info_callback_t callback_func, void **callback_data TSRMLS_DC);
 
 PHP_HTTP_API void php_http_header_to_callback(HashTable *headers, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg TSRMLS_DC);
+PHP_HTTP_API void php_http_header_to_callback_ex(const char *key, zval *val, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg TSRMLS_DC);
 PHP_HTTP_API void php_http_header_to_string(php_http_buffer_t *str, HashTable *headers TSRMLS_DC);
+PHP_HTTP_API void php_http_header_to_string_ex(php_http_buffer_t *str, const char *key, zval *val TSRMLS_DC);
 
 PHP_HTTP_API zval *php_http_header_value_to_string(zval *header TSRMLS_DC);
 
index 3ea40e1..2b3090a 100644 (file)
@@ -1286,17 +1286,27 @@ static PHP_METHOD(HttpMessage, addHeader)
        if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &name_str, &name_len, &zvalue)) {
                php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
                char *name = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1);
-               zval *header;
+               zval *header, *cpy = php_http_header_value_to_string(zvalue TSRMLS_CC);
 
                PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
 
-               Z_ADDREF_P(zvalue);
-               if ((header = php_http_message_header(obj->message, name, name_len, 0))) {
+               if ((name_len != lenof("Set-Cookie") && strcmp(name, "Set-Cookie"))
+               &&      (header = php_http_message_header(obj->message, name, name_len, 1))) {
+                       zval *tmp;
+                       char *hdr_str;
+                       size_t hdr_len = spprintf(&hdr_str, 0, "%s, %s", Z_STRVAL_P(header), Z_STRVAL_P(cpy));
+
+                       MAKE_STD_ZVAL(tmp);
+                       ZVAL_STRINGL(tmp, hdr_str, hdr_len, 0);
+                       zend_symtable_update(&obj->message->hdrs, name, name_len + 1, &tmp, sizeof(void *), NULL);
+                       zval_ptr_dtor(&header);
+                       zval_ptr_dtor(&cpy);
+               } else if ((header = php_http_message_header(obj->message, name, name_len, 0))) {
                        convert_to_array(header);
-                       zend_hash_next_index_insert(Z_ARRVAL_P(header), &zvalue, sizeof(void *), NULL);
+                       zend_hash_next_index_insert(Z_ARRVAL_P(header), &cpy, sizeof(void *), NULL);
                        zval_ptr_dtor(&header);
                } else {
-                       zend_symtable_update(&obj->message->hdrs, name, name_len + 1, &zvalue, sizeof(void *), NULL);
+                       zend_symtable_update(&obj->message->hdrs, name, name_len + 1, &cpy, sizeof(void *), NULL);
                }
                efree(name);
        }
index 6f7a546..0372091 100644 (file)
@@ -28,5 +28,8 @@ String: foobar
 
 ===
 UNKNOWN / HTTP/1.1
-Numbers: 1, 2, 3, 4.5
+Numbers: 1
+Numbers: 2
+Numbers: 3
+Numbers: 4.5
 DONE
index 53aa992..1b1a3f2 100644 (file)
@@ -23,18 +23,24 @@ class strval {
 
 $m = new http\Message;
 $m->addHeaders(array("foo"=>"bar","bar"=>"foo"));
-var_dump(array("Foo"=>"bar", "Bar"=>"foo") === $m->getHeaders());
+if (array("Foo"=>"bar", "Bar"=>"foo") !== $m->getHeaders()) {
+       var_dump($m->getHeaders());
+}
 $m->addHeaders(array("key"=>"val","more"=>"Stuff"));
-var_dump(array("Foo"=>"bar", "Bar"=>"foo","Key"=>"val","More"=>"Stuff") === $m->getHeaders());
+if (array("Foo"=>"bar", "Bar"=>"foo","Key"=>"val","More"=>"Stuff") !== $m->getHeaders()) {
+       var_dump($m->getHeaders());
+}
 $m = new http\Message("GET / HTTP/1.1");
 $m->addHeader("Accept", "text/html");
 $m->addHeader("Accept", "text/xml;q=0");
 $m->addHeader("Accept", "text/plain;q=0.5");
-var_dump(
+if (
                "GET / HTTP/1.1\r\n".
-               "Accept: text/html, text/xml;q=0, text/plain;q=0.5\r\n" ===
-               $m->toString()
-);
+               "Accept: text/html, text/xml;q=0, text/plain;q=0.5\r\n" !==
+               $m->toString()) {
+       var_dump($m->toString());
+}
+
 $m = new http\Message("HTTP/1.1 200 Ok");
 $m->addHeader("Bool", true);
 $m->addHeader("Int", 123);
@@ -42,7 +48,7 @@ $m->addHeader("Float", 1.23);
 $m->addHeader("Array", array(1,2,3));
 $m->addHeader("Object", new strval("test"));
 $m->addHeader("Set-Cookie",
-               array(
+               new http\Cookie(
                                array(
                                                "cookies" => array("foo" => "bar"),
                                                "expires" => date_create("2012-12-31 22:59:59 GMT")->format(
@@ -54,7 +60,7 @@ $m->addHeader("Set-Cookie",
 );
 $m->addHeader("Set-Cookie", "val=0");
 
-var_dump(
+if (
                "HTTP/1.1 200 Ok\r\n".
                "Bool: true\r\n".
                "Int: 123\r\n".
@@ -62,16 +68,13 @@ var_dump(
                "Array: 1, 2, 3\r\n".
                "Object: test\r\n".
                "Set-Cookie: foo=bar; path=/somewhere; expires=Mon, 31 Dec 2012 22:59:59 GMT; \r\n".
-               "Set-Cookie: val=0\r\n" ===
-               $m->toString()
-);
+               "Set-Cookie: val=0\r\n" !==
+               $m->toString()) {
+       var_dump($m->toString());
+}
 
 ?>
 Done
 --EXPECT--
 Test
-bool(true)
-bool(true)
-bool(true)
-bool(true)
 Done
index 1001f8e..8c5f23b 100644 (file)
@@ -92,7 +92,9 @@ bykey: 1
 by1ref: 2
 by2ref: 1
 byXref: 2
-bynext: 1, 2, 3
+bynext: 1
+bynext: 2
+bynext: 3
 
 DONE