From: Michael Wallner Date: Wed, 18 Feb 2015 09:33:35 +0000 (+0100) Subject: Merge branch 'master' into phpng X-Git-Tag: RELEASE_3_0_0_RC1~56 X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=commitdiff_plain;h=95c6c900d04096d332c422e3f597186b7184c5ab Merge branch 'master' into phpng Conflicts: package.xml php_http_client_curl.c php_http_env.c php_http_env_response.c php_http_header_parser.c php_http_message.c php_http_message_body.c php_http_message_parser.c php_http_misc.c php_http_strlist.c php_http_url.h tests/info.phpt --- 95c6c900d04096d332c422e3f597186b7184c5ab diff --cc php_http_client.c index 22fc6ca,4f0de3c..467ed2a --- a/php_http_client.c +++ b/php_http_client.c @@@ -814,6 -843,22 +814,22 @@@ static PHP_METHOD(HttpClient, wait } } + 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 == zend_parse_parameters(ZEND_NUM_ARGS(), "|H!", &settings), invalid_arg, return); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + 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(); @@@ -1139,6 -1189,30 +1155,30 @@@ static PHP_METHOD(HttpClient, getAvaila } } + 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); ++ php_http_client_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + 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); ++ php_http_client_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + 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) diff --cc php_http_client_curl.c index c507799,97408bd..df832ec --- a/php_http_client_curl.c +++ b/php_http_client_curl.c @@@ -962,7 -934,10 +962,10 @@@ static ZEND_RESULT_CODE php_http_curle_ php_http_client_curl_handler_t *curl = userdata; CURL *ch = curl->handle; + #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)) { + if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_ACCEPT_ENCODING, Z_TYPE_P(val) == IS_TRUE ? "" : NULL)) { return FAILURE; } return SUCCESS; @@@ -1506,11 -1493,8 +1509,12 @@@ static zval *php_http_curle_get_option( zval *option; if ((option = php_http_option_get(opt, options, NULL))) { - option = php_http_ztyp(opt->type, option); - zend_hash_quick_update(&curl->options.cache, opt->name.s, opt->name.l, opt->name.h, &option, sizeof(zval *), NULL); + zval zopt; + + ZVAL_DUP(&zopt, option); - convert_to_explicit_type(option, opt->type); ++ convert_to_explicit_type(&zopt, opt->type); + zend_hash_update(&curl->options.cache, opt->name, &zopt); ++ return zend_hash_find(&curl->options.cache, opt->name); } return option; } @@@ -1599,6 -1580,182 +1603,187 @@@ static ZEND_RESULT_CODE php_http_curle_ 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) ++static ZEND_RESULT_CODE 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; ++ zval *entry; + 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); ++ ZEND_HASH_FOREACH_VAL(&tmp_ht, entry) ++ { ++ *ptr++ = Z_STRVAL_P(entry); + } ++ ZEND_HASH_FOREACH_END(); + } + + 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) ++static inline ZEND_RESULT_CODE 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) ++static ZEND_RESULT_CODE 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)); ++ return php_http_curlm_use_eventloop(client, value && Z_TYPE_P(value) == IS_TRUE); + } + #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); ++ 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))) { ++ 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) ++static ZEND_RESULT_CODE 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); ++ ZEND_RESULT_CODE rv = SUCCESS; + + 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); ++ zval zopt; ++ ++ ZVAL_DUP(&zopt, val); ++ convert_to_explicit_type(&zopt, opt->type); ++ ++ val = &zopt; + } + + 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)))) { ++ case _IS_BOOL: ++ if (CURLM_OK != (rc = curl_multi_setopt(ch, opt->option, (long) zend_is_true(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); ++ 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)); ++ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s (%s)", opt->name->val, curl_easy_strerror(rc)); + } + return rv; + } /* client ops */ @@@ -1912,24 -2064,37 +2097,40 @@@ static void queue_dtor(php_http_client_ php_http_client_curl_handler_dtor(handler); } - static php_resource_factory_t *create_rf(php_http_url_t *url) -static php_resource_factory_t *create_rf(php_http_client_t *h, php_http_client_enqueue_t *enqueue TSRMLS_DC) ++static php_resource_factory_t *create_rf(php_http_client_t *h, php_http_client_enqueue_t *enqueue) { - php_persistent_handle_factory_t *pf; + php_persistent_handle_factory_t *pf = NULL; php_resource_factory_t *rf = NULL; - zend_string *id; - char *id_str = NULL; - size_t id_len; + php_http_url_t *url = enqueue->request->http.info.request.url; if (!url || (!url->host && !url->path)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot request empty URL"); + php_error_docref(NULL, E_WARNING, "Cannot request empty URL"); return NULL; } - id_len = spprintf(&id_str, 0, "%s:%d", STR_PTR(url->host), url->port ? url->port : 80); - id = php_http_cs2zs(id_str, id_len); + /* only if the client itself is setup for persistence */ + if (h->rf->dtor == (void (*)(void*)) php_persistent_handle_abandon) { ++ zend_string *id; + char *id_str = NULL; + size_t id_len; + int port = url->port ? url->port : 80; - zval **zport; ++ zval *zport; ++ ++ id_len = spprintf(&id_str, 0, "%s:%d", STR_PTR(url->host), url->port ? url->port : 80); ++ id = php_http_cs2zs(id_str, id_len); + - if (SUCCESS == zend_hash_find(enqueue->options, ZEND_STRS("port"), (void *) &zport)) { - zval *zcpy = php_http_ztyp(IS_LONG, *zport); ++ if ((zport = zend_hash_str_find(enqueue->options, ZEND_STRL("port")))) { ++ zend_long lport = zval_get_long(zport); + - if (Z_LVAL_P(zcpy)) { - port = Z_LVAL_P(zcpy); ++ if (lport > 0) { ++ port = lport; + } - zval_ptr_dtor(&zcpy); + } - pf = php_persistent_handle_concede(NULL, PHP_HTTP_G->client.curl.driver.request_name, id, NULL, NULL); - zend_string_release(id); + 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); ++ pf = php_persistent_handle_concede(NULL, PHP_HTTP_G->client.curl.driver.request_name, id, NULL, NULL); ++ zend_string_release(id); + } if (pf) { rf = php_resource_factory_init(NULL, php_persistent_handle_get_resource_factory_ops(), pf, (void (*)(void*)) php_persistent_handle_abandon); @@@ -1947,8 -2112,9 +2148,8 @@@ static ZEND_RESULT_CODE php_http_client php_http_client_curl_handler_t *handler; php_http_client_progress_state_t *progress; php_resource_factory_t *rf; - TSRMLS_FETCH_FROM_CTX(h->ts); - rf = create_rf(enqueue->request->http.info.request.url); - rf = create_rf(h, enqueue TSRMLS_CC); ++ rf = create_rf(h, enqueue); if (!rf) { return FAILURE; } @@@ -2167,9 -2325,42 +2356,40 @@@ static ZEND_RESULT_CODE php_http_client return SUCCESS; } -static int apply_available_options(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) ++static int apply_available_options(zval *pDest, int num_args, va_list args, zend_hash_key *hash_key) + { - php_http_option_t *opt = pDest; ++ php_http_option_t *opt = Z_PTR_P(pDest); + HashTable *ht; - zval *entry; ++ 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)); ++ array_init_size(&entry, c); ++ zend_hash_apply_with_arguments(&opt->suboptions.options, apply_available_options, 1, Z_ARRVAL(entry)); + } else { + /* catch deliberate NULL options */ + if (Z_TYPE(opt->defval) == IS_STRING && !Z_STRVAL(opt->defval)) { - ZVAL_NULL(entry); ++ ZVAL_NULL(&entry); + } else { - ZVAL_ZVAL(entry, &opt->defval, 1, 0); ++ ZVAL_ZVAL(&entry, &opt->defval, 1, 0); + } + } + - if (hash_key->nKeyLength) { - zend_hash_quick_update(ht, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &entry, sizeof(zval *), NULL); ++ if (hash_key->key) { ++ zend_hash_update(ht, hash_key->key, &entry); + } else { - zend_hash_index_update(ht, hash_key->h, (void *) &entry, sizeof(zval *), NULL); ++ zend_hash_index_update(ht, hash_key->h, &entry); + } + + 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) +static ZEND_RESULT_CODE 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: @@@ -2241,8 -2439,14 +2469,14 @@@ PHP_MINIT_FUNCTION(http_client_curl options->getter = php_http_curle_get_option; options->setter = php_http_curle_set_option; - php_http_curle_options_init(options TSRMLS_CC); + php_http_curle_options_init(options); } + 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); ++ php_http_curlm_options_init(options); + } /* * HTTP Protocol Version Constants @@@ -2321,13 -2525,11 +2555,14 @@@ PHP_MSHUTDOWN_FUNCTION(http_client_curl) { - php_persistent_handle_cleanup(ZEND_STRL("http\\Client\\Curl"), NULL, 0 TSRMLS_CC); - php_persistent_handle_cleanup(ZEND_STRL("http\\Client\\Curl\\Request"), NULL, 0 TSRMLS_CC); + php_persistent_handle_cleanup(PHP_HTTP_G->client.curl.driver.client_name, NULL); + php_persistent_handle_cleanup(PHP_HTTP_G->client.curl.driver.request_name, NULL); + zend_string_release(PHP_HTTP_G->client.curl.driver.client_name); + zend_string_release(PHP_HTTP_G->client.curl.driver.request_name); + zend_string_release(PHP_HTTP_G->client.curl.driver.driver_name); php_http_options_dtor(&php_http_curle_options); + php_http_options_dtor(&php_http_curlm_options); return SUCCESS; } diff --cc php_http_env.c index 311541d,6de5ecb..0ede201 --- a/php_http_env.c +++ b/php_http_env.c @@@ -709,12 -638,16 +624,16 @@@ ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getRe ZEND_END_ARG_INFO(); static PHP_METHOD(HttpEnv, getResponseStatusForCode) { - long code; + zend_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)); + + if ((status = php_http_env_get_response_status_for_code(code))) { - RETURN_STRING(status, 1); ++ RETURN_STRING(status); + } } ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getResponseStatusForAllCodes, 0, 0, 0) @@@ -730,12 -659,9 +645,9 @@@ static PHP_METHOD(HttpEnv, getResponseS } 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); - } -#define PHP_HTTP_RESPONSE_CODE(code, status) add_index_string(return_value, code, status, 1); ++#define PHP_HTTP_RESPONSE_CODE(code, status) add_index_string(return_value, code, status); + #include "php_http_response_codes.h" + #undef PHP_HTTP_RESPONSE_CODE } ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getResponseHeader, 0, 0, 0) diff --cc php_http_env_response.c index 7c76c08,2f2f161..5b46820 --- a/php_http_env_response.c +++ b/php_http_env_response.c @@@ -841,19 -891,25 +842,21 @@@ typedef struct php_http_env_response_st 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) +static ZEND_RESULT_CODE 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 (!ctx->stream || SUCCESS != zend_list_addref(ctx->stream->rsrc_id)) { - efree(ctx); - return FAILURE; - } + ++GC_REFCOUNT(ctx->stream->res); + ZEND_INIT_SYMTABLE(&ctx->header); + php_http_version_init(&ctx->version, 1, 1); + 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); + ctx->request = get_request(&r->options); /* there are some limitations regarding TE:chunked, see https://tools.ietf.org/html/rfc7230#section-3.3.1 */ if (ctx->request && ctx->request->http.version.major == 1 && ctx->request->http.version.minor == 0) { @@@ -867,22 -923,26 +870,25 @@@ static void php_http_env_response_stream_dtor(php_http_env_response_t *r) { 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); ++ ctx->chunked_filter = php_stream_filter_remove(ctx->chunked_filter, 1); + } zend_hash_destroy(&ctx->header); - zend_list_delete(ctx->stream->rsrc_id); + zend_list_delete(ctx->stream->res); efree(ctx); r->ctx = NULL; } - static void php_http_env_response_stream_header(php_http_env_response_stream_ctx_t *ctx, HashTable *header) -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) ++static void php_http_env_response_stream_header(php_http_env_response_stream_ctx_t *ctx, HashTable *header, php_http_buffer_t *buf) { - HashPosition pos; - zval **val; + zval *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), buf TSRMLS_CC); + ZEND_HASH_FOREACH_VAL(header, val) + { + if (Z_TYPE_P(val) == IS_ARRAY) { - php_http_env_response_stream_header(ctx, Z_ARRVAL_P(val)); ++ php_http_env_response_stream_header(ctx, Z_ARRVAL_P(val), buf); } else { - zval *tmp = php_http_ztyp(IS_STRING, *val); + zend_string *zs = zval_get_string(val); if (ctx->chunked) { /* disable chunked transfer encoding if we've got an explicit content-length */ @@@ -890,15 -950,16 +896,17 @@@ ctx->chunked = 0; } } - php_stream_write(ctx->stream, zs->val, zs->len); - php_stream_write_string(ctx->stream, PHP_HTTP_CRLF); - php_http_buffer_append(buf, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); ++ php_http_buffer_append(buf, zs->val, zs->len); + php_http_buffer_appends(buf, PHP_HTTP_CRLF); - zval_ptr_dtor(&tmp); + zend_string_release(zs); } } + ZEND_HASH_FOREACH_END(); } -static STATUS php_http_env_response_stream_start(php_http_env_response_stream_ctx_t *ctx TSRMLS_DC) +static ZEND_RESULT_CODE php_http_env_response_stream_start(php_http_env_response_stream_ctx_t *ctx) { + php_http_buffer_t header_buf; + if (ctx->started || ctx->finished) { return FAILURE; } @@@ -914,18 -976,26 +923,27 @@@ ctx->chunked = 0; } - php_http_env_response_stream_header(ctx, &ctx->header); - php_http_env_response_stream_header(ctx, &ctx->header, &header_buf TSRMLS_CC); ++ php_http_env_response_stream_header(ctx, &ctx->header, &header_buf); /* enable chunked transfer encoding */ if (ctx->chunked) { - php_stream_write_string(ctx->stream, "Transfer-Encoding: chunked" PHP_HTTP_CRLF); + php_http_buffer_appends(&header_buf, "Transfer-Encoding: chunked" PHP_HTTP_CRLF); } + - php_stream_write_string(ctx->stream, PHP_HTTP_CRLF); + php_http_buffer_appends(&header_buf, PHP_HTTP_CRLF); - ctx->started = 1; + 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); - return SUCCESS; + if (ctx->chunked) { - ctx->chunked_filter = php_stream_filter_create("http.chunked_encode", NULL, 0 TSRMLS_CC); ++ ctx->chunked_filter = php_stream_filter_create("http.chunked_encode", NULL, 0); + 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) { @@@ -1047,15 -1115,12 +1061,11 @@@ static ZEND_RESULT_CODE php_http_env_re return FAILURE; } - if (stream_ctx->chunked && 2 != php_stream_write_string(stream_ctx->stream, PHP_HTTP_CRLF)) { - return FAILURE; - } - return SUCCESS; } -static STATUS php_http_env_response_stream_flush(php_http_env_response_t *r) +static ZEND_RESULT_CODE php_http_env_response_stream_flush(php_http_env_response_t *r) { php_http_env_response_stream_ctx_t *stream_ctx = r->ctx; - TSRMLS_FETCH_FROM_CTX(r->ts); if (stream_ctx->finished) { return FAILURE; @@@ -1068,15 -1133,16 +1078,15 @@@ return php_stream_flush(stream_ctx->stream); } -static STATUS php_http_env_response_stream_finish(php_http_env_response_t *r) +static ZEND_RESULT_CODE 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)) { + if (!ctx->started) { - if (SUCCESS != php_http_env_response_stream_start(ctx TSRMLS_CC)) { ++ if (SUCCESS != php_http_env_response_stream_start(ctx)) { return FAILURE; } } diff --cc php_http_header_parser.c index e14b443,cea2dbf..cf30c84 --- a/php_http_header_parser.c +++ b/php_http_header_parser.c @@@ -90,15 -96,33 +94,30 @@@ void php_http_header_parser_free(php_ht } } + /* 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) ++static void php_http_header_parser_error(size_t valid_len, char *str, size_t len, const char *eol_str ) + { - int escaped_len; - char *escaped_str; ++ zend_string *escaped_str = zend_string_init(str, len, 0); + - escaped_str = php_addcslashes(str, len, &escaped_len, 0, ZEND_STRL("\x0..\x1F\x7F..\xFF") TSRMLS_CC); ++ escaped_str = php_addcslashes(escaped_str, 1, ZEND_STRL("\x0..\x1F\x7F..\xFF")); + + 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); ++ php_error_docref(NULL, E_WARNING, "Failed to parse headers: unexpected character '\\%03o' at pos %zu of '%s'", str[valid_len], valid_len, escaped_str->val); + } 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); ++ php_error_docref(NULL, E_WARNING, "Failed to parse headers: unexpected end of line at pos %zu of '%s'", eol_str - str, escaped_str->val); + } 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); ++ php_error_docref(NULL, E_WARNING, "Failed to parse headers: unexpected end of input at pos %zu of '%s'", len, escaped_str->val); + } + + 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) +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) { - 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 + #if DBG_PARSER const char *state[] = {"START", "KEY", "VALUE", "VALUE_EX", "HEADER_DONE", "DONE"}; - int num_headers = headers ? zend_hash_num_elements(headers) : 0; - fprintf(stderr, "#HP: (%d) %s (avail:%zu, num:%d)\n", php_http_header_parser_state_is(parser), - php_http_header_parser_state_is(parser) < 0 ? "FAILURE" : state[php_http_header_parser_state_is(parser)], - buffer->used, num_headers); + 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)) { @@@ -213,19 -245,30 +240,30 @@@ case PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE: if (parser->_key.str && parser->_val.str) { - zval array, **exist; + zval tmp, *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); + callback_func(callback_arg, &headers, NULL); } - INIT_PZVAL_ARRAY(&array, headers); php_http_pretty_key(parser->_key.str, parser->_key.len, 1, 1); - if (SUCCESS == zend_symtable_find(headers, parser->_key.str, parser->_key.len + 1, (void *) &exist)) { - convert_to_array(*exist); - add_next_index_stringl(*exist, parser->_val.str, parser->_val.len, 0); + if ((exist = zend_symtable_str_find(headers, parser->_key.str, parser->_key.len))) { + convert_to_array(exist); + add_next_index_str(exist, php_http_cs2zs(parser->_val.str, parser->_val.len)); } else { - add_assoc_stringl_ex(&array, parser->_key.str, parser->_key.len + 1, parser->_val.str, parser->_val.len, 0); + ZVAL_STR(&tmp, php_http_cs2zs(parser->_val.str, parser->_val.len)); + zend_symtable_str_update(headers, parser->_key.str, parser->_key.len, &tmp); } parser->_val.str = NULL; } @@@ -244,6 -287,195 +282,193 @@@ 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) ++zend_object *php_http_header_parser_object_new(zend_class_entry *ce) + { - return php_http_header_parser_object_new_ex(ce, NULL, NULL TSRMLS_CC); ++ return &php_http_header_parser_object_new_ex(ce, NULL)->zo; + } + -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 *php_http_header_parser_object_new_ex(zend_class_entry *ce, php_http_header_parser_t *parser) + { + 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; - } ++ o = ecalloc(1, sizeof(php_http_header_parser_object_t) + zend_object_properties_size(ce)); ++ zend_object_std_init(&o->zo, ce); ++ object_properties_init(&o->zo, ce); + + if (parser) { + o->parser = parser; + } else { - o->parser = php_http_header_parser_init(NULL TSRMLS_CC); ++ o->parser = php_http_header_parser_init(NULL); + } + 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; ++ o->zo.handlers = &php_http_header_parser_object_handlers; + - return o->zv; ++ return o; + } + -void php_http_header_parser_object_free(void *object TSRMLS_DC) ++void php_http_header_parser_object_free(zend_object *object) + { - php_http_header_parser_object_t *o = (php_http_header_parser_object_t *) object; ++ php_http_header_parser_object_t *o = PHP_HTTP_OBJ(object, NULL); + + 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_object_std_dtor(object); + } + + 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); ++ php_http_header_parser_object_t *parser_obj = PHP_HTTP_OBJ(NULL, getThis()); + + 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; ++ size_t data_len; ++ zend_long flags; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slz", &data_str, &data_len, &flags, &zmsg), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "slz", &data_str, &data_len, &flags, &zmsg), invalid_arg, return); + ++ ZVAL_DEREF(zmsg); + if (Z_TYPE_P(zmsg) != IS_ARRAY) { + zval_dtor(zmsg); + array_init(zmsg); + } - parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ parser_obj = PHP_HTTP_OBJ(NULL, getThis()); + 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; ++ zend_long flags; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz", &zstream, &flags, &zmsg), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "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); ++ zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh); ++ php_stream_from_zval(s, zstream); ++ zend_restore_error_handling(&zeh); + ++ ZVAL_DEREF(zmsg); + if (Z_TYPE_P(zmsg) != IS_ARRAY) { + zval_dtor(zmsg); + array_init(zmsg); + } - parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ parser_obj = PHP_HTTP_OBJ(NULL, getThis()); + 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); ++ php_http_header_parser_class_entry = zend_register_internal_class(&ce); + 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.offset = XtOffsetOf(php_http_header_parser_object_t, zo); + php_http_header_parser_object_handlers.clone_obj = NULL; ++ php_http_header_parser_object_handlers.free_obj = php_http_header_parser_object_free; + - 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("CLEANUP"), PHP_HTTP_HEADER_PARSER_CLEANUP); + - 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); ++ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_FAILURE"), PHP_HTTP_HEADER_PARSER_STATE_FAILURE); ++ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_START"), PHP_HTTP_HEADER_PARSER_STATE_START); ++ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_KEY"), PHP_HTTP_HEADER_PARSER_STATE_KEY); ++ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_VALUE"), PHP_HTTP_HEADER_PARSER_STATE_VALUE); ++ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_VALUE_EX"), PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX); ++ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_HEADER_DONE"), PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE); ++ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_DONE"), PHP_HTTP_HEADER_PARSER_STATE_DONE); + + return SUCCESS; + } + /* * Local variables: * tab-width: 4 diff --cc php_http_header_parser.h index 5b4993a,ed9ecaf..33707bd --- a/php_http_header_parser.h +++ b/php_http_header_parser.h @@@ -47,6 -50,22 +47,21 @@@ PHP_HTTP_API php_http_header_parser_sta 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; ++ zend_object zo; + } 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); ++zend_object *php_http_header_parser_object_new(zend_class_entry *ce); ++php_http_header_parser_object_t *php_http_header_parser_object_new_ex(zend_class_entry *ce, php_http_header_parser_t *parser); ++void php_http_header_parser_object_free(zend_object *object); #endif /* PHP_HTTP_HEADER_PARSER_H */ diff --cc php_http_message.c index 6ecf6e7,b7b500b..3d3822c --- a/php_http_message.c +++ b/php_http_message.c @@@ -275,33 -297,40 +275,36 @@@ void php_http_message_update_headers(ph 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))) { ++ } else if (php_http_message_header(msg, ZEND_STRL("Content-Range"))) { + /* 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); - zend_hash_update(&msg->hdrs, "Content-Length", sizeof("Content-Length"), &h, sizeof(zval *), NULL); + ZVAL_LONG(&h, size); + zend_hash_str_update(&msg->hdrs, "Content-Length", lenof("Content-Length"), &h); if (msg->body->boundary) { char *str; size_t len; + zend_string *ct; - if (!(h = php_http_message_header(msg, ZEND_STRL("Content-Type"), 1))) { + if (!(ct = php_http_message_header_string(msg, ZEND_STRL("Content-Type")))) { len = spprintf(&str, 0, "multipart/form-data; boundary=\"%s\"", msg->body->boundary); - MAKE_STD_ZVAL(h); - ZVAL_STRINGL(h, str, len, 0); - zend_hash_update(&msg->hdrs, "Content-Type", sizeof("Content-Type"), &h, sizeof(zval *), NULL); - } else if (!php_http_match(Z_STRVAL_P(h), "boundary=", PHP_HTTP_MATCH_WORD)) { - zval_dtor(h); - Z_STRLEN_P(h) = spprintf(&Z_STRVAL_P(h), 0, "%s; boundary=\"%s\"", Z_STRVAL_P(h), msg->body->boundary); - zend_hash_update(&msg->hdrs, "Content-Type", sizeof("Content-Type"), &h, sizeof(zval *), NULL); + ZVAL_STR(&h, php_http_cs2zs(str, len)); + zend_hash_str_update(&msg->hdrs, "Content-Type", lenof("Content-Type"), &h); + } else if (!php_http_match(ct->val, "boundary=", PHP_HTTP_MATCH_WORD)) { + len = spprintf(&str, 0, "%s; boundary=\"%s\"", ct->val, msg->body->boundary); + ZVAL_STR(&h, php_http_cs2zs(str, len)); + zend_hash_str_update(&msg->hdrs, "Content-Type", lenof("Content-Type"), &h); + zend_string_release(ct); } else { - zval_ptr_dtor(&h); + zend_string_release(ct); } } - } else if ((h = php_http_message_header(msg, ZEND_STRL("Content-Length"), 1))) { - zval *h_cpy = php_http_ztyp(IS_LONG, h); - - zval_ptr_dtor(&h); - if (Z_LVAL_P(h_cpy)) { + } else if ((cl = php_http_message_header_string(msg, ZEND_STRL("Content-Length")))) { + if (!zend_string_equals_literal(cl, "0")) { + /* body->size == 0, so get rid of old Content-Length */ - zend_hash_del(&msg->hdrs, "Content-Length", sizeof("Content-Length")); + zend_hash_str_del(&msg->hdrs, ZEND_STRL("Content-Length")); } - zval_ptr_dtor(&h_cpy); + zend_string_release(cl); } } diff --cc php_http_message_body.c index 1c540c7,fa91551..4a3174a --- a/php_http_message_body.c +++ b/php_http_message_body.c @@@ -112,8 -118,9 +112,8 @@@ const char *php_http_message_body_bound { if (!body->boundary) { union { double dbl; int num[2]; } data; - TSRMLS_FETCH_FROM_CTX(body->ts); -- data.dbl = php_combined_lcg(TSRMLS_C); ++ data.dbl = php_combined_lcg(); spprintf(&body->boundary, 0, "%x.%x", data.num[0], data.num[1]); } return body->boundary; @@@ -121,29 -128,35 +121,33 @@@ 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->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); + 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))) { ++ if ((etag = php_http_etag_init(PHP_HTTP_G->env.etag_mode))) { + 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) +zend_string *php_http_message_body_to_string(php_http_message_body_t *body, off_t offset, size_t forlen) { php_stream *s = php_http_message_body_stream(body); - TSRMLS_FETCH_FROM_CTX(body->ts); php_stream_seek(s, offset, SEEK_SET); if (!forlen) { @@@ -212,7 -228,7 +216,7 @@@ size_t php_http_message_body_append(php written = php_stream_write(s, buf, len); if (written != len) { -- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to append %zu bytes to body; wrote %zu", len, written); ++ php_error_docref(NULL, E_WARNING, "Failed to append %zu bytes to body; wrote %zu", len, written); } return len; @@@ -562,29 -589,32 +566,29 @@@ php_http_message_body_object_t *php_htt o->body = body; } - o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_message_body_object_free, NULL TSRMLS_CC); - o->zv.handlers = &php_http_message_body_object_handlers; + o->zo.handlers = &php_http_message_body_object_handlers; - return o->zv; + return o; } -zend_object_value php_http_message_body_object_clone(zval *object TSRMLS_DC) +zend_object *php_http_message_body_object_clone(zval *object) { - zend_object_value new_ov; php_http_message_body_object_t *new_obj = NULL; - php_http_message_body_object_t *old_obj = zend_object_store_get_object(object TSRMLS_CC); + php_http_message_body_object_t *old_obj = PHP_HTTP_OBJ(NULL, object); php_http_message_body_t *body = php_http_message_body_copy(old_obj->body, NULL); - new_ov = php_http_message_body_object_new_ex(old_obj->zo.ce, body, &new_obj TSRMLS_CC); - zend_objects_clone_members(&new_obj->zo, new_ov, &old_obj->zo, Z_OBJ_HANDLE_P(object) TSRMLS_CC); + new_obj = php_http_message_body_object_new_ex(old_obj->zo.ce, body); + zend_objects_clone_members(&new_obj->zo, &old_obj->zo); - return new_ov; + return &new_obj->zo; } -void php_http_message_body_object_free(void *object TSRMLS_DC) +void php_http_message_body_object_free(zend_object *object) { - php_http_message_body_object_t *obj = object; + php_http_message_body_object_t *obj = PHP_HTTP_OBJ(object, NULL); php_http_message_body_free(&obj->body); - zend_object_std_dtor(object TSRMLS_CC); - zend_object_std_dtor((zend_object *) obj TSRMLS_CC); - efree(obj); ++ zend_object_std_dtor(object); } #define PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj) \ @@@ -811,10 -848,10 +815,10 @@@ ZEND_END_ARG_INFO() PHP_METHOD(HttpMessageBody, stat) { char *field_str = NULL; - int field_len = 0; + size_t field_len = 0; -- if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &field_str, &field_len)) { - php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &field_str, &field_len)) { + php_http_message_body_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); const php_stream_statbuf *sb; PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj); diff --cc php_http_message_parser.c index e40e7b3,3b55efb..7bf9a16 --- a/php_http_message_parser.c +++ b/php_http_message_parser.c @@@ -247,93 -268,92 +264,94 @@@ php_http_message_parser_state_t php_htt case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE: { - zval *h, *h_loc = NULL, *h_con = NULL, **h_cl = NULL, **h_cr = NULL, **h_te = NULL; + zval h, *h_ptr, *h_loc = NULL, *h_con = NULL, *h_ce; + zend_bool chunked = 0; + zend_long content_length = -1; + zend_string *content_range = 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"), (void *) &h, sizeof(zval *), (void *) &h_te); - zend_hash_del(&(*message)->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding")); + if ((h_ptr = php_http_message_header(*message, ZEND_STRL("Transfer-Encoding")))) { + zend_string *zs = zval_get_string(h_ptr); + + chunked = zend_string_equals_literal(zs, "chunked"); + zend_string_release(zs); + + Z_TRY_ADDREF_P(h_ptr); + zend_hash_str_update(&(*message)->hdrs, "X-Original-Transfer-Encoding", lenof("X-Original-Transfer-Encoding"), h_ptr); + zend_hash_str_del(&(*message)->hdrs, "Transfer-Encoding", lenof("Transfer-Encoding")); - } - if ((h_ptr = php_http_message_header(*message, ZEND_STRL("Content-Length")))) { + /* 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); ++ ZVAL_LONG(&h, 0); ++ zend_hash_str_update(&(*message)->hdrs, "Content-Length", lenof("Content-Length"), &h); ++ } else if ((h_ptr = php_http_message_header(*message, ZEND_STRL("Content-Length")))) { + content_length = zval_get_long(h_ptr); - + Z_TRY_ADDREF_P(h_ptr); + zend_hash_str_update(&(*message)->hdrs, "X-Original-Content-Length", lenof("X-Original-Content-Length"), h_ptr); } - if ((h_ptr = php_http_message_header(*message, ZEND_STRL("Content-Range")))) { - content_range = zval_get_string(h_ptr); - Z_TRY_ADDREF_P(h_ptr); - zend_hash_str_update(&(*message)->hdrs, "X-Original-Content-Range", lenof("X-Original-Content-Range"), h_ptr); - zend_hash_str_del(&(*message)->hdrs, "Content-Range", lenof("Content-Range")); - if ((h = php_http_message_header(*message, ZEND_STRL("Content-Range"), 1))) { - 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); - } ++ if ((content_range = php_http_message_header_string(*message, ZEND_STRL("Content-Range")))) { ++ ZVAL_STR_COPY(&h, content_range); ++ zend_hash_str_update(&(*message)->hdrs, "Content-Range", lenof("Content-Range"), &h); } - /* default */ - ZVAL_LONG(&h, 0); - zend_hash_str_update(&(*message)->hdrs, "Content-Length", lenof("Content-Length"), &h); - /* so, if curl sees a 3xx code, a Location header and a Connection:close header * it decides not to read the response body. */ if ((flags & PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS) && (*message)->type == PHP_HTTP_RESPONSE && (*message)->http.info.response.code/100 == 3 - && (h_loc = php_http_message_header(*message, ZEND_STRL("Location"), 1)) - && (h_con = php_http_message_header(*message, ZEND_STRL("Connection"), 1)) + && (h_loc = php_http_message_header(*message, ZEND_STRL("Location"))) + && (h_con = php_http_message_header(*message, ZEND_STRL("Connection"))) ) { -- if (php_http_match(Z_STRVAL_P(h_con), "close", PHP_HTTP_MATCH_WORD)) { ++ zend_string *con = zval_get_string(h_con); ++ ++ if (php_http_match(con->val, "close", PHP_HTTP_MATCH_WORD)) { ++ zend_string_release(con); php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE); - zval_ptr_dtor(&h_loc); - zval_ptr_dtor(&h_con); break; } ++ zend_string_release(con); } - if (h_loc) { - zval_ptr_dtor(&h_loc); - } - if (h_con) { - zval_ptr_dtor(&h_con); - } - if ((h = php_http_message_header(*message, ZEND_STRL("Content-Encoding"), 1))) { - if (php_http_match(Z_STRVAL_P(h), "gzip", PHP_HTTP_MATCH_WORD) - || php_http_match(Z_STRVAL_P(h), "x-gzip", PHP_HTTP_MATCH_WORD) - || php_http_match(Z_STRVAL_P(h), "deflate", PHP_HTTP_MATCH_WORD) + if ((h_ce = php_http_message_header(*message, ZEND_STRL("Content-Encoding")))) { - if (php_http_match(Z_STRVAL_P(h_ce), "gzip", PHP_HTTP_MATCH_WORD) - || php_http_match(Z_STRVAL_P(h_ce), "x-gzip", PHP_HTTP_MATCH_WORD) - || php_http_match(Z_STRVAL_P(h_ce), "deflate", PHP_HTTP_MATCH_WORD) ++ zend_string *ce = zval_get_string(h_ce); ++ ++ if (php_http_match(ce->val, "gzip", PHP_HTTP_MATCH_WORD) ++ || php_http_match(ce->val, "x-gzip", PHP_HTTP_MATCH_WORD) ++ || php_http_match(ce->val, "deflate", PHP_HTTP_MATCH_WORD) ) { if (parser->inflate) { php_http_encoding_stream_reset(&parser->inflate); } else { - parser->inflate = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), 0 TSRMLS_CC); + parser->inflate = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), 0); } - zend_hash_update(&(*message)->hdrs, "X-Original-Content-Encoding", sizeof("X-Original-Content-Encoding"), &h, sizeof(zval *), NULL); - zend_hash_del(&(*message)->hdrs, "Content-Encoding", sizeof("Content-Encoding")); - } else { - zval_ptr_dtor(&h); + Z_TRY_ADDREF_P(h_ce); + zend_hash_str_update(&(*message)->hdrs, "X-Original-Content-Encoding", lenof("X-Original-Content-Encoding"), h_ce); + zend_hash_str_del(&(*message)->hdrs, "Content-Encoding", lenof("Content-Encoding")); } ++ zend_string_release(ce); } if ((flags & PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES)) { php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB); } else { - if (h_te) { - if (strstr(Z_STRVAL_PP(h_te), "chunked")) { - parser->dechunk = php_http_encoding_stream_init(parser->dechunk, php_http_encoding_stream_get_dechunk_ops(), 0 TSRMLS_CC); - php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED); - break; - } + if (chunked) { + parser->dechunk = php_http_encoding_stream_init(parser->dechunk, php_http_encoding_stream_get_dechunk_ops(), 0); + php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED); + break; } - if (content_length >= 0) { - parser->body_length = content_length; - 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) { + if (content_range) { ulong total = 0, start = 0, end = 0; - if (!strncasecmp(Z_STRVAL_PP(h_cr), "bytes", lenof("bytes")) - && ( Z_STRVAL_PP(h_cr)[lenof("bytes")] == ':' - || Z_STRVAL_PP(h_cr)[lenof("bytes")] == ' ' - || Z_STRVAL_PP(h_cr)[lenof("bytes")] == '=' + if (!strncasecmp(content_range->val, "bytes", lenof("bytes")) + && ( content_range->val[lenof("bytes")] == ':' + || content_range->val[lenof("bytes")] == ' ' + || content_range->val[lenof("bytes")] == '=' ) ) { char *total_at = NULL, *end_at = NULL; @@@ -346,18 -366,31 +364,23 @@@ 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); + zend_string_release(content_range); 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); + zend_string_release(content_range); + } - 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 (content_length >= 0) { ++ parser->body_length = content_length; ++ 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); @@@ -371,8 -404,7 +394,6 @@@ 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; @@@ -389,10 -421,7 +410,6 @@@ } php_stream_write(php_http_message_body_stream((*message)->body), str, len); -- - /* keep track */ - ZVAL_LONG(&zcl, php_http_message_body_size((*message)->body)); - zend_hash_str_update(&(*message)->hdrs, "Content-Length", lenof("Content-Length"), &zcl); } if (cut) { @@@ -485,7 -514,17 +502,17 @@@ 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); ++ zval zcl; ++ ++ ZVAL_LONG(&zcl, php_http_message_body_size((*message)->body)); ++ zend_hash_str_update(&(*message)->hdrs, "Content-Length", lenof("Content-Length"), &zcl); + break; + } + + case PHP_HTTP_MESSAGE_PARSER_STATE_DONE: + { char *ptr = buffer->data; while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) { @@@ -631,24 -671,23 +658,25 @@@ PHP_MINIT_FUNCTION(http_message_parser 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_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); + php_http_message_parser_object_handlers.free_obj = php_http_message_parser_object_free; + php_http_message_parser_object_handlers.offset = XtOffsetOf(php_http_message_parser_object_t, zo); + + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("CLEANUP"), PHP_HTTP_MESSAGE_PARSER_CLEANUP); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("DUMB_BODIES"), PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("EMPTY_REDIRECTS"), PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("GREEDY"), PHP_HTTP_MESSAGE_PARSER_GREEDY); + + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_FAILURE"), PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_START"), PHP_HTTP_MESSAGE_PARSER_STATE_START); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DUMB"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_LENGTH"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_CHUNKED"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE); ++ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_UPDATE_CL"), PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_DONE); return SUCCESS; } diff --cc php_http_url.h index b7402e9,303b936..6ae0ac3 --- a/php_http_url.h +++ b/php_http_url.h @@@ -68,32 -68,14 +68,17 @@@ PHP_HTTP_API char *php_http_url_to_stri PHP_HTTP_API char *php_http_url_authority_to_string(const php_http_url_t *url, char **url_str, size_t *url_len); PHP_HTTP_API void php_http_url_free(php_http_url_t **url); -PHP_HTTP_API STATUS php_http_url_encode_hash(HashTable *hash, const char *pre_encoded_str, size_t pre_encoded_len, char **encoded_str, size_t *encoded_len TSRMLS_DC); -PHP_HTTP_API STATUS php_http_url_encode_hash_ex(HashTable *hash, php_http_buffer_t *qstr, const char *arg_sep_str, size_t arg_sep_len, const char *val_sep_str, size_t val_sep_len, const char *pre_encoded_str, size_t pre_encoded_len TSRMLS_DC); +PHP_HTTP_API ZEND_RESULT_CODE php_http_url_encode_hash(HashTable *hash, const char *pre_encoded_str, size_t pre_encoded_len, char **encoded_str, size_t *encoded_len); +PHP_HTTP_API ZEND_RESULT_CODE php_http_url_encode_hash_ex(HashTable *hash, php_http_buffer_t *qstr, const char *arg_sep_str, size_t arg_sep_len, const char *val_sep_str, size_t val_sep_len, const char *pre_encoded_str, size_t pre_encoded_len); -static inline void php_http_url_argsep(const char **str, size_t *len TSRMLS_DC) +static inline void php_http_url_argsep(const char **str, size_t *len) { - php_http_ini_entry(ZEND_STRL("arg_separator.output"), str, len, 0 TSRMLS_CC); + if (SUCCESS != php_http_ini_entry(ZEND_STRL("arg_separator.output"), str, len, 0) || !*len) { + *str = PHP_HTTP_URL_ARGSEP; + *len = lenof(PHP_HTTP_URL_ARGSEP); + } } - 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); } diff --cc tests/messageparser002.phpt index c704766,71f2b5e..acac6cd --- a/tests/messageparser002.phpt +++ b/tests/messageparser002.phpt @@@ -63,4 -63,4 +63,4 @@@ object(http\Message)#%d (9) } string(3) "OK " --DONE ++DONE diff --cc tests/phpinfo.phpt index 0000000,373cb45..c1f58d9 mode 000000,100644..100644 --- a/tests/phpinfo.phpt +++ b/tests/phpinfo.phpt @@@ -1,0 -1,19 +1,19 @@@ + --TEST-- + phpinfo + --SKIPIF-- + + --FILE-- + + Done + --EXPECTF-- + Test + %a + HTTP Support => enabled -Extension Version => 2.%s ++Extension Version => 3.%s + %a + Done