Merge branch 'master' into phpng
authorMichael Wallner <mike@php.net>
Sun, 8 Feb 2015 15:23:13 +0000 (16:23 +0100)
committerMichael Wallner <mike@php.net>
Sun, 8 Feb 2015 20:44:03 +0000 (21:44 +0100)
Conflicts:
php_http_client.c
php_http_client.h
php_http_client_curl.c
php_http_env_response.c
php_http_info.c
php_http_message.c
php_http_object.c
php_http_object.h
php_http_url.c
php_http_url.h

19 files changed:
1  2 
config9.m4
gen_curlinfo.php
php_http_buffer.c
php_http_client.c
php_http_client.h
php_http_client_curl.c
php_http_cookie.c
php_http_encoding.c
php_http_env_response.c
php_http_info.c
php_http_info.h
php_http_message.c
php_http_message_body.c
php_http_message_parser.c
php_http_object.c
php_http_object.h
php_http_url.c
php_http_url.h
php_http_version.c

diff --cc config9.m4
Simple merge
Simple merge
@@@ -78,13 -72,9 +78,10 @@@ PHP_HTTP_BUFFER_API size_t php_http_buf
        return 0;
  }
  
 -PHP_HTTP_BUFFER_API char *php_http_buffer_account(php_http_buffer_t *buf, size_t to_account)
 +PHP_HTTP_BUFFER_API char *php_http_buffer_account(
 +              php_http_buffer_t *buf, size_t to_account)
  {
-       /* it's probably already too late but check anyway */
-       if (to_account > buf->free) {
-               return NULL;
-       }
+       assert(to_account <= buf->free);
  
        buf->free -= to_account;
        buf->used += to_account;
@@@ -321,21 -324,24 +321,23 @@@ ZEND_RESULT_CODE php_http_client_getopt
  zend_class_entry *php_http_client_class_entry;
  static zend_object_handlers php_http_client_object_handlers;
  
 -void php_http_client_object_free(void *object TSRMLS_DC)
 +void php_http_client_object_free(zend_object *object)
  {
 -      php_http_client_object_t *o = (php_http_client_object_t *) object;
 +      php_http_client_object_t *o = PHP_HTTP_OBJ(object, NULL);
  
        php_http_client_free(&o->client);
 -      zend_object_std_dtor((zend_object *) o TSRMLS_CC);
 -      efree(o);
+       php_http_object_method_dtor(&o->notify);
+       php_http_object_method_free(&o->update);
 +      zend_object_std_dtor(object);
  }
  
 -zend_object_value php_http_client_object_new_ex(zend_class_entry *ce, php_http_client_t *client, php_http_client_object_t **ptr TSRMLS_DC)
 +php_http_client_object_t *php_http_client_object_new_ex(zend_class_entry *ce, php_http_client_t *client)
  {
        php_http_client_object_t *o;
  
-       o = ecalloc(1, sizeof(php_http_client_object_t) + (ce->default_properties_count - 1) * sizeof(zval));
 -      o = ecalloc(1, sizeof(php_http_client_object_t));
 -      zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
 -      object_properties_init((zend_object *) o, ce);
++      o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
 +      zend_object_std_init(&o->zo, ce);
 +      object_properties_init(&o->zo, ce);
  
        o->client = client;
  
@@@ -367,7 -377,7 +369,7 @@@ static void handle_history(zval *zclien
        zval_ptr_dtor(&new_hist);
  }
  
- static ZEND_RESULT_CODE handle_response(void *arg, php_http_client_t *client, php_http_client_enqueue_t *e, php_http_message_t **request, php_http_message_t **response)
 -static STATUS handle_response(void *arg, php_http_client_t *client, php_http_client_enqueue_t *e, php_http_message_t **response)
++static ZEND_RESULT_CODE handle_response(void *arg, php_http_client_t *client, php_http_client_enqueue_t *e, php_http_message_t **response)
  {
        zend_bool dequeue = 0;
        zval zclient;
                /* ensure the message is of type response (could be uninitialized in case of early error, like DNS) */
                php_http_message_set_type(msg, PHP_HTTP_RESPONSE);
  
 -              if (z_is_true(zend_read_property(php_http_client_class_entry, &zclient, ZEND_STRL("recordHistory"), 0 TSRMLS_CC))) {
 -                      handle_history(&zclient, e->request, *response TSRMLS_CC);
 +              if (zend_is_true(zend_read_property(php_http_client_class_entry, &zclient, ZEND_STRL("recordHistory"), 0, &rec_hist_tmp))) {
-                       handle_history(&zclient, *request, *response);
++                      handle_history(&zclient, e->request, *response);
                }
  
                /* hard detach, redirects etc. are in the history */
  
  static void handle_progress(void *arg, php_http_client_t *client, php_http_client_enqueue_t *e, php_http_client_progress_state_t *progress)
  {
-       zval zrequest, zprogress, retval, zclient;
 -      zval *zrequest, *zprogress, *zclient, **args[2];
++      zval zclient, args[2];
+       php_http_client_object_t *client_obj = arg;
        zend_error_handling zeh;
 -      TSRMLS_FETCH_FROM_CTX(client->ts);
 -
 -      MAKE_STD_ZVAL(zclient);
 -      ZVAL_OBJVAL(zclient, client_obj->zv, 1);
 -
 -      MAKE_STD_ZVAL(zrequest);
 -      ZVAL_OBJVAL(zrequest, ((php_http_message_object_t *) e->opaque)->zv, 1);
 -      args[0] = &zrequest;
 -
 -      MAKE_STD_ZVAL(zprogress);
 -      object_init(zprogress);
 -      add_property_bool(zprogress, "started", progress->started);
 -      add_property_bool(zprogress, "finished", progress->finished);
 -      add_property_string(zprogress, "info", STR_PTR(progress->info), 1);
 -      add_property_double(zprogress, "dltotal", progress->dl.total);
 -      add_property_double(zprogress, "dlnow", progress->dl.now);
 -      add_property_double(zprogress, "ultotal", progress->ul.total);
 -      add_property_double(zprogress, "ulnow", progress->ul.now);
 -      args[1] = &zprogress;
 -
 -      zend_replace_error_handling(EH_NORMAL, NULL, &zeh TSRMLS_CC);
 -      php_http_object_method_call(&client_obj->notify, zclient, NULL, 2, args TSRMLS_CC);
 -      zend_restore_error_handling(&zeh TSRMLS_CC);
 +
-       ZVAL_UNDEF(&retval);
-       ZVAL_OBJECT(&zclient, &((php_http_client_object_t *) arg)->zo, 1);
-       ZVAL_OBJECT(&zrequest, &((php_http_message_object_t *) e->opaque)->zo, 1);
-       object_init(&zprogress);
-       add_property_bool(&zprogress, "started", progress->started);
-       add_property_bool(&zprogress, "finished", progress->finished);
-       add_property_string(&zprogress, "info", STR_PTR(progress->info));
-       add_property_double(&zprogress, "dltotal", progress->dl.total);
-       add_property_double(&zprogress, "dlnow", progress->dl.now);
-       add_property_double(&zprogress, "ultotal", progress->ul.total);
-       add_property_double(&zprogress, "ulnow", progress->ul.now);
++      ZVAL_OBJECT(&zclient, &client_obj->zo, 1);
++      ZVAL_OBJECT(&args[0], &((php_http_message_object_t *) e->opaque)->zo, 1);
++      object_init(&args[1]);
++      add_property_bool(&args[1], "started", progress->started);
++      add_property_bool(&args[1], "finished", progress->finished);
++      add_property_string(&args[1], "info", STR_PTR(progress->info));
++      add_property_double(&args[1], "dltotal", progress->dl.total);
++      add_property_double(&args[1], "dlnow", progress->dl.now);
++      add_property_double(&args[1], "ultotal", progress->ul.total);
++      add_property_double(&args[1], "ulnow", progress->ul.now);
++
 +      zend_replace_error_handling(EH_NORMAL, NULL, &zeh);
-       zend_call_method_with_2_params(&zclient, NULL, NULL, "notify", &retval, &zrequest, &zprogress);
++      php_http_object_method_call(&client_obj->notify, &zclient, NULL, 2, args);
 +      zend_restore_error_handling(&zeh);
        zval_ptr_dtor(&zclient);
--      zval_ptr_dtor(&zrequest);
--      zval_ptr_dtor(&zprogress);
-       zval_ptr_dtor(&retval);
++      zval_ptr_dtor(&args[0]);
++      zval_ptr_dtor(&args[1]);
  }
  
  static void response_dtor(void *data)
@@@ -496,22 -519,32 +499,24 @@@ static PHP_METHOD(HttpClient, __constru
                        return;
                }
  
 -              MAKE_STD_ZVAL(os);
 -              object_init_ex(os, spl_ce_SplObjectStorage);
 -              zend_update_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), os TSRMLS_CC);
 +              object_init_ex(&os, spl_ce_SplObjectStorage);
 +              zend_update_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), &os);
                zval_ptr_dtor(&os);
  
 -              if (persistent_handle_len) {
 -                      char *name_str;
 -                      size_t name_len;
 +              if (persistent_handle_name) {
                        php_persistent_handle_factory_t *pf;
  
 -                      name_len = spprintf(&name_str, 0, "http\\Client\\%s", driver.name_str);
 -                      php_http_pretty_key(name_str + sizeof("http\\Client"), driver.name_len, 1, 1);
 -
 -                      if ((pf = php_persistent_handle_concede(NULL , name_str, name_len, persistent_handle_str, persistent_handle_len, NULL, NULL TSRMLS_CC))) {
 +                      if ((pf = php_persistent_handle_concede(NULL, driver->client_name, persistent_handle_name, NULL, NULL))) {
                                rf = php_resource_factory_init(NULL, php_persistent_handle_get_resource_factory_ops(), pf, (void (*)(void *)) php_persistent_handle_abandon);
                        }
 -
 -                      efree(name_str);
                }
  
 -              obj = zend_object_store_get_object(getThis() TSRMLS_CC);
 +              obj = PHP_HTTP_OBJ(NULL, getThis());
  
 -              php_http_expect(obj->client = php_http_client_init(NULL, driver.client_ops, rf, NULL TSRMLS_CC), runtime, return);
 +              php_http_expect(obj->client = php_http_client_init(NULL, driver->client_ops, rf, NULL), runtime, return);
  
 -              php_http_object_method_init(&obj->notify, getThis(), ZEND_STRL("notify") TSRMLS_CC);
++              php_http_object_method_init(&obj->notify, getThis(), ZEND_STRL("notify"));
                obj->client->callback.response.func = handle_response;
                obj->client->callback.response.arg = obj;
                obj->client->callback.progress.func = handle_progress;
@@@ -840,16 -874,23 +845,22 @@@ static PHP_METHOD(HttpClient, enableEve
  
        php_http_expect(SUCCESS == php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_USE_EVENTS, &enable), unexpected_val, return);
  
 -      RETVAL_ZVAL(getThis(), 1, 0);
 +      RETVAL_ZVAL_FAST(getThis());
  }
  
 -      zval **args[3];
+ struct notify_arg {
+       php_http_object_method_t *cb;
 -static int notify(zend_object_iterator *iter, void *puser TSRMLS_DC)
++      zval args[3];
+       int argc;
+ };
 +static int notify(zend_object_iterator *iter, void *puser)
  {
-       zval *observer, *args = puser;
 -      zval **observer = NULL;
++      zval *observer;
+       struct notify_arg *arg = puser;
  
 -      iter->funcs->get_current_data(iter, &observer TSRMLS_CC);
 -      if (observer) {
 -              return php_http_object_method_call(arg->cb, *observer, NULL, arg->argc, arg->args TSRMLS_CC);
 +      if ((observer = iter->funcs->get_current_data(iter))) {
-               int num_args = !Z_ISUNDEF(args[0]) + !Z_ISUNDEF(args[1]) + !Z_ISUNDEF(args[2]);
-               return php_http_method_call(observer, ZEND_STRL("update"), num_args, args, NULL);
++              return php_http_object_method_call(arg->cb, observer, NULL, arg->argc, arg->args);
        }
        return FAILURE;
  }
@@@ -859,40 -900,51 +870,46 @@@ ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_no
  ZEND_END_ARG_INFO();
  static PHP_METHOD(HttpClient, notify)
  {
-       zval *request = NULL, *zprogress = NULL, observers_tmp, *observers, args[3];
 -      zval *request = NULL, *zprogress = NULL, *observers;
++      zval *request = NULL, *zprogress = NULL, observers_tmp, *observers;
+       php_http_client_object_t *client_obj;
+       struct notify_arg arg = {NULL};
  
 -      php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|O!o!", &request, php_http_client_request_class_entry, &zprogress), invalid_arg, return);
 +      php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|O!o!", &request, php_http_client_request_class_entry, &zprogress), invalid_arg, return);
  
 -      client_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
 -      observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
++      client_obj = PHP_HTTP_OBJ(NULL, getThis());
 +      observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0, &observers_tmp);
  
        if (Z_TYPE_P(observers) != IS_OBJECT) {
                php_http_throw(unexpected_val, "Observer storage is corrupted", NULL);
                return;
        }
  
-       ZVAL_COPY(&args[0], getThis());
-       if (request) {
-               ZVAL_COPY(&args[1], request);
-       } else {
-               ZVAL_UNDEF(&args[1]);
-       }
-       if (zprogress) {
-               ZVAL_COPY(&args[2], zprogress);
-       } else {
-               ZVAL_UNDEF(&args[2]);
-       }
+       if (client_obj->update) {
+               arg.cb = client_obj->update;
 -
 -              Z_ADDREF_P(getThis());
 -              arg.args[0] = &getThis();
++              ZVAL_COPY(&arg.args[0], getThis());
+               arg.argc = 1;
  
-       spl_iterator_apply(observers, notify, args);
+               if (request) {
 -                      Z_ADDREF_P(request);
 -                      arg.args[1] = &request;
++                      ZVAL_COPY(&arg.args[1], request);
+                       arg.argc += 1;
+               }
 -
+               if (zprogress) {
 -                      Z_ADDREF_P(zprogress);
 -                      arg.args[2] = &zprogress;
++                      ZVAL_COPY(&arg.args[2], zprogress);
+                       arg.argc += 1;
+               }
  
-       zval_ptr_dtor(getThis());
-       if (request) {
-               zval_ptr_dtor(request);
-       }
-       if (zprogress) {
-               zval_ptr_dtor(zprogress);
 -              spl_iterator_apply(observers, notify, &arg TSRMLS_CC);
++              spl_iterator_apply(observers, notify, &arg);
 -              zval_ptr_dtor(&getThis());
++              zval_ptr_dtor(getThis());
+               if (request) {
 -                      zval_ptr_dtor(&request);
++                      zval_ptr_dtor(request);
+               }
+               if (zprogress) {
 -                      zval_ptr_dtor(&zprogress);
++                      zval_ptr_dtor(zprogress);
+               }
        }
  
 -      RETVAL_ZVAL(getThis(), 1, 0);
 +      RETVAL_ZVAL_FAST(getThis());
  }
  
  ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_attach, 0, 0, 1)
  ZEND_END_ARG_INFO();
  static PHP_METHOD(HttpClient, attach)
  {
 -      zval *observers, *observer, *retval = NULL;
 +      zval observers_tmp, *observers, *observer, retval;
+       php_http_client_object_t *client_obj;
  
 -      php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &observer, spl_ce_SplObserver), invalid_arg, return);
 +      php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "O", &observer, spl_ce_SplObserver), invalid_arg, return);
  
 -      client_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
 -      observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
