Merge branch 'v2.6.x' of github.com:m6w6/ext-http
authorMichael Wallner <mike@php.net>
Wed, 27 Apr 2016 09:34:04 +0000 (11:34 +0200)
committerMichael Wallner <mike@php.net>
Wed, 27 Apr 2016 09:34:04 +0000 (11:34 +0200)
12 files changed:
config9.m4
src/php_http_client_curl.c
src/php_http_curl.h
src/php_http_header.c
src/php_http_header.h
src/php_http_message.c
tests/client021.phpt
tests/client027.phpt [new file with mode: 0644]
tests/helper/server.inc
tests/message005.phpt
tests/message011.phpt
tests/propertyproxy001.phpt

index 5c697d0..3c02edc 100644 (file)
@@ -409,19 +409,44 @@ dnl ----
 
                        dnl end compile tests
 
-                       AC_MSG_CHECKING([for bundled SSL CA info])
-                       CURL_CAINFO=
-                       for i in `$CURL_CONFIG --ca` "/etc/ssl/certs/ca-certificates.crt" "/etc/ssl/certs/ca-bundle.crt"; do
-                               if test -f "$i"; then
-                                       CURL_CAINFO="$i"
-                                       break
+                       AC_MSG_CHECKING([for default SSL CA info/path])
+                       CURL_CA_PATH=
+                       CURL_CA_INFO=
+                       CURL_CONFIG_CA=$($CURL_CONFIG --ca)
+                       if test -z "$CURL_CONFIG_CA"; then
+                               CURL_CONFIG_CA=$($CURL_CONFIG --configure  | $EGREP -o -- "--with-ca@<:@^'@:>@*" | $SED 's/.*=//')
+                       fi
+                       for i in \
+                               "$CURL_CONFIG_CA" \
+                               /etc/ssl/certs \
+                               /etc/ssl/certs/ca-bundle.crt \
+                               /etc/ssl/certs/ca-certificates.crt \
+                               /etc/pki/tls/certs/ca-bundle.crt \
+                               /etc/pki/tls/certs/ca-bundle.trust.crt \
+                               /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem \
+                               /System/Library/OpenSSL
+                       do
+                               if test -z "$CURL_CA_PATH" && test -d "$i"; then
+                                       # check if it's actually a hashed directory
+                                       if ls "$i"/@<:@0-9a-f@:>@@<:@0-9a-f@:>@@<:@0-9a-f@:>@@<:@0-9a-f@:>@@<:@0-9a-f@:>@@<:@0-9a-f@:>@@<:@0-9a-f@:>@@<:@0-9a-f@:>@.0 >/dev/null 2>&1; then
+                                               CURL_CA_PATH="$i"
+                                       fi
+                               elif test -z "$CURL_CA_INFO" && test -f "$i"; then
+                                       CURL_CA_INFO="$i"
                                fi
                        done
-                       if test "x$CURL_CAINFO" = "x"; then
-                               AC_MSG_RESULT([not found])
+                       if test -n "$CURL_CA_PATH" && test -n "$CURL_CA_INFO"; then
+                               AC_MSG_RESULT([path:$CURL_CA_PATH, info:$CURL_CA_INFO])
+                               AC_DEFINE_UNQUOTED([PHP_HTTP_CURL_CAPATH], ["$CURL_CA_PATH"], [path to default SSL CA path])
+                               AC_DEFINE_UNQUOTED([PHP_HTTP_CURL_CAINFO], ["$CURL_CA_INFO"], [path to default SSL CA info])
+                       elif test -n "$CURL_CA_INFO"; then
+                               AC_MSG_RESULT([info:$CURL_CA_INFO])
+                               AC_DEFINE_UNQUOTED([PHP_HTTP_CURL_CAINFO], ["$CURL_CA_INFO"], [path to default SSL CA info])
+                       elif test -n "$CURL_CA_PATH"; then
+                               AC_MSG_RESULT([path:$CURL_CA_PATH])
+                               AC_DEFINE_UNQUOTED([PHP_HTTP_CURL_CAPATH], ["$CURL_CA_PATH"], [path to default SSL CA path])
                        else
-                               AC_MSG_RESULT([$CURL_CAINFO])
-                               AC_DEFINE_UNQUOTED([PHP_HTTP_CURL_CAINFO], ["$CURL_CAINFO"], [path to bundled SSL CA info])
+                               AC_MSG_RESULT([none])
                        fi
 
                        PHP_ADD_INCLUDE($CURL_DIR/include)
index e56f8d4..cc62c15 100644 (file)
 #      include <gnutls.h>
 #endif
 
