Merge branch 'master' into phpng
authorMichael Wallner <mike@php.net>
Thu, 12 Mar 2015 08:30:46 +0000 (09:30 +0100)
committerMichael Wallner <mike@php.net>
Thu, 12 Mar 2015 08:30:46 +0000 (09:30 +0100)
37 files changed:
config.w32
config9.m4
php_http.c
php_http_api.h
php_http_env.c
php_http_env.h
php_http_header_parser.c
php_http_querystring.c
php_http_url.c
php_http_utf8.h
tests/bug66388.phpt
tests/bug66891.phpt
tests/client003.phpt
tests/client008.phpt
tests/client015.phpt
tests/client016.phpt
tests/client017.phpt
tests/client018.phpt
tests/client019.phpt
tests/client021.phpt
tests/client023.phpt
tests/client025.phpt
tests/client026.phpt
tests/envrequestbody002.phpt [deleted file]
tests/envrequestbody003.phpt [deleted file]
tests/envrequestjson001.phpt [deleted file]
tests/envrequestjson002.phpt [deleted file]
tests/envresponse017.phpt
tests/headerparser001.phpt
tests/headerparser002.phpt
tests/headerparser003.phpt
tests/helper/pipeline.inc
tests/helper/server.inc
tests/info002.phpt
tests/messageparser001.phpt
tests/querystring003.phpt [new file with mode: 0644]
tests/skipif.inc

index b84101f79af9a389546b4731db267a7791a37e5e..13c24ab122f31a7ef203360e6af664c15fdc5317 100644 (file)
@@ -53,8 +53,7 @@ if (PHP_HTTP != "no") {
                "php_http_env_response.c php_http_etag.c php_http_exception.c php_http_filter.c php_http_header_parser.c " +\r
                "php_http_header.c php_http_info.c php_http_message.c php_http_message_body.c php_http_message_parser.c " +\r
                "php_http_misc.c php_http_negotiate.c php_http_object.c php_http_options.c php_http_params.c " +\r
-               "php_http_querystring.c " +\r
-               "php_http_strlist.c php_http_url.c php_http_version.c",\r
+               "php_http_querystring.c php_http_url.c php_http_version.c",\r
                null,\r
                null);\r
        AC_DEFINE("HAVE_HTTP", 1, "Have extended HTTP support");\r
index 8a1011818c9fd9a00c0f8ae9b60abce5604fe743..bd5ba2f08feb59dc9ee852cdfff7d16d15fe5947 100644 (file)
@@ -122,6 +122,11 @@ dnl ----
        done
        if test "x$IDNA_DIR" = "x"; then
                AC_MSG_RESULT([not found])
+               case $host_os in
+               darwin*)
+                       AC_CHECK_HEADERS(unicode/uidna.h)
+                       PHP_CHECK_FUNC(uidna_IDNToASCII, icucore);;
+               esac
        else
                AC_MSG_RESULT([found in $IDNA_DIR])
                AC_DEFINE([PHP_HTTP_HAVE_IDN], [1], [Have libidn support])
@@ -505,12 +510,6 @@ dnl ----
                fi
        ])
 
-dnl ----
-dnl JSON
-dnl ----
-       HTTP_HAVE_PHP_EXT([json])
-
-
 dnl ----
 dnl ICONV
 dnl ----
@@ -555,11 +554,10 @@ dnl ----
        dnl shared extension deps
        HTTP_SHARED_DEP([hash])
        HTTP_SHARED_DEP([iconv])
-       HTTP_SHARED_DEP([json])
        
        dnl extension deps
        PHP_ADD_EXTENSION_DEP([http], [raphf], true)
-       PHP_ADD_EXTENSION_DEP([http], [propo], true)
+       PHP_ADD_EXTENSION_DEP([http], [propro], true)
        
        PHP_SUBST([HTTP_SHARED_LIBADD])
 
