modules/
pecl_http-*.tgz
*.lo
+*.o
run-tests.php
tests/*.diff
tests/*.exp
tests/*.php
tests/*.sh
lcov_data
+*~
-* let http_info.request.url be a php_url
* let the message body be a simple query string unless files are added
* php_http_message_serialize reverses the chain twice; remove that
* CURLOPT_PROXY_HEADER and CURLOPT_HEADEROPT
\ No newline at end of file
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"
php_http_options.c \
php_http_params.c \
php_http_querystring.c \
- php_http_strlist.c \
php_http_url.c \
php_http_version.c \
"
php_http_options.h \
php_http_params.h \
php_http_querystring.h \
- php_http_strlist.h \
+ php_http_response_codes.h \
php_http_url.h \
+ php_http_utf8.h \
php_http_version.h \
"
PHP_INSTALL_HEADERS(ext/http, $PHP_HTTP_HEADERS)
);
$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>2014-08-19</date>
+ <date>2015-02-19</date>
<version>
- <release>2.2.0dev</release>
- <api>2.2.0</api>
+ <release>2.3.0dev</release>
+ <api>2.3.0</api>
</version>
<stability>
<release>beta</release>
- <api>stable</api>
+ <api>beta</api>
</stability>
<license>BSD, revised</license>
<notes><![CDATA[
-- var_dump(http\Message) no longer automatically creates an empty body
-+ Added http\Message\Parser class
-+ Made http\Client::once() and http\Client::wait() available when using events
-+ Added http\Url::parse() method
-+ Added http\Url::PARSE_MBLOC, http\Url::PARSE_MBUTF8, http\Url::PARSE_TOIDN and http\Url::PARSE_TOPCT constants
++ Preliminiary HTTP2 support for http\Client (libcurl with nghttp2 support)
++ Improved performance of HTTP info parser (request/response line)
++ Improved performance of updating client observers
++ Improved performance of http\Env\Response output to streams
++ Improved the error messages of the header parser
++ Added http\Header\Parser class
++ Added http\Client::configure() method accepting an array with the following options for libcurl:
+ . maxconnects (int, size of the connection cache)
+ . max_host_connections (int, max number of connections to a single host, libcurl >= 7.30.0)
+ . max_pipeline_length (int, max number of requests in a pipeline, libcurl >= 7.30.0)
+ . max_total_connections (int, max number of simultaneous open connections of this client, libcurl >= 7.30.0)
+ . pipelining (bool, whether to enable HTTP/1.1 pipelining)
+ . chunk_length_penalty_size (int, chunk length threshold for pipelining, libcurl >= 7.30.0)
+ . content_length_penalty_size (int, size threshold for pipelining, libcurl >= 7.30.0)
+ . pipelining_server_bl (array, list of server software names to blacklist for pipelining, libcurl >= 7.30.0)
+ . pipelining_site_bl (array, list of server host names to blacklist for pipelining, libcurl >= 7.30.0)
+ . use_eventloop (bool, whether to use libevent, libcurl+libevent)
++ Added http\Client::getAvailableOptions() and http\Client::getAvailableConfiguration() methods
++ 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 http\Client\Curl\TLS_AUTH_SRP constant (libcurl >= 7.21.4)
++ Added pinned_publickey SSL request option (libcurl >= 7.39.0)
++ Added tlsauthtype, tlsauthuser and tlsauthpass SSL request option (libcurl >= 7.21.4)
++ Added verifystatus (a.k.a OCSP) SSL request option (libcurl >= 7.41.0)
++ Added proxyheader request option (libcurl >= 7.37.0)
++ Added unix_socket_path request option (libcurl >= 7.40.0)
+* Fixed compress request option
+* Fixed parsing authorities of CONNECT messages
+* Fixed parsing Content-Range messages
+* Fixed http\Env\Response to default to chunked encoding over streams
+* Fixed superfluous output of Content-Length:0 headers
+* Fixed persistent easy handles to be only created for persistent multi handles
+* Fixed the header parser to accept not-yet-complete header lines
+* Fixed http\Message::toStream() crash in ZTS mode
+* Fixed the message stream parser to handle intermediary data bigger than 4k
+* Fixed the message stream parser to handle single header lines without EOL
+* Fixed http\Message\Body to not generate stat based etags for temporary streams
+- Deprecated http\Client::enablePipelining(), use http\Client::configure(["pipelining" => true]) instead
+- Deprecated http\Client::enableEvents(), use http\Client::configure(["use_eventloop" => true]) instead
+- Removed the cookies entry from the transfer info, wich was very slow and generated a Netscape formatted list of cookies
+- Changed the header parser to reject illegal characters
]]></notes>
<contents>
<dir name="/">
<file role="src" name="php_http_params.h"/>
<file role="src" name="php_http_querystring.c"/>
<file role="src" name="php_http_querystring.h"/>
- <file role="src" name="php_http_strlist.c"/>
- <file role="src" name="php_http_strlist.h"/>
+ <file role="src" name="php_http_response_codes.h"/>
<file role="src" name="php_http_url.c"/>
<file role="src" name="php_http_url.h"/>
<file role="src" name="php_http_utf8.h"/>
<dir name="tests">
<file role="test" name="skipif.inc"/>
- <dir name="data">
- <file role="test" name="message_r_multipart_put.txt"/>
- <file role="test" name="message_rr_empty.txt"/>
- <file role="test" name="message_rr_empty_chunked.txt"/>
- <file role="test" name="message_rr_empty_gzip.txt"/>
- <file role="test" name="message_rr_helloworld_chunked.txt"/>
- <file role="test" name="urls.txt"/>
- </dir>
+ <dir name="data">
+ <file role="test" name="message_r_content_range.txt"/>
+ <file role="test" name="message_r_multipart_put.txt"/>
+ <file role="test" name="message_rr_empty.txt"/>
+ <file role="test" name="message_rr_empty_chunked.txt"/>
+ <file role="test" name="message_rr_empty_gzip.txt"/>
+ <file role="test" name="message_rr_helloworld_chunked.txt"/>
+ <file role="test" name="urls.txt"/>
+ </dir>
+ <dir name="helper">
+ <file role="test" name="cookie.inc"/>
+ <file role="test" name="http2.crt"/>
+ <file role="test" name="http2.key"/>
+ <file role="test" name="pipeline.inc"/>
+ <file role="test" name="proxy.inc"/>
+ <file role="test" name="server.inc"/>
+ <dir name="html">
+ <file role="test" name="index.html"/>
+ </dir>
+ </dir>
<file role="test" name="bug61444.phpt"/>
<file role="test" name="bug66388.phpt"/>
+ <file role="test" name="bug66891.phpt"/>
<file role="test" name="bug67932.phpt"/>
+ <file role="test" name="bug69000.phpt"/>
<file role="test" name="client001.phpt"/>
<file role="test" name="client002.phpt"/>
<file role="test" name="client003.phpt"/>
<file role="test" name="client014.phpt"/>
<file role="test" name="client015.phpt"/>
<file role="test" name="client016.phpt"/>
+ <file role="test" name="client017.phpt"/>
+ <file role="test" name="client018.phpt"/>
+ <file role="test" name="client019.phpt"/>
+ <file role="test" name="client020.phpt"/>
+ <file role="test" name="client021.phpt"/>
+ <file role="test" name="client022.phpt"/>
+ <file role="test" name="client023.phpt"/>
+ <file role="test" name="client024.phpt"/>
+ <file role="test" name="client025.phpt"/>
+ <file role="test" name="client026.phpt"/>
<file role="test" name="clientrequest001.phpt"/>
<file role="test" name="clientrequest002.phpt"/>
<file role="test" name="clientrequest003.phpt"/>
<file role="test" name="envrequestbody001.phpt"/>
<file role="test" name="envrequestbody002.phpt"/>
<file role="test" name="envrequestbody003.phpt"/>
+ <file role="test" name="envrequestcookie001.phpt"/>
<file role="test" name="envrequestfiles001.phpt"/>
<file role="test" name="envrequestfiles002.phpt"/>
<file role="test" name="envrequestform.phpt"/>
<file role="test" name="envresponse014.phpt"/>
<file role="test" name="envresponse015.phpt"/>
<file role="test" name="envresponse016.phpt"/>
+ <file role="test" name="envresponse017.phpt"/>
+ <file role="test" name="envresponse018.phpt"/>
<file role="test" name="envresponsebody001.phpt"/>
<file role="test" name="envresponsebody002.phpt"/>
<file role="test" name="envresponsecodes.phpt"/>
+ <file role="test" name="envresponsecookie001.phpt"/>
<file role="test" name="envresponseheader001.phpt"/>
<file role="test" name="envresponseranges001.phpt"/>
<file role="test" name="etag001.phpt"/>
<file role="test" name="header007.phpt"/>
<file role="test" name="header008.phpt"/>
<file role="test" name="header009.phpt"/>
- <file role="test" name="info_001.phpt"/>
- <file role="test" name="info.phpt"/>
+ <file role="test" name="headerparser001.phpt"/>
+ <file role="test" name="headerparser002.phpt"/>
+ <file role="test" name="headerparser003.phpt"/>
+ <file role="test" name="info001.phpt"/>
+ <file role="test" name="info002.phpt"/>
<file role="test" name="message001.phpt"/>
<file role="test" name="message002.phpt"/>
<file role="test" name="message003.phpt"/>
<file role="test" name="message013.phpt"/>
<file role="test" name="message014.phpt"/>
<file role="test" name="message015.phpt"/>
+ <file role="test" name="message016.phpt"/>
<file role="test" name="messagebody001.phpt"/>
<file role="test" name="messagebody002.phpt"/>
<file role="test" name="messagebody003.phpt"/>
<file role="test" name="params013.phpt"/>
<file role="test" name="params014.phpt"/>
<file role="test" name="params015.phpt"/>
+ <file role="test" name="phpinfo.phpt"/>
<file role="test" name="propertyproxy001.phpt"/>
<file role="test" name="querystring001.phpt"/>
<file role="test" name="querystring002.phpt"/>
<file role="test" name="urlparser008.phpt"/>
<file role="test" name="urlparser009.phpt"/>
<file role="test" name="urlparser010.phpt"/>
+ <file role="test" name="urlparser011.phpt"/>
<file role="test" name="version001.phpt"/>
</dir>
</dir>
|| SUCCESS != PHP_MINIT_CALL(http_encoding)
|| SUCCESS != PHP_MINIT_CALL(http_filter)
|| SUCCESS != PHP_MINIT_CALL(http_header)
+ || SUCCESS != PHP_MINIT_CALL(http_header_parser)
|| SUCCESS != PHP_MINIT_CALL(http_message)
|| SUCCESS != PHP_MINIT_CALL(http_message_parser)
|| SUCCESS != PHP_MINIT_CALL(http_message_body)
#ifndef PHP_EXT_HTTP_H
#define PHP_EXT_HTTP_H
-#define PHP_PECL_HTTP_VERSION "2.2.0dev"
+#define PHP_PECL_HTTP_VERSION "2.3.0dev"
extern zend_module_entry http_module_entry;
#define phpext_http_ptr &http_module_entry
#include "php_http.h"
#include "php_http_buffer.h"
-#include "php_http_strlist.h"
#include "php_http_misc.h"
#include "php_http_options.h"
{
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;
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;
while ((got = php_http_buffer_chunk_buffer(s, data, data_len, &chunk, chunk_len))) {
if (PHP_HTTP_BUFFER_PASS0 == passout(opaque, chunk, got TSRMLS_CC)) {
- STR_SET(chunk, NULL);
+ PTR_SET(chunk, NULL);
return PHP_HTTP_BUFFER_PASS0;
}
++passed;
}
data = NULL;
data_len = 0;
- STR_SET(chunk, NULL);
+ PTR_SET(chunk, NULL);
}
- STR_FREE(chunk);
+ PTR_FREE(chunk);
return passed;
}
#define PHP_HTTP_BUFFER_NOMEM PHP_HTTP_BUFFER_ERROR
#define PHP_HTTP_BUFFER_PASS0 PHP_HTTP_BUFFER_ERROR
-#ifndef STR_FREE
-# define STR_FREE(STR) \
+#ifndef PTR_FREE
+# define PTR_FREE(PTR) \
{ \
- if (STR) { \
- efree(STR); \
+ if (PTR) { \
+ efree(PTR); \
} \
}
#endif
-#ifndef STR_SET
-# define STR_SET(STR, SET) \
+#ifndef PTR_SET
+# define PTR_SET(PTR, SET) \
{ \
- STR_FREE(STR); \
- STR = SET; \
+ PTR_FREE(PTR); \
+ PTR = SET; \
}
#endif
#ifndef TSRMLS_D
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;
}
}
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_configure, 0, 0, 1)
+ ZEND_ARG_ARRAY_INFO(0, settings, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, configure)
+{
+ HashTable *settings = NULL;
+ php_http_client_object_t *obj;
+
+ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|H!", &settings), invalid_arg, return);
+ obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ php_http_expect(SUCCESS == php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_CONFIGURATION, settings), unexpected_val, return);
+
+ RETVAL_ZVAL(getThis(), 1, 0);
+}
+
ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_enablePipelining, 0, 0, 0)
ZEND_ARG_INFO(0, enable)
ZEND_END_ARG_INFO();
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);
}
}
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getAvailableOptions, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, getAvailableOptions)
+{
+ if (SUCCESS == zend_parse_parameters_none()) {
+ php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ array_init(return_value);
+ php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_AVAILABLE_OPTIONS, NULL, &Z_ARRVAL_P(return_value));
+ }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getAvailableConfiguration, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, getAvailableConfiguration)
+{
+ if (SUCCESS == zend_parse_parameters_none()) {
+ php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ array_init(return_value);
+ php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_AVAILABLE_CONFIGURATION, NULL, &Z_ARRVAL_P(return_value));
+ }
+}
+
static zend_function_entry php_http_client_methods[] = {
PHP_ME(HttpClient, __construct, ai_HttpClient_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
PHP_ME(HttpClient, reset, ai_HttpClient_reset, ZEND_ACC_PUBLIC)
PHP_ME(HttpClient, wait, ai_HttpClient_wait, ZEND_ACC_PUBLIC)
PHP_ME(HttpClient, getResponse, ai_HttpClient_getResponse, ZEND_ACC_PUBLIC)
PHP_ME(HttpClient, getHistory, ai_HttpClient_getHistory, ZEND_ACC_PUBLIC)
- PHP_ME(HttpClient, enablePipelining, ai_HttpClient_enablePipelining, ZEND_ACC_PUBLIC)
- PHP_ME(HttpClient, enableEvents, ai_HttpClient_enableEvents, ZEND_ACC_PUBLIC)
+ PHP_ME(HttpClient, configure, ai_HttpClient_configure, ZEND_ACC_PUBLIC)
+ PHP_ME(HttpClient, enablePipelining, ai_HttpClient_enablePipelining, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+ PHP_ME(HttpClient, enableEvents, ai_HttpClient_enableEvents, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
PHP_ME(HttpClient, notify, ai_HttpClient_notify, ZEND_ACC_PUBLIC)
PHP_ME(HttpClient, attach, ai_HttpClient_attach, ZEND_ACC_PUBLIC)
PHP_ME(HttpClient, detach, ai_HttpClient_detach, ZEND_ACC_PUBLIC)
PHP_ME(HttpClient, addCookies, ai_HttpClient_addCookies, ZEND_ACC_PUBLIC)
PHP_ME(HttpClient, getCookies, ai_HttpClient_getCookies, ZEND_ACC_PUBLIC)
PHP_ME(HttpClient, getAvailableDrivers, ai_HttpClient_getAvailableDrivers, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ PHP_ME(HttpClient, getAvailableOptions, ai_HttpClient_getAvailableOptions, ZEND_ACC_PUBLIC)
+ PHP_ME(HttpClient, getAvailableConfiguration, ai_HttpClient_getAvailableConfiguration, ZEND_ACC_PUBLIC)
EMPTY_FUNCTION_ENTRY
};
typedef enum php_http_client_setopt_opt {
PHP_HTTP_CLIENT_OPT_ENABLE_PIPELINING,
PHP_HTTP_CLIENT_OPT_USE_EVENTS,
+ PHP_HTTP_CLIENT_OPT_CONFIGURATION,
} php_http_client_setopt_opt_t;
typedef enum php_http_client_getopt_opt {
- PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, /* php_http_client_progress_state_t** */
- PHP_HTTP_CLIENT_OPT_TRANSFER_INFO, /* HashTable* */
+ PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, /* php_http_client_enqueue_t*, php_http_client_progress_state_t** */
+ PHP_HTTP_CLIENT_OPT_TRANSFER_INFO, /* php_http_client_enqueue_t*, HashTable* */
+ PHP_HTTP_CLIENT_OPT_AVAILABLE_OPTIONS, /* NULL, HashTable* */
+ PHP_HTTP_CLIENT_OPT_AVAILABLE_CONFIGURATION,/* NULL, HashTable */
} php_http_client_getopt_opt_t;
typedef struct php_http_client_enqueue {
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);
/* curl options */
-static php_http_options_t php_http_curle_options;
+static php_http_options_t php_http_curle_options, php_http_curlm_options;
#define PHP_HTTP_CURLE_OPTION_CHECK_STRLEN 0x0001
#define PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR 0x0002
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 !PHP_HTTP_CURL_VERSION(7,21,6)
+# define CURLOPT_ACCEPT_ENCODING CURLOPT_ENCODING
+#endif
+ 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) && defined(PHP_HTTP_CURL_TLSAUTH_SRP)
+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) && 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
}
}
return rv;
}
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+static STATUS php_http_curlm_option_set_pipelining_bl(php_http_option_t *opt, zval *value, void *userdata)
+{
+ php_http_client_t *client = userdata;
+ php_http_client_curl_t *curl = client->ctx;
+ CURLM *ch = curl->handle;
+ HashTable tmp_ht;
+ char **bl = NULL;
+ TSRMLS_FETCH_FROM_CTX(client->ts);
+
+ /* array of char *, ending with a NULL */
+ if (value && Z_TYPE_P(value) != IS_NULL) {
+ zval **entry;
+ HashPosition pos;
+ HashTable *ht = HASH_OF(value);
+ int c = zend_hash_num_elements(ht);
+ char **ptr = ecalloc(c + 1, sizeof(char *));
+
+ bl = ptr;
+
+ zend_hash_init(&tmp_ht, c, NULL, ZVAL_PTR_DTOR, 0);
+ array_join(ht, &tmp_ht, 0, ARRAY_JOIN_STRINGIFY);
+
+ FOREACH_HASH_VAL(pos, &tmp_ht, entry) {
+ *ptr++ = Z_STRVAL_PP(entry);
+ }
+ }
+
+ if (CURLM_OK != curl_multi_setopt(ch, opt->option, bl)) {
+ if (bl) {
+ efree(bl);
+ zend_hash_destroy(&tmp_ht);
+ }
+ return FAILURE;
+ }
+
+ if (bl) {
+ efree(bl);
+ zend_hash_destroy(&tmp_ht);
+ }
+ return SUCCESS;
+}
+#endif
+
+#if PHP_HTTP_HAVE_EVENT
+static inline STATUS php_http_curlm_use_eventloop(php_http_client_t *h, zend_bool enable)
+{
+ php_http_client_curl_t *curl = h->ctx;
+
+ if ((curl->useevents = enable)) {
+ if (!curl->evbase) {
+ curl->evbase = event_base_new();
+ }
+ if (!curl->timeout) {
+ curl->timeout = ecalloc(1, sizeof(struct event));
+ }
+ curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, h);
+ curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, php_http_curlm_socket_callback);
+ curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, h);
+ curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, php_http_curlm_timer_callback);
+ } else {
+ curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, NULL);
+ curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, NULL);
+ curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, NULL);
+ curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, NULL);
+ }
+
+ return SUCCESS;
+}
+
+static STATUS php_http_curlm_option_set_use_eventloop(php_http_option_t *opt, zval *value, void *userdata)
+{
+ php_http_client_t *client = userdata;
+
+ return php_http_curlm_use_eventloop(client, value && Z_BVAL_P(value));
+}
+#endif
+
+static void php_http_curlm_options_init(php_http_options_t *registry TSRMLS_DC)
+{
+ php_http_option_t *opt;
+
+ /* set size of connection cache */
+ if ((opt = php_http_option_register(registry, ZEND_STRL("maxconnects"), CURLMOPT_MAXCONNECTS, IS_LONG))) {
+ /* -1 == default, 0 == unlimited */
+ ZVAL_LONG(&opt->defval, -1);
+ }
+ /* set max number of connections to a single host */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+ php_http_option_register(registry, ZEND_STRL("max_host_connections"), CURLMOPT_MAX_HOST_CONNECTIONS, IS_LONG);
+#endif
+ /* maximum number of requests in a pipeline */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+ if ((opt = php_http_option_register(registry, ZEND_STRL("max_pipeline_length"), CURLMOPT_MAX_PIPELINE_LENGTH, IS_LONG))) {
+ ZVAL_LONG(&opt->defval, 5);
+ }
+#endif
+ /* max simultaneously open connections */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+ php_http_option_register(registry, ZEND_STRL("max_total_connections"), CURLMOPT_MAX_TOTAL_CONNECTIONS, IS_LONG);
+#endif
+ /* enable/disable HTTP pipelining */
+ php_http_option_register(registry, ZEND_STRL("pipelining"), CURLMOPT_PIPELINING, IS_BOOL);
+ /* chunk length threshold for pipelining */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+ php_http_option_register(registry, ZEND_STRL("chunk_length_penalty_size"), CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE, IS_LONG);
+#endif
+ /* size threshold for pipelining penalty */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+ php_http_option_register(registry, ZEND_STRL("content_length_penalty_size"), CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE, IS_LONG);
+#endif
+ /* pipelining server blacklist */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+ if ((opt = php_http_option_register(registry, ZEND_STRL("pipelining_server_bl"), CURLMOPT_PIPELINING_SERVER_BL, IS_ARRAY))) {
+ opt->setter = php_http_curlm_option_set_pipelining_bl;
+ }
+#endif
+ /* pipelining host blacklist */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+ if ((opt = php_http_option_register(registry, ZEND_STRL("pipelining_site_bl"), CURLMOPT_PIPELINING_SITE_BL, IS_ARRAY))) {
+ opt->setter = php_http_curlm_option_set_pipelining_bl;
+ }
+#endif
+ /* events */
+#if PHP_HTTP_HAVE_EVENT
+ if ((opt = php_http_option_register(registry, ZEND_STRL("use_eventloop"), 0, IS_BOOL))) {
+ opt->setter = php_http_curlm_option_set_use_eventloop;
+ }
+#endif
+}
+
+static STATUS php_http_curlm_set_option(php_http_option_t *opt, zval *val, void *userdata)
+{
+ php_http_client_t *client = userdata;
+ php_http_client_curl_t *curl = client->ctx;
+ CURLM *ch = curl->handle;
+ zval *orig = val;
+ CURLMcode rc = CURLM_UNKNOWN_OPTION;
+ STATUS rv = SUCCESS;
+ TSRMLS_FETCH_FROM_CTX(client->ts);
+
+ if (!val) {
+ val = &opt->defval;
+ } else if (opt->type && Z_TYPE_P(val) != opt->type && !(Z_TYPE_P(val) == IS_NULL && opt->type == IS_ARRAY)) {
+ val = php_http_ztyp(opt->type, val);
+ }
+
+ if (opt->setter) {
+ rv = opt->setter(opt, val, client);
+ } else {
+ switch (opt->type) {
+ case IS_BOOL:
+ if (CURLM_OK != (rc = curl_multi_setopt(ch, opt->option, (long) Z_BVAL_P(val)))) {
+ rv = FAILURE;
+ }
+ break;
+ case IS_LONG:
+ if (CURLM_OK != (rc = curl_multi_setopt(ch, opt->option, Z_LVAL_P(val)))) {
+ rv = FAILURE;
+ }
+ break;
+ default:
+ rv = FAILURE;
+ break;
+ }
+ }
+
+ if (val && val != orig && val != &opt->defval) {
+ zval_ptr_dtor(&val);
+ }
+
+ if (rv != SUCCESS) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s (%s)", opt->name.s, curl_easy_strerror(rc));
+ }
+ return rv;
+}
/* client ops */
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);
if (storage->url) {
pefree(storage->url, 1);
}
- storage->url = pestrdup(PHP_HTTP_INFO(msg).request.url, 1);
+ php_http_url_to_string(PHP_HTTP_INFO(msg).request.url, &storage->url, NULL, 1);
curl_easy_setopt(curl->handle, CURLOPT_URL, storage->url);
/* request method */
}
}
+ /* 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);
php_http_client_curl_handler_dtor(handler);
}
-static php_resource_factory_t *create_rf(const char *url TSRMLS_DC)
+static php_resource_factory_t *create_rf(php_http_client_t *h, php_http_client_enqueue_t *enqueue TSRMLS_DC)
{
- php_url *purl;
+ php_persistent_handle_factory_t *pf = NULL;
php_resource_factory_t *rf = NULL;
+ php_http_url_t *url = enqueue->request->http.info.request.url;
- if (!url || !*url) {
+ if (!url || (!url->host && !url->path)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot request empty URL");
return NULL;
}
- purl = php_url_parse(url);
-
- if (!purl) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not parse URL '%s'", url);
- return NULL;
- } else {
+ /* only if the client itself is setup for persistence */
+ if (h->rf->dtor == (void (*)(void*)) php_persistent_handle_abandon) {
char *id_str = NULL;
- size_t id_len = spprintf(&id_str, 0, "%s:%d", STR_PTR(purl->host), purl->port ? purl->port : 80);
- php_persistent_handle_factory_t *pf = php_persistent_handle_concede(NULL, ZEND_STRL("http\\Client\\Curl\\Request"), id_str, id_len, NULL, NULL TSRMLS_CC);
+ size_t id_len;
+ int port = url->port ? url->port : 80;
+ zval **zport;
+
+ if (SUCCESS == zend_hash_find(enqueue->options, ZEND_STRS("port"), (void *) &zport)) {
+ zval *zcpy = php_http_ztyp(IS_LONG, *zport);
- if (pf) {
- rf = php_resource_factory_init(NULL, php_persistent_handle_get_resource_factory_ops(), pf, (void (*)(void*)) php_persistent_handle_abandon);
+ if (Z_LVAL_P(zcpy)) {
+ port = Z_LVAL_P(zcpy);
+ }
+ zval_ptr_dtor(&zcpy);
}
- php_url_free(purl);
+ id_len = spprintf(&id_str, 0, "%s:%d", STR_PTR(url->host), port);
+ pf = php_persistent_handle_concede(NULL, ZEND_STRL("http\\Client\\Curl\\Request"), id_str, id_len, NULL, NULL TSRMLS_CC);
efree(id_str);
}
- if (!rf) {
+ if (pf) {
+ rf = php_resource_factory_init(NULL, php_persistent_handle_get_resource_factory_ops(), pf, (void (*)(void*)) php_persistent_handle_abandon);
+ } else {
rf = php_resource_factory_init(NULL, &php_http_curle_resource_factory_ops, NULL, NULL);
}
php_resource_factory_t *rf;
TSRMLS_FETCH_FROM_CTX(h->ts);
- rf = create_rf(enqueue->request->http.info.request.url TSRMLS_CC);
+ rf = create_rf(h, enqueue TSRMLS_CC);
if (!rf) {
return FAILURE;
}
php_http_client_curl_t *curl = h->ctx;
switch (opt) {
+ case PHP_HTTP_CLIENT_OPT_CONFIGURATION:
+ return php_http_options_apply(&php_http_curlm_options, (HashTable *) arg, h);
+ break;
+
case PHP_HTTP_CLIENT_OPT_ENABLE_PIPELINING:
if (CURLM_OK != curl_multi_setopt(curl->handle, CURLMOPT_PIPELINING, (long) *((zend_bool *) arg))) {
return FAILURE;
case PHP_HTTP_CLIENT_OPT_USE_EVENTS:
#if PHP_HTTP_HAVE_EVENT
- if ((curl->useevents = *((zend_bool *) arg))) {
- if (!curl->evbase) {
- curl->evbase = event_base_new();
- }
- if (!curl->timeout) {
- curl->timeout = ecalloc(1, sizeof(struct event));
- }
- curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, h);
- curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, php_http_curlm_socket_callback);
- curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, h);
- curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, php_http_curlm_timer_callback);
- } else {
- curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, NULL);
- curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, NULL);
- curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, NULL);
- curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, NULL);
- }
+ return php_http_curlm_use_eventloop(h, *(zend_bool *) arg);
break;
#endif
return SUCCESS;
}
+static int apply_available_options(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
+{
+ php_http_option_t *opt = pDest;
+ HashTable *ht;
+ zval *entry;
+ int c;
+
+ ht = va_arg(args, HashTable*);
+
+ MAKE_STD_ZVAL(entry);
+
+ if ((c = zend_hash_num_elements(&opt->suboptions.options))) {
+ array_init_size(entry, c);
+ zend_hash_apply_with_arguments(&opt->suboptions.options TSRMLS_CC, apply_available_options, 1, Z_ARRVAL_P(entry));
+ } else {
+ /* catch deliberate NULL options */
+ if (Z_TYPE(opt->defval) == IS_STRING && !Z_STRVAL(opt->defval)) {
+ ZVAL_NULL(entry);
+ } else {
+ ZVAL_COPY_VALUE(entry, &opt->defval);
+ zval_copy_ctor(entry);
+ }
+ }
+
+ if (hash_key->nKeyLength) {
+ zend_hash_quick_update(ht, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &entry, sizeof(zval *), NULL);
+ } else {
+ zend_hash_index_update(ht, hash_key->h, (void *) &entry, sizeof(zval *), NULL);
+ }
+
+ return ZEND_HASH_APPLY_KEEP;
+}
+
static STATUS php_http_client_curl_getopt(php_http_client_t *h, php_http_client_getopt_opt_t opt, void *arg, void **res)
{
php_http_client_enqueue_t *enqueue;
+ TSRMLS_FETCH_FROM_CTX(h->ts);
switch (opt) {
case PHP_HTTP_CLIENT_OPT_PROGRESS_INFO:
}
break;
+ case PHP_HTTP_CLIENT_OPT_AVAILABLE_OPTIONS:
+ zend_hash_apply_with_arguments(&php_http_curle_options.options TSRMLS_CC, apply_available_options, 1, *(HashTable **) res);
+ break;
+
+ case PHP_HTTP_CLIENT_OPT_AVAILABLE_CONFIGURATION:
+ zend_hash_apply_with_arguments(&php_http_curlm_options.options TSRMLS_CC, apply_available_options, 1, *(HashTable **) res);
+ break;
+
default:
break;
}
};
if (SUCCESS != php_http_client_driver_add(&driver)) {
- return FAILURE;
- }
+ return FAILURE;
+ }
if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl"), &php_http_curlm_resource_factory_ops, NULL, NULL TSRMLS_CC)) {
return FAILURE;
php_http_curle_options_init(options TSRMLS_CC);
}
+ if ((options = php_http_options_init(&php_http_curlm_options, 1))) {
+ options->getter = php_http_option_get;
+ options->setter = php_http_curlm_set_option;
+
+ php_http_curlm_options_init(options TSRMLS_CC);
+ }
/*
* HTTP Protocol Version Constants
*/
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) && defined(PHP_HTTP_CURL_TLSAUTH_SRP)
+ REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "TLSAUTH_SRP", CURL_TLSAUTH_SRP, CONST_CS|CONST_PERSISTENT);
+#endif
/*
* DNS IPvX resolving
php_persistent_handle_cleanup(ZEND_STRL("http\\Client\\Curl\\Request"), NULL, 0 TSRMLS_CC);
php_http_options_dtor(&php_http_curle_options);
+ php_http_options_dtor(&php_http_curlm_options);
return SUCCESS;
}
ZEND_END_ARG_INFO();
static PHP_METHOD(HttpClientRequest, __construct)
{
- char *meth_str = NULL, *url_str = NULL;
- int meth_len = 0, url_len = 0;
- zval *zheaders = NULL, *zbody = NULL;
+ char *meth_str = NULL;
+ int meth_len = 0;
+ zval *zheaders = NULL, *zbody = NULL, *zurl = NULL;
php_http_message_object_t *obj;
- php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!s!a!O!", &meth_str, &meth_len, &url_str, &url_len, &zheaders, &zbody, php_http_message_body_class_entry), invalid_arg, return);
+ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!z!a!O!", &meth_str, &meth_len, &zurl, &zheaders, &zbody, php_http_message_body_class_entry), invalid_arg, return);
obj = zend_object_store_get_object(getThis() TSRMLS_CC);
if (meth_str && meth_len) {
PHP_HTTP_INFO(obj->message).request.method = estrndup(meth_str, meth_len);
}
- if (url_str && url_len) {
- PHP_HTTP_INFO(obj->message).request.url = estrndup(url_str, url_len);
+ if (zurl) {
+ PHP_HTTP_INFO(obj->message).request.url = php_http_url_from_zval(zurl, ~0 TSRMLS_CC);
}
if (zheaders) {
array_copy(Z_ARRVAL_P(zheaders), &obj->message->hdrs);
{
zval *qdata = NULL;
php_http_message_object_t *obj;
- php_url *old_url = NULL, new_url = {NULL};
+ php_http_url_t *old_url = NULL, new_url = {NULL};
char empty[] = "";
+ unsigned flags = PHP_HTTP_URL_REPLACE;
php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z!", &qdata), invalid_arg, return);
new_url.query = Z_STRVAL(str);
zval_dtor(&arr);
} else {
- new_url.query = &empty[0];
+ flags = PHP_HTTP_URL_STRIP_QUERY;
}
if (obj->message->http.info.request.url) {
- old_url = php_url_parse(obj->message->http.info.request.url);
- efree(obj->message->http.info.request.url);
+ old_url = obj->message->http.info.request.url;
}
- php_http_url(PHP_HTTP_URL_REPLACE, old_url, &new_url, NULL, &obj->message->http.info.request.url, NULL TSRMLS_CC);
+ obj->message->http.info.request.url = php_http_url_mod(old_url, &new_url, flags TSRMLS_CC);
if (old_url) {
- php_url_free(old_url);
+ php_http_url_free(&old_url);
}
if (new_url.query != &empty[0]) {
- STR_FREE(new_url.query);
+ PTR_FREE(new_url.query);
}
RETVAL_ZVAL(getThis(), 1, 0);
PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj);
- if (obj->message->http.info.request.url) {
- php_url *purl = php_url_parse(obj->message->http.info.request.url);
-
- if (purl) {
- if (purl->query) {
- RETVAL_STRING(purl->query, 0);
- purl->query = NULL;
- }
- php_url_free(purl);
- }
+ if (obj->message->http.info.request.url && obj->message->http.info.request.url->query) {
+ RETVAL_STRING(obj->message->http.info.request.url->query, 1);
}
}
}
{
zval *qdata, arr, str;
php_http_message_object_t *obj;
- php_url *old_url = NULL, new_url = {NULL};
+ php_http_url_t *old_url = NULL, new_url = {NULL};
php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &qdata), invalid_arg, return);
zval_dtor(&arr);
if (obj->message->http.info.request.url) {
- old_url = php_url_parse(obj->message->http.info.request.url);
- efree(obj->message->http.info.request.url);
+ old_url = obj->message->http.info.request.url;
}
- php_http_url(PHP_HTTP_URL_JOIN_QUERY, old_url, &new_url, NULL, &obj->message->http.info.request.url, NULL TSRMLS_CC);
+ obj->message->http.info.request.url = php_http_url_mod(old_url, &new_url, PHP_HTTP_URL_JOIN_QUERY TSRMLS_CC);
if (old_url) {
- php_url_free(old_url);
+ php_http_url_free(&old_url);
}
- STR_FREE(new_url.query);
+ PTR_FREE(new_url.query);
RETVAL_ZVAL(getThis(), 1, 0);
}
array_copy(&from->cookies, &to->cookies);
array_copy(&from->extras, &to->extras);
- STR_SET(to->path, from->path ? estrdup(from->path) : NULL);
- STR_SET(to->domain, from->domain ? estrdup(from->domain) : NULL);
+ PTR_SET(to->path, from->path ? estrdup(from->path) : NULL);
+ PTR_SET(to->domain, from->domain ? estrdup(from->domain) : NULL);
to->expires = from->expires;
to->max_age = from->max_age;
to->flags = from->flags;
zend_hash_destroy(&list->cookies);
zend_hash_destroy(&list->extras);
- STR_SET(list->path, NULL);
- STR_SET(list->domain, NULL);
+ PTR_SET(list->path, NULL);
+ PTR_SET(list->domain, NULL);
}
}
}
if _KEY_IS("path") {
- STR_SET(list->path, estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(arg)));
+ PTR_SET(list->path, estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(arg)));
} else if _KEY_IS("domain") {
- STR_SET(list->domain, estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(arg)));
+ PTR_SET(list->domain, estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(arg)));
} else if _KEY_IS("expires") {
char *date = estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(arg));
list->expires = php_parse_date(date, NULL);
PHP_HTTP_COOKIE_OBJECT_INIT(obj);
- STR_SET(obj->list->domain, domain_str ? estrndup(domain_str, domain_len) : NULL);
+ PTR_SET(obj->list->domain, domain_str ? estrndup(domain_str, domain_len) : NULL);
RETVAL_ZVAL(getThis(), 1, 0);
}
PHP_HTTP_COOKIE_OBJECT_INIT(obj);
- STR_SET(obj->list->path, path_str ? estrndup(path_str, path_len) : NULL);
+ PTR_SET(obj->list->path, path_str ? estrndup(path_str, path_len) : NULL);
RETVAL_ZVAL(getThis(), 1, 0);
}
(*encoded)[*encoded_len = Z.total_out] = '\0';
return SUCCESS;
} else {
- STR_SET(*encoded, NULL);
+ PTR_SET(*encoded, NULL);
*encoded_len = 0;
}
}
return SUCCESS;
}
- STR_SET(*encoded, NULL);
+ PTR_SET(*encoded, NULL);
*encoded_len = 0;
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to update deflate stream: %s", zError(status));
return FAILURE;
return SUCCESS;
}
- STR_SET(*encoded, NULL);
+ PTR_SET(*encoded, NULL);
*encoded_len = 0;
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to flush deflate stream: %s", zError(status));
return FAILURE;
return SUCCESS;
}
- STR_SET(*encoded, NULL);
+ PTR_SET(*encoded, NULL);
*encoded_len = 0;
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to finish deflate stream: %s", zError(status));
return FAILURE;
return SUCCESS;
}
- STR_SET(*decoded, NULL);
+ PTR_SET(*decoded, NULL);
*decoded_len = 0;
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to finish inflate stream: %s", zError(status));
return FAILURE;
RETURN_EMPTY_STRING();
}
} else {
- STR_FREE(encoded_str);
+ PTR_FREE(encoded_str);
}
}
}
}
}
- STR_SET(SG(request_info).content_type_dup, NULL);
+ PTR_SET(SG(request_info).content_type_dup, NULL);
return SUCCESS;
}
return PHP_HTTP_RANGE_NO;
}
if (strncmp(range, "bytes=", lenof("bytes="))) {
- STR_FREE(range);
+ PTR_FREE(range);
return PHP_HTTP_RANGE_NO;
}
switch (end) {
/* "0-" */
case -1:
- STR_FREE(range);
+ PTR_FREE(range);
return PHP_HTTP_RANGE_NO;
/* "0-0" */
case -1:
/* "-", "-0" */
if (end == -1 || end == -10) {
- STR_FREE(range);
+ PTR_FREE(range);
return PHP_HTTP_RANGE_ERR;
}
begin = length - end;
/* "12345-(NNN)" */
default:
if (length <= (size_t) begin) {
- STR_FREE(range);
+ PTR_FREE(range);
return PHP_HTTP_RANGE_ERR;
}
switch (end) {
/* "12345-0" */
case -10:
- STR_FREE(range);
+ PTR_FREE(range);
return PHP_HTTP_RANGE_ERR;
/* "12345-" */
if (length <= (size_t) end) {
end = length - 1;
} else if (end < begin) {
- STR_FREE(range);
+ PTR_FREE(range);
return PHP_HTTP_RANGE_ERR;
}
break;
break;
default:
- STR_FREE(range);
+ PTR_FREE(range);
return PHP_HTTP_RANGE_NO;
}
} while (c != 0);
- STR_FREE(range);
+ PTR_FREE(range);
return PHP_HTTP_RANGE_OK;
}
ret = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, (void *) &h TSRMLS_CC);
zval_ptr_dtor(&data);
- STR_FREE(h.line);
+ PTR_FREE(h.line);
return ret;
}
}
}
-static PHP_HTTP_STRLIST(php_http_env_response_status) =
- PHP_HTTP_STRLIST_ITEM("Continue")
- PHP_HTTP_STRLIST_ITEM("Switching Protocols")
- PHP_HTTP_STRLIST_ITEM("Processing")
- PHP_HTTP_STRLIST_NEXT
- PHP_HTTP_STRLIST_ITEM("OK")
- PHP_HTTP_STRLIST_ITEM("Created")
- PHP_HTTP_STRLIST_ITEM("Accepted")
- PHP_HTTP_STRLIST_ITEM("Non-Authoritative Information")
- PHP_HTTP_STRLIST_ITEM("No Content")
- PHP_HTTP_STRLIST_ITEM("Reset Content")
- PHP_HTTP_STRLIST_ITEM("Partial Content")
- PHP_HTTP_STRLIST_ITEM("Multi-Status")
- PHP_HTTP_STRLIST_ITEM("Already Reported")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("IM Used")
- PHP_HTTP_STRLIST_NEXT
- PHP_HTTP_STRLIST_ITEM("Multiple Choices")
- PHP_HTTP_STRLIST_ITEM("Moved Permanently")
- PHP_HTTP_STRLIST_ITEM("Found")
- PHP_HTTP_STRLIST_ITEM("See Other")
- PHP_HTTP_STRLIST_ITEM("Not Modified")
- PHP_HTTP_STRLIST_ITEM("Use Proxy")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("Temporary Redirect")
- PHP_HTTP_STRLIST_ITEM("Permanent Redirect")
- PHP_HTTP_STRLIST_NEXT
- PHP_HTTP_STRLIST_ITEM("Bad Request")
- PHP_HTTP_STRLIST_ITEM("Unauthorized")
- PHP_HTTP_STRLIST_ITEM("Payment Required")
- PHP_HTTP_STRLIST_ITEM("Forbidden")
- PHP_HTTP_STRLIST_ITEM("Not Found")
- PHP_HTTP_STRLIST_ITEM("Method Not Allowed")
- PHP_HTTP_STRLIST_ITEM("Not Acceptable")
- PHP_HTTP_STRLIST_ITEM("Proxy Authentication Required")
- PHP_HTTP_STRLIST_ITEM("Request Timeout")
- PHP_HTTP_STRLIST_ITEM("Conflict")
- PHP_HTTP_STRLIST_ITEM("Gone")
- PHP_HTTP_STRLIST_ITEM("Length Required")
- PHP_HTTP_STRLIST_ITEM("Precondition Failed")
- PHP_HTTP_STRLIST_ITEM("Request Entity Too Large")
- PHP_HTTP_STRLIST_ITEM("Request URI Too Long")
- PHP_HTTP_STRLIST_ITEM("Unsupported Media Type")
- PHP_HTTP_STRLIST_ITEM("Requested Range Not Satisfiable")
- PHP_HTTP_STRLIST_ITEM("Expectation Failed")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("Unprocessible Entity")
- PHP_HTTP_STRLIST_ITEM("Locked")
- PHP_HTTP_STRLIST_ITEM("Failed Dependency")
- PHP_HTTP_STRLIST_ITEM("(Reserved)")
- PHP_HTTP_STRLIST_ITEM("Upgrade Required")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("Precondition Required")
- PHP_HTTP_STRLIST_ITEM("Too Many Requests")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("Request Header Fields Too Large")
- PHP_HTTP_STRLIST_NEXT
- PHP_HTTP_STRLIST_ITEM("Internal Server Error")
- PHP_HTTP_STRLIST_ITEM("Not Implemented")
- PHP_HTTP_STRLIST_ITEM("Bad Gateway")
- PHP_HTTP_STRLIST_ITEM("Service Unavailable")
- PHP_HTTP_STRLIST_ITEM("Gateway Timeout")
- PHP_HTTP_STRLIST_ITEM("HTTP Version Not Supported")
- PHP_HTTP_STRLIST_ITEM("Variant Also Negotiates")
- PHP_HTTP_STRLIST_ITEM("Insufficient Storage")
- PHP_HTTP_STRLIST_ITEM("Loop Detected")
- PHP_HTTP_STRLIST_ITEM("(Unused)")
- PHP_HTTP_STRLIST_ITEM("Not Extended")
- PHP_HTTP_STRLIST_ITEM("Network Authentication Required")
- PHP_HTTP_STRLIST_STOP
-;
-
const char *php_http_env_get_response_status_for_code(unsigned code)
{
- return php_http_strlist_find(php_http_env_response_status, 100, code);
+ switch (code) {
+#define PHP_HTTP_RESPONSE_CODE(c, s) case c: return s;
+#include "php_http_response_codes.h"
+#undef PHP_HTTP_RESPONSE_CODE
+ default:
+ return NULL;
+ }
}
ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getRequestHeader, 0, 0, 0)
static PHP_METHOD(HttpEnv, getResponseStatusForCode)
{
long code;
+ const char *status;
if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) {
return;
}
- RETURN_STRING(php_http_env_get_response_status_for_code(code), 1);
+
+ if ((status = php_http_env_get_response_status_for_code(code))) {
+ RETURN_STRING(status, 1);
+ }
}
ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getResponseStatusForAllCodes, 0, 0, 0)
ZEND_END_ARG_INFO();
static PHP_METHOD(HttpEnv, getResponseStatusForAllCodes)
{
- const char *s;
- unsigned c;
- php_http_strlist_iterator_t i;
-
if (SUCCESS != zend_parse_parameters_none()) {
return;
}
array_init(return_value);
- for ( php_http_strlist_iterator_init(&i, php_http_env_response_status, 100);
- *(s = php_http_strlist_iterator_this(&i, &c));
- php_http_strlist_iterator_next(&i)
- ) {
- add_index_string(return_value, c, s, 1);
- }
+#define PHP_HTTP_RESPONSE_CODE(code, status) add_index_string(return_value, code, status, 1);
+#include "php_http_response_codes.h"
+#undef PHP_HTTP_RESPONSE_CODE
}
ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getResponseHeader, 0, 0, 0)
}
}
#if PHP_VERSION_ID >= 50600
- STR_FREE(json_str);
+ PTR_FREE(json_str);
#endif
}
zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("form"), zqs TSRMLS_CC);
zval_ptr_dtor(&zqs);
+ zsg = php_http_env_get_superglobal(ZEND_STRL("_COOKIE") TSRMLS_CC);
+ MAKE_STD_ZVAL(zqs);
+ object_init_ex(zqs, php_http_querystring_class_entry);
+ php_http_expect(SUCCESS == php_http_querystring_ctor(zqs, zsg TSRMLS_CC), unexpected_val,
+ zval_ptr_dtor(&zqs);
+ return;
+ );
+ zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("cookie"), zqs TSRMLS_CC);
+ zval_ptr_dtor(&zqs);
+
MAKE_STD_ZVAL(zqs);
array_init(zqs);
if ((zsg = php_http_env_get_superglobal(ZEND_STRL("_FILES") TSRMLS_CC))) {
}
}
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvRequest_getCookie, 0, 0, 0)
+ ZEND_ARG_INFO(0, name)
+ ZEND_ARG_INFO(0, type)
+ ZEND_ARG_INFO(0, defval)
+ ZEND_ARG_INFO(0, delete)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvRequest, getCookie)
+{
+ if (ZEND_NUM_ARGS()) {
+ call_querystring_get("cookie");
+ } else {
+ zval *zcookie = zend_read_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("cookie"), 0 TSRMLS_CC);
+ RETURN_ZVAL(zcookie, 1, 0);
+ }
+}
+
ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvRequest_getFiles, 0, 0, 0)
ZEND_END_ARG_INFO();
static PHP_METHOD(HttpEnvRequest, getFiles)
PHP_ME(HttpEnvRequest, __construct, ai_HttpEnvRequest___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
PHP_ME(HttpEnvRequest, getForm, ai_HttpEnvRequest_getForm, ZEND_ACC_PUBLIC)
PHP_ME(HttpEnvRequest, getQuery, ai_HttpEnvRequest_getQuery, ZEND_ACC_PUBLIC)
+ PHP_ME(HttpEnvRequest, getCookie, ai_HttpEnvRequest_getCookie, ZEND_ACC_PUBLIC)
PHP_ME(HttpEnvRequest, getFiles, ai_HttpEnvRequest_getFiles, ZEND_ACC_PUBLIC)
EMPTY_FUNCTION_ENTRY
};
zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("query"), ZEND_ACC_PROTECTED TSRMLS_CC);
zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("form"), ZEND_ACC_PROTECTED TSRMLS_CC);
+ zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("cookie"), ZEND_ACC_PROTECTED TSRMLS_CC);
zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("files"), ZEND_ACC_PROTECTED TSRMLS_CC);
return SUCCESS;
case IS_STRING:
zend_update_property_stringl(Z_OBJCE_P(options), options, name_str, name_len, value_ptr, value_len TSRMLS_CC);
break;
+ case IS_ARRAY:
case IS_OBJECT:
zend_update_property(Z_OBJCE_P(options), options, name_str, name_len, value_ptr TSRMLS_CC);
break;
char *value = estrndup(value_ptr, value_len);
add_assoc_stringl_ex(options, name_str, name_len + 1, value, value_len, 0);
break;
+ case IS_ARRAY:
case IS_OBJECT:
Z_ADDREF_P(value_ptr);
add_assoc_zval_ex(options, name_str, name_len + 1, value_ptr);
return request;
}
+static void set_cookie(zval *options, zval *zcookie_new TSRMLS_DC)
+{
+ HashPosition pos;
+ zval *zcookies_set;
+ php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+ php_http_cookie_object_t *obj = zend_object_store_get_object(zcookie_new TSRMLS_CC);
+
+ zcookies_set = get_option(options, ZEND_STRL("cookies") TSRMLS_CC);
+ if (!zcookies_set || Z_TYPE_P(zcookies_set) != IS_ARRAY) {
+ if (zcookies_set) {
+ zval_ptr_dtor(&zcookies_set);
+ }
+ MAKE_STD_ZVAL(zcookies_set);
+ array_init_size(zcookies_set, zend_hash_num_elements(&obj->list->cookies));
+ } else {
+ SEPARATE_ZVAL(&zcookies_set);
+ }
+
+ FOREACH_HASH_KEY(pos, &obj->list->cookies, key) {
+ Z_ADDREF_P(zcookie_new);
+ if (key.type == HASH_KEY_IS_STRING) {
+ add_assoc_zval_ex(zcookies_set, key.str, key.len, zcookie_new);
+ } else {
+ add_index_zval(zcookies_set, key.num, zcookie_new);
+ }
+ }
+
+ set_option(options, ZEND_STRL("cookies"), IS_ARRAY, zcookies_set, 0 TSRMLS_CC);
+ zval_ptr_dtor(&zcookies_set);
+}
php_http_cache_status_t php_http_env_is_response_cached_by_etag(zval *options, const char *header_str, size_t header_len, php_http_message_t *request TSRMLS_DC)
{
efree(etag);
}
- STR_FREE(header);
+ PTR_FREE(header);
return ret;
}
}
}
- STR_FREE(header);
+ PTR_FREE(header);
return ret;
}
return SUCCESS;
}
chunks_sent = php_http_buffer_chunked_output(&r->buffer, enc_str, enc_len, buf ? chunk : 0, output, r TSRMLS_CC);
- STR_FREE(enc_str);
+ PTR_FREE(enc_str);
} else {
chunks_sent = php_http_buffer_chunked_output(&r->buffer, buf, len, buf ? chunk : 0, output, r TSRMLS_CC);
}
}
php_http_buffer_free(&r->buffer);
zval_ptr_dtor(&r->options);
- STR_FREE(r->content.type);
- STR_FREE(r->content.encoding);
+ PTR_FREE(r->content.type);
+ PTR_FREE(r->content.encoding);
if (r->content.encoder) {
php_http_encoding_stream_free(&r->content.encoder);
}
return ret;
}
+ if ((zoption = get_option(options, ZEND_STRL("cookies") TSRMLS_CC))) {
+ if (Z_TYPE_P(zoption) == IS_ARRAY) {
+ HashPosition pos;
+ zval **zcookie;
+
+ FOREACH_VAL(pos, zoption, zcookie) {
+ if (Z_TYPE_PP(zcookie) == IS_OBJECT && instanceof_function(Z_OBJCE_PP(zcookie), php_http_cookie_class_entry TSRMLS_CC)) {
+ php_http_cookie_object_t *obj = zend_object_store_get_object(*zcookie TSRMLS_CC);
+ char *str;
+ size_t len;
+
+ php_http_cookie_list_to_string(obj->list, &str, &len);
+ if (SUCCESS != (ret = r->ops->add_header(r, "Set-Cookie: %s", str))) {
+ efree(str);
+ break;
+ }
+ efree(str);
+ }
+ }
+ }
+ zval_ptr_dtor(&zoption);
+ }
+
+ if (ret != SUCCESS) {
+ return ret;
+ }
+
if ((zoption = get_option(options, ZEND_STRL("contentType") TSRMLS_CC))) {
zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
long status_code;
php_stream *stream;
+ php_stream_filter *chunked_filter;
+ 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)
{
php_http_env_response_stream_ctx_t *ctx;
+ size_t buffer_size = 0x1000;
TSRMLS_FETCH_FROM_CTX(r->ts);
ctx = ecalloc(1, sizeof(*ctx));
ctx->stream = init_arg;
- if (SUCCESS != zend_list_addref(ctx->stream->rsrc_id)) {
+ if (!ctx->stream || SUCCESS != zend_list_addref(ctx->stream->rsrc_id)) {
efree(ctx);
return FAILURE;
}
+ php_stream_set_option(ctx->stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_FULL, &buffer_size);
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;
php_http_env_response_stream_ctx_t *ctx = r->ctx;
TSRMLS_FETCH_FROM_CTX(r->ts);
+ if (ctx->chunked_filter) {
+ php_stream_filter_free(ctx->chunked_filter TSRMLS_CC);
+ }
zend_hash_destroy(&ctx->header);
zend_list_delete(ctx->stream->rsrc_id);
efree(ctx);
r->ctx = NULL;
}
-static void php_http_env_response_stream_header(php_http_env_response_stream_ctx_t *ctx, HashTable *header TSRMLS_DC)
+static void php_http_env_response_stream_header(php_http_env_response_stream_ctx_t *ctx, HashTable *header, php_http_buffer_t *buf TSRMLS_DC)
{
HashPosition pos;
zval **val;
- FOREACH_HASH_VAL(pos, &ctx->header, val) {
+ FOREACH_HASH_VAL(pos, header, val) {
if (Z_TYPE_PP(val) == IS_ARRAY) {
- php_http_env_response_stream_header(ctx, Z_ARRVAL_PP(val) TSRMLS_CC);
+ php_http_env_response_stream_header(ctx, Z_ARRVAL_PP(val), buf TSRMLS_CC);
} else {
- php_stream_write(ctx->stream, Z_STRVAL_PP(val), Z_STRLEN_PP(val));
- php_stream_write_string(ctx->stream, PHP_HTTP_CRLF);
+ 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_http_buffer_append(buf, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
+ php_http_buffer_appends(buf, PHP_HTTP_CRLF);
+ zval_ptr_dtor(&tmp);
}
}
}
static STATUS php_http_env_response_stream_start(php_http_env_response_stream_ctx_t *ctx TSRMLS_DC)
{
+ php_http_buffer_t header_buf;
+
if (ctx->started || ctx->finished) {
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_http_env_response_stream_header(ctx, &ctx->header TSRMLS_CC);
- php_stream_write_string(ctx->stream, PHP_HTTP_CRLF);
- ctx->started = 1;
- return SUCCESS;
+ php_http_buffer_init(&header_buf);
+ php_http_buffer_appendf(&header_buf, "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, &header_buf TSRMLS_CC);
+
+ /* enable chunked transfer encoding */
+ if (ctx->chunked) {
+ php_http_buffer_appends(&header_buf, "Transfer-Encoding: chunked" PHP_HTTP_CRLF);
+ }
+ php_http_buffer_appends(&header_buf, PHP_HTTP_CRLF);
+
+ if (header_buf.used == php_stream_write(ctx->stream, header_buf.data, header_buf.used)) {
+ ctx->started = 1;
+ }
+ php_http_buffer_dtor(&header_buf);
+ php_stream_flush(ctx->stream);
+
+ if (ctx->chunked) {
+ ctx->chunked_filter = php_stream_filter_create("http.chunked_encode", NULL, 0 TSRMLS_CC);
+ php_stream_filter_append(&ctx->stream->writefilters, ctx->chunked_filter);
+ }
+
+ return ctx->started ? SUCCESS : FAILURE;
}
static long php_http_env_response_stream_get_status(php_http_env_response_t *r)
{
}
static STATUS php_http_env_response_stream_finish(php_http_env_response_t *r)
{
- php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
+ php_http_env_response_stream_ctx_t *ctx = r->ctx;
TSRMLS_FETCH_FROM_CTX(r->ts);
- if (stream_ctx->finished) {
+ if (ctx->finished) {
return FAILURE;
}
- if (!stream_ctx->started) {
- if (SUCCESS != php_http_env_response_stream_start(stream_ctx TSRMLS_CC)) {
+ if (!ctx->started) {
+ if (SUCCESS != php_http_env_response_stream_start(ctx TSRMLS_CC)) {
return FAILURE;
}
}
- stream_ctx->finished = 1;
+ php_stream_flush(ctx->stream);
+ if (ctx->chunked && ctx->chunked_filter) {
+ php_stream_filter_flush(ctx->chunked_filter, 1);
+ ctx->chunked_filter = php_stream_filter_remove(ctx->chunked_filter, 1 TSRMLS_CC);
+ }
+
+ ctx->finished = 1;
return SUCCESS;
}
RETVAL_ZVAL(getThis(), 1, 0);
}
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setCookie, 0, 0, 1)
+ ZEND_ARG_INFO(0, cookie)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvResponse, setCookie)
+{
+ zval *zcookie_new;
+ zend_error_handling zeh;
+ php_http_cookie_list_t *list = NULL;
+
+ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zcookie_new), invalid_arg, return);
+
+ zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC);
+ switch (Z_TYPE_P(zcookie_new)) {
+ case IS_OBJECT:
+ if (instanceof_function(Z_OBJCE_P(zcookie_new), php_http_cookie_class_entry TSRMLS_CC)) {
+ Z_ADDREF_P(zcookie_new);
+ break;
+ }
+ /* no break */
+ case IS_ARRAY:
+ list = php_http_cookie_list_from_struct(NULL, zcookie_new TSRMLS_CC);
+ MAKE_STD_ZVAL(zcookie_new);
+ ZVAL_OBJVAL(zcookie_new, php_http_cookie_object_new_ex(php_http_cookie_class_entry, list, NULL TSRMLS_CC), 0);
+ break;
+
+ default:
+ zcookie_new = php_http_ztyp(IS_STRING, zcookie_new);
+ list = php_http_cookie_list_parse(NULL, Z_STRVAL_P(zcookie_new), Z_STRLEN_P(zcookie_new), 0, NULL TSRMLS_CC);
+ zval_ptr_dtor(&zcookie_new);
+ MAKE_STD_ZVAL(zcookie_new);
+ ZVAL_OBJVAL(zcookie_new, php_http_cookie_object_new_ex(php_http_cookie_class_entry, list, NULL TSRMLS_CC), 0);
+ }
+ zend_restore_error_handling(&zeh TSRMLS_CC);
+
+ set_cookie(getThis(), zcookie_new TSRMLS_CC);
+ zval_ptr_dtor(&zcookie_new);
+
+ RETVAL_ZVAL(getThis(), 1, 0);
+}
+
ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_send, 0, 0, 0)
ZEND_ARG_INFO(0, stream)
ZEND_END_ARG_INFO();
#else
php_end_ob_buffers(1 TSRMLS_CC);
#endif
+
if (zstream) {
php_http_env_response_t *r;
PHP_ME(HttpEnvResponse, __construct, ai_HttpEnvResponse___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
PHP_ME(HttpEnvResponse, __invoke, ai_HttpEnvResponse___invoke, ZEND_ACC_PUBLIC)
PHP_ME(HttpEnvResponse, setEnvRequest, ai_HttpEnvResponse_setEnvRequest, ZEND_ACC_PUBLIC)
+ PHP_ME(HttpEnvResponse, setCookie, ai_HttpEnvResponse_setCookie, ZEND_ACC_PUBLIC)
PHP_ME(HttpEnvResponse, setContentType, ai_HttpEnvResponse_setContentType, ZEND_ACC_PUBLIC)
PHP_ME(HttpEnvResponse, setContentDisposition, ai_HttpEnvResponse_setContentDisposition, ZEND_ACC_PUBLIC)
PHP_ME(HttpEnvResponse, setContentEncoding, ai_HttpEnvResponse_setContentEncoding, ZEND_ACC_PUBLIC)
zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_MISS"), PHP_HTTP_CACHE_MISS TSRMLS_CC);
zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("request"), ZEND_ACC_PROTECTED TSRMLS_CC);
+ zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("cookies"), ZEND_ACC_PROTECTED TSRMLS_CC);
zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentType"), ZEND_ACC_PROTECTED TSRMLS_CC);
zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentDisposition"), ZEND_ACC_PROTECTED TSRMLS_CC);
zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentEncoding"), ZEND_ACC_PROTECTED TSRMLS_CC);
void *ctx;
php_http_env_response_ops_t *ops;
+ php_http_cookie_list_t *cookies;
php_http_buffer_t *buffer;
zval *options;
php_http_header_parser_dtor(&ctx);
php_http_buffer_dtor(&buf);
- if (rs == PHP_HTTP_HEADER_PARSER_STATE_FAILURE) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not parse headers");
- return FAILURE;
- }
-
- return SUCCESS;
+ return rs == PHP_HTTP_HEADER_PARSER_STATE_FAILURE ? FAILURE : SUCCESS;
}
void php_http_header_to_callback(HashTable *headers, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg TSRMLS_DC)
#include "php_http_api.h"
+#ifndef DBG_PARSER
+# define DBG_PARSER 0
+#endif
+
typedef struct php_http_header_parser_state_spec {
php_http_header_parser_state_t state;
unsigned need_data:1;
{PHP_HTTP_HEADER_PARSER_STATE_START, 1},
{PHP_HTTP_HEADER_PARSER_STATE_KEY, 1},
{PHP_HTTP_HEADER_PARSER_STATE_VALUE, 1},
- {PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX, 1},
+ {PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX, 0},
{PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE, 0},
{PHP_HTTP_HEADER_PARSER_STATE_DONE, 0}
};
{
zend_ptr_stack_destroy(&parser->stack);
php_http_info_dtor(&parser->info);
- STR_FREE(parser->_key.str);
- STR_FREE(parser->_val.str);
+ PTR_FREE(parser->_key.str);
+ PTR_FREE(parser->_val.str);
}
void php_http_header_parser_free(php_http_header_parser_t **parser)
}
}
+/* NOTE: 'str' has to be null terminated */
+static void php_http_header_parser_error(size_t valid_len, char *str, size_t len, const char *eol_str TSRMLS_DC)
+{
+ int escaped_len;
+ char *escaped_str;
+
+ escaped_str = php_addcslashes(str, len, &escaped_len, 0, ZEND_STRL("\x0..\x1F\x7F..\xFF") TSRMLS_CC);
+
+ if (valid_len != len && (!eol_str || (str+valid_len) != eol_str)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse headers: unexpected character '\\%03o' at pos %zu of '%.*s'", str[valid_len], valid_len, escaped_len, escaped_str);
+ } else if (eol_str) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse headers: unexpected end of line at pos %zu of '%.*s'", eol_str - str, escaped_len, escaped_str);
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse headers: unexpected end of input at pos %zu of '%.*s'", len, escaped_len, escaped_str);
+ }
+
+ efree(escaped_str);
+}
+
STATUS php_http_header_parser_parse(php_http_header_parser_t *parser, php_http_buffer_t *buffer, unsigned flags, HashTable *headers, php_http_info_callback_t callback_func, void *callback_arg)
{
TSRMLS_FETCH_FROM_CTX(parser->ts);
while (buffer->used || !php_http_header_parser_states[php_http_header_parser_state_is(parser)].need_data) {
-#if 0
- const char *state[] = {"START", "KEY", "VALUE", "HEADER_DONE", "DONE"};
- fprintf(stderr, "#HP: %s (avail:%zu, num:%d)\n", php_http_header_parser_state_is(parser) < 0 ? "FAILURE" : state[php_http_header_parser_state_is(parser)], buffer->used, headers?zend_hash_num_elements(headers):0);
+#if DBG_PARSER
+ const char *state[] = {"START", "KEY", "VALUE", "VALUE_EX", "HEADER_DONE", "DONE"};
+ fprintf(stderr, "#HP: %s (avail:%zu, num:%d cleanup:%u)\n", php_http_header_parser_state_is(parser) < 0 ? "FAILURE" : state[php_http_header_parser_state_is(parser)], buffer->used, headers?zend_hash_num_elements(headers):0, flags);
_dpf(0, buffer->data, buffer->used);
#endif
switch (php_http_header_parser_state_pop(parser)) {
case PHP_HTTP_HEADER_PARSER_STATE_FAILURE:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse headers");
return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
case PHP_HTTP_HEADER_PARSER_STATE_START: {
php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
} else if ((colon = memchr(buffer->data, ':', buffer->used)) && (!eol_str || eol_str > colon)) {
/* header: string */
- parser->_key.str = estrndup(buffer->data, parser->_key.len = colon - buffer->data);
+ size_t valid_len;
+
+ parser->_key.len = colon - buffer->data;
+ parser->_key.str = estrndup(buffer->data, parser->_key.len);
+
+ valid_len = strspn(parser->_key.str, PHP_HTTP_HEADER_NAME_CHARS);
+ if (valid_len != parser->_key.len) {
+ php_http_header_parser_error(valid_len, parser->_key.str, parser->_key.len, eol_str TSRMLS_CC);
+ PTR_SET(parser->_key.str, NULL);
+ return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
+ }
while (PHP_HTTP_IS_CTYPE(space, *++colon) && *colon != '\n' && *colon != '\r');
php_http_buffer_cut(buffer, 0, colon - buffer->data);
php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE);
- } else {
- /* neither reqeust/response line nor header: string */
+ } else if (eol_str || (flags & PHP_HTTP_HEADER_PARSER_CLEANUP)) {
+ /* neither reqeust/response line nor 'header:' string, or injected new line or NUL etc. */
+ php_http_buffer_fix(buffer);
+ php_http_header_parser_error(strspn(buffer->data, PHP_HTTP_HEADER_NAME_CHARS), buffer->data, buffer->used, eol_str TSRMLS_CC);
return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
+ } else {
+ /* keep feeding */
+ return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY);
}
break;
}
if ((eol_str = php_http_locate_bin_eol(buffer->data, buffer->used, &eol_len))) {
SET_ADD_VAL(eol_str - buffer->data, eol_len);
-
- if (buffer->used) {
- if (*buffer->data != '\t' && *buffer->data != ' ') {
- php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
- break;
- } else {
- php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE);
- break;
- }
- }
- }
-
- if (flags & PHP_HTTP_HEADER_PARSER_CLEANUP) {
+ php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX);
+ } else if (flags & PHP_HTTP_HEADER_PARSER_CLEANUP) {
if (buffer->used) {
SET_ADD_VAL(buffer->used, 0);
}
php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
} else {
- return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX);
+ return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE);
}
break;
}
case PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX:
- if (*buffer->data == ' ' || *buffer->data == '\t') {
+ if (buffer->used && (*buffer->data == ' ' || *buffer->data == '\t')) {
php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE);
- } else {
+ } else if (buffer->used || (flags & PHP_HTTP_HEADER_PARSER_CLEANUP)) {
php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
+ } else {
+ /* keep feeding */
+ return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX);
}
break;
case PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE:
if (parser->_key.str && parser->_val.str) {
zval array, **exist;
+ size_t valid_len = strlen(parser->_val.str);
+
+ /* check for truncation */
+ if (valid_len != parser->_val.len) {
+ php_http_header_parser_error(valid_len, parser->_val.str, parser->_val.len, NULL TSRMLS_CC);
+
+ PTR_SET(parser->_key.str, NULL);
+ PTR_SET(parser->_val.str, NULL);
+
+ return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
+ }
if (!headers && callback_func) {
callback_func(callback_arg, &headers, NULL TSRMLS_CC);
parser->_val.str = NULL;
}
- STR_SET(parser->_key.str, NULL);
- STR_SET(parser->_val.str, NULL);
+ PTR_SET(parser->_key.str, NULL);
+ PTR_SET(parser->_val.str, NULL);
php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY);
break;
return php_http_header_parser_state_is(parser);
}
+php_http_header_parser_state_t php_http_header_parser_parse_stream(php_http_header_parser_t *parser, php_http_buffer_t *buf, php_stream *s, unsigned flags, HashTable *headers, php_http_info_callback_t callback_func, void *callback_arg)
+{
+ php_http_message_parser_state_t state = PHP_HTTP_MESSAGE_PARSER_STATE_START;
+ TSRMLS_FETCH_FROM_CTX(parser->ts);
+
+ if (!buf->data) {
+ php_http_buffer_resize_ex(buf, 0x1000, 1, 0);
+ }
+ while (1) {
+ size_t justread = 0;
+#if DBG_PARSER
+ const char *states[] = {"START", "KEY", "VALUE", "VALUE_EX", "HEADER_DONE", "DONE"};
+ fprintf(stderr, "#SHP: %s (f:%u)\n", states[state], flags);
+#endif
+ /* resize if needed */
+ if (buf->free < 0x1000) {
+ php_http_buffer_resize_ex(buf, 0x1000, 1, 0);
+ }
+ switch (state) {
+ case PHP_HTTP_HEADER_PARSER_STATE_FAILURE:
+ case PHP_HTTP_HEADER_PARSER_STATE_DONE:
+ return state;
+
+ default:
+ /* read line */
+ php_stream_get_line(s, buf->data + buf->used, buf->free, &justread);
+ /* if we fail reading a whole line, try a single char */
+ if (!justread) {
+ int c = php_stream_getc(s);
+
+ if (c != EOF) {
+ char s[1] = {c};
+ justread = php_http_buffer_append(buf, s, 1);
+ }
+ }
+ php_http_buffer_account(buf, justread);
+ }
+
+ if (justread) {
+ state = php_http_header_parser_parse(parser, buf, flags, headers, callback_func, callback_arg);
+ } else if (php_stream_eof(s)) {
+ return php_http_header_parser_parse(parser, buf, flags | PHP_HTTP_HEADER_PARSER_CLEANUP, headers, callback_func, callback_arg);
+ } else {
+ return state;
+ }
+ }
+
+ return PHP_HTTP_HEADER_PARSER_STATE_DONE;
+}
+
+zend_class_entry *php_http_header_parser_class_entry;
+static zend_object_handlers php_http_header_parser_object_handlers;
+
+zend_object_value php_http_header_parser_object_new(zend_class_entry *ce TSRMLS_DC)
+{
+ return php_http_header_parser_object_new_ex(ce, NULL, NULL TSRMLS_CC);
+}
+
+zend_object_value php_http_header_parser_object_new_ex(zend_class_entry *ce, php_http_header_parser_t *parser, php_http_header_parser_object_t **ptr TSRMLS_DC)
+{
+ php_http_header_parser_object_t *o;
+
+ o = ecalloc(1, sizeof(php_http_header_parser_object_t));
+ zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
+ object_properties_init((zend_object *) o, ce);
+
+ if (ptr) {
+ *ptr = o;
+ }
+
+ if (parser) {
+ o->parser = parser;
+ } else {
+ o->parser = php_http_header_parser_init(NULL TSRMLS_CC);
+ }
+ o->buffer = php_http_buffer_new();
+
+ o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_header_parser_object_free, NULL TSRMLS_CC);
+ o->zv.handlers = &php_http_header_parser_object_handlers;
+
+ return o->zv;
+}
+
+void php_http_header_parser_object_free(void *object TSRMLS_DC)
+{
+ php_http_header_parser_object_t *o = (php_http_header_parser_object_t *) object;
+
+ if (o->parser) {
+ php_http_header_parser_free(&o->parser);
+ }
+ if (o->buffer) {
+ php_http_buffer_free(&o->buffer);
+ }
+ zend_object_std_dtor((zend_object *) o TSRMLS_CC);
+ efree(o);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeaderParser_getState, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpHeaderParser, getState)
+{
+ php_http_header_parser_object_t *parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ zend_parse_parameters_none();
+ /* always return the real state */
+ RETVAL_LONG(php_http_header_parser_state_is(parser_obj->parser));
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeaderParser_parse, 0, 0, 3)
+ ZEND_ARG_INFO(0, data)
+ ZEND_ARG_INFO(0, flags)
+ ZEND_ARG_ARRAY_INFO(1, headers, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpHeaderParser, parse)
+{
+ php_http_header_parser_object_t *parser_obj;
+ zval *zmsg;
+ char *data_str;
+ int data_len;
+ long flags;
+
+ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slz", &data_str, &data_len, &flags, &zmsg), invalid_arg, return);
+
+ if (Z_TYPE_P(zmsg) != IS_ARRAY) {
+ zval_dtor(zmsg);
+ array_init(zmsg);
+ }
+ parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+ php_http_buffer_append(parser_obj->buffer, data_str, data_len);
+ RETVAL_LONG(php_http_header_parser_parse(parser_obj->parser, parser_obj->buffer, flags, Z_ARRVAL_P(zmsg), NULL, NULL));
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeaderParser_stream, 0, 0, 3)
+ ZEND_ARG_INFO(0, stream)
+ ZEND_ARG_INFO(0, flags)
+ ZEND_ARG_ARRAY_INFO(1, headers, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpHeaderParser, stream)
+{
+ php_http_header_parser_object_t *parser_obj;
+ zend_error_handling zeh;
+ zval *zmsg, *zstream;
+ php_stream *s;
+ long flags;
+
+ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz", &zstream, &flags, &zmsg), invalid_arg, return);
+
+ zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC);
+ php_stream_from_zval(s, &zstream);
+ zend_restore_error_handling(&zeh TSRMLS_CC);
+
+ if (Z_TYPE_P(zmsg) != IS_ARRAY) {
+ zval_dtor(zmsg);
+ array_init(zmsg);
+ }
+ parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+ RETVAL_LONG(php_http_header_parser_parse_stream(parser_obj->parser, parser_obj->buffer, s, flags, Z_ARRVAL_P(zmsg), NULL, NULL));
+}
+
+static zend_function_entry php_http_header_parser_methods[] = {
+ PHP_ME(HttpHeaderParser, getState, ai_HttpHeaderParser_getState, ZEND_ACC_PUBLIC)
+ PHP_ME(HttpHeaderParser, parse, ai_HttpHeaderParser_parse, ZEND_ACC_PUBLIC)
+ PHP_ME(HttpHeaderParser, stream, ai_HttpHeaderParser_stream, ZEND_ACC_PUBLIC)
+ {NULL, NULL, NULL}
+};
+
+PHP_MINIT_FUNCTION(http_header_parser)
+{
+ zend_class_entry ce;
+
+ INIT_NS_CLASS_ENTRY(ce, "http\\Header", "Parser", php_http_header_parser_methods);
+ php_http_header_parser_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
+ memcpy(&php_http_header_parser_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+ php_http_header_parser_class_entry->create_object = php_http_header_parser_object_new;
+ php_http_header_parser_object_handlers.clone_obj = NULL;
+
+ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("CLEANUP"), PHP_HTTP_HEADER_PARSER_CLEANUP TSRMLS_CC);
+
+ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_FAILURE"), PHP_HTTP_HEADER_PARSER_STATE_FAILURE TSRMLS_CC);
+ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_START"), PHP_HTTP_HEADER_PARSER_STATE_START TSRMLS_CC);
+ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_KEY"), PHP_HTTP_HEADER_PARSER_STATE_KEY TSRMLS_CC);
+ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_VALUE"), PHP_HTTP_HEADER_PARSER_STATE_VALUE TSRMLS_CC);
+ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_VALUE_EX"), PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX TSRMLS_CC);
+ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_HEADER_DONE"), PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE TSRMLS_CC);
+ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_DONE"), PHP_HTTP_HEADER_PARSER_STATE_DONE TSRMLS_CC);
+
+ return SUCCESS;
+}
+
/*
* Local variables:
* tab-width: 4
PHP_HTTP_API void php_http_header_parser_dtor(php_http_header_parser_t *parser);
PHP_HTTP_API void php_http_header_parser_free(php_http_header_parser_t **parser);
PHP_HTTP_API php_http_header_parser_state_t php_http_header_parser_parse(php_http_header_parser_t *parser, php_http_buffer_t *buffer, unsigned flags, HashTable *headers, php_http_info_callback_t callback_func, void *callback_arg);
+PHP_HTTP_API php_http_header_parser_state_t php_http_headerparser_parse_stream(php_http_header_parser_t *parser, php_http_buffer_t *buffer, php_stream *s, unsigned flags, HashTable *headers, php_http_info_callback_t callback_func, void *callback_arg);
+
+typedef struct php_http_header_parser_object {
+ zend_object zo;
+ zend_object_value zv;
+ php_http_buffer_t *buffer;
+ php_http_header_parser_t *parser;
+} php_http_header_parser_object_t;
+
+PHP_HTTP_API zend_class_entry *php_http_header_parser_class_entry;
+
+PHP_MINIT_FUNCTION(http_header_parser);
+
+zend_object_value php_http_header_parser_object_new(zend_class_entry *ce TSRMLS_DC);
+zend_object_value php_http_header_parser_object_new_ex(zend_class_entry *ce, php_http_header_parser_t *parser, php_http_header_parser_object_t **ptr TSRMLS_DC);
+void php_http_header_parser_object_free(void *object TSRMLS_DC);
#endif /* PHP_HTTP_HEADER_PARSER_H */
{
switch (i->type) {
case PHP_HTTP_REQUEST:
- STR_SET(PHP_HTTP_INFO(i).request.method, NULL);
- STR_SET(PHP_HTTP_INFO(i).request.url, NULL);
+ PTR_SET(PHP_HTTP_INFO(i).request.method, NULL);
+ PTR_SET(PHP_HTTP_INFO(i).request.url, NULL);
break;
case PHP_HTTP_RESPONSE:
- STR_SET(PHP_HTTP_INFO(i).response.status, NULL);
+ PTR_SET(PHP_HTTP_INFO(i).response.status, NULL);
break;
default:
}
/* 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 = estrndup(url, http - url);
+ /* 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 {
- STR_SET(PHP_HTTP_INFO(info).request.method, NULL);
+ PTR_SET(PHP_HTTP_INFO(info).request.method, NULL);
return NULL;
}
} else {
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_H
#include "php_http_version.h"
+#include "php_http_url.h"
-#define PHP_HTTP_INFO_REQUEST_FMT_ARGS(_http_ptr, eol) "%s %s HTTP/%u.%u" eol, \
+#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?(_http_ptr)->info.request.url:"/", \
+ (_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
-#define PHP_HTTP_INFO_RESPONSE_FMT_ARGS(_http_ptr, eol) "HTTP/%u.%u %d%s%s" eol, \
+#define PHP_HTTP_INFO_RESPONSE_FMT_ARGS(_http_ptr, tmp, eol) "HTTP/%u.%u %d%s%s" eol, \
(_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, \
(_http_ptr)->info.response.code?(_http_ptr)->info.response.code:200, \
typedef struct php_http_info_data {
union {
/* GET /foo/bar */
- struct { char *method; char *url; } request;
+ struct { char *method; php_http_url_t *url; } request;
/* 200 Ok */
struct { unsigned code; char *status; } response;
} info;
message->http.info.request.method = estrdup(Z_STRVAL_P(sval));
}
if ((sval = php_http_env_get_server_var(ZEND_STRL("REQUEST_URI"), 1 TSRMLS_CC))) {
- message->http.info.request.url = estrdup(Z_STRVAL_P(sval));
+ message->http.info.request.url = php_http_url_parse(Z_STRVAL_P(sval), Z_STRLEN_P(sval), ~0 TSRMLS_CC);
}
php_http_env_get_request_headers(&message->hdrs TSRMLS_CC);
/* free request info */
switch (message->type) {
case PHP_HTTP_REQUEST:
- STR_FREE(message->http.info.request.method);
- STR_FREE(message->http.info.request.url);
+ PTR_FREE(message->http.info.request.method);
+ PTR_FREE(message->http.info.request.url);
break;
case PHP_HTTP_RESPONSE:
- STR_FREE(message->http.info.response.status);
+ PTR_FREE(message->http.info.response.status);
break;
default:
message->http.version = info->http.version;
switch (message->type) {
case PHP_HTTP_REQUEST:
- STR_SET(PHP_HTTP_INFO(message).request.url, PHP_HTTP_INFO(info).request.url ? estrdup(PHP_HTTP_INFO(info).request.url) : NULL);
- STR_SET(PHP_HTTP_INFO(message).request.method, PHP_HTTP_INFO(info).request.method ? estrdup(PHP_HTTP_INFO(info).request.method) : NULL);
+ PTR_SET(PHP_HTTP_INFO(message).request.url, PHP_HTTP_INFO(info).request.url ? php_http_url_copy(PHP_HTTP_INFO(info).request.url, 0) : NULL);
+ PTR_SET(PHP_HTTP_INFO(message).request.method, PHP_HTTP_INFO(info).request.method ? estrdup(PHP_HTTP_INFO(info).request.method) : NULL);
break;
case PHP_HTTP_RESPONSE:
PHP_HTTP_INFO(message).response.code = PHP_HTTP_INFO(info).response.code;
- STR_SET(PHP_HTTP_INFO(message).response.status, PHP_HTTP_INFO(info).response.status ? estrdup(PHP_HTTP_INFO(info).response.status) : NULL);
+ PTR_SET(PHP_HTTP_INFO(message).response.status, PHP_HTTP_INFO(info).response.status ? estrdup(PHP_HTTP_INFO(info).response.status) : NULL);
break;
default:
if (php_http_message_body_stream(msg->body)->readfilters.head) {
/* if a read stream filter is attached to the body the caller must also care for the headers */
+ } else if ((h = php_http_message_header(msg, ZEND_STRL("Content-Range"), 0))) {
+ /* don't mess around with a Content-Range message */
+ zval_ptr_dtor(&h);
} else if ((size = php_http_message_body_size(msg->body))) {
MAKE_STD_ZVAL(h);
ZVAL_LONG(h, size);
zval_ptr_dtor(&h);
if (Z_LVAL_P(h_cpy)) {
+ /* body->size == 0, so get rid of old Content-Length */
zend_hash_del(&msg->hdrs, "Content-Length", sizeof("Content-Length"));
}
zval_ptr_dtor(&h_cpy);
static void message_headers(php_http_message_t *msg, php_http_buffer_t *str)
{
+ char *tmp = NULL;
TSRMLS_FETCH_FROM_CTX(msg->ts);
switch (msg->type) {
case PHP_HTTP_REQUEST:
- php_http_buffer_appendf(str, PHP_HTTP_INFO_REQUEST_FMT_ARGS(&msg->http, PHP_HTTP_CRLF));
+ php_http_buffer_appendf(str, PHP_HTTP_INFO_REQUEST_FMT_ARGS(&msg->http, tmp, PHP_HTTP_CRLF));
+ PTR_FREE(tmp);
break;
case PHP_HTTP_RESPONSE:
- php_http_buffer_appendf(str, PHP_HTTP_INFO_RESPONSE_FMT_ARGS(&msg->http, PHP_HTTP_CRLF));
+ php_http_buffer_appendf(str, PHP_HTTP_INFO_RESPONSE_FMT_ARGS(&msg->http, tmp, PHP_HTTP_CRLF));
+ PTR_FREE(tmp);
break;
default:
switch (message->type) {
case PHP_HTTP_REQUEST:
- STR_SET(message->http.info.request.method, NULL);
- STR_SET(message->http.info.request.url, NULL);
+ PTR_SET(message->http.info.request.method, NULL);
+ PTR_SET(message->http.info.request.url, NULL);
break;
case PHP_HTTP_RESPONSE:
- STR_SET(message->http.info.response.status, NULL);
+ PTR_SET(message->http.info.response.status, NULL);
break;
default:
static void php_http_message_object_prophandler_set_request_method(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message)) {
zval *cpy = php_http_ztyp(IS_STRING, value);
- STR_SET(obj->message->http.info.request.method, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
+ PTR_SET(obj->message->http.info.request.method, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
zval_ptr_dtor(&cpy);
}
}
static void php_http_message_object_prophandler_get_request_url(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
- if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message) && obj->message->http.info.request.url) {
- RETVAL_STRING(obj->message->http.info.request.url, 1);
+ char *url_str;
+ size_t url_len;
+
+ if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message) && obj->message->http.info.request.url && php_http_url_to_string(obj->message->http.info.request.url, &url_str, &url_len, 0)) {
+ RETVAL_STRINGL(url_str, url_len, 0);
} else {
RETVAL_NULL();
}
}
static void php_http_message_object_prophandler_set_request_url(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message)) {
- zval *cpy = php_http_ztyp(IS_STRING, value);
- STR_SET(obj->message->http.info.request.url, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
- zval_ptr_dtor(&cpy);
+ PTR_SET(obj->message->http.info.request.url, php_http_url_from_zval(value, ~0 TSRMLS_CC));
}
}
static void php_http_message_object_prophandler_get_response_status(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
static void php_http_message_object_prophandler_set_response_status(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message)) {
zval *cpy = php_http_ztyp(IS_STRING, value);
- STR_SET(obj->message->http.info.response.status, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
+ PTR_SET(obj->message->http.info.response.status, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
zval_ptr_dtor(&cpy);
}
}
if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message)) {
zval *cpy = php_http_ztyp(IS_LONG, value);
obj->message->http.info.response.code = Z_LVAL_P(cpy);
- STR_SET(obj->message->http.info.response.status, estrdup(php_http_env_get_response_status_for_code(obj->message->http.info.response.code)));
+ PTR_SET(obj->message->http.info.response.status, estrdup(php_http_env_get_response_status_for_code(obj->message->http.info.response.code)));
zval_ptr_dtor(&cpy);
}
}
php_http_message_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
HashTable *props = zend_get_std_object_handlers()->get_properties(object TSRMLS_CC);
zval array, *parent, *body;
- char *version;
- int verlen;
+ char *ver_str, *url_str = NULL;
+ size_t ver_len, url_len = 0;
PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
INIT_PZVAL_ARRAY(&array, props);
} while(0)
ASSOC_PROP(long, "type", obj->message->type);
- verlen = spprintf(&version, 0, "%u.%u", obj->message->http.version.major, obj->message->http.version.minor);
- ASSOC_STRINGL_EX("httpVersion", version, verlen, 0);
+ ver_len = spprintf(&ver_str, 0, "%u.%u", obj->message->http.version.major, obj->message->http.version.minor);
+ ASSOC_STRINGL_EX("httpVersion", ver_str, ver_len, 0);
switch (obj->message->type) {
case PHP_HTTP_REQUEST:
ASSOC_PROP(long, "responseCode", 0);
ASSOC_STRINGL("responseStatus", "", 0);
ASSOC_STRING("requestMethod", STR_PTR(obj->message->http.info.request.method));
- ASSOC_STRING("requestUrl", STR_PTR(obj->message->http.info.request.url));
+ if (obj->message->http.info.request.url) {
+ php_http_url_to_string(obj->message->http.info.request.url, &url_str, &url_len, 0);
+ ASSOC_STRINGL_EX("requestUrl", url_str, url_len, 0);
+ } else {
+ ASSOC_STRINGL("requestUrl", "", 0);
+ }
+
break;
case PHP_HTTP_RESPONSE:
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);
static PHP_METHOD(HttpMessage, getInfo)
{
if (SUCCESS == zend_parse_parameters_none()) {
+ char *tmp = NULL;
php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
switch (obj->message->type) {
case PHP_HTTP_REQUEST:
- Z_STRLEN_P(return_value) = spprintf(&Z_STRVAL_P(return_value), 0, PHP_HTTP_INFO_REQUEST_FMT_ARGS(&obj->message->http, ""));
+ Z_STRLEN_P(return_value) = spprintf(&Z_STRVAL_P(return_value), 0, PHP_HTTP_INFO_REQUEST_FMT_ARGS(&obj->message->http, tmp, ""));
+ PTR_FREE(tmp);
break;
case PHP_HTTP_RESPONSE:
- Z_STRLEN_P(return_value) = spprintf(&Z_STRVAL_P(return_value), 0, PHP_HTTP_INFO_RESPONSE_FMT_ARGS(&obj->message->http, ""));
+ Z_STRLEN_P(return_value) = spprintf(&Z_STRVAL_P(return_value), 0, PHP_HTTP_INFO_RESPONSE_FMT_ARGS(&obj->message->http, tmp, ""));
+ PTR_FREE(tmp);
break;
default:
RETURN_NULL();
}
obj->message->http.info.response.code = code;
- STR_SET(obj->message->http.info.response.status, estrdup(php_http_env_get_response_status_for_code(code)));
+ PTR_SET(obj->message->http.info.response.status, estrdup(php_http_env_get_response_status_for_code(code)));
RETVAL_ZVAL(getThis(), 1, 0);
}
php_http_throw(bad_method_call, "http\\Message is not of type response", NULL);
}
- STR_SET(obj->message->http.info.response.status, estrndup(status, status_len));
+ PTR_SET(obj->message->http.info.response.status, estrndup(status, status_len));
RETVAL_ZVAL(getThis(), 1, 0);
}
return;
}
- STR_SET(obj->message->http.info.request.method, estrndup(method, method_len));
+ PTR_SET(obj->message->http.info.request.method, estrndup(method, method_len));
RETVAL_ZVAL(getThis(), 1, 0);
}
}
if (obj->message->http.info.request.url) {
- RETURN_STRING(obj->message->http.info.request.url, 1);
+ char *url_str;
+ size_t url_len;
+
+ php_http_url_to_string(obj->message->http.info.request.url, &url_str, &url_len, 0);
+ RETURN_STRINGL(url_str, url_len, 0);
} else {
RETURN_EMPTY_STRING();
}
ZEND_END_ARG_INFO();
static PHP_METHOD(HttpMessage, setRequestUrl)
{
- char *url_str;
- int url_len;
+ zval *zurl;
+ php_http_url_t *url;
php_http_message_object_t *obj;
+ zend_error_handling zeh;
- php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &url_str, &url_len), invalid_arg, return);
+ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zurl), invalid_arg, return);
obj = zend_object_store_get_object(getThis() TSRMLS_CC);
return;
}
- if (url_len < 1) {
+ zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC);
+ url = php_http_url_from_zval(zurl, ~0 TSRMLS_CC);
+ zend_restore_error_handling(&zeh TSRMLS_CC);
+
+ if (php_http_url_is_empty(url)) {
+ php_http_url_free(&url);
php_http_throw(invalid_arg, "Cannot set http\\Message's request url to an empty string", NULL);
- return;
+ } else {
+ PTR_SET(obj->message->http.info.request.url, url);
}
- STR_SET(obj->message->http.info.request.url, estrndup(url_str, url_len));
RETVAL_ZVAL(getThis(), 1, 0);
}
RETURN_EMPTY_STRING();
}
+#ifdef ZTS
+static size_t write_to_stream(void *s, const char *str, size_t len)
+{
+ TSRMLS_FETCH();
+ return php_stream_write(s, str, len);
+}
+#else
+# define write_to_stream (php_http_pass_callback_t)_php_stream_write
+#endif
+
ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_toStream, 0, 0, 1)
ZEND_ARG_INFO(0, stream)
ZEND_END_ARG_INFO();
PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
php_stream_from_zval(s, &zstream);
- php_http_message_to_callback(obj->message, (php_http_pass_callback_t) _php_stream_write, s);
+ php_http_message_to_callback(obj->message, write_to_stream, s);
}
}
php_http_expect(msg = php_http_message_body_split(obj->message->body, boundary), bad_message, return);
- STR_FREE(boundary);
+ PTR_FREE(boundary);
RETURN_OBJVAL(php_http_message_object_new_ex(php_http_message_class_entry, msg, NULL TSRMLS_CC), 0);
}
TSRMLS_FETCH_FROM_CTX(body->ts);
/* NOFIXME: shows leakinfo in DEBUG mode */
zend_list_delete(body->stream_id);
- STR_FREE(body->boundary);
+ PTR_FREE(body->boundary);
efree(body);
}
*body_ptr = NULL;
char *php_http_message_body_etag(php_http_message_body_t *body)
{
- const php_stream_statbuf *ssb = php_http_message_body_stat(body);
+ php_http_etag_t *etag;
+ php_stream *s = php_http_message_body_stream(body);
TSRMLS_FETCH_FROM_CTX(body->ts);
/* real file or temp buffer ? */
- if (ssb && ssb->sb.st_mtime) {
- char *etag;
+ if (s->ops != &php_stream_temp_ops && s->ops != &php_stream_memory_ops) {
+ php_stream_stat(php_http_message_body_stream(body), &body->ssb);
- spprintf(&etag, 0, "%lx-%lx-%lx", ssb->sb.st_ino, ssb->sb.st_mtime, ssb->sb.st_size);
- return etag;
- } else {
- php_http_etag_t *etag = php_http_etag_init(PHP_HTTP_G->env.etag_mode TSRMLS_CC);
+ if (body->ssb.sb.st_mtime) {
+ char *etag;
- if (etag) {
- php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_etag_update, etag, 0, 0);
- return php_http_etag_finish(etag);
- } else {
- return NULL;
+ spprintf(&etag, 0, "%lx-%lx-%lx", body->ssb.sb.st_ino, body->ssb.sb.st_mtime, body->ssb.sb.st_size);
+ return etag;
}
}
+
+ /* content based */
+ if ((etag = php_http_etag_init(PHP_HTTP_G->env.etag_mode TSRMLS_CC))) {
+ php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_etag_update, etag, 0, 0);
+ return php_http_etag_finish(etag);
+ }
+
+ return NULL;
}
void php_http_message_body_to_string(php_http_message_body_t *body, char **buf, size_t *len, off_t offset, size_t forlen)
php_http_buffer_free(&tmp);
php_http_message_parser_free(&arg.parser);
php_http_buffer_dtor(&arg.buf);
- STR_FREE(arg.boundary_str);
+ PTR_FREE(arg.boundary_str);
return msg;
}
static const php_http_message_parser_state_spec_t php_http_message_parser_states[] = {
{PHP_HTTP_MESSAGE_PARSER_STATE_START, 1},
- {PHP_HTTP_MESSAGE_PARSER_STATE_HEADER, 1},
+ {PHP_HTTP_MESSAGE_PARSER_STATE_HEADER, 0},
{PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE, 0},
{PHP_HTTP_MESSAGE_PARSER_STATE_BODY, 0},
{PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB, 1},
{PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH, 1},
{PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED, 1},
{PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE, 0},
+ {PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL, 0},
{PHP_HTTP_MESSAGE_PARSER_STATE_DONE, 0}
};
#if DBG_PARSER
const char *php_http_message_parser_state_name(php_http_message_parser_state_t state) {
- const char *states[] = {"START", "HEADER", "HEADER_DONE", "BODY", "BODY_DUMB", "BODY_LENGTH", "BODY_CHUNK", "BODY_DONE", "DONE"};
+ const char *states[] = {"START", "HEADER", "HEADER_DONE", "BODY", "BODY_DUMB", "BODY_LENGTH", "BODY_CHUNK", "BODY_DONE", "UPDATE_CL", "DONE"};
if (state < 0 || state > (sizeof(states)/sizeof(char*))-1) {
return "FAILURE";
if (!buf->data) {
php_http_buffer_resize_ex(buf, 0x1000, 1, 0);
}
- while (!php_stream_eof(s)) {
+ while (1) {
size_t justread = 0;
#if DBG_PARSER
fprintf(stderr, "#SP: %s (f:%u)\n", php_http_message_parser_state_name(state), flags);
#endif
+ /* resize if needed */
+ if (buf->free < 0x1000) {
+ php_http_buffer_resize_ex(buf, 0x1000, 1, 0);
+ }
switch (state) {
case PHP_HTTP_MESSAGE_PARSER_STATE_START:
case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER:
case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE:
/* read line */
php_stream_get_line(s, buf->data + buf->used, buf->free, &justread);
+ /* if we fail reading a whole line, try a single char */
+ if (!justread) {
+ int c = php_stream_getc(s);
+
+ if (c != EOF) {
+ char s[1] = {c};
+ justread = php_http_buffer_append(buf, s, 1);
+ }
+ }
php_http_buffer_account(buf, justread);
break;
case PHP_HTTP_MESSAGE_PARSER_STATE_BODY:
case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:
+ case PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL:
/* should not occur */
abort();
break;
if (justread) {
state = php_http_message_parser_parse(parser, buf, flags, message);
- } else {
+ } else if (php_stream_eof(s)) {
+ return php_http_message_parser_parse(parser, buf, flags | PHP_HTTP_MESSAGE_PARSER_CLEANUP, message);
+ } else {
return state;
}
}
break;
default:
- php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER);
- if (buffer->used) {
- return PHP_HTTP_MESSAGE_PARSER_STATE_HEADER;
+ if (buffer->used || !(flags & PHP_HTTP_MESSAGE_PARSER_CLEANUP)) {
+ return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER);
+ } else {
+ php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE);
}
}
break;
{
zval *h, *h_loc = NULL, *h_con = NULL, **h_cl = NULL, **h_cr = NULL, **h_te = NULL;
+ /* Content-Range has higher precedence than Content-Length,
+ * and content-length denotes the original length of the entity,
+ * so let's *NOT* remove CR/CL, because that would fundamentally
+ * change the meaning of the whole message
+ */
if ((h = php_http_message_header(*message, ZEND_STRL("Transfer-Encoding"), 1))) {
- zend_hash_update(&(*message)->hdrs, "X-Original-Transfer-Encoding", sizeof("X-Original-Transfer-Encoding"), &h, sizeof(zval *), (void *) &h_te);
+ zend_hash_update(&(*message)->hdrs, "X-Original-Transfer-Encoding", sizeof("X-Original-Transfer-Encoding"), (void *) &h, sizeof(zval *), (void *) &h_te);
zend_hash_del(&(*message)->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding"));
+
+ /* reset */
+ MAKE_STD_ZVAL(h);
+ ZVAL_LONG(h, 0);
+ zend_hash_update(&(*message)->hdrs, "Content-Length", sizeof("Content-Length"), (void *) &h, sizeof(zval *), NULL);
+ } else if ((h = php_http_message_header(*message, ZEND_STRL("Content-Length"), 1))) {
+ zend_hash_update(&(*message)->hdrs, "X-Original-Content-Length", sizeof("X-Original-Content-Length"), (void *) &h, sizeof(zval *), (void *) &h_cl);
}
- if ((h = php_http_message_header(*message, ZEND_STRL("Content-Length"), 1))) {
- zend_hash_update(&(*message)->hdrs, "X-Original-Content-Length", sizeof("X-Original-Content-Length"), &h, sizeof(zval *), (void *) &h_cl);
- }
+
if ((h = php_http_message_header(*message, ZEND_STRL("Content-Range"), 1))) {
- zend_hash_update(&(*message)->hdrs, "X-Original-Content-Range", sizeof("X-Original-Content-Range"), &h, sizeof(zval *), (void *) &h_cr);
- zend_hash_del(&(*message)->hdrs, "Content-Range", sizeof("Content-Range"));
+ zend_hash_find(&(*message)->hdrs, ZEND_STRS("Content-Range"), (void *) &h_cr);
+ if (h != *h_cr) {
+ zend_hash_update(&(*message)->hdrs, "Content-Range", sizeof("Content-Range"), &h, sizeof(zval *), (void *) &h_cr);
+ } else {
+ zval_ptr_dtor(&h);
+ }
}
- /* default */
- MAKE_STD_ZVAL(h);
- ZVAL_LONG(h, 0);
- zend_hash_update(&(*message)->hdrs, "Content-Length", sizeof("Content-Length"), &h, sizeof(zval *), NULL);
-
/* so, if curl sees a 3xx code, a Location header and a Connection:close header
* it decides not to read the response body.
*/
}
}
- if (h_cl) {
- char *stop;
-
- if (Z_TYPE_PP(h_cl) == IS_STRING) {
- parser->body_length = strtoul(Z_STRVAL_PP(h_cl), &stop, 10);
-
- if (stop != Z_STRVAL_PP(h_cl)) {
- php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
- break;
- }
- } else if (Z_TYPE_PP(h_cl) == IS_LONG) {
- parser->body_length = Z_LVAL_PP(h_cl);
- php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
- break;
- }
- }
-
if (h_cr) {
ulong total = 0, start = 0, end = 0;
if (!strncasecmp(Z_STRVAL_PP(h_cr), "bytes", lenof("bytes"))
- && ( Z_STRVAL_P(h)[lenof("bytes")] == ':'
- || Z_STRVAL_P(h)[lenof("bytes")] == ' '
- || Z_STRVAL_P(h)[lenof("bytes")] == '='
+ && ( Z_STRVAL_PP(h_cr)[lenof("bytes")] == ':'
+ || Z_STRVAL_PP(h_cr)[lenof("bytes")] == ' '
+ || Z_STRVAL_PP(h_cr)[lenof("bytes")] == '='
)
) {
char *total_at = NULL, *end_at = NULL;
total = strtoul(total_at + 1, NULL, 10);
}
- if (end >= start && (!total || end < total)) {
+ if (end >= start && (!total || end <= total)) {
parser->body_length = end + 1 - start;
php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
break;
}
}
+ if (h_cl) {
+ char *stop;
+
+ if (Z_TYPE_PP(h_cl) == IS_STRING) {
+ parser->body_length = strtoul(Z_STRVAL_PP(h_cl), &stop, 10);
+
+ if (stop != Z_STRVAL_PP(h_cl)) {
+ php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
+ break;
+ }
+ } else if (Z_TYPE_PP(h_cl) == IS_LONG) {
+ parser->body_length = Z_LVAL_PP(h_cl);
+ php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
+ break;
+ }
+ }
if ((*message)->type == PHP_HTTP_REQUEST) {
php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
case PHP_HTTP_MESSAGE_PARSER_STATE_BODY:
{
if (len) {
- zval *zcl;
-
+ /* FIXME: what if we re-use the parser? */
if (parser->inflate) {
char *dec_str = NULL;
size_t dec_len;
}
if (str != buffer->data) {
- STR_FREE(str);
+ PTR_FREE(str);
}
str = dec_str;
len = dec_len;
php_stream_write(php_http_message_body_stream((*message)->body), str, len);
- /* keep track */
- MAKE_STD_ZVAL(zcl);
- ZVAL_LONG(zcl, php_http_message_body_size((*message)->body));
- zend_hash_update(&(*message)->hdrs, "Content-Length", sizeof("Content-Length"), &zcl, sizeof(zval *), NULL);
}
if (cut) {
}
if (str != buffer->data) {
- STR_FREE(str);
+ PTR_FREE(str);
}
str = NULL;
{
php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
- if (parser->dechunk) {
+ if (parser->dechunk && parser->dechunk->ctx) {
char *dec_str = NULL;
size_t dec_len;
str = dec_str;
len = dec_len;
cut = 0;
- php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
+ php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
}
}
break;
}
- case PHP_HTTP_MESSAGE_PARSER_STATE_DONE: {
+ case PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL:
+ {
+ zval *zcl;
+ MAKE_STD_ZVAL(zcl);
+ ZVAL_LONG(zcl, php_http_message_body_size((*message)->body));
+ zend_hash_update(&(*message)->hdrs, "Content-Length", sizeof("Content-Length"), &zcl, sizeof(zval *), NULL);
+ break;
+ }
+
+ case PHP_HTTP_MESSAGE_PARSER_STATE_DONE:
+ {
char *ptr = buffer->data;
while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) {
zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_LENGTH"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH TSRMLS_CC);
zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_CHUNKED"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED TSRMLS_CC);
zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE TSRMLS_CC);
+ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_UPDATE_CL"), PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL TSRMLS_CC);
zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_DONE TSRMLS_CC);
return SUCCESS;
PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH,
PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED,
PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE,
+ PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL,
PHP_HTTP_MESSAGE_PARSER_STATE_DONE
} php_http_message_parser_state_t;
}
}
- STR_FREE(haystack);
- STR_FREE(needle);
+ PTR_FREE(haystack);
+ PTR_FREE(needle);
}
return result;
if ((flags & ARRAY_JOIN_PRETTIFY) && hash_key->nKeyLength) {
key = php_http_pretty_key(estrndup(hash_key->arKey, hash_key->nKeyLength - 1), hash_key->nKeyLength - 1, 1, 1);
zend_hash_find(dst, key, hash_key->nKeyLength, (void *) &data);
- } else {
+ } else if (hash_key->nKeyLength) {
zend_hash_quick_find(dst, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &data);
+ } else {
+ zend_hash_index_find(dst, hash_key->h, (void *) &data);
}
if (flags & ARRAY_JOIN_STRINGIFY) {
add_next_index_zval(*data, value);
} else if (key) {
zend_symtable_update(dst, key, hash_key->nKeyLength, &value, sizeof(zval *), NULL);
- } else {
+ } else if (hash_key->nKeyLength) {
zend_hash_quick_add(dst, hash_key->arKey, hash_key->nKeyLength, hash_key->h, &value, sizeof(zval *), NULL);
+ } else {
+ zend_hash_index_update(dst, hash_key->h, (void *) &value, sizeof(zval *), NULL);
}
if (key) {
key = php_http_pretty_key(estrndup(hash_key->arKey, hash_key->nKeyLength - 1), hash_key->nKeyLength - 1, 1, 1);
zend_hash_update(dst, key, hash_key->nKeyLength, (void *) &value, sizeof(zval *), NULL);
efree(key);
- } else {
+ } else if (hash_key->nKeyLength) {
zend_hash_quick_update(dst, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &value, sizeof(zval *), NULL);
+ } else {
+ zend_hash_index_update(dst, hash_key->h, (void *) &value, sizeof(zval *), NULL);
}
}
/* send buffer size */
#define PHP_HTTP_SENDBUF_SIZE 40960
+/* allowed characters of header field names */
+#define PHP_HTTP_HEADER_NAME_CHARS "!#$%&'*+-.^_`|~1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
/* SLEEP */
#define PHP_HTTP_DIFFSEC (0.001)
/* STRING UTILITIES */
-#ifndef STR_SET
-# define STR_SET(STR, SET) \
+#ifndef PTR_SET
+# define PTR_SET(STR, SET) \
{ \
- STR_FREE(STR); \
+ PTR_FREE(STR); \
STR = SET; \
}
#endif
static inline void php_http_array_hashkey_stringfree(php_http_array_hashkey_t *key)
{
if (key->type != HASH_KEY_IS_STRING || key->dup) {
- STR_FREE(key->str);
+ PTR_FREE(key->str);
}
}
add_index_double(&arr, key.num, q);
}
- STR_FREE(key.str);
+ PTR_FREE(key.str);
}
#if 0
if (value) {
result = php_http_negotiate(value, length, supported, "-", 1 TSRMLS_CC);
}
- STR_FREE(value);
+ PTR_FREE(value);
return result;
}
if (value) {
result = php_http_negotiate(value, length, supported, NULL, 0 TSRMLS_CC);
}
- STR_FREE(value);
+ PTR_FREE(value);
return result;
}
if (value) {
result = php_http_negotiate(value, length, supported, NULL, 0 TSRMLS_CC);
}
- STR_FREE(value);
+ PTR_FREE(value);
return result;
}
if (value) {
result = php_http_negotiate(value, length, supported, "/", 1 TSRMLS_CC);
}
- STR_FREE(value);
+ PTR_FREE(value);
return result;
}
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)
+static inline zend_function *get_object_method(zval *zobject, zval *zmeth TSRMLS_DC)
+{
+#if PHP_VERSION_ID >= 50400
+ return Z_OBJ_HT_P(zobject)->get_method(&zobject, Z_STRVAL_P(zmeth), Z_STRLEN_P(zmeth), NULL TSRMLS_CC);
+#else
+ return Z_OBJ_HT_P(zobject)->get_method(&zobject, Z_STRVAL_P(zmeth), Z_STRLEN_P(zmeth) TSRMLS_CC);
+#endif
+}
+
+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 = get_object_method(zobject, cb->fci.function_name 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;
+
+ Z_ADDREF_P(zobject);
+ cb->fci.object_ptr = zobject;
+ cb->fcc.object_ptr = zobject;
+
+ cb->fci.retval_ptr_ptr = retval_ptr ? retval_ptr : &retval;
- 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;
+ cb->fci.param_count = argc;
+ cb->fci.params = args;
- INIT_PZVAL(&zmethod);
- ZVAL_STRINGL(&zmethod, method_str, method_len, 0);
- rv = zend_call_function(&fci, NULL TSRMLS_CC);
+ if (cb->fcc.called_scope != Z_OBJCE_P(zobject)) {
+ cb->fcc.called_scope = Z_OBJCE_P(zobject);
+ cb->fcc.function_handler = get_object_method(zobject, cb->fci.function_name 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
ZVAL_COPY_VALUE(tmp, zv);
array_init(zv);
add_assoc_zval(zv, language, tmp);
- STR_FREE(language);
+ PTR_FREE(language);
}
}
php_http_params_token_t **sep = separator;
if (sep) {
while (*sep) {
- STR_FREE((*sep)->str);
+ PTR_FREE((*sep)->str);
efree(*sep);
++sep;
}
--- /dev/null
+/*
+ +--------------------------------------------------------------------+
+ | PECL :: http |
+ +--------------------------------------------------------------------+
+ | Redistribution and use in source and binary forms, with or without |
+ | modification, are permitted provided that the conditions mentioned |
+ | in the accompanying LICENSE file are met. |
+ +--------------------------------------------------------------------+
+ | Copyright (c) 2004-2015, Michael Wallner <mike@php.net> |
+ +--------------------------------------------------------------------+
+*/
+
+#ifndef PHP_HTTP_RESPONSE_CODE
+# define PHP_HTTP_RESPONSE_CODE(code, status)
+#endif
+
+PHP_HTTP_RESPONSE_CODE(100, "Continue")
+PHP_HTTP_RESPONSE_CODE(101, "Switching Protocols")
+PHP_HTTP_RESPONSE_CODE(102, "Processing")
+PHP_HTTP_RESPONSE_CODE(200, "OK")
+PHP_HTTP_RESPONSE_CODE(201, "Created")
+PHP_HTTP_RESPONSE_CODE(202, "Accepted")
+PHP_HTTP_RESPONSE_CODE(203, "Non-Authoritative Information")
+PHP_HTTP_RESPONSE_CODE(204, "No Content")
+PHP_HTTP_RESPONSE_CODE(205, "Reset Content")
+PHP_HTTP_RESPONSE_CODE(206, "Partial Content")
+PHP_HTTP_RESPONSE_CODE(207, "Multi-Status")
+PHP_HTTP_RESPONSE_CODE(208, "Already Reported")
+PHP_HTTP_RESPONSE_CODE(226, "IM Used")
+PHP_HTTP_RESPONSE_CODE(300, "Multiple Choices")
+PHP_HTTP_RESPONSE_CODE(301, "Moved Permanently")
+PHP_HTTP_RESPONSE_CODE(302, "Found")
+PHP_HTTP_RESPONSE_CODE(303, "See Other")
+PHP_HTTP_RESPONSE_CODE(304, "Not Modified")
+PHP_HTTP_RESPONSE_CODE(305, "Use Proxy")
+PHP_HTTP_RESPONSE_CODE(307, "Temporary Redirect")
+PHP_HTTP_RESPONSE_CODE(308, "Permanent Redirect")
+PHP_HTTP_RESPONSE_CODE(400, "Bad Request")
+PHP_HTTP_RESPONSE_CODE(401, "Unauthorized")
+PHP_HTTP_RESPONSE_CODE(402, "Payment Required")
+PHP_HTTP_RESPONSE_CODE(403, "Forbidden")
+PHP_HTTP_RESPONSE_CODE(404, "Not Found")
+PHP_HTTP_RESPONSE_CODE(405, "Method Not Allowed")
+PHP_HTTP_RESPONSE_CODE(406, "Not Acceptable")
+PHP_HTTP_RESPONSE_CODE(407, "Proxy Authentication Required")
+PHP_HTTP_RESPONSE_CODE(408, "Request Timeout")
+PHP_HTTP_RESPONSE_CODE(409, "Conflict")
+PHP_HTTP_RESPONSE_CODE(410, "Gone")
+PHP_HTTP_RESPONSE_CODE(411, "Length Required")
+PHP_HTTP_RESPONSE_CODE(412, "Precondition Failed")
+PHP_HTTP_RESPONSE_CODE(413, "Request Entity Too Large")
+PHP_HTTP_RESPONSE_CODE(414, "Request URI Too Long")
+PHP_HTTP_RESPONSE_CODE(415, "Unsupported Media Type")
+PHP_HTTP_RESPONSE_CODE(416, "Requested Range Not Satisfiable")
+PHP_HTTP_RESPONSE_CODE(417, "Expectation Failed")
+PHP_HTTP_RESPONSE_CODE(422, "Unprocessible Entity")
+PHP_HTTP_RESPONSE_CODE(423, "Locked")
+PHP_HTTP_RESPONSE_CODE(424, "Failed Dependency")
+PHP_HTTP_RESPONSE_CODE(426, "Upgrade Required")
+PHP_HTTP_RESPONSE_CODE(428, "Precondition Required")
+PHP_HTTP_RESPONSE_CODE(429, "Too Many Requests")
+PHP_HTTP_RESPONSE_CODE(431, "Request Header Fields Too Large")
+PHP_HTTP_RESPONSE_CODE(500, "Internal Server Error")
+PHP_HTTP_RESPONSE_CODE(501, "Not Implemented")
+PHP_HTTP_RESPONSE_CODE(502, "Bad Gateway")
+PHP_HTTP_RESPONSE_CODE(503, "Service Unavailable")
+PHP_HTTP_RESPONSE_CODE(504, "Gateway Timeout")
+PHP_HTTP_RESPONSE_CODE(505, "HTTP Version Not Supported")
+PHP_HTTP_RESPONSE_CODE(506, "Variant Also Negotiates")
+PHP_HTTP_RESPONSE_CODE(507, "Insufficient Storage")
+PHP_HTTP_RESPONSE_CODE(508, "Loop Detected")
+PHP_HTTP_RESPONSE_CODE(510, "Not Extended")
+PHP_HTTP_RESPONSE_CODE(511, "Network Authentication Required")
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
+++ /dev/null
-/*
- +--------------------------------------------------------------------+
- | PECL :: http |
- +--------------------------------------------------------------------+
- | Redistribution and use in source and binary forms, with or without |
- | modification, are permitted provided that the conditions mentioned |
- | in the accompanying LICENSE file are met. |
- +--------------------------------------------------------------------+
- | Copyright (c) 2004-2014, Michael Wallner <mike@php.net> |
- +--------------------------------------------------------------------+
-*/
-
-#include "php_http_api.h"
-
-php_http_strlist_iterator_t *php_http_strlist_iterator_init(php_http_strlist_iterator_t *iter, const char list[], unsigned factor)
-{
- if (!iter) {
- iter = emalloc(sizeof(*iter));
- }
- memset(iter, 0, sizeof(*iter));
-
- iter->p = &list[0];
- iter->factor = factor;
-
- return iter;
-}
-
-const char *php_http_strlist_iterator_this(php_http_strlist_iterator_t *iter, unsigned *id)
-{
- if (id) {
- *id = (iter->major + 1) * iter->factor + iter->minor;
- }
-
- return iter->p;
-}
-
-const char *php_http_strlist_iterator_next(php_http_strlist_iterator_t *iter)
-{
- if (*iter->p) {
- while (*iter->p) {
- ++iter->p;
- }
- ++iter->p;
- ++iter->minor;
-
- if (!*iter->p) {
- ++iter->p;
- ++iter->major;
- iter->minor = 0;
- }
- }
-
- return iter->p;
-}
-
-void php_http_strlist_iterator_dtor(php_http_strlist_iterator_t *iter)
-{
-
-}
-
-void php_http_strlist_iterator_free(php_http_strlist_iterator_t **iter)
-{
- if (*iter) {
- efree(*iter);
- *iter = NULL;
- }
-}
-
-const char *php_http_strlist_find(const char list[], unsigned factor, unsigned item)
-{
- unsigned M = 0, m = 0, major, minor;
- const char *p = &list[0];
-
- if (factor) {
- major = (item / factor) - 1;
- minor = item % factor;
- } else {
- major = 0;
- minor = item;
- }
- while (*p && major != M++) {
- while (*p) {
- while (*p) {
- ++p;
- }
- ++p;
- }
- ++p;
- }
-
- while (*p && minor != m++) {
- while (*p) {
- ++p;
- }
- ++p;
- }
-
- return p;
-}
-
-/*
- * Local variables:
- * tab-width: 4
- * c-basic-offset: 4
- * End:
- * vim600: noet sw=4 ts=4 fdm=marker
- * vim<600: noet sw=4 ts=4
- */
-
+++ /dev/null
-/*
- +--------------------------------------------------------------------+
- | PECL :: http |
- +--------------------------------------------------------------------+
- | Redistribution and use in source and binary forms, with or without |
- | modification, are permitted provided that the conditions mentioned |
- | in the accompanying LICENSE file are met. |
- +--------------------------------------------------------------------+
- | Copyright (c) 2004-2014, Michael Wallner <mike@php.net> |
- +--------------------------------------------------------------------+
-*/
-
-#ifndef PHP_HTTP_STRLIST_H
-#define PHP_HTTP_STRLIST_H
-
-#ifdef NUL
-# undef NUL
-#endif
-#define NUL "\0"
-
-#define PHP_HTTP_STRLIST(name) const char name[]
-#define PHP_HTTP_STRLIST_ITEM(item) item NUL
-#define PHP_HTTP_STRLIST_NEXT NUL
-#define PHP_HTTP_STRLIST_STOP NUL NUL
-
-PHP_HTTP_API const char *php_http_strlist_find(const char list[], unsigned factor, unsigned item);
-
-typedef struct php_http_strlist_iterator {
- const char *p;
- unsigned factor, major, minor;
-} php_http_strlist_iterator_t;
-
-PHP_HTTP_API php_http_strlist_iterator_t *php_http_strlist_iterator_init(php_http_strlist_iterator_t *iter, const char list[], unsigned factor);
-PHP_HTTP_API const char *php_http_strlist_iterator_this(php_http_strlist_iterator_t *iter, unsigned *id);
-PHP_HTTP_API const char *php_http_strlist_iterator_next(php_http_strlist_iterator_t *iter);
-PHP_HTTP_API void php_http_strlist_iterator_dtor(php_http_strlist_iterator_t *iter);
-PHP_HTTP_API void php_http_strlist_iterator_free(php_http_strlist_iterator_t **iter);
-
-#endif /* PHP_HTTP_STRLIST_H */
-
-/*
- * Local variables:
- * tab-width: 4
- * c-basic-offset: 4
- * End:
- * vim600: noet sw=4 ts=4 fdm=marker
- * vim<600: noet sw=4 ts=4
- */
-
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(TSRMLS_D)
{
return url(buf);
}
-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)
-{
- php_http_url_t *url = php_http_url_mod((const php_http_url_t *) old_url, (const php_http_url_t *) new_url, flags TSRMLS_CC);
-
- if (url_ptr) {
- *url_ptr = php_http_url_to_php_url(url);
- }
- if (url_str) {
- php_http_url_to_string(url, url_str, url_len TSRMLS_CC);
- }
-
- php_http_url_free(&url);
-}
-
#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)
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;
- url(buf)->path = &buf.data[buf.used];
-
if (url_isset(new_url, path)) {
path = new_url->path;
} else if (url_isset(old_url, path)) {
path = old_url->path;
- } else {
- php_http_buffer_append(&buf, "/", sizeof("/"));
}
if (path) {
- if (path[0] != '/') {
- php_http_buffer_append(&buf, "/", 1);
- }
- php_http_buffer_append(&buf, path, strlen(path) + 1);
+ url(buf)->path = &buf.data[buf.used];
+
+ url_append(&buf, php_http_buffer_append(&buf, path, strlen(path) + 1));
}
php_http_querystring_update(&qarr, NULL, &qstr TSRMLS_CC);
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);
php_http_url_free(&tmp_url);
}
- /* set some sane defaults */
-
- if (!url(buf)->scheme) {
- url(buf)->scheme = &buf.data[buf.used];
- php_http_buffer_append(&buf, "http", sizeof("http"));
- }
-
- if (!url(buf)->host) {
- url(buf)->host = &buf.data[buf.used];
- php_http_buffer_append(&buf, "localhost", sizeof("localhost"));
- }
-
- if (!url(buf)->path) {
- url(buf)->path = &buf.data[buf.used];
- php_http_buffer_append(&buf, "/", sizeof("/"));
- }
/* replace directory references if path is not a single slash */
if ((flags & PHP_HTTP_URL_SANITIZE_PATH)
&& url(buf)->path[0] && url(buf)->path[1]) {
}
/* 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 url(buf);
}
-void php_http_url_to_string(const php_http_url_t *url, char **url_str, size_t *url_len TSRMLS_DC)
+char *php_http_url_to_string(const php_http_url_t *url, char **url_str, size_t *url_len, zend_bool persistent)
{
php_http_buffer_t buf;
- php_http_buffer_init(&buf);
+ php_http_buffer_init_ex(&buf, PHP_HTTP_BUFFER_DEFAULT_SIZE, persistent ?
+ PHP_HTTP_BUFFER_INIT_PERSISTENT : 0);
if (url->scheme && *url->scheme) {
php_http_buffer_appendl(&buf, url->scheme);
php_http_buffer_appends(&buf, "://");
- } else {
+ } else if ((url->user && *url->user) || (url->host && *url->host)) {
php_http_buffer_appends(&buf, "//");
}
if (url->host && *url->host) {
php_http_buffer_appendl(&buf, url->host);
- } else {
- php_http_buffer_appends(&buf, "localhost");
- }
-
- if (url->port) {
- php_http_buffer_appendf(&buf, ":%hu", url->port);
+ if (url->port) {
+ php_http_buffer_appendf(&buf, ":%hu", url->port);
+ }
}
if (url->path && *url->path) {
+ if (*url->path != '/') {
+ php_http_buffer_appends(&buf, "/");
+ }
php_http_buffer_appendl(&buf, url->path);
+ } else if (buf.used) {
+ php_http_buffer_appends(&buf, "/");
}
if (url->query && *url->query) {
if (url_str) {
*url_str = buf.data;
- } else {
- php_http_buffer_dtor(&buf);
}
+
+ return buf.data;
}
-php_http_url_t *php_http_url_from_struct(HashTable *ht TSRMLS_DC)
+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;
+ php_http_url_t *purl;
+
+ switch (Z_TYPE_P(value)) {
+ case IS_ARRAY:
+ case IS_OBJECT:
+ purl = php_http_url_from_struct(HASH_OF(value));
+ break;
+
+ default:
+ zcpy = php_http_ztyp(IS_STRING, value);
+ purl = php_http_url_parse(Z_STRVAL_P(zcpy), Z_STRLEN_P(zcpy), flags TSRMLS_CC);
+ zval_ptr_dtor(&zcpy);
+ }
+
+ return purl;
+}
+
+php_http_url_t *php_http_url_from_struct(HashTable *ht)
{
zval **e;
php_http_buffer_t buf;
if (SUCCESS == zend_hash_find(ht, "scheme", sizeof("scheme"), (void *) &e)) {
zval *cpy = php_http_ztyp(IS_STRING, *e);
url(buf)->scheme = &buf.data[buf.used];
- php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1);
+ url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
zval_ptr_dtor(&cpy);
}
if (SUCCESS == zend_hash_find(ht, "user", sizeof("user"), (void *) &e)) {
zval *cpy = php_http_ztyp(IS_STRING, *e);
url(buf)->user = &buf.data[buf.used];
- php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1);
+ url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
zval_ptr_dtor(&cpy);
}
if (SUCCESS == zend_hash_find(ht, "pass", sizeof("pass"), (void *) &e)) {
zval *cpy = php_http_ztyp(IS_STRING, *e);
url(buf)->pass = &buf.data[buf.used];
- php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1);
+ url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
zval_ptr_dtor(&cpy);
}
if (SUCCESS == zend_hash_find(ht, "host", sizeof("host"), (void *) &e)) {
zval *cpy = php_http_ztyp(IS_STRING, *e);
url(buf)->host = &buf.data[buf.used];
- php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1);
+ url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
zval_ptr_dtor(&cpy);
}
if (SUCCESS == zend_hash_find(ht, "port", sizeof("port"), (void *) &e)) {
if (SUCCESS == zend_hash_find(ht, "path", sizeof("path"), (void *) &e)) {
zval *cpy = php_http_ztyp(IS_STRING, *e);
url(buf)->path = &buf.data[buf.used];
- php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1);
+ url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
zval_ptr_dtor(&cpy);
}
if (SUCCESS == zend_hash_find(ht, "query", sizeof("query"), (void *) &e)) {
zval *cpy = php_http_ztyp(IS_STRING, *e);
url(buf)->query = &buf.data[buf.used];
- php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1);
+ url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
zval_ptr_dtor(&cpy);
}
if (SUCCESS == zend_hash_find(ht, "fragment", sizeof("fragment"), (void *) &e)) {
zval *cpy = php_http_ztyp(IS_STRING, *e);
url(buf)->fragment = &buf.data[buf.used];
- php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1);
+ url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
zval_ptr_dtor(&cpy);
}
}
}
+php_http_url_t *php_http_url_copy(const php_http_url_t *url, zend_bool persistent)
+{
+ php_http_url_t *cpy;
+ const char *end = NULL, *url_ptr = (const char *) url;
+ char *cpy_ptr;
+
+ end = MAX(url->scheme, end);
+ end = MAX(url->pass, end);
+ end = MAX(url->user, end);
+ end = MAX(url->host, end);
+ end = MAX(url->path, end);
+ end = MAX(url->query, end);
+ end = MAX(url->fragment, end);
+
+ if (end) {
+ end += strlen(end) + 1;
+ cpy_ptr = pecalloc(1, end - url_ptr, persistent);
+ cpy = (php_http_url_t *) cpy_ptr;
+
+ memcpy(cpy_ptr + sizeof(*cpy), url_ptr + sizeof(*url), end - url_ptr - sizeof(*url));
+
+ cpy->scheme = url->scheme ? cpy_ptr + (url->scheme - url_ptr) : NULL;
+ cpy->pass = url->pass ? cpy_ptr + (url->pass - url_ptr) : NULL;
+ cpy->user = url->user ? cpy_ptr + (url->user - url_ptr) : NULL;
+ cpy->host = url->host ? cpy_ptr + (url->host - url_ptr) : NULL;
+ cpy->path = url->path ? cpy_ptr + (url->path - url_ptr) : NULL;
+ cpy->query = url->query ? cpy_ptr + (url->query - url_ptr) : NULL;
+ cpy->fragment = url->fragment ? cpy_ptr + (url->fragment - url_ptr) : NULL;
+ } else {
+ cpy = ecalloc(1, sizeof(*url));
+ }
+
+ cpy->port = url->port;
+
+ return cpy;
+}
+
static size_t parse_mb_utf8(unsigned *wc, const char *ptr, const char *end)
{
unsigned wchar;
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)
switch (*state->ptr) {
case '#':
case '?':
- case '\0':
- /* did we have any path component ? */
- if (tmp != state->ptr) {
- state->buffer[state->offset++] = 0;
- } else {
- state->url.path = NULL;
- }
- return state->ptr;
+ goto done;
case '%':
if (state->ptr[1] != '%' && (state->end - state->ptr <= 2 || !isxdigit(*(state->ptr+1)) || !isxdigit(*(state->ptr+2)))) {
}
state->ptr += mb - 1;
}
- } while (++state->ptr <= state->end);
+ } while (++state->ptr < state->end);
- return NULL;
+ done:
+ /* did we have any path component ? */
+ if (tmp != state->ptr) {
+ state->buffer[state->offset++] = 0;
+ } else {
+ state->url.path = NULL;
+ }
+ return state->ptr;
}
static const char *parse_query(struct parse_state *state)
do {
switch (*state->ptr) {
case '#':
- case '\0':
- state->buffer[state->offset++] = 0;
- return state->ptr;
+ goto done;
case '%':
if (state->ptr[1] != '%' && (state->end - state->ptr <= 2 || !isxdigit(*(state->ptr+1)) || !isxdigit(*(state->ptr+2)))) {
}
state->ptr += mb - 1;
}
- } while (++state->ptr <= state->end);
+ } while (++state->ptr < state->end);
- return NULL;
+ done:
+ state->buffer[state->offset++] = 0;
+ return state->ptr;
}
static const char *parse_fragment(struct parse_state *state)
do {
switch (*state->ptr) {
- case '\0':
- state->buffer[state->offset++] = 0;
- return state->ptr;
-
case '%':
if (state->ptr[1] != '%' && (state->end - state->ptr <= 2 || !isxdigit(*(state->ptr+1)) || !isxdigit(*(state->ptr+2)))) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
}
state->ptr += mb - 1;
}
- } while (++state->ptr <= state->end);
+ } while (++state->ptr < state->end);
- return NULL;
+ state->buffer[state->offset++] = 0;
+ return state->ptr;
}
static const char *parse_hier(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)
php_http_url_t *res_purl, *new_purl = NULL, *old_purl = NULL;
if (new_url) {
- switch (Z_TYPE_P(new_url)) {
- case IS_OBJECT:
- case IS_ARRAY:
- new_purl = php_http_url_from_struct(HASH_OF(new_url) TSRMLS_CC);
- break;
- default: {
- zval *cpy = php_http_ztyp(IS_STRING, new_url);
-
- new_purl = php_http_url_parse(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy), flags TSRMLS_CC);
- zval_ptr_dtor(&cpy);
- break;
- }
- }
+ new_purl = php_http_url_from_zval(new_url, flags TSRMLS_CC);
if (!new_purl) {
zend_restore_error_handling(&zeh TSRMLS_CC);
return;
}
}
if (old_url) {
- switch (Z_TYPE_P(old_url)) {
- case IS_OBJECT:
- case IS_ARRAY:
- old_purl = php_http_url_from_struct(HASH_OF(old_url) TSRMLS_CC);
- break;
- default: {
- zval *cpy = php_http_ztyp(IS_STRING, old_url);
-
- old_purl = php_http_url_parse(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy), flags TSRMLS_CC);
- zval_ptr_dtor(&cpy);
- break;
- }
- }
+ old_purl = php_http_url_from_zval(old_url, flags TSRMLS_CC);
if (!old_purl) {
if (new_purl) {
php_http_url_free(&new_purl);
php_http_url_t *new_purl = NULL, *old_purl = NULL;
if (new_url) {
- switch (Z_TYPE_P(new_url)) {
- case IS_OBJECT:
- case IS_ARRAY:
- new_purl = php_http_url_from_struct(HASH_OF(new_url) TSRMLS_CC);
- break;
- default: {
- zval *cpy = php_http_ztyp(IS_STRING, new_url);
-
- new_purl = php_http_url_parse(Z_STRVAL_P(new_url), Z_STRLEN_P(new_url), flags TSRMLS_CC);
- zval_ptr_dtor(&cpy);
- break;
- }
- }
+ new_purl = php_http_url_from_zval(new_url, flags TSRMLS_CC);
if (!new_purl) {
zend_restore_error_handling(&zeh TSRMLS_CC);
return;
}
}
- if ((old_purl = php_http_url_from_struct(HASH_OF(getThis()) TSRMLS_CC))) {
+ if ((old_purl = php_http_url_from_struct(HASH_OF(getThis())))) {
php_http_url_t *res_purl;
ZVAL_OBJVAL(return_value, zend_objects_clone_obj(getThis() TSRMLS_CC), 0);
if (SUCCESS == zend_parse_parameters_none()) {
php_http_url_t *purl;
- if ((purl = php_http_url_from_struct(HASH_OF(getThis()) TSRMLS_CC))) {
+ if ((purl = php_http_url_from_struct(HASH_OF(getThis())))) {
char *str;
size_t len;
- php_http_url_to_string(purl, &str, &len TSRMLS_CC);
+ php_http_url_to_string(purl, &str, &len, 0);
php_http_url_free(&purl);
RETURN_STRINGL(str, len, 0);
}
}
/* strip any non-URL properties */
- purl = php_http_url_from_struct(HASH_OF(getThis()) TSRMLS_CC);
+ purl = php_http_url_from_struct(HASH_OF(getThis()));
php_http_url_to_struct(purl, return_value TSRMLS_CC);
php_http_url_free(&purl);
}
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_parse, 0, 0, 1)
- ZEND_ARG_INFO(0, url)
- ZEND_ARG_INFO(0, flags)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpUrl, parse)
-{
- char *str;
- int len;
- long flags = 0;
- php_http_url_t *url;
- zend_error_handling zeh;
-
- php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &len, &flags), invalid_arg, return);
-
- zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC);
- if ((url = php_http_url_parse(str, len, flags TSRMLS_CC))) {
- object_init_ex(return_value, php_http_url_class_entry);
- if (url->scheme) {
- zend_update_property_string(php_http_url_class_entry, return_value,
- ZEND_STRL("scheme"), url->scheme TSRMLS_CC);
- }
- if (url->user) {
- zend_update_property_string(php_http_url_class_entry, return_value,
- ZEND_STRL("user"), url->user TSRMLS_CC);
- }
- if (url->pass) {
- zend_update_property_string(php_http_url_class_entry, return_value,
- ZEND_STRL("pass"), url->pass TSRMLS_CC);
- }
- if (url->host) {
- zend_update_property_string(php_http_url_class_entry, return_value,
- ZEND_STRL("host"), url->host TSRMLS_CC);
- }
- if (url->port) {
- zend_update_property_long(php_http_url_class_entry, return_value,
- ZEND_STRL("port"), url->port TSRMLS_CC);
- }
- if (url->path) {
- zend_update_property_string(php_http_url_class_entry, return_value,
- ZEND_STRL("path"), url->path TSRMLS_CC);
- }
- if (url->query) {
- zend_update_property_string(php_http_url_class_entry, return_value,
- ZEND_STRL("query"), url->query TSRMLS_CC);
- }
- if (url->fragment) {
- zend_update_property_string(php_http_url_class_entry, return_value,
- ZEND_STRL("fragment"), url->fragment TSRMLS_CC);
- }
- php_http_url_free(&url);
- }
- zend_restore_error_handling(&zeh TSRMLS_CC);
-}
-
static zend_function_entry php_http_url_methods[] = {
PHP_ME(HttpUrl, __construct, ai_HttpUrl___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
PHP_ME(HttpUrl, mod, ai_HttpUrl_mod, ZEND_ACC_PUBLIC)
PHP_ME(HttpUrl, toString, ai_HttpUrl_toString, ZEND_ACC_PUBLIC)
ZEND_MALIAS(HttpUrl, __toString, toString, ai_HttpUrl_toString, ZEND_ACC_PUBLIC)
PHP_ME(HttpUrl, toArray, ai_HttpUrl_toArray, ZEND_ACC_PUBLIC)
- PHP_ME(HttpUrl, parse, ai_HttpUrl_parse, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
EMPTY_FUNCTION_ENTRY
};
#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 void php_http_url_free(php_http_url_t **url);
-
-/* 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_parse_authority(const char *str, size_t len, unsigned flags TSRMLS_DC);
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 TSRMLS_DC);
+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 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);
static inline void php_http_url_argsep(const char **str, size_t *len TSRMLS_DC)
{
- if (SUCCESS != php_http_ini_entry(ZEND_STRL("arg_separator.output"), str, len, 0 TSRMLS_CC) || !*len) {
- *str = PHP_HTTP_URL_ARGSEP;
- *len = lenof(PHP_HTTP_URL_ARGSEP);
- }
+ php_http_ini_entry(ZEND_STRL("arg_separator.output"), str, len, 0 TSRMLS_CC);
}
-static inline php_url *php_http_url_to_php_url(php_http_url_t *url)
-{
- php_url *purl = ecalloc(1, sizeof(*purl));
-
- if (url->scheme) purl->scheme = estrdup(url->scheme);
- if (url->pass) purl->pass = estrdup(url->pass);
- if (url->user) purl->user = estrdup(url->user);
- if (url->host) purl->host = estrdup(url->host);
- if (url->path) purl->path = estrdup(url->path);
- if (url->query) purl->query = estrdup(url->query);
- if (url->fragment) purl->fragment = estrdup(url->fragment);
-
- return purl;
+static inline zend_bool php_http_url_is_empty(const php_http_url_t *url) {
+ return !(url->scheme || url->pass || url->user || url->host || url->port || url->path || url->query || url->fragment);
}
-PHP_HTTP_API php_http_url_t *php_http_url_from_struct(HashTable *ht TSRMLS_DC);
-PHP_HTTP_API HashTable *php_http_url_to_struct(const php_http_url_t *url, zval *strct TSRMLS_DC);
-PHP_HTTP_API void php_http_url_to_string(const php_http_url_t *url, char **url_str, size_t *url_len TSRMLS_DC);
-
PHP_HTTP_API zend_class_entry *php_http_url_class_entry;
PHP_MINIT_FUNCTION(http_url);
{
unsigned char ub = utf8_mblen[*uc];
- if (!ub || ub > len || ub > 3) {
+ if (!ub || ub > len || ub > 4) {
return 0;
}
static inline zend_bool isualpha(unsigned ch)
{
- unsigned i, j;
+ unsigned i = 0, j;
- for (i = 0; i < sizeof(utf8_ranges)/sizeof(utf8_range_t); ++i) {
+ PHP_HTTP_DUFF(sizeof(utf8_ranges)/sizeof(utf8_range_t),
if (utf8_ranges[i].start == ch) {
return 1;
} else if (utf8_ranges[i].start <= ch && utf8_ranges[i].end >= ch) {
}
return 0;
}
- }
+ ++i;
+ );
return 0;
}
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);
}
}
$r = new http\Env\Response;
$r->setResponseCode(200);
$r->send();
-var_dump(http_response_code());
+var_dump(http\Env::getResponseCode());
?>
--EXPECT--
int(200)
\ No newline at end of file
--- /dev/null
+--TEST--
+Bug #69000 (http\Url breaks down with very long URL query strings)
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+echo "Test\n";
+echo new http\Url("http://foo.bar/?".str_repeat("a", 1024));
+?>
+
+===DONE===
+--EXPECT--
+Test
+http://foo.bar/?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+===DONE===
--SKIPIF--
<?php
include "skipif.inc";
+skip_client_test();
?>
--FILE--
<?php
--SKIPIF--
<?php
include "skipif.inc";
-skip_online_test();
+skip_client_test();
?>
--FILE--
<?php
+
+include "helper/server.inc";
+
echo "Test\n";
class Observer implements SplObserver
}
}
-$observer = new Observer;
-$request = new http\Client\Request("GET", "http://www.example.org/");
-
-foreach (http\Client::getAvailableDrivers() as $driver) {
- $client = new http\Client($driver);
- $client->attach($observer);
- $client->enqueue($request);
- $client->send();
-}
+server("proxy.inc", function($port, $stdin, $stdout, $stderr) {
+ foreach (http\Client::getAvailableDrivers() as $driver) {
+ $client = new http\Client($driver);
+ $client->attach(new Observer);
+ $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/"));
+ $client->send();
+ }
+});
?>
--SKIPIF--
<?php
include "skipif.inc";
-skip_online_test();
+skip_client_test();
?>
--FILE--
<?php
-echo "Test\n";
-$request = new http\Client\Request("GET", "http://www.example.org/");
+include "helper/server.inc";
+
+echo "Test\n";
-foreach (http\Client::getAvailableDrivers() as $driver) {
- $client = new http\Client($driver);
- $client->enqueue($request);
+server("proxy.inc", function($port) {
+ $request = new http\Client\Request("GET", "http://www.example.org/");
- while ($client->once()) {
- $client->wait(.1);
- }
+ foreach (http\Client::getAvailableDrivers() as $driver) {
+ $client = new http\Client($driver);
+ $client->enqueue($request);
+
+ while ($client->once()) {
+ $client->wait(.1);
+ }
- if (!$client->getResponse()) {
- var_dump($client);
+ if (!$client->getResponse()) {
+ var_dump($client);
+ }
}
-}
+});
?>
Done
--EXPECT--
--SKIPIF--
<?php
include "skipif.inc";
-skip_online_test();
+skip_client_test();
?>
--FILE--
<?php
-echo "Test\n";
-$request = new http\Client\Request("GET", "http://www.example.org");
+include "helper/server.inc";
-foreach (http\Client::getAvailableDrivers() as $driver) {
- $client = new http\Client($driver);
- $client->enqueue($request)->send();
- if (!($client->getResponse($request) instanceof http\Client\Response)) {
- var_dump($client);
- }
- try {
+echo "Test\n";
+
+server("proxy.inc", function($port) {
+ $request = new http\Client\Request("GET", "http://localhost:$port");
+
+ foreach (http\Client::getAvailableDrivers() as $driver) {
+ $client = new http\Client($driver);
+ $client->enqueue($request)->send();
+ if (!($client->getResponse($request) instanceof http\Client\Response)) {
+ var_dump($client);
+ }
+ try {
+ $client->enqueue($request);
+ } catch (Exception $e) {
+ echo $e->getMessage(),"\n";
+ }
+ $client->reset();
+ if (($response = $client->getResponse())) {
+ var_dump($response);
+ }
$client->enqueue($request);
- } catch (Exception $e) {
- echo $e->getMessage(),"\n";
- }
- $client->reset();
- if (($response = $client->getResponse())) {
- var_dump($response);
}
- $client->enqueue($request);
-}
+ });
?>
Done
--EXPECTREGEX--
--SKIPIF--
<?php
include "skipif.inc";
-skip_online_test();
+skip_client_test();
?>
--FILE--
<?php
+
+include "helper/server.inc";
+
echo "Test\n";
-foreach (http\Client::getAvailableDrivers() as $driver) {
- $client = new http\Client($driver);
- $client->enqueue(new http\Client\Request("GET", "http://www.example.org"), function($response) {
- echo "R\n";
- if (!($response instanceof http\Client\Response)) {
- var_dump($response);
- }
- });
- $client->send();
-}
+server("proxy.inc", function($port) {
+ foreach (http\Client::getAvailableDrivers() as $driver) {
+ $client = new http\Client($driver);
+ $client->enqueue(new http\Client\Request("GET", "http://localhost:$port"), function($response) {
+ echo "R\n";
+ if (!($response instanceof http\Client\Response)) {
+ var_dump($response);
+ }
+ });
+ $client->send();
+ }
+});
?>
Done
--SKIPIF--
<?php
include "skipif.inc";
-skip_online_test();
+skip_client_test();
?>
--FILE--
<?php
+
+include "helper/server.inc";
+
echo "Test\n";
function response($response) {
return true;
}
-$request = new http\Client\Request("GET", "http://www.example.org");
-
-foreach (http\Client::getAvailableDrivers() as $driver) {
- $client = new http\Client($driver);
- for ($i=0; $i < 2; ++ $i) {
- $client->enqueue($request, "response");
- $client->send();
- try {
- $client->dequeue($request);
- } catch (Exception $e) {
- echo $e->getMessage(),"\n";
+server("proxy.inc", function($port) {
+ $request = new http\Client\Request("GET", "http://localhost:$port");
+
+ foreach (http\Client::getAvailableDrivers() as $driver) {
+ $client = new http\Client($driver);
+ for ($i=0; $i < 2; ++ $i) {
+ $client->enqueue($request, "response");
+ $client->send();
+ try {
+ $client->dequeue($request);
+ } catch (Exception $e) {
+ echo $e->getMessage(),"\n";
+ }
}
}
-}
+});
?>
Done
--SKIPIF--
<?php
include "skipif.inc";
-skip_online_test();
+skip_client_test();
?>
--FILE--
<?php
+
+include "helper/server.inc";
+
echo "Test\n";
function response($response) {
}
}
-$request = new http\Client\Request("GET", "http://www.example.org");
-
-foreach (http\Client::getAvailableDrivers() as $driver) {
- $client = new http\Client($driver);
- for ($i=0; $i < 2; ++ $i) {
- $client->requeue($request, "response");
- $client->send();
+server("proxy.inc", function($port) {
+ $request = new http\Client\Request("GET", "http://localhost:$port");
+
+ foreach (http\Client::getAvailableDrivers() as $driver) {
+ $client = new http\Client($driver);
+ for ($i=0; $i < 2; ++ $i) {
+ $client->requeue($request, "response");
+ $client->send();
+ }
}
-}
+});
?>
Done
--SKIPIF--
<?php
include "skipif.inc";
-skip_online_test();
+skip_client_test();
?>
--FILE--
<?php
-echo "Test\n";
-$request = new http\Client\Request("GET", "http://www.example.org");
+include "helper/server.inc";
+
+echo "Test\n";
-foreach (http\Client::getAvailableDrivers() as $driver) {
- $client = new http\Client($driver);
- $client->enablePipelining(true);
- $client->enableEvents(true);
+server("pipeline.inc", function($port, $stdin) {
+ fputs($stdin, "2\n");
+ $request = new http\Client\Request("GET", "http://localhost:$port");
+
+ $client = new http\Client();
+ $client->configure(array("pipelining" => true, "use_eventloop" => true));
+
$client->enqueue($request);
+ $client->send();
+
$client->enqueue(clone $request);
$client->enqueue(clone $request);
-
+
$client->send();
-
+
while ($client->getResponse()) {
echo "R\n";
}
-}
+});
?>
Done
<?php
include "skipif.inc";
skip_online_test();
+skip_client_test();
?>
--FILE--
<?php
<?php
include "skipif.inc";
skip_online_test();
+skip_client_test();
?>
--FILE--
<?php
<?php
include "skipif.inc";
skip_online_test();
+skip_client_test();
?>
--FILE--
<?php
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
<?php
include "skipif.inc";
skip_online_test();
+skip_client_test();
?>
--FILE--
<?php
-
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()
);
$ti = (array) $client->getTransferInfo($req);
var_dump(array_key_exists("ssl_engines", $ti));
-var_dump(0 < count($ti["ssl_engines"]));
+var_dump(0 < count($ti["ssl_engines"] || $ti["tls_session"]["backend"] != "openssl"));
?>
Done
--EXPECTF--
--SKIPIF--
<?php
include "skipif.inc";
-skip_online_test();
+skip_client_test();
?>
--FILE--
<?php
+include "helper/server.inc";
+
echo "Test\n";
class Client extends http\Client {
}
}
-$client = new Client;
-$client->attach($o1 = new ProgressObserver1);
-$client->attach($o2 = new ProgressObserver2);
-$client->attach(
- $o3 = new CallbackObserver(
- function ($c, $r) {
- $p = (array) $c->getProgressInfo($r);
- var_dump(array_key_exists("started", $p));
- var_dump(array_key_exists("finished", $p));
- var_dump(array_key_exists("dlnow", $p));
- var_dump(array_key_exists("ulnow", $p));
- var_dump(array_key_exists("dltotal", $p));
- var_dump(array_key_exists("ultotal", $p));
- var_dump(array_key_exists("info", $p));
- }
- )
-);
-
-$client->enqueue(new http\Client\Request("GET", "http://www.example.com/"))->send();
-var_dump(1 === preg_match("/(\.-)+/", $client->pi));
-var_dump(3 === count($client->getObservers()));
-$client->detach($o1);
-var_dump(2 === count($client->getObservers()));
-$client->detach($o2);
-var_dump(1 === count($client->getObservers()));
-$client->detach($o3);
-var_dump(0 === count($client->getObservers()));
+server("proxy.inc", function($port) {
+ $client = new Client;
+ $client->attach($o1 = new ProgressObserver1);
+ $client->attach($o2 = new ProgressObserver2);
+ $client->attach(
+ $o3 = new CallbackObserver(
+ function ($c, $r) {
+ $p = (array) $c->getProgressInfo($r);
+ var_dump(array_key_exists("started", $p));
+ var_dump(array_key_exists("finished", $p));
+ var_dump(array_key_exists("dlnow", $p));
+ var_dump(array_key_exists("ulnow", $p));
+ var_dump(array_key_exists("dltotal", $p));
+ var_dump(array_key_exists("ultotal", $p));
+ var_dump(array_key_exists("info", $p));
+ }
+ )
+ );
+
+ $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/"))->send();
+ var_dump(1 === preg_match("/(\.-)+/", $client->pi));
+ var_dump(3 === count($client->getObservers()));
+ $client->detach($o1);
+ var_dump(2 === count($client->getObservers()));
+ $client->detach($o2);
+ var_dump(1 === count($client->getObservers()));
+ $client->detach($o3);
+ var_dump(0 === count($client->getObservers()));
+
+});
?>
Done
--SKIPIF--
<?php
include "skipif.inc";
+skip_client_test();
?>
--FILE--
<?php
--SKIPIF--
<?php
include "skipif.inc";
-try {
- $client = new http\Client;
- if (!$client->enableEvents())
- throw new Exception("need events support");
-} catch (Exception $e) {
- die("skip ".$e->getMessage());
-}
-skip_online_test();
+skip_client_test();
?>
--FILE--
<?php
-echo "Test\n";
-
-$client1 = new http\Client;
-$client2 = new http\Client;
-
-$client1->enableEvents();
-$client2->enableEvents();
-$client1->enqueue(new http\Client\Request("GET", "http://www.google.ca/"));
-$client2->enqueue(new http\Client\Request("GET", "http://www.google.co.uk/"));
+include "helper/server.inc";
-$client1->send();
-
-if (($r = $client1->getResponse())) {
- var_dump($r->getTransferInfo("response_code"));
-}
-if (($r = $client2->getResponse())) {
- var_dump($r->getTransferInfo("response_code"));
-}
+echo "Test\n";
+server("proxy.inc", function($port) {
+ $client1 = new http\Client;
+ $client2 = new http\Client;
+
+ $client1->configure(array("use_eventloop" => true));
+ $client2->configure(array("use_eventloop" => true));
+
+ $client1->enqueue(new http\Client\Request("GET", "http://localhost:$port/"));
+ $client2->enqueue(new http\Client\Request("GET", "http://localhost:$port/"));
+
+ $client1->send();
+
+ if (($r = $client1->getResponse())) {
+ var_dump($r->getTransferInfo("response_code"));
+ }
+ if (($r = $client2->getResponse())) {
+ var_dump($r->getTransferInfo("response_code"));
+ }
+
+});
?>
DONE
--EXPECT--
--SKIPIF--
<?php
include "skipif.inc";
-try {
- $client = new http\Client;
- if (!$client->enableEvents())
- throw new Exception("need events support");
-} catch (Exception $e) {
- die("skip ".$e->getMessage());
-}
-skip_online_test();
+skip_client_test();
?>
--FILE--
<?php
-echo "Test\n";
-$request = new http\Client\Request("GET", "http://www.example.org/");
+include "helper/server.inc";
+
+echo "Test\n";
-foreach (http\Client::getAvailableDrivers() as $driver) {
- $client = new http\Client($driver);
- $client->enableEvents(true);
- $client->enqueue($request);
+server("proxy.inc", function($port) {
+ $request = new http\Client\Request("GET", "http://localhost:$port/");
- while ($client->once()) {
- $client->wait(.1);
- }
+ foreach (http\Client::getAvailableDrivers() as $driver) {
+ $client = new http\Client($driver);
+ $client->configure(array("use_eventloop" => true));
+ $client->enqueue($request);
+
+ while ($client->once()) {
+ $client->wait(.1);
+ }
- if (!$client->getResponse()) {
- var_dump($client);
+ if (!$client->getResponse()) {
+ var_dump($client);
+ }
}
-}
+});
?>
Done
--EXPECT--
--- /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(array("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
--- /dev/null
+--TEST--
+client pipelining
+--SKIPIF--
+<?php
+include "skipif.inc";
+skip_client_test();
+?>
+--FILE--
+<?php
+
+include "helper/server.inc";
+
+echo "Test\n";
+
+server("pipeline.inc", function($port, $stdin, $stdout, $stderr) {
+ /* tell the server we're about to send 3 pipelined messages */
+ fputs($stdin, "3\n");
+
+ $client = new http\Client(null);
+ $client->configure(array("pipelining" => true, "max_host_connections" => 0));
+
+ /* this is just to let curl know the server may be capable of pipelining */
+ $client->enqueue(new http\Client\Request("GET", "http://localhost:$port"));
+ $client->send();
+
+ $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/1"));
+ $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/2"));
+ $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/3"));
+ $client->send();
+
+ while (($response = $client->getResponse())) {
+ echo $response;
+ }
+});
+
+?>
+===DONE===
+--EXPECT--
+Test
+HTTP/1.1 200 OK
+X-Req: /3
+Etag: ""
+X-Original-Transfer-Encoding: chunked
+HTTP/1.1 200 OK
+X-Req: /2
+Etag: ""
+X-Original-Transfer-Encoding: chunked
+HTTP/1.1 200 OK
+X-Req: /1
+Etag: ""
+X-Original-Transfer-Encoding: chunked
+HTTP/1.1 200 OK
+X-Req: /
+Etag: ""
+X-Original-Transfer-Encoding: chunked
+===DONE===
--- /dev/null
+--TEST--
+client proxy - send proxy headers for a proxy request
+--SKIPIF--
+<?php
+include "skipif.inc";
+skip_client_test();
+?>
+--FILE--
+<?php
+
+include "helper/server.inc";
+
+echo "Test\n";
+
+server("proxy.inc", function($port, $stdin, $stdout, $stderr) {
+ echo "Server on port $port\n";
+
+ $c = new http\Client;
+ $r = new http\Client\Request("GET", "http://www.example.com/");
+ $r->setOptions(array(
+ "timeout" => 10,
+ "proxytunnel" => true,
+ "proxyheader" => array("Hello" => "there!"),
+ "proxyhost" => "localhost",
+ "proxyport" => $port,
+ ));
+ try {
+ $c->enqueue($r)->send();
+ } catch (Exception $e) {
+ echo $e;
+ }
+ echo $c->getResponse()->getBody();
+});
+
+?>
+===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!
+===DONE===
--- /dev/null
+--TEST--
+client proxy - don't send proxy headers for a standard request
+--SKIPIF--
+<?php
+include "skipif.inc";
+skip_client_test();
+?>
+--FILE--
+<?php
+
+include "helper/server.inc";
+
+echo "Test\n";
+
+server("proxy.inc", function($port, $stdin, $stdout, $stderr) {
+ 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();
+ 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: */*
+===DONE===
--- /dev/null
+--TEST--
+client cookies
+--SKIPIF--
+<?php
+include "skipif.inc";
+skip_client_test();
+?>
+--FILE--
+<?php
+
+include "helper/server.inc";
+
+echo "Test\n";
+
+$tmpfile = tempnam(sys_get_temp_dir(), "cookie.");
+$request = new http\Client\Request("GET", "http://localhost");
+$request->setOptions(array("cookiestore" => $tmpfile));
+
+server("cookie.inc", function($port) use($request) {
+ $request->setOptions(array("port" => $port));
+ $client = new http\Client;
+ echo $client->requeue($request)->send()->getResponse();
+ echo $client->requeue($request)->send()->getResponse();
+ echo $client->requeue($request)->send()->getResponse();
+});
+
+server("cookie.inc", function($port) use($request) {
+ $request->setOptions(array("port" => $port));
+ $client = new http\Client;
+ echo $client->requeue($request)->send()->getResponse();
+ echo $client->requeue($request)->send()->getResponse();
+ echo $client->requeue($request)->send()->getResponse();
+});
+
+server("cookie.inc", function($port) use($request) {
+ $request->setOptions(array("port" => $port, "cookiesession" => true));
+ $client = new http\Client;
+ echo $client->requeue($request)->send()->getResponse();
+ echo $client->requeue($request)->send()->getResponse();
+ echo $client->requeue($request)->send()->getResponse();
+});
+
+server("cookie.inc", function($port) use($request) {
+ $request->setOptions(array("port" => $port, "cookiesession" => false));
+ $client = new http\Client;
+ echo $client->requeue($request)->send()->getResponse();
+ echo $client->requeue($request)->send()->getResponse();
+ echo $client->requeue($request)->send()->getResponse();
+});
+
+unlink($tmpfile);
+
+?>
+===DONE===
+--EXPECT--
+Test
+HTTP/1.1 200 OK
+Set-Cookie: counter=1;
+Etag: ""
+X-Original-Transfer-Encoding: chunked
+HTTP/1.1 200 OK
+Set-Cookie: counter=2;
+Etag: ""
+X-Original-Transfer-Encoding: chunked
+HTTP/1.1 200 OK
+Set-Cookie: counter=3;
+Etag: ""
+X-Original-Transfer-Encoding: chunked
+HTTP/1.1 200 OK
+Set-Cookie: counter=4;
+Etag: ""
+X-Original-Transfer-Encoding: chunked
+HTTP/1.1 200 OK
+Set-Cookie: counter=5;
+Etag: ""
+X-Original-Transfer-Encoding: chunked
+HTTP/1.1 200 OK
+Set-Cookie: counter=6;
+Etag: ""
+X-Original-Transfer-Encoding: chunked
+HTTP/1.1 200 OK
+Set-Cookie: counter=1;
+Etag: ""
+X-Original-Transfer-Encoding: chunked
+HTTP/1.1 200 OK
+Set-Cookie: counter=1;
+Etag: ""
+X-Original-Transfer-Encoding: chunked
+HTTP/1.1 200 OK
+Set-Cookie: counter=1;
+Etag: ""
+X-Original-Transfer-Encoding: chunked
+HTTP/1.1 200 OK
+Set-Cookie: counter=2;
+Etag: ""
+X-Original-Transfer-Encoding: chunked
+HTTP/1.1 200 OK
+Set-Cookie: counter=3;
+Etag: ""
+X-Original-Transfer-Encoding: chunked
+HTTP/1.1 200 OK
+Set-Cookie: counter=4;
+Etag: ""
+X-Original-Transfer-Encoding: chunked
+===DONE===
--- /dev/null
+--TEST--
+client http2
+--SKIPIF--
+<?php
+include "skipif.inc";
+skip_client_test();
+skip_http2_test();
+?>
+--FILE--
+<?php
+
+include "helper/server.inc";
+
+echo "Test\n";
+
+nghttpd(function($port) {
+ $client = new http\Client;
+ $client->setOptions([
+ "protocol" => http\Client\Curl\HTTP_VERSION_2_0,
+ "ssl" => [
+ "cainfo" => __DIR__."/helper/http2.crt",
+ ]
+ ]);
+ $client->enqueue(new http\Client\Request("GET", "https://localhost:$port"));
+ echo $client->send()->getResponse();
+});
+
+?>
+===DONE===
+--EXPECTF--
+Test
+HTTP/2.0 200
+%a
+
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>HTTP2</title>
+ </head>
+ <body>
+ Nothing to see here.
+ </body>
+</html>
+===DONE===
--- /dev/null
+--TEST--
+client available options and configuration
+--SKIPIF--
+<?php
+include "skipif.inc";
+skip_client_test();
+?>
+--FILE--
+<?php
+
+echo "Test\n";
+
+$client = new http\Client;
+if (($opt = $client->getOptions())) {
+ var_dump($options);
+}
+$client->setOptions($avail = $client->getAvailableOptions());
+$opt = $client->getOptions();
+
+foreach ($avail as $k => $v) {
+ if (is_array($v)) {
+ $oo = $opt[$k];
+ foreach ($v as $kk => $vv) {
+ if (isset($vv) && $oo[$kk] !== $vv) {
+ var_dump(array($kk => array($vv, $oo[$kk])));
+ }
+ }
+ } else if (isset($v) && $opt[$k] !== $v) {
+ var_dump(array($k => array($v, $opt[$k])));
+ }
+}
+var_dump($client === $client->configure($client->getAvailableConfiguration()));
+
+?>
+===DONE===
+--EXPECT--
+Test
+bool(true)
+===DONE===
--- /dev/null
+--TEST--
+client deprecated methods
+--SKIPIF--
+<?php
+include "skipif.inc";
+skip_client_test();
+?>
+--FILE--
+<?php
+
+echo "Test\n";
+
+$client = new http\Client;
+$client->enableEvents(false);
+$client->enablePipelining(false);
+
+?>
+===DONE===
+--EXPECTF--
+Test
+
+Deprecated: Function http\Client::enableEvents() is deprecated in %sclient024.php on line %d
+
+Deprecated: Function http\Client::enablePipelining() is deprecated in %sclient024.php on line %d
+===DONE===
--- /dev/null
+--TEST--
+client seek
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+
+include "helper/server.inc";
+
+echo "Test\n";
+
+server("proxy.inc", function($port) {
+ $client = new http\Client;
+ $request = new http\Client\Request("PUT", "http://localhost:$port");
+ $request->setOptions(array("resume" => 1, "expect_100_timeout" => 0));
+ $request->getBody()->append("123");
+ echo $client->enqueue($request)->send()->getResponse();
+});
+
+?>
+===DONE===
+--EXPECTF--
+Test
+HTTP/1.1 200 OK
+Accept-Ranges: bytes
+Etag: "%x"
+X-Original-Transfer-Encoding: chunked
+Content-Length: %d
+
+PUT / HTTP/1.1
+Content-Range: bytes 1-2/3
+User-Agent: %s
+Host: localhost:%d
+Accept: */*
+Content-Length: 3
+Expect: 100-continue
+X-Original-Content-Length: 3
+
+23===DONE===
--- /dev/null
+--TEST--
+client stream 128M
+--SKIPIF--
+<?php
+include "skipif.inc";
+skip_client_test();
+?>
+--FILE--
+<?php
+
+include "helper/server.inc";
+
+echo "Test\n";
+
+server("proxy.inc", function($port) {
+ $client = new http\Client;
+ $request = new http\Client\Request("PUT", "http://localhost:$port");
+ $request->setContentType("application/octet-stream");
+ for ($i = 0, $data = str_repeat("a",1024); $i < 128*1024; ++$i) {
+ $request->getBody()->append($data);
+ }
+ $request->setOptions(array("timeout" => 10, "expect_100_timeout" => 0));
+ $client->enqueue($request);
+ $client->send();
+ var_dump($client->getResponse()->getHeaders());
+});
+
+?>
+===DONE===
+--EXPECTF--
+Test
+array(5) {
+ ["Accept-Ranges"]=>
+ string(5) "bytes"
+ ["Etag"]=>
+ string(%d) "%s"
+ ["Last-Modified"]=>
+ string(%d) "%s"
+ ["X-Original-Transfer-Encoding"]=>
+ string(7) "chunked"
+ ["Content-Length"]=>
+ int(134217%d%d%d)
+}
+===DONE===
<?php
include "skipif.inc";
skip_online_test();
+skip_client_test();
?>
--FILE--
<?php
<?php
include "skipif.inc";
skip_online_test();
+skip_client_test();
?>
--FILE--
<?php
--SKIPIF--
<?php
include "skipif.inc";
-skip_online_test();
+skip_client_test();
?>
--FILE--
<?php
-echo "Test\n";
-$request = new http\Client\Request("GET", "http://www.example.org");
+include "helper/server.inc";
+
+echo "Test\n";
-foreach (http\Client::getAvailableDrivers() as $driver) {
- $client = new http\Client($driver);
- $response = $client->enqueue($request)->send()->getResponse();
- var_dump($response->getTransferInfo("response_code"));
- var_dump(count((array)$response->getTransferInfo()));
-}
+server("proxy.inc", function($port) {
+ $request = new http\Client\Request("GET", "http://localhost:$port");
+
+ foreach (http\Client::getAvailableDrivers() as $driver) {
+ $client = new http\Client($driver);
+ $response = $client->enqueue($request)->send()->getResponse();
+ var_dump($response->getTransferInfo("response_code"));
+ var_dump(count((array)$response->getTransferInfo()));
+ }
+});
?>
Done
--EXPECTREGEX--
--- /dev/null
+PUT / HTTP/1.1
+User-Agent: PECL_HTTP/2.3.0dev PHP/5.6.6-dev libcurl/7.41.0-DEV
+Host: localhost:8000
+Accept: */*
+Expect: 100-continue
+Content-Length: 3
+Content-Range: bytes 1-2/3
+
+23
\ No newline at end of file
--- /dev/null
+--TEST--
+env request cookie
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--COOKIE--
+foo=bar;bar=123
+--FILE--
+<?php
+echo "Test\n";
+
+$r = new http\Env\Request;
+var_dump($r->getCookie()->toArray());
+var_dump($r->getCookie("foo", "s"));
+var_dump($r->getCookie("bar", "i"));
+var_dump($r->getCookie("baz", "b", true));
+
+?>
+DONE
+--EXPECT--
+Test
+array(2) {
+ ["foo"]=>
+ string(3) "bar"
+ ["bar"]=>
+ string(3) "123"
+}
+string(3) "bar"
+int(123)
+bool(true)
+DONE
ob_end_flush();
$r->send();
+?>
--EXPECTHEADERS--
Accept-Ranges: bytes
Cache-Control: public,must-revalidate,max-age=0
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(array("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===
--- /dev/null
+--TEST--
+env response don't generate stat based etag for temp stream
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$b = new http\Message\Body(fopen("php://temp/maxmemory:8", "r+"));
+$b->append("1234567890\n");
+
+$r = new http\Env\Response;
+$r->setBody($b);
+$r->send(STDOUT);
+
+?>
+===DONE===
+--EXPECTF--
+Test
+HTTP/1.1 200 OK
+Accept-Ranges: bytes
+ETag: "%x"
+Last-Modified: %s
+Transfer-Encoding: chunked
+
+b
+1234567890
+
+0
+
+===DONE===
--- /dev/null
+--TEST--
+env response cookie
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+
+$r = new http\Env\Response;
+$c = new http\Cookie;
+$c->addCookie("foo","bar");
+$c->setMaxAge(60);
+$r->setCookie($c);
+$r->setCookie("baz");
+$r->setCookie(123);
+$r->send(STDOUT);
+
+?>
+--EXPECT--
+HTTP/1.1 200 OK
+Set-Cookie: foo=bar; max-age=60;
+Set-Cookie: baz=1;
+Set-Cookie: 123=1;
+ETag: ""
+Transfer-Encoding: chunked
+
+0
+
--EXPECTF--
Test
-Warning: http\Header::parse(): Could not parse headers in %s on line %d
+Warning: http\Header::parse(): Failed to parse headers: unexpected end of line at pos 4 of 'wass\nup' in %s on line %d
Done
--- /dev/null
+--TEST--
+header parser
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$headers = array(
+ "One: ","header\n",
+ "Two: header\n\tlines\n",
+ "Three",": header\n lines\n here\n",
+ "More: than one header\n",
+ "More: ", "than: ", "you: ", "expect\n",
+ "\n",
+);
+
+$states = array(-1=>"FAILURE",0=>"START","KEY","VALUE","VALUE_EX","HEADER_DONE","DONE");
+$parser = new http\Header\Parser;
+do {
+ $state = $parser->parse($part = array_shift($headers),
+ $headers ? 0 : http\Header\Parser::CLEANUP,
+ $result);
+ printf("%2\$-32s | %1\$s\n", $states[$state], addcslashes($part, "\r\n\t\0"));
+} while ($headers && $state !== http\Header\Parser::STATE_FAILURE);
+
+var_dump($result);
+
+?>
+===DONE===
+--EXPECT--
+Test
+One: | VALUE
+header\n | VALUE_EX
+Two: header\n\tlines\n | VALUE_EX
+Three | KEY
+: header\n lines\n here\n | VALUE_EX
+More: than one header\n | VALUE_EX
+More: | VALUE
+than: | VALUE
+you: | VALUE
+expect\n | VALUE_EX
+\n | DONE
+array(4) {
+ ["One"]=>
+ string(6) "header"
+ ["Two"]=>
+ string(12) "header lines"
+ ["Three"]=>
+ string(17) "header lines here"
+ ["More"]=>
+ array(2) {
+ [0]=>
+ string(15) "than one header"
+ [1]=>
+ string(17) "than: you: expect"
+ }
+}
+===DONE===
--- /dev/null
+--TEST--
+header parser errors
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$headers = array(
+ "Na\0me: value",
+ "Na\nme: value",
+ "Name:\0value",
+ "Name:\nvalue",
+ "Name: val\0ue",
+ "Name: value\0",
+);
+
+foreach ($headers as $header) {
+ $parsed = null;
+ $parser = new http\Header\Parser;
+ var_dump($parser->parse($header, http\Header\Parser::CLEANUP, $parsed), $parsed);
+}
+?>
+===DONE===
+--EXPECTF--
+Test
+
+Warning: http\Header\Parser::parse(): Failed to parse headers: unexpected character '\000' at pos 2 of 'Na\000me' in %sheaderparser002.php on line %d
+int(-1)
+array(0) {
+}
+
+Warning: http\Header\Parser::parse(): Failed to parse headers: unexpected end of line at pos 2 of 'Na\nme: value' in %sheaderparser002.php on line %d
+int(-1)
+array(0) {
+}
+
+Warning: http\Header\Parser::parse(): Failed to parse headers: unexpected character '\000' at pos 0 of '\000value' in %sheaderparser002.php on line %d
+int(-1)
+array(0) {
+}
+
+Warning: http\Header\Parser::parse(): Failed to parse headers: unexpected end of input at pos 5 of 'value' in %sheaderparser002.php on line %d
+int(-1)
+array(0) {
+}
+
+Warning: http\Header\Parser::parse(): Failed to parse headers: unexpected character '\000' at pos 3 of 'val\000ue' in %sheaderparser002.php on line %d
+int(-1)
+array(0) {
+}
+
+Warning: http\Header\Parser::parse(): Failed to parse headers: unexpected character '\000' at pos 5 of 'value\000' in %sheaderparser002.php on line %d
+int(-1)
+array(0) {
+}
+===DONE===
\ No newline at end of file
--- /dev/null
+--TEST--
+header parser with nonblocking stream
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$parser = new http\Header\Parser;
+$socket = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
+stream_set_blocking($socket[0], 0);
+
+$headers = array(
+"GET / HTTP/1.1\n",
+"Host: localhost","\n",
+"Content","-length: 3\n",
+"\n",
+);
+
+while ($headers) {
+ $line = array_shift($headers);
+ $parser->stream($socket[0], 0, $hdrs);
+ fwrite($socket[1], $line);
+ var_dump($parser->getState());
+ var_dump($parser->stream($socket[0], 0, $hdrs));
+}
+
+var_dump($hdrs);
+
+?>
+DONE
+--EXPECT--
+Test
+int(0)
+int(1)
+int(1)
+int(2)
+int(2)
+int(3)
+int(3)
+int(1)
+int(1)
+int(3)
+int(3)
+int(5)
+array(2) {
+ ["Host"]=>
+ string(9) "localhost"
+ ["Content-Length"]=>
+ string(1) "3"
+}
+DONE
--- /dev/null
+<?php
+
+include "server.inc";
+
+serve(function($client) {
+ $request = new http\Message($client, false);
+ $cookies = new http\Cookie($request->getHeader("cookie"));
+ $response = new http\Env\Response;
+ $response->setCookie($cookies->setCookie("counter", $cookies->getCookie("counter")+1));
+ $response->send($client);
+});
--- /dev/null
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>HTTP2</title>
+ </head>
+ <body>
+ Nothing to see here.
+ </body>
+</html>
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDNzCCAh+gAwIBAgIJAKOw1awbt7aIMA0GCSqGSIb3DQEBCwUAMDIxCzAJBgNV
+BAYTAkFUMQ8wDQYDVQQKDAZQSFAgUUExEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0x
+NTAyMTIxMjQ2NTRaFw0xNzExMDcxMjQ2NTRaMDIxCzAJBgNVBAYTAkFUMQ8wDQYD
+VQQKDAZQSFAgUUExEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAMxa+A6xEKQOYme55nQyu0qpvvGB4c4wGBNa6X6YAEzE
+Hc19Nbix02UZWQgHM1dmBhbVDW3stO42CQmcViIKfAy5R1A9UZjUcn9nQpaxL/sp
+9LrrCANyljISXS40QcBvhCzmcUvDWBRhVTGjV+QTaYmmaM+8hJJJM6W7ByIP22Zv
+jHoAnzTx9sjs+OMyWmjYT9pWv6aE+u5iSC8bn3B0GgtfPwoPqDF8ePxIMBpmtbk7
+JqXFzHxTVywkypnsF34XK94QjpvRcFGSg/Dc1TopJG2Wfq8hxwtoKerSlL5tyKy0
+ZrltxCiLkfVZixwNZEqkg7jPSUvLS299mBrwy+ikmr8CAwEAAaNQME4wHQYDVR0O
+BBYEFDiHynoXXjMChfYhc1k7TNtU8ey0MB8GA1UdIwQYMBaAFDiHynoXXjMChfYh
+c1k7TNtU8ey0MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAGD9GERC
+uJv+oHfMwkNkDV5ZB4F+SQPzXVxDx+rJm1aGJs9PcwPNiV5GweXbvgcnvRAL4h8h
+uv3MLQPgVOq0iqp1QPFCoUXxbYYjYzi9FVbR/154sg0uWEHElU2e3fSjcinNRfXD
+12232k6HNwSeeZUFQqn2fuk+cae5BsYT8GfsyMq5EfPtG2d8IG+Ji4eEOJeKu4gl
+Y33yQnHhw6QKbx8vWaDpZK8qtpapCtLrTtw/nRhX5YV6kMGK+htQUK1etV2O0VQQ
+OtDrhOWWaDxQULsHIvCMgTTnZqNQD+Xz4MBm3PGEGi/QUsrEaSQYppb8xxQKZU4X
+MyTh7rO5TdNSXmo=
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAzFr4DrEQpA5iZ7nmdDK7Sqm+8YHhzjAYE1rpfpgATMQdzX01
+uLHTZRlZCAczV2YGFtUNbey07jYJCZxWIgp8DLlHUD1RmNRyf2dClrEv+yn0uusI
+A3KWMhJdLjRBwG+ELOZxS8NYFGFVMaNX5BNpiaZoz7yEkkkzpbsHIg/bZm+MegCf
+NPH2yOz44zJaaNhP2la/poT67mJILxufcHQaC18/Cg+oMXx4/EgwGma1uTsmpcXM
+fFNXLCTKmewXfhcr3hCOm9FwUZKD8NzVOikkbZZ+ryHHC2gp6tKUvm3IrLRmuW3E
+KIuR9VmLHA1kSqSDuM9JS8tLb32YGvDL6KSavwIDAQABAoIBAQCzUdAB9FYJ36Vy
+J6qVpD69EZ7ABZzDdWhq84eY0oDQ2/ba7lhJraE2QbviU481zgzh1CponyFVNo1P
+paPfUxvvflWZj3Ueiq2+JjpESU81MmfR7ZOmktJBNeQWOzzHRBPT4pLgTJXprE85
+s3/YX0BozWGDiIU8aIetkiR8OzXm97+BrJWiPFl733dGnHvfpHAZu/PwKZc2+8ft
+CnQw4GHRhTBWCVNj29HLwm+CA66zQqiAXItgijWMKzs+9ciPn+aCsCnZDNF+o1zs
+4pWt60CtIJQtD3r3rSRy7nBaCKcTrr/JU3FvwqKdunuUBUsuYeSaMBokW67kpVzS
+dO9L9p6BAoGBAP+pvcAd8qfC1WIrn9vka3aK25ulbYSCae3YaWmABc93INJPBMvO
+GrcUuaLKiQC1oou+E64vGyJ9MeEFwxh2zbvU75a9ezeKAajgaq92ciMX2QqREh0N
+IntNOc4w7eB6BK8NpaDXNvTtxNWMvlYyhVN0M7FVQRmYJfCJdnTZAkzvAoGBAMyf
+6rvWuc/wmIcAtBVe+jIeQ69EJJ/Idcjk0JUm6lFECAAraPpsCglha+wTHWWRQZ9u
+pPqBnb7QPjevU+3bZHnMvGoEzd5F4Rw74J+p5EZeMUJpuKmon7Ekzemcb0DV+qX9
+NorB781D2Z0MG9SCptKyKpN5TPHTjGz4BB3mLC8xAoGAdq99/ynn9ClmleRameI4
+YRelS2RIqzM/qcLFbMyZ5e4PtpIoT9SmYkekxgXwA/xOMUFUMZB8sE4eUbAzGbBN
+Yd1APGJKSUYv7w3/eOUrp07y2wzts77dOxBmvWnJhGQguINFWJ2QTbPzpI9p7OoX
+Kt7PAIvrZM5VDo1CCIyVnNECgYEAgLmpZXlzcwicK3GZ2EfjhVvcoIlxsMLetf6b
+6PiON4lgrxqf88m7lqMezWhI+fgjHDTyvFSF89/1A/rcBaoazzSo4tka2VWEg8p3
+SHoMDOh8fJcdgD2AGGRa1TeAFX2HLJzajvfp72tbnpxbdZircah7eEK60PaQRIzR
+qi1+ZkECgYEAi7GkO7Ey98DppLnt7567veQoEj0u8ixTlCyJ4V278nHR5+6eAZP5
+PfdZVC3MtKGLnxrrPTVUy5wU0hR6Gk9EVDmrAF8TgJdnZFlBK7X23eWZ0q4qO/eO
+3xIi+UrNwLag8BjYOr32nP/i+F+TLikgRIFR4oiZjk867+ofkTXmNFA=
+-----END RSA PRIVATE KEY-----
--- /dev/null
+<?php
+
+include "server.inc";
+
+function respond($client, $msg) {
+ $r = new http\Env\Response;
+ $r->setEnvRequest($msg)
+ ->setHeader("X-Req", $msg->getRequestUrl())
+ ->send($client);
+}
+
+serve(function($client) {
+ $count = trim(fgets(STDIN));
+
+ /* the peek message */
+ respond($client, new http\Message($client, false));
+
+ /* pipelined messages */
+ $req = array();
+ for ($i=0; $i < $count; ++ $i) {
+ $req[] = new http\Message($client, false);
+ }
+ foreach ($req as $msg) {
+ respond($client, $msg);
+ }
+});
--- /dev/null
+<?php
+
+include "server.inc";
+
+serve(function($client) {
+ /* 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->setEnvRequest($request);
+ $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;
+ /* avoid OOM with $response->getBody()->append($request); */
+ $request->toStream($response->getBody()->getResource());
+ $response->send($client);
+});
--- /dev/null
+<?php
+
+// PHP-5.3
+if (!defined("PHP_BINARY")) {
+ define("PHP_BINARY", PHP_BINDIR.DIRECTORY_SEPARATOR."php");
+}
+
+function serve($cb) {
+ foreach (range(8000, 9000) as $port) {
+ if (($server = @stream_socket_server("tcp://localhost:$port"))) {
+ fprintf(STDERR, "%s\n", $port);
+ do {
+ $R = array($server); $W = array(); $E = array();
+ $select = stream_select($R, $E, $E, 0, 10000);
+ if ($select && ($client = stream_socket_accept($server, 1))) {
+ if (getenv("PHP_HTTP_TEST_SSL")) {
+ stream_socket_enable_crypto($client, true, STREAM_CRYPTO_METHOD_SSLv23_SERVER);
+ }
+ try {
+ while (!feof($client)) {
+ $cb($client);
+ }
+ } catch (Exception $ex) {
+ /* ignore disconnect */
+ if ($ex->getMessage() !== "Empty message received from stream") {
+ fprintf(STDERR, "%s\n", $ex);
+ }
+ break;
+ }
+ }
+ } while ($select !== false);
+ return;
+ }
+ }
+}
+
+function server($handler, $cb) {
+ proc(PHP_BINARY, array(__DIR__."/$handler"), $cb);
+}
+
+function nghttpd($cb) {
+ $spec = array(array("pipe","r"), array("pipe","w"), array("pipe","w"));
+ foreach (range(8000, 9000) as $port) {
+ $comm = "exec nghttpd -d html $port http2.key http2.crt";
+ if (($proc = proc_open($comm, $spec, $pipes, __DIR__))) {
+ $stdin = $pipes[0];
+ $stdout = $pipes[1];
+ $stderr = $pipes[2];
+
+ usleep(50000);
+ $status = proc_get_status($proc);
+
+ if (!$status["running"]) {
+ continue;
+ }
+
+ try {
+ $cb($port, $stdin, $stdout, $stderr);
+ } catch (Exception $e) {
+ echo $e,"\n";
+ }
+
+ proc_terminate($proc);
+
+ fpassthru($stderr);
+ fpassthru($stdout);
+ return;
+ }
+ }
+
+}
+
+function proc($bin, $args, $cb) {
+ $spec = array(array("pipe","r"), array("pipe","w"), array("pipe","w"));
+ $comm = escapeshellcmd($bin) . " ". implode(" ", array_map("escapeshellarg", $args));
+ if (($proc = proc_open($comm, $spec, $pipes, __DIR__))) {
+ $stdin = $pipes[0];
+ $stdout = $pipes[1];
+ $stderr = $pipes[2];
+
+ do {
+ $port = trim(fgets($stderr));
+ $R = array($stderr); $W = array(); $E = array();
+ } while (is_numeric($port) && stream_select($R, $W, $E, 0, 10000));
+
+ if (is_numeric($port)) {
+ try {
+ $cb($port, $stdin, $stdout, $stderr);
+ } catch (Exception $e) {
+ echo $e,"\n";
+ }
+ }
+
+ proc_terminate($proc);
+
+ fpassthru($stderr);
+ fpassthru($stdout);
+ }
+}
\ No newline at end of file
+++ /dev/null
---TEST--
-phpinfo
---SKIPIF--
-<?php
-include "skipif.inc";
-?>
---FILE--
-<?php
-echo "Test\n";
-phpinfo(INFO_MODULES);
-?>
-Done
---EXPECTF--
-Test
-%a
-HTTP Support => enabled
-Extension Version => 2.%s
-%a
-Done
--- /dev/null
+--TEST--
+invalid HTTP info
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+try {
+ var_dump(new http\Message("GET HTTP/1.1"));
+} catch (Exception $e) {
+ echo $e, "\n";
+}
+try {
+ var_dump(new http\Message("GET HTTP/1.123"));
+} catch (Exception $e) {
+ echo $e, "\n";
+}
+try {
+ var_dump(new http\Message("GETHTTP/1.1"));
+} catch (Exception $e) {
+ echo $e, "\n";
+}
+var_dump(new http\Message("GET / HTTP/1.1"));
+?>
+DONE
+--EXPECTF--
+exception 'http\Exception\BadMessageException' with message 'http\Message::__construct(): Failed to parse headers: unexpected character '\040' at pos 3 of 'GET HTTP/1.1'' in %s
+Stack trace:
+#0 %s: http\Message->__construct('GET HTTP/1.1')
+#1 {main}
+exception 'http\Exception\BadMessageException' with message 'http\Message::__construct(): Failed to parse headers: unexpected character '\040' at pos 3 of 'GET HTTP/1.123'' in %s
+Stack trace:
+#0 %s: http\Message->__construct('GET HTTP/1.123')
+#1 {main}
+exception 'http\Exception\BadMessageException' with message 'http\Message::__construct(): Failed to parse headers: unexpected character '\057' at pos 7 of 'GETHTTP/1.1'' %s
+Stack trace:
+#0 %s: http\Message->__construct('GETHTTP/1.1')
+#1 {main}
+object(http\Message)#%d (9) {
+ ["type":protected]=>
+ int(1)
+ ["body":protected]=>
+ NULL
+ ["requestMethod":protected]=>
+ string(3) "GET"
+ ["requestUrl":protected]=>
+ string(1) "/"
+ ["responseStatus":protected]=>
+ string(0) ""
+ ["responseCode":protected]=>
+ int(0)
+ ["httpVersion":protected]=>
+ string(3) "1.1"
+ ["headers":protected]=>
+ array(0) {
+ }
+ ["parentMessage":protected]=>
+ NULL
+}
+DONE
--- /dev/null
+--TEST--
+invalid HTTP info
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+
+echo "Test\n";
+
+function trap($cb) {
+ try {
+ $cb();
+ } catch (Exception $e) {
+ echo $e,"\n";
+ }
+}
+
+trap(function() {
+ echo new http\Message("HTTP/1.1 99 Apples in my Basket");
+});
+
+trap(function() {
+ echo new http\Message("CONNECT HTTP/1.1");
+});
+
+echo new http\Message("HTTP/1.1");
+echo new http\Message("CONNECT www.example.org:80 HTTP/1.1");
+
+?>
+===DONE===
+--EXPECTF--
+Test
+exception 'http\Exception\BadMessageException' with message 'http\Message::__construct(): Failed to parse headers: unexpected character '\057' at pos 4 of 'HTTP/1.1 99 Apples in my Basket'' in %sinfo002.php:%d
+Stack trace:
+#0 %sinfo002.php(%d): http\Message->__construct('HTTP/1.1 99 App...')
+#1 %sinfo002.php(%d): {closure}()
+#2 %sinfo002.php(%d): trap(Object(Closure))
+#3 {main}
+exception 'http\Exception\BadMessageException' with message 'http\Message::__construct(): Failed to parse headers: unexpected character '\040' at pos 7 of 'CONNECT HTTP/1.1'' in %sinfo002.php:%d
+Stack trace:
+#0 %sinfo002.php(%d): http\Message->__construct('CONNECT HTTP/1....')
+#1 %sinfo002.php(%d): {closure}()
+#2 %sinfo002.php(%d): trap(Object(Closure))
+#3 {main}
+HTTP/1.1 200
+CONNECT www.example.org:80 HTTP/1.1
+===DONE===
+++ /dev/null
---TEST--
-invalid HTTP info
---SKIPIF--
-<?php include "skipif.inc"; ?>
---FILE--
-<?php
-
-try {
- var_dump(new http\Message("GET HTTP/1.1"));
-} catch (Exception $e) {
- echo $e, "\n";
-}
-try {
- var_dump(new http\Message("GET HTTP/1.123"));
-} catch (Exception $e) {
- echo $e, "\n";
-}
-try {
- var_dump(new http\Message("GETHTTP/1.1"));
-} catch (Exception $e) {
- echo $e, "\n";
-}
-var_dump(new http\Message("GET / HTTP/1.1"));
-?>
-DONE
---EXPECTF--
-exception 'http\Exception\BadMessageException' with message 'Could not parse message: GET HTTP/1.1' in %s
-Stack trace:
-#0 %s: http\Message->__construct('GET HTTP/1.1')
-#1 {main}
-exception 'http\Exception\BadMessageException' with message 'Could not parse message: GET HTTP/1.123' in %s
-Stack trace:
-#0 %s: http\Message->__construct('GET HTTP/1.123')
-#1 {main}
-exception 'http\Exception\BadMessageException' with message 'Could not parse message: GETHTTP/1.1' in %s
-Stack trace:
-#0 %s: http\Message->__construct('GETHTTP/1.1')
-#1 {main}
-object(http\Message)#%d (9) {
- ["type":protected]=>
- int(1)
- ["body":protected]=>
- NULL
- ["requestMethod":protected]=>
- string(3) "GET"
- ["requestUrl":protected]=>
- string(1) "/"
- ["responseStatus":protected]=>
- string(0) ""
- ["responseCode":protected]=>
- int(0)
- ["httpVersion":protected]=>
- string(3) "1.1"
- ["headers":protected]=>
- array(0) {
- }
- ["parentMessage":protected]=>
- NULL
-}
-DONE
["Accept-Ranges"]=>
string(5) "bytes"
["Content-Length"]=>
- int(0)
+ string(1) "0"
["Vary"]=>
string(15) "Accept-Encoding"
["Connection"]=>
GET /default/empty.txt HTTP/1.1
Host: localhost
Connection: close
-Content-Length: 0
HTTP/1.1 200 OK
Date: Thu, 26 Aug 2010 09:55:09 GMT
Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6
Etag: "2002a-0-48549d615a35c"
Accept-Ranges: bytes
Vary: Accept-Encoding
-Content-Length: 0
Connection: close
Content-Type: text/plain
X-Original-Content-Length: 20
bool(true)
int(200)
string(2) "OK"
-array(11) {
+array(10) {
["Date"]=>
string(29) "Thu, 26 Aug 2010 09:55:09 GMT"
["Server"]=>
string(5) "bytes"
["Vary"]=>
string(15) "Accept-Encoding"
- ["Content-Length"]=>
- int(0)
["Connection"]=>
string(5) "close"
["Content-Type"]=>
Host: localhost
Accept-Encoding: gzip
Connection: close
-Content-Length: 0
HTTP/1.1 200 OK
Date: Thu, 26 Aug 2010 11:41:02 GMT
Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6
GET /default/empty.php HTTP/1.1
Connection: close
Host: localhost
-Content-Length: 0
HTTP/1.1 200 OK
Date: Thu, 26 Aug 2010 12:51:28 GMT
Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6
GET /cgi-bin/chunked.sh HTTP/1.1
Host: localhost
Connection: close
-Content-Length: 0
---
HTTP/1.1 200 OK
Date: Wed, 25 Aug 2010 12:11:44 GMT
["Accept-Ranges"]=>
string(5) "bytes"
["Content-Length"]=>
- int(0)
+ string(1) "0"
["Vary"]=>
string(15) "Accept-Encoding"
["Connection"]=>
GET /default/empty.txt HTTP/1.1
Host: localhost
Connection: close
-Content-Length: 0
HTTP/1.1 200 OK
Date: Thu, 26 Aug 2010 09:55:09 GMT
Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6
Etag: "2002a-0-48549d615a35c"
Accept-Ranges: bytes
Vary: Accept-Encoding
-Content-Length: 0
Connection: close
Content-Type: text/plain
X-Original-Content-Length: 20
bool(true)
int(200)
string(2) "OK"
-array(11) {
+array(10) {
["Date"]=>
string(29) "Thu, 26 Aug 2010 09:55:09 GMT"
["Server"]=>
string(5) "bytes"
["Vary"]=>
string(15) "Accept-Encoding"
- ["Content-Length"]=>
- int(0)
["Connection"]=>
string(5) "close"
["Content-Type"]=>
Host: localhost
Accept-Encoding: gzip
Connection: close
-Content-Length: 0
HTTP/1.1 200 OK
Date: Thu, 26 Aug 2010 11:41:02 GMT
Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6
GET /default/empty.php HTTP/1.1
Connection: close
Host: localhost
-Content-Length: 0
HTTP/1.1 200 OK
Date: Thu, 26 Aug 2010 12:51:28 GMT
Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6
GET /cgi-bin/chunked.sh HTTP/1.1
Host: localhost
Connection: close
-Content-Length: 0
Done
Done
--EXPECTF--
Test
-object(%s)#%d (12) {
+object(%s)#%d (13) {
["type":protected]=>
int(1)
["body":protected]=>
["parentMessage":protected]=>
NULL
["query":protected]=>
- object(http\QueryString)#2 (1) {
+ object(http\QueryString)#%d (1) {
["queryArray":"http\QueryString":private]=>
array(0) {
}
}
["form":protected]=>
- object(http\QueryString)#3 (1) {
+ object(http\QueryString)#%d (1) {
["queryArray":"http\QueryString":private]=>
array(0) {
}
}
+ ["cookie":protected]=>
+ object(http\QueryString)#%d (1) {
+ ["queryArray":"http\QueryString":private]=>
+ array(1) {
+ ["foo"]=>
+ string(3) "bar"
+ }
+ }
["files":protected]=>
array(0) {
}
--- /dev/null
+--TEST--
+message content range
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+echo "Test\n";
+echo new http\Message(fopen(__DIR__."/data/message_r_content_range.txt", "r"));
+?>
+===DONE===
+--EXPECT--
+Test
+PUT / HTTP/1.1
+User-Agent: PECL_HTTP/2.3.0dev PHP/5.6.6-dev libcurl/7.41.0-DEV
+Host: localhost:8000
+Accept: */*
+Expect: 100-continue
+Content-Length: 3
+Content-Range: bytes 1-2/3
+X-Original-Content-Length: 3
+
+23===DONE===
use http\Message\Parser;
foreach (glob(__DIR__."/data/message_*.txt") as $file) {
+ $string = "";
$parser = new Parser;
$fd = fopen($file, "r") or die("Could not open $file");
while (!feof($fd)) {
throw new Exception(($e = error_get_last()) ? $e["message"] : "Could not parse $file");
}
}
+
+ if (!$string) {
+ $s = array("START", "HEADER", "HEADER_DONE", "BODY", "BODY_DUMB", "BODY_LENGTH", "BODY_CHUNK", "BODY_DONE", "UPDATE_CL", "DONE");
+ printf("Unexpected state: %s (%s)\n", $s[$parser->getState()], $file);
+ }
$parser = new Parser;
rewind($fd);
if ($string !== (string) $message) {
$a = explode("\n", $string);
$b = explode("\n", (string) $message);
- while ((null !== ($aa = array_shift($a))) || (null !== ($bb = array_shift($b)))) {
+ do {
+ $aa = array_shift($a);
+ $bb = array_shift($b);
if ($aa !== $bb) {
isset($aa) and printf("-- %s\n", $aa);
isset($bb) and printf("++ %s\n", $bb);
}
- }
+ } while ($a || $b);
}
}
?>
["Host"]=>
string(9) "localhost"
["Content-Length"]=>
- int(3)
+ string(1) "3"
["X-Original-Content-Length"]=>
string(1) "3"
}
--- /dev/null
+--TEST--
+phpinfo
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+echo "Test\n";
+phpinfo(INFO_MODULES);
+?>
+Done
+--EXPECTF--
+Test
+%a
+HTTP Support => enabled
+Extension Version => 2.%s
+%a
+Done
<?php
include "skipif.inc";
?>
+--XFAIL--
+TBD
--FILE--
<?php
}
}
-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);
+ }
+}
+
+function skip_http2_test($message = "skip need http2 support (nghttpd in PATH)\n") {
+ if (defined("http\\Client\\Curl\\HTTP_VERSION_2_0")) {
+ foreach (explode(":", $_ENV["PATH"]) as $path) {
+ if (is_executable($path . "/nghttpd")) {
+ return;
+ }
+ }
+ }
+ die($message);
+}
\ No newline at end of file
http://example.com:55555/index?s=b&i=0&e=&a[]=1&a[]=2
https://example.com/?s=b&i=0&e=&a[]=1&a[]=2
http://example.com:55555/index.php/
-http://localhost/
+
DONE
foreach ($urls as $url) {
printf("\n%s\n", $url);
- var_dump(http\Url::parse($url));
+ var_dump(new http\Url($url, null, 0));
}
?>
foreach ($urls as $url) {
printf("\n%s\n", $url);
- var_dump(http\Url::parse($url));
+ var_dump(new http\Url($url, null, 0));
}
?>
DONE
foreach ($urls as $url) {
printf("\n%s\n", $url);
- var_dump(http\Url::parse($url));
+ var_dump(new http\Url($url, null, 0));
}
?>
DONE
foreach ($urls as $url) {
printf("\n%s\n", $url);
- var_dump(http\Url::parse($url, http\Url::PARSE_MBLOC));
+ var_dump(new http\Url($url, null, http\Url::PARSE_MBLOC));
}
?>
DONE
foreach ($urls as $url) {
printf("\n%s\n", $url);
- var_dump(http\Url::parse($url, http\Url::PARSE_MBUTF8));
+ var_dump(new http\Url($url, null, http\Url::PARSE_MBUTF8));
}
?>
DONE
foreach ($urls as $url) {
printf("\n%s\n", $url);
- var_dump(http\Url::parse($url, http\Url::PARSE_MBLOC|http\Url::PARSE_TOIDN));
+ var_dump(new http\Url($url, null, http\Url::PARSE_MBLOC|http\Url::PARSE_TOIDN));
}
?>
DONE
foreach ($urls as $url) {
printf("\n%s\n", $url);
- var_dump(http\Url::parse($url, http\Url::PARSE_MBUTF8|http\Url::PARSE_TOIDN));
+ var_dump(new http\Url($url, null, http\Url::PARSE_MBUTF8|http\Url::PARSE_TOIDN));
}
?>
DONE
foreach ($urls as $url) {
try {
printf("\n%s\n", $url);
- var_dump(http\Url::parse($url));
+ var_dump(new http\Url($url, null, 0));
} catch (Exception $e) {
echo $e->getMessage(),"\n";
}
Test
s://[a:80
-http\Url::parse(): Failed to parse hostinfo; expected ']'
+http\Url::__construct(): Failed to parse hostinfo; expected ']'
s://[0]
-http\Url::parse(): Failed to parse hostinfo; unexpected '['
+http\Url::__construct(): Failed to parse hostinfo; unexpected '['
s://[::1]:80
object(http\Url)#%d (8) {
foreach ($urls as $url) {
try {
printf("\n%s\n", $url);
- var_dump(http\Url::parse($url));
+ var_dump(new http\Url($url, null, 0));
} catch (Exception $e) {
echo $e->getMessage(),"\n";
}
--TEST--
-url parser multibyte/utf-8/topct
+url parser multibyte/locale/topct
--SKIPIF--
<?php
include "skipif.inc";
+if (!defined("http\\Url::PARSE_MBLOC") or
+ !stristr(setlocale(LC_CTYPE, NULL), ".utf")) {
+ die("skip need http\\Url::PARSE_MBLOC support and LC_CTYPE=*.UTF-8");
+}
+
?>
--FILE--
<?php
echo "Test\n";
$urls = array(
- "http://mike:paßwort@sörver.net/for/€/?by=¢#ø"
+ "http://mike:paßwort@𐌀𐌁𐌂.it/for/€/?by=¢#ø"
);
foreach ($urls as $url) {
- var_dump(http\Url::parse($url, http\Url::PARSE_MBUTF8|http\Url::PARSE_TOPCT));
+ var_dump(new http\Url($url, null, http\Url::PARSE_MBLOC|http\Url::PARSE_TOPCT));
}
?>
DONE
["pass"]=>
string(12) "pa%C3%9Fwort"
["host"]=>
- string(11) "sörver.net"
+ string(15) "𐌀𐌁𐌂.it"
["port"]=>
NULL
["path"]=>
--- /dev/null
+--TEST--
+url parser multibyte/utf-8/topct
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$urls = array(
+ "http://mike:paßwort@𐌀𐌁𐌂.it/for/€/?by=¢#ø"
+);
+
+foreach ($urls as $url) {
+ var_dump(new http\Url($url, null, http\Url::PARSE_MBUTF8|http\Url::PARSE_TOPCT));
+}
+?>
+DONE
+--EXPECTF--
+Test
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(4) "http"
+ ["user"]=>
+ string(4) "mike"
+ ["pass"]=>
+ string(12) "pa%C3%9Fwort"
+ ["host"]=>
+ string(15) "𐌀𐌁𐌂.it"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(15) "/for/%E2%82%AC/"
+ ["query"]=>
+ string(9) "by=%C2%A2"
+ ["fragment"]=>
+ string(6) "%C3%B8"
+}
+DONE