Merge branch 'R_2_2'
authorMichael Wallner <mike@php.net>
Sat, 7 Feb 2015 11:30:04 +0000 (12:30 +0100)
committerMichael Wallner <mike@php.net>
Sat, 7 Feb 2015 11:30:04 +0000 (12:30 +0100)
32 files changed:
config9.m4
gen_curlinfo.php
package.xml
php_http.h
php_http_client.c
php_http_client.h
php_http_client_curl.c
php_http_env_response.c
php_http_info.c
php_http_info.h
php_http_message.c
php_http_object.c
php_http_object.h
php_http_url.c
php_http_url.h
php_http_version.c
tests/client011.phpt
tests/client012.phpt
tests/client017.phpt [new file with mode: 0644]
tests/envresponse006.phpt
tests/envresponse007.phpt
tests/envresponse008.phpt
tests/envresponse009.phpt
tests/envresponse010.phpt
tests/envresponse014.phpt
tests/envresponse015.phpt
tests/envresponse017.phpt [new file with mode: 0644]
tests/envresponsecookie001.phpt
tests/proxy.inc [new file with mode: 0644]
tests/proxy001.phpt [new file with mode: 0644]
tests/proxy002.phpt [new file with mode: 0644]
tests/skipif.inc

index 3b1f7015554d1683469836e9548efd85b2eb0a08..20e7d6c9317dac3d9417ec31a678ea9df9aac068 100644 (file)
@@ -202,6 +202,14 @@ dnl ----
                        if test `echo $CURL_VERSION | $SED -e 's/[[^0-9]]/ /g' | $AWK '{print $1*10000 + $2*100 + $3}'` -lt 71802; then
                                AC_MSG_ERROR([libcurl version greater or equal to 7.18.2 required])
                        fi
+                       
+                       AC_MSG_CHECKING([for HTTP2 support in libcurl])
+                       if $CURL_CONFIG --features | $EGREP -q HTTP2; then
+                               AC_MSG_RESULT([yes])
+                               AC_DEFINE([PHP_HTTP_HAVE_HTTP2], [1], [ ])
+                       else
+                               AC_MSG_RESULT([no])
+                       fi
                
                        dnl
                        dnl compile tests
@@ -210,11 +218,11 @@ dnl ----
                        save_INCLUDES="$INCLUDES"
                        INCLUDES=
                        save_LIBS="$LIBS"
-                       LIBS=
+                       LIBS=-lcurl
                        save_CFLAGS="$CFLAGS"
                        CFLAGS="$CFLAGS `$CURL_CONFIG --cflags`"
                        save_LDFLAGS="$LDFLAGS"
-                       LDFLAGS="$LDFLAGS `$CURL_CONFIG --libs` $ld_runpath_switch$CURL_DIR/$PHP_LIBDIR"
+                       LDFLAGS="$ld_runpath_switch$CURL_DIR/$PHP_LIBDIR"
                
                        AC_MSG_CHECKING([for SSL support in libcurl])
                        CURL_SSL=`$CURL_CONFIG --feature | $EGREP SSL`
@@ -296,6 +304,37 @@ dnl ----
                        ], [
                                AC_MSG_RESULT([no])
                        ])
+                       
+                       AC_MSG_CHECKING([whether CURLOPT_TLSAUTH_TYPE expects CURL_TLSAUTH_SRP or literal "SRP"])
+                       AC_TRY_RUN([
+                               #include <curl/curl.h>
+                               int main(int argc, char *argv[]) {
+                                       CURL *ch = curl_easy_init();
+                                       return curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, CURL_TLSAUTH_SRP);
+                               }
+                       ], [
+                               AC_MSG_RESULT([CURL_TLSAUTH_SRP])
+                               AC_DEFINE([PHP_HTTP_CURL_TLSAUTH_SRP], [CURL_TLSAUTH_SRP], [ ])
+                               AC_DEFINE([PHP_HTTP_CURL_TLSAUTH_DEF], [CURL_TLSAUTH_NONE], [ ])
+                       ], [
+                               AC_TRY_RUN([
+                                       #include <curl/curl.h>
+                                       int main(int argc, char *argv[]) {
+                                               CURL *ch = curl_easy_init();
+                                               return curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, "SRP");
+                                       }
+                               ], [
+                                       AC_MSG_RESULT(["SRP"])
+                                       AC_DEFINE([PHP_HTTP_CURL_TLSAUTH_SRP], ["SRP"], [ ])
+                                       AC_DEFINE([PHP_HTTP_CURL_TLSAUTH_DEF], [""], [ ])
+                               ], [
+                                       AC_MSG_RESULT([neither])
+                               ], [
+                                       AC_MSG_RESULT([neither])
+                               ])                      
+                       ], [
+                               AC_MSG_RESULT([neither])
+                       ])
                
                        INCLUDES="$save_INCLUDES"
                        LIBS="$save_LIBS"
index 0e8dfa1272da5553a3c6267e0c594dee6d991767..076a10d10f86bd34e1a354ea5a34b028bb9dc6ed 100755 (executable)
@@ -41,7 +41,8 @@ $ifdefs = array(
 );
 $exclude = array(
     'PRIVATE', 'LASTSOCKET', 'FTP_ENTRY_PATH', 'CERTINFO', 'TLS_SESSION',
-    'RTSP_SESSION_ID', 'RTSP_CLIENT_CSEQ', 'RTSP_SERVER_CSEQ', 'RTSP_CSEQ_RECV'
+    'RTSP_SESSION_ID', 'RTSP_CLIENT_CSEQ', 'RTSP_SERVER_CSEQ', 'RTSP_CSEQ_RECV',
+       'COOKIELIST'
 );
 
 $translate = array(
index 9b358489d381beea97c042b3d90810dddb3fc710..77cd1a06b63a7bd4da8f2d651789ce521ddcdc2a 100644 (file)
@@ -37,10 +37,10 @@ v2: http://dev.iworks.at/ext-http/lcov/ext/http/
   <email>mike@php.net</email>
   <active>yes</active>
  </lead>
- <date>2015-02-07</date>
+ <date>2015-01-27</date>
  <version>
-  <release>2.2.2</release>
-  <api>2.2.0</api>
+  <release>2.3.0</release>
+  <api>2.3.0</api>
  </version>
  <stability>
   <release>stable</release>
@@ -48,6 +48,9 @@ v2: http://dev.iworks.at/ext-http/lcov/ext/http/
  </stability>
  <license>BSD, revised</license>
  <notes><![CDATA[
++ Added support for HTTP2 if libcurl was built with nghttp2 support.
++ Added http\Client\Curl\HTTP_VERSION_2_0 constant (libcurl >= 7.33.0)
++ Added pinned_publickey SSL request option (libcurl >= 7.39.0)
 ]]></notes>
  <contents>
   <dir name="/">
index 63ecc1928bbb2c7276662e95587cdcc21f643e8d..5a0575d269bb8e799783974f4d961aeeb0c1a65d 100644 (file)
@@ -13,7 +13,7 @@
 #ifndef PHP_EXT_HTTP_H
 #define PHP_EXT_HTTP_H
 
-#define PHP_PECL_HTTP_VERSION "2.2.2dev"
+#define PHP_PECL_HTTP_VERSION "2.3.0dev"
 
 extern zend_module_entry http_module_entry;
 #define phpext_http_ptr &http_module_entry
index f96164bbba08ad449733b745503149948412de03..3050bd80fa7cbc9619966614571707980f118819 100644 (file)
@@ -329,6 +329,8 @@ void php_http_client_object_free(void *object TSRMLS_DC)
        php_http_client_object_t *o = (php_http_client_object_t *) object;
 
        php_http_client_free(&o->client);
+       php_http_object_method_dtor(&o->notify);
+       php_http_object_method_free(&o->update);
        zend_object_std_dtor((zend_object *) o TSRMLS_CC);
        efree(o);
 }