index 98df8433b403f1c3b12113204c660ea00aa97339..d430e1f13538fce89cfb75a8005e18a0c5e6d6ad 100644 (file)
@@ -44,7 +44,6 @@ zend_function_entry http_functions[] = {
 
 PHP_MINIT_FUNCTION(http);
 PHP_MSHUTDOWN_FUNCTION(http);
-PHP_RINIT_FUNCTION(http);
 PHP_RSHUTDOWN_FUNCTION(http);
 PHP_MINFO_FUNCTION(http);
 
@@ -57,9 +56,6 @@ static zend_module_dep http_module_deps[] = {
 #endif
 #ifdef PHP_HTTP_HAVE_ICONV
        ZEND_MOD_REQUIRED("iconv")
-#endif
-#ifdef PHP_HTTP_HAVE_JSON
-       ZEND_MOD_REQUIRED("json")
 #endif
        {NULL, NULL, NULL, 0}
 };
@@ -72,7 +68,7 @@ zend_module_entry http_module_entry = {
        http_functions,
        PHP_MINIT(http),
        PHP_MSHUTDOWN(http),
-       PHP_RINIT(http),
+       NULL,
        PHP_RSHUTDOWN(http),
        PHP_MINFO(http),
        PHP_PECL_HTTP_VERSION,
@@ -178,17 +174,6 @@ PHP_MSHUTDOWN_FUNCTION(http)
        return SUCCESS;
 }
 
-PHP_RINIT_FUNCTION(http)
-{
-       if (0
-       || SUCCESS != PHP_RINIT_CALL(http_env)
-       ) {
-               return FAILURE;
-       }
-       
-       return SUCCESS;
-}
-
 PHP_RSHUTDOWN_FUNCTION(http)
 {
        if (0
index 68fdac88015029a05592dda8732e12d17288a2bb..2313413f30af7761cd216f34bbd45718c8f8df20 100644 (file)
@@ -49,9 +49,7 @@
 #      define PHP_HTTP_HAVE_HASH
 #endif
 
-#if (defined(HAVE_JSON) || defined(PHP_HTTP_HAVE_EXT_JSON)) && (PHP_HTTP_SHARED_DEPS || !defined(COMPILE_DL_JSON))
-#      define PHP_HTTP_HAVE_JSON
-#endif
+#include <stddef.h>
 
 #ifdef PHP_WIN32
 #      define CURL_STATICLIB
index 0ede201e875a349a850f9b443225119c0d248938..f5c72d66bd661f50d3c83475d95126bb330fd185 100644 (file)
 #include "php_http_api.h"
 #include "php_variables.h"
 
-PHP_RINIT_FUNCTION(http_env)
-{
-       /* populate form data on non-POST requests */
-       if (SG(request_info).request_method && strcasecmp(SG(request_info).request_method, "POST") && SG(request_info).content_type && *SG(request_info).content_type) {
-               uint ct_len = strlen(SG(request_info).content_type);
-               char *ct_str = estrndup(SG(request_info).content_type, ct_len);
-               php_http_params_opts_t opts;
-               HashTable params;
-
-               php_http_params_opts_default_get(&opts);
-               opts.input.str = ct_str;
-               opts.input.len = ct_len;
-
-               SG(request_info).content_type_dup = ct_str;
-
-               ZEND_INIT_SYMTABLE(&params);
-               if (php_http_params_parse(&params, &opts)) {
-                       zend_string *key_str;
-                       zend_ulong key_num;
-
-                       if (HASH_KEY_IS_STRING == zend_hash_get_current_key(&params, &key_str, &key_num)) {
-                               sapi_post_entry *post_entry = NULL;
-
-                               if ((post_entry = zend_hash_find_ptr(&SG(known_post_content_types), key_str))) {
-                                       SG(request_info).post_entry = post_entry;
-
-                                       if (post_entry->post_reader) {
-                                               post_entry->post_reader();
-                                       }
-
-                                       if (sapi_module.default_post_reader) {
-                                               sapi_module.default_post_reader();
-                                       }
-
-                                       sapi_handle_post(&PG(http_globals)[TRACK_VARS_POST]);
-
-                                       /*
-                                        * the rfc1867 handler is an awkward buddy
-                                        * FIXME: this leaks because php_auto_globals_create_files()
-                                        * as well as the rfc1867_handler call
-                                        * array_init(&PG(http_globals)[TRACK_VARS_FILES])
-                                        */
-                                       Z_TRY_ADDREF(PG(http_globals)[TRACK_VARS_FILES]);
-                                       zend_hash_str_update(&EG(symbol_table), "_FILES", lenof("_FILES"), &PG(http_globals)[TRACK_VARS_FILES]);
-                               }
-                       }
-                       zend_hash_destroy(&params);
-               }
-       }
-
-       PTR_SET(SG(request_info).content_type_dup, NULL);
-
-       return SUCCESS;
-}
 
 PHP_RSHUTDOWN_FUNCTION(http_env)
 {
@@ -485,6 +431,7 @@ ZEND_RESULT_CODE php_http_env_set_response_header(long http_code, const char *he
 {
        sapi_header_line h = {estrndup(header_str, header_len), header_len, http_code};
        ZEND_RESULT_CODE ret = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, (void *) &h);
+
        efree(h.line);
        return ret;
 }
@@ -848,53 +795,6 @@ static zend_function_entry php_http_env_methods[] = {
        EMPTY_FUNCTION_ENTRY
 };
 
-#ifdef PHP_HTTP_HAVE_JSON
-#include "ext/json/php_json.h"
-
-static SAPI_POST_HANDLER_FUNC(php_http_json_post_handler)
-{
-       zval *zarg = arg;
-       zend_string *json = NULL;
-
-       if (SG(request_info).request_body) {
-               /* FG(stream_wrappers) not initialized yet, so we cannot use php://input */
-               php_stream_rewind(SG(request_info).request_body);
-               json = php_stream_copy_to_mem(SG(request_info).request_body, PHP_STREAM_COPY_ALL, 0);
-       }
-
-       if (json) {
-               if (json->len) {
-                       zval tmp;
-
-                       ZVAL_NULL(&tmp);
-                       php_json_decode(&tmp, json->val, json->len, 1, PG(max_input_nesting_level));
-
-                       if (Z_TYPE(tmp) == IS_ARRAY) {
-                               array_copy(Z_ARRVAL(tmp), Z_ARRVAL_P(zarg));
-                       }
-                       zval_ptr_dtor(&tmp);
-               }
-               zend_string_release(json);
-       }
-}
-
-static void php_http_env_register_json_handler(void)
-{
-       sapi_post_entry entry = {NULL, 0, NULL, NULL};
-
-       entry.post_reader = sapi_read_standard_form_data;
-       entry.post_handler = php_http_json_post_handler;
-
-       entry.content_type = "text/json";
-       entry.content_type_len = lenof("text/json");
-       sapi_register_post_entry(&entry);
-
-       entry.content_type = "application/json";
-       entry.content_type_len = lenof("application/json");
-       sapi_register_post_entry(&entry);
-}
-#endif
-
 zend_class_entry *php_http_env_class_entry;
 
 PHP_MINIT_FUNCTION(http_env)
@@ -904,10 +804,6 @@ PHP_MINIT_FUNCTION(http_env)
        INIT_NS_CLASS_ENTRY(ce, "http", "Env", php_http_env_methods);
        php_http_env_class_entry = zend_register_internal_class(&ce);
 
-#ifdef PHP_HTTP_HAVE_JSON
-       php_http_env_register_json_handler();
-#endif
-
        return SUCCESS;
 }
 
index 1cb302d13a3665cd11613e33f06e4765242b37b7..273ba16c4ffa9c0481184d41a2ff8cda46ab9ca0 100644 (file)
@@ -75,9 +75,9 @@ static inline zend_bool php_http_env_got_server_var(const char *v)
        return NULL != php_http_env_get_server_var(v, strlen(v), 1);
 }
 
+
 PHP_HTTP_API zend_class_entry *php_http_env_class_entry;
 PHP_MINIT_FUNCTION(http_env);
-PHP_RINIT_FUNCTION(http_env);
 PHP_RSHUTDOWN_FUNCTION(http_env);
 
 #endif
index cf30c84c48bc5ba0be2415a9a5572224adcfa940..f4dcf95e9e63828d9b43db7accd9024e28d8cf82 100644 (file)
@@ -284,7 +284,7 @@ php_http_header_parser_state_t php_http_header_parser_parse(php_http_header_pars
 
 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;
+       php_http_header_parser_state_t state = PHP_HTTP_HEADER_PARSER_STATE_START;
        TSRMLS_FETCH_FROM_CTX(parser->ts);
 
        if (!buf->data) {
index bd04f5092f9b233a9a53851f75858d4d668c8362..83ce01b9ddbb91322e1acd1fbbf857ff8b6300ac 100644 (file)
@@ -592,16 +592,21 @@ ZEND_END_ARG_INFO();
 PHP_METHOD(HttpQueryString, offsetSet)
 {
        zend_string *offset;
-       zval *value, param;
+       zval *value, param, znull;
        
        if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Sz", &offset, &value)) {
                return;
        }
 
-       array_init(&param);
+       array_init_size(&param, 1);
+       /* unset first */
+       ZVAL_NULL(&znull);
+       zend_symtable_update(Z_ARRVAL(param), offset, &znull);
+       php_http_querystring_set(getThis(), &param, QS_MERGE);
+       /* then update, else QS_MERGE would merge sub-arrrays */
        Z_TRY_ADDREF_P(value);
        zend_symtable_update(Z_ARRVAL(param), offset, value);
-       php_http_querystring_set(getThis(), &param, 0);
+       php_http_querystring_set(getThis(), &param, QS_MERGE);
        zval_ptr_dtor(&param);
 }
 
index 6c70bc3f2566f04bdaf0b5e782a27054ff12ad66..5a267d898ee086fb3be58638e42c5d49f37cf840 100644 (file)
@@ -673,8 +673,9 @@ static size_t parse_mb_loc(unsigned *wc, const char *ptr, const char *end)
        wchar_t wchar;
        size_t consumed = 0;
 #if defined(HAVE_MBRTOWC)
-       mbstate_t ps = {0};
+       mbstate_t ps;
 
+       memset(&ps, 0, sizeof(ps));
        consumed = mbrtowc(&wchar, ptr, end - ptr, &ps);
 #elif defined(HAVE_MBTOWC)
        consumed = mbtowc(&wchar, ptr, end - ptr);
@@ -756,9 +757,15 @@ static size_t parse_mb(struct parse_state *state, parse_mb_what_t what, const ch
        }
 
        if (!silent) {
-               php_error_docref(NULL, E_WARNING,
-                               "Failed to parse %s; unexpected byte 0x%02x at pos %u in '%s'",
-                               parse_what[what], (unsigned char) *ptr, (unsigned) (ptr - begin), begin);
+               if (consumed) {
+                       php_error_docref(NULL, E_WARNING,
+                                       "Failed to parse %s; unexpected multibyte sequence 0x%x at pos %u in '%s'",
+                                       parse_what[what], wchar, (unsigned) (ptr - begin), begin);
+               } else {
+                       php_error_docref(NULL, E_WARNING,
+                                       "Failed to parse %s; unexpected byte 0x%02x at pos %u in '%s'",
+                                       parse_what[what], (unsigned char) *ptr, (unsigned) (ptr - begin), begin);
+               }
        }
 
        return 0;
@@ -828,12 +835,176 @@ static ZEND_RESULT_CODE parse_userinfo(struct parse_state *state, const char *pt
        return SUCCESS;
 }
 
+#if defined(PHP_WIN32) || defined(HAVE_UIDNA_IDNTOASCII)
+typedef size_t (*parse_mb_func)(unsigned *wc, const char *ptr, const char *end);
+static ZEND_RESULT_CODE to_utf16(parse_mb_func fn, const char *u8, uint16_t **u16, size_t *len)
+{
+       size_t offset = 0, u8_len = strlen(u8);
+
+       *u16 = ecalloc(4 * sizeof(uint16_t), u8_len + 1);
+       *len = 0;
+
+       while (offset < u8_len) {
+               unsigned wc;
+               uint16_t buf[2], *ptr = buf;
+               size_t consumed = fn(&wc, &u8[offset], &u8[u8_len]);
+
+               if (!consumed) {
+                       efree(*u16);
+                       php_error_docref(NULL, E_WARNING, "Failed to parse UTF-8 at pos %zu of '%s'", offset, u8);
+                       return FAILURE;
+               } else {
+                       offset += consumed;
+               }
+
+               switch (wctoutf16(buf, wc)) {
+               case 2:
+                       (*u16)[(*len)++] = *ptr++;
+                       /* no break */
+               case 1:
+                       (*u16)[(*len)++] = *ptr++;
+                       break;
+               case 0:
+               default:
+                       efree(*u16);
+                       php_error_docref(NULL, E_WARNING, "Failed to convert UTF-32 'U+%X' to UTF-16", wc);
+                       return FAILURE;
+               }
+       }
+
+       return SUCCESS;
+}
+#endif
+
+#ifndef MAXHOSTNAMELEN
+#      define MAXHOSTNAMELEN 256
+#endif
+
+#ifdef PHP_HTTP_HAVE_IDN
+static ZEND_RESULT_CODE parse_idn(struct parse_state *state, size_t prev_len)
+{
+       char *idn = NULL;
+       int rv = -1;
+
+       if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
+               rv = idna_to_ascii_8z(state->url.host, &idn, IDNA_ALLOW_UNASSIGNED|IDNA_USE_STD3_ASCII_RULES);
+       }
+#      ifdef PHP_HTTP_HAVE_WCHAR
+       else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
+               rv = idna_to_ascii_lz(state->url.host, &idn, IDNA_ALLOW_UNASSIGNED|IDNA_USE_STD3_ASCII_RULES);
+       }
+#      endif
+       if (rv != IDNA_SUCCESS) {
+               php_error_docref(NULL, E_WARNING, "Failed to parse IDN; %s", idna_strerror(rv));
+               return FAILURE;
+       } else {
+               size_t idnlen = strlen(idn);
+               memcpy(state->url.host, idn, idnlen + 1);
+               free(idn);
+               state->offset += idnlen - prev_len;
+               return SUCCESS;
+       }
+}
+#endif
+
+#ifdef HAVE_UIDNA_IDNTOASCII
+#      if HAVE_UNICODE_UIDNA_H
+#              include <unicode/uidna.h>
+#      else
+typedef uint16_t UChar;
+typedef enum { U_ZERO_ERROR = 0 } UErrorCode;
+int32_t uidna_IDNToASCII(const UChar *src, int32_t srcLength, UChar *dest, int32_t destCapacity, int32_t options, void *parseError, UErrorCode *status);
+#      endif
+static ZEND_RESULT_CODE parse_uidn(struct parse_state *state)
+{
+       char *host_ptr;
+       uint16_t *uhost_str, ahost_str[MAXHOSTNAMELEN], *ahost_ptr;
+       size_t uhost_len, ahost_len;
+       UErrorCode error = U_ZERO_ERROR;
+       TSRMLS_FETCH_FROM_CTX(state->ts);
+
+       if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
+               if (SUCCESS != to_utf16(parse_mb_utf8, state->url.host, &uhost_str, &uhost_len)) {
+                       return FAILURE;
+               }
+#ifdef PHP_HTTP_HAVE_WCHAR
+       } else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
+               if (SUCCESS != to_utf16(parse_mb_loc, state->url.host, &uhost_str, &uhost_len)) {
+                       return FAILURE;
+               }
+#endif
+       } else {
+               php_error_docref(NULL, E_WARNING, "Failed to parse IDN; codepage not specified");
+               return FAILURE;
+       }
+
+       ahost_len = uidna_IDNToASCII(uhost_str, uhost_len, ahost_str, MAXHOSTNAMELEN, 3, NULL, &error);
+       efree(uhost_str);
+
+       if (error != U_ZERO_ERROR) {
+               php_error_docref(NULL, E_WARNING, "Failed to parse IDN; ICU error %d", error);
+               return FAILURE;
+       }
+
+       host_ptr = state->url.host;
+       ahost_ptr = ahost_str;
+       PHP_HTTP_DUFF(ahost_len, *host_ptr++ = *ahost_ptr++);
+
+       *host_ptr = '\0';
+       state->offset += host_ptr - state->url.host;
+
+       return SUCCESS;
+}
+#endif
+
+#if 0 && defined(PHP_WIN32)
+static ZEND_RESULT_CODE parse_widn(struct parse_state *state)
+{
+       char *host_ptr;
+       uint16_t *uhost_str, ahost_str[MAXHOSTNAMELEN], *ahost_ptr;
+       size_t uhost_len;
+
+       if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
+               if (SUCCESS != to_utf16(parse_mb_utf8, state->url.host, &uhost_str, &uhost_len)) {
+                       php_error_docref(NULL, E_WARNING, "Failed to parse IDN");
+                       return FAILURE;
+               }
+#ifdef PHP_HTTP_HAVE_WCHAR
+       } else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
+               if (SUCCESS != to_utf16(parse_mb_loc, state->url.host, &uhost_str, &uhost_len)) {
+                       php_error_docref(NULL, E_WARNING, "Failed to parse IDN");
+                       return FAILURE;
+               }
+#endif
+       } else {
+               php_error_docref(NULL, E_WARNING, "Failed to parse IDN");
+               return FAILURE;
+       }
+
+       if (!IdnToAscii(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES, uhost_str, uhost_len, ahost_str, MAXHOSTNAMELEN)) {
+               efree(uhost_str);
+               php_error_docref(NULL, E_WARNING, "Failed to parse IDN");
+               return FAILURE;
+       }
+
+       efree(uhost_str);
+       host_ptr = state->url.host;
+       ahost_ptr = ahost_str;
+       PHP_HTTP_DUFF(wcslen(ahost_str), *host_ptr++ = *ahost_ptr++);
+       efree(ahost_str);
+
+       *host_ptr = '\0';
+       state->offset += host_ptr - state->url.host;
+
+       return SUCCESS;
+}
+#endif
+
 static ZEND_RESULT_CODE parse_hostinfo(struct parse_state *state, const char *ptr)
 {
        size_t mb, len;
        const char *end = state->ptr, *tmp = ptr, *port = NULL;
 
-
 #ifdef HAVE_INET_PTON
        if (*ptr == '[') {
                char *error = NULL, *tmp = memchr(ptr, ']', end - ptr);
@@ -941,30 +1112,17 @@ static ZEND_RESULT_CODE parse_hostinfo(struct parse_state *state, const char *pt
                state->buffer[state->offset++] = 0;
        }
 
-#ifdef PHP_HTTP_HAVE_IDN
        if (state->flags & PHP_HTTP_URL_PARSE_TOIDN) {
-               char *idn = NULL;
-               int rv = -1;
-
-               if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
-                       rv = idna_to_ascii_8z(state->url.host, &idn, IDNA_ALLOW_UNASSIGNED|IDNA_USE_STD3_ASCII_RULES);
-               }
-#      ifdef PHP_HTTP_HAVE_WCHAR
-               else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
-                       rv = idna_to_ascii_lz(state->url.host, &idn, IDNA_ALLOW_UNASSIGNED|IDNA_USE_STD3_ASCII_RULES);
-               }
-#      endif
-               if (rv != IDNA_SUCCESS) {
-                       php_error_docref(NULL, E_WARNING, "Failed to parse IDN; %s", idna_strerror(rv));
-                       return FAILURE;
-               } else {
-                       size_t idnlen = strlen(idn);
-                       memcpy(state->url.host, idn, idnlen + 1);
-                       free(idn);
-                       state->offset += idnlen - len;
-               }
-       }
+#ifdef PHP_HTTP_HAVE_IDN
+               return parse_idn(state, len);
+#endif
+#ifdef HAVE_UIDNA_IDNTOASCII
+               return parse_uidn(state);
 #endif