++      client_obj = PHP_HTTP_OBJ(NULL, getThis());
 +      observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0, &observers_tmp);
  
        if (Z_TYPE_P(observers) != IS_OBJECT) {
                php_http_throw(unexpected_val, "Observer storage is corrupted", NULL);
                return;
        }
  
 -              client_obj->update = php_http_object_method_init(NULL, observer, ZEND_STRL("update") TSRMLS_CC);
+       if (!client_obj->update) {
 -      zend_call_method_with_1_params(&observers, NULL, NULL, "attach", &retval, observer);
 -      if (retval) {
 -              zval_ptr_dtor(&retval);
 -      }
++              client_obj->update = php_http_object_method_init(NULL, observer, ZEND_STRL("update"));
+       }
 +      ZVAL_UNDEF(&retval);
 +      zend_call_method_with_1_params(observers, NULL, NULL, "attach", &retval, observer);
 +      zval_ptr_dtor(&retval);
  
 -      RETVAL_ZVAL(getThis(), 1, 0);
 +      RETVAL_ZVAL_FAST(getThis());
  }
  
  ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_detach, 0, 0, 1)
@@@ -85,7 -84,7 +85,7 @@@ typedef struct php_http_client_progress
        unsigned finished:1;
  } php_http_client_progress_state_t;
  
- typedef ZEND_RESULT_CODE (*php_http_client_response_callback_t)(void *arg, struct php_http_client *client, php_http_client_enqueue_t *e, php_http_message_t **request, php_http_message_t **response);
 -typedef STATUS (*php_http_client_response_callback_t)(void *arg, struct php_http_client *client, php_http_client_enqueue_t *e, php_http_message_t **response);
