Merge branch 'v2.6.x' of github.com:m6w6/ext-http
authorMichael Wallner <mike@php.net>
Wed, 27 Apr 2016 09:34:04 +0000 (11:34 +0200)
committerMichael Wallner <mike@php.net>
Wed, 27 Apr 2016 09:34:04 +0000 (11:34 +0200)
1  2 
config9.m4
src/php_http_client_curl.c
src/php_http_header.c
src/php_http_header.h
src/php_http_message.c
tests/helper/server.inc
tests/propertyproxy001.phpt

diff --cc config9.m4
Simple merge
index e56f8d416b6a6e1701922f504945d1e09a2bcd41,5dd7a54b9995e76f5a9b0e2c4e0e1ee5b24445ee..cc62c1528e4944639b6ca4c78e87febb9fc3cea5
@@@ -156,14 -161,31 +161,31 @@@ static php_resource_factory_ops_t php_h
        php_http_curle_dtor
  };
  
 -static void *php_http_curlm_ctor(void *opaque, void *init_arg TSRMLS_DC)
 +static void *php_http_curlm_ctor(void *opaque, void *init_arg)
  {
-       return curl_multi_init();
+       php_http_client_curl_handle_t *curl = calloc(1, sizeof(*curl));
+       if (!(curl->multi = curl_multi_init())) {
+               free(curl);
+               return NULL;
+       }
+       if (!(curl->share = curl_share_init())) {
+               curl_multi_cleanup(curl->multi);
+               free(curl);
+               return NULL;
+       }
+       curl_share_setopt(curl->share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
+       curl_share_setopt(curl->share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
+       return curl;
  }
  
 -static void php_http_curlm_dtor(void *opaque, void *handle TSRMLS_DC)
 +static void php_http_curlm_dtor(void *opaque, void *handle)
  {
-       curl_multi_cleanup(handle);
+       php_http_client_curl_handle_t *curl = handle;
+       curl_share_cleanup(curl->share);
+       curl_multi_cleanup(curl->multi);
+       free(handle);
  }
  
  static php_resource_factory_ops_t php_http_curlm_resource_factory_ops = {
@@@ -665,9 -657,10 +687,9 @@@ static void php_http_curlm_responsehand
        php_http_curle_storage_t *st, *err = NULL;
        php_http_client_enqueue_t *enqueue;
        php_http_client_curl_t *curl = context->ctx;
 -      TSRMLS_FETCH_FROM_CTX(context->ts);
  
        do {
-               CURLMsg *msg = curl_multi_info_read(curl->handle, &remaining);
+               CURLMsg *msg = curl_multi_info_read(curl->handle->multi, &remaining);
  
                if (msg && CURLMSG_DONE == msg->msg) {
                        if (CURLE_OK != msg->data.result) {
@@@ -749,10 -743,10 +771,10 @@@ static void php_http_curlm_timeout_call
                (void) socket;
                (void) action;
  
-               while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle, CURL_SOCKET_TIMEOUT, 0, &curl->unfinished)));
+               while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle->multi, CURL_SOCKET_TIMEOUT, 0, &curl->unfinished)));
  
                if (CURLM_OK != rc) {
 -                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s",  curl_multi_strerror(rc));
 +                      php_error_docref(NULL, E_WARNING, "%s",  curl_multi_strerror(rc));
                }
  
                php_http_curlm_responsehandler(context);
@@@ -769,11 -763,12 +791,11 @@@ static void php_http_curlm_event_callba
  #endif
        if (curl->useevents) {
                CURLMcode rc = CURLM_OK;
 -              TSRMLS_FETCH_FROM_CTX(context->ts);
  
-               while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle, socket, etoca(action), &curl->unfinished)));
+               while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle->multi, socket, etoca(action), &curl->unfinished)));
  
                if (CURLM_OK != rc) {
 -                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", curl_multi_strerror(rc));
 +                      php_error_docref(NULL, E_WARNING, "%s", curl_multi_strerror(rc));
                }
  
                php_http_curlm_responsehandler(context);
