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

36 files changed:
config9.m4
gen_curlinfo.php
php_http_buffer.c
php_http_client.c
php_http_client.h
php_http_client_curl.c
php_http_cookie.c
php_http_encoding.c
php_http_env_response.c
php_http_info.c
php_http_info.h
php_http_message.c
php_http_message_body.c
php_http_message_parser.c
php_http_object.c
php_http_object.h
php_http_url.c
php_http_url.h
php_http_version.c
tests/bug69000.phpt [new file with mode: 0644]
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 9b88242153e9c47969731984ce7717a59c79ff79..46f70f9f4d2c501a518b75c6bd5026562a904bb7 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 66fd87b92db03d302d4d56bbc0277e88600db5ac..2e2100fd68d7174768315e7f86fa31bb972bd262 100755 (executable)
@@ -40,7 +40,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 059db0670e18cab29461c0c12fe7431c0b2692c0..b84bcd0cb37ac247d06b5e7b7591435c02798a67 100644 (file)
@@ -50,7 +50,7 @@ PHP_HTTP_BUFFER_API size_t php_http_buffer_resize_ex(
 {
        char *ptr = NULL;
 #if 0
-       fprintf(stderr, "RESIZE: len=%lu, size=%lu, used=%lu, free=%lu\n", len, buf->size, buf->used, buf->free);
+       fprintf(stderr, "RESIZE: len=%lu, size=%lu, used=%lu, free=%lu, total=%lu\n", len, buf->size, buf->used, buf->free, buf->free+buf->used);
 #endif
        if (buf->free < len) {
                size_t size = override_size ? override_size : buf->size;
@@ -81,10 +81,7 @@ PHP_HTTP_BUFFER_API size_t php_http_buffer_resize_ex(
 PHP_HTTP_BUFFER_API char *php_http_buffer_account(
                php_http_buffer_t *buf, size_t to_account)
 {
-       /* it's probably already too late but check anyway */
-       if (to_account > buf->free) {
-               return NULL;
-       }
+       assert(to_account <= buf->free);
 
        buf->free -= to_account;
        buf->used += to_account;
index 66afb7cd7957fc6ca0ec9f760da1f9e7bd6eb9f1..4e55356ed4abf2f86f8c2a62914025c65d7b6f7e 100644 (file)
@@ -326,6 +326,8 @@ void php_http_client_object_free(zend_object *object)
        php_http_client_object_t *o = PHP_HTTP_OBJ(object, NULL);
 
        php_http_client_free(&o->client);
+       php_http_object_method_dtor(&o->notify);
+       php_http_object_method_free(&o->update);
        zend_object_std_dtor(object);
 }
 
@@ -333,7 +335,7 @@ php_http_client_object_t *php_http_client_object_new_ex(zend_class_entry *ce, ph
 {
        php_http_client_object_t *o;
 
-       o = ecalloc(1, sizeof(php_http_client_object_t) + (ce->default_properties_count - 1) * sizeof(zval));
+       o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
        zend_object_std_init(&o->zo, ce);
        object_properties_init(&o->zo, ce);
 
@@ -367,7 +369,7 @@ static void handle_history(zval *zclient, php_http_message_t *request, php_http_
        zval_ptr_dtor(&new_hist);
 }
 
-static ZEND_RESULT_CODE handle_response(void *arg, php_http_client_t *client, php_http_client_enqueue_t *e, php_http_message_t **request, php_http_message_t **response)
+static ZEND_RESULT_CODE handle_response(void *arg, php_http_client_t *client, php_http_client_enqueue_t *e, php_http_message_t **response)
 {
        zend_bool dequeue = 0;
        zval zclient;
@@ -385,7 +387,7 @@ static ZEND_RESULT_CODE handle_response(void *arg, php_http_client_t *client, ph
                php_http_message_set_type(msg, PHP_HTTP_RESPONSE);
 
                if (zend_is_true(zend_read_property(php_http_client_class_entry, &zclient, ZEND_STRL("recordHistory"), 0, &rec_hist_tmp))) {
-                       handle_history(&zclient, *request, *response);
+                       handle_history(&zclient, e->request, *response);
                }
 
                /* hard detach, redirects etc. are in the history */
@@ -443,27 +445,28 @@ static ZEND_RESULT_CODE handle_response(void *arg, php_http_client_t *client, ph
 
 static void handle_progress(void *arg, php_http_client_t *client, php_http_client_enqueue_t *e, php_http_client_progress_state_t *progress)
 {
-       zval zrequest, zprogress, retval, zclient;
+       zval zclient, args[2];
+       php_http_client_object_t *client_obj = arg;
        zend_error_handling zeh;
 
-       ZVAL_UNDEF(&retval);
-       ZVAL_OBJECT(&zclient, &((php_http_client_object_t *) arg)->zo, 1);
-       ZVAL_OBJECT(&zrequest, &((php_http_message_object_t *) e->opaque)->zo, 1);
-       object_init(&zprogress);
-       add_property_bool(&zprogress, "started", progress->started);
-       add_property_bool(&zprogress, "finished", progress->finished);
-       add_property_string(&zprogress, "info", STR_PTR(progress->info));
-       add_property_double(&zprogress, "dltotal", progress->dl.total);
-       add_property_double(&zprogress, "dlnow", progress->dl.now);
-       add_property_double(&zprogress, "ultotal", progress->ul.total);
-       add_property_double(&zprogress, "ulnow", progress->ul.now);
+       ZVAL_OBJECT(&zclient, &client_obj->zo, 1);
+       ZVAL_OBJECT(&args[0], &((php_http_message_object_t *) e->opaque)->zo, 1);
+       object_init(&args[1]);
+       add_property_bool(&args[1], "started", progress->started);
+       add_property_bool(&args[1], "finished", progress->finished);
+       add_property_string(&args[1], "info", STR_PTR(progress->info));
+       add_property_double(&args[1], "dltotal", progress->dl.total);
+       add_property_double(&args[1], "dlnow", progress->dl.now);
+       add_property_double(&args[1], "ultotal", progress->ul.total);
+       add_property_double(&args[1], "ulnow", progress->ul.now);
+
        zend_replace_error_handling(EH_NORMAL, NULL, &zeh);
-       zend_call_method_with_2_params(&zclient, NULL, NULL, "notify", &retval, &zrequest, &zprogress);
+       php_http_object_method_call(&client_obj->notify, &zclient, NULL, 2, args);
        zend_restore_error_handling(&zeh);
+
        zval_ptr_dtor(&zclient);
-       zval_ptr_dtor(&zrequest);
-       zval_ptr_dtor(&zprogress);
-       zval_ptr_dtor(&retval);
+       zval_ptr_dtor(&args[0]);
+       zval_ptr_dtor(&args[1]);
 }
 
 static void response_dtor(void *data)
@@ -512,6 +515,8 @@ static PHP_METHOD(HttpClient, __construct)
 
                php_http_expect(obj->client = php_http_client_init(NULL, driver->client_ops, rf, NULL), runtime, return);
 
+               php_http_object_method_init(&obj->notify, getThis(), ZEND_STRL("notify"));
+
                obj->client->callback.response.func = handle_response;
                obj->client->callback.response.arg = obj;
                obj->client->callback.progress.func = handle_progress;
@@ -843,13 +848,19 @@ static PHP_METHOD(HttpClient, enableEvents)
        RETVAL_ZVAL_FAST(getThis());
 }
 
+struct notify_arg {
+       php_http_object_method_t *cb;
+       zval args[3];
+       int argc;
+};
+
 static int notify(zend_object_iterator *iter, void *puser)
 {
-       zval *observer, *args = puser;
+       zval *observer;
+       struct notify_arg *arg = puser;
 
        if ((observer = iter->funcs->get_current_data(iter))) {
-               int num_args = !Z_ISUNDEF(args[0]) + !Z_ISUNDEF(args[1]) + !Z_ISUNDEF(args[2]);
-               return php_http_method_call(observer, ZEND_STRL("update"), num_args, args, NULL);
+               return php_http_object_method_call(arg->cb, observer, NULL, arg->argc, arg->args);
        }
        return FAILURE;
 }
@@ -859,10 +870,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_tmp, *observers, args[3];
+       zval *request = NULL, *zprogress = NULL, observers_tmp, *observers;
+       php_http_client_object_t *client_obj;
+       struct notify_arg arg = {NULL};
 
        php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|O!o!", &request, php_http_client_request_class_entry, &zprogress), invalid_arg, return);
 
+       client_obj = PHP_HTTP_OBJ(NULL, getThis());
        observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0, &observers_tmp);
 
        if (Z_TYPE_P(observers) != IS_OBJECT) {
@@ -870,26 +884,29 @@ static PHP_METHOD(HttpClient, notify)
                return;
        }
 
-       ZVAL_COPY(&args[0], getThis());
-       if (request) {
-               ZVAL_COPY(&args[1], request);
-       } else {
-               ZVAL_UNDEF(&args[1]);
-       }
-       if (zprogress) {
-               ZVAL_COPY(&args[2], zprogress);
-       } else {
-               ZVAL_UNDEF(&args[2]);
-       }
+       if (client_obj->update) {
+               arg.cb = client_obj->update;
+               ZVAL_COPY(&arg.args[0], getThis());
+               arg.argc = 1;
 
-       spl_iterator_apply(observers, notify, args);
+               if (request) {
+                       ZVAL_COPY(&arg.args[1], request);
+                       arg.argc += 1;
+               }
+               if (zprogress) {
+                       ZVAL_COPY(&arg.args[2], zprogress);
+                       arg.argc += 1;
+               }
 
-       zval_ptr_dtor(getThis());
-       if (request) {
-               zval_ptr_dtor(request);
-       }
-       if (zprogress) {
-               zval_ptr_dtor(zprogress);
+               spl_iterator_apply(observers, notify, &arg);
+
+               zval_ptr_dtor(getThis());
+               if (request) {
+                       zval_ptr_dtor(request);
+               }
+               if (zprogress) {
+                       zval_ptr_dtor(zprogress);
+               }
        }
 
        RETVAL_ZVAL_FAST(getThis());
@@ -901,9 +918,11 @@ ZEND_END_ARG_INFO();
 static PHP_METHOD(HttpClient, attach)
 {
        zval observers_tmp, *observers, *observer, retval;
+       php_http_client_object_t *client_obj;
 
        php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "O", &observer, spl_ce_SplObserver), invalid_arg, return);
 
+       client_obj = PHP_HTTP_OBJ(NULL, getThis());
        observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0, &observers_tmp);
 
        if (Z_TYPE_P(observers) != IS_OBJECT) {
@@ -911,6 +930,10 @@ static PHP_METHOD(HttpClient, attach)
                return;
        }
 
+       if (!client_obj->update) {
+               client_obj->update = php_http_object_method_init(NULL, observer, ZEND_STRL("update"));
+       }
+
        ZVAL_UNDEF(&retval);
        zend_call_method_with_1_params(observers, NULL, NULL, "attach", &retval, observer);
        zval_ptr_dtor(&retval);
index 5067f6c5832415cf905d704c6f99e5ef1308220f..41671b9ff813a0c061615636ebc32615d297a138 100644 (file)
@@ -85,7 +85,7 @@ typedef struct php_http_client_progress_state {
        unsigned finished:1;
 } php_http_client_progress_state_t;
 
-typedef ZEND_RESULT_CODE (*php_http_client_response_callback_t)(void *arg, struct php_http_client *client, php_http_client_enqueue_t *e, php_http_message_t **request, php_http_message_t **response);
+typedef ZEND_RESULT_CODE (*php_http_client_response_callback_t)(void *arg, struct php_http_client *client, php_http_client_enqueue_t *e, php_http_message_t **response);
 typedef void (*php_http_client_progress_callback_t)(void *arg, struct php_http_client *client, php_http_client_enqueue_t *e, php_http_client_progress_state_t *state);
 
 typedef struct php_http_client {
@@ -112,6 +112,8 @@ PHP_HTTP_API zend_class_entry *php_http_client_class_entry;
 
 typedef struct php_http_client_object {
        php_http_client_t *client;
+       php_http_object_method_t *update;
+       php_http_object_method_t notify;
        long iterator;
        zend_object zo;
 } php_http_client_object_t;
index fc06b38d25093f24cbec4595ff1ecbdeeb546e43..05db912a1cb05da061e110ce4d8eaa9baa267830 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;
@@ -224,25 +217,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 && 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) {
@@ -299,32 +290,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 */
@@ -334,9 +299,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)
 {
-       return n*l;
+       php_http_client_curl_handler_t *h = arg;
+
+       return php_http_buffer_append(&h->response.headers, data, n * l);
+}
+
+static int php_http_curle_body_callback(char *data, size_t n, size_t l, void *arg)
+{
+       php_http_client_curl_handler_t *h = arg;
+
+       return php_http_message_body_append(h->response.body, data, n*l);
 }
 
 static ZEND_RESULT_CODE php_http_curle_get_info(CURL *ch, HashTable *info)
@@ -458,16 +432,6 @@ static ZEND_RESULT_CODE php_http_curle_get_info(CURL *ch, HashTable *info)
                zend_hash_str_update(info, "ssl_engines", lenof("ssl_engines"), &tmp);
                curl_slist_free_all(s);
        }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_COOKIELIST, &s)) {
-               array_init(&tmp);
-               for (p = s; p; p = p->next) {
-                       if (p->data) {
-                               add_next_index_string(&tmp, p->data);
-                       }
-               }
-               zend_hash_str_update(info, "cookies", lenof("cookies"), &tmp);
-               curl_slist_free_all(s);
-       }
        if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_URL, &c)) {
                ZVAL_STRING(&tmp, STR_PTR(c));
                zend_hash_str_update(info, "redirect_url", lenof("redirect_url"), &tmp);
@@ -639,6 +603,54 @@ 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")))) {
+               Z_TRY_ADDREF_P(zh);
+               zend_hash_str_update(&response->hdrs, "X-Original-Content-Length", lenof("X-Original-Content-Length"), zh);
+       }
+       if ((zh = php_http_message_header(response, ZEND_STRL("Transfer-Encoding")))) {
+               Z_TRY_ADDREF_P(zh);
+               zend_hash_str_update(&response->hdrs, "X-Original-Transfer-Encoding", lenof("X-Original-Transfer-Encoding"), zh);
+               zend_hash_str_del(&response->hdrs, "Transfer-Encoding", lenof("Transfer-Encoding"));
+       }
+       if ((zh = php_http_message_header(response, ZEND_STRL("Content-Range")))) {
+               Z_TRY_ADDREF_P(zh);
+               zend_hash_str_update(&response->hdrs, "X-Original-Content-Range", lenof("X-Original-Content-Range"), zh);
+               zend_hash_str_del(&response->hdrs, "Content-Range", lenof("Content-Range"));
+       }
+       if ((zh = php_http_message_header(response, ZEND_STRL("Content-Encoding")))) {
+               Z_TRY_ADDREF_P(zh);
+               zend_hash_str_update(&response->hdrs, "X-Original-Content-Encoding", lenof("X-Original-Content-Encoding"), zh);
+               zend_hash_str_del(&response->hdrs, "Content-Encoding", lenof("Content-Encoding"));
+       }
+       php_http_message_update_headers(response);
+
+       return response;
+}
+
 static void php_http_curlm_responsehandler(php_http_client_t *context)
 {
        int remaining = 0;
@@ -656,8 +668,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);
@@ -944,9 +960,10 @@ static ZEND_RESULT_CODE php_http_curle_option_set_lastmodified(php_http_option_t
 static ZEND_RESULT_CODE php_http_curle_option_set_compress(php_http_option_t *opt, zval *val, void *userdata)
 {
        php_http_client_curl_handler_t *curl = userdata;
+       CURL *ch = curl->handle;
 
-       if (Z_TYPE_P(val) == IS_TRUE) {
-               curl->options.headers = curl_slist_append(curl->options.headers, "Accept-Encoding: gzip;q=1.0,deflate;q=0.5");
+       if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_ACCEPT_ENCODING, Z_TYPE_P(val) == IS_TRUE ? "" : NULL)) {
+               return FAILURE;
        }
        return SUCCESS;
 }