@@ -375,7 +377,7 @@ static void handle_history(zval *zclient, php_http_message_t *request, php_http_
        zval_ptr_dtor(&new_hist);
 }
 
-static STATUS 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)
 {
        zend_bool dequeue = 0;
        zval zclient;
@@ -395,7 +397,7 @@ static STATUS handle_response(void *arg, php_http_client_t *client, php_http_cli
                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, *request, *response TSRMLS_CC);
+                       handle_history(&zclient, e->request, *response TSRMLS_CC);
                }
 
                /* hard detach, redirects etc. are in the history */
@@ -457,14 +459,18 @@ static STATUS handle_response(void *arg, php_http_client_t *client, php_http_cli
 
 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 = NULL, *zclient;
+       zval *zrequest, *zprogress, *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, ((php_http_client_object_t *) arg)->zv, 1);
+       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);
@@ -474,15 +480,15 @@ static void handle_progress(void *arg, php_http_client_t *client, php_http_clien
        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);
-       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 TSRMLS_CC);
        zend_restore_error_handling(&zeh TSRMLS_CC);
+
        zval_ptr_dtor(&zclient);
        zval_ptr_dtor(&zrequest);
        zval_ptr_dtor(&zprogress);
-       if (retval) {
-               zval_ptr_dtor(&retval);
-       }
 }
 
 static void response_dtor(void *data)
@@ -537,6 +543,8 @@ static PHP_METHOD(HttpClient, __construct)
 
                php_http_expect(obj->client = php_http_client_init(NULL, driver.client_ops, rf, NULL TSRMLS_CC), runtime, return);
 
+               php_http_object_method_init(&obj->notify, getThis(), ZEND_STRL("notify") TSRMLS_CC);
+
                obj->client->callback.response.func = handle_response;
                obj->client->callback.response.arg = obj;
                obj->client->callback.progress.func = handle_progress;
@@ -869,13 +877,20 @@ static PHP_METHOD(HttpClient, enableEvents)
        RETVAL_ZVAL(getThis(), 1, 0);
 }
 
+struct notify_arg {
+       php_http_object_method_t *cb;
+       zval **args[3];
+       int argc;
+};
+
 static int notify(zend_object_iterator *iter, void *puser TSRMLS_DC)
 {
-       zval **observer = NULL, ***args = puser;
+       zval **observer = NULL;
+       struct notify_arg *arg = puser;
 
        iter->funcs->get_current_data(iter, &observer TSRMLS_CC);
        if (observer) {
-               return php_http_method_call(*observer, ZEND_STRL("update"), args[2]?3:args[1]?2:args[0]?1:0, args, NULL TSRMLS_CC);
+               return php_http_object_method_call(arg->cb, *observer, NULL, arg->argc, arg->args TSRMLS_CC);
        }
        return FAILURE;
 }
@@ -885,10 +900,13 @@ ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_notify, 0, 0, 0)
 ZEND_END_ARG_INFO();
 static PHP_METHOD(HttpClient, notify)
 {
-       zval *request = NULL, *zprogress = NULL, *observers, **args[3];
+       zval *request = NULL, *zprogress = NULL, *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);
 
+       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);
 
        if (Z_TYPE_P(observers) != IS_OBJECT) {
@@ -896,23 +914,34 @@ static PHP_METHOD(HttpClient, notify)
                return;
        }
 
-       Z_ADDREF_P(getThis());
-       args[0] = &getThis();
-       if (request) {
-               Z_ADDREF_P(request);
-       }
-       args[1] = &request;
-       if (zprogress) {
-               Z_ADDREF_P(zprogress);
-       }
-       args[2] = &zprogress;
-       spl_iterator_apply(observers, notify, args TSRMLS_CC);
-       zval_ptr_dtor(&getThis());
-       if (request) {
-               zval_ptr_dtor(&request);
-       }
-       if (zprogress) {
-               zval_ptr_dtor(&zprogress);
+       if (client_obj->update) {
+               arg.cb = client_obj->update;
+
+               Z_ADDREF_P(getThis());
+               arg.args[0] = &getThis();
+               arg.argc = 1;
+
+               if (request) {
+                       Z_ADDREF_P(request);
+                       arg.args[1] = &request;
+                       arg.argc += 1;
+               }
+
+               if (zprogress) {
+                       Z_ADDREF_P(zprogress);
+                       arg.args[2] = &zprogress;
+                       arg.argc += 1;
+               }
+
+               spl_iterator_apply(observers, notify, &arg TSRMLS_CC);
+
+               zval_ptr_dtor(&getThis());
+               if (request) {
+                       zval_ptr_dtor(&request);
+               }
+               if (zprogress) {
+                       zval_ptr_dtor(&zprogress);
+               }
        }
 
        RETVAL_ZVAL(getThis(), 1, 0);
@@ -924,9 +953,11 @@ ZEND_END_ARG_INFO();
 static PHP_METHOD(HttpClient, attach)
 {
        zval *observers, *observer, *retval = NULL;
+       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);
 
+       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);
 
        if (Z_TYPE_P(observers) != IS_OBJECT) {
@@ -934,6 +965,10 @@ static PHP_METHOD(HttpClient, attach)
                return;
        }
 