+#if 0 && defined(PHP_WIN32)
+               return parse_widn(state);
+#endif
+       }
 
        return SUCCESS;
 }
@@ -1498,7 +1656,7 @@ PHP_MINIT_FUNCTION(http_url)
        zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_MBLOC"), PHP_HTTP_URL_PARSE_MBLOC);
 #endif
        zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_MBUTF8"), PHP_HTTP_URL_PARSE_MBUTF8);
-#ifdef PHP_HTTP_HAVE_IDN
+#if defined(PHP_HTTP_HAVE_IDN) || defined(HAVE_UIDNA_IDNTOASCII)
        zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_TOIDN"), PHP_HTTP_URL_PARSE_TOIDN);
 #endif
        zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_TOPCT"), PHP_HTTP_URL_PARSE_TOPCT);
index 2503f0644e605db64aae577b6523e641b3e54098..f9008032dff0eea98abc8c59cb1134b35ab3b7c3 100644 (file)
@@ -625,6 +625,39 @@ static inline zend_bool isualnum(unsigned ch)
        return isualpha(ch);
 }
 
+static inline size_t wctoutf16(unsigned short u16[2], unsigned wc)
+{
+       if (wc > 0x10ffff || (wc >= 0xd800 && wc <= 0xdfff)) {
+               return 0;
+       }
+
+       if (wc <= 0xffff) {
+               u16[0] = (unsigned short) wc;
+               return 1;
+       }
+
+       wc -= 0x10000;
+       u16[0] = (unsigned short) ((wc >> 10) + 0xd800);
+       u16[1] = (unsigned short) ((wc & 0x3ff) + 0xdc00);
+       return 2;
+}
+
+static inline size_t utf16towc(unsigned *wc, unsigned short *u16_str, size_t u16_len)
+{
+       if (u16_len < 1) {
+               return 0;
+       }
+       if (u16_str[0] - 0xd800 >= 0x800) {
+               *wc = u16_str[0];
+               return 1;
+       }
+       if (u16_len < 2 || (u16_str[0] & 0xfffffc00) != 0xd800 || (u16_str[1] & 0xfffffc00) != 0xdc00) {
+               return 0;
+       }
+       *wc = (u16_str[0] << 10) + u16_str[1] - 0x35fdc00;
+       return 2;
+}
+
 #endif /* PHP_HTTP_UTF8_H */
 
 /*
index 9f9c95e9ed1aa723fe3b41c717e0850b9715f10c..e4cbf2ace925966ab1964cfdc92cf1a0b4167148 100644 (file)
@@ -27,7 +27,7 @@ echo $client->send()->getResponse()->getResponseCode();
 ?>
 
 ===DONE===
---EXPECT--
+--EXPECTF--
 Test
-401
+40%d
 ===DONE===
index 0fd84f882b4f03a0c026452d18d056e475490bfa..30f9d7e9b16aaf8373fb375ba6426ef06368d46f 100644 (file)
@@ -12,7 +12,7 @@ header("WWW-Authenticate: none");
 $r = new http\Env\Response;
 $r->setResponseCode(200);
 $r->send();
-var_dump(http_response_code());
+var_dump(http\Env::getResponseCode());
 ?>
 --EXPECT--
 int(200)
\ No newline at end of file
index a6b1a4c7cfb4fef4d971196e6fe70bd6a99e1f6e..bca33024f93019ac1c2f2a495a88530c7ad20b5e 100644 (file)
@@ -3,6 +3,7 @@ client once & wait
 --SKIPIF--
 <?php
 include "skipif.inc";
+skip_online_test();
 skip_client_test();
 ?>
 --FILE--
index f8b8774eb8edc1c7511b8c7961438470fd9d5a88..98fc8f8aebc34d9da458bbf1a252a10f208b8e83 100644 (file)
@@ -18,7 +18,7 @@ server("pipeline.inc", function($port, $stdin) {
        $request = new http\Client\Request("GET", "http://localhost:$port");
        
        $client = new http\Client();
-       $client->configure(["pipelining" => true, "use_eventloop" => true]);
+       $client->configure(array("pipelining" => true, "use_eventloop" => true));
 
        $client->enqueue($request);
        $client->send();
index 70b5f43bc603c8a417bc08eeeac0d5c5787ba674..f378653808824a2eb873857fe5242d8267fb86bc 100644 (file)
@@ -16,8 +16,8 @@ server("proxy.inc", function($port) {
        $client1 = new http\Client;
        $client2 = new http\Client;
        
-       $client1->configure(["use_eventloop" => true]);
-       $client2->configure(["use_eventloop" => true]);
+       $client1->configure(array("use_eventloop" => true));
+       $client2->configure(array("use_eventloop" => true));
        
        $client1->enqueue(new http\Client\Request("GET", "http://localhost:$port/"));
        $client2->enqueue(new http\Client\Request("GET", "http://localhost:$port/"));
index 58f97c1ff0332cf8a1e7cace580e33499063e5df..d2aef073d08d986cec6cdc839a315aafe17224bf 100644 (file)
@@ -17,7 +17,7 @@ server("proxy.inc", function($port) {
        
        foreach (http\Client::getAvailableDrivers() as $driver) {
                $client = new http\Client($driver);
-               $client->configure(["use_eventloop" => true]);
+               $client->configure(array("use_eventloop" => true));
                $client->enqueue($request);
        
                while ($client->once()) {
index ea54146dcbe8bed7c1b82bc633d8dd1cd005c946..a50502803590a0e93c366f51569127b3503ea391 100644 (file)
@@ -12,7 +12,7 @@ skip_client_test();
 echo "Test\n";
 
 $client = new http\Client;
-$client->setOptions(["compress" => true]);
+$client->setOptions(array("compress" => true));
 
 $client->enqueue(new http\Client\Request("GET", "http://dev.iworks.at/ext-http/.print_request.php"));
 $client->send();
index 7acb911ad6c94a92ea51dd5a203be964200cceaa..c3ca9f96f3f655b9dff2f602187ed8f32b704955 100644 (file)
@@ -17,7 +17,7 @@ server("pipeline.inc", function($port, $stdin, $stdout, $stderr) {
        fputs($stdin, "3\n");
        
        $client = new http\Client(null);
-       $client->configure(["pipelining" => true, "max_host_connections" => 0]);
+       $client->configure(array("pipelining" => true, "max_host_connections" => 0));
        
        /* this is just to let curl know the server may be capable of pipelining */
        $client->enqueue(new http\Client\Request("GET", "http://localhost:$port"));