@@ -1079,6 +1096,42 @@ static ZEND_RESULT_CODE php_http_curle_option_set_portrange(php_http_option_t *o
        return SUCCESS;
 }
 
+#if PHP_HTTP_CURL_VERSION(7,37,0)
+static ZEND_RESULT_CODE php_http_curle_option_set_proxyheader(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+
+       if (val && Z_TYPE_P(val) != IS_NULL) {
+               php_http_arrkey_t header_key;
+               zval *header_val;
+               php_http_buffer_t header;
+
+               php_http_buffer_init(&header);
+               ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(val), header_key.h, header_key.key, header_val)
+               {
+                       if (header_key.key) {
+                               zend_string *zs = zval_get_string(header_val);
+                               php_http_buffer_appendf(&header, "%s: %s", header_key.key->val, zs->val);
+                               php_http_buffer_fix(&header);
+                               curl->options.proxyheaders = curl_slist_append(curl->options.proxyheaders, header.data);
+                               php_http_buffer_reset(&header);
+                               zend_string_release(zs);
+                       }
+               }
+               ZEND_HASH_FOREACH_END();
+               php_http_buffer_dtor(&header);
+       }
+       if (CURLE_OK != curl_easy_setopt(curl->handle, CURLOPT_PROXYHEADER, curl->options.proxyheaders)) {
+               return FAILURE;
+       }
+       if (CURLE_OK != curl_easy_setopt(curl->handle, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE)) {
+               curl_easy_setopt(curl->handle, CURLOPT_PROXYHEADER, NULL);
+               return FAILURE;
+       }
+       return SUCCESS;
+}
+#endif
+
 #if PHP_HTTP_CURL_VERSION(7,21,3)
 static ZEND_RESULT_CODE php_http_curle_option_set_resolve(php_http_option_t *opt, zval *val, void *userdata)
 {
@@ -1109,6 +1162,30 @@ static ZEND_RESULT_CODE php_http_curle_option_set_resolve(php_http_option_t *opt
 }
 #endif
 
+#if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP)
+static ZEND_RESULT_CODE php_http_curle_option_set_ssl_tlsauthtype(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+       CURL *ch = curl->handle;
+
+       if (val && Z_LVAL_P(val)) {
+               switch (Z_LVAL_P(val)) {
+               case CURL_TLSAUTH_SRP:
+                       if (CURLE_OK == curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, PHP_HTTP_CURL_TLSAUTH_SRP)) {
+                               return SUCCESS;
+                       }
+                       /* no break */
+               default:
+                       return FAILURE;
+               }
+       }
+       if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, PHP_HTTP_CURL_TLSAUTH_DEF)) {
+               return FAILURE;
+       }
+       return SUCCESS;
+}
+#endif
+
 static void php_http_curle_options_init(php_http_options_t *registry)
 {
        php_http_option_t *opt;
@@ -1130,6 +1207,19 @@ static void php_http_curle_options_init(php_http_options_t *registry)
        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;
@@ -1341,6 +1431,9 @@ static void php_http_curle_options_init(php_http_options_t *registry)
                        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;
@@ -1383,6 +1476,23 @@ static void php_http_curle_options_init(php_http_options_t *registry)
                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) && defined(PHP_HTTP_CURL_TLSAUTH_SRP)