+       if (!client_obj->update) {
+               client_obj->update = php_http_object_method_init(NULL, observer, ZEND_STRL("update") TSRMLS_CC);
+       }
+
        zend_call_method_with_1_params(&observers, NULL, NULL, "attach", &retval, observer);
        if (retval) {
                zval_ptr_dtor(&retval);
index 6c455166e6233c877862f769a27ece04865608de..b5367530a376fa18ac48d0617d58a8fa14d88210 100644 (file)
@@ -84,7 +84,7 @@ typedef struct php_http_client_progress_state {
        unsigned finished:1;
 } php_http_client_progress_state_t;
 
-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 **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 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 {
@@ -118,6 +118,8 @@ typedef struct php_http_client_object {
        zend_object_value zv;
        php_http_client_t *client;
        long iterator;
+       php_http_object_method_t *update;
+       php_http_object_method_t notify;
 } 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);
index b057216a8cfb80bad4b4aee58b65d6b19b40b0ed..3b4d5b187ca3e9efe328be60871eb0e00fefad07 100644 (file)
@@ -60,24 +60,17 @@ typedef struct php_http_client_curl_handler {
        php_resource_factory_t *rf;
        php_http_client_t *client;
        php_http_client_progress_state_t progress;
-
        php_http_client_enqueue_t queue;
 
        struct {
-               php_http_message_parser_t *parser;
-               php_http_message_t *message;
-               php_http_buffer_t *buffer;
-       } request;
-
-       struct {
-               php_http_message_parser_t *parser;
-               php_http_message_t *message;
-               php_http_buffer_t *buffer;
+               php_http_buffer_t headers;
+               php_http_message_body_t *body;
        } response;
 
        struct {
                HashTable cache;
 
+               struct curl_slist *proxyheaders;
                struct curl_slist *headers;
                struct curl_slist *resolve;
                php_http_buffer_t cookies;
@@ -225,29 +218,23 @@ static int php_http_curle_progress_callback(void *ctx, double dltotal, double dl
        return 0;
 }
 
-static curlioerr php_http_curle_ioctl_callback(CURL *ch, curliocmd cmd, void *ctx)
+static int php_http_curle_seek_callback(void *userdata, curl_off_t offset, int origin)
 {
-       php_http_message_body_t *body = ctx;
+       php_http_message_body_t *body = userdata;
+       TSRMLS_FETCH_FROM_CTX(body->ts);
 
-       if (cmd != CURLIOCMD_RESTARTREAD) {
-               return CURLIOE_UNKNOWNCMD;
+       if (!body) {
+               return 1;
        }
-
-       if (body) {
-               TSRMLS_FETCH_FROM_CTX(body->ts);
-
-               if (SUCCESS == php_stream_rewind(php_http_message_body_stream(body))) {
-                       return CURLIOE_OK;
-               }
+       if (0 == php_stream_seek(php_http_message_body_stream(body), offset, origin)) {
+               return 0;
        }
-
-       return CURLIOE_FAILRESTART;
+       return 2;
 }
 
 static int php_http_curle_raw_callback(CURL *ch, curl_infotype type, char *data, size_t length, void *ctx)
 {
        php_http_client_curl_handler_t *h = ctx;
-       unsigned flags = 0;
 
        /* catch progress */
        switch (type) {
@@ -304,32 +291,6 @@ static int php_http_curle_raw_callback(CURL *ch, curl_infotype type, char *data,
                default:
                        break;
        }
-       /* process data */
-       switch (type) {
-               case CURLINFO_HEADER_IN:
-               case CURLINFO_DATA_IN:
-                       php_http_buffer_append(h->response.buffer, data, length);
-
-                       if (h->options.redirects) {
-                               flags |= PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS;
-                       }
-
-                       if (PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE == php_http_message_parser_parse(h->response.parser, h->response.buffer, flags, &h->response.message)) {
-                               return -1;
-                       }
-                       break;
-
-               case CURLINFO_HEADER_OUT:
-               case CURLINFO_DATA_OUT:
-                       php_http_buffer_append(h->request.buffer, data, length);
-
-                       if (PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE == php_http_message_parser_parse(h->request.parser, h->request.buffer, flags, &h->request.message)) {
-                               return -1;
-                       }
-                       break;
-               default:
-                       break;
-       }
 
 #if 0
        /* debug */
@@ -339,9 +300,18 @@ static int php_http_curle_raw_callback(CURL *ch, curl_infotype type, char *data,
        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)
+{
+       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)
 {
-       return n*l;
+       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)
@@ -440,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);
        }
@@ -617,6 +576,50 @@ static int compare_queue(php_http_client_enqueue_t *e, void *handle)
        return handle == ((php_http_client_curl_handler_t *) e->opaque)->handle;
 }
 
+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("Content-Length"), 1))) {
+               zend_hash_update(&response->hdrs, "X-Original-Content-Length", sizeof("X-Original-Content-Length"), &zh, sizeof(zval *), NULL);
+       }
+       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-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("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"));
+       }
+       php_http_message_update_headers(response);
+
+       return response;
+}
+
 static void php_http_curlm_responsehandler(php_http_client_t *context)
 {
        int remaining = 0;
@@ -635,8 +638,12 @@ static void php_http_curlm_responsehandler(php_http_client_t *context)
 
                        if ((enqueue = php_http_client_enqueued(context, msg->easy_handle, compare_queue))) {
                                php_http_client_curl_handler_t *handler = enqueue->opaque;
+                               php_http_message_t *response = php_http_curlm_responseparser(handler TSRMLS_CC);
 
-                               context->callback.response.func(context->callback.response.arg, context, &handler->queue, &handler->request.message, &handler->response.message);
+                               if (response) {
+                                       context->callback.response.func(context->callback.response.arg, context, &handler->queue, &response);
+                                       php_http_message_free(&response);
+                               }
                        }
                }
        } while (remaining);
@@ -925,9 +932,10 @@ static STATUS php_http_curle_option_set_lastmodified(php_http_option_t *opt, zva
 static STATUS 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_BVAL_P(val)) {
-               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)) {
+               return FAILURE;
        }
        return SUCCESS;
 }
@@ -1073,6 +1081,43 @@ static STATUS php_http_curle_option_set_portrange(php_http_option_t *opt, zval *
        return SUCCESS;
 }
 
+#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)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+       TSRMLS_FETCH_FROM_CTX(curl->client->ts);
+
+       if (val && Z_TYPE_P(val) != IS_NULL) {
+               php_http_array_hashkey_t header_key = php_http_array_hashkey_init(0);
+               zval **header_val, *header_cpy;
+               HashPosition pos;
+               php_http_buffer_t header;
+
+               php_http_buffer_init(&header);
+               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_buffer_fix(&header);
+                               curl->options.proxyheaders = curl_slist_append(curl->options.proxyheaders, header.data);
+                               php_http_buffer_reset(&header);
+
+                               zval_ptr_dtor(&header_cpy);
+                       }
+               }
+               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)
 {
@@ -1103,6 +1148,30 @@ 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)
+{
+       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)
 {
        php_http_option_t *opt;
@@ -1124,6 +1193,19 @@ static void php_http_curle_options_init(php_http_options_t *registry TSRMLS_DC)
        php_http_option_register(registry, ZEND_STRL("noproxy"), CURLOPT_NOPROXY, IS_STRING);
 #endif
 
+#if PHP_HTTP_CURL_VERSION(7,37,0)
+       if ((opt = php_http_option_register(registry, ZEND_STRL("proxyheader"), CURLOPT_PROXYHEADER, IS_ARRAY))) {
+               opt->setter = php_http_curle_option_set_proxyheader;
+       }
+#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;
@@ -1336,6 +1418,9 @@ static void php_http_curle_options_init(php_http_options_t *registry TSRMLS_DC)
                        ZVAL_BOOL(&opt->defval, 1);
                        opt->setter = php_http_curle_option_set_ssl_verifyhost;
                }