index e60e5aa0a1a4b75c579e880ba695b541ceb6ce68..c41a260f0acff98b8e4d29167374ee97f5f018ae 100644 (file)
@@ -4,6 +4,9 @@ client proxy - send proxy headers for a proxy request
 <?php 
 include "skipif.inc";
 skip_client_test();
+$client = new http\Client("curl");
+array_key_exists("proxyheader", $client->getAvailableOptions())
+       or die("skip need libcurl with CUTLOPT_PROXYHEADER support\n");
 ?>
 --FILE--
 <?php
index 41a220a6d48e7925924761c966461790db6b05ab..1335aaa8ebe80bd9f0c068ea4e3921a1d54b229a 100644 (file)
@@ -14,10 +14,10 @@ echo "Test\n";
 
 $tmpfile = tempnam(sys_get_temp_dir(), "cookie.");
 $request = new http\Client\Request("GET", "http://localhost");
-$request->setOptions(["cookiestore" => $tmpfile]);
+$request->setOptions(array("cookiestore" => $tmpfile));
 
 server("cookie.inc", function($port) use($request) {
-       $request->setOptions(["port" => $port]);
+       $request->setOptions(array("port" => $port));
        $client = new http\Client;
        echo $client->requeue($request)->send()->getResponse();
        echo $client->requeue($request)->send()->getResponse();
@@ -25,7 +25,7 @@ server("cookie.inc", function($port) use($request) {
 });
 
 server("cookie.inc", function($port) use($request) {
-       $request->setOptions(["port" => $port]);
+       $request->setOptions(array("port" => $port));
        $client = new http\Client;
        echo $client->requeue($request)->send()->getResponse();
        echo $client->requeue($request)->send()->getResponse();
@@ -33,7 +33,7 @@ server("cookie.inc", function($port) use($request) {
 });
 
 server("cookie.inc", function($port) use($request) {
-       $request->setOptions(["port" => $port, "cookiesession" => true]);
+       $request->setOptions(array("port" => $port, "cookiesession" => true));
        $client = new http\Client;
        echo $client->requeue($request)->send()->getResponse();
        echo $client->requeue($request)->send()->getResponse();
@@ -41,7 +41,7 @@ server("cookie.inc", function($port) use($request) {
 });
 
 server("cookie.inc", function($port) use($request) {
-       $request->setOptions(["port" => $port, "cookiesession" => false]);
+       $request->setOptions(array("port" => $port, "cookiesession" => false));
        $client = new http\Client;
        echo $client->requeue($request)->send()->getResponse();
        echo $client->requeue($request)->send()->getResponse();
index b09c2e2b655337618bc7b50c48465ec8c0009f66..77ea7cee6f81deb3a3eb25e56057e6243a56d6dd 100644 (file)
@@ -22,11 +22,11 @@ foreach ($avail as $k => $v) {
                $oo = $opt[$k];
                foreach ($v as $kk => $vv) {
                        if (isset($vv) && $oo[$kk] !== $vv) {
-                               var_dump([$kk => [$vv, $oo[$kk]]]);
+                               var_dump(array($kk => array($vv, $oo[$kk])));
                        }
                }
        } else if (isset($v) && $opt[$k] !== $v) {
-               var_dump([$k => [$v, $opt[$k]]]);
+               var_dump(array($k => array($v, $opt[$k])));
        }
 }
 var_dump($client === $client->configure($client->getAvailableConfiguration()));
index 18c5095cdf01d8289f4b61991aea2d3805db903c..3c4793e96b5793a5a2b25f31fa1c80e4b59b7c7f 100644 (file)
@@ -14,11 +14,11 @@ echo "Test\n";
 server("proxy.inc", function($port) {
        $client = new http\Client;
        $request = new http\Client\Request("PUT", "http://localhost:$port");
-       $request->setOptions(["resume" => 1, "expect_100_timeout" => 0]);
+       $request->setOptions(array("resume" => 1, "expect_100_timeout" => 0));
        $request->getBody()->append("123");
        echo $client->enqueue($request)->send()->getResponse();
 });
-
+// Content-length is 2 instead of 3 in older libcurls
 ?>
 ===DONE===
 --EXPECTF--
@@ -34,8 +34,8 @@ Content-Range: bytes 1-2/3
 User-Agent: %s
 Host: localhost:%d
 Accept: */*
-Content-Length: 3
+Content-Length: %d
 Expect: 100-continue
-X-Original-Content-Length: 3
+X-Original-Content-Length: %d
 
 23===DONE===
index d89e98ef592f7dbf5bed6a9a75cbb36b2bb64f3b..1bafdc1b154d1d08a663e1856135b2e83e2f78ae 100644 (file)
@@ -19,7 +19,7 @@ server("proxy.inc", function($port) {
        for ($i = 0, $data = str_repeat("a",1024); $i < 128*1024; ++$i) {
                $request->getBody()->append($data);
        }
-       $request->setOptions(["timeout" => 10, "expect_100_timeout" => 0]);
+       $request->setOptions(array("timeout" => 10, "expect_100_timeout" => 0));
        $client->enqueue($request);
        $client->send();
        var_dump($client->getResponse()->getHeaders());
diff --git a/tests/envrequestbody002.phpt b/tests/envrequestbody002.phpt
deleted file mode 100644 (file)
index 69c5988..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
---TEST--
-env request body
---SKIPIF--
-<?php include "skipif.inc"; ?>
---PUT--
-Content-Type: application/x-www-form-urlencoded
-foo=bar&baz=buh
---FILE--
-<?php
-var_dump($_POST);
-?>
-DONE
---EXPECT--
-array(2) {
-  ["foo"]=>
-  string(3) "bar"
-  ["baz"]=>
-  string(3) "buh"
-}
-DONE
diff --git a/tests/envrequestbody003.phpt b/tests/envrequestbody003.phpt
deleted file mode 100644 (file)
index c2bde83..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
---TEST--
-env request body
---SKIPIF--
-<?php include "skipif.inc"; ?>
---PUT--
-Content-Type: multipart/form-data;boundary=123
---123
-Content-Disposition: form-data; name="foo"
-
-bar
---123
-Content-Disposition: form-data; name="baz"
-
-buh
---123
-Content-Disposition: form-data; name="up"; filename="up.txt"
-
-foo=bar&baz=buh
---123--
---FILE--
-<?php
-var_dump($_POST);
-var_dump($_FILES);
-?>
-DONE
---EXPECTF--
-array(2) {
-  ["foo"]=>
-  string(3) "bar"
-  ["baz"]=>
-  string(3) "buh"
-}
-array(1) {
-  ["up"]=>
-  array(5) {
-    ["name"]=>
-    string(6) "up.txt"
-    ["type"]=>
-    string(0) ""
-    ["tmp_name"]=>
-    string(%d) "%s"
-    ["error"]=>
-    int(0)
-    ["size"]=>
-    int(15)
-  }
-}
-DONE
diff --git a/tests/envrequestjson001.phpt b/tests/envrequestjson001.phpt
deleted file mode 100644 (file)
index 3dfd9a8..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
---TEST--
-env request json
---SKIPIF--
-<?php
-include "skipif.inc";
-_ext("json");
-?>
---POST_RAW--
-Content-Type: application/json
-
-{"foo": "bar", "a": [1,2,3]}
---FILE--
-<?php
-echo "Test\n";
-print_r($_POST);
-?>
-Done
---EXPECT--
-Test
-Array
-(
-    [foo] => bar
-    [a] => Array
-        (
-            [0] => 1
-            [1] => 2
-            [2] => 3
-        )
-
-)
-Done
-
diff --git a/tests/envrequestjson002.phpt b/tests/envrequestjson002.phpt
deleted file mode 100644 (file)
index 4c404c0..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
---TEST--
-env request json
---SKIPIF--
-<?php
-include "skipif.inc";
-_ext("json");
-?>
---PUT--
-Content-Type: application/json
-
-{"foo": "bar", "a": [1,2,3]}
---FILE--
-<?php
-echo "Test\n";
-print_r($_POST);
-?>
-Done
---EXPECT--
-Test
-Array
-(
-    [foo] => bar
-    [a] => Array
-        (
-            [0] => 1
-            [1] => 2
-            [2] => 3
-        )
-
-)
-Done
-
index ffa40cbb6f13fcd444c18ace971671e9b77d4393..4c832f25536f4fb652ea4fc9bbaf22b6ff7902fc 100644 (file)
@@ -10,7 +10,7 @@ echo "Test\n";
 
 $req = new http\Env\Request;
 $req->setRequestMethod("CONNECT");
-$req->setRequestUrl(["host"=>"www.example.com", "port"=>80]);
+$req->setRequestUrl(array("host"=>"www.example.com", "port"=>80));
 
 echo $req;
 
index 0a1eb37ad676a5c31db57d9b5ed7729e3ad6db82..cdd55e4fefd9480992d934ffe7f3a13bff02e14f 100644 (file)
@@ -8,16 +8,16 @@ include "skipif.inc";
 <?php
 echo "Test\n";
 
-$headers = [
+$headers = array(
        "One: ","header\n",
        "Two: header\n\tlines\n",
        "Three",": header\n lines\n here\n",
        "More: than one header\n",
        "More: ", "than: ", "you: ", "expect\n",
        "\n",
-];
+);
 
-$states = [-1=>"FAILURE",0=>"START","KEY","VALUE","VALUE_EX","HEADER_DONE","DONE"];
+$states = array(-1=>"FAILURE",0=>"START","KEY","VALUE","VALUE_EX","HEADER_DONE","DONE");
 $parser = new http\Header\Parser;
 do {
        $state = $parser->parse($part = array_shift($headers), 
index c5a02f19778c4e0f705971b10e7e17175204ae55..67e6681f25e391644593ad57d8eae7dfe2f7b123 100644 (file)
@@ -8,14 +8,14 @@ include "skipif.inc";
 <?php 
 echo "Test\n";
 
-$headers = [
+$headers = array(
        "Na\0me: value",
        "Na\nme: value",
        "Name:\0value",
        "Name:\nvalue",
        "Name: val\0ue",
        "Name: value\0",
-];
+);
 
 foreach ($headers as $header) {
        $parsed = null;
index e1954e7a0a02011fc665cccea721a4c4e3a660ea..1216fac16797196b093ad8f235979cd8a8a7ba07 100644 (file)
@@ -12,12 +12,12 @@ $parser = new http\Header\Parser;
 $socket = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
 stream_set_blocking($socket[0], 0);
 
-$headers = [
+$headers = array(
 "GET / HTTP/1.1\n",
 "Host: localhost","\n",
 "Content","-length: 3\n",
 "\n",
-];
+);
 
 while ($headers) {
        $line = array_shift($headers);
index 815b46339b672574284138ec976cf8da0b472827..b7175c109fac1b24d4f4a31e000e08cda288ae4d 100644 (file)
@@ -3,7 +3,8 @@
 include "server.inc";
 
 function respond($client, $msg) {
-       (new http\Env\Response)->setEnvRequest($msg)
+       $r = new http\Env\Response;
+       $r->setEnvRequest($msg)
                ->setHeader("X-Req", $msg->getRequestUrl())
                ->send($client);
 }
index 0605adc145a9871f0051ea6d4d926b5886e32fa7..506e08313b2c70f71a97f494bb5097171bc7a1f0 100644 (file)
@@ -1,11 +1,25 @@
-<?php 
+<?php
 
-function serve(callable $cb) {
-       foreach (range(8000, 9000) as $port) {
+$php = getenv('TEST_PHP_EXECUTABLE');
+if ($php) {
+       define('PHP_BIN', $php);
+} else if (defined('PHP_BINARY')) {
+       define('PHP_BIN', PHP_BINARY);
+} else {
+       // PHP-5.3
+       define("PHP_BIN", PHP_BINDIR.DIRECTORY_SEPARATOR."php");
+}
+
+function serve($cb) {
+       /* stream_socket_server() automatically sets SO_REUSEADDR, 
+        * which is, well, bad if the tests are run in parallel
+        */
+       $offset = rand(0,2000);
+       foreach (range(8000+$offset, 9000+$offset) as $port) {
                if (($server = @stream_socket_server("tcp://localhost:$port"))) {
                        fprintf(STDERR, "%s\n", $port);
                        do {
-                               $R = [$server]; $W = []; $E = [];
+                               $R = array($server); $W = array(); $E = array();
                                $select = stream_select($R, $E, $E, 0, 10000);
                                if ($select && ($client = stream_socket_accept($server, 1))) {
                                        if (getenv("PHP_HTTP_TEST_SSL")) {
@@ -29,13 +43,19 @@ function serve(callable $cb) {
        }
 }
 
-function server($handler, callable $cb) {
-       proc(PHP_BINARY, [__DIR__."/$handler"], $cb);
+function server($handler, $cb) {
+       $args = explode(' ', getenv('TEST_PHP_ARGS'));
+       $args[] = __DIR__."/$handler";
+       foreach ($args as $k => $v) {
+               if (!$v) unset($args[$k]);
+       }
+       proc(PHP_BIN, $args, $cb);
 }
 
-function nghttpd(callable $cb) {
-       $spec = [["pipe","r"], ["pipe","w"], ["pipe","w"]];
-       foreach (range(8000, 9000) as $port) {
+function nghttpd($cb) {
+       $spec = array(array("pipe","r"), array("pipe","w"), array("pipe","w"));
+       $offset = rand(0,2000);
+       foreach (range(8000+$offset, 9000+$offset) as $port) {
                $comm = "exec nghttpd -d html $port http2.key http2.crt";
                if (($proc = proc_open($comm, $spec, $pipes, __DIR__))) {
                        $stdin = $pipes[0];
@@ -65,17 +85,17 @@ function nghttpd(callable $cb) {
                        
 }
 
-function proc($bin, $args, callable $cb) {
-       $spec = [["pipe","r"], ["pipe","w"], ["pipe","w"]];
+function proc($bin, $args, $cb) {
+       $spec = array(array("pipe","r"), array("pipe","w"), array("pipe","w"));
        $comm = escapeshellcmd($bin) . " ". implode(" ", array_map("escapeshellarg", $args));
        if (($proc = proc_open($comm, $spec, $pipes, __DIR__))) {
                $stdin = $pipes[0];
                $stdout = $pipes[1];
                $stderr = $pipes[2];
-               
+
                do {
                        $port = trim(fgets($stderr));
-                       $R = [$stderr]; $W = []; $E = [];
+                       $R = array($stderr); $W = array(); $E = array();
                } while (is_numeric($port) && stream_select($R, $W, $E, 0, 10000));
        
                if (is_numeric($port)) {
index 72690e472ba290db865fa54e4e8cb5cdc995daa7..093dcd169c63e3334271666cb390efa4693f4cba 100644 (file)
@@ -9,7 +9,7 @@ include "skipif.inc";
 
 echo "Test\n";
 
-function trap(callable $cb) {
+function trap($cb) {
        try {
                $cb();
        } catch (Exception $e) { 
index d2d22a50aafe2c2d1eaa857f8eb59878c7bb3d93..1167c64115b2d85d8ef07958dad756f36f77e017 100644 (file)
@@ -26,7 +26,7 @@ foreach (glob(__DIR__."/data/message_*.txt") as $file) {
        }
        
        if (!$string) {
-               $s = ["START", "HEADER", "HEADER_DONE", "BODY", "BODY_DUMB", "BODY_LENGTH", "BODY_CHUNK", "BODY_DONE", "UPDATE_CL", "DONE"];
+               $s = array("START", "HEADER", "HEADER_DONE", "BODY", "BODY_DUMB", "BODY_LENGTH", "BODY_CHUNK", "BODY_DONE", "UPDATE_CL", "DONE");
                printf("Unexpected state: %s (%s)\n", $s[$parser->getState()], $file);
        }
 
diff --git a/tests/querystring003.phpt b/tests/querystring003.phpt
new file mode 100644 (file)
index 0000000..caa1745
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+querystring offset set
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+
+echo "Test\n";
+
+$qs = new http\QueryString("foo=bar&bar=baz");
+echo $qs,"\n";
+$qs["foo"] = "baz";
+echo $qs,"\n";
+?>
+===DONE===
+--EXPECT--
+Test
+foo=bar&bar=baz
+bar=baz&foo=baz
+===DONE===
index cceeaf077e82820daeea9e84423986ac1524177a..4ab631e63d5b9b00b2e1be2c3efe174bfc3791bb 100644 (file)
@@ -21,9 +21,11 @@ function skip_client_test($message = "skip need a client driver\n") {
 }
 
 function skip_http2_test($message = "skip need http2 support (nghttpd in PATH)\n") {
-       foreach (explode(":", $_ENV["PATH"]) as $path) {
-               if (is_executable($path . "/nghttpd")) {
-                       return;
+       if (defined("http\\Client\\Curl\\HTTP_VERSION_2_0")) {
+               foreach (explode(":", $_ENV["PATH"]) as $path) {
+                       if (is_executable($path . "/nghttpd")) {
+                               return;
+                       }
                }
        }
        die($message);