+               if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthtype"), CURLOPT_TLSAUTH_TYPE, IS_LONG))) {
+                       opt->setter = php_http_curle_option_set_ssl_tlsauthtype;
+               }
+               if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthuser"), CURLOPT_TLSAUTH_USERNAME, IS_STRING))) {
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+               }
+               if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthpass"), CURLOPT_TLSAUTH_PASSWORD, IS_STRING))) {
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+               }
 #endif
        }
 }
@@ -1393,6 +1503,7 @@ static zval *php_http_curle_get_option(php_http_option_t *opt, HashTable *option
        zval *option;
 
        if ((option = php_http_option_get(opt, options, NULL))) {
+               Z_TRY_ADDREF_P(option);
                convert_to_explicit_type_ex(option, opt->type);
                zend_hash_update(&curl->options.cache, opt->name, option);
        }
@@ -1530,6 +1641,10 @@ static ZEND_RESULT_CODE php_http_client_curl_handler_reset(php_http_client_curl_
                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);
@@ -1551,12 +1666,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);
-       handler->request.message = php_http_message_init(NULL, 0, NULL);
-       handler->response.buffer = php_http_buffer_init(NULL);
-       handler->response.parser = php_http_message_parser_init(NULL);
-       handler->response.message = php_http_message_init(NULL, 0, NULL);
+       handler->response.body = php_http_message_body_init(NULL, NULL);
+       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);
@@ -1569,11 +1680,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);
@@ -1582,6 +1693,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);
 