@@@ -1661,9 -1663,10 +1687,9 @@@ static ZEND_RESULT_CODE php_http_curlm_
  {
        php_http_client_t *client = userdata;
        php_http_client_curl_t *curl = client->ctx;
-       CURLM *ch = curl->handle;
+       CURLM *ch = curl->handle->multi;
        HashTable tmp_ht;
        char **bl = NULL;
 -      TSRMLS_FETCH_FROM_CTX(client->ts);
  
        /* array of char *, ending with a NULL */
        if (value && Z_TYPE_P(value) != IS_NULL) {
@@@ -1897,10 -1896,12 +1924,11 @@@ static ZEND_RESULT_CODE php_http_client
  static php_http_client_curl_handler_t *php_http_client_curl_handler_init(php_http_client_t *h, php_resource_factory_t *rf)
  {
        void *handle;
+       php_http_client_curl_t *curl = h->ctx;
        php_http_client_curl_handler_t *handler;
 -      TSRMLS_FETCH_FROM_CTX(h->ts);
  
 -      if (!(handle = php_resource_factory_handle_ctor(rf, NULL TSRMLS_CC))) {
 -              php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to initialize curl handle->multi");
 +      if (!(handle = php_resource_factory_handle_ctor(rf, NULL))) {
 +              php_error_docref(NULL, E_WARNING, "Failed to initialize curl handle");
                return NULL;
        }
  
@@@ -2166,20 -2163,21 +2197,21 @@@ static php_resource_factory_t *create_r
                char *id_str = NULL;
                size_t id_len;
                int port = url->port ? url->port : 80;
 -              zval **zport;
 +              zval *zport;
+               php_persistent_handle_factory_t *phf = h->rf->data;
  
 -              if (SUCCESS == zend_hash_find(enqueue->options, ZEND_STRS("port"), (void *) &zport)) {
 -                      zval *zcpy = php_http_ztyp(IS_LONG, *zport);
 +              if ((zport = zend_hash_str_find(enqueue->options, ZEND_STRL("port")))) {
 +                      zend_long lport = zval_get_long(zport);
  
 -                      if (Z_LVAL_P(zcpy)) {
 -                              port = Z_LVAL_P(zcpy);
 +                      if (lport > 0) {
 +                              port = lport;
                        }
 -                      zval_ptr_dtor(&zcpy);
                }
  
-               id_len = spprintf(&id_str, 0, "%s:%d", STR_PTR(url->host), port);
 -              id_len = spprintf(&id_str, 0, "%.*s:%s:%d", (int) phf->ident.len, phf->ident.str, STR_PTR(url->host), port);
 -              pf = php_persistent_handle_concede(NULL, ZEND_STRL("http\\Client\\Curl\\Request"), id_str, id_len, NULL, NULL TSRMLS_CC);
 -              efree(id_str);
++              id_len = spprintf(&id_str, 0, "%.*s:%s:%d", (int) phf->ident->len, phf->ident->val, STR_PTR(url->host), port);
 +              id = php_http_cs2zs(id_str, id_len);
 +              pf = php_persistent_handle_concede(NULL, PHP_HTTP_G->client.curl.driver.request_name, id, NULL, NULL);
 +              zend_string_release(id);
        }
  
        if (pf) {
@@@ -2218,21 -2217,22 +2250,22 @@@ static ZEND_RESULT_CODE php_http_client
        enqueue->opaque = handler;
        enqueue->dtor = queue_dtor;
  
-       if (CURLM_OK == (rs = curl_multi_add_handle(curl->handle, handler->handle))) {
-               zend_llist_add_element(&h->requests, enqueue);
-               ++curl->unfinished;
-               if (h->callback.progress.func && SUCCESS == php_http_client_getopt(h, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, enqueue->request, &progress)) {
-                       progress->info = "start";
-                       h->callback.progress.func(h->callback.progress.arg, h, &handler->queue, progress);
-                       progress->started = 1;
-               }
-               return SUCCESS;
-       } else {
+       if (CURLM_OK != (rs = curl_multi_add_handle(curl->handle->multi, handler->handle))) {
+               php_http_client_curl_handler_dtor(handler);
 -              php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not enqueue request: %s", curl_multi_strerror(rs));
 +              php_error_docref(NULL, E_WARNING, "Could not enqueue request: %s", curl_multi_strerror(rs));
                return FAILURE;
        }
+       zend_llist_add_element(&h->requests, enqueue);
+       ++curl->unfinished;
+       if (h->callback.progress.func && SUCCESS == php_http_client_getopt(h, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, enqueue->request, &progress)) {
+               progress->info = "start";
+               h->callback.progress.func(h->callback.progress.arg, h, &handler->queue, progress);
+               progress->started = 1;
+       }
+       return SUCCESS;
  }
  
  static ZEND_RESULT_CODE php_http_client_curl_dequeue(php_http_client_t *h, php_http_client_enqueue_t *enqueue)
        CURLMcode rs;
        php_http_client_curl_t *curl = h->ctx;
        php_http_client_curl_handler_t *handler = enqueue->opaque;
 -      TSRMLS_FETCH_FROM_CTX(h->ts);
  
        php_http_client_curl_handler_clear(handler);
-       if (CURLM_OK == (rs = curl_multi_remove_handle(curl->handle, handler->handle))) {
+       if (CURLM_OK == (rs = curl_multi_remove_handle(curl->handle->multi, handler->handle))) {
                zend_llist_del_element(&h->requests, handler->handle, (int (*)(void *, void *)) compare_queue);
                return SUCCESS;
        } else {
@@@ -2496,14 -2504,14 +2529,15 @@@ php_http_client_ops_t *php_http_client_
  
  PHP_MINIT_FUNCTION(http_client_curl)
  {
+       curl_version_info_data *info;
        php_http_options_t *options;
 -      php_http_client_driver_t driver = {
 -              ZEND_STRL("curl"),
 -              &php_http_client_curl_ops
 -      };
  
 -      if (SUCCESS != php_http_client_driver_add(&driver)) {
 +      PHP_HTTP_G->client.curl.driver.driver_name = zend_string_init(ZEND_STRL("curl"), 1);
 +      PHP_HTTP_G->client.curl.driver.client_name = zend_string_init(ZEND_STRL("http\\Client\\Curl"), 1);
 +      PHP_HTTP_G->client.curl.driver.request_name = zend_string_init(ZEND_STRL("http\\Client\\Curl\\Request"), 1);
 +      PHP_HTTP_G->client.curl.driver.client_ops = &php_http_client_curl_ops;
 +
 +      if (SUCCESS != php_http_client_driver_add(&PHP_HTTP_G->client.curl.driver)) {
                return FAILURE;
        }
  
                options->getter = php_http_option_get;
                options->setter = php_http_curlm_set_option;
  
 -              php_http_curlm_options_init(options TSRMLS_CC);
 +              php_http_curlm_options_init(options);
        }
  
+       if ((info = curl_version_info(CURLVERSION_NOW))) {
+               /*
+                * Feature constants
+                */
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "FEATURES", info->features, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "IPV6", CURL_VERSION_IPV6, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "KERBEROS4", CURL_VERSION_KERBEROS4, CONST_CS|CONST_PERSISTENT);
+ #if PHP_HTTP_CURL_VERSION(7,40,0)
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "KERBEROS5", CURL_VERSION_KERBEROS5, CONST_CS|CONST_PERSISTENT);
+ #endif
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "SSL", CURL_VERSION_SSL, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "LIBZ", CURL_VERSION_LIBZ, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "NTLM", CURL_VERSION_NTLM, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "GSSNEGOTIATE", CURL_VERSION_GSSNEGOTIATE, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "ASYNCHDNS", CURL_VERSION_ASYNCHDNS, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "SPNEGO", CURL_VERSION_SPNEGO, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "LARGEFILE", CURL_VERSION_LARGEFILE, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "IDN", CURL_VERSION_IDN, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "SSPI", CURL_VERSION_SSPI, CONST_CS|CONST_PERSISTENT);
+ #if PHP_HTTP_CURL_VERSION(7,38,0)
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "GSSAPI", CURL_VERSION_GSSAPI, CONST_CS|CONST_PERSISTENT);
+ #endif
+ #if PHP_HTTP_CURL_VERSION(7,21,4)
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "TLSAUTH_SRP", CURL_VERSION_TLSAUTH_SRP, CONST_CS|CONST_PERSISTENT);
+ #endif
+ #if PHP_HTTP_CURL_VERSION(7,22,0)
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "NTLM_WB", CURL_VERSION_NTLM_WB, CONST_CS|CONST_PERSISTENT);
+ #endif
+ #if PHP_HTTP_CURL_VERSION(7,33,0)
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "HTTP2", CURL_VERSION_HTTP2, CONST_CS|CONST_PERSISTENT);
+ #endif
+ #if PHP_HTTP_CURL_VERSION(7,40,0)
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "UNIX_SOCKETS", CURL_VERSION_UNIX_SOCKETS, CONST_CS|CONST_PERSISTENT);
+ #endif
+ #if PHP_HTTP_CURL_VERSION(7,47,0)
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "PSL", CURL_VERSION_PSL, CONST_CS|CONST_PERSISTENT);
+ #endif
+               /*
+                * Version constants
+                */
+               REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl", "VERSIONS", curl_version(), CONST_CS|CONST_PERSISTENT);
+ #if CURLVERSION_NOW >= 0
+               REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "CURL", (char *) info->version, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "SSL", (char *) info->ssl_version, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "LIBZ", (char *) info->libz_version, CONST_CS|CONST_PERSISTENT);
+ # if CURLVERSION_NOW >= 1
+               REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "ARES", (char *) info->ares, CONST_CS|CONST_PERSISTENT);
+ #  if CURLVERSION_NOW >= 2
+               REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "IDN", (char *) info->libidn, CONST_CS|CONST_PERSISTENT);
+ #  endif
+ # endif
+ #endif
+       }
        /*
        * HTTP Protocol Version Constants
        */