+typedef struct php_http_client_curl_handle {
+       CURLM *multi;
+       CURLSH *share;
+} php_http_client_curl_handle_t;
+
 typedef struct php_http_client_curl {
-       CURLM *handle;
+       php_http_client_curl_handle_t *handle;
 
        int unfinished;  /* int because of curl_multi_perform() */
 
@@ -158,12 +163,29 @@ static php_resource_factory_ops_t php_http_curle_resource_factory_ops = {
 
 static void *php_http_curlm_ctor(void *opaque, void *init_arg)
 {
-       return curl_multi_init();
+       php_http_client_curl_handle_t *curl = calloc(1, sizeof(*curl));
+
+       if (!(curl->multi = curl_multi_init())) {
+               free(curl);
+               return NULL;
+       }
+       if (!(curl->share = curl_share_init())) {
+               curl_multi_cleanup(curl->multi);
+               free(curl);
+               return NULL;
+       }
+       curl_share_setopt(curl->share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
+       curl_share_setopt(curl->share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
+       return curl;
 }
 
 static void php_http_curlm_dtor(void *opaque, void *handle)
 {
-       curl_multi_cleanup(handle);
+       php_http_client_curl_handle_t *curl = handle;
+
+       curl_share_cleanup(curl->share);
+       curl_multi_cleanup(curl->multi);
+       free(handle);
 }
 
 static php_resource_factory_ops_t php_http_curlm_resource_factory_ops = {
@@ -667,7 +689,7 @@ static void php_http_curlm_responsehandler(php_http_client_t *context)
        php_http_client_curl_t *curl = context->ctx;
 
        do {
-               CURLMsg *msg = curl_multi_info_read(curl->handle, &remaining);
+               CURLMsg *msg = curl_multi_info_read(curl->handle->multi, &remaining);
 
                if (msg && CURLMSG_DONE == msg->msg) {
                        if (CURLE_OK != msg->data.result) {
@@ -749,7 +771,7 @@ static void php_http_curlm_timeout_callback(int socket, short action, void *even
                (void) socket;
                (void) action;
 
-               while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle, CURL_SOCKET_TIMEOUT, 0, &curl->unfinished)));
+               while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle->multi, CURL_SOCKET_TIMEOUT, 0, &curl->unfinished)));
 
                if (CURLM_OK != rc) {
                        php_error_docref(NULL, E_WARNING, "%s",  curl_multi_strerror(rc));
@@ -770,7 +792,7 @@ static void php_http_curlm_event_callback(int socket, short action, void *event_
        if (curl->useevents) {
                CURLMcode rc = CURLM_OK;
 
-               while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle, socket, etoca(action), &curl->unfinished)));
+               while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle->multi, socket, etoca(action), &curl->unfinished)));
 
                if (CURLM_OK != rc) {
                        php_error_docref(NULL, E_WARNING, "%s", curl_multi_strerror(rc));
@@ -800,7 +822,7 @@ static int php_http_curlm_socket_callback(CURL *easy, curl_socket_t sock, int ac
                if (!ev) {
                        ev = ecalloc(1, sizeof(php_http_curlm_event_t));
                        ev->context = context;
-                       curl_multi_assign(curl->handle, sock, ev);
+                       curl_multi_assign(curl->handle->multi, sock, ev);
                } else {
                        event_del(&ev->evnt);
                }
@@ -901,6 +923,7 @@ static ZEND_RESULT_CODE php_http_curle_option_set_cookiestore(php_http_option_t
        ) {
                return FAILURE;
        }
+
        return SUCCESS;
 }
 
@@ -1234,9 +1257,7 @@ static void php_http_curle_options_init(php_http_options_t *registry)
 #endif
 
        /* proxy */
-       if ((opt = php_http_option_register(registry, ZEND_STRL("proxyhost"), CURLOPT_PROXY, IS_STRING))) {
-               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-       }
+       php_http_option_register(registry, ZEND_STRL("proxyhost"), CURLOPT_PROXY, IS_STRING);
        php_http_option_register(registry, ZEND_STRL("proxytype"), CURLOPT_PROXYTYPE, IS_LONG);
        php_http_option_register(registry, ZEND_STRL("proxyport"), CURLOPT_PROXYPORT, IS_LONG);
        if ((opt = php_http_option_register(registry, ZEND_STRL("proxyauth"), CURLOPT_PROXYUSERPWD, IS_STRING))) {
@@ -1256,7 +1277,9 @@ static void php_http_curle_options_init(php_http_options_t *registry)
        }
 #endif
 #if PHP_HTTP_CURL_VERSION(7,43,0)
-       if ((opt = php_http_option_register(registry, ZEND_STRL("proxy_service_name"), CURLOPT_PROXY_SERVICE_NAME, IS_STRING))) {
+       if (PHP_HTTP_CURL_FEATURE(CURL_VERSION_GSSAPI)
+       && (opt = php_http_option_register(registry, ZEND_STRL("proxy_service_name"), CURLOPT_PROXY_SERVICE_NAME, IS_STRING))
+       ) {
                opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
        }
 #endif
@@ -1498,6 +1521,9 @@ static void php_http_curle_options_init(php_http_options_t *registry)
                if ((opt = php_http_option_register(registry, ZEND_STRL("capath"), CURLOPT_CAPATH, IS_STRING))) {
                        opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
                        opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
+#ifdef PHP_HTTP_CURL_CAPATH
+                       ZVAL_STRING(&opt->defval, PHP_HTTP_CURL_CAPATH, 0);
+#endif
                }
                if ((opt = php_http_option_register(registry, ZEND_STRL("random_file"), CURLOPT_RANDOM_FILE, IS_STRING))) {
                        opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
@@ -1661,7 +1687,7 @@ static ZEND_RESULT_CODE php_http_curlm_option_set_pipelining_bl(php_http_option_
 {
        php_http_client_t *client = userdata;
        php_http_client_curl_t *curl = client->ctx;
-       CURLM *ch = curl->handle;
+       CURLM *ch = curl->handle->multi;
        HashTable tmp_ht;
        char **bl = NULL;
 
@@ -1712,15 +1738,15 @@ static inline ZEND_RESULT_CODE php_http_curlm_use_eventloop(php_http_client_t *h
                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);
+               curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, h);
+               curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, php_http_curlm_socket_callback);
+               curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, h);
+               curl_multi_setopt(curl->handle->multi, 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);
+               curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, NULL);
+               curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, NULL);
+               curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, NULL);
+               curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, NULL);
        }
 
        return SUCCESS;