@@ -1636,6 +1749,9 @@ static ZEND_RESULT_CODE php_http_client_curl_handler_prepare(php_http_client_cur
                }
        }
 
+       /* 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)) {
@@ -1679,19 +1795,17 @@ static ZEND_RESULT_CODE php_http_client_curl_handler_prepare(php_http_client_cur
                 * 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;
 }
 
@@ -1714,12 +1828,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);
        php_resource_factory_free(&handler->rf);
 
-       php_http_message_parser_free(&handler->request.parser);
-       php_http_message_free(&handler->request.message);
-       php_http_buffer_free(&handler->request.buffer);
-       php_http_message_parser_free(&handler->response.parser);
-       php_http_message_free(&handler->response.message);
-       php_http_buffer_free(&handler->response.buffer);
+       php_http_message_body_free(&handler->response.body);
+       php_http_buffer_dtor(&handler->response.headers);
        php_http_buffer_dtor(&handler->options.ranges);
        php_http_buffer_dtor(&handler->options.cookies);
        zend_hash_destroy(&handler->options.cache);
@@ -2095,7 +2205,6 @@ php_http_client_ops_t *php_http_client_curl_get_ops(void)
 PHP_MINIT_FUNCTION(http_client_curl)
 {
        php_http_options_t *options;
-       php_http_client_driver_t driver;
 
        PHP_HTTP_G->client.curl.driver.driver_name = zend_string_init(ZEND_STRL("curl"), 1);
        PHP_HTTP_G->client.curl.driver.client_name = zend_string_init(ZEND_STRL("http\\Client\\Curl"), 1);
@@ -2125,6 +2234,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);
 
        /*
@@ -2139,6 +2251,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 706fc10fe2d41a1b12334d9f9c4618337b97f9e0..ff67b1c1d2c6ed2a189f2e18647b9db196ac53b2 100644 (file)
@@ -382,7 +382,7 @@ php_http_cookie_object_t *php_http_cookie_object_new_ex(zend_class_entry *ce, ph
                ce = php_http_cookie_class_entry;
        }
 
-       o = ecalloc(sizeof(*o) + sizeof(zval) * (ce->default_properties_count - 1), 1);
+       o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
        zend_object_std_init(&o->zo, ce);
        object_properties_init(&o->zo, ce);
        o->zo.handlers = &php_http_cookie_object_handlers;
index 1ef355e817d1e4ce75b379b65b496ed9567ebb2b..ffa10b379a2467f0222141cc6d2b38694df6efd4 100644 (file)
@@ -909,7 +909,7 @@ php_http_encoding_stream_object_t *php_http_encoding_stream_object_new_ex(zend_c
 {
        php_http_encoding_stream_object_t *o;
 
-       o = ecalloc(1, sizeof(*o) + (ce->default_properties_count - 1) * sizeof(zval));
+       o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
        zend_object_std_init(&o->zo, ce);
        object_properties_init(&o->zo, ce);
 
index 80043a22f089ff7d562a4822c935ff2b00ee7136..7c76c081870ed8fbfe9ec7a6a345b2dbecc2cb4f 100644 (file)
@@ -834,9 +834,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 ZEND_RESULT_CODE php_http_env_response_stream_init(php_http_env_response_t *r, void *init_arg)
@@ -850,6 +852,13 @@ static ZEND_RESULT_CODE php_http_env_response_stream_init(php_http_env_response_
        ZEND_INIT_SYMTABLE(&ctx->header);
        php_http_version_init(&ctx->version, 1, 1);
        ctx->status_code = 200;
+       ctx->chunked = 1;
+       ctx->request = get_request(&r->options);
+
+       /* there are some limitations regarding TE:chunked, see https://tools.ietf.org/html/rfc7230#section-3.3.1 */
+       if (ctx->request && ctx->request->http.version.major == 1 && ctx->request->http.version.minor == 0) {
+               ctx->version.minor = 0;
+       }
 
        r->ctx = ctx;
 