+#if PHP_HTTP_CURL_VERSION(7,41,0)
+               php_http_option_register(registry, ZEND_STRL("verifystatus"), CURLOPT_SSL_VERIFYSTATUS, IS_BOOL);
+#endif
                php_http_option_register(registry, ZEND_STRL("cipher_list"), CURLOPT_SSL_CIPHER_LIST, IS_STRING);
                if ((opt = php_http_option_register(registry, ZEND_STRL("cainfo"), CURLOPT_CAINFO, IS_STRING))) {
                        opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
@@ -1378,6 +1463,23 @@ static void php_http_curle_options_init(php_http_options_t *registry TSRMLS_DC)
                if ((opt = php_http_option_register(registry, ZEND_STRL("enable_alpn"), CURLOPT_SSL_ENABLE_ALPN, IS_BOOL))) {
                        ZVAL_BOOL(&opt->defval, 1);
                }
+#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)
+               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
        }
 }
@@ -1522,6 +1624,10 @@ static STATUS php_http_client_curl_handler_reset(php_http_client_curl_handler_t
                curl_slist_free_all(curl->options.headers);
                curl->options.headers = NULL;
        }
+       if (curl->options.proxyheaders) {
+               curl_slist_free_all(curl->options.proxyheaders);
+               curl->options.proxyheaders = NULL;
+       }
 
        php_http_buffer_reset(&curl->options.cookies);
        php_http_buffer_reset(&curl->options.ranges);
@@ -1544,12 +1650,8 @@ static php_http_client_curl_handler_t *php_http_client_curl_handler_init(php_htt
        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 TSRMLS_CC);
-       handler->request.message = php_http_message_init(NULL, 0, NULL TSRMLS_CC);
-       handler->response.buffer = php_http_buffer_init(NULL);
-       handler->response.parser = php_http_message_parser_init(NULL TSRMLS_CC);
-       handler->response.message = php_http_message_init(NULL, 0, NULL TSRMLS_CC);
+       handler->response.body = php_http_message_body_init(NULL, NULL TSRMLS_CC);
+       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);
@@ -1562,11 +1664,11 @@ static php_http_client_curl_handler_t *php_http_client_curl_handler_init(php_htt
        curl_easy_setopt(handle, CURLOPT_AUTOREFERER, 1L);
        curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L);
        curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0L);
-       curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, NULL);
-       curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, php_http_curle_dummy_callback);
+       curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, php_http_curle_header_callback);
+       curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, php_http_curle_body_callback);
        curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, php_http_curle_raw_callback);
        curl_easy_setopt(handle, CURLOPT_READFUNCTION, php_http_curle_read_callback);
-       curl_easy_setopt(handle, CURLOPT_IOCTLFUNCTION, php_http_curle_ioctl_callback);
+       curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, php_http_curle_seek_callback);
 #if PHP_HTTP_CURL_VERSION(7,32,0)
        curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, php_http_curle_xferinfo_callback);
        curl_easy_setopt(handle, CURLOPT_XFERINFODATA, handler);
@@ -1575,6 +1677,8 @@ static php_http_client_curl_handler_t *php_http_client_curl_handler_init(php_htt
        curl_easy_setopt(handle, CURLOPT_PROGRESSDATA, handler);
 #endif
        curl_easy_setopt(handle, CURLOPT_DEBUGDATA, handler);
+       curl_easy_setopt(handle, CURLOPT_WRITEDATA, handler);
+       curl_easy_setopt(handle, CURLOPT_HEADERDATA, handler);
 
        php_http_client_curl_handler_reset(handler);
 
@@ -1630,6 +1734,9 @@ static STATUS php_http_client_curl_handler_prepare(php_http_client_curl_handler_
                }
        }
 
+       /* apply options */
+       php_http_options_apply(&php_http_curle_options, enqueue->options, curl);
+
        /* request headers */
        php_http_message_update_headers(msg);
        if (zend_hash_num_elements(&msg->hdrs)) {
@@ -1674,19 +1781,17 @@ static STATUS php_http_client_curl_handler_prepare(php_http_client_curl_handler_
                 * does not allow a request body.
                 */
                php_stream_rewind(php_http_message_body_stream(msg->body));
-               curl_easy_setopt(curl->handle, CURLOPT_IOCTLDATA, msg->body);
+               curl_easy_setopt(curl->handle, CURLOPT_SEEKDATA, msg->body);
                curl_easy_setopt(curl->handle, CURLOPT_READDATA, msg->body);
                curl_easy_setopt(curl->handle, CURLOPT_INFILESIZE, body_size);
                curl_easy_setopt(curl->handle, CURLOPT_POSTFIELDSIZE, body_size);
        } else {
-               curl_easy_setopt(curl->handle, CURLOPT_IOCTLDATA, NULL);
+               curl_easy_setopt(curl->handle, CURLOPT_SEEKDATA, NULL);
                curl_easy_setopt(curl->handle, CURLOPT_READDATA, NULL);
                curl_easy_setopt(curl->handle, CURLOPT_INFILESIZE, 0L);
                curl_easy_setopt(curl->handle, CURLOPT_POSTFIELDSIZE, 0L);
        }
 
-       php_http_options_apply(&php_http_curle_options, enqueue->options, curl);
-
        return SUCCESS;
 }
 
@@ -1711,12 +1816,8 @@ static void php_http_client_curl_handler_dtor(php_http_client_curl_handler_t *ha
        php_resource_factory_handle_dtor(handler->rf, handler->handle TSRMLS_CC);
        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);
@@ -2123,6 +2224,9 @@ PHP_MINIT_FUNCTION(http_client_curl)
        */
        REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_1_0", CURL_HTTP_VERSION_1_0, CONST_CS|CONST_PERSISTENT);
        REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_1_1", CURL_HTTP_VERSION_1_1, CONST_CS|CONST_PERSISTENT);
+#if PHP_HTTP_CURL_VERSION(7,33,0)
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_2_0", CURL_HTTP_VERSION_2_0, CONST_CS|CONST_PERSISTENT);
+#endif
        REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_ANY", CURL_HTTP_VERSION_NONE, CONST_CS|CONST_PERSISTENT);
 
        /*
@@ -2137,6 +2241,9 @@ 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)
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "TLSAUTH_SRP", CURL_TLSAUTH_SRP, CONST_CS|CONST_PERSISTENT);
+#endif
 
        /*
        * DNS IPvX resolving
index 52d561ac19eeaae62417e1e3840afdb166a0a6e8..a4431b60af08c9be636272ea6110220aaae6ae78 100644 (file)
@@ -883,9 +883,11 @@ typedef struct php_http_env_response_stream_ctx {
        long status_code;
 
        php_stream *stream;
+       php_http_message_t *request;
 
        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)
@@ -903,6 +905,13 @@ static STATUS php_http_env_response_stream_init(php_http_env_response_t *r, void
        zend_hash_init(&ctx->header, 0, NULL, ZVAL_PTR_DTOR, 0);
        php_http_version_init(&ctx->version, 1, 1 TSRMLS_CC);
        ctx->status_code = 200;
+       ctx->chunked = 1;
+       ctx->request = get_request(r->options TSRMLS_CC);
+
+       /* 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;
 
@@ -929,6 +938,12 @@ static void php_http_env_response_stream_header(php_http_env_response_stream_ctx
                } else {
                        zval *tmp = php_http_ztyp(IS_STRING, *val);
 
+                       if (ctx->chunked) {
+                               /* disable chunked transfer encoding if we've got an explicit content-length */
+                               if (!strncasecmp(Z_STRVAL_P(tmp), "Content-Length:", lenof("Content-Length:"))) {
+                                       ctx->chunked = 0;
+                               }
+                       }
                        php_stream_write(ctx->stream, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
                        php_stream_write_string(ctx->stream, PHP_HTTP_CRLF);
                        zval_ptr_dtor(&tmp);
@@ -942,9 +957,26 @@ static STATUS php_http_env_response_stream_start(php_http_env_response_stream_ct
        }
 
        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));
