Merge branch 'master' of git.php.net:/pecl/http/pecl_http
authorRemi Collet <remi@php.net>
Wed, 8 Apr 2015 10:58:19 +0000 (12:58 +0200)
committerRemi Collet <remi@php.net>
Wed, 8 Apr 2015 10:58:19 +0000 (12:58 +0200)
* 'master' of git.php.net:/pecl/http/pecl_http:
  back to dev
  release 2.4.3
  fix bug #69357
  back to dev
  release 2.4.2
  Fixed bug #69313
  release 2.4.2
  CS
  fix for bug #69076, fix handling of URLs with lone '?' as last character
  support libidn2 and ICU as fallbacksx
  defer warnings/exceptions of the client
  back to dev
  release 2.4.1

config9.m4
package.xml
php_http.c
php_http.h
php_http_client_curl.c
php_http_url.c
tests/bug69076.phpt [new file with mode: 0644]
tests/bug69313.phpt [new file with mode: 0644]
tests/bug69357.phpt [new file with mode: 0644]
tests/helper/upload.inc [new file with mode: 0644]

index 02075136667636706976d8274c2af7eefe5099b0..356fb8172374ea871ca9172eb5dfc84491007d9a 100644 (file)
@@ -11,7 +11,7 @@ PHP_ARG_WITH([http-libcurl-dir], [],
 PHP_ARG_WITH([http-libevent-dir], [],
 [  --with-http-libevent-dir[=DIR] HTTP: where to find libevent], $PHP_HTTP_LIBCURL_DIR, "")
 PHP_ARG_WITH([http-libidn-dir], [],
-[  --with-http-libidn-dir=[=DIR]  HTTP: where to find libidn], $PHP_HTTP_LIBCURL_DIR, "")
+[  --with-http-libidn-dir[=DIR]   HTTP: where to find libidn], $PHP_HTTP_LIBCURL_DIR, "")
 
 if test "$PHP_HTTP" != "no"; then
 
@@ -120,18 +120,70 @@ dnl ----
                        break;
                fi
        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
+       if test "x$IDNA_DIR" != "x"; then
                AC_MSG_RESULT([found in $IDNA_DIR])
                AC_DEFINE([PHP_HTTP_HAVE_IDN], [1], [Have libidn support])
                PHP_ADD_INCLUDE($IDNA_DIR/include)
                PHP_ADD_LIBRARY_WITH_PATH(idn, $IDNA_DIR/$PHP_LIBDIR, HTTP_SHARED_LIBADD)