@@ -875,6 +884,12 @@ static void php_http_env_response_stream_header(php_http_env_response_stream_ctx
                } else {
                        zend_string *zs = zval_get_string(val);
 
+                       if (ctx->chunked) {
+                               /* disable chunked transfer encoding if we've got an explicit content-length */
+                               if (!strncasecmp(zs->val, "Content-Length:", lenof("Content-Length:"))) {
+                                       ctx->chunked = 0;
+                               }
+                       }
                        php_stream_write(ctx->stream, zs->val, zs->len);
                        php_stream_write_string(ctx->stream, PHP_HTTP_CRLF);
                        zend_string_release(zs);
@@ -888,10 +903,28 @@ static ZEND_RESULT_CODE php_http_env_response_stream_start(php_http_env_response
                return FAILURE;
        }
 
-       php_stream_printf(ctx->stream TSRMLS_CC, "HTTP/%u.%u %ld %s" PHP_HTTP_CRLF, ctx->version.major, ctx->version.minor, ctx->status_code, php_http_env_get_response_status_for_code(ctx->status_code));
+       php_stream_printf(ctx->stream, "HTTP/%u.%u %ld %s" PHP_HTTP_CRLF, ctx->version.major, ctx->version.minor, ctx->status_code, php_http_env_get_response_status_for_code(ctx->status_code));
+
+       /* there are some limitations regarding TE:chunked, see https://tools.ietf.org/html/rfc7230#section-3.3.1 */
+       if (ctx->version.major == 1 && ctx->version.minor == 0) {
+               ctx->chunked = 0;
+       } else if (ctx->status_code == 204 || ctx->status_code/100 == 1) {
+               ctx->chunked = 0;
+       } else if (ctx->request && ctx->status_code/100 == 2 && !strcasecmp(ctx->request->http.info.request.method, "CONNECT")) {
+               ctx->chunked = 0;
+       }
+
        php_http_env_response_stream_header(ctx, &ctx->header);
+
+       /* 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)
@@ -1006,10 +1039,18 @@ static ZEND_RESULT_CODE php_http_env_response_stream_write(php_http_env_response
                }
        }
 
+       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 ZEND_RESULT_CODE php_http_env_response_stream_flush(php_http_env_response_t *r)
@@ -1040,6 +1081,10 @@ static ZEND_RESULT_CODE php_http_env_response_stream_finish(php_http_env_respons
                }
        }
 
+       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;
index f0fe7ad72bcc703b4e259a240498e72a4985e5af..66007bf33d32edac36520a9a657235807da4e9af 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)
-       ||      (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);
+                               /* 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);
+                               } else {
+                                       PHP_HTTP_INFO(info).request.url = php_http_url_parse_authority(url, http - url, ~0);
+                               }
                        } else {
                                PTR_SET(PHP_HTTP_INFO(info).request.method, NULL);
                                return NULL;
@@ -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 579f5733b91967be9db0c27b95904472cad3b88a..b771c5cc40912cf0e0798e6f5119e8e18dc85b61 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 145695037d45e00fa0f29296eca7768fc44bf85f..ae7ca6e9706c16ac80e34c37fded29215329e956 100644 (file)
@@ -604,9 +604,8 @@ static void php_http_message_object_prophandler_set_headers(php_http_message_obj
 
        if (Z_TYPE_P(value) != IS_ARRAY && Z_TYPE_P(value) != IS_OBJECT) {
                convert_to_array_ex(value);
-       } else {
-               headers = HASH_OF(value);
        }
+       headers = HASH_OF(value);
 
        zend_hash_clean(&obj->message->hdrs);
        array_copy(headers, &obj->message->hdrs);
@@ -798,7 +797,7 @@ php_http_message_object_t *php_http_message_object_new_ex(zend_class_entry *ce,
 {
        php_http_message_object_t *o;
 
-       o = ecalloc(1, sizeof(php_http_message_object_t) + (ce->default_properties_count - 1) * sizeof(zval));
+       o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
        zend_object_std_init(&o->zo, ce);
        object_properties_init(&o->zo, ce);
 
@@ -1109,13 +1108,16 @@ static PHP_METHOD(HttpMessage, getHeader)
                        if (!header_ce) {
                                RETURN_ZVAL(header, 1, 1);
                        } else if (instanceof_function(header_ce, php_http_header_class_entry)) {
+                               php_http_object_method_t cb;
                                zval argv[2];
 
                                ZVAL_STRINGL(&argv[0], header_str, header_len);
                                ZVAL_COPY(&argv[1], header);
 
                                object_init_ex(return_value, header_ce);
-                               php_http_method_call(return_value, ZEND_STRL("__construct"), 2, argv, NULL);
+                               php_http_object_method_init(&cb, return_value, ZEND_STRL("__construct"));
+                               php_http_object_method_call(&cb, return_value, NULL, 2, argv);
+                               php_http_object_method_dtor(&cb);
 
                                zval_ptr_dtor(&argv[0]);
                                zval_ptr_dtor(&argv[1]);
index 6c677bd6f4af5aafdb5a7a1b4ffb7cc494094554..1c540c77389dba84e58b1040128ae040579cfec8 100644 (file)
@@ -554,7 +554,7 @@ php_http_message_body_object_t *php_http_message_body_object_new_ex(zend_class_e
 {
        php_http_message_body_object_t *o;
 
-       o = ecalloc(1, sizeof(php_http_message_body_object_t) + (ce->default_properties_count - 1) * sizeof(zval));
+       o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
        zend_object_std_init(&o->zo, php_http_message_body_class_entry);
        object_properties_init(&o->zo, ce);
 
index 440a5f3d5dbac9e6ee91522480428421f335a6d6..e40e7b3e79c1edb282da9dd16406b8ddc7e65920 100644 (file)
@@ -517,7 +517,7 @@ php_http_message_parser_object_t *php_http_message_parser_object_new_ex(zend_cla
 {
        php_http_message_parser_object_t *o;
 
-       o = ecalloc(1, sizeof(php_http_message_parser_object_t) + (ce->default_properties_count - 1) * sizeof(zval));
+       o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
        zend_object_std_init(&o->zo, ce);
        object_properties_init(&o->zo, ce);
 
index 9024ee9a47b53a4d757cead7afc3c4c765d48a4b..e42998ea773407c830ac3cb9282a3ade55cd7081 100644 (file)
@@ -23,7 +23,7 @@ php_http_object_t *php_http_object_new_ex(zend_class_entry *ce, void *intern)
 {
        php_http_object_t *o;
 
-       o = ecalloc(1, sizeof(php_http_object_t) + (ce->default_properties_count - 1) * sizeof(zval));
+       o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
        zend_object_std_init(&o->zo, ce);
        object_properties_init(&o->zo, ce);
 
@@ -35,7 +35,6 @@ php_http_object_t *php_http_object_new_ex(zend_class_entry *ce, void *intern)
 
 void php_http_object_free(zend_object *object)
 {
-       php_http_object_t *obj = PHP_HTTP_OBJ(object, NULL);
        zend_object_std_dtor(object);
 }
 
@@ -57,29 +56,64 @@ ZEND_RESULT_CODE php_http_new(void **obj_ptr, zend_class_entry *ce, php_http_new
        return SUCCESS;
 }
 
-ZEND_RESULT_CODE php_http_method_call(zval *object, const char *method_str, size_t method_len, int argc, zval argv[], zval *retval_ptr)
+php_http_object_method_t *php_http_object_method_init(php_http_object_method_t *cb, zval *zobject, const char *method_str, size_t method_len)
+{
+       if (!cb) {
+               cb = ecalloc(1, sizeof(*cb));
+       } else {
+               memset(cb, 0, sizeof(*cb));
+       }
+
+       cb->fci.size = sizeof(cb->fci);
+       ZVAL_STRINGL(&cb->fci.function_name, method_str, method_len);
+       cb->fcc.initialized = 1;
+       cb->fcc.calling_scope = cb->fcc.called_scope = Z_OBJCE_P(zobject);
+       cb->fcc.function_handler = Z_OBJ_HT_P(zobject)->get_method(&Z_OBJ_P(zobject), Z_STR(cb->fci.function_name), NULL);
+
+       return cb;
+}
+
+void php_http_object_method_dtor(php_http_object_method_t *cb)
+{
+       zval_ptr_dtor(&cb->fci.function_name);
+}
+
+void php_http_object_method_free(php_http_object_method_t **cb)
+{
+       if (*cb) {
+               php_http_object_method_dtor(*cb);
+               efree(*cb);
+               *cb = NULL;
+       }
+}
+
+ZEND_RESULT_CODE php_http_object_method_call(php_http_object_method_t *cb, zval *zobject, zval *retval_ptr, int argc, zval *args)
 {
-       zend_fcall_info fci;
-       zval retval;
        ZEND_RESULT_CODE rv;
+       zval retval;
 
        ZVAL_UNDEF(&retval);
-       fci.size = sizeof(fci);
-       fci.object = Z_OBJ_P(object);
-       fci.retval = retval_ptr ? retval_ptr : &retval;
-       fci.param_count = argc;
-       fci.params = argv;
-       fci.no_separation = 1;
-       fci.symbol_table = NULL;
-       fci.function_table = NULL;
-
-       ZVAL_STRINGL(&fci.function_name, method_str, method_len);
-       rv = zend_call_function(&fci, NULL TSRMLS_CC);
-       zval_ptr_dtor(&fci.function_name);
-
-       if (!retval_ptr) {
+       Z_ADDREF_P(zobject);
+       cb->fci.object = Z_OBJ_P(zobject);
+       cb->fcc.object = Z_OBJ_P(zobject);
+
+       cb->fci.retval = retval_ptr ? retval_ptr : &retval;
+
+       cb->fci.param_count = argc;
+       cb->fci.params = args;
+
+       if (cb->fcc.called_scope != Z_OBJCE_P(zobject)) {
+               cb->fcc.called_scope = Z_OBJCE_P(zobject);
+               cb->fcc.function_handler = Z_OBJ_HT_P(zobject)->get_method(&Z_OBJ_P(zobject), Z_STR(cb->fci.function_name), NULL);
+       }
+
+       rv = zend_call_function(&cb->fci, &cb->fcc);
+
+       zval_ptr_dtor(zobject);
+       if (!retval_ptr && !Z_ISUNDEF(retval)) {
                zval_ptr_dtor(&retval);
        }
+
        return rv;
 }
 
index 24e5a181c568983344ca2952447d2e5073e3087b..c6bd17f58617a3a53266c5e3372f7b28a9e254e3 100644 (file)
@@ -24,10 +24,19 @@ php_http_object_t *php_http_object_new_ex(zend_class_entry *ce, void *nothing);
 typedef void *(*php_http_new_t)(zend_class_entry *ce, void *);
 
 ZEND_RESULT_CODE php_http_new(void **obj_ptr, zend_class_entry *ce, php_http_new_t create, zend_class_entry *parent_ce, void *intern_ptr);
-ZEND_RESULT_CODE php_http_method_call(zval *object, const char *method_str, size_t method_len, int argc, zval argv[], zval *retval_ptr);
 
 PHP_MINIT_FUNCTION(http_object);
 
+typedef struct php_http_method {
+       zend_fcall_info fci;
+       zend_fcall_info_cache fcc;
+} php_http_object_method_t;
+
+php_http_object_method_t *php_http_object_method_init(php_http_object_method_t *cb, zval *zobject, const char *method_str, size_t method_len);
+ZEND_RESULT_CODE php_http_object_method_call(php_http_object_method_t *cb, zval *zobject, zval *retval, int argc, zval *args);
+void php_http_object_method_dtor(php_http_object_method_t *cb);
+void php_http_object_method_free(php_http_object_method_t **cb);
+
 #endif
 
 
index c4c0e15e72293067c1fb0b5433da817bcaf1e0ac..048bbeec7633f09b60059ac67c186c44ecdddee7 100644 (file)
@@ -57,7 +57,7 @@ static inline char *localhostname(void)
        return estrndup("localhost", lenof("localhost"));
 }
 
-#define url(buf) ((php_http_url_t *) buf.data)
+#define url(buf) ((php_http_url_t *) (buf).data)
 
 static php_http_url_t *php_http_url_from_env(void)
 {
@@ -125,13 +125,30 @@ static php_http_url_t *php_http_url_from_env(void)
 
 #define url_isset(u,n) \
        ((u)&&(u)->n)
+#define url_append(buf, append) do { \
+       char *_ptr = (buf)->data; \
+       php_http_url_t *_url = (php_http_url_t *) _ptr, _mem = *_url; \
+       append; \
+       /* relocate */ \
+       if (_ptr != (buf)->data) { \
+               ptrdiff_t diff = (buf)->data - _ptr; \
+               _url = (php_http_url_t *) (buf)->data; \
+               if (_mem.scheme)        _url->scheme += diff; \
+               if (_mem.user)          _url->user += diff; \
+               if (_mem.pass)          _url->pass += diff; \
+               if (_mem.host)          _url->host += diff; \
+               if (_mem.path)          _url->path += diff; \
+               if (_mem.query)         _url->query += diff; \
+               if (_mem.fragment)      _url->fragment += diff; \
+       } \
+} while (0)
 #define url_copy(n) do { \
        if (url_isset(new_url, n)) { \
                url(buf)->n = &buf.data[buf.used]; \
-               php_http_buffer_append(&buf, new_url->n, strlen(new_url->n) + 1); \
+               url_append(&buf, php_http_buffer_append(&buf, new_url->n, strlen(new_url->n) + 1)); \
        } else if (url_isset(old_url, n)) { \
                url(buf)->n = &buf.data[buf.used]; \
-               php_http_buffer_append(&buf, old_url->n, strlen(old_url->n) + 1); \
+               url_append(&buf, php_http_buffer_append(&buf, old_url->n, strlen(old_url->n) + 1)); \
        } \
 } while (0)
 