+
+       /* 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);
+
+       /* 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)
@@ -1061,10 +1093,18 @@ static STATUS php_http_env_response_stream_write(php_http_env_response_t *r, con
                }
        }
 
+       if (stream_ctx->chunked && 0 == php_stream_printf(stream_ctx->stream TSRMLS_CC, "%lx" PHP_HTTP_CRLF, (unsigned long) data_len)) {
+               return FAILURE;
+       }
+
        if (data_len != php_stream_write(stream_ctx->stream, data_str, data_len)) {
                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)
@@ -1097,6 +1137,10 @@ static STATUS php_http_env_response_stream_finish(php_http_env_response_t *r)
                }
        }
 
+       if (stream_ctx->chunked && 5 != php_stream_write_string(stream_ctx->stream, "0" PHP_HTTP_CRLF PHP_HTTP_CRLF)) {
+               return FAILURE;
+       }
+
        stream_ctx->finished = 1;
 
        return SUCCESS;
@@ -1367,6 +1411,7 @@ static PHP_METHOD(HttpEnvResponse, send)
 #else
                php_end_ob_buffers(1 TSRMLS_CC);
 #endif
+
                if (zstream) {
                        php_http_env_response_t *r;
 
index 7efd70ec7914426f697a3a7718984400e5f24253..905091979a70ac08159ac451bb0c561e92181e18 100644 (file)
@@ -65,15 +65,15 @@ php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_head
        }
        
        /* there must be HTTP/1.x in the line */
-       if (!(http = php_http_locate_str(pre_header, end - pre_header, "HTTP/1.", lenof("HTTP/1.")))) {
+       if (!(http = php_http_locate_str(pre_header, end - pre_header, "HTTP/", lenof("HTTP/")))) {
                return NULL;
        }
        
        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)
-       ||      (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);
                }
@@ -90,13 +90,22 @@ php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_head
 
        /* is response */
        if (pre_header == http) {
-               char *status = NULL;
-               const char *code = http + sizeof("HTTP/1.1");
+               const char *status = NULL, *code = http + sizeof("HTTP/X.x");
                
                info->type = PHP_HTTP_RESPONSE;
                while (' ' == *code) ++code;
                if (code && end > code) {
-                       PHP_HTTP_INFO(info).response.code = strtol(code, &status, 10);
+                       /* rfc7230#3.1.2 The status-code element is a 3-digit integer code */
+                       PHP_HTTP_INFO(info).response.code = 100*(*code++ - '0');
+                       PHP_HTTP_INFO(info).response.code += 10*(*code++ - '0');
+                       PHP_HTTP_INFO(info).response.code +=     *code++ - '0';
+                       if (PHP_HTTP_INFO(info).response.code < 100 || PHP_HTTP_INFO(info).response.code > 599) {
+                               if (free_info) {
+                                       php_http_info_free(&info);
+                               }
+                               return NULL;
+                       }
+                       status = code;
                } else {
                        PHP_HTTP_INFO(info).response.code = 0;
                }
@@ -111,16 +120,25 @@ php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_head
        }
        
        /* is request */
-       else if (*(http - 1) == ' ' && (!http[lenof("HTTP/1.x")] || http[lenof("HTTP/1.x")] == '\r' || http[lenof("HTTP/1.x")] == '\n')) {
+       else if (*(http - 1) == ' ' && (!http[lenof("HTTP/X.x")] || http[lenof("HTTP/X.x")] == '\r' || http[lenof("HTTP/X.x")] == '\n')) {
                const char *url = strchr(pre_header, ' ');
                
                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 TSRMLS_CC);
+                               /* 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);
+                               } else {
+                                       PHP_HTTP_INFO(info).request.url = php_http_url_parse_authority(url, http - url, ~0 TSRMLS_CC);
+                               }
                        } else {
                                PTR_SET(PHP_HTTP_INFO(info).request.method, NULL);
                                return NULL;
@@ -133,7 +151,7 @@ php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_head
                return info;
        }
 
-       /* some darn header containing HTTP/1.x */
+       /* some darn header containing HTTP/X.x */
        else {
                if (free_info) {
                        php_http_info_free(&info);
index d31b505d8d3e378b99628acbf4b906e2cb179121..4f02908446b6c0c430eb6bb39269f45967500b00 100644 (file)
@@ -18,7 +18,9 @@
 
 #define PHP_HTTP_INFO_REQUEST_FMT_ARGS(_http_ptr, tmp, eol) "%s %s HTTP/%u.%u" eol, \
                                (_http_ptr)->info.request.method?(_http_ptr)->info.request.method:"UNKNOWN", \
-                               (_http_ptr)->info.request.url?php_http_url_to_string((_http_ptr)->info.request.url, &(tmp), NULL, 0):"/", \
+                               (_http_ptr)->info.request.method&&!strcasecmp((_http_ptr)->info.request.method,"CONNECT")?( \
+                               (_http_ptr)->info.request.url?php_http_url_authority_to_string((_http_ptr)->info.request.url, &(tmp), NULL):"0"):( \
+                               (_http_ptr)->info.request.url?php_http_url_to_string((_http_ptr)->info.request.url, &(tmp), NULL, 0):"/"), \
                                (_http_ptr)->version.major||(_http_ptr)->version.major?(_http_ptr)->version.major:1, \
                                (_http_ptr)->version.major||(_http_ptr)->version.minor?(_http_ptr)->version.minor:1
 
index 8a7e564a761990743270c2e7c736de49cc2471ce..5a278f68c675f14cc51a24eaad7cdbb4cfce4ec4 100644 (file)
@@ -1161,17 +1161,19 @@ static PHP_METHOD(HttpMessage, getHeader)
                        if (!header_ce) {
                                RETURN_ZVAL(header, 1, 1);
                        } else if (instanceof_function(header_ce, php_http_header_class_entry TSRMLS_CC)) {
+                               php_http_object_method_t cb;
                                zval *header_name, **argv[2];
 
                                MAKE_STD_ZVAL(header_name);
                                ZVAL_STRINGL(header_name, header_str, header_len, 1);
-                               Z_ADDREF_P(header);
 
                                argv[0] = &header_name;
                                argv[1] = &header;
 
                                object_init_ex(return_value, header_ce);
-                               php_http_method_call(return_value, ZEND_STRL("__construct"), 2, argv, NULL TSRMLS_CC);
+                               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_dtor(&cb);
 
                                zval_ptr_dtor(&header_name);
                                zval_ptr_dtor(&header);
index abf72290e26de0ad9dea5052e0d922a82602e8d2..7d902eaa3c84f66b19bc169c18ac987366d8017c 100644 (file)
@@ -53,30 +53,71 @@ STATUS php_http_new(zend_object_value *ovp, zend_class_entry *ce, php_http_new_t
        return SUCCESS;
 }
 
-STATUS php_http_method_call(zval *object, const char *method_str, size_t method_len, int argc, zval **argv[], zval **retval_ptr 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 TSRMLS_DC)
+{
+       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;
+       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);
+
+       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;
+       }
+}
+
+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_fcall_info fci;
-       zval zmethod;
-       zval *retval;
        STATUS rv;
+       zval *retval = NULL;
 
-       fci.size = sizeof(fci);
-       fci.object_ptr = object;
-       fci.function_name = &zmethod;
-       fci.retval_ptr_ptr = retval_ptr ? retval_ptr : &retval;
-       fci.param_count = argc;
-       fci.params = argv;
-       fci.no_separation = 1;
-       fci.symbol_table = NULL;
-       fci.function_table = NULL;
+       Z_ADDREF_P(zobject);
+       cb->fci.object_ptr = zobject;
+       cb->fcc.object_ptr = zobject;
 
-       INIT_PZVAL(&zmethod);
-       ZVAL_STRINGL(&zmethod, method_str, method_len, 0);
-       rv = zend_call_function(&fci, NULL TSRMLS_CC);
+       cb->fci.retval_ptr_ptr = 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);
+               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);
+       }
+
+       rv = zend_call_function(&cb->fci, &cb->fcc TSRMLS_CC);
+
+       zval_ptr_dtor(&zobject);
        if (!retval_ptr && retval) {
                zval_ptr_dtor(&retval);
        }