+               AC_MSG_CHECKING([for libidn version])
+               IDNA_VER=$(pkg-config --version libidn 2>/dev/null || echo unknown)
+               AC_MSG_RESULT([$IDNA_VER])
+               AC_DEFINE_UNQUOTED([PHP_HTTP_LIBIDN_VERSION], "$IDNA_VER", [ ])
+       else
+               AC_MSG_RESULT([not found])
+               AC_MSG_CHECKING([for idn2.h])
+               IDNA_DIR=
+               for i in "$PHP_HTTP_LIBIDN_DIR" "$IDN_DIR" /usr/local /usr /opt; do
+                       if test -f "$i/include/idn2.h"; then
+                               IDNA_DIR=$i
+                               break;
+                       fi
+               done
+               if test "x$IDNA_DIR" != "x"; then
+                       AC_MSG_RESULT([found in $IDNA_DIR])
+                       AC_DEFINE([PHP_HTTP_HAVE_IDN2], [1], [Have libidn2 support])
+                       PHP_ADD_INCLUDE($IDNA_DIR/include)
+                       PHP_ADD_LIBRARY_WITH_PATH(idn2, $IDNA_DIR/$PHP_LIBDIR, HTTP_SHARED_LIBADD)
+                       AC_MSG_CHECKING([for libidn2 version])
+                       IDNA_VER=`$EGREP "define IDN2_VERSION " $IDNA_DIR/include/idn2.h | $SED -e's/^.*VERSION //g' -e 's/[[^0-9\.]]//g'`
+                       AC_MSG_RESULT([$IDNA_VER])
+                       AC_DEFINE_UNQUOTED([PHP_HTTP_LIBIDN2_VERSION], "$IDNA_VER", [ ])
+               else
+                       AC_MSG_RESULT([not found])
+                       AC_CHECK_HEADERS([unicode/uidna.h])
+                       case $host_os in
+                       darwin*)
+                               PHP_CHECK_FUNC(uidna_IDNToASCII, icucore);;
+                       *)
+                               AC_PATH_PROG(ICU_CONFIG, icu-config, no, [$PATH:/usr/local/bin])
+                               if test ! -x "$ICU_CONFIG"; then
+                                       ICU_CONFIG="icu-config"
+                               fi
+                               AC_MSG_CHECKING([for uidna_IDNToASCII])
+                               if ! test -x "$ICU_CONFIG"; then
+                                       ICU_CONFIG=icu-config
+                               fi
+                               if $ICU_CONFIG --exists 2>/dev/null >&2; then
+                                       save_LIBS=$LIBS
+                                       LIBS=$($ICU_CONFIG --ldflags)
+                                       AC_TRY_RUN([
+                                               #include <unicode/uidna.h>
+                                               int main(int argc, char *argv[]) {
+                                                       return uidna_IDNToASCII(0, 0, 0, 0, 0, 0, 0);
+                                               }
+                                       ], [
+                                               AC_MSG_RESULT([yes])
+                                               AC_DEFINE([HAVE_UIDNA_IDNTOASCII], [1], [ ])
+                                               LIBS=$save_LIBS
+                                               PHP_EVAL_LIBLINE(`$ICU_CONFIG --ldflags`, HTTP_SHARED_LIBADD)
+                                       ], [
+                                               LIBS=$save_LIBS
+                                               AC_MSG_RESULT([no])
+                                       ])
+                               fi
+                               ;;
+                       esac
+               fi
        fi
 
 dnl ----
index bdf3b890296613e60b1cc3af2d91ee03663a8ca1..1634ddb6992d6237fc68533ebbf826784725e172 100644 (file)
@@ -35,9 +35,9 @@ http://dev.iworks.at/ext-http/lcov/ext/http/
   <email>mike@php.net</email>
   <active>yes</active>
  </lead>
- <date>2015-03-18</date>
+ <date>2015-04-08</date>
  <version>
-  <release>2.4.0</release>
+  <release>2.4.4dev</release>
   <api>2.4.0</api>
  </version>
  <stability>
@@ -46,7 +46,7 @@ http://dev.iworks.at/ext-http/lcov/ext/http/
  </stability>
  <license>BSD, revised</license>
  <notes><![CDATA[
-* Split off pecl/apfd and pecl/json_post
+*
 ]]></notes>
  <contents>
   <dir name="/">
@@ -148,6 +148,8 @@ http://dev.iworks.at/ext-http/lcov/ext/http/
      <file role="test" name="bug66891.phpt"/>
      <file role="test" name="bug67932.phpt"/>
      <file role="test" name="bug69000.phpt"/>
+     <file role="test" name="bug69076.phpt"/>
+     <file role="test" name="bug69357.phpt"/>
      <file role="test" name="client001.phpt"/>
      <file role="test" name="client002.phpt"/>
      <file role="test" name="client003.phpt"/>
index bd7baff1078fffbad0a4cb5bd1b3e7be73b6c497..2ff20f105321e0ba4271480d3695570ecaf60e88 100644 (file)
 #              endif
 #      endif
 #endif
-#if PHP_HTTP_HAVE_SERF
-#      include <serf.h>
+#if PHP_HTTP_HAVE_IDN2
+#      include <idn2.h>
+#elif PHP_HTTP_HAVE_IDN
+#      include <idna.h>
 #endif
 
 ZEND_DECLARE_MODULE_GLOBALS(php_http);
@@ -227,6 +229,12 @@ PHP_MINFO_FUNCTION(http)
        php_info_print_table_row(3, "libevent", "disabled", "disabled");
 #endif
 
+#if PHP_HTTP_HAVE_IDN2
+       php_info_print_table_row(3, "libidn2 (IDNA2008)", IDN2_VERSION, idn2_check_version(NULL));
+#elif PHP_HTTP_HAVE_IDN
+       php_info_print_table_row(3, "libidn (IDNA2003)", PHP_HTTP_LIBIDN_VERSION, "unknown");
+#endif
+
        php_info_print_table_end();
        
        DISPLAY_INI_ENTRIES();