index 8242a7c526ed9cb52212e67aee6b7eaf59ca3b1b,2f808c346e2811a55a06a52d837e11aacbfbc5b2..91204c13fa7169f1ab25de7c9dc8dbbea6840e16
@@@ -36,90 -36,80 +36,137 @@@ ZEND_RESULT_CODE php_http_header_parse(
        return rs == PHP_HTTP_HEADER_PARSER_STATE_FAILURE ? FAILURE : SUCCESS;
  }
  
 -void php_http_header_to_callback(HashTable *headers, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg TSRMLS_DC)
 +void php_http_header_to_callback(HashTable *headers, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg)
  {
 -      HashPosition pos;
 -      php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
 -      zval **header;
++      php_http_arrkey_t key;
++      zval *header;
 -      FOREACH_HASH_KEYVAL(pos, headers, key, header) {
 -              if (key.type == HASH_KEY_IS_STRING) {
 -                      php_http_header_to_callback_ex(key.str, *header, crlf, cb, cb_arg TSRMLS_CC);
++      ZEND_HASH_FOREACH_KEY_VAL(headers, key.h, key.key, header)
++      {
++              if (key.key) {
++                      php_http_header_to_callback_ex(key.key->val, header, crlf, cb, cb_arg);
+               }
+       }
++      ZEND_HASH_FOREACH_END();
++/*
++<<<<<<< HEAD
 +      php_http_arrkey_t key;
 +      zval *header, *single_header;
 +
 +      ZEND_HASH_FOREACH_KEY_VAL(headers, key.h, key.key, header)
 +      {
 +              if (key.key) {
 +                      if (zend_string_equals_literal(key.key, "Set-Cookie") && Z_TYPE_P(header) == IS_ARRAY) {
 +                              ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(header), single_header)
 +                              {
 +                                      if (Z_TYPE_P(single_header) == IS_ARRAY) {
 +                                              php_http_cookie_list_t *cookie = php_http_cookie_list_from_struct(NULL, single_header);
 +
 +                                              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 {
 +                                              zend_string *zs = php_http_header_value_to_string(single_header);
 +
 +                                              cb(cb_arg, crlf ? "Set-Cookie: %s" PHP_HTTP_CRLF : "Set-Cookie: %s", zs->val);
 +                                              zend_string_release(zs);
 +                                      }
 +                              }
 +                              ZEND_HASH_FOREACH_END();
 +                      } else {
 +                              zend_string *zs = php_http_header_value_to_string(header);
 +
 +                              cb(cb_arg, crlf ? "%s: %s" PHP_HTTP_CRLF : "%s: %s", key.key->val, zs->val);
 +                              zend_string_release(zs);
 +                      }
-               }
-       }
-       ZEND_HASH_FOREACH_END();
++=======
++>>>>>>> 343738ad56eb70017704fdac57cf0d74da3d0f2e
++*/
  }
  
 -void php_http_header_to_string(php_http_buffer_t *str, HashTable *headers TSRMLS_DC)
 +void php_http_header_to_string(php_http_buffer_t *str, HashTable *headers)
  {
 -      php_http_header_to_callback(headers, 1, (php_http_pass_format_callback_t) php_http_buffer_appendf, str TSRMLS_CC);
 +      php_http_header_to_callback(headers, 1, (php_http_pass_format_callback_t) php_http_buffer_appendf, str);
  }
  
 -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)
++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)
+ {
 -      HashPosition pos;
 -      zval **aval, *tmp;
++      zval *aval;
++      zend_string *str;
+       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);
++              ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(val), aval)
++              {
++                      php_http_header_to_callback_ex(key, aval, crlf, cb, cb_arg);
+               }
++              ZEND_HASH_FOREACH_END();
++              break;
++
++      case IS_TRUE:
++              cb(cb_arg, "%s: true%s", key, crlf ? PHP_HTTP_CRLF:"");
+               break;
 -      case IS_BOOL:
 -              cb(cb_arg, "%s: %s%s", key, Z_BVAL_P(val) ? "true" : "false", crlf ? PHP_HTTP_CRLF:"");