+
        return rv;
 }
 
index 63730b423ae195cc53d9609c43bb2048a6798ecf..1fbd817454ad6ddccbf3cb263aae3fd21b6af261 100644 (file)
@@ -24,7 +24,16 @@ zend_object_value php_http_object_new_ex(zend_class_entry *ce, void *nothing, ph
 typedef zend_object_value (*php_http_new_t)(zend_class_entry *ce, void *, void ** TSRMLS_DC);
 
 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);
-STATUS php_http_method_call(zval *object, const char *method_str, size_t method_len, int argc, zval **argv[], zval **retval_ptr 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 TSRMLS_DC);
+STATUS php_http_object_method_call(php_http_object_method_t *cb, zval *zobject, zval **retval, int argc, zval ***args TSRMLS_DC);
+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
 
index b4a614ea2727234851dd8b3463c7d06766109dad..5316265521c7592d0de4f113e6dc442f02fa51c9 100644 (file)
@@ -325,8 +325,8 @@ php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_u
        }
        /* unset default ports */
        if (url(buf)->port) {
-               if (    ((url(buf)->port == 80) && !strcmp(url(buf)->scheme, "http"))
-                       ||      ((url(buf)->port ==443) && !strcmp(url(buf)->scheme, "https"))
+               if (    ((url(buf)->port == 80) && url(buf)->scheme && !strcmp(url(buf)->scheme, "http"))
+                       ||      ((url(buf)->port ==443) && url(buf)->scheme && !strcmp(url(buf)->scheme, "https"))
                ) {
                        url(buf)->port = 0;
                }
@@ -398,6 +398,42 @@ char *php_http_url_to_string(const php_http_url_t *url, char **url_str, size_t *
        return buf.data;
 }
 
+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 TSRMLS_DC)
 {
        zval *zcpy;
@@ -890,7 +926,9 @@ static STATUS parse_hostinfo(struct parse_state *state, const char *ptr)
                        break;
 
                default:
-                       if (port) {
+                       if (ptr == end) {
+                               break;
+                       } else if (port) {
                                php_error_docref(NULL TSRMLS_CC, E_WARNING,
                                                "Failed to parse port; unexpected byte 0x%02x at pos %u in '%s'",
                                                (unsigned char) *ptr, (unsigned) (ptr - tmp), tmp);
@@ -961,6 +999,7 @@ static const char *parse_authority(struct parse_state *state)
                case '?':
                case '#':
                case '\0':
+                       EOD:
                        /* host delimiter */
                        if (tmp != state->ptr && SUCCESS != parse_hostinfo(state, tmp)) {
                                return NULL;
@@ -969,7 +1008,8 @@ static const char *parse_authority(struct parse_state *state)
                }
        } while (++state->ptr <= state->end);
 
-       return NULL;
+       --state->ptr;
+       goto EOD;
 }
 
 static const char *parse_path(struct parse_state *state)
@@ -1270,6 +1310,33 @@ php_http_url_t *php_http_url_parse(const char *str, size_t len, unsigned flags T
        return (php_http_url_t *) state;
 }
 
+php_http_url_t *php_http_url_parse_authority(const char *str, size_t len, unsigned flags TSRMLS_DC)
+{
+       size_t maxlen = 3 * len;
+       struct parse_state *state = ecalloc(1, sizeof(*state) + maxlen);
+
+       state->end = str + len;
+       state->ptr = str;
+       state->flags = flags;
+       state->maxlen = maxlen;
+       TSRMLS_SET_CTX(state->ts);
+
+       if (!(state->ptr = parse_authority(state))) {
+               efree(state);
+               return NULL;
+       }
+
+       if (state->ptr != state->end) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                               "Failed to parse URL authority, unexpected character at pos %u in '%s'",
+                               (unsigned) (state->ptr - str), str);
+               efree(state);
+               return NULL;
+       }
+
+       return (php_http_url_t *) state;
+}
+
 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl___construct, 0, 0, 0)
        ZEND_ARG_INFO(0, old_url)
        ZEND_ARG_INFO(0, new_url)
index fb9193200c567e2d0a227f252c2c20f4b09ba7c4..926804806701f1ecba675f8f54d8fbd6e12571e9 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <ext/standard/url.h>
 
+/* php_http_url_mod() */
 #define PHP_HTTP_URL_REPLACE           0x000
 #define PHP_HTTP_URL_JOIN_PATH         0x001
 #define PHP_HTTP_URL_JOIN_QUERY                0x002
@@ -57,6 +58,7 @@ typedef struct php_http_url {
 } 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 */
@@ -66,9 +68,9 @@ 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 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);
 
