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
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`
], [
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"
);
$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(
<email>mike@php.net</email>
<active>yes</active>
</lead>
- <date>2015-02-07</date>
+ <date>2015-01-27</date>
<version>
- <release>2.2.2</release>
- <api>2.2.0</api>
+ <release>2.3.0</release>
+ <api>2.3.0</api>
</version>
<stability>
<release>stable</release>
</stability>
<license>BSD, revised</license>
<notes><![CDATA[
++ Added support for HTTP2 if libcurl was built with nghttp2 support.
++ Added http\Client\Curl\HTTP_VERSION_2_0 constant (libcurl >= 7.33.0)
++ Added pinned_publickey SSL request option (libcurl >= 7.39.0)
]]></notes>
<contents>
<dir name="/">
#ifndef PHP_EXT_HTTP_H
#define PHP_EXT_HTTP_H
-#define PHP_PECL_HTTP_VERSION "2.2.2dev"
+#define PHP_PECL_HTTP_VERSION "2.3.0dev"
extern zend_module_entry http_module_entry;
#define phpext_http_ptr &http_module_entry
php_http_client_object_t *o = (php_http_client_object_t *) object;
php_http_client_free(&o->client);
+ php_http_object_method_dtor(&o->notify);
+ php_http_object_method_free(&o->update);
zend_object_std_dtor((zend_object *) o TSRMLS_CC);
efree(o);
}
zval_ptr_dtor(&new_hist);
}
-static STATUS handle_response(void *arg, php_http_client_t *client, php_http_client_enqueue_t *e, php_http_message_t **request, php_http_message_t **response)
+static STATUS handle_response(void *arg, php_http_client_t *client, php_http_client_enqueue_t *e, php_http_message_t **response)
{
zend_bool dequeue = 0;
zval zclient;
php_http_message_set_type(msg, PHP_HTTP_RESPONSE);
if (z_is_true(zend_read_property(php_http_client_class_entry, &zclient, ZEND_STRL("recordHistory"), 0 TSRMLS_CC))) {
- handle_history(&zclient, *request, *response TSRMLS_CC);
+ handle_history(&zclient, e->request, *response TSRMLS_CC);
}
/* hard detach, redirects etc. are in the history */
static void handle_progress(void *arg, php_http_client_t *client, php_http_client_enqueue_t *e, php_http_client_progress_state_t *progress)
{
- zval *zrequest, *zprogress, *retval = NULL, *zclient;
+ zval *zrequest, *zprogress, *zclient, **args[2];
+ php_http_client_object_t *client_obj = arg;
zend_error_handling zeh;
TSRMLS_FETCH_FROM_CTX(client->ts);
MAKE_STD_ZVAL(zclient);
- ZVAL_OBJVAL(zclient, ((php_http_client_object_t *) arg)->zv, 1);
+ ZVAL_OBJVAL(zclient, client_obj->zv, 1);
+
MAKE_STD_ZVAL(zrequest);
ZVAL_OBJVAL(zrequest, ((php_http_message_object_t *) e->opaque)->zv, 1);
+ args[0] = &zrequest;
+
MAKE_STD_ZVAL(zprogress);
object_init(zprogress);
add_property_bool(zprogress, "started", progress->started);
add_property_double(zprogress, "dlnow", progress->dl.now);
add_property_double(zprogress, "ultotal", progress->ul.total);
add_property_double(zprogress, "ulnow", progress->ul.now);
+ args[1] = &zprogress;
+
zend_replace_error_handling(EH_NORMAL, NULL, &zeh TSRMLS_CC);
- zend_call_method_with_2_params(&zclient, NULL, NULL, "notify", &retval, zrequest, zprogress);
+ php_http_object_method_call(&client_obj->notify, zclient, NULL, 2, args TSRMLS_CC);
zend_restore_error_handling(&zeh TSRMLS_CC);
+
zval_ptr_dtor(&zclient);
zval_ptr_dtor(&zrequest);
zval_ptr_dtor(&zprogress);
- if (retval) {
- zval_ptr_dtor(&retval);
- }
}
static void response_dtor(void *data)
php_http_expect(obj->client = php_http_client_init(NULL, driver.client_ops, rf, NULL TSRMLS_CC), runtime, return);
+ php_http_object_method_init(&obj->notify, getThis(), ZEND_STRL("notify") TSRMLS_CC);
+
obj->client->callback.response.func = handle_response;
obj->client->callback.response.arg = obj;
obj->client->callback.progress.func = handle_progress;
RETVAL_ZVAL(getThis(), 1, 0);
}
+struct notify_arg {
+ php_http_object_method_t *cb;
+ zval **args[3];
+ int argc;
+};
+
static int notify(zend_object_iterator *iter, void *puser TSRMLS_DC)
{
- zval **observer = NULL, ***args = puser;
+ zval **observer = NULL;
+ struct notify_arg *arg = puser;
iter->funcs->get_current_data(iter, &observer TSRMLS_CC);
if (observer) {
- return php_http_method_call(*observer, ZEND_STRL("update"), args[2]?3:args[1]?2:args[0]?1:0, args, NULL TSRMLS_CC);
+ return php_http_object_method_call(arg->cb, *observer, NULL, arg->argc, arg->args TSRMLS_CC);
}
return FAILURE;
}
ZEND_END_ARG_INFO();
static PHP_METHOD(HttpClient, notify)
{
- zval *request = NULL, *zprogress = NULL, *observers, **args[3];
+ zval *request = NULL, *zprogress = NULL, *observers;
+ php_http_client_object_t *client_obj;
+ struct notify_arg arg = {NULL};
php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|O!o!", &request, php_http_client_request_class_entry, &zprogress), invalid_arg, return);
+ client_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
if (Z_TYPE_P(observers) != IS_OBJECT) {
return;
}
- Z_ADDREF_P(getThis());
- args[0] = &getThis();
- if (request) {
- Z_ADDREF_P(request);
- }
- args[1] = &request;
- if (zprogress) {
- Z_ADDREF_P(zprogress);
- }
- args[2] = &zprogress;
- spl_iterator_apply(observers, notify, args TSRMLS_CC);
- zval_ptr_dtor(&getThis());
- if (request) {
- zval_ptr_dtor(&request);
- }
- if (zprogress) {
- zval_ptr_dtor(&zprogress);
+ if (client_obj->update) {
+ arg.cb = client_obj->update;
+
+ Z_ADDREF_P(getThis());
+ arg.args[0] = &getThis();
+ arg.argc = 1;
+
+ if (request) {
+ Z_ADDREF_P(request);
+ arg.args[1] = &request;
+ arg.argc += 1;
+ }
+
+ if (zprogress) {
+ Z_ADDREF_P(zprogress);
+ arg.args[2] = &zprogress;
+ arg.argc += 1;
+ }
+
+ spl_iterator_apply(observers, notify, &arg TSRMLS_CC);
+
+ zval_ptr_dtor(&getThis());
+ if (request) {
+ zval_ptr_dtor(&request);
+ }
+ if (zprogress) {
+ zval_ptr_dtor(&zprogress);
+ }
}
RETVAL_ZVAL(getThis(), 1, 0);
static PHP_METHOD(HttpClient, attach)
{
zval *observers, *observer, *retval = NULL;
+ php_http_client_object_t *client_obj;
php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &observer, spl_ce_SplObserver), invalid_arg, return);
+ client_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
if (Z_TYPE_P(observers) != IS_OBJECT) {
return;
}
+ if (!client_obj->update) {
+ client_obj->update = php_http_object_method_init(NULL, observer, ZEND_STRL("update") TSRMLS_CC);
+ }
+
zend_call_method_with_1_params(&observers, NULL, NULL, "attach", &retval, observer);
if (retval) {
zval_ptr_dtor(&retval);
unsigned finished:1;
} php_http_client_progress_state_t;
-typedef STATUS (*php_http_client_response_callback_t)(void *arg, struct php_http_client *client, php_http_client_enqueue_t *e, php_http_message_t **request, php_http_message_t **response);
+typedef STATUS (*php_http_client_response_callback_t)(void *arg, struct php_http_client *client, php_http_client_enqueue_t *e, php_http_message_t **response);
typedef void (*php_http_client_progress_callback_t)(void *arg, struct php_http_client *client, php_http_client_enqueue_t *e, php_http_client_progress_state_t *state);
typedef struct php_http_client {
zend_object_value zv;
php_http_client_t *client;
long iterator;
+ php_http_object_method_t *update;
+ php_http_object_method_t notify;
} php_http_client_object_t;
PHP_HTTP_API php_http_client_t *php_http_client_init(php_http_client_t *h, php_http_client_ops_t *ops, php_resource_factory_t *rf, void *init_arg TSRMLS_DC);
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;
return 0;
}
-static curlioerr php_http_curle_ioctl_callback(CURL *ch, curliocmd cmd, void *ctx)
+static int php_http_curle_seek_callback(void *userdata, curl_off_t offset, int origin)
{
- php_http_message_body_t *body = ctx;
+ php_http_message_body_t *body = userdata;
+ TSRMLS_FETCH_FROM_CTX(body->ts);
- if (cmd != CURLIOCMD_RESTARTREAD) {
- return CURLIOE_UNKNOWNCMD;
+ if (!body) {
+ return 1;
}
-
- if (body) {
- TSRMLS_FETCH_FROM_CTX(body->ts);
-
- if (SUCCESS == php_stream_rewind(php_http_message_body_stream(body))) {
- return CURLIOE_OK;
- }
+ if (0 == php_stream_seek(php_http_message_body_stream(body), offset, origin)) {
+ return 0;
}
-
- return CURLIOE_FAILRESTART;
+ return 2;
}
static int php_http_curle_raw_callback(CURL *ch, curl_infotype type, char *data, size_t length, void *ctx)
{
php_http_client_curl_handler_t *h = ctx;
- unsigned flags = 0;
/* catch progress */
switch (type) {
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 */
return 0;
}
-static int php_http_curle_dummy_callback(char *data, size_t n, size_t l, void *s)
+static int php_http_curle_header_callback(char *data, size_t n, size_t l, void *arg)
+{
+ php_http_client_curl_handler_t *h = arg;
+
+ return php_http_buffer_append(&h->response.headers, data, n * l);
+}
+
+static int php_http_curle_body_callback(char *data, size_t n, size_t l, void *arg)
{
- return n*l;
+ php_http_client_curl_handler_t *h = arg;
+
+ return php_http_message_body_append(h->response.body, data, n*l);
}
static STATUS php_http_curle_get_info(CURL *ch, HashTable *info)
add_assoc_zval_ex(&array, "ssl_engines", sizeof("ssl_engines"), subarray);
curl_slist_free_all(s);
}
- if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_COOKIELIST, &s)) {
- MAKE_STD_ZVAL(subarray);
- array_init(subarray);
- for (p = s; p; p = p->next) {
- if (p->data) {
- add_next_index_string(subarray, p->data, 1);
- }
- }
- add_assoc_zval_ex(&array, "cookies", sizeof("cookies"), subarray);
- curl_slist_free_all(s);
- }
if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_URL, &c)) {
add_assoc_string_ex(&array, "redirect_url", sizeof("redirect_url"), c ? c : "", 1);
}
return handle == ((php_http_client_curl_handler_t *) e->opaque)->handle;
}
+static php_http_message_t *php_http_curlm_responseparser(php_http_client_curl_handler_t *h TSRMLS_DC)
+{
+ php_http_message_t *response;
+ php_http_header_parser_t parser;
+ zval *zh;
+
+ response = php_http_message_init(NULL, 0, h->response.body TSRMLS_CC);
+ php_http_header_parser_init(&parser TSRMLS_CC);
+ php_http_header_parser_parse(&parser, &h->response.headers, PHP_HTTP_HEADER_PARSER_CLEANUP, &response->hdrs, (php_http_info_callback_t) php_http_message_info_callback, (void *) &response);
+ php_http_header_parser_dtor(&parser);
+
+ /* move body to right message */
+ if (response->body != h->response.body) {
+ php_http_message_t *ptr = response;
+
+ while (ptr->parent) {
+ ptr = ptr->parent;
+ }
+ response->body = ptr->body;
+ ptr->body = NULL;
+ }
+ php_http_message_body_addref(h->response.body);
+
+ /* let's update the response headers */
+ if ((zh = php_http_message_header(response, ZEND_STRL("Content-Length"), 1))) {
+ zend_hash_update(&response->hdrs, "X-Original-Content-Length", sizeof("X-Original-Content-Length"), &zh, sizeof(zval *), NULL);
+ }
+ if ((zh = php_http_message_header(response, ZEND_STRL("Transfer-Encoding"), 0))) {
+ zend_hash_update(&response->hdrs, "X-Original-Transfer-Encoding", sizeof("X-Original-Transfer-Encoding"), (void *) &zh, sizeof(zval *), NULL);
+ zend_hash_del(&response->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding"));
+ }
+ if ((zh = php_http_message_header(response, ZEND_STRL("Content-Range"), 0))) {
+ zend_hash_update(&response->hdrs, "X-Original-Content-Range", sizeof("X-Original-Content-Range"), &zh, sizeof(zval *), NULL);
+ zend_hash_del(&response->hdrs, "Content-Range", sizeof("Content-Range"));
+ }
+ if ((zh = php_http_message_header(response, ZEND_STRL("Content-Encoding"), 0))) {
+ zend_hash_update(&response->hdrs, "X-Original-Content-Encoding", sizeof("X-Original-Content-Encoding"), &zh, sizeof(zval *), NULL);
+ zend_hash_del(&response->hdrs, "Content-Encoding", sizeof("Content-Encoding"));
+ }
+ php_http_message_update_headers(response);
+
+ return response;
+}
+
static void php_http_curlm_responsehandler(php_http_client_t *context)
{
int remaining = 0;
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);
static STATUS php_http_curle_option_set_compress(php_http_option_t *opt, zval *val, void *userdata)
{
php_http_client_curl_handler_t *curl = userdata;
+ CURL *ch = curl->handle;
- if (Z_BVAL_P(val)) {
- curl->options.headers = curl_slist_append(curl->options.headers, "Accept-Encoding: gzip;q=1.0,deflate;q=0.5");
+ if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_ACCEPT_ENCODING, Z_BVAL_P(val) ? "" : NULL)) {
+ return FAILURE;
}
return SUCCESS;
}
return SUCCESS;
}
+#if PHP_HTTP_CURL_VERSION(7,37,0)
+static STATUS php_http_curle_option_set_proxyheader(php_http_option_t *opt, zval *val, void *userdata)
+{
+ php_http_client_curl_handler_t *curl = userdata;
+ TSRMLS_FETCH_FROM_CTX(curl->client->ts);
+
+ if (val && Z_TYPE_P(val) != IS_NULL) {
+ php_http_array_hashkey_t header_key = php_http_array_hashkey_init(0);
+ zval **header_val, *header_cpy;
+ HashPosition pos;
+ php_http_buffer_t header;
+
+ php_http_buffer_init(&header);
+ FOREACH_KEYVAL(pos, val, header_key, header_val) {
+ if (header_key.type == HASH_KEY_IS_STRING) {
+ header_cpy = php_http_ztyp(IS_STRING, *header_val);
+ php_http_buffer_appendf(&header, "%s: %s", header_key.str, Z_STRVAL_P(header_cpy));
+ php_http_buffer_fix(&header);
+ curl->options.proxyheaders = curl_slist_append(curl->options.proxyheaders, header.data);
+ php_http_buffer_reset(&header);
+
+ zval_ptr_dtor(&header_cpy);
+ }
+ }
+ php_http_buffer_dtor(&header);
+ }
+ if (CURLE_OK != curl_easy_setopt(curl->handle, CURLOPT_PROXYHEADER, curl->options.proxyheaders)) {
+ return FAILURE;
+ }
+ if (CURLE_OK != curl_easy_setopt(curl->handle, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE)) {
+ curl_easy_setopt(curl->handle, CURLOPT_PROXYHEADER, NULL);
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+#endif
+
#if PHP_HTTP_CURL_VERSION(7,21,3)
static STATUS php_http_curle_option_set_resolve(php_http_option_t *opt, zval *val, void *userdata)
{
}
#endif
+#if PHP_HTTP_CURL_VERSION(7,21,4)
+static STATUS php_http_curle_option_set_ssl_tlsauthtype(php_http_option_t *opt, zval *val, void *userdata)
+{
+ php_http_client_curl_handler_t *curl = userdata;
+ CURL *ch = curl->handle;
+
+ if (val && Z_LVAL_P(val)) {
+ switch (Z_LVAL_P(val)) {
+ case CURL_TLSAUTH_SRP:
+ if (CURLE_OK == curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, PHP_HTTP_CURL_TLSAUTH_SRP)) {
+ return SUCCESS;
+ }
+ /* no break */
+ default:
+ return FAILURE;
+ }
+ }
+ if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, PHP_HTTP_CURL_TLSAUTH_DEF)) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+#endif
+
static void php_http_curle_options_init(php_http_options_t *registry TSRMLS_DC)
{
php_http_option_t *opt;
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;
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;
if ((opt = php_http_option_register(registry, ZEND_STRL("enable_alpn"), CURLOPT_SSL_ENABLE_ALPN, IS_BOOL))) {
ZVAL_BOOL(&opt->defval, 1);
}
+#endif
+#if PHP_HTTP_CURL_VERSION(7,39,0)
+ if ((opt = php_http_option_register(registry, ZEND_STRL("pinned_publickey"), CURLOPT_PINNEDPUBLICKEY, IS_STRING))) {
+ opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+ opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
+ }
+#endif
+#if PHP_HTTP_CURL_VERSION(7,21,4)
+ if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthtype"), CURLOPT_TLSAUTH_TYPE, IS_LONG))) {
+ opt->setter = php_http_curle_option_set_ssl_tlsauthtype;
+ }
+ if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthuser"), CURLOPT_TLSAUTH_USERNAME, IS_STRING))) {
+ opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+ }
+ if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthpass"), CURLOPT_TLSAUTH_PASSWORD, IS_STRING))) {
+ opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+ }
#endif
}
}
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);
handler->rf = rf;
handler->client = h;
handler->handle = handle;
- handler->request.buffer = php_http_buffer_init(NULL);
- handler->request.parser = php_http_message_parser_init(NULL TSRMLS_CC);
- handler->request.message = php_http_message_init(NULL, 0, NULL TSRMLS_CC);
- handler->response.buffer = php_http_buffer_init(NULL);
- handler->response.parser = php_http_message_parser_init(NULL TSRMLS_CC);
- handler->response.message = php_http_message_init(NULL, 0, NULL TSRMLS_CC);
+ handler->response.body = php_http_message_body_init(NULL, NULL TSRMLS_CC);
+ php_http_buffer_init(&handler->response.headers);
php_http_buffer_init(&handler->options.cookies);
php_http_buffer_init(&handler->options.ranges);
zend_hash_init(&handler->options.cache, 0, NULL, ZVAL_PTR_DTOR, 0);
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);
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);
}
}
+ /* 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)) {
* 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;
}
php_resource_factory_handle_dtor(handler->rf, handler->handle TSRMLS_CC);
php_resource_factory_free(&handler->rf);
- php_http_message_parser_free(&handler->request.parser);
- php_http_message_free(&handler->request.message);
- php_http_buffer_free(&handler->request.buffer);
- php_http_message_parser_free(&handler->response.parser);
- php_http_message_free(&handler->response.message);
- php_http_buffer_free(&handler->response.buffer);
+ php_http_message_body_free(&handler->response.body);
+ php_http_buffer_dtor(&handler->response.headers);
php_http_buffer_dtor(&handler->options.ranges);
php_http_buffer_dtor(&handler->options.cookies);
zend_hash_destroy(&handler->options.cache);
*/
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);
/*
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
long status_code;
php_stream *stream;
+ php_http_message_t *request;
unsigned started:1;
unsigned finished:1;
+ unsigned chunked:1;
} php_http_env_response_stream_ctx_t;
static STATUS php_http_env_response_stream_init(php_http_env_response_t *r, void *init_arg)
zend_hash_init(&ctx->header, 0, NULL, ZVAL_PTR_DTOR, 0);
php_http_version_init(&ctx->version, 1, 1 TSRMLS_CC);
ctx->status_code = 200;
+ ctx->chunked = 1;
+ ctx->request = get_request(r->options TSRMLS_CC);
+
+ /* there are some limitations regarding TE:chunked, see https://tools.ietf.org/html/rfc7230#section-3.3.1 */
+ if (ctx->request && ctx->request->http.version.major == 1 && ctx->request->http.version.minor == 0) {
+ ctx->version.minor = 0;
+ }
r->ctx = ctx;
} else {
zval *tmp = php_http_ztyp(IS_STRING, *val);
+ if (ctx->chunked) {
+ /* disable chunked transfer encoding if we've got an explicit content-length */
+ if (!strncasecmp(Z_STRVAL_P(tmp), "Content-Length:", lenof("Content-Length:"))) {
+ ctx->chunked = 0;
+ }
+ }
php_stream_write(ctx->stream, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
php_stream_write_string(ctx->stream, PHP_HTTP_CRLF);
zval_ptr_dtor(&tmp);
}
php_stream_printf(ctx->stream TSRMLS_CC, "HTTP/%u.%u %ld %s" PHP_HTTP_CRLF, ctx->version.major, ctx->version.minor, ctx->status_code, php_http_env_get_response_status_for_code(ctx->status_code));
+
+ /* there are some limitations regarding TE:chunked, see https://tools.ietf.org/html/rfc7230#section-3.3.1 */
+ if (ctx->version.major == 1 && ctx->version.minor == 0) {
+ ctx->chunked = 0;
+ } else if (ctx->status_code == 204 || ctx->status_code/100 == 1) {
+ ctx->chunked = 0;
+ } else if (ctx->request && ctx->status_code/100 == 2 && !strcasecmp(ctx->request->http.info.request.method, "CONNECT")) {
+ ctx->chunked = 0;
+ }
+
php_http_env_response_stream_header(ctx, &ctx->header TSRMLS_CC);
+
+ /* enable chunked transfer encoding */
+ if (ctx->chunked) {
+ php_stream_write_string(ctx->stream, "Transfer-Encoding: chunked" PHP_HTTP_CRLF);
+ }
php_stream_write_string(ctx->stream, PHP_HTTP_CRLF);
+
ctx->started = 1;
+
return SUCCESS;
}
static long php_http_env_response_stream_get_status(php_http_env_response_t *r)
}
}
+ if (stream_ctx->chunked && 0 == php_stream_printf(stream_ctx->stream TSRMLS_CC, "%lx" PHP_HTTP_CRLF, (unsigned long) data_len)) {
+ return FAILURE;
+ }
+
if (data_len != php_stream_write(stream_ctx->stream, data_str, data_len)) {
return FAILURE;
}
+ if (stream_ctx->chunked && 2 != php_stream_write_string(stream_ctx->stream, PHP_HTTP_CRLF)) {
+ return FAILURE;
+ }
+
return SUCCESS;
}
static STATUS php_http_env_response_stream_flush(php_http_env_response_t *r)
}
}
+ 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;
#else
php_end_ob_buffers(1 TSRMLS_CC);
#endif
+
if (zstream) {
php_http_env_response_t *r;
}
/* there must be HTTP/1.x in the line */
- if (!(http = php_http_locate_str(pre_header, end - pre_header, "HTTP/1.", lenof("HTTP/1.")))) {
+ if (!(http = php_http_locate_str(pre_header, end - pre_header, "HTTP/", lenof("HTTP/")))) {
return NULL;
}
info = php_http_info_init(info TSRMLS_CC);
- /* and nothing than SPACE or NUL after HTTP/1.x */
+ /* and nothing than SPACE or NUL after HTTP/X.x */
if (!php_http_version_parse(&info->http.version, http TSRMLS_CC)
- || (http[lenof("HTTP/1.1")] && (!PHP_HTTP_IS_CTYPE(space, http[lenof("HTTP/1.1")])))) {
+ || (http[lenof("HTTP/X.x")] && (!PHP_HTTP_IS_CTYPE(space, http[lenof("HTTP/X.x")])))) {
if (free_info) {
php_http_info_free(&info);
}
/* 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;
}
}
/* is request */
- else if (*(http - 1) == ' ' && (!http[lenof("HTTP/1.x")] || http[lenof("HTTP/1.x")] == '\r' || http[lenof("HTTP/1.x")] == '\n')) {
+ else if (*(http - 1) == ' ' && (!http[lenof("HTTP/X.x")] || http[lenof("HTTP/X.x")] == '\r' || http[lenof("HTTP/X.x")] == '\n')) {
const char *url = strchr(pre_header, ' ');
info->type = PHP_HTTP_REQUEST;
if (url && http > url) {
- PHP_HTTP_INFO(info).request.method = estrndup(pre_header, url - pre_header);
+ size_t url_len = url - pre_header;
+
+ PHP_HTTP_INFO(info).request.method = estrndup(pre_header, url_len);
+
while (' ' == *url) ++url;
while (' ' == *(http-1)) --http;
+
if (http > url) {
- PHP_HTTP_INFO(info).request.url = php_http_url_parse(url, http - url, ~0 TSRMLS_CC);
+ /* CONNECT presents an authority only */
+ if (strcasecmp(PHP_HTTP_INFO(info).request.method, "CONNECT")) {
+ PHP_HTTP_INFO(info).request.url = php_http_url_parse(url, http - url, ~0 TSRMLS_CC);
+ } else {
+ PHP_HTTP_INFO(info).request.url = php_http_url_parse_authority(url, http - url, ~0 TSRMLS_CC);
+ }
} else {
PTR_SET(PHP_HTTP_INFO(info).request.method, NULL);
return NULL;
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);
#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
if (!header_ce) {
RETURN_ZVAL(header, 1, 1);
} else if (instanceof_function(header_ce, php_http_header_class_entry TSRMLS_CC)) {
+ php_http_object_method_t cb;
zval *header_name, **argv[2];
MAKE_STD_ZVAL(header_name);
ZVAL_STRINGL(header_name, header_str, header_len, 1);
- Z_ADDREF_P(header);
argv[0] = &header_name;
argv[1] = &header;
object_init_ex(return_value, header_ce);
- php_http_method_call(return_value, ZEND_STRL("__construct"), 2, argv, NULL TSRMLS_CC);
+ php_http_object_method_init(&cb, return_value, ZEND_STRL("__construct") TSRMLS_CC);
+ php_http_object_method_call(&cb, return_value, NULL, 2, argv TSRMLS_CC);
+ php_http_object_method_dtor(&cb);
zval_ptr_dtor(&header_name);
zval_ptr_dtor(&header);
return SUCCESS;
}
-STATUS php_http_method_call(zval *object, const char *method_str, size_t method_len, int argc, zval **argv[], zval **retval_ptr TSRMLS_DC)
+php_http_object_method_t *php_http_object_method_init(php_http_object_method_t *cb, zval *zobject, const char *method_str, size_t method_len TSRMLS_DC)
+{
+ zval *zfn;
+
+ if (!cb) {
+ cb = ecalloc(1, sizeof(*cb));
+ } else {
+ memset(cb, 0, sizeof(*cb));
+ }
+
+ MAKE_STD_ZVAL(zfn);
+ ZVAL_STRINGL(zfn, method_str, method_len, 1);
+
+ cb->fci.size = sizeof(cb->fci);
+ cb->fci.function_name = zfn;
+ cb->fcc.initialized = 1;
+ cb->fcc.calling_scope = cb->fcc.called_scope = Z_OBJCE_P(zobject);
+ cb->fcc.function_handler = Z_OBJ_HT_P(zobject)->get_method(&zobject, Z_STRVAL_P(cb->fci.function_name), Z_STRLEN_P(cb->fci.function_name), NULL TSRMLS_CC);
+
+ return cb;
+}
+
+void php_http_object_method_dtor(php_http_object_method_t *cb)
+{
+ if (cb->fci.function_name) {
+ zval_ptr_dtor(&cb->fci.function_name);
+ cb->fci.function_name = NULL;
+ }
+}
+
+void php_http_object_method_free(php_http_object_method_t **cb)
+{
+ if (*cb) {
+ php_http_object_method_dtor(*cb);
+ efree(*cb);
+ *cb = NULL;
+ }
+}
+
+STATUS php_http_object_method_call(php_http_object_method_t *cb, zval *zobject, zval **retval_ptr, int argc, zval ***args TSRMLS_DC)
{
- zend_fcall_info fci;
- zval zmethod;
- zval *retval;
STATUS rv;
+ zval *retval = NULL;
- fci.size = sizeof(fci);
- fci.object_ptr = object;
- fci.function_name = &zmethod;
- fci.retval_ptr_ptr = retval_ptr ? retval_ptr : &retval;
- fci.param_count = argc;
- fci.params = argv;
- fci.no_separation = 1;
- fci.symbol_table = NULL;
- fci.function_table = NULL;
+ Z_ADDREF_P(zobject);
+ cb->fci.object_ptr = zobject;
+ cb->fcc.object_ptr = zobject;
- INIT_PZVAL(&zmethod);
- ZVAL_STRINGL(&zmethod, method_str, method_len, 0);
- rv = zend_call_function(&fci, NULL TSRMLS_CC);
+ cb->fci.retval_ptr_ptr = retval_ptr ? retval_ptr : &retval;
+ cb->fci.param_count = argc;
+ cb->fci.params = args;
+
+ if (cb->fcc.called_scope != Z_OBJCE_P(zobject)) {
+ cb->fcc.called_scope = Z_OBJCE_P(zobject);
+ cb->fcc.function_handler = Z_OBJ_HT_P(zobject)->get_method(&zobject, Z_STRVAL_P(cb->fci.function_name), Z_STRLEN_P(cb->fci.function_name), NULL TSRMLS_CC);
+ }
+
+ rv = zend_call_function(&cb->fci, &cb->fcc TSRMLS_CC);
+
+ zval_ptr_dtor(&zobject);
if (!retval_ptr && retval) {
zval_ptr_dtor(&retval);
}
+
return rv;
}
typedef zend_object_value (*php_http_new_t)(zend_class_entry *ce, void *, void ** TSRMLS_DC);
STATUS php_http_new(zend_object_value *ov, zend_class_entry *ce, php_http_new_t create, zend_class_entry *parent_ce, void *intern_ptr, void **obj_ptr TSRMLS_DC);
-STATUS php_http_method_call(zval *object, const char *method_str, size_t method_len, int argc, zval **argv[], zval **retval_ptr TSRMLS_DC);
+
+typedef struct php_http_method {
+ zend_fcall_info fci;
+ zend_fcall_info_cache fcc;
+} php_http_object_method_t;
+
+php_http_object_method_t *php_http_object_method_init(php_http_object_method_t *cb, zval *zobject, const char *method_str, size_t method_len TSRMLS_DC);
+STATUS php_http_object_method_call(php_http_object_method_t *cb, zval *zobject, zval **retval, int argc, zval ***args TSRMLS_DC);
+void php_http_object_method_dtor(php_http_object_method_t *cb);
+void php_http_object_method_free(php_http_object_method_t **cb);
#endif
}
/* 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;
}
return buf.data;
}
+char *php_http_url_authority_to_string(const php_http_url_t *url, char **url_str, size_t *url_len)
+{
+ php_http_buffer_t buf;
+
+ php_http_buffer_init(&buf);
+
+ if (url->user && *url->user) {
+ php_http_buffer_appendl(&buf, url->user);
+ if (url->pass && *url->pass) {
+ php_http_buffer_appends(&buf, ":");
+ php_http_buffer_appendl(&buf, url->pass);
+ }
+ php_http_buffer_appends(&buf, "@");
+ }
+
+ if (url->host && *url->host) {
+ php_http_buffer_appendl(&buf, url->host);
+ if (url->port) {
+ php_http_buffer_appendf(&buf, ":%hu", url->port);
+ }
+ }
+
+ php_http_buffer_shrink(&buf);
+ php_http_buffer_fix(&buf);
+
+ if (url_len) {
+ *url_len = buf.used;
+ }
+
+ if (url_str) {
+ *url_str = buf.data;
+ }
+
+ return buf.data;
+}
+
php_http_url_t *php_http_url_from_zval(zval *value, unsigned flags TSRMLS_DC)
{
zval *zcpy;
break;
default:
- if (port) {
+ if (ptr == end) {
+ break;
+ } else if (port) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Failed to parse port; unexpected byte 0x%02x at pos %u in '%s'",
(unsigned char) *ptr, (unsigned) (ptr - tmp), tmp);
case '?':
case '#':
case '\0':
+ EOD:
/* host delimiter */
if (tmp != state->ptr && SUCCESS != parse_hostinfo(state, tmp)) {
return NULL;
}
} while (++state->ptr <= state->end);
- return NULL;
+ --state->ptr;
+ goto EOD;
}
static const char *parse_path(struct parse_state *state)
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)
#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
} php_http_url_t;
PHP_HTTP_API php_http_url_t *php_http_url_parse(const char *str, size_t len, unsigned flags TSRMLS_DC);
+PHP_HTTP_API php_http_url_t *php_http_url_parse_authority(const char *str, size_t len, unsigned flags TSRMLS_DC);
/* deprecated */
PHP_HTTP_API void php_http_url(int flags, const php_url *old_url, const php_url *new_url, php_url **url_ptr, char **url_str, size_t *url_len TSRMLS_DC);
/* use this instead */
PHP_HTTP_API php_http_url_t *php_http_url_from_zval(zval *value, unsigned flags TSRMLS_DC);
PHP_HTTP_API HashTable *php_http_url_to_struct(const php_http_url_t *url, zval *strct TSRMLS_DC);
PHP_HTTP_API char *php_http_url_to_string(const php_http_url_t *url, char **url_str, size_t *url_len, zend_bool persistent);
+PHP_HTTP_API char *php_http_url_authority_to_string(const php_http_url_t *url, char **url_str, size_t *url_len);
PHP_HTTP_API void php_http_url_free(php_http_url_t **url);
-
PHP_HTTP_API STATUS php_http_url_encode_hash(HashTable *hash, const char *pre_encoded_str, size_t pre_encoded_len, char **encoded_str, size_t *encoded_len TSRMLS_DC);
PHP_HTTP_API STATUS php_http_url_encode_hash_ex(HashTable *hash, php_http_buffer_t *qstr, const char *arg_sep_str, size_t arg_sep_len, const char *val_sep_str, size_t val_sep_len, const char *pre_encoded_str, size_t pre_encoded_len TSRMLS_DC);
php_http_version_t *php_http_version_parse(php_http_version_t *v, const char *str TSRMLS_DC)
{
long major, minor;
- char separator = 0, *stop = NULL;
+ char separator = 0;
register const char *ptr = str;
switch (*ptr) {
++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);
}
}
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
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
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
$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()
);
--- /dev/null
+--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
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
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
rewind($f);
var_dump(stream_get_contents($f));
+?>
--EXPECTREGEX--
string\(\d+\) "HTTP\/1\.1 200 OK
Accept-Ranges: bytes
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
+
+"
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;
rewind($f);
var_dump(stream_get_contents($f));
?>
+%c
+0%c
+%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
+"
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
"
HTTP/1.1 200 OK
Accept-Ranges: bytes
ETag: "fc8305a1"
+Transfer-Encoding: chunked
+9
foo: bar
+
+0
+
--- /dev/null
+--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===
Set-Cookie: baz=1;
Set-Cookie: 123=1;
ETag: ""
+Transfer-Encoding: chunked
+
+0
--- /dev/null
+<?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
--- /dev/null
+--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===
--- /dev/null
+--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===
}
}
-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);
+ }
+}