++      case IS_FALSE:
++              cb(cb_arg, "%s: false%s", key, 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);
++              str = zval_get_string(val);
++              cb(cb_arg, "%s: %s%s", key, str->val, crlf ? PHP_HTTP_CRLF:"");
++              zend_string_release(str);
+               break;
+       }
+ }
 -void php_http_header_to_string_ex(php_http_buffer_t *str, const char *key, zval *val TSRMLS_DC)
++void php_http_header_to_string_ex(php_http_buffer_t *str, const char *key, zval *val)
+ {
 -      php_http_header_to_callback_ex(key, val, 1, (php_http_pass_format_callback_t) php_http_buffer_appendf, str TSRMLS_CC);
++      php_http_header_to_callback_ex(key, val, 1, (php_http_pass_format_callback_t) php_http_buffer_appendf, str);
+ }
 -zval *php_http_header_value_to_string(zval *header TSRMLS_DC)
 +zend_string *php_http_header_value_array_to_string(zval *header)
  {
 -      zval *ret;
 -
 -      if (Z_TYPE_P(header) == IS_BOOL) {
 -              MAKE_STD_ZVAL(ret);
 -              ZVAL_STRING(ret, Z_BVAL_P(header) ? "true" : "false", 1);
 -      } else if (Z_TYPE_P(header) == IS_ARRAY) {
 -              zval **val;
 -              HashPosition pos;
 -              php_http_buffer_t str;
 -
 -              php_http_buffer_init(&str);
 -              MAKE_STD_ZVAL(ret);
 -              FOREACH_VAL(pos,header, val) {
 -                      zval *strval = php_http_header_value_to_string(*val TSRMLS_CC);
 -
 -                      php_http_buffer_appendf(&str, str.used ? ", %s":"%s", Z_STRVAL_P(strval));
 -                      zval_ptr_dtor(&strval);
 -              }
 -              php_http_buffer_fix(&str);
 -              ZVAL_STRINGL(ret, str.data, str.used, 0);
 -      } else  {
 -              ret = php_http_zsep(1, IS_STRING, header);
 +      zval *val;
 +      php_http_buffer_t str;
 +
 +      php_http_buffer_init(&str);
 +      ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(header), val)
 +      {
 +              zend_string *zs = php_http_header_value_to_string(val);
 +
 +              php_http_buffer_appendf(&str, str.used ? ", %s":"%s", zs->val);
 +              zend_string_release(zs);
 +      }
 +      ZEND_HASH_FOREACH_END();
 +      php_http_buffer_fix(&str);
 +
 +      return php_http_cs2zs(str.data, str.used);
 +}
 +
 +zend_string *php_http_header_value_to_string(zval *header)
 +{
 +      switch (Z_TYPE_P(header)) {
 +      case IS_TRUE:
 +              return zend_string_init(ZEND_STRL("true"), 0);
 +      case IS_FALSE:
 +              return zend_string_init(ZEND_STRL("false"), 0);
 +      case IS_ARRAY:
 +              return php_http_header_value_array_to_string(header);
 +      default:
 +              return zval_get_string(header);
        }
 +}
  
 -      return ret;
 +static zend_class_entry *php_http_header_class_entry;
 +zend_class_entry *php_http_header_get_class_entry(void)
 +{
 +      return php_http_header_class_entry;
  }
  
  ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader___construct, 0, 0, 0)