index f4fcfc85367417dee7cb8f711fe39d89f18250b3..7adef9d3c897ce66f5923b981c74bd3b46a992d0 100644 (file)
@@ -27,7 +27,7 @@ php_http_version_t *php_http_version_init(php_http_version_t *v, unsigned major,
 php_http_version_t *php_http_version_parse(php_http_version_t *v, const char *str TSRMLS_DC)
 {
        long major, minor;
-       char separator = 0, *stop = NULL;
+       char separator = 0;
        register const char *ptr = str;
 
        switch (*ptr) {
@@ -40,16 +40,16 @@ php_http_version_t *php_http_version_parse(php_http_version_t *v, const char *st
                ++ptr;
                /* no break */
        default:
-               major = strtol(ptr, &stop, 10);
-               if (stop && stop != ptr && major != LONG_MIN && major != LONG_MAX) {
-                       separator = *stop;
+               /* rfc7230#2.6 The HTTP version number consists of two decimal digits separated by a "." (period or decimal point) */
+               major = *ptr++ - '0';
+               if (major >= 0 && major <= 9) {
+                       separator = *ptr++;
                        if (separator) {
                                if (separator != '.' && separator != ',') {
-                                       php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Non-standard version separator '%c' in HTTP protocol version '%s'", separator, ptr);
+                                       php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Non-standard version separator '%c' in HTTP protocol version '%s'", separator, ptr - 2);
                                }
-                               ptr = stop + 1;
-                               minor = strtol(ptr, &stop, 10);
-                               if (minor != LONG_MIN && minor != LONG_MAX) {
+                               minor = *ptr - '0';
+                               if (minor >= 0 && minor <= 9) {
                                        return php_http_version_init(v, major, minor TSRMLS_CC);
                                }
                        }
index 17c60ed61ec869b6b4bf8544e0e78f6e04c24b1a..284edb25d10dac448cbd7230c328510373b02e06 100644 (file)
@@ -31,13 +31,8 @@ echo $client->getHistory()->toString(true);
 Done
 --EXPECTF--
 Test
-POST /ext-http/.print_request.php HTTP/1.1
-User-Agent: %s
-Host: dev.iworks.at
-Accept: */*
+POST http://dev.iworks.at/ext-http/.print_request.php HTTP/1.1
 Content-Length: 6
-Content-Type: application/x-www-form-urlencoded
-X-Original-Content-Length: 6
 
 foobar
 HTTP/1.1 200 OK
@@ -50,13 +45,8 @@ Content-Length: 19
 
 string(6) "foobar"
 
-POST /ext-http/.print_request.php HTTP/1.1
-User-Agent: %s
-Host: dev.iworks.at
-Accept: */*
+POST http://dev.iworks.at/ext-http/.print_request.php HTTP/1.1
 Content-Length: 6
-Content-Type: application/x-www-form-urlencoded
-X-Original-Content-Length: 6
 
 foobar
 HTTP/1.1 200 OK
@@ -69,13 +59,8 @@ Content-Length: 19
 
 string(6) "foobar"
 
-POST /ext-http/.print_request.php HTTP/1.1
-User-Agent: %s
-Host: dev.iworks.at
-Accept: */*
+POST http://dev.iworks.at/ext-http/.print_request.php HTTP/1.1
 Content-Length: 6
-Content-Type: application/x-www-form-urlencoded
-X-Original-Content-Length: 6
 
 foobar
 HTTP/1.1 200 OK
index ad6c2eb15f0d477cddc9e7712b6dd18999233176..e4c188c7ffd44cd8bac36c5567e859ad5ea2e4a3 100644 (file)
@@ -12,12 +12,12 @@ echo "Test\n";
 
 $client = new http\Client;
 
-$client->setSslOptions(array("verify_peer" => true));
-$client->addSslOptions(array("verify_host" => 2));
+$client->setSslOptions(array("verifypeer" => true));
+$client->addSslOptions(array("verifyhost" => 2));
 var_dump(
        array(
-               "verify_peer" => true,
-               "verify_host" => 2,
+               "verifypeer" => true,
+               "verifyhost" => 2,
        ) === $client->getSslOptions()
 );
 
diff --git a/tests/client017.phpt b/tests/client017.phpt
new file mode 100644 (file)
index 0000000..ea54146
--- /dev/null
@@ -0,0 +1,33 @@
+--TEST--
+client request gzip
+--SKIPIF--
+<?php 
+include "skipif.inc";
+skip_online_test();
+skip_client_test();
+?>
+--FILE--
+<?php 
+
+echo "Test\n";
+
+$client = new http\Client;
+$client->setOptions(["compress" => true]);
+
+$client->enqueue(new http\Client\Request("GET", "http://dev.iworks.at/ext-http/.print_request.php"));
+$client->send();
+
+echo $client->getResponse();
+
+?>
+===DONE===
+--EXPECTF--
+Test
+HTTP/1.1 200 OK
+Vary: Accept-Encoding
+Content-Type: text/html
+Date: %s
+Server: %s
+X-Original-Transfer-Encoding: chunked
+X-Original-Content-Encoding: gzip
+===DONE===
\ No newline at end of file
index 76141c16258ef3f098d8c84d3e1585aa65f0e9bc..248b999becb07c5c0bf47a7b465d89865bb79de8 100644 (file)
@@ -22,10 +22,15 @@ var_dump(stream_get_contents($f));
 Done
 --EXPECT--
 Test
-string(77) "HTTP/1.1 200 OK
+string(115) "HTTP/1.1 200 OK
 Accept-Ranges: bytes
 Foo: bar, baz
 ETag: "8c736521"
+Transfer-Encoding: chunked
 
-foo"
+3
+foo
+0
+
+"
 Done
index 5495fe2ba2708a0ee1d24b78a3dea093f8001455..d41be58907a5eeb1c83f00d573a91d84c9e0e84e 100644 (file)
@@ -34,6 +34,11 @@ Accept-Ranges: bytes%c
 X-Powered-By: %s%c
 Content-Type: text/plain%c
 Content-Range: bytes 2-4/9%c
+Transfer-Encoding: chunked%c
 %c
-234"
+3%c
+234%c
+0%c
+%c
+"
 Done
index 379ab57a8eb72ee94149bfb9a3ff7aeda9e65316..acbda8901e42ce308d94be6fa98fa461c5c61d14 100644 (file)
@@ -18,6 +18,7 @@ $r->send($f);
 rewind($f);
 var_dump(stream_get_contents($f));
 
+?>
 --EXPECTREGEX--
 string\(\d+\) "HTTP\/1\.1 200 OK
 Accept-Ranges: bytes
@@ -26,5 +27,10 @@ Content-Encoding: gzip
 Vary: Accept-Encoding
 ETag: "\w+-\w+-\w+"
 Last-Modified: \w+, \d+ \w+ \d{4} \d\d:\d\d:\d\d GMT
+Transfer-Encoding: chunked
 
+d0
 \x1f\x8b\x08.+
+0
+
+"
index 2a337e4b59d53dc03fb9d01e79b0096be3b38efb..1ca5fad1da91cf168c15759bb5719ecfef4409ce 100644 (file)
@@ -24,7 +24,9 @@ Accept-Ranges: bytes%c
 X-Powered-By: %s%c
 ETag: "abc"%c
 Last-Modified: %s%c
+Transfer-Encoding: chunked%c
 %c
+e1%c
 <?php
 $f = tmpfile();
 $r = new http\Env\Response;
@@ -35,5 +37,8 @@ $r->send($f);
 rewind($f);
 var_dump(stream_get_contents($f));
 ?>
+%c
+0%c
+%c
 "
 
index d365497e4aa334b6e0e4520f21c47f214c77bfd4..9b040d55619d870a29d37cab9878314aeb035e43 100644 (file)
@@ -25,5 +25,10 @@ Accept-Ranges: bytes%c
 X-Powered-By: PHP/%s%c
 Content-Type: text/plain%c
 Content-Range: bytes 2-4/311%c
+Transfer-Encoding: chunked%c
 %c
-php"
+3%c
+php%c
+0%c
+%c
+"
index ef85e2c2f7cbc157f9dae7fa5c4f8f12bfb3f8d4..6b71aacaae5816d66c3882897e2bca4ee874c8af 100644 (file)
@@ -16,9 +16,12 @@ $res->send($f);
 rewind($f);
 var_dump(stream_get_contents($f));
 --EXPECTF--
-string(96) "HTTP/1.1 416 Requested Range Not Satisfiable
+string(129) "HTTP/1.1 416 Requested Range Not Satisfiable
 Accept-Ranges: bytes
 Content-Range: bytes */6
+Transfer-Encoding: chunked
+
+0
 
 "
 
index abad2bb095c36b5e4be4ff04cb40cdf2466843b2..1e8b15eae0b3042ef793173177b1c24738273560 100644 (file)
@@ -32,5 +32,10 @@ echo stream_get_contents($f);
 HTTP/1.1 200 OK
 Accept-Ranges: bytes
 ETag: "fc8305a1"
+Transfer-Encoding: chunked
 
+9
 foo: bar
+
+0
+
diff --git a/tests/envresponse017.phpt b/tests/envresponse017.phpt
new file mode 100644 (file)
index 0000000..ffa40cb
--- /dev/null
@@ -0,0 +1,28 @@
+--TEST--
+env response stream: no chunked transfer encoding for CONNECTs
+--SKIPIF--
+<?php 
+include "skipif.inc";
+?>
+--FILE--
+<?php 
+echo "Test\n";
+
+$req = new http\Env\Request;
+$req->setRequestMethod("CONNECT");
+$req->setRequestUrl(["host"=>"www.example.com", "port"=>80]);
+
+echo $req;
+
+$res = new http\Env\Response;
+$res->setEnvRequest($req);
+$res->send(STDOUT);
+
+?>
+===DONE===
+--EXPECTF--
+Test
+CONNECT www.example.com:80 HTTP/1.1
+HTTP/1.1 200 OK
+
+===DONE===
index 1fe11a041ef247ab342ffc453c9ed194ffc8c914..c0e93ed01afa0102c194074613e7d5af263968c3 100644 (file)
@@ -23,4 +23,7 @@ Set-Cookie: foo=bar; max-age=60;
 Set-Cookie: baz=1; 
 Set-Cookie: 123=1; 
 ETag: ""
+Transfer-Encoding: chunked
+
+0
 
diff --git a/tests/proxy.inc b/tests/proxy.inc
new file mode 100644 (file)
index 0000000..89d31f4
--- /dev/null
@@ -0,0 +1,26 @@
+<?php 
+
+foreach (range(8000, 9000) as $port) {
+       if (($server = stream_socket_server("tcp://localhost:$port"))) {
+               fprintf(STDERR, "%s\n", $port);
+               if (($client = stream_socket_accept($server))) {
+                       /* this might be a proxy connect or a standard request */
+                       $request = new http\Message($client, false);
+                       
+                       if ($request->getHeader("Proxy-Connection")) {
+                               $response = new http\Env\Response;
+                               $response->setHeader("Content-Length", 0);
+                               $response->send($client);
+                               
+                               /* soak up the request following the connect */
+                               new http\Message($client, false);
+                       }
+                       
+                       /* return the initial message as response body */
+                       $response = new http\Env\Response;
+                       $response->getBody()->append($request);
+                       $response->send($client);
+               }
+               return;
+       }
+}
\ No newline at end of file
diff --git a/tests/proxy001.phpt b/tests/proxy001.phpt
new file mode 100644 (file)
index 0000000..c8a2e63
--- /dev/null
@@ -0,0 +1,48 @@
+--TEST--
+proxy - send proxy headers for a proxy request
+--SKIPIF--
+<?php 
+include "skipif.inc";
+skip_client_test();
+?>
+--FILE--
+<?php
+
+echo "Test\n";
+
+$spec = array(array("pipe","r"), array("pipe","w"), array("pipe","w"));
+if (($proc = proc_open(PHP_BINARY . " proxy.inc", $spec, $pipes, __DIR__))) {
+       $port = trim(fgets($pipes[2]));
+       echo "Server on port $port\n";
+       $c = new http\Client;
+       $r = new http\Client\Request("GET", "http://www.example.com/");
+       $r->setOptions(array(
+               "timeout" => 3,
+               "proxytunnel" => true,
+               "proxyheader" => array("Hello" => "there!"),
+               "proxyhost" => "localhost",
+               "proxyport" => $port,
+       ));
+       try {
+               $c->enqueue($r)->send();
+       } catch (Exception $e) {
+               echo $e;
+       }
+       echo $c->getResponse()->getBody();
+       while (!feof($pipes[1])) {
+               echo fgets($pipes[1]);
+       }
+       unset($r, $client);
+}
+?>
+===DONE===
+--EXPECTF--
+Test
+Server on port %d
+CONNECT www.example.com:80 HTTP/1.1
+Host: www.example.com:80
+User-Agent: PECL_HTTP/%s PHP/%s libcurl/%s
+Proxy-Connection: Keep-Alive
+Hello: there!
+Content-Length: 0
+===DONE===
diff --git a/tests/proxy002.phpt b/tests/proxy002.phpt
new file mode 100644 (file)
index 0000000..86bee61
--- /dev/null
@@ -0,0 +1,44 @@
+--TEST--
+proxy - don't send proxy headers for a standard request
+--SKIPIF--
+<?php 
+include "skipif.inc";
+skip_client_test();
+?>
+--FILE--
+<?php
+
+echo "Test\n";
+
+$spec = array(array("pipe","r"), array("pipe","w"), array("pipe","w"));
+if (($proc = proc_open(PHP_BINARY . " proxy.inc", $spec, $pipes, __DIR__))) {
+       $port = trim(fgets($pipes[2]));
+       echo "Server on port $port\n";
+       $c = new http\Client;
+       $r = new http\Client\Request("GET", "http://localhost:$port/");
+       $r->setOptions(array(
+               "timeout" => 3,
+               "proxyheader" => array("Hello" => "there!"),
+       ));
+       try {
+               $c->enqueue($r)->send();
+       } catch (Exception $e) {
+               echo $e;
+       }
+       echo $c->getResponse()->getBody();
+       while (!feof($pipes[1])) {
+               echo fgets($pipes[1]);
+       }
+       unset($r, $client);
+}
+?>
+===DONE===
+--EXPECTF--
+Test
+Server on port %d
+GET / HTTP/1.1
+User-Agent: PECL_HTTP/%s PHP/%s libcurl/%s
+Host: localhost:%d
+Accept: */*
+Content-Length: 0
+===DONE===
index 28d9a1325ffb9c50a6f8746e611f4eaa12a359b4..4b2627a9c4436e0f720f6ccbb3e0503edc746911 100644 (file)
@@ -8,8 +8,14 @@ function skip_online_test($message = "skip test requiring internet connection\n"
        }
 }
 
-function skip_slow_test($message = "skip slow test") {
+function skip_slow_test($message = "skip slow test\n") {
        if (getenv("SKIP_SLOW_TESTS")) {
                die($message);
        }
 }
+
+function skip_client_test($message = "skip need a client driver\n") {
+       if (!http\Client::getAvailableDrivers()) {
+               die($message);
+       }
+}