@@ -182,9 +199,9 @@ php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_u
                        
                        url(buf)->path = &buf.data[buf.used];
                        if (path[0] != '/') {
-                               php_http_buffer_append(&buf, "/", 1);
+                               url_append(&buf, php_http_buffer_append(&buf, "/", 1));
                        }
-                       php_http_buffer_append(&buf, path, strlen(path) + 1);
+                       url_append(&buf, php_http_buffer_append(&buf, path, strlen(path) + 1));
                        efree(path);
                } else {
                        const char *path = NULL;
@@ -198,7 +215,7 @@ php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_u
                        if (path) {
                                url(buf)->path = &buf.data[buf.used];
 
-                               php_http_buffer_append(&buf, path, strlen(path) + 1);
+                               url_append(&buf, php_http_buffer_append(&buf, path, strlen(path) + 1));
                        }
 
 
@@ -222,7 +239,7 @@ php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_u
                        php_http_querystring_update(&qarr, NULL, &qstr);
 
                        url(buf)->query = &buf.data[buf.used];
-                       php_http_buffer_append(&buf, Z_STRVAL(qstr), Z_STRLEN(qstr) + 1);
+                       url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL(qstr), Z_STRLEN(qstr) + 1));
 
                        zval_dtor(&qstr);
                        zval_dtor(&qarr);