@@ -1791,7 +1817,7 @@ static ZEND_RESULT_CODE php_http_curlm_set_option(php_http_option_t *opt, zval *
 {
        php_http_client_t *client = userdata;
        php_http_client_curl_t *curl = client->ctx;
-       CURLM *ch = curl->handle;
+       CURLM *ch = curl->handle->multi;
        zval *orig = val;
        CURLMcode rc = CURLM_UNKNOWN_OPTION;
        ZEND_RESULT_CODE rv = SUCCESS;
@@ -1854,6 +1880,7 @@ static ZEND_RESULT_CODE php_http_client_curl_handler_reset(php_http_client_curl_
                        st->cookiestore = NULL;
                }
                st->errorbuffer[0] = '\0';
+               st->errorcode = 0;
        }
 
        curl_easy_setopt(ch, CURLOPT_URL, NULL);
@@ -1897,6 +1924,7 @@ static ZEND_RESULT_CODE php_http_client_curl_handler_reset(php_http_client_curl_
 static php_http_client_curl_handler_t *php_http_client_curl_handler_init(php_http_client_t *h, php_resource_factory_t *rf)
 {
        void *handle;
+       php_http_client_curl_t *curl = h->ctx;
        php_http_client_curl_handler_t *handler;
 
        if (!(handle = php_resource_factory_handle_ctor(rf, NULL))) {
@@ -1937,6 +1965,7 @@ static php_http_client_curl_handler_t *php_http_client_curl_handler_init(php_htt
        curl_easy_setopt(handle, CURLOPT_DEBUGDATA, handler);
        curl_easy_setopt(handle, CURLOPT_WRITEDATA, handler);
        curl_easy_setopt(handle, CURLOPT_HEADERDATA, handler);
+       curl_easy_setopt(handle, CURLOPT_SHARE, curl->handle->share);
 
        php_http_client_curl_handler_reset(handler);
 
@@ -2061,6 +2090,8 @@ static void php_http_client_curl_handler_clear(php_http_client_curl_handler_t *h
 #endif
        curl_easy_setopt(handler->handle, CURLOPT_VERBOSE, 0L);
        curl_easy_setopt(handler->handle, CURLOPT_DEBUGFUNCTION, NULL);
+       curl_easy_setopt(handler->handle, CURLOPT_COOKIELIST, "FLUSH");
+       curl_easy_setopt(handler->handle, CURLOPT_SHARE, NULL);
 }
 
 static void php_http_client_curl_handler_dtor(php_http_client_curl_handler_t *handler)
@@ -2167,6 +2198,7 @@ static php_resource_factory_t *create_rf(php_http_client_t *h, php_http_client_e
                size_t id_len;
                int port = url->port ? url->port : 80;
                zval *zport;
+               php_persistent_handle_factory_t *phf = h->rf->data;
 
                if ((zport = zend_hash_str_find(enqueue->options, ZEND_STRL("port")))) {
                        zend_long lport = zval_get_long(zport);
@@ -2176,7 +2208,7 @@ static php_resource_factory_t *create_rf(php_http_client_t *h, php_http_client_e
                        }
                }
 
-               id_len = spprintf(&id_str, 0, "%s:%d", STR_PTR(url->host), port);
+               id_len = spprintf(&id_str, 0, "%.*s:%s:%d", (int) phf->ident->len, phf->ident->val, STR_PTR(url->host), port);
                id = php_http_cs2zs(id_str, id_len);
                pf = php_persistent_handle_concede(NULL, PHP_HTTP_G->client.curl.driver.request_name, id, NULL, NULL);
                zend_string_release(id);
@@ -2218,21 +2250,22 @@ static ZEND_RESULT_CODE php_http_client_curl_enqueue(php_http_client_t *h, php_h
        enqueue->opaque = handler;
        enqueue->dtor = queue_dtor;
 
-       if (CURLM_OK == (rs = curl_multi_add_handle(curl->handle, handler->handle))) {
-               zend_llist_add_element(&h->requests, enqueue);
-               ++curl->unfinished;
-
-               if (h->callback.progress.func && SUCCESS == php_http_client_getopt(h, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, enqueue->request, &progress)) {
-                       progress->info = "start";
-                       h->callback.progress.func(h->callback.progress.arg, h, &handler->queue, progress);
-                       progress->started = 1;
-               }
-
-               return SUCCESS;
-       } else {
+       if (CURLM_OK != (rs = curl_multi_add_handle(curl->handle->multi, handler->handle))) {
+               php_http_client_curl_handler_dtor(handler);
                php_error_docref(NULL, E_WARNING, "Could not enqueue request: %s", curl_multi_strerror(rs));
                return FAILURE;
        }
+
+       zend_llist_add_element(&h->requests, enqueue);
+       ++curl->unfinished;
+
+       if (h->callback.progress.func && SUCCESS == php_http_client_getopt(h, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, enqueue->request, &progress)) {
+               progress->info = "start";
+               h->callback.progress.func(h->callback.progress.arg, h, &handler->queue, progress);
+               progress->started = 1;
+       }
+
+       return SUCCESS;
 }
 
 static ZEND_RESULT_CODE php_http_client_curl_dequeue(php_http_client_t *h, php_http_client_enqueue_t *enqueue)
@@ -2242,7 +2275,7 @@ static ZEND_RESULT_CODE php_http_client_curl_dequeue(php_http_client_t *h, php_h
        php_http_client_curl_handler_t *handler = enqueue->opaque;
 
        php_http_client_curl_handler_clear(handler);
-       if (CURLM_OK == (rs = curl_multi_remove_handle(curl->handle, handler->handle))) {
+       if (CURLM_OK == (rs = curl_multi_remove_handle(curl->handle->multi, handler->handle))) {
                zend_llist_del_element(&h->requests, handler->handle, (int (*)(void *, void *)) compare_queue);
                return SUCCESS;
        } else {
@@ -2264,7 +2297,7 @@ static void php_http_client_curl_reset(php_http_client_t *h)
 
 static inline void php_http_client_curl_get_timeout(php_http_client_curl_t *curl, long max_tout, struct timeval *timeout)
 {
-       if ((CURLM_OK == curl_multi_timeout(curl->handle, &max_tout)) && (max_tout > 0)) {
+       if ((CURLM_OK == curl_multi_timeout(curl->handle->multi, &max_tout)) && (max_tout > 0)) {
                timeout->tv_sec = max_tout / 1000;
                timeout->tv_usec = (max_tout % 1000) * 1000;
        } else {
@@ -2307,7 +2340,7 @@ static ZEND_RESULT_CODE php_http_client_curl_wait(php_http_client_t *h, struct t
        FD_ZERO(&W);
        FD_ZERO(&E);
 
-       if (CURLM_OK == curl_multi_fdset(curl->handle, &R, &W, &E, &MAX)) {
+       if (CURLM_OK == curl_multi_fdset(curl->handle->multi, &R, &W, &E, &MAX)) {
                if (custom_timeout && timerisset(custom_timeout)) {
                        timeout = *custom_timeout;
                } else {
@@ -2333,7 +2366,7 @@ static int php_http_client_curl_once(php_http_client_t *h)
                event_base_loop(curl->evbase, EVLOOP_NONBLOCK);
        } else
 #endif
-       while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curl->handle, &curl->unfinished));
+       while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curl->handle->multi, &curl->unfinished));
 
        php_http_curlm_responsehandler(h);
 
@@ -2389,7 +2422,7 @@ static ZEND_RESULT_CODE php_http_client_curl_setopt(php_http_client_t *h, php_ht
                        break;
 
                case PHP_HTTP_CLIENT_OPT_ENABLE_PIPELINING:
-                       if (CURLM_OK != curl_multi_setopt(curl->handle, CURLMOPT_PIPELINING, (long) *((zend_bool *) arg))) {
+                       if (CURLM_OK != curl_multi_setopt(curl->handle->multi, CURLMOPT_PIPELINING, (long) *((zend_bool *) arg))) {
                                return FAILURE;
                        }
                        break;
@@ -2496,6 +2529,7 @@ php_http_client_ops_t *php_http_client_curl_get_ops(void)
 
 PHP_MINIT_FUNCTION(http_client_curl)
 {
+       curl_version_info_data *info;
        php_http_options_t *options;
 
        PHP_HTTP_G->client.curl.driver.driver_name = zend_string_init(ZEND_STRL("curl"), 1);
@@ -2527,6 +2561,62 @@ PHP_MINIT_FUNCTION(http_client_curl)
                php_http_curlm_options_init(options);
        }
 
+       if ((info = curl_version_info(CURLVERSION_NOW))) {
+               /*
+                * Feature constants
+                */
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "FEATURES", info->features, CONST_CS|CONST_PERSISTENT);
+
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "IPV6", CURL_VERSION_IPV6, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "KERBEROS4", CURL_VERSION_KERBEROS4, CONST_CS|CONST_PERSISTENT);
+#if PHP_HTTP_CURL_VERSION(7,40,0)
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "KERBEROS5", CURL_VERSION_KERBEROS5, CONST_CS|CONST_PERSISTENT);
+#endif
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "SSL", CURL_VERSION_SSL, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "LIBZ", CURL_VERSION_LIBZ, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "NTLM", CURL_VERSION_NTLM, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "GSSNEGOTIATE", CURL_VERSION_GSSNEGOTIATE, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "ASYNCHDNS", CURL_VERSION_ASYNCHDNS, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "SPNEGO", CURL_VERSION_SPNEGO, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "LARGEFILE", CURL_VERSION_LARGEFILE, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "IDN", CURL_VERSION_IDN, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "SSPI", CURL_VERSION_SSPI, CONST_CS|CONST_PERSISTENT);
+#if PHP_HTTP_CURL_VERSION(7,38,0)
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "GSSAPI", CURL_VERSION_GSSAPI, CONST_CS|CONST_PERSISTENT);
+#endif
+#if PHP_HTTP_CURL_VERSION(7,21,4)
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "TLSAUTH_SRP", CURL_VERSION_TLSAUTH_SRP, CONST_CS|CONST_PERSISTENT);
+#endif
+#if PHP_HTTP_CURL_VERSION(7,22,0)
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "NTLM_WB", CURL_VERSION_NTLM_WB, CONST_CS|CONST_PERSISTENT);
+#endif
+#if PHP_HTTP_CURL_VERSION(7,33,0)
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "HTTP2", CURL_VERSION_HTTP2, CONST_CS|CONST_PERSISTENT);
+#endif
+#if PHP_HTTP_CURL_VERSION(7,40,0)
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "UNIX_SOCKETS", CURL_VERSION_UNIX_SOCKETS, CONST_CS|CONST_PERSISTENT);
+#endif
+#if PHP_HTTP_CURL_VERSION(7,47,0)
+               REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl\\Features", "PSL", CURL_VERSION_PSL, CONST_CS|CONST_PERSISTENT);
+#endif
+
+               /*
+                * Version constants
+                */
+               REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl", "VERSIONS", curl_version(), CONST_CS|CONST_PERSISTENT);
+#if CURLVERSION_NOW >= 0
+               REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "CURL", (char *) info->version, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "SSL", (char *) info->ssl_version, CONST_CS|CONST_PERSISTENT);
+               REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "LIBZ", (char *) info->libz_version, CONST_CS|CONST_PERSISTENT);
+# if CURLVERSION_NOW >= 1
+               REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "ARES", (char *) info->ares, CONST_CS|CONST_PERSISTENT);
+#  if CURLVERSION_NOW >= 2
+               REGISTER_NS_STRING_CONSTANT("http\\Client\\Curl\\Versions", "IDN", (char *) info->libidn, CONST_CS|CONST_PERSISTENT);
+#  endif
+# endif
+#endif
+       }
+
        /*
        * HTTP Protocol Version Constants
        */
index ab8b63c..0c533bd 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <curl/curl.h>
 #define PHP_HTTP_CURL_VERSION(x, y, z) (LIBCURL_VERSION_NUM >= (((x)<<16) + ((y)<<8) + (z)))
+#define PHP_HTTP_CURL_FEATURE(f) (curl_version_info(CURLVERSION_NOW)->features & (f))
 
 PHP_MINIT_FUNCTION(http_curl);
 PHP_MSHUTDOWN_FUNCTION(http_curl);
index 8242a7c..91204c1 100644 (file)
@@ -38,6 +38,18 @@ ZEND_RESULT_CODE php_http_header_parse(const char *header, size_t length, HashTa
 
 void php_http_header_to_callback(HashTable *headers, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg)
 {
+       php_http_arrkey_t key;
+       zval *header;
+
+       ZEND_HASH_FOREACH_KEY_VAL(headers, key.h, key.key, header)
+       {
+               if (key.key) {
+                       php_http_header_to_callback_ex(key.key->val, header, crlf, cb, cb_arg);
+               }
+       }
+       ZEND_HASH_FOREACH_END();
+/*
+<<<<<<< HEAD
        php_http_arrkey_t key;
        zval *header, *single_header;
 
@@ -73,9 +85,9 @@ void php_http_header_to_callback(HashTable *headers, zend_bool crlf, php_http_pa
                                cb(cb_arg, crlf ? "%s: %s" PHP_HTTP_CRLF : "%s: %s", key.key->val, zs->val);
                                zend_string_release(zs);
                        }
-               }
-       }
-       ZEND_HASH_FOREACH_END();
+=======
+>>>>>>> 343738ad56eb70017704fdac57cf0d74da3d0f2e
+*/
 }
 
 void php_http_header_to_string(php_http_buffer_t *str, HashTable *headers)
@@ -83,6 +95,41 @@ void php_http_header_to_string(php_http_buffer_t *str, HashTable *headers)
        php_http_header_to_callback(headers, 1, (php_http_pass_format_callback_t) php_http_buffer_appendf, str);
 }
 
+void php_http_header_to_callback_ex(const char *key, zval *val, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg)
+{
+       zval *aval;
+       zend_string *str;
+
+       switch (Z_TYPE_P(val)) {
+       case IS_ARRAY:
+               ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(val), aval)
+               {
+                       php_http_header_to_callback_ex(key, aval, crlf, cb, cb_arg);
+               }
+               ZEND_HASH_FOREACH_END();
+               break;
+
+       case IS_TRUE:
+               cb(cb_arg, "%s: true%s", key, crlf ? PHP_HTTP_CRLF:"");
+               break;
+
+       case IS_FALSE:
+               cb(cb_arg, "%s: false%s", key, crlf ? PHP_HTTP_CRLF:"");
+               break;
+
+       default:
+               str = zval_get_string(val);
+               cb(cb_arg, "%s: %s%s", key, str->val, crlf ? PHP_HTTP_CRLF:"");
+               zend_string_release(str);
+               break;
+       }
+}
+
+void php_http_header_to_string_ex(php_http_buffer_t *str, const char *key, zval *val)
+{
+       php_http_header_to_callback_ex(key, val, 1, (php_http_pass_format_callback_t) php_http_buffer_appendf, str);
+}
+
 zend_string *php_http_header_value_array_to_string(zval *header)
 {
        zval *val;
index 1ba9ef5..00ef264 100644 (file)
@@ -18,7 +18,9 @@
 PHP_HTTP_API ZEND_RESULT_CODE php_http_header_parse(const char *header, size_t length, HashTable *headers, php_http_info_callback_t callback_func, void **callback_data);
 
 PHP_HTTP_API void php_http_header_to_callback(HashTable *headers, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg);
+PHP_HTTP_API void php_http_header_to_callback_ex(const char *key, zval *val, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg);
 PHP_HTTP_API void php_http_header_to_string(php_http_buffer_t *str, HashTable *headers);
+PHP_HTTP_API void php_http_header_to_string_ex(php_http_buffer_t *str, const char *key, zval *val);
 
 PHP_HTTP_API zend_string *php_http_header_value_to_string(zval *header);
 PHP_HTTP_API zend_string *php_http_header_value_array_to_string(zval *header);
index 8f49e92..56499bb 100644 (file)
@@ -1247,16 +1247,27 @@ static PHP_METHOD(HttpMessage, addHeader)
        if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &name_str, &name_len, &zvalue)) {
                php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
                char *name = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1);