index 1ba9ef59d534df94ec58721a74bfe1b4cd506879,9a57d8f475f255019645c932e8028c4118a2f5ea..00ef264209a6947d228c88130d5f4a68bf3630a0
  
  #include "php_http_info.h"
  
 -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 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);
  
 -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 void php_http_header_to_callback(HashTable *headers, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg);
++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);
 +PHP_HTTP_API void php_http_header_to_string(php_http_buffer_t *str, HashTable *headers);
++PHP_HTTP_API void php_http_header_to_string_ex(php_http_buffer_t *str, const char *key, zval *val);
  
 -PHP_HTTP_API zval *php_http_header_value_to_string(zval *header TSRMLS_DC);
 +PHP_HTTP_API zend_string *php_http_header_value_to_string(zval *header);
 +PHP_HTTP_API zend_string *php_http_header_value_array_to_string(zval *header);
  
 -PHP_HTTP_API zend_class_entry *php_http_header_class_entry;
 +PHP_HTTP_API zend_class_entry *php_http_header_get_class_entry(void);
  PHP_MINIT_FUNCTION(http_header);
  
  #endif
index 8f49e92156ba61f46fb12eb037231f39a3f64f23,2b3090a0509ce4f1ccc861afdedb8d9d9a802678..56499bb8158d69db9f3e1f14c3d0cd849786fcea
@@@ -1242,21 -1281,32 +1242,32 @@@ static PHP_METHOD(HttpMessage, addHeade
  {
        zval *zvalue;
        char *name_str;
 -      int name_len;
 +      size_t name_len;
  
 -      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);
 +      if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &name_str, &name_len, &zvalue)) {
 +              php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
                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);