@@ -294,8 +311,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;
                }
@@ -367,6 +384,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)
 {
        zend_string *zs;
@@ -399,25 +452,25 @@ php_http_url_t *php_http_url_from_struct(HashTable *ht)
        if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("scheme")))) {
                zend_string *zs = zval_get_string(e);
                url(buf)->scheme = &buf.data[buf.used];
-               php_http_buffer_append(&buf, zs->val, zs->len + 1);
+               url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1));
                zend_string_release(zs);
        }
        if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("user")))) {
                zend_string *zs = zval_get_string(e);
                url(buf)->user = &buf.data[buf.used];
-               php_http_buffer_append(&buf, zs->val, zs->len + 1);
+               url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1));
                zend_string_release(zs);
        }
        if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("pass")))) {
                zend_string *zs = zval_get_string(e);
                url(buf)->pass = &buf.data[buf.used];
-               php_http_buffer_append(&buf, zs->val, zs->len + 1);
+               url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1));
                zend_string_release(zs);
        }
        if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("host")))) {
                zend_string *zs = zval_get_string(e);
                url(buf)->host = &buf.data[buf.used];
-               php_http_buffer_append(&buf, zs->val, zs->len + 1);
+               url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1));
                zend_string_release(zs);
        }
        if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("port")))) {
@@ -426,19 +479,19 @@ php_http_url_t *php_http_url_from_struct(HashTable *ht)
        if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("path")))) {
                zend_string *zs = zval_get_string(e);
                url(buf)->path = &buf.data[buf.used];
-               php_http_buffer_append(&buf, zs->val, zs->len + 1);
+               url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1));
                zend_string_release(zs);
        }
        if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("query")))) {
                zend_string *zs = zval_get_string(e);
                url(buf)->query = &buf.data[buf.used];
-               php_http_buffer_append(&buf, zs->val, zs->len + 1);
+               url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1));
                zend_string_release(zs);
        }
        if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("fragment")))) {
                zend_string *zs = zval_get_string(e);
                url(buf)->fragment = &buf.data[buf.used];
-               php_http_buffer_append(&buf, zs->val, zs->len + 1);
+               url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1));
                zend_string_release(zs);
        }
 
