Merge R_2_5
authorMichael Wallner <mike@php.net>
Fri, 25 Sep 2015 12:37:15 +0000 (14:37 +0200)
committerMichael Wallner <mike@php.net>
Fri, 25 Sep 2015 12:37:15 +0000 (14:37 +0200)
php_http_client_curl.c
php_http_info.c
php_http_message.c
php_http_url.c
tests/bug66388.phpt
tests/client003.phpt
tests/gh-issue12.phpt [new file with mode: 0644]

index a4e84ced00cec07c97a0e44fdeecbff04d16914e..85f12b7734f4201d6c308687d52f9e471d3e8150 100644 (file)
@@ -1228,6 +1228,11 @@ static void php_http_curle_options_init(php_http_options_t *registry)
 {
        php_http_option_t *opt;
 
+       /* url options */
+#if PHP_HTTP_CURL_VERSION(7,42,0)
+       php_http_option_register(registry, ZEND_STRL("path_as_is"), CURLOPT_PATH_AS_IS, IS_BOOL);
+#endif
+
        /* proxy */
        if ((opt = php_http_option_register(registry, ZEND_STRL("proxyhost"), CURLOPT_PROXY, IS_STRING))) {
                opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
@@ -1528,6 +1533,7 @@ static void php_http_curle_options_init(php_http_options_t *registry)
                }
 #endif
 #if PHP_HTTP_CURL_VERSION(7,39,0)
+               /* FIXME: see http://curl.haxx.se/libcurl/c/CURLOPT_PINNEDPUBLICKEY.html#AVAILABILITY */
                if ((opt = php_http_option_register(registry, ZEND_STRL("pinned_publickey"), CURLOPT_PINNEDPUBLICKEY, IS_STRING))) {
                        opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
                        opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
@@ -2027,9 +2033,14 @@ static ZEND_RESULT_CODE php_http_client_curl_handler_prepare(php_http_client_cur
         * See also https://tools.ietf.org/html/rfc7231#section-5.1.1
         */
        if (PHP_HTTP_INFO(msg).request.method) {
-               if (!strcasecmp("PUT", PHP_HTTP_INFO(msg).request.method)) {
+               switch(php_http_select_str(PHP_HTTP_INFO(msg).request.method, 2, "HEAD", "PUT")) {
+               case 0:
+                       curl_easy_setopt(curl->handle, CURLOPT_NOBODY, 1L);
+                       break;
+               case 1:
                        curl_easy_setopt(curl->handle, CURLOPT_UPLOAD, 1L);
-               } else {
+                       break;
+               default:
                        curl_easy_setopt(curl->handle, CURLOPT_CUSTOMREQUEST, PHP_HTTP_INFO(msg).request.method);
                }
        } else {
index f389be9c9fa02da5e756b10e6241aecda6a0fba9..6eef822707c1bfa6082672be7a91762e31ee094e 100644 (file)
@@ -139,6 +139,10 @@ php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_head
                                } else {
                                        PHP_HTTP_INFO(info).request.url = php_http_url_parse_authority(url, http - url, ~0);
                                }
+                               if (!PHP_HTTP_INFO(info).request.url) {
+                                       PTR_SET(PHP_HTTP_INFO(info).request.method, NULL);
+                                       return NULL;
+                               }
                        } else {
                                PTR_SET(PHP_HTTP_INFO(info).request.method, NULL);
                                return NULL;
index f06065a4eb05ef138e5b09ed2ebb031a64da7961..e6cdbc8f143782b56784239e6ebde8e16b552477 100644 (file)
@@ -1575,10 +1575,10 @@ static PHP_METHOD(HttpMessage, setRequestUrl)
        url = php_http_url_from_zval(zurl, ~0);
        zend_restore_error_handling(&zeh);
 
-       if (php_http_url_is_empty(url)) {
+       if (url && php_http_url_is_empty(url)) {
                php_http_url_free(&url);
                php_http_throw(invalid_arg, "Cannot set http\\Message's request url to an empty string", NULL);
-       } else {
+       } else if (url) {
                PTR_SET(obj->message->http.info.request.url, url);
        }
 
index afe39bfe4511711cb5db2cff518c31e0f4e8952c..c5c19a55568bd6fd10ee8f310fb5a247d0e72030 100644 (file)
@@ -1026,44 +1026,55 @@ static ZEND_RESULT_CODE parse_widn(struct parse_state *state)
 }
 #endif
 
-static ZEND_RESULT_CODE parse_hostinfo(struct parse_state *state, const char *ptr)
+#ifdef HAVE_INET_PTON
+static const char *parse_ip6(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);
-
-               if (tmp) {
-                       size_t addrlen = tmp - ptr + 1;
-                       char buf[16], *addr = estrndup(ptr + 1, addrlen - 2);
-                       int rv = inet_pton(AF_INET6, addr, buf);
-
-                       efree(addr);
-                       if (rv == 1) {
-                               state->buffer[state->offset] = '[';
-                               state->url.host = &state->buffer[state->offset];
-                               inet_ntop(AF_INET6, buf, state->url.host + 1, state->maxlen - state->offset);
-                               state->offset += strlen(state->url.host);
-                               state->buffer[state->offset++] = ']';
-                               state->buffer[state->offset++] = 0;
-                               ptr = tmp + 1;
-                       } else if (rv == -1) {
-                               error = strerror(errno);
-                       } else {
-                               error = "unexpected '['";
-                       }
+       const char *error = NULL, *end = state->ptr, *tmp = memchr(ptr, ']', end - ptr);
+
+       if (tmp) {
+               size_t addrlen = tmp - ptr + 1;
+               char buf[16], *addr = estrndup(ptr + 1, addrlen - 2);
+               int rv = inet_pton(AF_INET6, addr, buf);
+
+               if (rv == 1) {
+                       state->buffer[state->offset] = '[';
+                       state->url.host = &state->buffer[state->offset];
+                       inet_ntop(AF_INET6, buf, state->url.host + 1, state->maxlen - state->offset);
+                       state->offset += strlen(state->url.host);
+                       state->buffer[state->offset++] = ']';
+                       state->buffer[state->offset++] = 0;
+                       ptr = tmp + 1;
+               } else if (rv == -1) {
+                       error = strerror(errno);
                } else {
-                       error = "expected ']'";
+                       error = "unexpected '['";
                }
+               efree(addr);
+       } else {
+               error = "expected ']'";
+       }
 
-               if (error) {
-                       php_error_docref(NULL, E_WARNING, "Failed to parse hostinfo; %s", error);
-                       return FAILURE;
-               }
+       if (error) {
+               php_error_docref(NULL, E_WARNING, "Failed to parse hostinfo; %s", error);
+               return NULL;
        }
+
+       return ptr;
+}
 #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, *label = NULL;
+
+#ifdef HAVE_INET_PTON
+       if (*ptr == '[' && !(ptr = parse_ip6(state, ptr))) {
+               return FAILURE;
+       }
+#endif
+
        if (ptr != end) do {
                switch (*ptr) {
                case ':':
@@ -1091,6 +1102,20 @@ static ZEND_RESULT_CODE parse_hostinfo(struct parse_state *state, const char *pt
                case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
                case '+': case ',': case ';': case '=': /* sub-delims */
                case '-': case '.': case '_': case '~': /* unreserved */
+                       if (port || !label) {
+                               /* sort of a compromise, just ensure we don't end up
+                                * with a dot at the beginning or two consecutive dots
+                                */
+                               php_error_docref(NULL, E_WARNING,
+                                               "Failed to parse %s; unexpected '%c' at pos %u in '%s'",
+                                               port ? "port" : "host",
+                                               (unsigned char) *ptr, (unsigned) (ptr - tmp), tmp);
+                               return FAILURE;
+                       }
+                       state->buffer[state->offset++] = *ptr;
+                       label = NULL;
+                       break;
+
                case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
                case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
                case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
@@ -1113,6 +1138,7 @@ static ZEND_RESULT_CODE parse_hostinfo(struct parse_state *state, const char *pt
                                state->url.port *= 10;
                                state->url.port += *ptr - '0';
                        } else {
+                               label = ptr;
                                state->buffer[state->offset++] = *ptr;
                        }
                        break;
@@ -1128,6 +1154,7 @@ static ZEND_RESULT_CODE parse_hostinfo(struct parse_state *state, const char *pt
                        } else if (!(mb = parse_mb(state, PARSE_HOSTINFO, ptr, end, tmp, 0))) {
                                return FAILURE;
                        }
+                       label = ptr;
                        ptr += mb - 1;
                }
        } while (++ptr != end);
index e4cbf2ace925966ab1964cfdc92cf1a0b4167148..77d50c7349384823b71ed5f7272a343a2ca1cdd6 100644 (file)
@@ -11,23 +11,27 @@ skip_online_test();
 use http\Client,
        http\Client\Request;
 
+include "helper/server.inc";
+
 echo "Test\n";
 
-$client = new Client();
-$request = new Request(
-       'POST',
-       'https://api.twitter.com/oauth/request_token',
-       array(
-               'Content-Length' => 0
-       )
-);
-$client->enqueue($request);
-echo $client->send()->getResponse()->getResponseCode();
+server("proxy.inc", function($port) {
+       $client = new Client();
+       $request = new Request(
+               'POST',
+               "http://localhost:$port/",
+               array(
+                       'Content-Length' => 0
+               )
+       );
+       $client->enqueue($request);
+       echo $client->send()->getResponse()->getResponseCode();
+});
 
 ?>
 
 ===DONE===
 --EXPECTF--
 Test
-40%d
+200
 ===DONE===
index bca33024f93019ac1c2f2a495a88530c7ad20b5e..63e0ef332f5c246cae21280849551a97c829cb11 100644 (file)
@@ -14,7 +14,7 @@ include "helper/server.inc";
 echo "Test\n";
 
 server("proxy.inc", function($port) {
-       $request = new http\Client\Request("GET", "http://www.example.org/");
+       $request = new http\Client\Request("GET", "http://localhost:$port/");
        
        foreach (http\Client::getAvailableDrivers() as $driver) {
                $client = new http\Client($driver);
diff --git a/tests/gh-issue12.phpt b/tests/gh-issue12.phpt
new file mode 100644 (file)
index 0000000..0721586
--- /dev/null
@@ -0,0 +1,31 @@
+--TEST--
+crash with bad url passed to http\Message::setRequestUrl()
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php 
+echo "Test\n";
+
+$urls = array(
+               "http://.foo.bar",
+               "http://foo..bar",
+               "http://foo.bar.",
+);
+
+foreach ($urls as $url) {
+       try {
+               (new http\Client\Request)->setRequestUrl($url);
+               printf("OK: %s\n", $url);
+       } catch (Exception $e) {
+               printf("%s\n", $e->getMessage());
+       }
+}
+
+?>
+===DONE===
+--EXPECT--
+Test
+http\Message::setRequestUrl(): Failed to parse host; unexpected '.' at pos 0 in '.foo.bar'
+http\Message::setRequestUrl(): Failed to parse host; unexpected '.' at pos 4 in 'foo..bar'
+OK: http://foo.bar.
+===DONE===