-               zval *header;
+               zend_string *hstr, *vstr = php_http_header_value_to_string(zvalue);
+               zval tmp, *header;
 
                PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
 
-               Z_TRY_ADDREF_P(zvalue);
-               if ((header = php_http_message_header(obj->message, name, name_len))) {
+               if ((name_len != lenof("Set-Cookie") && strcmp(name, "Set-Cookie"))
+               &&      (hstr = php_http_message_header_string(obj->message, name, name_len))) {
+                       char *hdr_str;
+                       size_t hdr_len = spprintf(&hdr_str, 0, "%s, %s", hstr->val, vstr->val);
+
+                       ZVAL_STR(&tmp, php_http_cs2zs(hdr_str, hdr_len));
+                       zend_symtable_str_update(&obj->message->hdrs, name, name_len, &tmp);
+                       zend_string_release(hstr);
+                       zend_string_release(vstr);
+               } else if ((header = php_http_message_header(obj->message, name, name_len))) {
                        convert_to_array(header);
-                       zend_hash_next_index_insert(Z_ARRVAL_P(header), zvalue);
+                       ZVAL_STR(&tmp, vstr);
+                       zend_hash_next_index_insert(Z_ARRVAL_P(header), &tmp);
                } else {
-                       zend_symtable_str_update(&obj->message->hdrs, name, name_len, zvalue);
+                       ZVAL_STR(&tmp, vstr);
+                       zend_symtable_str_update(&obj->message->hdrs, name, name_len, &tmp);
                }
                efree(name);
        }