@@ -868,7 +921,9 @@ static ZEND_RESULT_CODE parse_hostinfo(struct parse_state *state, const char *pt
                        break;
 
                default:
-                       if (port) {
+                       if (ptr == end) {
+                               break;
+                       } else if (port) {
                                php_error_docref(NULL, E_WARNING,
                                                "Failed to parse port; unexpected byte 0x%02x at pos %u in '%s'",
                                                (unsigned char) *ptr, (unsigned) (ptr - tmp), tmp);
@@ -938,6 +993,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;
@@ -946,7 +1002,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)
@@ -1243,6 +1300,33 @@ php_http_url_t *php_http_url_parse(const char *str, size_t len, unsigned flags)
        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 df35a9e3a690eaab0748736076c99541c7506772..b7402e93cff861a025be126e68b3f72d211291f5 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,15 +58,16 @@ 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);
+PHP_HTTP_API php_http_url_t *php_http_url_parse_authority(const char *str, size_t len, unsigned flags);
 PHP_HTTP_API php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_url_t *new_url, unsigned flags);
 PHP_HTTP_API php_http_url_t *php_http_url_copy(const php_http_url_t *url, zend_bool persistent);
 PHP_HTTP_API php_http_url_t *php_http_url_from_struct(HashTable *ht);
 PHP_HTTP_API php_http_url_t *php_http_url_from_zval(zval *value, unsigned flags);
 PHP_HTTP_API HashTable *php_http_url_to_struct(const php_http_url_t *url, zval *strct);
 PHP_HTTP_API char *php_http_url_to_string(const php_http_url_t *url, char **url_str, size_t *url_len, zend_bool persistent);
+PHP_HTTP_API char *php_http_url_authority_to_string(const php_http_url_t *url, char **url_str, size_t *url_len);
 PHP_HTTP_API void php_http_url_free(php_http_url_t **url);
 
-
 PHP_HTTP_API ZEND_RESULT_CODE php_http_url_encode_hash(HashTable *hash, const char *pre_encoded_str, size_t pre_encoded_len, char **encoded_str, size_t *encoded_len);
 PHP_HTTP_API ZEND_RESULT_CODE php_http_url_encode_hash_ex(HashTable *hash, php_http_buffer_t *qstr, const char *arg_sep_str, size_t arg_sep_len, const char *val_sep_str, size_t val_sep_len, const char *pre_encoded_str, size_t pre_encoded_len);
 
index 0579467fc1e9cb9a79e5f015582c8dbd1c24e382..5e8008dc2aacb3f19e9a80161d804270d3cf34cc 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)
 {
        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);
                                }
                        }
diff --git a/tests/bug69000.phpt b/tests/bug69000.phpt
new file mode 100644 (file)
index 0000000..9b23e47
--- /dev/null
@@ -0,0 +1,17 @@
+--TEST--
+Bug #69000 (http\Url breaks down with very long URL query strings)
+--SKIPIF--
+<?php 
+include "skipif.inc";
+?>
+--FILE--
+<?php 
+echo "Test\n";
+echo new http\Url("http://foo.bar/?".str_repeat("a", 1024));
+?>
+
+===DONE===
+--EXPECT--
+Test
+http://foo.bar/?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+===DONE===
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);
+       }
+}