++typedef ZEND_RESULT_CODE (*php_http_client_response_callback_t)(void *arg, struct php_http_client *client, php_http_client_enqueue_t *e, php_http_message_t **response);
  typedef void (*php_http_client_progress_callback_t)(void *arg, struct php_http_client *client, php_http_client_enqueue_t *e, php_http_client_progress_state_t *state);
  
  typedef struct php_http_client {
  PHP_HTTP_API zend_class_entry *php_http_client_class_entry;
  
  typedef struct php_http_client_object {
 -      zend_object zo;
 -      zend_object_value zv;
        php_http_client_t *client;
 -      long iterator;
+       php_http_object_method_t *update;
+       php_http_object_method_t notify;
 +      long iterator;
 +      zend_object zo;
  } php_http_client_object_t;
  
 -PHP_HTTP_API php_http_client_t *php_http_client_init(php_http_client_t *h, php_http_client_ops_t *ops, php_resource_factory_t *rf, void *init_arg TSRMLS_DC);
 +PHP_HTTP_API php_http_client_t *php_http_client_init(php_http_client_t *h, php_http_client_ops_t *ops, php_resource_factory_t *rf, void *init_arg);
  PHP_HTTP_API php_http_client_t *php_http_client_copy(php_http_client_t *from, php_http_client_t *to);
  PHP_HTTP_API void php_http_client_dtor(php_http_client_t *h);
  PHP_HTTP_API void php_http_client_free(php_http_client_t **h);
@@@ -334,143 -300,118 +299,142 @@@ static int php_http_curle_raw_callback(
        return 0;
  }
  
- static int php_http_curle_dummy_callback(char *data, size_t n, size_t l, void *s)
+ static int php_http_curle_header_callback(char *data, size_t n, size_t l, void *arg)
  {
-       return n*l;
+       php_http_client_curl_handler_t *h = arg;
+       return php_http_buffer_append(&h->response.headers, data, n * l);
+ }
+ static int php_http_curle_body_callback(char *data, size_t n, size_t l, void *arg)
+ {
+       php_http_client_curl_handler_t *h = arg;
+       return php_http_message_body_append(h->response.body, data, n*l);
  }
  
 -static STATUS php_http_curle_get_info(CURL *ch, HashTable *info)
 +static ZEND_RESULT_CODE php_http_curle_get_info(CURL *ch, HashTable *info)
  {
 -      char *c;
 -      long l;
 -      double d;
 -      struct curl_slist *s, *p;
 -      zval *subarray, array;
 -      INIT_PZVAL_ARRAY(&array, info);
 +      char *c = NULL;
 +      long l = 0;
 +      double d = 0;
 +      struct curl_slist *s = NULL, *p = NULL;
 +      zval tmp = {{0}};
  
        /* BEGIN::CURLINFO */
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_EFFECTIVE_URL, &c)) {
 -              add_assoc_string_ex(&array, "effective_url", sizeof("effective_url"), c ? c : "", 1);
 +              ZVAL_STRING(&tmp, STR_PTR(c));
 +              zend_hash_str_update(info, "effective_url", lenof("effective_url"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &l)) {
 -              add_assoc_long_ex(&array, "response_code", sizeof("response_code"), l);
 +              ZVAL_LONG(&tmp, l);
 +              zend_hash_str_update(info, "response_code", lenof("response_code"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_TOTAL_TIME, &d)) {
 -              add_assoc_double_ex(&array, "total_time", sizeof("total_time"), d);
 +              ZVAL_DOUBLE(&tmp, d);
 +              zend_hash_str_update(info, "total_time", lenof("total_time"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_NAMELOOKUP_TIME, &d)) {
 -              add_assoc_double_ex(&array, "namelookup_time", sizeof("namelookup_time"), d);
 +              ZVAL_DOUBLE(&tmp, d);
 +              zend_hash_str_update(info, "namelookup_time", lenof("namelookup_time"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONNECT_TIME, &d)) {
 -              add_assoc_double_ex(&array, "connect_time", sizeof("connect_time"), d);
 +              ZVAL_DOUBLE(&tmp, d);
 +              zend_hash_str_update(info, "connect_time", lenof("connect_time"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PRETRANSFER_TIME, &d)) {
 -              add_assoc_double_ex(&array, "pretransfer_time", sizeof("pretransfer_time"), d);
 +              ZVAL_DOUBLE(&tmp, d);
 +              zend_hash_str_update(info, "pretransfer_time", lenof("pretransfer_time"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SIZE_UPLOAD, &d)) {
 -              add_assoc_double_ex(&array, "size_upload", sizeof("size_upload"), d);
 +              ZVAL_DOUBLE(&tmp, d);
 +              zend_hash_str_update(info, "size_upload", lenof("size_upload"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SIZE_DOWNLOAD, &d)) {
 -              add_assoc_double_ex(&array, "size_download", sizeof("size_download"), d);
 +              ZVAL_DOUBLE(&tmp, d);
 +              zend_hash_str_update(info, "size_download", lenof("size_download"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SPEED_DOWNLOAD, &d)) {
 -              add_assoc_double_ex(&array, "speed_download", sizeof("speed_download"), d);
 +              ZVAL_DOUBLE(&tmp, d);
 +              zend_hash_str_update(info, "speed_download", lenof("speed_download"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SPEED_UPLOAD, &d)) {
 -              add_assoc_double_ex(&array, "speed_upload", sizeof("speed_upload"), d);
 +              ZVAL_DOUBLE(&tmp, d);
 +              zend_hash_str_update(info, "speed_upload", lenof("speed_upload"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HEADER_SIZE, &l)) {
 -              add_assoc_long_ex(&array, "header_size", sizeof("header_size"), l);
 +              ZVAL_LONG(&tmp, l);
 +              zend_hash_str_update(info, "header_size", lenof("header_size"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REQUEST_SIZE, &l)) {
 -              add_assoc_long_ex(&array, "request_size", sizeof("request_size"), l);
 +              ZVAL_LONG(&tmp, l);
 +              zend_hash_str_update(info, "request_size", lenof("request_size"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SSL_VERIFYRESULT, &l)) {
 -              add_assoc_long_ex(&array, "ssl_verifyresult", sizeof("ssl_verifyresult"), l);
 +              ZVAL_LONG(&tmp, l);
 +              zend_hash_str_update(info, "ssl_verifyresult", lenof("ssl_verifyresult"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_FILETIME, &l)) {
 -              add_assoc_long_ex(&array, "filetime", sizeof("filetime"), l);
 +              ZVAL_LONG(&tmp, l);
 +              zend_hash_str_update(info, "filetime", lenof("filetime"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d)) {
 -              add_assoc_double_ex(&array, "content_length_download", sizeof("content_length_download"), d);
 +              ZVAL_DOUBLE(&tmp, d);
 +              zend_hash_str_update(info, "content_length_download", lenof("content_length_download"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_LENGTH_UPLOAD, &d)) {
 -              add_assoc_double_ex(&array, "content_length_upload", sizeof("content_length_upload"), d);
 +              ZVAL_DOUBLE(&tmp, d);
 +              zend_hash_str_update(info, "content_length_upload", lenof("content_length_upload"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_STARTTRANSFER_TIME, &d)) {
 -              add_assoc_double_ex(&array, "starttransfer_time", sizeof("starttransfer_time"), d);
 +              ZVAL_DOUBLE(&tmp, d);
 +              zend_hash_str_update(info, "starttransfer_time", lenof("starttransfer_time"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_TYPE, &c)) {
 -              add_assoc_string_ex(&array, "content_type", sizeof("content_type"), c ? c : "", 1);
 +              ZVAL_STRING(&tmp, STR_PTR(c));
 +              zend_hash_str_update(info, "content_type", lenof("content_type"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_TIME, &d)) {
 -              add_assoc_double_ex(&array, "redirect_time", sizeof("redirect_time"), d);
 +              ZVAL_DOUBLE(&tmp, d);
 +              zend_hash_str_update(info, "redirect_time", lenof("redirect_time"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_COUNT, &l)) {
 -              add_assoc_long_ex(&array, "redirect_count", sizeof("redirect_count"), l);
 +              ZVAL_LONG(&tmp, l);
 +              zend_hash_str_update(info, "redirect_count", lenof("redirect_count"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HTTP_CONNECTCODE, &l)) {
 -              add_assoc_long_ex(&array, "connect_code", sizeof("connect_code"), l);
 +              ZVAL_LONG(&tmp, l);
 +              zend_hash_str_update(info, "connect_code", lenof("connect_code"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HTTPAUTH_AVAIL, &l)) {
 -              add_assoc_long_ex(&array, "httpauth_avail", sizeof("httpauth_avail"), l);
 +              ZVAL_LONG(&tmp, l);
 +              zend_hash_str_update(info, "httpauth_avail", lenof("httpauth_avail"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PROXYAUTH_AVAIL, &l)) {
 -              add_assoc_long_ex(&array, "proxyauth_avail", sizeof("proxyauth_avail"), l);
 +              ZVAL_LONG(&tmp, l);
 +              zend_hash_str_update(info, "proxyauth_avail", lenof("proxyauth_avail"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_OS_ERRNO, &l)) {
 -              add_assoc_long_ex(&array, "os_errno", sizeof("os_errno"), l);
 +              ZVAL_LONG(&tmp, l);
 +              zend_hash_str_update(info, "os_errno", lenof("os_errno"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_NUM_CONNECTS, &l)) {
 -              add_assoc_long_ex(&array, "num_connects", sizeof("num_connects"), l);
 +              ZVAL_LONG(&tmp, l);
 +              zend_hash_str_update(info, "num_connects", lenof("num_connects"), &tmp);
        }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SSL_ENGINES, &s)) {
 -              MAKE_STD_ZVAL(subarray);
 -              array_init(subarray);
 +              array_init(&tmp);
                for (p = s; p; p = p->next) {
                        if (p->data) {
 -                              add_next_index_string(subarray, p->data, 1);
 +                              add_next_index_string(&tmp, p->data);
                        }
                }
 -              add_assoc_zval_ex(&array, "ssl_engines", sizeof("ssl_engines"), subarray);
 +              zend_hash_str_update(info, "ssl_engines", lenof("ssl_engines"), &tmp);
                curl_slist_free_all(s);
        }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_COOKIELIST, &s)) {
-               array_init(&tmp);
-               for (p = s; p; p = p->next) {
-                       if (p->data) {
-                               add_next_index_string(&tmp, p->data);
-                       }
-               }
-               zend_hash_str_update(info, "cookies", lenof("cookies"), &tmp);
-               curl_slist_free_all(s);
-       }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_URL, &c)) {
 -              add_assoc_string_ex(&array, "redirect_url", sizeof("redirect_url"), c ? c : "", 1);
 +              ZVAL_STRING(&tmp, STR_PTR(c));
 +              zend_hash_str_update(info, "redirect_url", lenof("redirect_url"), &tmp);
        }
  #if PHP_HTTP_CURL_VERSION(7,19,0)
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PRIMARY_IP, &c)) {
@@@ -639,6 -576,50 +603,54 @@@ static int compare_queue(php_http_clien
        return handle == ((php_http_client_curl_handler_t *) e->opaque)->handle;
  }
  
 -      if ((zh = php_http_message_header(response, ZEND_STRL("Content-Length"), 1))) {
 -              zend_hash_update(&response->hdrs, "X-Original-Content-Length", sizeof("X-Original-Content-Length"), &zh, sizeof(zval *), NULL);
+ static php_http_message_t *php_http_curlm_responseparser(php_http_client_curl_handler_t *h TSRMLS_DC)
+ {
+       php_http_message_t *response;
+       php_http_header_parser_t parser;
+       zval *zh;
+       response = php_http_message_init(NULL, 0, h->response.body TSRMLS_CC);
+       php_http_header_parser_init(&parser TSRMLS_CC);
+       php_http_header_parser_parse(&parser, &h->response.headers, PHP_HTTP_HEADER_PARSER_CLEANUP, &response->hdrs, (php_http_info_callback_t) php_http_message_info_callback, (void *) &response);
+       php_http_header_parser_dtor(&parser);
+       /* move body to right message */
+       if (response->body != h->response.body) {
+               php_http_message_t *ptr = response;
+               while (ptr->parent) {
+                       ptr = ptr->parent;
+               }
+               response->body = ptr->body;
+               ptr->body = NULL;
+       }
+       php_http_message_body_addref(h->response.body);
+       /* let's update the response headers */
 -      if ((zh = php_http_message_header(response, ZEND_STRL("Transfer-Encoding"), 0))) {
 -              zend_hash_update(&response->hdrs, "X-Original-Transfer-Encoding", sizeof("X-Original-Transfer-Encoding"), (void *) &zh, sizeof(zval *), NULL);
 -              zend_hash_del(&response->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding"));
++      if ((zh = php_http_message_header(response, ZEND_STRL("Content-Length")))) {
++              Z_TRY_ADDREF_P(zh);
++              zend_hash_str_update(&response->hdrs, "X-Original-Content-Length", lenof("X-Original-Content-Length"), zh);
+       }
 -      if ((zh = php_http_message_header(response, ZEND_STRL("Content-Range"), 0))) {
 -              zend_hash_update(&response->hdrs, "X-Original-Content-Range", sizeof("X-Original-Content-Range"), &zh, sizeof(zval *), NULL);
 -              zend_hash_del(&response->hdrs, "Content-Range", sizeof("Content-Range"));
++      if ((zh = php_http_message_header(response, ZEND_STRL("Transfer-Encoding")))) {
++              Z_TRY_ADDREF_P(zh);
++              zend_hash_str_update(&response->hdrs, "X-Original-Transfer-Encoding", lenof("X-Original-Transfer-Encoding"), zh);
++              zend_hash_str_del(&response->hdrs, "Transfer-Encoding", lenof("Transfer-Encoding"));
+       }
 -      if ((zh = php_http_message_header(response, ZEND_STRL("Content-Encoding"), 0))) {
 -              zend_hash_update(&response->hdrs, "X-Original-Content-Encoding", sizeof("X-Original-Content-Encoding"), &zh, sizeof(zval *), NULL);
 -              zend_hash_del(&response->hdrs, "Content-Encoding", sizeof("Content-Encoding"));
++      if ((zh = php_http_message_header(response, ZEND_STRL("Content-Range")))) {
++              Z_TRY_ADDREF_P(zh);
++              zend_hash_str_update(&response->hdrs, "X-Original-Content-Range", lenof("X-Original-Content-Range"), zh);
++              zend_hash_str_del(&response->hdrs, "Content-Range", lenof("Content-Range"));
+       }
++      if ((zh = php_http_message_header(response, ZEND_STRL("Content-Encoding")))) {
++              Z_TRY_ADDREF_P(zh);
++              zend_hash_str_update(&response->hdrs, "X-Original-Content-Encoding", lenof("X-Original-Content-Encoding"), zh);
++              zend_hash_str_del(&response->hdrs, "Content-Encoding", lenof("Content-Encoding"));
+       }
+       php_http_message_update_headers(response);
+       return response;
+ }
  static void php_http_curlm_responsehandler(php_http_client_t *context)
  {
        int remaining = 0;
@@@ -941,12 -929,13 +957,13 @@@ static ZEND_RESULT_CODE php_http_curle_
        return SUCCESS;
  }
  
 -static STATUS php_http_curle_option_set_compress(php_http_option_t *opt, zval *val, void *userdata)
 +static ZEND_RESULT_CODE php_http_curle_option_set_compress(php_http_option_t *opt, zval *val, void *userdata)
  {
        php_http_client_curl_handler_t *curl = userdata;
+       CURL *ch = curl->handle;
  
-       if (Z_TYPE_P(val) == IS_TRUE) {
-               curl->options.headers = curl_slist_append(curl->options.headers, "Accept-Encoding: gzip;q=1.0,deflate;q=0.5");
 -      if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_ACCEPT_ENCODING, Z_BVAL_P(val) ? "" : NULL)) {
++      if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_ACCEPT_ENCODING, Z_TYPE_P(val) == IS_TRUE ? "" : NULL)) {
+               return FAILURE;
        }
        return SUCCESS;
  }
@@@ -1079,8 -1081,45 +1096,44 @@@ static ZEND_RESULT_CODE php_http_curle_
        return SUCCESS;
  }
  
 -static STATUS php_http_curle_option_set_proxyheader(php_http_option_t *opt, zval *val, void *userdata)
+ #if PHP_HTTP_CURL_VERSION(7,37,0)
 -      TSRMLS_FETCH_FROM_CTX(curl->client->ts);
++static ZEND_RESULT_CODE php_http_curle_option_set_proxyheader(php_http_option_t *opt, zval *val, void *userdata)
+ {
+       php_http_client_curl_handler_t *curl = userdata;
 -              php_http_array_hashkey_t header_key = php_http_array_hashkey_init(0);
 -              zval **header_val, *header_cpy;
 -              HashPosition pos;
+       if (val && Z_TYPE_P(val) != IS_NULL) {
 -              FOREACH_KEYVAL(pos, val, header_key, header_val) {
 -                      if (header_key.type == HASH_KEY_IS_STRING) {
 -                              header_cpy = php_http_ztyp(IS_STRING, *header_val);
 -                              php_http_buffer_appendf(&header, "%s: %s", header_key.str, Z_STRVAL_P(header_cpy));
++              php_http_arrkey_t header_key;
++              zval *header_val;
+               php_http_buffer_t header;
+               php_http_buffer_init(&header);
 -
 -                              zval_ptr_dtor(&header_cpy);
++              ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(val), header_key.h, header_key.key, header_val)
++              {
++                      if (header_key.key) {
++                              zend_string *zs = zval_get_string(header_val);
++                              php_http_buffer_appendf(&header, "%s: %s", header_key.key->val, zs->val);
+                               php_http_buffer_fix(&header);
+                               curl->options.proxyheaders = curl_slist_append(curl->options.proxyheaders, header.data);
+                               php_http_buffer_reset(&header);
++                              zend_string_release(zs);
+                       }
+               }
++              ZEND_HASH_FOREACH_END();
+               php_http_buffer_dtor(&header);
+       }
+       if (CURLE_OK != curl_easy_setopt(curl->handle, CURLOPT_PROXYHEADER, curl->options.proxyheaders)) {
+               return FAILURE;
+       }
+       if (CURLE_OK != curl_easy_setopt(curl->handle, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE)) {
+               curl_easy_setopt(curl->handle, CURLOPT_PROXYHEADER, NULL);
+               return FAILURE;
+       }
+       return SUCCESS;
+ }
+ #endif
  #if PHP_HTTP_CURL_VERSION(7,21,3)
 -static STATUS php_http_curle_option_set_resolve(php_http_option_t *opt, zval *val, void *userdata)
 +static ZEND_RESULT_CODE php_http_curle_option_set_resolve(php_http_option_t *opt, zval *val, void *userdata)
  {
        php_http_client_curl_handler_t *curl = userdata;
        CURL *ch = curl->handle;
  }
  #endif
  
 -#if PHP_HTTP_CURL_VERSION(7,21,4)
 -static STATUS php_http_curle_option_set_ssl_tlsauthtype(php_http_option_t *opt, zval *val, void *userdata)
++#if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP)
++static ZEND_RESULT_CODE php_http_curle_option_set_ssl_tlsauthtype(php_http_option_t *opt, zval *val, void *userdata)
+ {
+       php_http_client_curl_handler_t *curl = userdata;
+       CURL *ch = curl->handle;
+       if (val && Z_LVAL_P(val)) {
+               switch (Z_LVAL_P(val)) {
+               case CURL_TLSAUTH_SRP:
+                       if (CURLE_OK == curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, PHP_HTTP_CURL_TLSAUTH_SRP)) {
+                               return SUCCESS;
+                       }
+                       /* no break */
+               default:
+                       return FAILURE;
+               }
+       }
+       if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, PHP_HTTP_CURL_TLSAUTH_DEF)) {
+               return FAILURE;
+       }
+       return SUCCESS;
+ }
+ #endif
 -static void php_http_curle_options_init(php_http_options_t *registry TSRMLS_DC)
 +static void php_http_curle_options_init(php_http_options_t *registry)
  {
        php_http_option_t *opt;
  
  #     endif
  #endif
  #if PHP_HTTP_CURL_VERSION(7,19,1) && defined(PHP_HTTP_HAVE_OPENSSL)
 -              php_http_option_register(registry, ZEND_STRL("certinfo"), CURLOPT_CERTINFO, IS_BOOL);
 +              php_http_option_register(registry, ZEND_STRL("certinfo"), CURLOPT_CERTINFO, _IS_BOOL);
  #endif
  #if PHP_HTTP_CURL_VERSION(7,36,0)
 -              if ((opt = php_http_option_register(registry, ZEND_STRL("enable_npn"), CURLOPT_SSL_ENABLE_NPN, IS_BOOL))) {
 +              if ((opt = php_http_option_register(registry, ZEND_STRL("enable_npn"), CURLOPT_SSL_ENABLE_NPN, _IS_BOOL))) {
                        ZVAL_BOOL(&opt->defval, 1);
                }
 -              if ((opt = php_http_option_register(registry, ZEND_STRL("enable_alpn"), CURLOPT_SSL_ENABLE_ALPN, IS_BOOL))) {
 +              if ((opt = php_http_option_register(registry, ZEND_STRL("enable_alpn"), CURLOPT_SSL_ENABLE_ALPN, _IS_BOOL))) {
                        ZVAL_BOOL(&opt->defval, 1);
                }
 -#if PHP_HTTP_CURL_VERSION(7,21,4)
+ #endif
+ #if PHP_HTTP_CURL_VERSION(7,39,0)
+               if ((opt = php_http_option_register(registry, ZEND_STRL("pinned_publickey"), CURLOPT_PINNEDPUBLICKEY, IS_STRING))) {
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
+               }
+ #endif
++#if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP)
+               if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthtype"), CURLOPT_TLSAUTH_TYPE, IS_LONG))) {
+                       opt->setter = php_http_curle_option_set_ssl_tlsauthtype;
+               }
+               if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthuser"), CURLOPT_TLSAUTH_USERNAME, IS_STRING))) {
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+               }
+               if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthpass"), CURLOPT_TLSAUTH_PASSWORD, IS_STRING))) {
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+               }
  #endif
        }
  }
@@@ -1393,8 -1490,8 +1503,9 @@@ static zval *php_http_curle_get_option(
        zval *option;
  
        if ((option = php_http_option_get(opt, options, NULL))) {
 -              option = php_http_ztyp(opt->type, option);
 -              zend_hash_quick_update(&curl->options.cache, opt->name.s, opt->name.l, opt->name.h, &option, sizeof(zval *), NULL);
++              Z_TRY_ADDREF_P(option);
 +              convert_to_explicit_type_ex(option, opt->type);
 +              zend_hash_update(&curl->options.cache, opt->name, option);
        }
        return option;
  }
@@@ -1551,12 -1650,8 +1666,8 @@@ static php_http_client_curl_handler_t *
        handler->rf = rf;
        handler->client = h;
        handler->handle = handle;
-       handler->request.buffer = php_http_buffer_init(NULL);
-       handler->request.parser = php_http_message_parser_init(NULL);
-       handler->request.message = php_http_message_init(NULL, 0, NULL);
-       handler->response.buffer = php_http_buffer_init(NULL);
-       handler->response.parser = php_http_message_parser_init(NULL);
-       handler->response.message = php_http_message_init(NULL, 0, NULL);
 -      handler->response.body = php_http_message_body_init(NULL, NULL TSRMLS_CC);
++      handler->response.body = php_http_message_body_init(NULL, NULL);
+       php_http_buffer_init(&handler->response.headers);
        php_http_buffer_init(&handler->options.cookies);
        php_http_buffer_init(&handler->options.ranges);
        zend_hash_init(&handler->options.cache, 0, NULL, ZVAL_PTR_DTOR, 0);
@@@ -1709,17 -1809,15 +1823,13 @@@ static void php_http_client_curl_handle
  
  static void php_http_client_curl_handler_dtor(php_http_client_curl_handler_t *handler)
  {
 -      TSRMLS_FETCH_FROM_CTX(handler->client->ts);
 -
        php_http_client_curl_handler_clear(handler);
  
 -      php_resource_factory_handle_dtor(handler->rf, handler->handle TSRMLS_CC);
 +      php_resource_factory_handle_dtor(handler->rf, handler->handle);
        php_resource_factory_free(&handler->rf);
  
-       php_http_message_parser_free(&handler->request.parser);
-       php_http_message_free(&handler->request.message);
-       php_http_buffer_free(&handler->request.buffer);
-       php_http_message_parser_free(&handler->response.parser);
-       php_http_message_free(&handler->response.message);
-       php_http_buffer_free(&handler->response.buffer);
+       php_http_message_body_free(&handler->response.body);
+       php_http_buffer_dtor(&handler->response.headers);
        php_http_buffer_dtor(&handler->options.ranges);
        php_http_buffer_dtor(&handler->options.cookies);
        zend_hash_destroy(&handler->options.cache);
@@@ -2095,21 -2196,19 +2205,20 @@@ php_http_client_ops_t *php_http_client_
  PHP_MINIT_FUNCTION(http_client_curl)
  {
        php_http_options_t *options;
-       php_http_client_driver_t driver;
 -      php_http_client_driver_t driver = {
 -              ZEND_STRL("curl"),
 -              &php_http_client_curl_ops
 -      };
  
 -      if (SUCCESS != php_http_client_driver_add(&driver)) {
 -                      return FAILURE;
 -              }
 +      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;
 +      }
  
 -      if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl"), &php_http_curlm_resource_factory_ops, NULL, NULL TSRMLS_CC)) {
 +      if (SUCCESS != php_persistent_handle_provide(PHP_HTTP_G->client.curl.driver.client_name, &php_http_curlm_resource_factory_ops, NULL, NULL)) {
                return FAILURE;
        }
 -      if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl\\Request"), &php_http_curle_resource_factory_ops, NULL, NULL TSRMLS_CC)) {
 +      if (SUCCESS != php_persistent_handle_provide(PHP_HTTP_G->client.curl.driver.request_name, &php_http_curle_resource_factory_ops, NULL, NULL)) {
                return FAILURE;
        }
  
@@@ -378,14 -372,9 +378,14 @@@ php_http_cookie_object_t *php_http_cook
  {
        php_http_cookie_object_t *o;
  
 -      o = ecalloc(sizeof(*o), 1);
 -      zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
 -      object_properties_init((zend_object *) o, ce);
 +      if (!ce) {
 +              ce = php_http_cookie_class_entry;
 +      }
 +
-       o = ecalloc(sizeof(*o) + sizeof(zval) * (ce->default_properties_count - 1), 1);
++      o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
 +      zend_object_std_init(&o->zo, ce);
 +      object_properties_init(&o->zo, ce);
 +      o->zo.handlers = &php_http_cookie_object_handlers;
  
        if (list) {
                o->list = list;
@@@ -909,9 -923,13 +909,9 @@@ php_http_encoding_stream_object_t *php_
  {
        php_http_encoding_stream_object_t *o;
  
-       o = ecalloc(1, sizeof(*o) + (ce->default_properties_count - 1) * sizeof(zval));
 -      o = ecalloc(1, sizeof(*o));
 -      zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
 -      object_properties_init((zend_object *) o, ce);
 -
 -      if (ptr) {
 -              *ptr = o;
 -      }
++      o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
 +      zend_object_std_init(&o->zo, ce);
 +      object_properties_init(&o->zo, ce);
  
        if (s) {
                o->stream = s;
@@@ -837,19 -887,31 +838,27 @@@ typedef struct php_http_env_response_st
  
        unsigned started:1;
        unsigned finished:1;
+       unsigned chunked:1;
  } php_http_env_response_stream_ctx_t;
  
 -static STATUS php_http_env_response_stream_init(php_http_env_response_t *r, void *init_arg)
 +static ZEND_RESULT_CODE php_http_env_response_stream_init(php_http_env_response_t *r, void *init_arg)
  {
        php_http_env_response_stream_ctx_t *ctx;
 -      TSRMLS_FETCH_FROM_CTX(r->ts);
  
        ctx = ecalloc(1, sizeof(*ctx));
  
        ctx->stream = init_arg;
 -      if (SUCCESS != zend_list_addref(ctx->stream->rsrc_id)) {
 -              efree(ctx);
 -              return FAILURE;
 -      }
 -      zend_hash_init(&ctx->header, 0, NULL, ZVAL_PTR_DTOR, 0);
 -      php_http_version_init(&ctx->version, 1, 1 TSRMLS_CC);
 +      ++GC_REFCOUNT(ctx->stream->res);
 +      ZEND_INIT_SYMTABLE(&ctx->header);
 +      php_http_version_init(&ctx->version, 1, 1);
        ctx->status_code = 200;
 -      ctx->request = get_request(r->options TSRMLS_CC);
+       ctx->chunked = 1;
++      ctx->request = get_request(&r->options);
+       /* there are some limitations regarding TE:chunked, see https://tools.ietf.org/html/rfc7230#section-3.3.1 */
+       if (ctx->request && ctx->request->http.version.major == 1 && ctx->request->http.version.minor == 0) {
+               ctx->version.minor = 0;
+       }
  
        r->ctx = ctx;
  
@@@ -864,34 -927,56 +873,58 @@@ static void php_http_env_response_strea
        efree(ctx);
        r->ctx = NULL;
  }
 -static void php_http_env_response_stream_header(php_http_env_response_stream_ctx_t *ctx, HashTable *header TSRMLS_DC)
 +static void php_http_env_response_stream_header(php_http_env_response_stream_ctx_t *ctx, HashTable *header)
  {
 -      HashPosition pos;
 -      zval **val;
 +      zval *val;
  
 -      FOREACH_HASH_VAL(pos, header, val) {
 -              if (Z_TYPE_PP(val) == IS_ARRAY) {
 -                      php_http_env_response_stream_header(ctx, Z_ARRVAL_PP(val) TSRMLS_CC);
 +      ZEND_HASH_FOREACH_VAL(header, val)
 +      {
 +              if (Z_TYPE_P(val) == IS_ARRAY) {
 +                      php_http_env_response_stream_header(ctx, Z_ARRVAL_P(val));
                } else {
 -                      zval *tmp = php_http_ztyp(IS_STRING, *val);
 +                      zend_string *zs = zval_get_string(val);
  
 -                              if (!strncasecmp(Z_STRVAL_P(tmp), "Content-Length:", lenof("Content-Length:"))) {
+                       if (ctx->chunked) {
+                               /* disable chunked transfer encoding if we've got an explicit content-length */
 -                      php_stream_write(ctx->stream, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
++                              if (!strncasecmp(zs->val, "Content-Length:", lenof("Content-Length:"))) {
+                                       ctx->chunked = 0;
+                               }
+                       }
 +                      php_stream_write(ctx->stream, zs->val, zs->len);
                        php_stream_write_string(ctx->stream, PHP_HTTP_CRLF);
 -                      zval_ptr_dtor(&tmp);
 +                      zend_string_release(zs);
                }
        }
 +      ZEND_HASH_FOREACH_END();
  }
 -static STATUS php_http_env_response_stream_start(php_http_env_response_stream_ctx_t *ctx TSRMLS_DC)
 +static ZEND_RESULT_CODE php_http_env_response_stream_start(php_http_env_response_stream_ctx_t *ctx)
  {
        if (ctx->started || ctx->finished) {
                return FAILURE;
        }
  
--      php_stream_printf(ctx->stream TSRMLS_CC, "HTTP/%u.%u %ld %s" PHP_HTTP_CRLF, ctx->version.major, ctx->version.minor, ctx->status_code, php_http_env_get_response_status_for_code(ctx->status_code));
++      php_stream_printf(ctx->stream, "HTTP/%u.%u %ld %s" PHP_HTTP_CRLF, ctx->version.major, ctx->version.minor, ctx->status_code, php_http_env_get_response_status_for_code(ctx->status_code));
+       /* there are some limitations regarding TE:chunked, see https://tools.ietf.org/html/rfc7230#section-3.3.1 */
+       if (ctx->version.major == 1 && ctx->version.minor == 0) {
+               ctx->chunked = 0;
+       } else if (ctx->status_code == 204 || ctx->status_code/100 == 1) {
+               ctx->chunked = 0;
+       } else if (ctx->request && ctx->status_code/100 == 2 && !strcasecmp(ctx->request->http.info.request.method, "CONNECT")) {
+               ctx->chunked = 0;
+       }
 -      php_http_env_response_stream_header(ctx, &ctx->header TSRMLS_CC);
 +      php_http_env_response_stream_header(ctx, &ctx->header);
+       /* enable chunked transfer encoding */
+       if (ctx->chunked) {
+               php_stream_write_string(ctx->stream, "Transfer-Encoding: chunked" PHP_HTTP_CRLF);
+       }
++
        php_stream_write_string(ctx->stream, PHP_HTTP_CRLF);
        ctx->started = 1;
        return SUCCESS;
  }
  static long php_http_env_response_stream_get_status(php_http_env_response_t *r)
@@@ -1010,11 -1101,16 +1047,15 @@@ static ZEND_RESULT_CODE php_http_env_re
                return FAILURE;
        }
  
+       if (stream_ctx->chunked && 2 != php_stream_write_string(stream_ctx->stream, PHP_HTTP_CRLF)) {
+               return FAILURE;
+       }
        return SUCCESS;
  }
 -static STATUS php_http_env_response_stream_flush(php_http_env_response_t *r)
 +static ZEND_RESULT_CODE php_http_env_response_stream_flush(php_http_env_response_t *r)
  {
        php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
 -      TSRMLS_FETCH_FROM_CTX(r->ts);
  
        if (stream_ctx->finished) {
                return FAILURE;
diff --cc php_http_info.c
@@@ -71,9 -71,9 +71,9 @@@ php_http_info_t *php_http_info_parse(ph
        
        info = php_http_info_init(info TSRMLS_CC);
  
-       /* and nothing than SPACE or NUL after HTTP/1.x */
+       /* and nothing than SPACE or NUL after HTTP/X.x */
 -      if (!php_http_version_parse(&info->http.version, http TSRMLS_CC)
 +      if (!php_http_version_parse(&info->http.version, http)
-       ||      (http[lenof("HTTP/1.1")] && (!PHP_HTTP_IS_CTYPE(space, http[lenof("HTTP/1.1")])))) {
+       ||      (http[lenof("HTTP/X.x")] && (!PHP_HTTP_IS_CTYPE(space, http[lenof("HTTP/X.x")])))) {
                if (free_info) {
                        php_http_info_free(&info);
                }
                
                info->type = PHP_HTTP_REQUEST;
                if (url && http > url) {
-                       PHP_HTTP_INFO(info).request.method = estrndup(pre_header, url - pre_header);
+                       size_t url_len = url - pre_header;
+                       PHP_HTTP_INFO(info).request.method = estrndup(pre_header, url_len);
                        while (' ' == *url) ++url;
                        while (' ' == *(http-1)) --http;
                        if (http > url) {
-                               PHP_HTTP_INFO(info).request.url = php_http_url_parse(url, http - url, ~0);
+                               /* CONNECT presents an authority only */
+                               if (strcasecmp(PHP_HTTP_INFO(info).request.method, "CONNECT")) {
 -                                      PHP_HTTP_INFO(info).request.url = php_http_url_parse(url, http - url, ~0 TSRMLS_CC);
++                                      PHP_HTTP_INFO(info).request.url = php_http_url_parse(url, http - url, ~0);
+                               } else {
 -                                      PHP_HTTP_INFO(info).request.url = php_http_url_parse_authority(url, http - url, ~0 TSRMLS_CC);
++                                      PHP_HTTP_INFO(info).request.url = php_http_url_parse_authority(url, http - url, ~0);
+                               }
                        } else {
                                PTR_SET(PHP_HTTP_INFO(info).request.method, NULL);
                                return NULL;
diff --cc php_http_info.h
Simple merge
@@@ -586,38 -616,28 +586,37 @@@ static void php_http_message_object_pro
        char *version_str;
        size_t version_len;
  
 -      php_http_version_to_string(&obj->message->http.version, &version_str, &version_len, NULL, NULL TSRMLS_CC);
 -      RETVAL_STRINGL(version_str, version_len, 0);
 +      php_http_version_to_string(&obj->message->http.version, &version_str, &version_len, NULL, NULL);
 +      RETVAL_STR(php_http_cs2zs(version_str, version_len));
  }
 -static void php_http_message_object_prophandler_set_http_version(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
 -      zval *cpy = php_http_ztyp(IS_STRING, value);
 -      php_http_version_parse(&obj->message->http.version, Z_STRVAL_P(cpy) TSRMLS_CC);
 -      zval_ptr_dtor(&cpy);
 +static void php_http_message_object_prophandler_set_http_version(php_http_message_object_t *obj, zval *value) {
 +      zend_string *zs = zval_get_string(value);
 +      php_http_version_parse(&obj->message->http.version, zs->val);
 +      zend_string_release(zs);
  }
 -static void php_http_message_object_prophandler_get_headers(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
 +static void php_http_message_object_prophandler_get_headers(php_http_message_object_t *obj, zval *return_value ) {
        array_init(return_value);
 -      zend_hash_copy(Z_ARRVAL_P(return_value), &obj->message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
 +      array_copy(&obj->message->hdrs, Z_ARRVAL_P(return_value));
  }
 -static void php_http_message_object_prophandler_set_headers(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
 -      zval *cpy = php_http_ztyp(IS_ARRAY, value);
 +static void php_http_message_object_prophandler_set_headers(php_http_message_object_t *obj, zval *value) {
 +      HashTable *headers;
 +      zval *orig_value = value;
 +
 +      if (Z_TYPE_P(value) != IS_ARRAY && Z_TYPE_P(value) != IS_OBJECT) {
 +              convert_to_array_ex(value);
-       } else {
-               headers = HASH_OF(value);
 +      }
++      headers = HASH_OF(value);
  
        zend_hash_clean(&obj->message->hdrs);
 -      zend_hash_copy(&obj->message->hdrs, Z_ARRVAL_P(cpy), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
 -      zval_ptr_dtor(&cpy);
 +      array_copy(headers, &obj->message->hdrs);
 +
 +      if (orig_value != value) {
 +              zval_ptr_dtor(value);
 +      }
  }
 -static void php_http_message_object_prophandler_get_body(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
 +static void php_http_message_object_prophandler_get_body(php_http_message_object_t *obj, zval *return_value) {
        if (obj->body) {
 -              RETVAL_OBJVAL(obj->body->zv, 1);
 +              RETVAL_OBJECT(&obj->body->zo, 1);
        } else {
                RETVAL_NULL();
        }
@@@ -798,9 -832,13 +797,9 @@@ php_http_message_object_t *php_http_mes
  {
        php_http_message_object_t *o;
  
-       o = ecalloc(1, sizeof(php_http_message_object_t) + (ce->default_properties_count - 1) * sizeof(zval));
 -      o = ecalloc(1, sizeof(php_http_message_object_t));
 -      zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
 -      object_properties_init((zend_object *) o, ce);
 -
 -      if (ptr) {
 -              *ptr = o;
 -      }
++      o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
 +      zend_object_std_init(&o->zo, ce);
 +      object_properties_init(&o->zo, ce);
  
        if (msg) {
                o->message = msg;
@@@ -1105,20 -1157,26 +1104,23 @@@ static PHP_METHOD(HttpMessage, getHeade
  
                PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
  
 -              if ((header = php_http_message_header(obj->message, header_str, header_len, 0))) {
 +              if ((header = php_http_message_header(obj->message, header_str, header_len))) {
                        if (!header_ce) {
                                RETURN_ZVAL(header, 1, 1);
 -                      } else if (instanceof_function(header_ce, php_http_header_class_entry TSRMLS_CC)) {
 +                      } else if (instanceof_function(header_ce, php_http_header_class_entry)) {
+                               php_http_object_method_t cb;
 -                              zval *header_name, **argv[2];
 +                              zval argv[2];
  
 -                              MAKE_STD_ZVAL(header_name);
 -                              ZVAL_STRINGL(header_name, header_str, header_len, 1);
 -
 -                              argv[0] = &header_name;
 -                              argv[1] = &header;
 +                              ZVAL_STRINGL(&argv[0], header_str, header_len);
 +                              ZVAL_COPY(&argv[1], header);
  
                                object_init_ex(return_value, header_ce);
-                               php_http_method_call(return_value, ZEND_STRL("__construct"), 2, argv, NULL);
 -                              php_http_object_method_init(&cb, return_value, ZEND_STRL("__construct") TSRMLS_CC);
 -                              php_http_object_method_call(&cb, return_value, NULL, 2, argv TSRMLS_CC);
++                              php_http_object_method_init(&cb, return_value, ZEND_STRL("__construct"));
++                              php_http_object_method_call(&cb, return_value, NULL, 2, argv);
+                               php_http_object_method_dtor(&cb);
  
 -                              zval_ptr_dtor(&header_name);
 -                              zval_ptr_dtor(&header);
 +                              zval_ptr_dtor(&argv[0]);
 +                              zval_ptr_dtor(&argv[1]);
  
                                return;
                        } else {
@@@ -554,9 -573,13 +554,9 @@@ php_http_message_body_object_t *php_htt
  {
        php_http_message_body_object_t *o;
  
-       o = ecalloc(1, sizeof(php_http_message_body_object_t) + (ce->default_properties_count - 1) * sizeof(zval));
 -      o = ecalloc(1, sizeof(php_http_message_body_object_t));
 -      zend_object_std_init((zend_object *) o, php_http_message_body_class_entry TSRMLS_CC);
 -      object_properties_init((zend_object *) o, ce);
 -
 -      if (ptr) {
 -              *ptr = o;
 -      }
++      o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
 +      zend_object_std_init(&o->zo, php_http_message_body_class_entry);
 +      object_properties_init(&o->zo, ce);
  
        if (body) {
                o->body = body;
@@@ -517,9 -525,13 +517,9 @@@ php_http_message_parser_object_t *php_h
  {
        php_http_message_parser_object_t *o;
  
-       o = ecalloc(1, sizeof(php_http_message_parser_object_t) + (ce->default_properties_count - 1) * sizeof(zval));
 -      o = ecalloc(1, sizeof(php_http_message_parser_object_t));
 -      zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
 -      object_properties_init((zend_object *) o, ce);
 -
 -      if (ptr) {
 -              *ptr = o;
 -      }
++      o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
 +      zend_object_std_init(&o->zo, ce);
 +      object_properties_init(&o->zo, ce);
  
        if (parser) {
                o->parser = parser;
@@@ -23,25 -21,23 +23,24 @@@ php_http_object_t *php_http_object_new_
  {
        php_http_object_t *o;
  
-       o = ecalloc(1, sizeof(php_http_object_t) + (ce->default_properties_count - 1) * sizeof(zval));
 -      o = ecalloc(1, sizeof(php_http_object_t));
 -      zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
 -      object_properties_init((zend_object *) o, ce);
++      o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
 +      zend_object_std_init(&o->zo, ce);
 +      object_properties_init(&o->zo, ce);
  
 -      if (ptr) {
 -              *ptr = o;
 -      }
 +      o->intern = intern;
 +      o->zo.handlers = &php_http_object_handlers;
  
 -      o->zv.handle = zend_objects_store_put(o, NULL, (zend_objects_free_object_storage_t) zend_objects_free_object_storage, NULL TSRMLS_CC);
 -      o->zv.handlers = zend_get_std_object_handlers();
 +      return o;
 +}
  
 -      return o->zv;
 +void php_http_object_free(zend_object *object)
 +{
-       php_http_object_t *obj = PHP_HTTP_OBJ(object, NULL);
 +      zend_object_std_dtor(object);
  }
  
 -STATUS php_http_new(zend_object_value *ovp, zend_class_entry *ce, php_http_new_t create, zend_class_entry *parent_ce, void *intern_ptr, void **obj_ptr TSRMLS_DC)
 +ZEND_RESULT_CODE php_http_new(void **obj_ptr, zend_class_entry *ce, php_http_new_t create, zend_class_entry *parent_ce, void *intern_ptr)
  {
 -      zend_object_value ov;
 +      void *obj;
  
        if (!ce) {
                ce = parent_ce;
        return SUCCESS;
  }
  
- ZEND_RESULT_CODE php_http_method_call(zval *object, const char *method_str, size_t method_len, int argc, zval argv[], zval *retval_ptr)
 -php_http_object_method_t *php_http_object_method_init(php_http_object_method_t *cb, zval *zobject, const char *method_str, size_t method_len TSRMLS_DC)
++php_http_object_method_t *php_http_object_method_init(php_http_object_method_t *cb, zval *zobject, const char *method_str, size_t method_len)
+ {
 -      zval *zfn;
 -
+       if (!cb) {
+               cb = ecalloc(1, sizeof(*cb));
+       } else {
+               memset(cb, 0, sizeof(*cb));
+       }
 -      MAKE_STD_ZVAL(zfn);
 -      ZVAL_STRINGL(zfn, method_str, method_len, 1);
 -
+       cb->fci.size = sizeof(cb->fci);
 -      cb->fci.function_name = zfn;
++      ZVAL_STRINGL(&cb->fci.function_name, method_str, method_len);
+       cb->fcc.initialized = 1;
+       cb->fcc.calling_scope = cb->fcc.called_scope = Z_OBJCE_P(zobject);
 -      cb->fcc.function_handler = Z_OBJ_HT_P(zobject)->get_method(&zobject, Z_STRVAL_P(cb->fci.function_name), Z_STRLEN_P(cb->fci.function_name), NULL TSRMLS_CC);
++      cb->fcc.function_handler = Z_OBJ_HT_P(zobject)->get_method(&Z_OBJ_P(zobject), Z_STR(cb->fci.function_name), NULL);
+       return cb;
+ }
+ void php_http_object_method_dtor(php_http_object_method_t *cb)
+ {
 -      if (cb->fci.function_name) {
 -              zval_ptr_dtor(&cb->fci.function_name);
 -              cb->fci.function_name = NULL;
 -      }
++      zval_ptr_dtor(&cb->fci.function_name);
+ }
+ void php_http_object_method_free(php_http_object_method_t **cb)
+ {
+       if (*cb) {
+               php_http_object_method_dtor(*cb);
+               efree(*cb);
+               *cb = NULL;
+       }
+ }
 -STATUS php_http_object_method_call(php_http_object_method_t *cb, zval *zobject, zval **retval_ptr, int argc, zval ***args TSRMLS_DC)
++ZEND_RESULT_CODE php_http_object_method_call(php_http_object_method_t *cb, zval *zobject, zval *retval_ptr, int argc, zval *args)
  {
-       zend_fcall_info fci;
-       zval retval;
 -      STATUS rv;
 -      zval *retval = NULL;
 +      ZEND_RESULT_CODE rv;
++      zval retval;
  
-       fci.size = sizeof(fci);
-       fci.object = Z_OBJ_P(object);
-       fci.retval = retval_ptr ? retval_ptr : &retval;
-       fci.param_count = argc;
-       fci.params = argv;
-       fci.no_separation = 1;
-       fci.symbol_table = NULL;
-       fci.function_table = NULL;
-       ZVAL_STRINGL(&fci.function_name, method_str, method_len);
-       rv = zend_call_function(&fci, NULL TSRMLS_CC);
-       zval_ptr_dtor(&fci.function_name);
-       if (!retval_ptr) {
 +      ZVAL_UNDEF(&retval);
 -      cb->fci.object_ptr = zobject;
 -      cb->fcc.object_ptr = zobject;
+       Z_ADDREF_P(zobject);
 -      cb->fci.retval_ptr_ptr = retval_ptr ? retval_ptr : &retval;
++      cb->fci.object = Z_OBJ_P(zobject);
++      cb->fcc.object = Z_OBJ_P(zobject);
 -              cb->fcc.function_handler = Z_OBJ_HT_P(zobject)->get_method(&zobject, Z_STRVAL_P(cb->fci.function_name), Z_STRLEN_P(cb->fci.function_name), NULL TSRMLS_CC);
++      cb->fci.retval = retval_ptr ? retval_ptr : &retval;
+       cb->fci.param_count = argc;
+       cb->fci.params = args;
+       if (cb->fcc.called_scope != Z_OBJCE_P(zobject)) {
+               cb->fcc.called_scope = Z_OBJCE_P(zobject);
 -      rv = zend_call_function(&cb->fci, &cb->fcc TSRMLS_CC);
++              cb->fcc.function_handler = Z_OBJ_HT_P(zobject)->get_method(&Z_OBJ_P(zobject), Z_STR(cb->fci.function_name), NULL);
+       }
 -      zval_ptr_dtor(&zobject);
 -      if (!retval_ptr && retval) {
++      rv = zend_call_function(&cb->fci, &cb->fcc);
++      zval_ptr_dtor(zobject);
++      if (!retval_ptr && !Z_ISUNDEF(retval)) {
                zval_ptr_dtor(&retval);
        }
        return rv;
  }
  
  #define PHP_HTTP_OBJECT_H
  
  typedef struct php_http_object {
 +      void *intern;
        zend_object zo;
 -      zend_object_value zv;
  } php_http_object_t;
  
 -zend_object_value php_http_object_new(zend_class_entry *ce TSRMLS_DC);
 -zend_object_value php_http_object_new_ex(zend_class_entry *ce, void *nothing, php_http_object_t **ptr TSRMLS_DC);
 +zend_object *php_http_object_new(zend_class_entry *ce TSRMLS_DC);
 +php_http_object_t *php_http_object_new_ex(zend_class_entry *ce, void *nothing);
  
 -typedef zend_object_value (*php_http_new_t)(zend_class_entry *ce, void *, void ** TSRMLS_DC);
 +typedef void *(*php_http_new_t)(zend_class_entry *ce, void *);
  
 -STATUS php_http_new(zend_object_value *ov, zend_class_entry *ce, php_http_new_t create, zend_class_entry *parent_ce, void *intern_ptr, void **obj_ptr TSRMLS_DC);
 +ZEND_RESULT_CODE php_http_new(void **obj_ptr, zend_class_entry *ce, php_http_new_t create, zend_class_entry *parent_ce, void *intern_ptr);
- ZEND_RESULT_CODE php_http_method_call(zval *object, const char *method_str, size_t method_len, int argc, zval argv[], zval *retval_ptr);
 +
 +PHP_MINIT_FUNCTION(http_object);
  
 -php_http_object_method_t *php_http_object_method_init(php_http_object_method_t *cb, zval *zobject, const char *method_str, size_t method_len TSRMLS_DC);
 -STATUS php_http_object_method_call(php_http_object_method_t *cb, zval *zobject, zval **retval, int argc, zval ***args TSRMLS_DC);
+ typedef struct php_http_method {
+       zend_fcall_info fci;
+       zend_fcall_info_cache fcc;
+ } php_http_object_method_t;
++php_http_object_method_t *php_http_object_method_init(php_http_object_method_t *cb, zval *zobject, const char *method_str, size_t method_len);
++ZEND_RESULT_CODE php_http_object_method_call(php_http_object_method_t *cb, zval *zobject, zval *retval, int argc, zval *args);
+ void php_http_object_method_dtor(php_http_object_method_t *cb);
+ void php_http_object_method_free(php_http_object_method_t **cb);
  #endif
  
  
diff --cc php_http_url.c
@@@ -57,9 -57,9 +57,9 @@@ static inline char *localhostname(void
        return estrndup("localhost", lenof("localhost"));
  }
  
- #define url(buf) ((php_http_url_t *) buf.data)
+ #define url(buf) ((php_http_url_t *) (buf).data)
  
 -static php_http_url_t *php_http_url_from_env(TSRMLS_D)
 +static php_http_url_t *php_http_url_from_env(void)
  {
        zval *https, *zhost, *zport;
        long port;
        return url(buf);
  }
  
 -void php_http_url(int flags, const php_url *old_url, const php_url *new_url, php_url **url_ptr, char **url_str, size_t *url_len TSRMLS_DC)
 -{
 -      php_http_url_t *url = php_http_url_mod((const php_http_url_t *) old_url, (const php_http_url_t *) new_url, flags TSRMLS_CC);
 -
 -      if (url_ptr) {
 -              *url_ptr = php_http_url_to_php_url(url);
 -      }
 -      if (url_str) {
 -              php_http_url_to_string(url, url_str, url_len, 0);
 -      }
 -
 -      php_http_url_free(&url);
 -}
 -
  #define url_isset(u,n) \
        ((u)&&(u)->n)
+ #define url_append(buf, append) do { \
+       char *_ptr = (buf)->data; \
+       php_http_url_t *_url = (php_http_url_t *) _ptr, _mem = *_url; \
+       append; \
+       /* relocate */ \
+       if (_ptr != (buf)->data) { \
+               ptrdiff_t diff = (buf)->data - _ptr; \
+               _url = (php_http_url_t *) (buf)->data; \
+               if (_mem.scheme)        _url->scheme += diff; \
+               if (_mem.user)          _url->user += diff; \
+               if (_mem.pass)          _url->pass += diff; \
+               if (_mem.host)          _url->host += diff; \
+               if (_mem.path)          _url->path += diff; \
+               if (_mem.query)         _url->query += diff; \
+               if (_mem.fragment)      _url->fragment += diff; \
+       } \
+ } while (0)
  #define url_copy(n) do { \
        if (url_isset(new_url, n)) { \
                url(buf)->n = &buf.data[buf.used]; \
@@@ -209,20 -240,20 +226,20 @@@ php_http_url_t *php_http_url_mod(const 
                if ((flags & PHP_HTTP_URL_JOIN_QUERY) && url_isset(new_url, query) && url_isset(old_url, query)) {
                        zval qarr, qstr;
                        
 -                      INIT_PZVAL(&qstr);
 -                      INIT_PZVAL(&qarr);
                        array_init(&qarr);
                        
 -                      ZVAL_STRING(&qstr, old_url->query, 0);
 -                      php_http_querystring_update(&qarr, &qstr, NULL TSRMLS_CC);
 -                      ZVAL_STRING(&qstr, new_url->query, 0);
 -                      php_http_querystring_update(&qarr, &qstr, NULL TSRMLS_CC);
 +                      ZVAL_STRING(&qstr, old_url->query);
 +                      php_http_querystring_update(&qarr, &qstr, NULL);
 +                      zval_ptr_dtor(&qstr);
 +                      ZVAL_STRING(&qstr, new_url->query);
 +                      php_http_querystring_update(&qarr, &qstr, NULL);
 +                      zval_ptr_dtor(&qstr);
                        
                        ZVAL_NULL(&qstr);
 -                      php_http_querystring_update(&qarr, NULL, &qstr TSRMLS_CC);
 +                      php_http_querystring_update(&qarr, NULL, &qstr);
  
                        url(buf)->query = &buf.data[buf.used];
-                       php_http_buffer_append(&buf, Z_STRVAL(qstr), Z_STRLEN(qstr) + 1);
+                       url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL(qstr), Z_STRLEN(qstr) + 1));
  
                        zval_dtor(&qstr);
                        zval_dtor(&qarr);
@@@ -367,9 -398,45 +384,45 @@@ char *php_http_url_to_string(const php_
        return buf.data;
  }
  
 -php_http_url_t *php_http_url_from_zval(zval *value, unsigned flags TSRMLS_DC)
+ char *php_http_url_authority_to_string(const php_http_url_t *url, char **url_str, size_t *url_len)
+ {
+       php_http_buffer_t buf;
+       php_http_buffer_init(&buf);
+       if (url->user && *url->user) {
+               php_http_buffer_appendl(&buf, url->user);
+               if (url->pass && *url->pass) {
+                       php_http_buffer_appends(&buf, ":");
+                       php_http_buffer_appendl(&buf, url->pass);
+               }
+               php_http_buffer_appends(&buf, "@");
+       }
+       if (url->host && *url->host) {
+               php_http_buffer_appendl(&buf, url->host);
+               if (url->port) {
+                       php_http_buffer_appendf(&buf, ":%hu", url->port);
+               }
+       }
+       php_http_buffer_shrink(&buf);
+       php_http_buffer_fix(&buf);
+       if (url_len) {
+               *url_len = buf.used;
+       }
+       if (url_str) {
+               *url_str = buf.data;
+       }
+       return buf.data;
+ }
 +php_http_url_t *php_http_url_from_zval(zval *value, unsigned flags)
  {
 -      zval *zcpy;
 +      zend_string *zs;
        php_http_url_t *purl;
  
        switch (Z_TYPE_P(value)) {
@@@ -396,50 -463,52 +449,50 @@@ php_http_url_t *php_http_url_from_struc
        php_http_buffer_account(&buf, sizeof(php_http_url_t));
        memset(buf.data, 0, buf.used);
  
 -      if (SUCCESS == zend_hash_find(ht, "scheme", sizeof("scheme"), (void *) &e)) {
 -              zval *cpy = php_http_ztyp(IS_STRING, *e);
 +      if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("scheme")))) {
 +              zend_string *zs = zval_get_string(e);
                url(buf)->scheme = &buf.data[buf.used];
-               php_http_buffer_append(&buf, zs->val, zs->len + 1);
 -              url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
 -              zval_ptr_dtor(&cpy);
++              url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1));
 +              zend_string_release(zs);
        }
 -      if (SUCCESS == zend_hash_find(ht, "user", sizeof("user"), (void *) &e)) {
 -              zval *cpy = php_http_ztyp(IS_STRING, *e);
 +      if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("user")))) {
 +              zend_string *zs = zval_get_string(e);
                url(buf)->user = &buf.data[buf.used];
-               php_http_buffer_append(&buf, zs->val, zs->len + 1);
 -              url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
 -              zval_ptr_dtor(&cpy);
++              url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1));
 +              zend_string_release(zs);
        }
 -      if (SUCCESS == zend_hash_find(ht, "pass", sizeof("pass"), (void *) &e)) {
 -              zval *cpy = php_http_ztyp(IS_STRING, *e);
 +      if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("pass")))) {
 +              zend_string *zs = zval_get_string(e);
                url(buf)->pass = &buf.data[buf.used];
-               php_http_buffer_append(&buf, zs->val, zs->len + 1);
 -              url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
 -              zval_ptr_dtor(&cpy);
++              url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1));
 +              zend_string_release(zs);
        }
 -      if (SUCCESS == zend_hash_find(ht, "host", sizeof("host"), (void *) &e)) {
 -              zval *cpy = php_http_ztyp(IS_STRING, *e);
 +      if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("host")))) {
 +              zend_string *zs = zval_get_string(e);
                url(buf)->host = &buf.data[buf.used];
-               php_http_buffer_append(&buf, zs->val, zs->len + 1);
 -              url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
 -              zval_ptr_dtor(&cpy);
++              url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1));
 +              zend_string_release(zs);
        }
 -      if (SUCCESS == zend_hash_find(ht, "port", sizeof("port"), (void *) &e)) {
 -              zval *cpy = php_http_ztyp(IS_LONG, *e);
 -              url(buf)->port = (unsigned short) Z_LVAL_P(cpy);
 -              zval_ptr_dtor(&cpy);
 +      if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("port")))) {
 +              url(buf)->port = (unsigned short) zval_get_long(e);
        }
 -      if (SUCCESS == zend_hash_find(ht, "path", sizeof("path"), (void *) &e)) {
 -              zval *cpy = php_http_ztyp(IS_STRING, *e);
 +      if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("path")))) {
 +              zend_string *zs = zval_get_string(e);
                url(buf)->path = &buf.data[buf.used];
-               php_http_buffer_append(&buf, zs->val, zs->len + 1);
 -              url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
 -              zval_ptr_dtor(&cpy);
++              url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1));
 +              zend_string_release(zs);
        }
 -      if (SUCCESS == zend_hash_find(ht, "query", sizeof("query"), (void *) &e)) {
 -              zval *cpy = php_http_ztyp(IS_STRING, *e);
 +      if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("query")))) {
 +              zend_string *zs = zval_get_string(e);
                url(buf)->query = &buf.data[buf.used];
-               php_http_buffer_append(&buf, zs->val, zs->len + 1);
 -              url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
 -              zval_ptr_dtor(&cpy);
++              url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1));
 +              zend_string_release(zs);
        }
 -      if (SUCCESS == zend_hash_find(ht, "fragment", sizeof("fragment"), (void *) &e)) {
 -              zval *cpy = php_http_ztyp(IS_STRING, *e);
 +      if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("fragment")))) {
 +              zend_string *zs = zval_get_string(e);
                url(buf)->fragment = &buf.data[buf.used];
-               php_http_buffer_append(&buf, zs->val, zs->len + 1);
 -              url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
 -              zval_ptr_dtor(&cpy);
++              url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1));
 +              zend_string_release(zs);
        }
  
        return url(buf);
@@@ -868,8 -926,10 +921,10 @@@ static ZEND_RESULT_CODE parse_hostinfo(
                        break;
  
                default:
-                       if (port) {
+                       if (ptr == end) {
+                               break;
+                       } else if (port) {
 -                              php_error_docref(NULL TSRMLS_CC, E_WARNING,
 +                              php_error_docref(NULL, E_WARNING,
                                                "Failed to parse port; unexpected byte 0x%02x at pos %u in '%s'",
                                                (unsigned char) *ptr, (unsigned) (ptr - tmp), tmp);
                                return FAILURE;
diff --cc php_http_url.h
@@@ -56,22 -57,26 +57,23 @@@ typedef struct php_http_url 
        char *fragment;
  } php_http_url_t;
  
 -PHP_HTTP_API php_http_url_t *php_http_url_parse(const char *str, size_t len, unsigned flags TSRMLS_DC);
 -PHP_HTTP_API php_http_url_t *php_http_url_parse_authority(const char *str, size_t len, unsigned flags TSRMLS_DC);
 -/* deprecated */
 -PHP_HTTP_API void php_http_url(int flags, const php_url *old_url, const php_url *new_url, php_url **url_ptr, char **url_str, size_t *url_len TSRMLS_DC);
 -/* use this instead */
 -PHP_HTTP_API php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_url_t *new_url, unsigned flags TSRMLS_DC);
 +PHP_HTTP_API php_http_url_t *php_http_url_parse(const char *str, size_t len, unsigned flags);
++PHP_HTTP_API php_http_url_t *php_http_url_parse_authority(const char *str, size_t len, unsigned flags);
 +PHP_HTTP_API php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_url_t *new_url, unsigned flags);
  PHP_HTTP_API php_http_url_t *php_http_url_copy(const php_http_url_t *url, zend_bool persistent);
  PHP_HTTP_API php_http_url_t *php_http_url_from_struct(HashTable *ht);
 -PHP_HTTP_API php_http_url_t *php_http_url_from_zval(zval *value, unsigned flags TSRMLS_DC);
 -PHP_HTTP_API HashTable *php_http_url_to_struct(const php_http_url_t *url, zval *strct TSRMLS_DC);
 +PHP_HTTP_API php_http_url_t *php_http_url_from_zval(zval *value, unsigned flags);
 +PHP_HTTP_API HashTable *php_http_url_to_struct(const php_http_url_t *url, zval *strct);
  PHP_HTTP_API char *php_http_url_to_string(const php_http_url_t *url, char **url_str, size_t *url_len, zend_bool persistent);
+ PHP_HTTP_API char *php_http_url_authority_to_string(const php_http_url_t *url, char **url_str, size_t *url_len);
  PHP_HTTP_API void php_http_url_free(php_http_url_t **url);
  
 -PHP_HTTP_API STATUS php_http_url_encode_hash(HashTable *hash, const char *pre_encoded_str, size_t pre_encoded_len, char **encoded_str, size_t *encoded_len TSRMLS_DC);
 -PHP_HTTP_API STATUS php_http_url_encode_hash_ex(HashTable *hash, php_http_buffer_t *qstr, const char *arg_sep_str, size_t arg_sep_len, const char *val_sep_str, size_t val_sep_len, const char *pre_encoded_str, size_t pre_encoded_len TSRMLS_DC);
 +PHP_HTTP_API ZEND_RESULT_CODE php_http_url_encode_hash(HashTable *hash, const char *pre_encoded_str, size_t pre_encoded_len, char **encoded_str, size_t *encoded_len);
 +PHP_HTTP_API ZEND_RESULT_CODE php_http_url_encode_hash_ex(HashTable *hash, php_http_buffer_t *qstr, const char *arg_sep_str, size_t arg_sep_len, const char *val_sep_str, size_t val_sep_len, const char *pre_encoded_str, size_t pre_encoded_len);
  
 -static inline void php_http_url_argsep(const char **str, size_t *len TSRMLS_DC)
 +static inline void php_http_url_argsep(const char **str, size_t *len)
  {
 -      if (SUCCESS != php_http_ini_entry(ZEND_STRL("arg_separator.output"), str, len, 0 TSRMLS_CC) || !*len) {
 +      if (SUCCESS != php_http_ini_entry(ZEND_STRL("arg_separator.output"), str, len, 0) || !*len) {
                *str = PHP_HTTP_URL_ARGSEP;
                *len = lenof(PHP_HTTP_URL_ARGSEP);
        }
@@@ -24,10 -24,10 +24,10 @@@ php_http_version_t *php_http_version_in
        return v;
  }
  
 -php_http_version_t *php_http_version_parse(php_http_version_t *v, const char *str TSRMLS_DC)
 +php_http_version_t *php_http_version_parse(php_http_version_t *v, const char *str)
  {
        long major, minor;
-       char separator = 0, *stop = NULL;
+       char separator = 0;
        register const char *ptr = str;
  
        switch (*ptr) {