From: Michael Wallner Date: Sun, 8 Feb 2015 15:23:13 +0000 (+0100) Subject: Merge branch 'master' into phpng X-Git-Tag: RELEASE_3_0_0_RC1~64 X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=commitdiff_plain;h=be9585b914061f84c5d5939673d29ecc048ab1d7;hp=e22fc5f78e8cfc1101c4548de0568add46a8694a Merge branch 'master' into phpng 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 --- diff --git a/config9.m4 b/config9.m4 index 9b88242..46f70f9 100644 --- a/config9.m4 +++ b/config9.m4 @@ -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 + 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 + 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" diff --git a/gen_curlinfo.php b/gen_curlinfo.php index 66fd87b..2e2100f 100755 --- a/gen_curlinfo.php +++ b/gen_curlinfo.php @@ -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( diff --git a/php_http_buffer.c b/php_http_buffer.c index 059db06..b84bcd0 100644 --- a/php_http_buffer.c +++ b/php_http_buffer.c @@ -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; diff --git a/php_http_client.c b/php_http_client.c index 66afb7c..4e55356 100644 --- a/php_http_client.c +++ b/php_http_client.c @@ -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); diff --git a/php_http_client.h b/php_http_client.h index 5067f6c..41671b9 100644 --- a/php_http_client.h +++ b/php_http_client.h @@ -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; diff --git a/php_http_client_curl.c b/php_http_client_curl.c index fc06b38..05db912 100644 --- a/php_http_client_curl.c +++ b/php_http_client_curl.c @@ -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 diff --git a/php_http_cookie.c b/php_http_cookie.c index 706fc10..ff67b1c 100644 --- a/php_http_cookie.c +++ b/php_http_cookie.c @@ -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; diff --git a/php_http_encoding.c b/php_http_encoding.c index 1ef355e..ffa10b3 100644 --- a/php_http_encoding.c +++ b/php_http_encoding.c @@ -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); diff --git a/php_http_env_response.c b/php_http_env_response.c index 80043a2..7c76c08 100644 --- a/php_http_env_response.c +++ b/php_http_env_response.c @@ -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; diff --git a/php_http_info.c b/php_http_info.c index f0fe7ad..66007bf 100644 --- a/php_http_info.c +++ b/php_http_info.c @@ -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); diff --git a/php_http_info.h b/php_http_info.h index 579f573..b771c5c 100644 --- a/php_http_info.h +++ b/php_http_info.h @@ -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 diff --git a/php_http_message.c b/php_http_message.c index 1456950..ae7ca6e 100644 --- a/php_http_message.c +++ b/php_http_message.c @@ -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]); diff --git a/php_http_message_body.c b/php_http_message_body.c index 6c677bd..1c540c7 100644 --- a/php_http_message_body.c +++ b/php_http_message_body.c @@ -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); diff --git a/php_http_message_parser.c b/php_http_message_parser.c index 440a5f3..e40e7b3 100644 --- a/php_http_message_parser.c +++ b/php_http_message_parser.c @@ -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); diff --git a/php_http_object.c b/php_http_object.c index 9024ee9..e42998e 100644 --- a/php_http_object.c +++ b/php_http_object.c @@ -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; } diff --git a/php_http_object.h b/php_http_object.h index 24e5a18..c6bd17f 100644 --- a/php_http_object.h +++ b/php_http_object.h @@ -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 diff --git a/php_http_url.c b/php_http_url.c index c4c0e15..048bbee 100644 --- a/php_http_url.c +++ b/php_http_url.c @@ -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) diff --git a/php_http_url.h b/php_http_url.h index df35a9e..b7402e9 100644 --- a/php_http_url.h +++ b/php_http_url.h @@ -15,6 +15,7 @@ #include +/* 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); diff --git a/php_http_version.c b/php_http_version.c index 0579467..5e8008d 100644 --- a/php_http_version.c +++ b/php_http_version.c @@ -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 index 0000000..9b23e47 --- /dev/null +++ b/tests/bug69000.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #69000 (http\Url breaks down with very long URL query strings) +--SKIPIF-- + +--FILE-- + + +===DONE=== +--EXPECT-- +Test +http://foo.bar/?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +===DONE=== diff --git a/tests/client011.phpt b/tests/client011.phpt index 17c60ed..284edb2 100644 --- a/tests/client011.phpt +++ b/tests/client011.phpt @@ -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 diff --git a/tests/client012.phpt b/tests/client012.phpt index ad6c2eb..e4c188c 100644 --- a/tests/client012.phpt +++ b/tests/client012.phpt @@ -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 index 0000000..ea54146 --- /dev/null +++ b/tests/client017.phpt @@ -0,0 +1,33 @@ +--TEST-- +client request gzip +--SKIPIF-- + +--FILE-- +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 diff --git a/tests/envresponse006.phpt b/tests/envresponse006.phpt index 76141c1..248b999 100644 --- a/tests/envresponse006.phpt +++ b/tests/envresponse006.phpt @@ -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 diff --git a/tests/envresponse007.phpt b/tests/envresponse007.phpt index 5495fe2..d41be58 100644 --- a/tests/envresponse007.phpt +++ b/tests/envresponse007.phpt @@ -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 diff --git a/tests/envresponse008.phpt b/tests/envresponse008.phpt index 379ab57..acbda89 100644 --- a/tests/envresponse008.phpt +++ b/tests/envresponse008.phpt @@ -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 + +" diff --git a/tests/envresponse009.phpt b/tests/envresponse009.phpt index 2a337e4..1ca5fad 100644 --- a/tests/envresponse009.phpt +++ b/tests/envresponse009.phpt @@ -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 send($f); rewind($f); var_dump(stream_get_contents($f)); ?> +%c +0%c +%c " diff --git a/tests/envresponse010.phpt b/tests/envresponse010.phpt index d365497..9b040d5 100644 --- a/tests/envresponse010.phpt +++ b/tests/envresponse010.phpt @@ -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 +" diff --git a/tests/envresponse014.phpt b/tests/envresponse014.phpt index ef85e2c..6b71aac 100644 --- a/tests/envresponse014.phpt +++ b/tests/envresponse014.phpt @@ -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 " diff --git a/tests/envresponse015.phpt b/tests/envresponse015.phpt index abad2bb..1e8b15e 100644 --- a/tests/envresponse015.phpt +++ b/tests/envresponse015.phpt @@ -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 index 0000000..ffa40cb --- /dev/null +++ b/tests/envresponse017.phpt @@ -0,0 +1,28 @@ +--TEST-- +env response stream: no chunked transfer encoding for CONNECTs +--SKIPIF-- + +--FILE-- +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=== diff --git a/tests/envresponsecookie001.phpt b/tests/envresponsecookie001.phpt index 1fe11a0..c0e93ed 100644 --- a/tests/envresponsecookie001.phpt +++ b/tests/envresponsecookie001.phpt @@ -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 index 0000000..89d31f4 --- /dev/null +++ b/tests/proxy.inc @@ -0,0 +1,26 @@ +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 index 0000000..c8a2e63 --- /dev/null +++ b/tests/proxy001.phpt @@ -0,0 +1,48 @@ +--TEST-- +proxy - send proxy headers for a proxy request +--SKIPIF-- + +--FILE-- +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 index 0000000..86bee61 --- /dev/null +++ b/tests/proxy002.phpt @@ -0,0 +1,44 @@ +--TEST-- +proxy - don't send proxy headers for a standard request +--SKIPIF-- + +--FILE-- +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=== diff --git a/tests/skipif.inc b/tests/skipif.inc index 28d9a13..4b2627a 100644 --- a/tests/skipif.inc +++ b/tests/skipif.inc @@ -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); + } +}