From: Remi Collet Date: Mon, 1 Sep 2014 11:41:44 +0000 (+0200) Subject: Merge branch 'master' of git.php.net:/pecl/http/pecl_http X-Git-Tag: RELEASE_2_2_0_RC1~15 X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=commitdiff_plain;h=9f8096ebef03a968e8fe3250317a35ed3ebb7a5b;hp=400771d35709df29e48f4ca43551d40beac108e9 Merge branch 'master' of git.php.net:/pecl/http/pecl_http * 'master' of git.php.net:/pecl/http/pecl_http: (329 commits) back to dev prepare 2.1.0 fix sovereignty of clients when using events simplify duff device usage simplify duff device usage expose http\Message\Parser class fix invalid read release 2.1.0RC3 prepare RC3 news fix possible bus error on shutdown when using events be clear what we want the user to do add curlcode transfer info define off_t Removed port and scheme guessing of http\Url for portability fix PHP-5.3 compatibility MSVC dumbness back to dev prepare R_2_1_0_RC2 fix write on stack ... --- diff --git a/config.w32 b/config.w32 index 497fb65..b84101f 100644 --- a/config.w32 +++ b/config.w32 @@ -61,8 +61,6 @@ if (PHP_HTTP != "no") { AC_DEFINE("HTTP_SHARED_DEPS", 1, "Depend on shared extensions"); AC_DEFINE("HAVE_GETHOSTNAME", 1); - AC_DEFINE("HAVE_GETSERVBYPORT", 1); - AC_DEFINE("HAVE_GETSERVBYNAME", 1); if (PHP_DEBUG != "no") { ADD_FLAG("CFLAGS_HTTP", "/W3"); diff --git a/config9.m4 b/config9.m4 index ba1b935..9ca8356 100644 --- a/config9.m4 +++ b/config9.m4 @@ -68,7 +68,6 @@ if test "$PHP_HTTP" != "no"; then AC_DEFUN([HTTP_HAVE_PHP_EXT], [ extname=$1 haveext=$[PHP_]translit($1,a-z_-,A-Z__) - AC_MSG_CHECKING([for ext/$extname support]) if test -x "$PHP_EXECUTABLE"; then grepext=`$PHP_EXECUTABLE -m | $EGREP ^$extname\$` @@ -96,13 +95,11 @@ if test "$PHP_HTTP" != "no"; then dnl ---- dnl STDC dnl ---- + AC_TYPE_OFF_T + dnl getdomainname() is declared in netdb.h on some platforms: AIX, OSF AC_CHECK_HEADERS([netdb.h unistd.h]) PHP_CHECK_FUNC(gethostname, nsl) PHP_CHECK_FUNC(getdomainname, nsl) - PHP_CHECK_FUNC(getservbyport, nsl) - PHP_CHECK_FUNC(getservbyport_r, nsl) - PHP_CHECK_FUNC(getservbyname, nsl) - PHP_CHECK_FUNC(getservbyname_r, nsl) dnl ---- dnl ZLIB @@ -379,7 +376,7 @@ dnl ---- PHP_ADD_INCLUDE([$HTTP_EXT_RAPHF_INCDIR]) fi ], [ - AC_MSG_ERROR([Please install pecl/raphf]) + AC_MSG_ERROR([Please install pecl/raphf and activate extension=raphf.$SHLIB_DL_SUFFIX_NAME in your php.ini]) ]) dnl ---- @@ -407,7 +404,7 @@ dnl ---- PHP_ADD_INCLUDE([$HTTP_EXT_PROPRO_INCDIR]) fi ], [ - AC_MSG_ERROR([Please install pecl/propro]) + AC_MSG_ERROR([Please install pecl/propro and activate extension=propro.$SHLIB_DL_SUFFIX_NAME in your php.ini]) ]) PHP_ARG_WITH([http-shared-deps], [whether to depend on extensions which have been built shared], diff --git a/package.xml b/package.xml index 33d50e6..8c74393 100644 --- a/package.xml +++ b/package.xml @@ -37,10 +37,10 @@ v2: http://dev.iworks.at/ext-http/lcov/ext/http/ mike@php.net yes - 2014-08-01 + 2014-08-19 - 2.1.0RC1 - 2.1.0 + 2.2.0dev + 2.2.0 beta @@ -48,16 +48,8 @@ v2: http://dev.iworks.at/ext-http/lcov/ext/http/ BSD, revised = 7.33.0) -+ Added request options: - expect_100_timeout (libcurl >= 7.36.0) - tcp_nodelay -+ Added transfer info: - tls_session (libcurl >= 7.34.0), only available during transfer +- var_dump(http\Message) no longer automatically creates an empty body ++ Added http\Message\Parser class ]]> @@ -256,6 +248,7 @@ v2: http://dev.iworks.at/ext-http/lcov/ext/http/ + diff --git a/php_http.c b/php_http.c index 2101f0b..f7a0b86 100644 --- a/php_http.c +++ b/php_http.c @@ -142,6 +142,7 @@ PHP_MINIT_FUNCTION(http) || SUCCESS != PHP_MINIT_CALL(http_filter) || SUCCESS != PHP_MINIT_CALL(http_header) || SUCCESS != PHP_MINIT_CALL(http_message) + || SUCCESS != PHP_MINIT_CALL(http_message_parser) || SUCCESS != PHP_MINIT_CALL(http_message_body) || SUCCESS != PHP_MINIT_CALL(http_querystring) || SUCCESS != PHP_MINIT_CALL(http_client) @@ -187,9 +188,6 @@ PHP_RINIT_FUNCTION(http) { if (0 || SUCCESS != PHP_RINIT_CALL(http_env) -#if PHP_HTTP_HAVE_CURL && PHP_HTTP_HAVE_EVENT - || SUCCESS != PHP_RINIT_CALL(http_client_curl) -#endif ) { return FAILURE; } @@ -200,9 +198,6 @@ PHP_RINIT_FUNCTION(http) PHP_RSHUTDOWN_FUNCTION(http) { if (0 -#if PHP_HTTP_HAVE_CURL && PHP_HTTP_HAVE_EVENT - || SUCCESS != PHP_RSHUTDOWN_CALL(http_client_curl) -#endif || SUCCESS != PHP_RSHUTDOWN_CALL(http_env) ) { return FAILURE; diff --git a/php_http.h b/php_http.h index 7267008..98332cb 100644 --- a/php_http.h +++ b/php_http.h @@ -13,7 +13,7 @@ #ifndef PHP_EXT_HTTP_H #define PHP_EXT_HTTP_H -#define PHP_PECL_HTTP_VERSION "2.1.0RC1" +#define PHP_PECL_HTTP_VERSION "2.2.0dev" extern zend_module_entry http_module_entry; #define phpext_http_ptr &http_module_entry diff --git a/php_http_api.h b/php_http_api.h index 9e0dc6c..0e65ccb 100644 --- a/php_http_api.h +++ b/php_http_api.h @@ -58,17 +58,14 @@ typedef int STATUS; #ifdef PHP_WIN32 # define CURL_STATICLIB -# define PHP_HTTP_HAVE_NETDB # include -#elif defined(HAVE_NETDB_H) -# define PHP_HTTP_HAVE_NETDB -# include +#else +# ifdef HAVE_NETDB_H +# include +# endif # ifdef HAVE_UNISTD_H # include # endif -# ifdef HAVE_ERRNO_H -# include -# endif #endif #include @@ -111,9 +108,6 @@ typedef int STATUS; ZEND_BEGIN_MODULE_GLOBALS(php_http) struct php_http_env_globals env; -#if PHP_HTTP_HAVE_CURL && PHP_HTTP_HAVE_EVENT - struct php_http_curl_globals curl; -#endif ZEND_END_MODULE_GLOBALS(php_http) ZEND_EXTERN_MODULE_GLOBALS(php_http); diff --git a/php_http_client_curl.c b/php_http_client_curl.c index 15d2619..db73a31 100644 --- a/php_http_client_curl.c +++ b/php_http_client_curl.c @@ -49,6 +49,7 @@ typedef struct php_http_client_curl { int unfinished; /* int because of curl_multi_perform() */ #if PHP_HTTP_HAVE_EVENT + struct event_base *evbase; struct event *timeout; unsigned useevents:1; #endif @@ -98,6 +99,7 @@ typedef struct php_http_client_curl_handler { typedef struct php_http_curle_storage { char *url; char *cookiestore; + CURLcode errorcode; char errorbuffer[0x100]; } php_http_curle_storage_t; @@ -487,7 +489,6 @@ static STATUS php_http_curle_get_info(CURL *ch, HashTable *info) #if PHP_HTTP_CURL_VERSION(7,34,0) { - int i; zval *ti_array; struct curl_tlssessioninfo *ti; @@ -599,7 +600,12 @@ static STATUS php_http_curle_get_info(CURL *ch, HashTable *info) } } #endif - add_assoc_string_ex(&array, "error", sizeof("error"), php_http_curle_get_storage(ch)->errorbuffer, 1); + { + php_http_curle_storage_t *st = php_http_curle_get_storage(ch); + + add_assoc_long_ex(&array, "curlcode", sizeof("curlcode"), st->errorcode); + add_assoc_string_ex(&array, "error", sizeof("error"), st->errorbuffer, 1); + } return SUCCESS; } @@ -622,7 +628,7 @@ static void php_http_curlm_responsehandler(php_http_client_t *context) if (msg && CURLMSG_DONE == msg->msg) { if (CURLE_OK != msg->data.result) { php_http_curle_storage_t *st = php_http_curle_get_storage(msg->easy_handle); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s; %s (%s)", curl_easy_strerror(msg->data.result), STR_PTR(st->errorbuffer), STR_PTR(st->url)); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s; %s (%s)", curl_easy_strerror(st->errorcode = msg->data.result), STR_PTR(st->errorbuffer), STR_PTR(st->url)); } if ((enqueue = php_http_client_enqueued(context, msg->easy_handle, compare_queue))) { @@ -753,7 +759,7 @@ static int php_http_curlm_socket_callback(CURL *easy, curl_socket_t sock, int ac return -1; } - event_assign(&ev->evnt, PHP_HTTP_G->curl.event_base, sock, events, php_http_curlm_event_callback, context); + event_assign(&ev->evnt, curl->evbase, sock, events, php_http_curlm_event_callback, context); event_add(&ev->evnt, NULL); } @@ -774,10 +780,9 @@ static void php_http_curlm_timer_callback(CURLM *multi, long timeout_ms, void *t php_http_curlm_timeout_callback(CURL_SOCKET_TIMEOUT, /*EV_READ|EV_WRITE*/0, context); } else if (timeout_ms > 0 || !event_initialized(curl->timeout) || !event_pending(curl->timeout, EV_TIMEOUT, NULL)) { struct timeval timeout; - TSRMLS_FETCH_FROM_CTX(context->ts); if (!event_initialized(curl->timeout)) { - event_assign(curl->timeout, PHP_HTTP_G->curl.event_base, CURL_SOCKET_TIMEOUT, 0, php_http_curlm_timeout_callback, context); + event_assign(curl->timeout, curl->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_curlm_timeout_callback, context); } else if (event_pending(curl->timeout, EV_TIMEOUT, NULL)) { event_del(curl->timeout); } @@ -1309,7 +1314,7 @@ static void php_http_curle_options_init(php_http_options_t *registry TSRMLS_DC) } if ((opt = php_http_option_register(registry, ZEND_STRL("certtype"), CURLOPT_SSLCERTTYPE, IS_STRING))) { opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; - ZVAL_STRING(&opt->defval, "PEM", 1); + ZVAL_STRING(&opt->defval, "PEM", 0); } if ((opt = php_http_option_register(registry, ZEND_STRL("key"), CURLOPT_SSLKEY, IS_STRING))) { opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; @@ -1317,7 +1322,7 @@ static void php_http_curle_options_init(php_http_options_t *registry TSRMLS_DC) } if ((opt = php_http_option_register(registry, ZEND_STRL("keytype"), CURLOPT_SSLKEYTYPE, IS_STRING))) { opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; - ZVAL_STRING(&opt->defval, "PEM", 1); + ZVAL_STRING(&opt->defval, "PEM", 0); } if ((opt = php_http_option_register(registry, ZEND_STRL("keypasswd"), CURLOPT_SSLKEYPASSWD, IS_STRING))) { opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; @@ -1731,9 +1736,16 @@ static void php_http_client_curl_dtor(php_http_client_t *h) #if PHP_HTTP_HAVE_EVENT if (curl->timeout) { + if (event_initialized(curl->timeout) && event_pending(curl->timeout, EV_TIMEOUT, NULL)) { + event_del(curl->timeout); + } efree(curl->timeout); curl->timeout = NULL; } + if (curl->evbase) { + event_base_free(curl->evbase); + curl->evbase = NULL; + } #endif curl->unfinished = 0; @@ -1943,7 +1955,7 @@ static STATUS php_http_client_curl_exec(php_http_client_t *h) if (curl->useevents) { php_http_curlm_timeout_callback(CURL_SOCKET_TIMEOUT, /*EV_READ|EV_WRITE*/0, h); do { - int ev_rc = event_base_dispatch(PHP_HTTP_G->curl.event_base); + int ev_rc = event_base_dispatch(curl->evbase); #if DBG_EVENTS fprintf(stderr, "%c", "X.0"[ev_rc+1]); @@ -1987,6 +1999,9 @@ static STATUS php_http_client_curl_setopt(php_http_client_t *h, php_http_client_ 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)); } @@ -2153,24 +2168,6 @@ PHP_MSHUTDOWN_FUNCTION(http_client_curl) return SUCCESS; } -#if PHP_HTTP_HAVE_EVENT -PHP_RINIT_FUNCTION(http_client_curl) -{ - if (!PHP_HTTP_G->curl.event_base && !(PHP_HTTP_G->curl.event_base = event_base_new())) { - return FAILURE; - } - return SUCCESS; -} -PHP_RSHUTDOWN_FUNCTION(http_client_curl) -{ - if (PHP_HTTP_G->curl.event_base) { - event_base_free(PHP_HTTP_G->curl.event_base); - PHP_HTTP_G->curl.event_base = NULL; - } - return SUCCESS; -} -#endif /* PHP_HTTP_HAVE_EVENT */ - #endif /* PHP_HTTP_HAVE_CURL */ /* diff --git a/php_http_client_curl.h b/php_http_client_curl.h index 03b9b6f..c82a09c 100644 --- a/php_http_client_curl.h +++ b/php_http_client_curl.h @@ -15,15 +15,6 @@ #if PHP_HTTP_HAVE_CURL -#if PHP_HTTP_HAVE_EVENT -struct php_http_curl_globals { - void *event_base; -}; - -PHP_RINIT_FUNCTION(http_client_curl); -PHP_RSHUTDOWN_FUNCTION(http_client_curl); -#endif /* PHP_HTTP_HAVE_EVENT */ - PHP_MINIT_FUNCTION(http_client_curl); PHP_MSHUTDOWN_FUNCTION(http_client_curl); #endif /* PHP_HTTP_HAVE_CURL */ diff --git a/php_http_curl.c b/php_http_curl.c index 30feba0..a995094 100644 --- a/php_http_curl.c +++ b/php_http_curl.c @@ -25,11 +25,6 @@ # elif defined(PHP_HTTP_HAVE_GNUTLS) # define PHP_HTTP_NEED_GNUTLS_TSL # include -# else -# warning \ - "libcurl was compiled with SSL support, but configure could not determine which" \ - "library was used; thus no SSL crypto locking callbacks will be set, which may " \ - "cause random crashes on SSL requests" # endif /* PHP_HTTP_HAVE_OPENSSL || PHP_HTTP_HAVE_GNUTLS */ # endif /* PHP_WIN32 */ #endif /* ZTS && PHP_HTTP_HAVE_SSL */ diff --git a/php_http_encoding.c b/php_http_encoding.c index 403d781..7f0462c 100644 --- a/php_http_encoding.c +++ b/php_http_encoding.c @@ -53,7 +53,6 @@ const char *php_http_encoding_dechunk(const char *encoded, size_t encoded_len, c php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Data does not seem to be chunked encoded"); memcpy(*decoded, encoded, encoded_len); *decoded_len = encoded_len; - decoded[*decoded_len] = '\0'; return encoded + encoded_len; } else { efree(*decoded); diff --git a/php_http_env_response.c b/php_http_env_response.c index 4d6ef03..c155dde 100644 --- a/php_http_env_response.c +++ b/php_http_env_response.c @@ -1096,7 +1096,11 @@ static PHP_METHOD(HttpEnvResponse, __invoke) php_http_message_object_init_body_object(obj); php_http_message_body_append(obj->message->body, ob_str, ob_len); +#if PHP_VERSION_ID >= 50400 RETURN_TRUE; +#else + RETURN_EMPTY_STRING(); +#endif } } diff --git a/php_http_header_parser.c b/php_http_header_parser.c index f560a85..ec41a24 100644 --- a/php_http_header_parser.c +++ b/php_http_header_parser.c @@ -62,7 +62,7 @@ php_http_header_parser_state_t php_http_header_parser_state_is(php_http_header_p php_http_header_parser_state_t state; if (parser->stack.top) { - return (php_http_header_parser_state_t) zend_ptr_stack_top(&parser->stack); + return (php_http_header_parser_state_t) parser->stack.elements[parser->stack.top - 1]; } return PHP_HTTP_HEADER_PARSER_STATE_START; diff --git a/php_http_message.c b/php_http_message.c index cc04edc..70dcae8 100644 --- a/php_http_message.c +++ b/php_http_message.c @@ -98,6 +98,19 @@ php_http_message_t *php_http_message_init_env(php_http_message_t *message, php_h zval_dtor(&tval); } } +#else + if (OG(ob_nesting_level)) { + if (php_get_output_start_filename(TSRMLS_C)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not fetch response body, output has already been sent at %s:%d", php_get_output_start_filename(TSRMLS_C), php_get_output_start_lineno(TSRMLS_C)); + goto error; + } else if (SUCCESS != php_ob_get_buffer(&tval TSRMLS_CC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not fetch response body"); + goto error; + } else { + php_http_message_body_append(message->body, Z_STRVAL(tval), Z_STRLEN(tval)); + zval_dtor(&tval); + } + } #endif break; @@ -924,13 +937,11 @@ static HashTable *php_http_message_object_get_props(zval *object TSRMLS_DC) { zval *headers; php_http_message_object_t *obj = zend_object_store_get_object(object TSRMLS_CC); - php_http_message_t *msg = obj->message; HashTable *props = zend_get_std_object_handlers()->get_properties(object TSRMLS_CC); zval array, *parent, *body; char *version; PHP_HTTP_MESSAGE_OBJECT_INIT(obj); - INIT_PZVAL_ARRAY(&array, props); #define ASSOC_PROP(ptype, n, val) \ @@ -951,20 +962,20 @@ static HashTable *php_http_message_object_get_props(zval *object TSRMLS_DC) } \ } while(0) - ASSOC_PROP(long, "type", msg->type); - ASSOC_STRINGL_EX("httpVersion", version, spprintf(&version, 0, "%u.%u", msg->http.version.major, msg->http.version.minor), 0); + ASSOC_PROP(long, "type", obj->message->type); + ASSOC_STRINGL_EX("httpVersion", version, spprintf(&version, 0, "%u.%u", obj->message->http.version.major, obj->message->http.version.minor), 0); - switch (msg->type) { + switch (obj->message->type) { case PHP_HTTP_REQUEST: ASSOC_PROP(long, "responseCode", 0); ASSOC_STRINGL("responseStatus", "", 0); - ASSOC_STRING("requestMethod", STR_PTR(msg->http.info.request.method)); - ASSOC_STRING("requestUrl", STR_PTR(msg->http.info.request.url)); + ASSOC_STRING("requestMethod", STR_PTR(obj->message->http.info.request.method)); + ASSOC_STRING("requestUrl", STR_PTR(obj->message->http.info.request.url)); break; case PHP_HTTP_RESPONSE: - ASSOC_PROP(long, "responseCode", msg->http.info.response.code); - ASSOC_STRING("responseStatus", STR_PTR(msg->http.info.response.status)); + ASSOC_PROP(long, "responseCode", obj->message->http.info.response.code); + ASSOC_STRING("responseStatus", STR_PTR(obj->message->http.info.response.status)); ASSOC_STRINGL("requestMethod", "", 0); ASSOC_STRINGL("requestUrl", "", 0); break; @@ -980,18 +991,19 @@ static HashTable *php_http_message_object_get_props(zval *object TSRMLS_DC) MAKE_STD_ZVAL(headers); array_init(headers); - zend_hash_copy(Z_ARRVAL_P(headers), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + zend_hash_copy(Z_ARRVAL_P(headers), &obj->message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); ASSOC_PROP(zval, "headers", headers); MAKE_STD_ZVAL(body); - if (!obj->body) { - php_http_new(NULL, php_http_message_body_class_entry, (php_http_new_t) php_http_message_body_object_new_ex, NULL, (void *) php_http_message_body_init(&obj->message->body, NULL TSRMLS_CC), (void *) &obj->body TSRMLS_CC); + if (obj->body) { + ZVAL_OBJVAL(body, obj->body->zv, 1); + } else { + ZVAL_NULL(body); } - ZVAL_OBJVAL(body, obj->body->zv, 1); ASSOC_PROP(zval, "body", body); MAKE_STD_ZVAL(parent); - if (msg->parent) { + if (obj->message->parent) { ZVAL_OBJVAL(parent, obj->parent->zv, 1); } else { ZVAL_NULL(parent); diff --git a/php_http_message_parser.c b/php_http_message_parser.c index 6328fa4..0d11bf6 100644 --- a/php_http_message_parser.c +++ b/php_http_message_parser.c @@ -80,7 +80,7 @@ php_http_message_parser_state_t php_http_message_parser_state_push(php_http_mess php_http_message_parser_state_t php_http_message_parser_state_is(php_http_message_parser_t *parser) { if (parser->stack.top) { - return (php_http_message_parser_state_t) zend_ptr_stack_top(&parser->stack); + return (php_http_message_parser_state_t) parser->stack.elements[parser->stack.top - 1]; } return PHP_HTTP_MESSAGE_PARSER_STATE_START; } @@ -97,6 +97,7 @@ void php_http_message_parser_dtor(php_http_message_parser_t *parser) { php_http_header_parser_dtor(&parser->header); zend_ptr_stack_destroy(&parser->stack); + php_http_message_free(&parser->message); if (parser->dechunk) { php_http_encoding_stream_free(&parser->dechunk); } @@ -508,6 +509,153 @@ php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_p return php_http_message_parser_state_is(parser); } +zend_class_entry *php_http_message_parser_class_entry; +static zend_object_handlers php_http_message_parser_object_handlers; + +zend_object_value php_http_message_parser_object_new(zend_class_entry *ce TSRMLS_DC) +{ + return php_http_message_parser_object_new_ex(ce, NULL, NULL TSRMLS_CC); +} + +zend_object_value php_http_message_parser_object_new_ex(zend_class_entry *ce, php_http_message_parser_t *parser, php_http_message_parser_object_t **ptr TSRMLS_DC) +{ + php_http_message_parser_object_t *o; + + o = ecalloc(1, sizeof(php_http_message_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_message_parser_init(NULL TSRMLS_CC); + } + o->buffer = php_http_buffer_new(); + + o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_message_parser_object_free, NULL TSRMLS_CC); + o->zv.handlers = &php_http_message_parser_object_handlers; + + return o->zv; +} + +void php_http_message_parser_object_free(void *object TSRMLS_DC) +{ + php_http_message_parser_object_t *o = (php_http_message_parser_object_t *) object; + + if (o->parser) { + php_http_message_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_HttpMessageParser_getState, 0, 0, 0) +ZEND_END_ARG_INFO(); +static PHP_METHOD(HttpMessageParser, getState) +{ + php_http_message_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_message_parser_state_is(parser_obj->parser)); +} + +ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_parse, 0, 0, 3) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(1, message) +ZEND_END_ARG_INFO(); +static PHP_METHOD(HttpMessageParser, parse) +{ + php_http_message_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); + + 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_message_parser_parse(parser_obj->parser, parser_obj->buffer, flags, &parser_obj->parser->message)); + + zval_dtor(zmsg); + if (parser_obj->parser->message) { + ZVAL_OBJVAL(zmsg, php_http_message_object_new_ex(php_http_message_class_entry, php_http_message_copy(parser_obj->parser->message, NULL), NULL TSRMLS_CC), 0); + } +} + +ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_stream, 0, 0, 3) + ZEND_ARG_INFO(0, stream) + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(1, message) +ZEND_END_ARG_INFO(); +static PHP_METHOD(HttpMessageParser, stream) +{ + php_http_message_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); + + parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC); + RETVAL_LONG(php_http_message_parser_parse_stream(parser_obj->parser, s, flags, &parser_obj->parser->message)); + + zval_dtor(zmsg); + if (parser_obj->parser->message) { + ZVAL_OBJVAL(zmsg, php_http_message_object_new_ex(php_http_message_class_entry, php_http_message_copy(parser_obj->parser->message, NULL), NULL TSRMLS_CC), 0); + } +} + +static zend_function_entry php_http_message_parser_methods[] = { + PHP_ME(HttpMessageParser, getState, ai_HttpMessageParser_getState, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessageParser, parse, ai_HttpMessageParser_parse, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessageParser, stream, ai_HttpMessageParser_stream, ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} +}; + +PHP_MINIT_FUNCTION(http_message_parser) +{ + zend_class_entry ce; + + INIT_NS_CLASS_ENTRY(ce, "http\\Message", "Parser", php_http_message_parser_methods); + php_http_message_parser_class_entry = zend_register_internal_class(&ce TSRMLS_CC); + memcpy(&php_http_message_parser_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + php_http_message_parser_class_entry->create_object = php_http_message_parser_object_new; + php_http_message_parser_object_handlers.clone_obj = NULL; + + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("CLEANUP"), PHP_HTTP_MESSAGE_PARSER_CLEANUP TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("DUMB_BODIES"), PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("EMPTY_REDIRECTS"), PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("GREEDY"), PHP_HTTP_MESSAGE_PARSER_GREEDY TSRMLS_CC); + + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_FAILURE"), PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_START"), PHP_HTTP_MESSAGE_PARSER_STATE_START TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DUMB"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB TSRMLS_CC); + 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_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_DONE TSRMLS_CC); + + return SUCCESS; +} + /* * Local variables: * tab-width: 4 diff --git a/php_http_message_parser.h b/php_http_message_parser.h index ebc2142..5b04351 100644 --- a/php_http_message_parser.h +++ b/php_http_message_parser.h @@ -56,6 +56,21 @@ PHP_HTTP_API void php_http_message_parser_free(php_http_message_parser_t **parse PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_parser_t *parser, php_http_buffer_t *buffer, unsigned flags, php_http_message_t **message); PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_message_parser_t *parser, php_stream *s, unsigned flags, php_http_message_t **message); +typedef struct php_http_message_parser_object { + zend_object zo; + zend_object_value zv; + php_http_buffer_t *buffer; + php_http_message_parser_t *parser; +} php_http_message_parser_object_t; + +PHP_HTTP_API zend_class_entry *php_http_message_parser_class_entry; + +PHP_MINIT_FUNCTION(http_message_parser); + +zend_object_value php_http_message_parser_object_new(zend_class_entry *ce TSRMLS_DC); +zend_object_value php_http_message_parser_object_new_ex(zend_class_entry *ce, php_http_message_parser_t *parser, php_http_message_parser_object_t **ptr TSRMLS_DC); +void php_http_message_parser_object_free(void *object TSRMLS_DC); + #endif /* diff --git a/php_http_misc.c b/php_http_misc.c index 7afa006..8fcb82e 100644 --- a/php_http_misc.c +++ b/php_http_misc.c @@ -96,7 +96,7 @@ char *php_http_pretty_key(register char *key, size_t key_len, zend_bool uctitle, if ((wasalpha = PHP_HTTP_IS_CTYPE(alpha, key[0]))) { key[0] = (char) (uctitle ? PHP_HTTP_TO_CTYPE(upper, key[0]) : PHP_HTTP_TO_CTYPE(lower, key[0])); } - PHP_HTTP_DUFF(1, key_len, + PHP_HTTP_DUFF(key_len, if (PHP_HTTP_IS_CTYPE(alpha, key[i])) { key[i] = (char) (((!wasalpha) && uctitle) ? PHP_HTTP_TO_CTYPE(upper, key[i]) : PHP_HTTP_TO_CTYPE(lower, key[i])); wasalpha = 1; diff --git a/php_http_misc.h b/php_http_misc.h index d47477c..3bd77d8 100644 --- a/php_http_misc.h +++ b/php_http_misc.h @@ -65,48 +65,44 @@ size_t php_http_boundary(char *buf, size_t len TSRMLS_DC); int php_http_select_str(const char *cmp, int argc, ...); /* See "A Reusable Duff Device" By Ralf Holly, August 01, 2005 */ -#define PHP_HTTP_DUFF_BREAK(i) do { \ - times_##i = 1; \ -} while (0) - -#define PHP_HTTP_DUFF(i, c, a) do { \ - size_t count_##i = (c); \ - size_t times_##i = (count_##i + 7) >> 3; \ - switch (count_##i & 7){ \ - case 0: do { a; \ - case 7: a; \ - case 6: a; \ - case 5: a; \ - case 4: a; \ - case 3: a; \ - case 2: a; \ - case 1: a; \ - } while (--times_##i > 0); \ +#define PHP_HTTP_DUFF_BREAK() times_=1 +#define PHP_HTTP_DUFF(c, a) do { \ + size_t count_ = (c); \ + size_t times_ = (count_ + 7) >> 3; \ + switch (count_ & 7){ \ + case 0: do { \ + a; \ + case 7: \ + a; \ + case 6: \ + a; \ + case 5: \ + a; \ + case 4: \ + a; \ + case 3: \ + a; \ + case 2: \ + a; \ + case 1: \ + a; \ + } while (--times_ > 0); \ } \ } while (0) - static inline const char *php_http_locate_str(register const char *h, size_t h_len, const char *n, size_t n_len) { - register const char *p1, *p2; - - if (n_len && h_len && h_len >= n_len) { - PHP_HTTP_DUFF(1, h_len - n_len + 1, - if (*h == *n) { - p1 = h; - p2 = n; - PHP_HTTP_DUFF(2, n_len, - if (*p1++ != *p2++) { - PHP_HTTP_DUFF_BREAK(2); - } else if (p2 == n + n_len - 1) { - return h; - } - ); - } - ++h; - ); + if (!n_len || !h_len || h_len < n_len) { + return NULL; } + PHP_HTTP_DUFF(h_len - n_len + 1, + if (*h == *n && !strncmp(h + 1, n + 1, n_len - 1)) { + return h; + } + ++h; + ); + return NULL; } @@ -125,7 +121,7 @@ static inline const char *php_http_locate_bin_eol(const char *bin, size_t len, i register const char *eol = bin; if (len > 0) { - PHP_HTTP_DUFF(1, len, + PHP_HTTP_DUFF(len, if (*eol == '\r' || *eol == '\n') { if (eol_len) { *eol_len = ((eol[0] == '\r' && eol[1] == '\n') ? 2 : 1); diff --git a/php_http_params.c b/php_http_params.c index 65504ac..5234244 100644 --- a/php_http_params.c +++ b/php_http_params.c @@ -224,6 +224,10 @@ static inline void sanitize_key(unsigned flags, char *str, size_t len, zval *zv, if (flags & PHP_HTTP_PARAMS_ESCAPED) { sanitize_escaped(zv TSRMLS_CC); } + + if (!Z_STRLEN_P(zv)) { + return; + } eos = &Z_STRVAL_P(zv)[Z_STRLEN_P(zv)-1]; if (*eos == '*') { @@ -253,7 +257,7 @@ static inline void sanitize_rfc5987(zval *zv, char **language, zend_bool *latin1 switch (Z_STRVAL_P(zv)[0]) { case 'I': case 'i': - if (!strncasecmp(Z_STRVAL_P(zv), ZEND_STRL("iso-8859-1"))) { + if (!strncasecmp(Z_STRVAL_P(zv), "iso-8859-1", lenof("iso-8859-1"))) { *latin1 = 1; ptr = Z_STRVAL_P(zv) + lenof("iso-8859-1"); break; @@ -261,7 +265,7 @@ static inline void sanitize_rfc5987(zval *zv, char **language, zend_bool *latin1 /* no break */ case 'U': case 'u': - if (!strncasecmp(Z_STRVAL_P(zv), ZEND_STRL("utf-8"))) { + if (!strncasecmp(Z_STRVAL_P(zv), "utf-8", lenof("utf-8"))) { *latin1 = 0; ptr = Z_STRVAL_P(zv) + lenof("utf-8"); break; diff --git a/php_http_url.c b/php_http_url.c index c4eb90b..7c8077b 100644 --- a/php_http_url.c +++ b/php_http_url.c @@ -42,94 +42,6 @@ static inline char *localhostname(void) return estrndup("localhost", lenof("localhost")); } -static inline unsigned port(const char *scheme) -{ - unsigned port = 80; - -#if defined(ZTS) && defined(HAVE_GETSERVBYPORT_R) - int rc; - size_t len = 0xff; - char *buf = NULL; - struct servent *se_res = NULL, se_buf = {0}; - - do { - buf = erealloc(buf, len); - rc = getservbyname_r(scheme, "tcp", &se_buf, buf, len, &se_res); - len *= 2; - } while (rc == ERANGE && len <= 0xfff); - - if (!rc) { - port = ntohs(se_res->s_port); - } - - efree(buf); -#elif !defined(ZTS) && defined(HAVE_GETSERVBYPORT) - struct servent *se; - - if ((se = getservbyname(scheme, "tcp")) && se->s_port) { - port = ntohs(se->s_port); - } -#endif - - return port; -} -static inline char *scheme(unsigned port) -{ - char *scheme; -#if defined(ZTS) && defined(HAVE_GETSERVBYPORT_R) - int rc; - size_t len = 0xff; - char *buf = NULL; - struct servent *se_res = NULL, se_buf = {0}; -#elif !defined(ZTS) && defined(HAVE_GETSERVBYPORT) - struct servent *se; -#endif - - switch (port) { - case 443: - scheme = estrndup("https", lenof("https")); - break; - -#if defined(ZTS) && !defined(HAVE_GETSERVBYPORT_R) - default: -#elif !defined(ZTS) && !defined(HAVE_GETSERVBYPORT) - default: -#endif - case 80: - case 0: - scheme = estrndup("http", lenof("http")); - break; - -#if defined(ZTS) && defined(HAVE_GETSERVBYPORT_R) - default: - do { - buf = erealloc(buf, len); - rc = getservbyport_r(htons(port), "tcp", &se_buf, buf, len, &se_res); - len *= 2; - } while (rc == ERANGE && len <= 0xfff); - - if (!rc && se_res) { - scheme = estrdup(se_res->s_name); - } else { - scheme = estrndup("http", lenof("http")); - } - - efree(buf); - break; - -#elif !defined(ZTS) && defined(HAVE_GETSERVBYPORT) - default: - if ((se = getservbyport(htons(port), "tcp")) && se->s_name) { - scheme = estrdup(se->s_name); - } else { - scheme = estrndup("http", lenof("http")); - } - break; -#endif - } - return scheme; -} - static php_url *php_http_url_from_env(php_url *url TSRMLS_DC) { zval *https, *zhost, *zport; @@ -150,7 +62,7 @@ static php_url *php_http_url_from_env(php_url *url TSRMLS_DC) if (https && !strcasecmp(Z_STRVAL_P(https), "ON")) { url->scheme = estrndup("https", lenof("https")); } else { - url->scheme = scheme(url->port); + url->scheme = estrndup("http", lenof("http")); } /* host */ @@ -339,7 +251,6 @@ void php_http_url(int flags, const php_url *old_url, const php_url *new_url, php if (url->port) { if ( ((url->port == 80) && !strcmp(url->scheme, "http")) || ((url->port ==443) && !strcmp(url->scheme, "https")) - || ( url->port == port(url->scheme)) ) { url->port = 0; } diff --git a/tests/client015.phpt b/tests/client015.phpt new file mode 100644 index 0000000..35981a5 --- /dev/null +++ b/tests/client015.phpt @@ -0,0 +1,41 @@ +--TEST-- +http client event base +--SKIPIF-- +enableEvents()) + throw new Exception("need events support"); +} catch (Exception $e) { + die("skip ".$e->getMessage()); +} +?> +--FILE-- +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/")); + +$client1->send(); + +if (($r = $client1->getResponse())) { + var_dump($r->getTransferInfo("response_code")); +} +if (($r = $client2->getResponse())) { + var_dump($r->getTransferInfo("response_code")); +} + +?> +DONE +--EXPECT-- +Test +int(200) +DONE diff --git a/tests/data/message_r_multipart_put.txt b/tests/data/message_r_multipart_put.txt index 52776d4..b3284cf 100644 --- a/tests/data/message_r_multipart_put.txt +++ b/tests/data/message_r_multipart_put.txt @@ -2,7 +2,7 @@ PUT /docs/ HTTP/1.1 User-Agent: curl/7.24.0 (x86_64-unknown-linux-gnu) libcurl/7.24.0 OpenSSL/1.0.0g zlib/1.2.6 libssh2/1.3.0 Host: drop Accept: */* -Content-Length: 2284 +Content-Length: 2273 Expect: 100-continue Content-Type: multipart/form-data; boundary=----------------------------6e182425881c diff --git a/tests/info_001.phpt b/tests/info_001.phpt index 11b83f6..370d70e 100644 --- a/tests/info_001.phpt +++ b/tests/info_001.phpt @@ -40,8 +40,7 @@ object(http\Message)#%d (9) { ["type":protected]=> int(1) ["body":protected]=> - object(http\Message\Body)#%d (0) { - } + NULL ["requestMethod":protected]=> string(3) "GET" ["requestUrl":protected]=> diff --git a/tests/message002.phpt b/tests/message002.phpt index 403fb26..0809676 100644 --- a/tests/message002.phpt +++ b/tests/message002.phpt @@ -37,8 +37,7 @@ object(%s)#%d (12) { ["type":protected]=> int(1) ["body":protected]=> - object(http\Message\Body)#%d (0) { - } + NULL ["requestMethod":protected]=> string(4) "POST" ["requestUrl":protected]=> diff --git a/tests/message006.phpt b/tests/message006.phpt index 52e534d..4d1a693 100644 --- a/tests/message006.phpt +++ b/tests/message006.phpt @@ -29,8 +29,7 @@ object(c)#%d (9) { ["type":protected]=> int(0) ["body":protected]=> - object(http\Message\Body)#%d (0) { - } + NULL ["requestMethod":protected]=> string(0) "" ["requestUrl":protected]=> diff --git a/tests/messageparser001.phpt b/tests/messageparser001.phpt new file mode 100644 index 0000000..f4ec2d9 --- /dev/null +++ b/tests/messageparser001.phpt @@ -0,0 +1,53 @@ +--TEST-- +message parser +--SKIPIF-- + +--FILE-- +parse(fgets($fd), 0, $message)) { + case Parser::STATE_DONE: + $string = (string) $message; + break 2; + case Parser::STATE_FAILURE: + throw new Exception(($e = error_get_last()) ? $e["message"] : "Could not parse $file"); + } + } + + $parser = new Parser; + rewind($fd); + unset($message); + + switch ($parser->stream($fd, 0, $message)) { + case Parser::STATE_DONE: + case Parser::STATE_START: + break; + default: + printf("Expected parser state 0 or 8, got %d", $parser->getState()); + } + if ($string !== (string) $message) { + $a = explode("\n", $string); + $b = explode("\n", (string) $message); + while ((null !== ($aa = array_shift($a))) || (null !== ($bb = array_shift($b)))) { + if ($aa !== $bb) { + isset($aa) and printf("-- %s\n", $aa); + isset($bb) and printf("++ %s\n", $bb); + } + } + } +} +?> +DONE +--EXPECT-- +Test +DONE diff --git a/tests/params015.phpt b/tests/params015.phpt index ad3948c..7501167 100644 --- a/tests/params015.phpt +++ b/tests/params015.phpt @@ -7,7 +7,7 @@ include "skipif.inc"; --FILE-- ["filename"=>"foo.bar"]]); +$p = new http\Params(array("attachment"=>array("filename"=>"foo.bar"))); var_dump($p->params); var_dump((string)$p); ?>