release 2.4.0
[m6w6/ext-http] / php_http_client_curl.c
index 0b9a4d3486020d0c64be2a9359e7b9c487695e81..18f1dec89ab2d27159219f08fba2013576366edd 100644 (file)
@@ -314,7 +314,7 @@ static int php_http_curle_body_callback(char *data, size_t n, size_t l, void *ar
        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;
@@ -410,17 +410,6 @@ static STATUS php_http_curle_get_info(CURL *ch, HashTable *info)
                add_assoc_zval_ex(&array, "ssl_engines", sizeof("ssl_engines"), subarray);
                curl_slist_free_all(s);
        }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_COOKIELIST, &s)) {
-               MAKE_STD_ZVAL(subarray);
-               array_init(subarray);
-               for (p = s; p; p = p->next) {
-                       if (p->data) {
-                               add_next_index_string(subarray, p->data, 1);
-                       }
-               }
-               add_assoc_zval_ex(&array, "cookies", sizeof("cookies"), subarray);
-               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);
        }
@@ -817,13 +806,13 @@ static void php_http_curlm_timer_callback(CURLM *multi, long timeout_ms, void *t
 
 /* curl options */
 
-static php_http_options_t php_http_curle_options;
+static php_http_options_t php_http_curle_options, php_http_curlm_options;
 
 #define PHP_HTTP_CURLE_OPTION_CHECK_STRLEN             0x0001
 #define PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR            0x0002
 #define PHP_HTTP_CURLE_OPTION_TRANSFORM_MS             0x0004
 
-static STATUS php_http_curle_option_set_ssl_verifyhost(php_http_option_t *opt, zval *val, void *userdata)
+static ZEND_RESULT_CODE php_http_curle_option_set_ssl_verifyhost(php_http_option_t *opt, zval *val, void *userdata)
 {
        php_http_client_curl_handler_t *curl = userdata;
        CURL *ch = curl->handle;
@@ -834,7 +823,7 @@ static STATUS php_http_curle_option_set_ssl_verifyhost(php_http_option_t *opt, z
        return SUCCESS;
 }
 
-static STATUS php_http_curle_option_set_cookiestore(php_http_option_t *opt, zval *val, void *userdata)
+static ZEND_RESULT_CODE php_http_curle_option_set_cookiestore(php_http_option_t *opt, zval *val, void *userdata)
 {
        php_http_client_curl_handler_t *curl = userdata;
        CURL *ch = curl->handle;
@@ -856,7 +845,7 @@ static STATUS php_http_curle_option_set_cookiestore(php_http_option_t *opt, zval
        return SUCCESS;
 }
 
-static STATUS php_http_curle_option_set_cookies(php_http_option_t *opt, zval *val, void *userdata)
+static ZEND_RESULT_CODE php_http_curle_option_set_cookies(php_http_option_t *opt, zval *val, void *userdata)
 {
        php_http_client_curl_handler_t *curl = userdata;
        CURL *ch = curl->handle;
@@ -903,7 +892,7 @@ static STATUS php_http_curle_option_set_cookies(php_http_option_t *opt, zval *va
        return SUCCESS;
 }
 
-static STATUS php_http_curle_option_set_encodecookies(php_http_option_t *opt, zval *val, void *userdata)
+static ZEND_RESULT_CODE php_http_curle_option_set_encodecookies(php_http_option_t *opt, zval *val, void *userdata)
 {
        php_http_client_curl_handler_t *curl = userdata;
 
@@ -911,7 +900,7 @@ static STATUS php_http_curle_option_set_encodecookies(php_http_option_t *opt, zv
        return SUCCESS;
 }
 
-static STATUS php_http_curle_option_set_lastmodified(php_http_option_t *opt, zval *val, void *userdata)
+static ZEND_RESULT_CODE php_http_curle_option_set_lastmodified(php_http_option_t *opt, zval *val, void *userdata)
 {
        php_http_client_curl_handler_t *curl = userdata;
        CURL *ch = curl->handle;
@@ -940,18 +929,21 @@ static STATUS php_http_curle_option_set_lastmodified(php_http_option_t *opt, zva
        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 !PHP_HTTP_CURL_VERSION(7,21,6)
+#      define CURLOPT_ACCEPT_ENCODING CURLOPT_ENCODING
+#endif
        if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_ACCEPT_ENCODING, Z_BVAL_P(val) ? "" : NULL)) {
                return FAILURE;
        }
        return SUCCESS;
 }
 
-static STATUS php_http_curle_option_set_etag(php_http_option_t *opt, zval *val, void *userdata)
+static ZEND_RESULT_CODE php_http_curle_option_set_etag(php_http_option_t *opt, zval *val, void *userdata)
 {
        php_http_client_curl_handler_t *curl = userdata;
        php_http_buffer_t header;
@@ -967,7 +959,7 @@ static STATUS php_http_curle_option_set_etag(php_http_option_t *opt, zval *val,
        return SUCCESS;
 }
 
-static STATUS php_http_curle_option_set_range(php_http_option_t *opt, zval *val, void *userdata)
+static ZEND_RESULT_CODE php_http_curle_option_set_range(php_http_option_t *opt, zval *val, void *userdata)
 {
        php_http_client_curl_handler_t *curl = userdata;
        CURL *ch = curl->handle;
@@ -1011,7 +1003,7 @@ static STATUS php_http_curle_option_set_range(php_http_option_t *opt, zval *val,
        return SUCCESS;
 }
 
-static STATUS php_http_curle_option_set_resume(php_http_option_t *opt, zval *val, void *userdata)
+static ZEND_RESULT_CODE php_http_curle_option_set_resume(php_http_option_t *opt, zval *val, void *userdata)
 {
        php_http_client_curl_handler_t *curl = userdata;
        CURL *ch = curl->handle;
@@ -1025,7 +1017,7 @@ static STATUS php_http_curle_option_set_resume(php_http_option_t *opt, zval *val
        return SUCCESS;
 }
 
-static STATUS php_http_curle_option_set_retrydelay(php_http_option_t *opt, zval *val, void *userdata)
+static ZEND_RESULT_CODE php_http_curle_option_set_retrydelay(php_http_option_t *opt, zval *val, void *userdata)
 {
        php_http_client_curl_handler_t *curl = userdata;
 
@@ -1033,7 +1025,7 @@ static STATUS php_http_curle_option_set_retrydelay(php_http_option_t *opt, zval
        return SUCCESS;
 }
 
-static STATUS php_http_curle_option_set_retrycount(php_http_option_t *opt, zval *val, void *userdata)
+static ZEND_RESULT_CODE php_http_curle_option_set_retrycount(php_http_option_t *opt, zval *val, void *userdata)
 {
        php_http_client_curl_handler_t *curl = userdata;
 
@@ -1041,7 +1033,7 @@ static STATUS php_http_curle_option_set_retrycount(php_http_option_t *opt, zval
        return SUCCESS;
 }
 
-static STATUS php_http_curle_option_set_redirect(php_http_option_t *opt, zval *val, void *userdata)
+static ZEND_RESULT_CODE php_http_curle_option_set_redirect(php_http_option_t *opt, zval *val, void *userdata)
 {
        php_http_client_curl_handler_t *curl = userdata;
        CURL *ch = curl->handle;
@@ -1054,7 +1046,7 @@ static STATUS php_http_curle_option_set_redirect(php_http_option_t *opt, zval *v
        return SUCCESS;
 }
 
-static STATUS php_http_curle_option_set_portrange(php_http_option_t *opt, zval *val, void *userdata)
+static ZEND_RESULT_CODE php_http_curle_option_set_portrange(php_http_option_t *opt, zval *val, void *userdata)
 {
        php_http_client_curl_handler_t *curl = userdata;
        CURL *ch = curl->handle;
@@ -1093,7 +1085,7 @@ static STATUS php_http_curle_option_set_portrange(php_http_option_t *opt, zval *
 }
 
 #if PHP_HTTP_CURL_VERSION(7,37,0)
-static STATUS php_http_curle_option_set_proxyheader(php_http_option_t *opt, zval *val, void *userdata)
+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;
        TSRMLS_FETCH_FROM_CTX(curl->client->ts);
@@ -1130,7 +1122,7 @@ static STATUS php_http_curle_option_set_proxyheader(php_http_option_t *opt, zval
 #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;
@@ -1159,8 +1151,8 @@ static STATUS php_http_curle_option_set_resolve(php_http_option_t *opt, zval *va
 }
 #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;
@@ -1210,6 +1202,13 @@ static void php_http_curle_options_init(php_http_options_t *registry TSRMLS_DC)
        }
 #endif
 
+#if PHP_HTTP_CURL_VERSION(7,40,0)
+       if ((opt = php_http_option_register(registry, ZEND_STRL("unix_socket_path"), CURLOPT_UNIX_SOCKET_PATH, IS_STRING))) {
+               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
+       }
+#endif
+
        /* dns */
        if ((opt = php_http_option_register(registry, ZEND_STRL("dns_cache_timeout"), CURLOPT_DNS_CACHE_TIMEOUT, IS_LONG))) {
                Z_LVAL(opt->defval) = 60;
@@ -1474,7 +1473,7 @@ static void php_http_curle_options_init(php_http_options_t *registry TSRMLS_DC)
                        opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
                }
 #endif
-#if PHP_HTTP_CURL_VERSION(7,21,4)
+#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;
                }
@@ -1500,13 +1499,13 @@ static zval *php_http_curle_get_option(php_http_option_t *opt, HashTable *option
        return option;
 }
 
-static STATUS php_http_curle_set_option(php_http_option_t *opt, zval *val, void *userdata)
+static ZEND_RESULT_CODE php_http_curle_set_option(php_http_option_t *opt, zval *val, void *userdata)
 {
        php_http_client_curl_handler_t *curl = userdata;
        CURL *ch = curl->handle;
        zval tmp;
        CURLcode rc = CURLE_OK;
-       STATUS rv = SUCCESS;
+       ZEND_RESULT_CODE rv = SUCCESS;
        TSRMLS_FETCH_FROM_CTX(curl->client->ts);
 
        if (!val) {
@@ -1581,10 +1580,186 @@ static STATUS php_http_curle_set_option(php_http_option_t *opt, zval *val, void
        return rv;
 }
 
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+static ZEND_RESULT_CODE php_http_curlm_option_set_pipelining_bl(php_http_option_t *opt, zval *value, void *userdata)
+{
+       php_http_client_t *client = userdata;
+       php_http_client_curl_t *curl = client->ctx;
+       CURLM *ch = curl->handle;
+       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) {
+               zval **entry;
+               HashPosition pos;
+               HashTable *ht = HASH_OF(value);
+               int c = zend_hash_num_elements(ht);
+               char **ptr = ecalloc(c + 1, sizeof(char *));
+
+               bl = ptr;
+
+               zend_hash_init(&tmp_ht, c, NULL, ZVAL_PTR_DTOR, 0);
+               array_join(ht, &tmp_ht, 0, ARRAY_JOIN_STRINGIFY);
+
+               FOREACH_HASH_VAL(pos, &tmp_ht, entry) {
+                       *ptr++ = Z_STRVAL_PP(entry);
+               }
+       }
+
+       if (CURLM_OK != curl_multi_setopt(ch, opt->option, bl)) {
+               if (bl) {
+                       efree(bl);
+                       zend_hash_destroy(&tmp_ht);
+               }
+               return FAILURE;
+       }
+
+       if (bl) {
+               efree(bl);
+               zend_hash_destroy(&tmp_ht);
+       }
+       return SUCCESS;
+}
+#endif
+
+#if PHP_HTTP_HAVE_EVENT
+static inline ZEND_RESULT_CODE php_http_curlm_use_eventloop(php_http_client_t *h, zend_bool enable)
+{
+       php_http_client_curl_t *curl = h->ctx;
+
+       if ((curl->useevents = enable)) {
+               if (!curl->evbase) {
+                       curl->evbase = event_base_new();
+               }
+               if (!curl->timeout) {
+                       curl->timeout = ecalloc(1, sizeof(struct event));
+               }
+               curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, h);
+               curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, php_http_curlm_socket_callback);
+               curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, h);
+               curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, php_http_curlm_timer_callback);
+       } else {
+               curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, NULL);
+               curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, NULL);
+               curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, NULL);
+               curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, NULL);
+       }
+
+       return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curlm_option_set_use_eventloop(php_http_option_t *opt, zval *value, void *userdata)
+{
+       php_http_client_t *client = userdata;
+
+       return php_http_curlm_use_eventloop(client, value && Z_BVAL_P(value));
+}
+#endif
+
+static void php_http_curlm_options_init(php_http_options_t *registry TSRMLS_DC)
+{
+       php_http_option_t *opt;
+
+       /* set size of connection cache */
+       if ((opt = php_http_option_register(registry, ZEND_STRL("maxconnects"), CURLMOPT_MAXCONNECTS, IS_LONG))) {
+               /* -1 == default, 0 == unlimited */
+               ZVAL_LONG(&opt->defval, -1);
+       }
+       /* set max number of connections to a single host */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+       php_http_option_register(registry, ZEND_STRL("max_host_connections"), CURLMOPT_MAX_HOST_CONNECTIONS, IS_LONG);
+#endif
+       /* maximum number of requests in a pipeline */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+       if ((opt = php_http_option_register(registry, ZEND_STRL("max_pipeline_length"), CURLMOPT_MAX_PIPELINE_LENGTH, IS_LONG))) {
+               ZVAL_LONG(&opt->defval, 5);
+       }
+#endif
+       /* max simultaneously open connections */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+       php_http_option_register(registry, ZEND_STRL("max_total_connections"), CURLMOPT_MAX_TOTAL_CONNECTIONS, IS_LONG);
+#endif
+       /* enable/disable HTTP pipelining */
+       php_http_option_register(registry, ZEND_STRL("pipelining"), CURLMOPT_PIPELINING, IS_BOOL);
+       /* chunk length threshold for pipelining */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+       php_http_option_register(registry, ZEND_STRL("chunk_length_penalty_size"), CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE, IS_LONG);
+#endif
+       /* size threshold for pipelining penalty */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+       php_http_option_register(registry, ZEND_STRL("content_length_penalty_size"), CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE, IS_LONG);
+#endif
+       /* pipelining server blacklist */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+       if ((opt = php_http_option_register(registry, ZEND_STRL("pipelining_server_bl"), CURLMOPT_PIPELINING_SERVER_BL, IS_ARRAY))) {
+               opt->setter = php_http_curlm_option_set_pipelining_bl;
+       }
+#endif
+       /* pipelining host blacklist */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+       if ((opt = php_http_option_register(registry, ZEND_STRL("pipelining_site_bl"), CURLMOPT_PIPELINING_SITE_BL, IS_ARRAY))) {
+               opt->setter = php_http_curlm_option_set_pipelining_bl;
+       }
+#endif
+       /* events */
+#if PHP_HTTP_HAVE_EVENT
+       if ((opt = php_http_option_register(registry, ZEND_STRL("use_eventloop"), 0, IS_BOOL))) {
+               opt->setter = php_http_curlm_option_set_use_eventloop;
+       }
+#endif
+}
+
+static ZEND_RESULT_CODE php_http_curlm_set_option(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_t *client = userdata;
+       php_http_client_curl_t *curl = client->ctx;
+       CURLM *ch = curl->handle;
+       zval *orig = val;
+       CURLMcode rc = CURLM_UNKNOWN_OPTION;
+       ZEND_RESULT_CODE rv = SUCCESS;
+       TSRMLS_FETCH_FROM_CTX(client->ts);
+
+       if (!val) {
+               val = &opt->defval;
+       } else if (opt->type && Z_TYPE_P(val) != opt->type && !(Z_TYPE_P(val) == IS_NULL && opt->type == IS_ARRAY)) {
+               val = php_http_ztyp(opt->type, val);
+       }
+
+       if (opt->setter) {
+               rv = opt->setter(opt, val, client);
+       } else {
+               switch (opt->type) {
+               case IS_BOOL:
+                       if (CURLM_OK != (rc = curl_multi_setopt(ch, opt->option, (long) Z_BVAL_P(val)))) {
+                               rv = FAILURE;
+                       }
+                       break;
+               case IS_LONG:
+                       if (CURLM_OK != (rc = curl_multi_setopt(ch, opt->option, Z_LVAL_P(val)))) {
+                               rv = FAILURE;
+                       }
+                       break;
+               default:
+                       rv = FAILURE;
+                       break;
+               }
+       }
+
+       if (val && val != orig && val != &opt->defval) {
+               zval_ptr_dtor(&val);
+       }
+
+       if (rv != SUCCESS) {
+               php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s (%s)", opt->name.s, curl_easy_strerror(rc));
+       }
+       return rv;
+}
 
 /* client ops */
 
-static STATUS php_http_client_curl_handler_reset(php_http_client_curl_handler_t *curl)
+static ZEND_RESULT_CODE php_http_client_curl_handler_reset(php_http_client_curl_handler_t *curl)
 {
        CURL *ch = curl->handle;
        php_http_curle_storage_t *st;
@@ -1690,7 +1865,7 @@ static php_http_client_curl_handler_t *php_http_client_curl_handler_init(php_htt
 }
 
 
-static STATUS php_http_client_curl_handler_prepare(php_http_client_curl_handler_t *curl, php_http_client_enqueue_t *enqueue)
+static ZEND_RESULT_CODE php_http_client_curl_handler_prepare(php_http_client_curl_handler_t *curl, php_http_client_enqueue_t *enqueue)
 {
        size_t body_size;
        php_http_message_t *msg = enqueue->request;
@@ -1889,33 +2064,48 @@ static void queue_dtor(php_http_client_enqueue_t *e)
        php_http_client_curl_handler_dtor(handler);
 }
 
-static php_resource_factory_t *create_rf(php_http_url_t *url TSRMLS_DC)
+static php_resource_factory_t *create_rf(php_http_client_t *h, php_http_client_enqueue_t *enqueue TSRMLS_DC)
 {
-       php_persistent_handle_factory_t *pf;
+       php_persistent_handle_factory_t *pf = NULL;
        php_resource_factory_t *rf = NULL;
-       char *id_str = NULL;
-       size_t id_len;
+       php_http_url_t *url = enqueue->request->http.info.request.url;
 
        if (!url || (!url->host && !url->path)) {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot request empty URL");
                return NULL;
        }
 
-       id_len = spprintf(&id_str, 0, "%s:%d", STR_PTR(url->host), url->port ? url->port : 80);
+       /* only if the client itself is setup for persistence */
+       if (h->rf->dtor == (void (*)(void*)) php_persistent_handle_abandon) {
+               char *id_str = NULL;
+               size_t id_len;
+               int port = url->port ? url->port : 80;
+               zval **zport;
+
+               if (SUCCESS == zend_hash_find(enqueue->options, ZEND_STRS("port"), (void *) &zport)) {
+                       zval *zcpy = php_http_ztyp(IS_LONG, *zport);
+
+                       if (Z_LVAL_P(zcpy)) {
+                               port = Z_LVAL_P(zcpy);
+                       }
+                       zval_ptr_dtor(&zcpy);
+               }
+
+               id_len = spprintf(&id_str, 0, "%s:%d", 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);
+       }
 
-       pf = php_persistent_handle_concede(NULL, ZEND_STRL("http\\Client\\Curl\\Request"), id_str, id_len, NULL, NULL TSRMLS_CC);
        if (pf) {
                rf = php_resource_factory_init(NULL, php_persistent_handle_get_resource_factory_ops(), pf, (void (*)(void*)) php_persistent_handle_abandon);
        } else {
                rf = php_resource_factory_init(NULL, &php_http_curle_resource_factory_ops, NULL, NULL);
        }
 
-       efree(id_str);
-
        return rf;
 }
 
-static STATUS php_http_client_curl_enqueue(php_http_client_t *h, php_http_client_enqueue_t *enqueue)
+static ZEND_RESULT_CODE php_http_client_curl_enqueue(php_http_client_t *h, php_http_client_enqueue_t *enqueue)
 {
        CURLMcode rs;
        php_http_client_curl_t *curl = h->ctx;
@@ -1924,7 +2114,7 @@ static STATUS php_http_client_curl_enqueue(php_http_client_t *h, php_http_client
        php_resource_factory_t *rf;
        TSRMLS_FETCH_FROM_CTX(h->ts);
 
-       rf = create_rf(enqueue->request->http.info.request.url TSRMLS_CC);
+       rf = create_rf(h, enqueue TSRMLS_CC);
        if (!rf) {
                return FAILURE;
        }
@@ -1960,7 +2150,7 @@ static STATUS php_http_client_curl_enqueue(php_http_client_t *h, php_http_client
        }
 }
 
-static STATUS php_http_client_curl_dequeue(php_http_client_t *h, php_http_client_enqueue_t *enqueue)
+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;
@@ -2005,7 +2195,7 @@ static inline void php_http_client_curl_get_timeout(php_http_client_curl_t *curl
 #      define SELECT_ERROR -1
 #endif
 
-static STATUS php_http_client_curl_wait(php_http_client_t *h, struct timeval *custom_timeout)
+static ZEND_RESULT_CODE php_http_client_curl_wait(php_http_client_t *h, struct timeval *custom_timeout)
 {
        int MAX;
        fd_set R, W, E;
@@ -2067,7 +2257,7 @@ static int php_http_client_curl_once(php_http_client_t *h)
 
 }
 
-static STATUS php_http_client_curl_exec(php_http_client_t *h)
+static ZEND_RESULT_CODE php_http_client_curl_exec(php_http_client_t *h)
 {
 #if PHP_HTTP_HAVE_EVENT
        php_http_client_curl_t *curl = h->ctx;
@@ -2108,11 +2298,15 @@ static STATUS php_http_client_curl_exec(php_http_client_t *h)
        return SUCCESS;
 }
 
-static STATUS php_http_client_curl_setopt(php_http_client_t *h, php_http_client_setopt_opt_t opt, void *arg)
+static ZEND_RESULT_CODE php_http_client_curl_setopt(php_http_client_t *h, php_http_client_setopt_opt_t opt, void *arg)
 {
        php_http_client_curl_t *curl = h->ctx;
 
        switch (opt) {
+               case PHP_HTTP_CLIENT_OPT_CONFIGURATION:
+                       return php_http_options_apply(&php_http_curlm_options, (HashTable *) arg,  h);
+                       break;
+
                case PHP_HTTP_CLIENT_OPT_ENABLE_PIPELINING:
                        if (CURLM_OK != curl_multi_setopt(curl->handle, CURLMOPT_PIPELINING, (long) *((zend_bool *) arg))) {
                                return FAILURE;
@@ -2121,23 +2315,7 @@ static STATUS php_http_client_curl_setopt(php_http_client_t *h, php_http_client_
 
                case PHP_HTTP_CLIENT_OPT_USE_EVENTS:
 #if PHP_HTTP_HAVE_EVENT
-                       if ((curl->useevents = *((zend_bool *) arg))) {
-                               if (!curl->evbase) {
-                                       curl->evbase = event_base_new();
-                               }
-                               if (!curl->timeout) {
-                                       curl->timeout = ecalloc(1, sizeof(struct event));
-                               }
-                               curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, h);
-                               curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, php_http_curlm_socket_callback);
-                               curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, h);
-                               curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, php_http_curlm_timer_callback);
-                       } else {
-                               curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, NULL);
-                               curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, NULL);
-                               curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, NULL);
-                               curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, NULL);
-                       }
+                       return php_http_curlm_use_eventloop(h, *(zend_bool *) arg);
                        break;
 #endif
 
@@ -2147,9 +2325,43 @@ static STATUS php_http_client_curl_setopt(php_http_client_t *h, php_http_client_
        return SUCCESS;
 }
 
-static STATUS php_http_client_curl_getopt(php_http_client_t *h, php_http_client_getopt_opt_t opt, void *arg, void **res)
+static int apply_available_options(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
+{
+       php_http_option_t *opt = pDest;
+       HashTable *ht;
+       zval *entry;
+       int c;
+
+       ht = va_arg(args, HashTable*);
+
+       MAKE_STD_ZVAL(entry);
+
+       if ((c = zend_hash_num_elements(&opt->suboptions.options))) {
+               array_init_size(entry, c);
+               zend_hash_apply_with_arguments(&opt->suboptions.options TSRMLS_CC, apply_available_options, 1, Z_ARRVAL_P(entry));
+       } else {
+               /* catch deliberate NULL options */
+               if (Z_TYPE(opt->defval) == IS_STRING && !Z_STRVAL(opt->defval)) {
+                       ZVAL_NULL(entry);
+               } else {
+                       ZVAL_COPY_VALUE(entry, &opt->defval);
+                       zval_copy_ctor(entry);
+               }
+       }
+
+       if (hash_key->nKeyLength) {
+               zend_hash_quick_update(ht, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &entry, sizeof(zval *), NULL);
+       } else {
+               zend_hash_index_update(ht, hash_key->h, (void *) &entry, sizeof(zval *), NULL);
+       }
+
+       return ZEND_HASH_APPLY_KEEP;
+}
+
+static ZEND_RESULT_CODE php_http_client_curl_getopt(php_http_client_t *h, php_http_client_getopt_opt_t opt, void *arg, void **res)
 {
        php_http_client_enqueue_t *enqueue;
+       TSRMLS_FETCH_FROM_CTX(h->ts);
 
        switch (opt) {
        case PHP_HTTP_CLIENT_OPT_PROGRESS_INFO:
@@ -2170,6 +2382,14 @@ static STATUS php_http_client_curl_getopt(php_http_client_t *h, php_http_client_
                }
                break;
 
+       case PHP_HTTP_CLIENT_OPT_AVAILABLE_OPTIONS:
+               zend_hash_apply_with_arguments(&php_http_curle_options.options TSRMLS_CC, apply_available_options, 1, *(HashTable **) res);
+               break;
+
+       case PHP_HTTP_CLIENT_OPT_AVAILABLE_CONFIGURATION:
+               zend_hash_apply_with_arguments(&php_http_curlm_options.options TSRMLS_CC, apply_available_options, 1, *(HashTable **) res);
+               break;
+
        default:
                break;
        }
@@ -2206,8 +2426,8 @@ PHP_MINIT_FUNCTION(http_client_curl)
        };
 
        if (SUCCESS != php_http_client_driver_add(&driver)) {
-                       return FAILURE;
-               }
+               return FAILURE;
+       }
 
        if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl"), &php_http_curlm_resource_factory_ops, NULL, NULL TSRMLS_CC)) {
                return FAILURE;
@@ -2222,6 +2442,12 @@ PHP_MINIT_FUNCTION(http_client_curl)
 
                php_http_curle_options_init(options TSRMLS_CC);
        }
+       if ((options = php_http_options_init(&php_http_curlm_options, 1))) {
+               options->getter = php_http_option_get;
+               options->setter = php_http_curlm_set_option;
+
+               php_http_curlm_options_init(options TSRMLS_CC);
+       }
 
        /*
        * HTTP Protocol Version Constants
@@ -2245,7 +2471,7 @@ PHP_MINIT_FUNCTION(http_client_curl)
        REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_SSLv2", CURL_SSLVERSION_SSLv2, CONST_CS|CONST_PERSISTENT);
        REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_SSLv3", CURL_SSLVERSION_SSLv3, CONST_CS|CONST_PERSISTENT);
        REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_ANY", CURL_SSLVERSION_DEFAULT, CONST_CS|CONST_PERSISTENT);
-#if PHP_HTTP_CURL_VERSION(7,21,4)
+#if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP)
        REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "TLSAUTH_SRP", CURL_TLSAUTH_SRP, CONST_CS|CONST_PERSISTENT);
 #endif
 
@@ -2304,6 +2530,7 @@ PHP_MSHUTDOWN_FUNCTION(http_client_curl)
        php_persistent_handle_cleanup(ZEND_STRL("http\\Client\\Curl\\Request"), NULL, 0 TSRMLS_CC);
 
        php_http_options_dtor(&php_http_curle_options);
+       php_http_options_dtor(&php_http_curlm_options);
 
        return SUCCESS;
 }