++              zend_string *hstr, *vstr = php_http_header_value_to_string(zvalue);
++              zval tmp, *header;
  
                PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
  
-               Z_TRY_ADDREF_P(zvalue);
-               if ((header = php_http_message_header(obj->message, name, name_len))) {
+               if ((name_len != lenof("Set-Cookie") && strcmp(name, "Set-Cookie"))
 -              &&      (header = php_http_message_header(obj->message, name, name_len, 1))) {
 -                      zval *tmp;
++              &&      (hstr = php_http_message_header_string(obj->message, name, name_len))) {
+                       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))) {
++                      size_t hdr_len = spprintf(&hdr_str, 0, "%s, %s", hstr->val, vstr->val);
++
++                      ZVAL_STR(&tmp, php_http_cs2zs(hdr_str, hdr_len));
++                      zend_symtable_str_update(&obj->message->hdrs, name, name_len, &tmp);
++                      zend_string_release(hstr);
++                      zend_string_release(vstr);
++              } else if ((header = php_http_message_header(obj->message, name, name_len))) {
                        convert_to_array(header);
-                       zend_hash_next_index_insert(Z_ARRVAL_P(header), zvalue);
 -                      zend_hash_next_index_insert(Z_ARRVAL_P(header), &cpy, sizeof(void *), NULL);
 -                      zval_ptr_dtor(&header);
++                      ZVAL_STR(&tmp, vstr);
++                      zend_hash_next_index_insert(Z_ARRVAL_P(header), &tmp);
                } else {
-                       zend_symtable_str_update(&obj->message->hdrs, name, name_len, zvalue);
 -                      zend_symtable_update(&obj->message->hdrs, name, name_len + 1, &cpy, sizeof(void *), NULL);
++                      ZVAL_STR(&tmp, vstr);
++                      zend_symtable_str_update(&obj->message->hdrs, name, name_len, &tmp);
                }
                efree(name);
        }
Simple merge
Simple merge