index 02326fb64828d520000fc5bbe4cc81dbc1a9926f..31c794567d540f4301706eddd3491177c2e3192a 100644 (file)
@@ -13,7 +13,7 @@
 #ifndef PHP_EXT_HTTP_H
 #define PHP_EXT_HTTP_H
 
-#define PHP_PECL_HTTP_VERSION "2.4.0"
+#define PHP_PECL_HTTP_VERSION "2.4.4dev"
 
 extern zend_module_entry http_module_entry;
 #define phpext_http_ptr &http_module_entry
index 18f1dec89ab2d27159219f08fba2013576366edd..9ebffa395f9da893751b778d60d3f72ea05834c5 100644 (file)
@@ -584,7 +584,14 @@ static php_http_message_t *php_http_curlm_responseparser(php_http_client_curl_ha
 
        response = php_http_message_init(NULL, 0, h->response.body TSRMLS_CC);
        php_http_header_parser_init(&parser TSRMLS_CC);
-       php_http_header_parser_parse(&parser, &h->response.headers, PHP_HTTP_HEADER_PARSER_CLEANUP, &response->hdrs, (php_http_info_callback_t) php_http_message_info_callback, (void *) &response);
+       while (h->response.headers.used) {
+               php_http_header_parser_state_t st = php_http_header_parser_parse(&parser,
+                               &h->response.headers, PHP_HTTP_HEADER_PARSER_CLEANUP, &response->hdrs,
+                               (php_http_info_callback_t) php_http_message_info_callback, (void *) &response);
+               if (PHP_HTTP_HEADER_PARSER_STATE_FAILURE == st) {
+                       break;
+               }
+       }
        php_http_header_parser_dtor(&parser);
 
        /* move body to right message */
@@ -594,6 +601,7 @@ static php_http_message_t *php_http_curlm_responseparser(php_http_client_curl_ha
                while (ptr->parent) {
                        ptr = ptr->parent;
                }
+               php_http_message_body_free(&response->body);
                response->body = ptr->body;
                ptr->body = NULL;
        }
@@ -622,7 +630,8 @@ static php_http_message_t *php_http_curlm_responseparser(php_http_client_curl_ha
 
 static void php_http_curlm_responsehandler(php_http_client_t *context)
 {
-       int remaining = 0;
+       int err_count = 0, remaining = 0;
+       php_http_curle_storage_t *st, *err = NULL;
        php_http_client_enqueue_t *enqueue;
        php_http_client_curl_t *curl = context->ctx;
        TSRMLS_FETCH_FROM_CTX(context->ts);
@@ -632,8 +641,18 @@ static void php_http_curlm_responsehandler(php_http_client_t *context)
 
                if (msg && CURLMSG_DONE == msg->msg) {
                        if (CURLE_OK != msg->data.result) {
-                               php_http_curle_storage_t *st = php_http_curle_get_storage(msg->easy_handle);
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s; %s (%s)", curl_easy_strerror(st->errorcode = msg->data.result), STR_PTR(st->errorbuffer), STR_PTR(st->url));
+                               st = php_http_curle_get_storage(msg->easy_handle);
+                               st->errorcode = msg->data.result;
+
+                               /* defer the warnings/exceptions, so the callback is still called for this request */
+                               if (!err) {
+                                       err = ecalloc(remaining + 1, sizeof(*err));
+                               }
+                               memcpy(&err[err_count], st, sizeof(*st));
+                               if (st->url) {
+                                       err[err_count].url = estrdup(st->url);
+                               }
+                               err_count++;
                        }
 
                        if ((enqueue = php_http_client_enqueued(context, msg->easy_handle, compare_queue))) {
@@ -647,6 +666,19 @@ static void php_http_curlm_responsehandler(php_http_client_t *context)
                        }
                }
        } while (remaining);
+
+       if (err_count) {
+               int i = 0;
+
+               do {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s; %s (%s)", curl_easy_strerror(err[i].errorcode), err[i].errorbuffer, STR_PTR(err[i].url));
+                       if (err[i].url) {
+                               efree(err[i].url);
+                       }
+               } while (++i < err_count);
+
+               efree(err);
+       }
 }
 
 #if PHP_HTTP_HAVE_EVENT
@@ -1884,35 +1916,6 @@ static ZEND_RESULT_CODE php_http_client_curl_handler_prepare(php_http_client_cur
        php_http_url_to_string(PHP_HTTP_INFO(msg).request.url, &storage->url, NULL, 1);
        curl_easy_setopt(curl->handle, CURLOPT_URL, storage->url);
 
-       /* request method */
-       switch (php_http_select_str(PHP_HTTP_INFO(msg).request.method, 4, "GET", "HEAD", "POST", "PUT")) {
-               case 0:
-                       curl_easy_setopt(curl->handle, CURLOPT_HTTPGET, 1L);
-                       break;
-
-               case 1:
-                       curl_easy_setopt(curl->handle, CURLOPT_NOBODY, 1L);
-                       break;
-
-               case 2:
-                       curl_easy_setopt(curl->handle, CURLOPT_POST, 1L);
-                       break;
-
-               case 3:
-                       curl_easy_setopt(curl->handle, CURLOPT_UPLOAD, 1L);
-                       break;
-
-               default: {
-                       if (PHP_HTTP_INFO(msg).request.method) {
-                               curl_easy_setopt(curl->handle, CURLOPT_CUSTOMREQUEST, PHP_HTTP_INFO(msg).request.method);
-                       } else {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot use empty request method");
-                               return FAILURE;
-                       }
-                       break;
-               }
-       }
-
        /* apply options */
        php_http_options_apply(&php_http_curle_options, enqueue->options, curl);
 
@@ -1964,6 +1967,7 @@ static ZEND_RESULT_CODE php_http_client_curl_handler_prepare(php_http_client_cur
                curl_easy_setopt(curl->handle, CURLOPT_READDATA, msg->body);
                curl_easy_setopt(curl->handle, CURLOPT_INFILESIZE, body_size);
                curl_easy_setopt(curl->handle, CURLOPT_POSTFIELDSIZE, body_size);
+               curl_easy_setopt(curl->handle, CURLOPT_POST, 1L);
        } else {
                curl_easy_setopt(curl->handle, CURLOPT_SEEKDATA, NULL);
                curl_easy_setopt(curl->handle, CURLOPT_READDATA, NULL);
@@ -1971,6 +1975,29 @@ static ZEND_RESULT_CODE php_http_client_curl_handler_prepare(php_http_client_cur
                curl_easy_setopt(curl->handle, CURLOPT_POSTFIELDSIZE, 0L);
        }
 
+       /*
+        * Always use CUSTOMREQUEST, else curl won't send any request body for GET etc.
+        * See e.g. bug #69313.
+        *
+        * Here's what curl does:
+        * - CURLOPT_HTTPGET: ignore request body
+        * - CURLOPT_UPLOAD: set "Expect: 100-continue" header
+        * - CURLOPT_POST: set "Content-Type: application/x-www-form-urlencoded" header
+        * Now select the least bad.
+        *
+        * 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)) {
+                       curl_easy_setopt(curl->handle, CURLOPT_UPLOAD, 1L);
+               } else {
+                       curl_easy_setopt(curl->handle, CURLOPT_CUSTOMREQUEST, PHP_HTTP_INFO(msg).request.method);
+               }
+       } else {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot use empty request method");
+               return FAILURE;
+       }
+
        return SUCCESS;
 }
 
@@ -2278,11 +2305,11 @@ static ZEND_RESULT_CODE php_http_client_curl_exec(php_http_client_t *h)
                                php_error_docref(NULL TSRMLS_CC, E_ERROR, "Error in event_base_dispatch()");
                                return FAILURE;
                        }
-               } while (curl->unfinished);
+               } while (curl->unfinished && !EG(exception));
        } else
 #endif
        {
-               while (php_http_client_curl_once(h)) {
+               while (php_http_client_curl_once(h) && !EG(exception)) {
                        if (SUCCESS != php_http_client_curl_wait(h, NULL)) {
 #ifdef PHP_WIN32
                                /* see http://msdn.microsoft.com/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp */
index 35178dc882a8b2314c821681088e9b6d0b43d3b9..0640aa710c24176786c3537f7adaadedbbf0ce54 100644 (file)
@@ -12,7 +12,9 @@
 
 #include "php_http_api.h"
 
-#ifdef PHP_HTTP_HAVE_IDN
+#if PHP_HTTP_HAVE_IDN2
+#      include <idn2.h>
+#elif PHP_HTTP_HAVE_IDN
 #      include <idna.h>
 #endif
 
@@ -827,7 +829,7 @@ static ZEND_RESULT_CODE parse_userinfo(struct parse_state *state, const char *pt
 
 #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)
+static ZEND_RESULT_CODE to_utf16(parse_mb_func fn, const char *u8, uint16_t **u16, size_t *len TSRMLS_DC)
 {
        size_t offset = 0, u8_len = strlen(u8);
 
@@ -870,7 +872,33 @@ static ZEND_RESULT_CODE to_utf16(parse_mb_func fn, const char *u8, uint16_t **u1
 #      define MAXHOSTNAMELEN 256
 #endif
 
-#ifdef PHP_HTTP_HAVE_IDN
+#if PHP_HTTP_HAVE_IDN2
+static ZEND_RESULT_CODE parse_idn2(struct parse_state *state, size_t prev_len)
+{
+       char *idn = NULL;
+       int rv = -1;
+       TSRMLS_FETCH_FROM_CTX(state->ts);
+
+       if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
+               rv = idn2_lookup_u8((const unsigned char *) state->url.host, (unsigned char **) &idn, IDN2_NFC_INPUT);
+       }
+#      ifdef PHP_HTTP_HAVE_WCHAR
+       else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
+               rv = idn2_lookup_ul(state->url.host, &idn, 0);
+       }
+#      endif
+       if (rv != IDN2_OK) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN; %s", idn2_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;
+       }
+}
+#elif PHP_HTTP_HAVE_IDN
 static ZEND_RESULT_CODE parse_idn(struct parse_state *state, size_t prev_len)
 {
        char *idn = NULL;
@@ -915,12 +943,12 @@ static ZEND_RESULT_CODE parse_uidn(struct parse_state *state)
        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)) {
+               if (SUCCESS != to_utf16(parse_mb_utf8, state->url.host, &uhost_str, &uhost_len TSRMLS_CC)) {
                        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)) {
+               if (SUCCESS != to_utf16(parse_mb_loc, state->url.host, &uhost_str, &uhost_len TSRMLS_CC)) {
                        return FAILURE;
                }
 #endif
@@ -1106,7 +1134,9 @@ static ZEND_RESULT_CODE parse_hostinfo(struct parse_state *state, const char *pt
        }
 
        if (state->flags & PHP_HTTP_URL_PARSE_TOIDN) {
-#ifdef PHP_HTTP_HAVE_IDN
+#if PHP_HTTP_HAVE_IDN2
+               return parse_idn2(state, len);
+#elif PHP_HTTP_HAVE_IDN
                return parse_idn(state, len);
 #endif
 #ifdef HAVE_UIDNA_IDNTOASCII
@@ -1241,7 +1271,7 @@ static const char *parse_query(struct parse_state *state)
        tmp = ++state->ptr;
        state->url.query = &state->buffer[state->offset];
 
-       do {
+       while (state->ptr < state->end) {
                switch (*state->ptr) {
                case '#':
                        goto done;
@@ -1293,7 +1323,9 @@ static const char *parse_query(struct parse_state *state)
                        }
                        state->ptr += mb - 1;
                }
-       } while (++state->ptr < state->end);
+
+               ++state->ptr;
+       }
 
        done:
        state->buffer[state->offset++] = 0;
@@ -1654,7 +1686,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 TSRMLS_CC);
 #endif
        zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_MBUTF8"), PHP_HTTP_URL_PARSE_MBUTF8 TSRMLS_CC);
-#if defined(PHP_HTTP_HAVE_IDN) || defined(HAVE_UIDNA_IDNTOASCII)
+#if defined(PHP_HTTP_HAVE_IDN2) || 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 TSRMLS_CC);
 #endif
        zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_TOPCT"), PHP_HTTP_URL_PARSE_TOPCT TSRMLS_CC);
diff --git a/tests/bug69076.phpt b/tests/bug69076.phpt
new file mode 100644 (file)
index 0000000..cd64958
--- /dev/null
@@ -0,0 +1,17 @@
+--TEST--
+Bug #69076 (URL parsing throws exception on empty query string)
+--SKIPIF--
+<?php 
+include "skipif.inc";
+?>
+--FILE--
+<?php 
+echo "Test\n";
+echo new http\Url("http://foo.bar/?");
+?>
+
+===DONE===
+--EXPECT--
+Test
+http://foo.bar/
+===DONE===
diff --git a/tests/bug69313.phpt b/tests/bug69313.phpt
new file mode 100644 (file)
index 0000000..824918c
--- /dev/null
@@ -0,0 +1,46 @@
+--TEST--
+Bug #69313 (http\Client doesn't send GET body)
+--SKIPIF--
+<?php
+include "./skipif.inc";
+skip_client_test();
+?>
+--FILE--
+<?php
+
+
+include "helper/server.inc";
+
+echo "Test\n";
+
+server("proxy.inc", function($port, $stdin, $stdout, $stderr) {
+       $request = new http\Client\Request("GET", "http://localhost:$port/");
+       $request->setHeader("Content-Type", "text/plain");
+       $request->getBody()->append("foo");
+       $client = new http\Client();
+       $client->enqueue($request);
+       $client->send();
+       echo $client->getResponse();
+});
+
+?>
+
+Done
+--EXPECTF--
+Test
+HTTP/1.1 200 OK
+Accept-Ranges: bytes
+Etag: "%s"
+X-Original-Transfer-Encoding: chunked
+Content-Length: %d
+
+GET / HTTP/1.1
+User-Agent: %s
+Host: localhost:%d
+Accept: */*
+Content-Type: text/plain
+Content-Length: 3
+X-Original-Content-Length: 3
+
+foo
+Done
diff --git a/tests/bug69357.phpt b/tests/bug69357.phpt
new file mode 100644 (file)
index 0000000..ac93baf
--- /dev/null
@@ -0,0 +1,41 @@
+--TEST--
+Bug #69357 (HTTP/1.1 100 Continue overriding subsequent 200 response code with PUT request)
+--SKIPIF--
+<?php
+include "skipif.inc";
+skip_client_test();
+?>
+--FILE--
+<?php 
+echo "Test\n";
+
+include "helper/server.inc";
+
+server("upload.inc", function($port) {
+       $r = new \http\Client\Request("PUT", "http://localhost:$port/", [],
+                       (new \http\Message\Body)->append("foo")
+       );
+       $c = new \http\Client;
+       $c->setOptions(["expect_100_timeout" => 0]);
+       $c->enqueue($r)->send();
+       
+       var_dump($c->getResponse($r)->getInfo());
+       var_dump($c->getResponse($r)->getHeaders());
+});
+
+?>
+===DONE===
+--EXPECTF--
+Test
+string(15) "HTTP/1.1 200 OK"
+array(4) {
+  ["Accept-Ranges"]=>
+  string(5) "bytes"
+  ["Etag"]=>
+  string(10) ""%x""
+  ["X-Original-Transfer-Encoding"]=>
+  string(7) "chunked"
+  ["Content-Length"]=>
+  int(%d)
+}
+===DONE===
diff --git a/tests/helper/upload.inc b/tests/helper/upload.inc
new file mode 100644 (file)
index 0000000..9502d2b
--- /dev/null
@@ -0,0 +1,20 @@
+<?php 
+
+include "server.inc";
+
+serve(function($client) {
+       $request = new http\Message($client, false);
+       
+       if ($request->getHeader("Expect") === "100-continue") {
+               $response = new http\Env\Response;
+               $response->setEnvRequest($request);
+               $response->setResponseCode(100);
+               $response->send($client);
+       }
+       
+       /* return the initial message as response body */
+       $response = new http\Env\Response;
+       /* avoid OOM with $response->getBody()->append($request); */
+       $request->toStream($response->getBody()->getResource());
+       $response->send($client);
+});