index 1335aaa..c31afd0 100644 (file)
@@ -1,51 +1,67 @@
 --TEST--
 client cookies
 --SKIPIF--
-<?php 
+<?php
 include "skipif.inc";
 skip_client_test();
 ?>
 --FILE--
-<?php 
+<?php
 
 include "helper/server.inc";
 
 echo "Test\n";
 
+function dump($f) {
+       return;
+       readfile($f);
+}
+
 $tmpfile = tempnam(sys_get_temp_dir(), "cookie.");
 $request = new http\Client\Request("GET", "http://localhost");
 $request->setOptions(array("cookiestore" => $tmpfile));
 
-server("cookie.inc", function($port) use($request) {
+server("cookie.inc", function($port) use($request, $tmpfile) {
        $request->setOptions(array("port" => $port));
        $client = new http\Client;
        echo $client->requeue($request)->send()->getResponse();
+#dump($tmpfile);
        echo $client->requeue($request)->send()->getResponse();
+#dump($tmpfile);
        echo $client->requeue($request)->send()->getResponse();
+#dump($tmpfile);
 });
-
-server("cookie.inc", function($port) use($request) {
+server("cookie.inc", function($port) use($request, $tmpfile) {
        $request->setOptions(array("port" => $port));
        $client = new http\Client;
        echo $client->requeue($request)->send()->getResponse();
+#dump($tmpfile);
        echo $client->requeue($request)->send()->getResponse();
+#dump($tmpfile);
        echo $client->requeue($request)->send()->getResponse();
+#dump($tmpfile);
 });
 
-server("cookie.inc", function($port) use($request) {
+server("cookie.inc", function($port) use($request, $tmpfile) {
        $request->setOptions(array("port" => $port, "cookiesession" => true));
        $client = new http\Client;
        echo $client->requeue($request)->send()->getResponse();
+dump($tmpfile);
        echo $client->requeue($request)->send()->getResponse();
+dump($tmpfile);
        echo $client->requeue($request)->send()->getResponse();
+dump($tmpfile);
 });
 
-server("cookie.inc", function($port) use($request) {
+server("cookie.inc", function($port) use($request, $tmpfile) {
        $request->setOptions(array("port" => $port, "cookiesession" => false));
        $client = new http\Client;
        echo $client->requeue($request)->send()->getResponse();
+dump($tmpfile);
        echo $client->requeue($request)->send()->getResponse();
+dump($tmpfile);
        echo $client->requeue($request)->send()->getResponse();
+dump($tmpfile);
 });
 
 unlink($tmpfile);
@@ -103,3 +119,5 @@ Set-Cookie: counter=4;
 Etag: ""
 X-Original-Transfer-Encoding: chunked
 ===DONE===
+--XFAIL--
+TBD
diff --git a/tests/client027.phpt b/tests/client027.phpt
new file mode 100644 (file)
index 0000000..5d81194
--- /dev/null
@@ -0,0 +1,50 @@
+--TEST--
+client cookie woes
+--SKIPIF--
+<?php
+include "skipif.inc";
+skip_client_test();
+?>
+--FILE--
+<?php
+
+include "helper/server.inc";
+include "helper/dump.inc";
+
+echo "Test\n";
+
+server("cookie.inc", function($port) {
+       $client = new http\Client(null, "cookies");
+       $client->configure(["pipelining" => false]);
+       $request = new http\Client\Request("GET", "http://localhost:$port?r1");
+       $client->enqueue($request);
+       $client->send();
+       while (($r = $client->getResponse())) {
+               dump_headers(null, $r->getHeaders());
+       }
+       $client->requeue($request);
+       $request = new http\Client\Request("GET", "http://localhost:$port?r2");
+       $client->enqueue($request);
+       $client->send();
+       while (($r = $client->getResponse())) {
+               dump_headers(null, $r->getHeaders());
+       }
+});
+
+?>
+===DONE===
+--EXPECTF--
+Test
+Etag: ""
+Set-Cookie: counter=1;
+X-Original-Transfer-Encoding: chunked
+
+Etag: ""
+Set-Cookie: counter=2;
+X-Original-Transfer-Encoding: chunked
+
+Etag: ""
+Set-Cookie: counter=2;
+X-Original-Transfer-Encoding: chunked
+
+===DONE===
index 091ff5c..78a63ee 100644 (file)
@@ -55,11 +55,12 @@ function serve($cb) {
                                                stream_socket_enable_crypto($client, true, STREAM_CRYPTO_METHOD_SSLv23_SERVER);
                                        }
                                        try {
-                                               while (!feof($client)) {
+                                               $R = array($client);
+                                               while (!feof($client) && stream_select($R, $W, $E, 1, 0)) {
                                                        logger("serve: Handle client %d", (int) $client);
                                                        $cb($client);
                                                }
-                                               logger("serve: EOF on client %d", (int) $client);
+                                               logger("serve: EOF/timeout on client %d", (int) $client);
                                        } catch (Exception $ex) {
                                                logger("serve: Exception on client %d: %s", (int) $client, $ex->getMessage());
                                                /* ignore disconnect */
index 6f7a546..0372091 100644 (file)
@@ -28,5 +28,8 @@ String: foobar
 
 ===
 UNKNOWN / HTTP/1.1
-Numbers: 1, 2, 3, 4.5
+Numbers: 1
+Numbers: 2
+Numbers: 3
+Numbers: 4.5
 DONE
index 53aa992..1b1a3f2 100644 (file)
@@ -23,18 +23,24 @@ class strval {
 
 $m = new http\Message;
 $m->addHeaders(array("foo"=>"bar","bar"=>"foo"));
-var_dump(array("Foo"=>"bar", "Bar"=>"foo") === $m->getHeaders());
+if (array("Foo"=>"bar", "Bar"=>"foo") !== $m->getHeaders()) {
+       var_dump($m->getHeaders());
+}
 $m->addHeaders(array("key"=>"val","more"=>"Stuff"));
-var_dump(array("Foo"=>"bar", "Bar"=>"foo","Key"=>"val","More"=>"Stuff") === $m->getHeaders());
+if (array("Foo"=>"bar", "Bar"=>"foo","Key"=>"val","More"=>"Stuff") !== $m->getHeaders()) {
+       var_dump($m->getHeaders());
+}
 $m = new http\Message("GET / HTTP/1.1");
 $m->addHeader("Accept", "text/html");
 $m->addHeader("Accept", "text/xml;q=0");
 $m->addHeader("Accept", "text/plain;q=0.5");
-var_dump(
+if (
                "GET / HTTP/1.1\r\n".
-               "Accept: text/html, text/xml;q=0, text/plain;q=0.5\r\n" ===
-               $m->toString()
-);
+               "Accept: text/html, text/xml;q=0, text/plain;q=0.5\r\n" !==
+               $m->toString()) {
+       var_dump($m->toString());
+}
+
 $m = new http\Message("HTTP/1.1 200 Ok");
 $m->addHeader("Bool", true);
 $m->addHeader("Int", 123);
@@ -42,7 +48,7 @@ $m->addHeader("Float", 1.23);
 $m->addHeader("Array", array(1,2,3));
 $m->addHeader("Object", new strval("test"));
 $m->addHeader("Set-Cookie",
-               array(
+               new http\Cookie(
                                array(
                                                "cookies" => array("foo" => "bar"),
                                                "expires" => date_create("2012-12-31 22:59:59 GMT")->format(
@@ -54,7 +60,7 @@ $m->addHeader("Set-Cookie",
 );
 $m->addHeader("Set-Cookie", "val=0");
 
-var_dump(
+if (
                "HTTP/1.1 200 Ok\r\n".
                "Bool: true\r\n".
                "Int: 123\r\n".
@@ -62,16 +68,13 @@ var_dump(
                "Array: 1, 2, 3\r\n".
                "Object: test\r\n".
                "Set-Cookie: foo=bar; path=/somewhere; expires=Mon, 31 Dec 2012 22:59:59 GMT; \r\n".
-               "Set-Cookie: val=0\r\n" ===
-               $m->toString()
-);
+               "Set-Cookie: val=0\r\n" !==
+               $m->toString()) {
+       var_dump($m->toString());
+}
 
 ?>
 Done
 --EXPECT--
 Test
-bool(true)
-bool(true)
-bool(true)
-bool(true)
 Done
index a1dc2d8..59971e5 100644 (file)
@@ -90,7 +90,9 @@ bykey: 1
 by1ref: 2
 by2ref: 1
 byXref: 2
-bynext: 1, 2, 3
+bynext: 1
+bynext: 2
+bynext: 3
 
 DONE