Merge pull request #74 from gbarosio/readme
authorMichael Wallner <mike@php.net>
Mon, 4 Feb 2019 13:37:10 +0000 (14:37 +0100)
committerGitHub <noreply@github.com>
Mon, 4 Feb 2019 13:37:10 +0000 (14:37 +0100)
Added the correct git url to the README.md fileE [ci skip]

71 files changed:
.gitignore
.travis.yml
autoconf/pecl/libbrotli.m4 [new file with mode: 0644]
autoconf/pecl/libevent.m4
autoconf/pecl/pecl.m4
config9.m4
package.xml
php_http.h
scripts/coverity.sh
scripts/gen_curlinfo.php
scripts/gen_if_utf8.php [new file with mode: 0755]
scripts/gen_stubs.php
scripts/gen_switch_utf8.php [new file with mode: 0755]
scripts/gen_travis_yml.php
scripts/gen_utf8.php
src/php_http.c
src/php_http_api.h
src/php_http_buffer.c
src/php_http_buffer.h
src/php_http_client.c
src/php_http_client_curl.c
src/php_http_client_curl_user.c
src/php_http_client_request.c
src/php_http_encoding.c
src/php_http_encoding.h
src/php_http_encoding_brotli.c [new file with mode: 0644]
src/php_http_encoding_brotli.h [new file with mode: 0644]
src/php_http_encoding_zlib.c [new file with mode: 0644]
src/php_http_encoding_zlib.h [new file with mode: 0644]
src/php_http_env_request.c
src/php_http_env_response.c
src/php_http_exception.h
src/php_http_filter.c
src/php_http_header.c
src/php_http_header_parser.c
src/php_http_header_parser.h
src/php_http_info.c
src/php_http_message.c
src/php_http_message_body.c
src/php_http_message_parser.c
src/php_http_message_parser.h
src/php_http_misc.c
src/php_http_misc.h
src/php_http_object.c
src/php_http_options.c
src/php_http_params.c
src/php_http_querystring.c
src/php_http_url.c
src/php_http_utf8.h
tests/bug66388.phpt
tests/client002.phpt
tests/client012.phpt
tests/client013.phpt
tests/client028.phpt
tests/encstream015.phpt [new file with mode: 0644]
tests/encstream016.phpt [new file with mode: 0644]
tests/encstream017.phpt [new file with mode: 0644]
tests/encstream018.phpt [new file with mode: 0644]
tests/encstream019.phpt [new file with mode: 0644]
tests/envresponse003.phpt
tests/filterbrotli.phpt [new file with mode: 0644]
tests/helper/server.inc
tests/propertyproxy001.phpt
tests/skipif.inc
tests/urlparser004.phpt
tests/urlparser006.phpt
tests/urlparser010.phpt
tests/urlparser012.phpt
tests/urlparser013.phpt
travis/brotli.sh [new file with mode: 0755]
travis/pecl

index 15cb267..95f72a2 100644 (file)
@@ -41,6 +41,7 @@ lcov_data
 *~
 *.phar
 !travis/*.phar
+bench/
 vendor/
 /php_http_api.h
 /php_http_buffer.h
@@ -53,6 +54,8 @@ vendor/
 /php_http_cookie.h
 /php_http_curl.h
 /php_http_encoding.h
+/php_http_encoding_zlib.h
+/php_http_encoding_brotli.h
 /php_http_env.h
 /php_http_env_request.h
 /php_http_env_response.h
@@ -76,4 +79,5 @@ vendor/
 /php_http_utf8.h
 /php_http_version.h
 /tests/helper/server.log
-*.gcov
+*gcov
+*lcov
index 5524d30..b1ef328 100644 (file)
@@ -1,6 +1,6 @@
 # autogenerated file; do not edit
 language: c
-sudo: required
+sudo: false
 dist: trusty
 
 addons:
@@ -14,68 +14,48 @@ addons:
    - libicu-dev
    - libevent-dev
 
+compiler:
+ - gcc
+ - clang
+
+cache:
+ directories:
+  - $HOME/cache
+before_cache:
+ - find $HOME/cache -name '*.gcda' -o -name '*.gcno' -delete
+
 env:
- - PHP=7.0 enable_debug=no enable_maintainer_zts=no enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=7.1 enable_debug=no enable_maintainer_zts=no enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=master enable_debug=no enable_maintainer_zts=no enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=7.0 enable_debug=yes enable_maintainer_zts=no enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=7.1 enable_debug=yes enable_maintainer_zts=no enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=master enable_debug=yes enable_maintainer_zts=no enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=7.0 enable_debug=no enable_maintainer_zts=yes enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=7.1 enable_debug=no enable_maintainer_zts=yes enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=master enable_debug=no enable_maintainer_zts=yes enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=7.0 enable_debug=yes enable_maintainer_zts=yes enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=7.1 enable_debug=yes enable_maintainer_zts=yes enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=master enable_debug=yes enable_maintainer_zts=yes enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=7.0 enable_debug=no enable_maintainer_zts=no enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=7.1 enable_debug=no enable_maintainer_zts=no enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=master enable_debug=no enable_maintainer_zts=no enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=7.0 enable_debug=yes enable_maintainer_zts=no enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=7.1 enable_debug=yes enable_maintainer_zts=no enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=master enable_debug=yes enable_maintainer_zts=no enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=7.0 enable_debug=no enable_maintainer_zts=yes enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=7.1 enable_debug=no enable_maintainer_zts=yes enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=master enable_debug=no enable_maintainer_zts=yes enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=7.0 enable_debug=yes enable_maintainer_zts=yes enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=7.1 enable_debug=yes enable_maintainer_zts=yes enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=master enable_debug=yes enable_maintainer_zts=yes enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=no
- - PHP=7.0 enable_debug=no enable_maintainer_zts=no enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=7.1 enable_debug=no enable_maintainer_zts=no enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=master enable_debug=no enable_maintainer_zts=no enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=7.0 enable_debug=yes enable_maintainer_zts=no enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=7.1 enable_debug=yes enable_maintainer_zts=no enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=master enable_debug=yes enable_maintainer_zts=no enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=7.0 enable_debug=no enable_maintainer_zts=yes enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=7.1 enable_debug=no enable_maintainer_zts=yes enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=master enable_debug=no enable_maintainer_zts=yes enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=7.0 enable_debug=yes enable_maintainer_zts=yes enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=7.1 enable_debug=yes enable_maintainer_zts=yes enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=master enable_debug=yes enable_maintainer_zts=yes enable_json=no enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=7.0 enable_debug=no enable_maintainer_zts=no enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=7.1 enable_debug=no enable_maintainer_zts=no enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=master enable_debug=no enable_maintainer_zts=no enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=7.0 enable_debug=yes enable_maintainer_zts=no enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=7.1 enable_debug=yes enable_maintainer_zts=no enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=master enable_debug=yes enable_maintainer_zts=no enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=7.0 enable_debug=no enable_maintainer_zts=yes enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=7.1 enable_debug=no enable_maintainer_zts=yes enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=master enable_debug=no enable_maintainer_zts=yes enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=7.0 enable_debug=yes enable_maintainer_zts=yes enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=7.1 enable_debug=yes enable_maintainer_zts=yes enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
- - PHP=master enable_debug=yes enable_maintainer_zts=yes enable_json=yes enable_hash=yes enable_iconv=yes with_http_libicu_dir=yes
-# once with gcov
- - CFLAGS="-O0 -g --coverage" CXXFLAGS="-O0 -g --coverage" PHP=master enable_json=yes enable_hash=yes enable_iconv=yes 
+ - PHP=7.0 enable_debug=yes enable_maintainer_zts=yes enable_json=yes enable_hash=yes enable_iconv=yes with_http_libbrotli_dir=/home/travis/brotli
+ - PHP=7.1 enable_debug=yes enable_maintainer_zts=yes enable_json=yes enable_hash=yes enable_iconv=yes with_http_libbrotli_dir=/home/travis/brotli
+ - PHP=7.3 enable_debug=yes enable_maintainer_zts=yes enable_json=yes enable_hash=yes enable_iconv=yes with_http_libbrotli_dir=/home/travis/brotli
+ - PHP=master enable_debug=yes enable_maintainer_zts=yes enable_json=yes enable_hash=yes enable_iconv=yes with_http_libbrotli_dir=/home/travis/brotli
+ - PHP=7.2 with_http_libicu_dir=no with_http_libidn_dir=no with_http_libidn2_dir=no with_http_libcurl_dir=no with_http_libevent_dir=no
+ - PHP=7.2 enable_debug=no enable_maintainer_zts=no enable_json=yes enable_hash=yes enable_iconv=yes with_http_libbrotli_dir=/home/travis/brotli
+ - PHP=7.2 enable_debug=yes enable_maintainer_zts=no enable_json=yes enable_hash=yes enable_iconv=yes with_http_libbrotli_dir=/home/travis/brotli
+ - PHP=7.2 enable_debug=no enable_maintainer_zts=yes enable_json=yes enable_hash=yes enable_iconv=yes with_http_libbrotli_dir=/home/travis/brotli
+ - PHP=7.2 enable_debug=yes enable_maintainer_zts=yes enable_json=yes enable_hash=yes enable_iconv=yes with_http_libbrotli_dir=/home/travis/brotli
+ - CFLAGS='-O0 -g --coverage' CXXFLAGS='-O0 -g --coverage' PHP=7.2 enable_json=yes enable_hash=yes enable_iconv=yes with_http_libbrotli_dir=/home/travis/brotli with_http_libicu_dir=yes with_http_libidn_dir=no with_http_libidn2_dir=no 
+ - CFLAGS='-O0 -g --coverage' CXXFLAGS='-O0 -g --coverage' PHP=7.2 enable_json=yes enable_hash=yes enable_iconv=yes with_http_libbrotli_dir=/home/travis/brotli with_http_libidn_dir=yes with_http_libicu_dir=no with_http_libidn2_dir=no 
+ - CFLAGS='-O0 -g --coverage' CXXFLAGS='-O0 -g --coverage' PHP=7.2 enable_json=yes enable_hash=yes enable_iconv=yes with_http_libbrotli_dir=/home/travis/brotli with_http_libidn2_dir=yes with_http_libicu_dir=no with_http_libidn_dir=no 
 
-before_script:
- - make -f travis/pecl/Makefile php
- - make -f travis/pecl/Makefile pecl PECL=raphf:raphf:2.0.0
- - make -f travis/pecl/Makefile pecl PECL=propro:propro:2.0.1
- - make -f travis/pecl/Makefile ext PECL=http
+install:
+ - ./travis/brotli.sh v1.0.2
+ - |
+   if test "$PHP" = master; then \
+     make -f travis/pecl/Makefile reconf; \
+     make -f travis/pecl/Makefile pecl-rm pecl-clean PECL=ext-raphf.git:raphf:master; \
+     make -f travis/pecl/Makefile pecl-rm pecl-clean PECL=ext-propro.git:propro:master; \
+   fi
+ - make -f travis/pecl/Makefile php || make -f travis/pecl/Makefile clean php
+ - make -f travis/pecl/Makefile pecl PECL=ext-raphf.git:raphf:master
+ - make -f travis/pecl/Makefile pecl PECL=ext-propro.git:propro:master
 
 script:
+ - make -f travis/pecl/Makefile ext PECL=http
  - make -f travis/pecl/Makefile test
- - make -f travis/pecl/Makefile cppcheck
 
+after_script:
+ - make -f travis/pecl/Makefile cppcheck
 after_failure:
  - test -e tests/helper/server.log && cat tests/helper/server.log
 after_success:
diff --git a/autoconf/pecl/libbrotli.m4 b/autoconf/pecl/libbrotli.m4
new file mode 100644 (file)
index 0000000..f8916e2
--- /dev/null
@@ -0,0 +1,47 @@
+
+AC_DEFUN([PECL_CHECK_LIBBROTLI], [
+       PECL_CHECK_LIBBROTLI_COMMON([$1], [$2])
+       PECL_CHECK_DONE(libbrotlicommon, [$PECL_VAR([HAVE_LIBBROTLI_COMMON])])
+       PECL_CHECK_LIBBROTLI_DEC([$1], [$2])
+       PECL_CHECK_DONE(libbrotlidec, [$PECL_VAR([HAVE_LIBBROTLI_DEC])])
+       PECL_CHECK_LIBBROTLI_ENC([$1], [$2])
+       PECL_CHECK_DONE(libbrotlienc, [$PECL_VAR([HAVE_LIBBROTLI_ENC])])
+       
+       if $PECL_VAR([HAVE_LIBBROTLI_COMMON]) \
+       && $PECL_VAR([HAVE_LIBBROTLI_DEC]) \
+       && $PECL_VAR([HAVE_LIBBROTLI_ENC]); then
+               PECL_VAR([HAVE_LIBBROTLI])=true
+       else
+               PECL_VAR([HAVE_LIBBROTLI])=false
+       fi
+])
+
+AC_DEFUN([PECL_CHECK_LIBBROTLI_COMMON], [
+       PECL_CHECK_PKGCONFIG(libbrotlicommon, [$1])
+       
+       PECL_HAVE_VERSION(libbrotlicommon, ifelse($2,,1.0,$2), [
+               PECL_VAR([HAVE_LIBBROTLI_COMMON])=true
+       ], [
+               PECL_VAR([HAVE_LIBBROTLI_COMMON])=false
+       ])
+])
+
+AC_DEFUN([PECL_CHECK_LIBBROTLI_DEC], [
+       PECL_CHECK_PKGCONFIG(libbrotlidec, [$1])
+       
+       PECL_HAVE_VERSION(libbrotlidec, ifelse($2,,1.0,$2), [
+               PECL_VAR([HAVE_LIBBROTLI_DEC])=true
+       ], [
+               PECL_VAR([HAVE_LIBBROTLI_DEC])=false
+       ])
+])
+
+AC_DEFUN([PECL_CHECK_LIBBROTLI_ENC], [
+       PECL_CHECK_PKGCONFIG(libbrotlienc, [$1])
+       
+       PECL_HAVE_VERSION(libbrotlienc, ifelse($2,,1.0,$2), [
+               PECL_VAR([HAVE_LIBBROTLI_ENC])=true
+       ], [
+               PECL_VAR([HAVE_LIBBROTLI_ENC])=false
+       ])
+])
index bd04296..88d082d 100644 (file)
@@ -1,17 +1,19 @@
 
 AC_DEFUN([PECL_CHECK_LIBEVENT], [
        PECL_CHECK_PKGCONFIG(libevent, [$1])
-       PECL_HAVE_VERSION(libevent, 2.0, [
-               PECL_DEFINE([HAVE_LIBEVENT2])
-       ])
-       ifelse([$2],,,[PECL_HAVE_VERSION(libevent, [$2])])
-       AC_CHECK_FUNC(event_base_new,,[
-               AC_DEFINE([event_base_new], [event_init], [missing event_base_new() in libevent1])
-       ])
-       AC_CHECK_FUNC(event_assign,,[
-               AC_DEFINE([event_assign(e, b, s, a, cb, d)], [do {\
-                       event_set(e, s, a, cb, d); \
-                       event_base_set(b, e);\
-               } while(0)], [missing event_assign() in libevent1])
-       ])
+       if test -n "$PECL_CHECKED_VERSION(libevent)"; then
+               PECL_HAVE_VERSION(libevent, 2.0, [
+                       PECL_DEFINE([HAVE_LIBEVENT2])
+               ], [ ])
+               ifelse([$2],,,[PECL_HAVE_VERSION(libevent, [$2])])
+               AC_CHECK_FUNC(event_base_new,,[
+                       AC_DEFINE([event_base_new], [event_init], [missing event_base_new() in libevent1])
+               ])
+               AC_CHECK_FUNC(event_assign,,[
+                       AC_DEFINE([event_assign(e, b, s, a, cb, d)], [do {\
+                               event_set(e, s, a, cb, d); \
+                               event_base_set(b, e);\
+                       } while(0)], [missing event_assign() in libevent1])
+               ])
+       fi
 ])
\ No newline at end of file
index 6721e1c..1763d5d 100644 (file)
@@ -221,7 +221,7 @@ AC_DEFUN([PECL_HAVE_VERSION], [
        aversion=_PECL_TR_VERSION([$PECL_CHECKED_VERSION([$1])])
        mversion=_PECL_TR_VERSION([$2])
        AC_MSG_CHECKING([whether $1 version $PECL_CHECKED_VERSION([$1]) >= $2])
-       if test "$aversion" -lt "$mversion"; then
+       if test -z "$aversion" || test "$aversion" -lt "$mversion"; then
                ifelse($4,,AC_MSG_ERROR([no]), [
                        AC_MSG_RESULT([no])
                        $4
@@ -332,8 +332,16 @@ dnl PECL_CHECK_PKGCONFIG(pkg[, additional-pkg-config-path])
 dnl
 AC_DEFUN([PECL_CHECK_PKGCONFIG], [dnl
        AC_REQUIRE([PECL_PROG_PKGCONFIG])dnl
-       ifelse($2,,,PKG_CONFIG_PATH="$2/lib/pkgconfig:$PKG_CONFIG_PATH")
+       ifelse($2,,, [
+               PECL_SAVE_VAR(pkgconfig_path)="$PKG_CONFIG_PATH"
+               if test -d "$2"; then
+                       export PKG_CONFIG_PATH="$2/lib/pkgconfig:$PKG_CONFIG_PATH"
+               fi
+       ])
        PECL_CHECK_CONFIG([$1], [$PKG_CONFIG $1], [--modversion], [--cflags-only-I], [--libs-only-L], [--libs-only-l])
+       ifelse($2,,, [
+               PKG_CONFIG_PATH="$PECL_SAVE_VAR(pkgconfig_path)"
+       ])
 ])
 dnl
 dnl PECL_CHECK_DONE(name, success[, incline, libline])
index dda5b01..897b92f 100644 (file)
@@ -2,6 +2,7 @@
 m4_foreach(dir, [., ext/http], [
        sinclude(dir/autoconf/pecl/pecl.m4)
        sinclude(dir/autoconf/pecl/zlib.m4)
+       sinclude(dir/autoconf/pecl/libbrotli.m4)
        sinclude(dir/autoconf/pecl/libcurl.m4)
        sinclude(dir/autoconf/pecl/libevent.m4)
 ])
@@ -29,6 +30,12 @@ if test "$PHP_HTTP" != "no"; then
        [  --with-http-zlib-dir[=DIR]         HTTP: where to find zlib], $PHP_HTTP, no)
        PECL_CHECK_ZLIB([$PHP_HTTP_ZLIB_DIR], [1.2.0.4])
        PECL_CHECK_DONE(zlib, $PECL_VAR([HAVE_ZLIB]))
+       
+       dnl BROTLI
+       PHP_ARG_WITH([http-libbrotli-dir], [whether/where to check for libbrotli],
+       [  --with-http-libbrotli-dir[=DIR]    HTTP: where to find libbrotli], $PHP_HTTP, no)
+       PECL_CHECK_LIBBROTLI([$PHP_HTTP_LIBBROTLI_DIR], [1.0])
+       PECL_CHECK_DONE(libbrotli, $PECL_VAR([HAVE_LIBBROTLI]))
 
        dnl CURL
        PHP_ARG_WITH([http-libcurl-dir], [whether/where to check for libcurl],
index 0f10907..046c82b 100644 (file)
@@ -31,10 +31,10 @@ https://mdref.m6w6.name/http
   <email>mike@php.net</email>
   <active>yes</active>
  </lead>
- <date>2017-04-04</date>
+ <date>2018-07-19</date>
  <version>
-  <release>3.1.1dev</release>
-  <api>3.1.0</api>
+  <release>3.2.1dev</release>
+  <api>3.2.0</api>
  </version>
  <stability>
   <release>stable</release>
@@ -42,7 +42,7 @@ https://mdref.m6w6.name/http
  </stability>
  <license uri="http://copyfree.org/content/standard/licenses/2bsd/license.txt">BSD-2-Clause</license>
  <notes><![CDATA[
-* Fix gh-issue #65: http\Client::enqueue(): Could not enqueue request: The easy handle is already added to a multi handle (@rcanavan, Mike)
+*
 ]]></notes>
  <contents>
   <dir name="/">
@@ -64,6 +64,7 @@ https://mdref.m6w6.name/http
    <dir name="autoconf">
     <dir name="pecl">
         <file role="src" name="pecl.m4"/>
+        <file role="src" name="libbrotli.m4"/>
         <file role="src" name="libcurl.m4"/>
         <file role="src" name="libevent.m4"/>
         <file role="src" name="zlib.m4"/>
@@ -92,6 +93,10 @@ https://mdref.m6w6.name/http
     <file role="src" name="php_http_curl.h"/>
     <file role="src" name="php_http_encoding.c"/>
     <file role="src" name="php_http_encoding.h"/>
+    <file role="src" name="php_http_encoding_zlib.c"/>
+    <file role="src" name="php_http_encoding_zlib.h"/>
+    <file role="src" name="php_http_encoding_brotli.c"/>
+    <file role="src" name="php_http_encoding_brotli.h"/>
     <file role="src" name="php_http_env.c"/>
     <file role="src" name="php_http_env.h"/>
     <file role="src" name="php_http_env_request.c"/>
@@ -228,6 +233,11 @@ https://mdref.m6w6.name/http
      <file role="test" name="encstream007.phpt"/>
      <file role="test" name="encstream008.phpt"/>
      <file role="test" name="encstream009.phpt"/>
+     <file role="test" name="encstream015.phpt"/>
+     <file role="test" name="encstream016.phpt"/>
+     <file role="test" name="encstream017.phpt"/>
+     <file role="test" name="encstream018.phpt"/>
+     <file role="test" name="encstream019.phpt"/>
      <file role="test" name="envrequestbody001.phpt"/>
      <file role="test" name="envrequestcookie001.phpt"/>
      <file role="test" name="envrequestfiles001.phpt"/>
@@ -260,6 +270,7 @@ https://mdref.m6w6.name/http
      <file role="test" name="envresponseheader001.phpt"/>
      <file role="test" name="envresponseranges001.phpt"/>
      <file role="test" name="etag001.phpt"/>
+     <file role="test" name="filterbrotli.phpt"/>
      <file role="test" name="filterchunked.phpt"/>
      <file role="test" name="filterzlib.phpt"/>
      <file role="test" name="gh-issue6.phpt"/>
@@ -269,6 +280,7 @@ https://mdref.m6w6.name/http
      <file role="test" name="gh-issue47.phpt"/>
      <file role="test" name="gh-issue48.phpt"/>
      <file role="test" name="gh-issue50.phpt"/>
+     <file role="test" name="gh-issue63.phpt"/>
      <file role="test" name="header001.phpt"/>
      <file role="test" name="header002.phpt"/>
      <file role="test" name="header003.phpt"/>
@@ -332,6 +344,7 @@ https://mdref.m6w6.name/http
      <file role="test" name="phpinfo.phpt"/>
      <file role="test" name="propertyproxy001.phpt"/>
      <file role="test" name="querystring001.phpt"/>
+     <file role="test" name="querystring001_a.phpt"/>
      <file role="test" name="querystring002.phpt"/>
      <file role="test" name="querystring003.phpt"/>
      <file role="test" name="serialize001.phpt"/>
index 2cf0de7..f698f64 100644 (file)
@@ -13,7 +13,7 @@
 #ifndef PHP_EXT_HTTP_H
 #define PHP_EXT_HTTP_H
 
-#define PHP_PECL_HTTP_VERSION "3.1.1dev"
+#define PHP_PECL_HTTP_VERSION "3.2.1dev"
 
 extern zend_module_entry http_module_entry;
 #define phpext_http_ptr &http_module_entry
index 08a04f1..883fd26 100755 (executable)
@@ -17,9 +17,18 @@ phpize
 ./configure
 make clean
 
+
 $COVERITY_BIN --dir $COVERITY_BUILD_DIR/cov-int make -j8
 
+echo >&2
+echo -n "Submit results to scan.coverity.com? (y/N) " >&2
+read submit
+echo >&2
+
+if test "$submit" != "y"; then
+       exit
+fi
+
 pushd $COVERITY_BUILD_DIR
 tar -czf cov-int{.tgz,}
 popd
index 376a7e5..9fae4cb 100755 (executable)
@@ -10,9 +10,9 @@ function failure() {
 
 function file_re($file, $pattern, $all = true) {
        static $path;
-       
+
        $path or $path = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1].'/include/curl/' : "/usr/local/include/curl/";
-       
+
        if ($content = file_get_contents($path . $file)) {
                if ($all) {
                        if (preg_match_all($pattern, $content, $matches, PREG_SET_ORDER)) {
@@ -35,7 +35,10 @@ $ifdefs = array(
     'PRIMARY_PORT' => 'PHP_HTTP_CURL_VERSION(7,21,0)',
     'LOCAL_PORT' => 'PHP_HTTP_CURL_VERSION(7,21,0)',
     'LOCAL_IP' => 'PHP_HTTP_CURL_VERSION(7,21,0)',
-       'HTTP_VERSION' => 'PHP_HTTP_CURL_VERSION(7,50,0)'
+       'HTTP_VERSION' => 'PHP_HTTP_CURL_VERSION(7,50,0)',
+       'PROXY_SSL_VERIFYRESULT' => 'PHP_HTTP_CURL_VERSION(7,52,0)',
+       'PROTOCOL' => 'PHP_HTTP_CURL_VERSION(7,52,0)',
+       'SCHEME' => 'PHP_HTTP_CURL_VERSION(7,52,0)',
 );
 $exclude = array(
        'ACTIVESOCKET',
@@ -58,19 +61,19 @@ $translate = array(
 );
 
 $templates = array(
-'STRING' => 
+'STRING' =>
 '      if (CURLE_OK == curl_easy_getinfo(ch, %s, &c)) {
                ZVAL_STRING(&tmp, STR_PTR(c));
                zend_hash_str_update(info, "%s", lenof("%2$s"), &tmp);
        }
 ',
-'DOUBLE' => 
+'DOUBLE' =>
 '      if (CURLE_OK == curl_easy_getinfo(ch, %s, &d)) {
                ZVAL_DOUBLE(&tmp, d);
                zend_hash_str_update(info, "%s", lenof("%2$s"), &tmp);
        }
 ',
-'LONG' => 
+'LONG' =>
 '      if (CURLE_OK == curl_easy_getinfo(ch, %s, &l)) {
                ZVAL_LONG(&tmp, l);
                zend_hash_str_update(info, "%s", lenof("%2$s"), &tmp);
@@ -101,7 +104,7 @@ foreach ($infos as $info) {
        if (isset($ifdefs[$short])) printf("#endif\n");
 }
 
-file_put_contents("src/php_http_client_curl.c", 
+file_put_contents("src/php_http_client_curl.c",
        preg_replace('/(\/\* BEGIN::CURLINFO \*\/\n).*(\n\s*\/\* END::CURLINFO \*\/)/s', '$1'. ob_get_contents() .'$2',
                file_get_contents("src/php_http_client_curl.c")));
 
diff --git a/scripts/gen_if_utf8.php b/scripts/gen_if_utf8.php
new file mode 100755 (executable)
index 0000000..720a5a9
--- /dev/null
@@ -0,0 +1,92 @@
+#!/usr/bin/env php
+<?php
+
+error_reporting(E_ALL);
+set_error_handler(function($c, $e, $f, $l) {
+       throw new Exception("$e in $f on line $l");
+});
+
+$i18n = $argc >= 2 ? $argv[1] : "/usr/share/i18n/locales/i18n";
+
+$f = fopen($i18n, "r");
+$c = false;
+$a = false;
+
+ob_start(null, 0xfffff);
+
+while (!feof($f)) {
+       $line = fgets($f);
+       if (!$c && $line !== "LC_CTYPE\n") {
+               continue;
+       }
+       $c = true;
+       if ($line === "END LC_CTYPE\n") {
+               break;
+       }
+       switch($line{0}) {
+       case "%":
+               break;
+       case "\n":
+               if ($a) {
+                       break 2;
+               }
+               break;
+       case " ":
+               if ($a) {
+                       foreach (explode(";", trim($line, "\n/ ;")) as $ranges) {
+                               $range = explode("..", $ranges);
+                               $step = 0;
+                               $end = 0;
+                               switch (count($range)) {
+                               case 3:
+                                       list($sstart, $sstep, $send) = $range;
+                                       sscanf($sstart, "<U%X>", $start);
+                                       sscanf($sstep, "(%d)", $step);
+                                       sscanf($send, "<U%X>", $end);
+
+                                       break;
+                               case 2:
+                                       list($sstart, $send) = $range;
+                                       $step = 1;
+                                       sscanf($sstart, "<U%X>", $start);
+                                       sscanf($send, "<U%X>", $end);
+                                       break;
+                               case 1:
+                                       list($sstart) = $range;
+                                       sscanf($sstart, "<U%X>", $start);
+                                       break;
+                               }
+                               $r[]=[$start,$end,$step];
+                       }
+               }
+               break;
+       default:
+               if ($a) {
+                       break 2;
+               } elseif ($line === "alpha /\n") {
+                       $a = true;
+               }
+               break;
+       }
+}
+function sp($sp, $ch = " ") { return str_repeat($ch, $sp); }
+$sp = 1;
+foreach ($r as list($start, $end, $step)) {
+       if ($end) {
+               if ($step > 1) {
+                       die("\nUNEXPECTED: step>1\n");
+                       printf("\tfor (i=0x%08X; i <= 0x%08X; i+= %d) { if (i == ch) return 1; }\n", $start, $end, $step);
+               } else {
+                       //printf(" if (ch >= 0x%08X && ch <= 0x%08X) return 1;\n", $start, $end);
+                       printf("%sif (ch >= 0x%08X) {\n", sp($sp), $start);
+                       printf("%sif (ch <= 0x%08X) return 1;\n", sp(++$sp), $end);
+               }
+       } else {
+               printf("%sif (ch == 0x%08X) return 1;\n", sp($sp), $start);
+       }
+}
+printf("  %s\n", sp($sp-1, "}"));
+
+file_put_contents("php_http_utf8.h",
+       preg_replace('/(\/\* BEGIN::UTF8SWITCH \*\/\n).*(\n\s*\/\* END::UTF8SWITCH \*\/)/s', '$1'. ob_get_contents() .'$2',
+               file_get_contents("php_http_utf8.h")));
index 20a1f0f..5f28545 100755 (executable)
@@ -1,8 +1,11 @@
 #!/usr/bin/env php
 <?php
 
-function m($m) {
+function m($m, $c = null) {
        $n = "";
+       if ($c && $c->isInterface()) {
+               $m ^= ReflectionMethod::IS_ABSTRACT;
+       }
        foreach (Reflection::getModifierNames($m) as $mn) {
                $n .= $mn . " ";
        }
@@ -169,7 +172,7 @@ foreach ($namespaces as $ns) {
 
             foreach ($c->getMethods() as $m) {
                 if ($m->getDeclaringClass()->getName() == $c->getName()) {
-                    fprintf($out, "\t\t%sfunction %s(", m($m->getModifiers()), 
+                    fprintf($out, "\t\t%sfunction %s(", m($m->getModifiers(), $c), 
                             $m->getName());
                     $ps = array();
                     foreach ($m->getParameters() as $p) {
diff --git a/scripts/gen_switch_utf8.php b/scripts/gen_switch_utf8.php
new file mode 100755 (executable)
index 0000000..66a97c8
--- /dev/null
@@ -0,0 +1,152 @@
+#!/usr/bin/env php
+<?php
+
+error_reporting(E_ALL);
+set_error_handler(function($c, $e, $f, $l) {
+       throw new Exception("$e in $f on line $l");
+});
+
+$i18n = $argc >= 2 ? $argv[1] : "/usr/share/i18n/locales/i18n";
+
+$b = 10;
+$m = 0xfffff000 >> $b;
+$b2 = 8;
+$x = $m >> $b2;
+
+$f = fopen($i18n, "r");
+$c = false;
+$a = false;
+
+$ranges = $lables = $gotos = [];
+
+ob_start(null, 0xfffff);
+
+while (!feof($f)) {
+       $line = fgets($f);
+       if (!$c && $line !== "LC_CTYPE\n") {
+               continue;
+       }
+       $c = true;
+       if ($line === "END LC_CTYPE\n") {
+               break;
+       }
+       switch($line{0}) {
+       case "%":
+               break;
+       case "\n":
+               if ($a) {
+                       break 2;
+               }
+               break;
+       case " ":
+               if ($a) {
+                       foreach (explode(";", trim($line, "\n/ ;")) as $list) {
+                               $range = explode("..", $list);
+                               $step = 0;
+                               $end = 0;
+                               switch (count($range)) {
+                               case 3:
+                                       list($sstart, $sstep, $send) = $range;
+                                       sscanf($sstart, "<U%X>", $start);
+                                       sscanf($sstep, "(%d)", $step);
+                                       sscanf($send, "<U%X>", $end);
+
+                                       break;
+                               case 2:
+                                       list($sstart, $send) = $range;
+                                       $step = 1;
+                                       sscanf($sstart, "<U%X>", $start);
+                                       sscanf($send, "<U%X>", $end);
+                                       break;
+                               case 1:
+                                       list($sstart) = $range;
+                                       sscanf($sstart, "<U%X>", $start);
+                                       break;
+                               }
+                               $sw = $start >> $b;
+                               $sw2 = ($start & $m) >> $b2;
+                               if (isset($ranges[$sw][$sw2])) {
+                                       //$ranges[$sw][$sw2] = array_filter($ranges[$sw][$sw2]);
+                               }
+                               $ranges[$sw][$sw2][]=[$start,$end,$step];
+                               if ($end) {
+                                       $goto = $start;
+                               }
+                               while (($start += $step) <= $end) {
+                                       $ssw = $start >> $b;
+                                       $ssw2 = ($start & $m) >> $b2;
+                                       if (!isset($ranges[$ssw][$ssw2]) || null !== end($ranges[$ssw][$ssw2])) {
+                                               $ranges[$ssw][$ssw2][]=null;
+                                       }
+                                       if ($ssw != $sw || $ssw2 != $sw2) {
+                                               $gotos[$ssw][$ssw2] = $goto;
+                                               $labels[$sw][$sw2] = $goto;
+                                       }
+                               }
+                       }
+               }
+               break;
+       default:
+               if ($a) {
+                       break 2;
+               } elseif ($line === "alpha /\n") {
+                       $a = true;
+               }
+               break;
+       }
+}
+
+function sp($sp, $ch = " ") { return str_repeat($ch, $sp); }
+
+printf("switch (ch >> %d) {\n", $b);
+foreach ($ranges as $sw => $sws) {
+       printf("case 0x%08X:\n", $sw);
+       printf(" switch((ch & 0x%08X) >> %d) {\n", $m, $b2);
+       foreach ($sws as $sw2 => $specs) {
+               printf(" case 0x%08X:\n", $sw2);
+               $sp = 2;
+               $start = null;
+               foreach ($specs as $index => $spec) {
+                       if ($spec) {
+                               list($start, $end, $step) = $spec;
+                               if (isset($labels[$sw][$sw2])) {
+                                       $label = $labels[$sw][$sw2];
+                                       if ((!$end && $label == $start) || ($end && $label >= $start && $label <= $end)) {
+                                               printf("%sc_%08X:;\n", sp($sp), $label);
+                                       }
+                               }
+                               if ($end) {
+                                       if ($step > 1) {
+                                               die("\nUNEXPECTED: step>1\n");
+                                               printf("\tfor (i=0x%08X; i <= 0x%08X; i+= %d) { if (i == ch) return 1; }\n", $start, $end, $step);
+                                       } else {
+                                               printf("%sif (ch >= 0x%08X) {\n", sp($sp), $start);
+                                               printf("%sif (ch <= 0x%08X) return 1;\n", sp(++$sp), $end);
+                                       }
+                               } else {
+                                       printf("%sif (ch == 0x%08X) return 1;\n", sp($sp), $start);
+                               }                       
+                       } else {
+                               if (isset($gotos[$sw][$sw2]) && !$start) {
+                                       if (isset($specs[$index + 1])) {
+                                               list($next) = $specs[$index + 1];
+                                               printf("%sif (ch < 0x%08X)\n ", sp($sp), $next);
+                                       }
+                                       $goto = $gotos[$sw][$sw2];
+                                       printf("%sgoto c_%08X;\n", sp($sp), $goto);
+                                       $start = $goto;
+                               }
+                       }
+               }
+               if ($sp > 2) {
+                       printf("  %s\n", sp($sp-2, "}"));
+               }
+               printf("  break;\n");
+       }
+       printf(" }\n break;\n");
+}
+printf("}\n");
+
+file_put_contents("php_http_utf8.h",
+       preg_replace('/(\/\* BEGIN::UTF8SWITCH \*\/\n).*(\n\s*\/\* END::UTF8SWITCH \*\/)/s', '$1'. ob_get_contents() .'$2',
+               file_get_contents("php_http_utf8.h")));
index ff43cf0..9471307 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/env php
 # autogenerated file; do not edit
 language: c
-sudo: required
+sudo: false
 dist: trusty
 
 addons:
@@ -15,37 +15,88 @@ addons:
    - libicu-dev
    - libevent-dev
 
+compiler:
+ - gcc
+ - clang
+
+cache:
+ directories:
+  - $HOME/cache
+before_cache:
+ - find $HOME/cache -name '*.gcda' -o -name '*.gcno' -delete
+
 env:
 <?php
 
 $gen = include "./travis/pecl/gen-matrix.php";
+$cur = "7.2";
 $env = $gen([
-       "PHP" => ["7.0", "7.1", "master"],
+// most useful for all additional versions except current
+       "PHP" => ["7.0", "7.1", "7.3", "master"],
+       "enable_debug" => "yes",
+       "enable_maintainer_zts" => "yes",
+       "enable_json" => "yes",
+       "enable_hash" => "yes",
+       "enable_iconv" => "yes",
+       "with_http_libbrotli_dir" => "/home/travis/brotli"
+], [
+// everything disabled for current
+       "PHP" => $cur,
+       "with_http_libicu_dir" => "no",
+       "with_http_libidn_dir" => "no",
+       "with_http_libidn2_dir" => "no",
+       "with_http_libcurl_dir" => "no",
+       "with_http_libevent_dir" => "no",
+], [
+// everything enabled for current, switching on debug/zts
+       "PHP" => $cur,
        "enable_debug",
        "enable_maintainer_zts",
-       "enable_json",
-       "enable_hash" => ["yes"],
-       "enable_iconv" => ["yes"],
-       "with_http_libicu_dir",
+       "enable_json" => "yes",
+       "enable_hash" => "yes",
+       "enable_iconv" => "yes",
+       "with_http_libbrotli_dir" => "/home/travis/brotli",
+], [
+// once everything enabled for current, with coverage
+       "CFLAGS" => "'-O0 -g --coverage'",
+       "CXXFLAGS" => "'-O0 -g --coverage'",
+       "PHP" => $cur,
+       "enable_json" => "yes",
+       "enable_hash" => "yes",
+       "enable_iconv" => "yes",
+       "with_http_libbrotli_dir" => "/home/travis/brotli",
+       [
+               "with_http_libicu_dir",
+               "with_http_libidn_dir",
+               "with_http_libidn2_dir",
+       ],
 ]);
-foreach ($env as $e) {
-       printf(" - %s\n", $e);
+foreach ($env as $grp) {
+       foreach ($grp as $e) {
+               printf(" - %s\n", $e);
+       }
 }
 
 ?>
-# once with gcov
- - CFLAGS="-O0 -g --coverage" CXXFLAGS="-O0 -g --coverage" PHP=master enable_json=yes enable_hash=yes enable_iconv=yes 
 
-before_script:
- - make -f travis/pecl/Makefile php
- - make -f travis/pecl/Makefile pecl PECL=raphf:raphf:2.0.0
- - make -f travis/pecl/Makefile pecl PECL=propro:propro:2.0.1
- - make -f travis/pecl/Makefile ext PECL=http
+install:
+ - ./travis/brotli.sh v1.0.2
+ - |
+   if test "$PHP" = master; then \
+     make -f travis/pecl/Makefile reconf; \
+     make -f travis/pecl/Makefile pecl-rm pecl-clean PECL=ext-raphf.git:raphf:master; \
+     make -f travis/pecl/Makefile pecl-rm pecl-clean PECL=ext-propro.git:propro:master; \
+   fi
+ - make -f travis/pecl/Makefile php || make -f travis/pecl/Makefile clean php
+ - make -f travis/pecl/Makefile pecl PECL=ext-raphf.git:raphf:master
+ - make -f travis/pecl/Makefile pecl PECL=ext-propro.git:propro:master
 
 script:
+ - make -f travis/pecl/Makefile ext PECL=http
  - make -f travis/pecl/Makefile test
- - make -f travis/pecl/Makefile cppcheck
 
+after_script:
+ - make -f travis/pecl/Makefile cppcheck
 after_failure:
  - test -e tests/helper/server.log && cat tests/helper/server.log
 after_success:
index 865a2f5..43b49e3 100755 (executable)
@@ -11,6 +11,8 @@ $i18n = $argc >= 2 ? $argv[1] : "/usr/share/i18n/locales/i18n";
 $f = fopen($i18n, "r");
 $c = false;
 $a = false;
+$r = [];
+$n = [];
 
 ob_start(null, 0xffff);
 while (!feof($f)) {
@@ -55,23 +57,14 @@ while (!feof($f)) {
                                        sscanf($sstart, "<U%X>", $start);
                                        break;
                                }
-                               print "\t{";
-                               if ($start >= 0xffff) {
-                                       printf("0x%08X, ", $start);
-                                       if ($end) {
-                                               printf("0x%08X, ", $end);
-                                       } else {
-                                               print("         0, ");
+                               if ($end) {
+                                       if ($step != 1) {
+                                               die("UNEXPECTED step=$step\n");
                                        }
+                                       $r[] = [$start, $end];
                                } else {
-                                       printf("    0x%04X, ", $start);
-                                       if ($end) {
-                                               printf("    0x%04X, ", $end);
-                                       } else {
-                                               print("         0, ");
-                                       }
+                                       $n[] = $start;
                                }
-                               printf("%d},\n", $step);
                        }
                }
                break;
@@ -85,6 +78,29 @@ while (!feof($f)) {
        }
 }
 
+$maxstep = 0;
+printf("static const utf8_range_t utf8_ranges[] = {\n\t{");
+foreach ($r as $i => list($start, $end)) {
+       if ($i) if ($i%3) {
+               printf(", {");
+       } else {
+               printf(",\n\t{");
+       }
+               
+       printf("0x%08X, 0x%08X}", $start, $end);
+}
+printf("\n};\n\n");
+printf("static const unsigned utf8_chars[] = {\n\t");
+foreach ($n as $i => $u) {
+       if ($i) if (($i%6)) {
+               printf(", ");
+       } else {
+               printf(",\n\t");
+       }
+       printf("0x%08X", $u);
+}
+printf("\n};\n");
+
 file_put_contents("php_http_utf8.h",
        preg_replace('/(\/\* BEGIN::UTF8TABLE \*\/\n).*(\n\s*\/\* END::UTF8TABLE \*\/)/s', '$1'. ob_get_contents() .'$2',
                file_get_contents("php_http_utf8.h")));
index 207c248..498a224 100644 (file)
 #if PHP_HTTP_HAVE_LIBICU
 #      include <unicode/uversion.h>
 #endif
-#if PHP_HTTP_HAVE_LIBIDN2
-#      include <idn2.h>
-#endif
 #if PHP_HTTP_HAVE_LIBIDN
 #      include <idna.h>
 #endif
+#if PHP_HTTP_HAVE_LIBIDN2
+#      include <idn2.h>
+#endif
 #if PHP_HTTP_HAVE_LIBIDNKIT2 || PHP_HTTP_HAVE_LIBIDNKIT
 #include "idn/version.h"
 #endif
@@ -137,6 +137,10 @@ PHP_MINIT_FUNCTION(http)
        || SUCCESS != PHP_MINIT_CALL(http_exception)
        || SUCCESS != PHP_MINIT_CALL(http_cookie)
        || SUCCESS != PHP_MINIT_CALL(http_encoding)
+       || SUCCESS != PHP_MINIT_CALL(http_encoding_zlib)
+#if PHP_HTTP_HAVE_LIBBROTLI
+       || SUCCESS != PHP_MINIT_CALL(http_encoding_brotli)
+#endif
        || SUCCESS != PHP_MINIT_CALL(http_filter)
        || SUCCESS != PHP_MINIT_CALL(http_header)
        || SUCCESS != PHP_MINIT_CALL(http_header_parser)
index 4071f0f..bca57f1 100644 (file)
 #ifndef PHP_HTTP_API_H
 #define PHP_HTTP_API_H
 
+#ifdef __COVERITY_GCC_VERSION_AT_LEAST
+# define _Float128 float
+# define _Float64 float
+# define _Float32 float
+# define _Float64x float
+# define _Float32x float
+#endif
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
@@ -80,6 +88,8 @@
 #include "php_http.h"
 #include "php_http_cookie.h"
 #include "php_http_encoding.h"
+#include "php_http_encoding_zlib.h"
+#include "php_http_encoding_brotli.h"
 #include "php_http_info.h"
 #include "php_http_message.h"
 #include "php_http_env.h"
index d7cfd56..5913cfc 100644 (file)
@@ -10,7 +10,7 @@
     +--------------------------------------------------------------------+
 */
 
-#include "php.h"
+#include "php_http_api.h"
 #include "php_http_buffer.h"
 
 PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_init_ex(
@@ -37,7 +37,7 @@ PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_from_string_ex(
 {
        int free_buf = !!buf;
 
-       if ((buf = php_http_buffer_init(buf))) {
+       if (EXPECTED(buf = php_http_buffer_init(buf))) {
                if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(buf, str, len)) {
                        if (free_buf) {
                                pefree(buf, buf->pmem);
@@ -59,7 +59,7 @@ PHP_HTTP_BUFFER_API size_t php_http_buffer_resize_ex(
        if (buf->free < len) {
                size_t size = override_size ? override_size : buf->size;
                
-               while ((size + buf->free) < len) {
+               while (UNEXPECTED((size + buf->free) < len)) {
                        size <<= 1;
                }
                
@@ -117,9 +117,11 @@ PHP_HTTP_BUFFER_API size_t php_http_buffer_append(php_http_buffer_t *buf,
        ) {
                return PHP_HTTP_BUFFER_NOMEM;
        }
-       memcpy(buf->data + buf->used, append, append_len);
-       buf->used += append_len;
-       buf->free -= append_len;
+       if (append_len) {
+               memcpy(buf->data + buf->used, append, append_len);
+               buf->used += append_len;
+               buf->free -= append_len;
+       }
        return append_len;
 }
 
@@ -147,7 +149,9 @@ PHP_HTTP_BUFFER_API char *php_http_buffer_data(const php_http_buffer_t *buf,
                char **into, size_t *len)
 {
        char *copy = ecalloc(1, buf->used + 1);
-       memcpy(copy, buf->data, buf->used);
+       if (buf->data) {
+               memcpy(copy, buf->data, buf->used);
+       }
        if (into) {
                *into = copy;
        }
@@ -300,7 +304,7 @@ PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_input(php_http_buffer_t **s,
        php_http_buffer_t *str;
        size_t passed;
 
-       if (!*s) {
+       if (UNEXPECTED(!*s)) {
                *s = php_http_buffer_init_ex(NULL, chunk_size,
                                chunk_size ? PHP_HTTP_BUFFER_INIT_PREALLOC : 0);
        }
index 818c443..6592ec4 100644 (file)
@@ -24,7 +24,7 @@
 #ifndef PTR_FREE
 #      define PTR_FREE(PTR) \
        { \
-               if (PTR) { \
+               if (EXPECTED(PTR)) { \
                        efree(PTR); \
                } \
        }
index cedc239..2f8b4ae 100644 (file)
@@ -689,7 +689,7 @@ static PHP_METHOD(HttpClient, enqueue)
        zend_fcall_info_cache fcc = empty_fcall_info_cache;
        php_http_client_object_t *obj;
        php_http_message_object_t *msg_obj;
-       php_http_client_enqueue_t q;
+       php_http_client_enqueue_t q = {0};
 
        php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "O|f", &request, php_http_get_client_request_class_entry(), &fci, &fcc), invalid_arg, return);
 
@@ -701,6 +701,17 @@ static PHP_METHOD(HttpClient, enqueue)
                return;
        }
 
+       /* set early for progress callback */
+       q.opaque = msg_obj;
+
+       if (obj->client->callback.progress.func) {
+               php_http_client_progress_state_t progress = {0};
+
+               progress.info = "prepare";
+               obj->client->callback.progress.func(obj->client->callback.progress.arg, obj->client, &q, &progress);
+       }
+
+       Z_ADDREF_P(request);
        q.request = msg_obj->message;
        q.options = combined_options(getThis(), request);
        q.dtor = msg_queue_dtor;
@@ -711,12 +722,10 @@ static PHP_METHOD(HttpClient, enqueue)
        if (fci.size) {
                Z_TRY_ADDREF(fci.function_name);
                if (fci.object) {
-                       ++GC_REFCOUNT(fci.object);
+                       GC_ADDREF(fci.object);
                }
        }
 
-       Z_ADDREF_P(request);
-
        php_http_expect(SUCCESS == php_http_client_enqueue(obj->client, &q), runtime,
                        msg_queue_dtor(&q);
                        return;
@@ -781,7 +790,7 @@ static PHP_METHOD(HttpClient, requeue)
        if (fci.size) {
                Z_TRY_ADDREF(fci.function_name);
                if (fci.object) {
-                       ++GC_REFCOUNT(fci.object);
+                       GC_ADDREF(fci.object);
                }
        }
 
@@ -1305,7 +1314,7 @@ static PHP_METHOD(HttpClient, setDebug)
 }
 
 static zend_function_entry php_http_client_methods[] = {
-       PHP_ME(HttpClient, __construct,          ai_HttpClient_construct,            ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+       PHP_ME(HttpClient, __construct,          ai_HttpClient_construct,            ZEND_ACC_PUBLIC)
        PHP_ME(HttpClient, reset,                ai_HttpClient_reset,                ZEND_ACC_PUBLIC)
        PHP_ME(HttpClient, enqueue,              ai_HttpClient_enqueue,              ZEND_ACC_PUBLIC)
        PHP_ME(HttpClient, dequeue,              ai_HttpClient_dequeue,              ZEND_ACC_PUBLIC)
index f07bb8f..1c84e3d 100644 (file)
@@ -218,6 +218,7 @@ static int php_http_curle_raw_callback(CURL *ch, curl_infotype type, char *data,
        switch (type) {
                case CURLINFO_TEXT:
                        if (data[0] == '-') {
+                               goto text;
                        } else if (php_memnstr(data, ZEND_STRL("Adding handle:"), data + length)) {
                                h->progress.info = "setup";
                        } else if (php_memnstr(data, ZEND_STRL("addHandle"), data + length)) {
@@ -234,8 +235,16 @@ static int php_http_curle_raw_callback(CURL *ch, curl_infotype type, char *data,
                                h->progress.info = "connected";
                        } else if (php_memnstr(data, ZEND_STRL("blacklisted"), data + length)) {
                                h->progress.info = "blacklist check";
+                       } else if (php_memnstr(data, ZEND_STRL("TLS"), data + length)) {
+                               h->progress.info = "ssl negotiation";
                        } else if (php_memnstr(data, ZEND_STRL("SSL"), data + length)) {
                                h->progress.info = "ssl negotiation";
+                       } else if (php_memnstr(data, ZEND_STRL("certificate"), data + length)) {
+                               h->progress.info = "ssl negotiation";
+                       } else if (php_memnstr(data, ZEND_STRL("ALPN"), data + length)) {
+                               h->progress.info = "alpn";
+                       } else if (php_memnstr(data, ZEND_STRL("NPN"), data + length)) {
+                               h->progress.info = "npn";
                        } else if (php_memnstr(data, ZEND_STRL("upload"), data + length)) {
                                h->progress.info = "uploaded";
                        } else if (php_memnstr(data, ZEND_STRL("left intact"), data + length)) {
@@ -247,6 +256,7 @@ static int php_http_curle_raw_callback(CURL *ch, curl_infotype type, char *data,
                        } else if (php_memnstr(data, ZEND_STRL("Operation timed out"), data + length)) {
                                h->progress.info = "timeout";
                        } else {
+                               text:;
 #if 0
                                h->progress.info = data;
                                data[length - 1] = '\0';
@@ -480,6 +490,24 @@ static ZEND_RESULT_CODE php_http_curle_get_info(CURL *ch, HashTable *info)
                zend_hash_str_update(info, "http_version", lenof("http_version"), &tmp);
        }
 #endif
+#if PHP_HTTP_CURL_VERSION(7,52,0)
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PROXY_SSL_VERIFYRESULT, &l)) {
+               ZVAL_LONG(&tmp, l);
+               zend_hash_str_update(info, "proxy_ssl_verifyresult", lenof("proxy_ssl_verifyresult"), &tmp);
+       }
+#endif
+#if PHP_HTTP_CURL_VERSION(7,52,0)
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PROTOCOL, &l)) {
+               ZVAL_LONG(&tmp, l);
+               zend_hash_str_update(info, "protocol", lenof("protocol"), &tmp);
+       }
+#endif
+#if PHP_HTTP_CURL_VERSION(7,52,0)
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SCHEME, &c)) {
+               ZVAL_STRING(&tmp, STR_PTR(c));
+               zend_hash_str_update(info, "scheme", lenof("scheme"), &tmp);
+       }
+#endif
 
        /* END::CURLINFO */
 
@@ -488,7 +516,11 @@ static ZEND_RESULT_CODE php_http_curle_get_info(CURL *ch, HashTable *info)
                zval ti_array, subarray;
                struct curl_tlssessioninfo *ti;
 
+#if PHP_HTTP_CURL_VERSION(7,48,0)
+               if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_TLS_SSL_PTR, &ti)) {
+#else
                if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_TLS_SESSION, &ti)) {
+#endif
                        char *backend;
 
                        ZVAL_NULL(&subarray);
@@ -502,15 +534,22 @@ static ZEND_RESULT_CODE php_http_curle_get_info(CURL *ch, HashTable *info)
                                backend = "openssl";
 #if PHP_HTTP_HAVE_LIBCURL_OPENSSL
                                {
+#if PHP_HTTP_CURL_VERSION(7,48,0)
+                                       SSL *ssl = ti->internals;
+                                       SSL_CTX *ctx = ssl ? SSL_get_SSL_CTX(ssl) : NULL;
+#else
                                        SSL_CTX *ctx = ti->internals;
+#endif
 
                                        array_init(&subarray);
-                                       add_assoc_long_ex(&subarray, ZEND_STRL("number"), SSL_CTX_sess_number(ctx));
-                                       add_assoc_long_ex(&subarray, ZEND_STRL("connect"), SSL_CTX_sess_connect(ctx));
-                                       add_assoc_long_ex(&subarray, ZEND_STRL("connect_good"), SSL_CTX_sess_connect_good(ctx));
-                                       add_assoc_long_ex(&subarray, ZEND_STRL("connect_renegotiate"), SSL_CTX_sess_connect_renegotiate(ctx));
-                                       add_assoc_long_ex(&subarray, ZEND_STRL("hits"), SSL_CTX_sess_hits(ctx));
-                                       add_assoc_long_ex(&subarray, ZEND_STRL("cache_full"), SSL_CTX_sess_cache_full(ctx));
+                                       if (ctx) {
+                                               add_assoc_long_ex(&subarray, ZEND_STRL("number"), SSL_CTX_sess_number(ctx));
+                                               add_assoc_long_ex(&subarray, ZEND_STRL("connect"), SSL_CTX_sess_connect(ctx));
+                                               add_assoc_long_ex(&subarray, ZEND_STRL("connect_good"), SSL_CTX_sess_connect_good(ctx));
+                                               add_assoc_long_ex(&subarray, ZEND_STRL("connect_renegotiate"), SSL_CTX_sess_connect_renegotiate(ctx));
+                                               add_assoc_long_ex(&subarray, ZEND_STRL("hits"), SSL_CTX_sess_hits(ctx));
+                                               add_assoc_long_ex(&subarray, ZEND_STRL("cache_full"), SSL_CTX_sess_cache_full(ctx));
+                                       }
                                }
 #endif
                                break;
@@ -522,11 +561,13 @@ static ZEND_RESULT_CODE php_http_curle_get_info(CURL *ch, HashTable *info)
                                        char *desc;
 
                                        array_init(&subarray);
-                                       if ((desc = gnutls_session_get_desc(sess))) {
-                                               add_assoc_string_ex(&subarray, ZEND_STRL("desc"), desc);
-                                               gnutls_free(desc);
+                                       if (sess) {
+                                               if ((desc = gnutls_session_get_desc(sess))) {
+                                                       add_assoc_string_ex(&subarray, ZEND_STRL("desc"), desc);
+                                                       gnutls_free(desc);
+                                               }
+                                               add_assoc_bool_ex(&subarray, ZEND_STRL("resumed"), gnutls_session_is_resumed(sess));
                                        }
-                                       add_assoc_bool_ex(&subarray, ZEND_STRL("resumed"), gnutls_session_is_resumed(sess));
                                }
 #endif
                                break;
index d44f3da..1f69a51 100644 (file)
@@ -22,7 +22,7 @@ typedef struct php_http_client_curl_user_ev {
        php_http_client_curl_user_context_t *context;
 } php_http_client_curl_user_ev_t;
 
-static void php_http_client_curl_user_handler(INTERNAL_FUNCTION_PARAMETERS)
+static ZEND_NAMED_FUNCTION(php_http_client_curl_user_handler)
 {
        zval *zstream = NULL, *zclient = NULL;
        php_stream *stream = NULL;
index 71a9129..9055f49 100644 (file)
@@ -273,7 +273,7 @@ static PHP_METHOD(HttpClientRequest, getSslOptions)
 }
 
 static zend_function_entry php_http_client_request_methods[] = {
-       PHP_ME(HttpClientRequest, __construct,    ai_HttpClientRequest___construct,    ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+       PHP_ME(HttpClientRequest, __construct,    ai_HttpClientRequest___construct,    ZEND_ACC_PUBLIC)
        PHP_ME(HttpClientRequest, setContentType, ai_HttpClientRequest_setContentType, ZEND_ACC_PUBLIC)
        PHP_ME(HttpClientRequest, getContentType, ai_HttpClientRequest_getContentType, ZEND_ACC_PUBLIC)
        PHP_ME(HttpClientRequest, setQuery,       ai_HttpClientRequest_setQuery,       ZEND_ACC_PUBLIC)
index 83d1794..c3e9a3a 100644 (file)
@@ -12,8 +12,6 @@
 
 #include "php_http_api.h"
 
-#include <zlib.h>
-
 static inline int eol_match(char **line, int *eol_len)
 {
        char *ptr = *line;
@@ -109,129 +107,6 @@ const char *php_http_encoding_dechunk(const char *encoded, size_t encoded_len, c
        return e_ptr;
 }
 
-static inline int php_http_inflate_rounds(z_stream *Z, int flush, char **buf, size_t *len)
-{
-       int status = 0, round = 0;
-       php_http_buffer_t buffer;
-       
-       *buf = NULL;
-       *len = 0;
-       
-       php_http_buffer_init_ex(&buffer, Z->avail_in, PHP_HTTP_BUFFER_INIT_PREALLOC);
-       
-       do {
-               if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize_ex(&buffer, buffer.size, 0, 1)) {
-                       status = Z_MEM_ERROR;
-               } else {
-                       Z->avail_out = buffer.free;
-                       Z->next_out = (Bytef *) buffer.data + buffer.used;
-#if 0
-                       fprintf(stderr, "\n%3d: %3d PRIOR: size=%7lu,\tfree=%7lu,\tused=%7lu,\tavail_in=%7lu,\tavail_out=%7lu\n", round, status, buffer.size, buffer.free, buffer.used, Z->avail_in, Z->avail_out);
-#endif
-                       status = inflate(Z, flush);
-                       php_http_buffer_account(&buffer, buffer.free - Z->avail_out);
-#if 0
-                       fprintf(stderr, "%3d: %3d AFTER: size=%7lu,\tfree=%7lu,\tused=%7lu,\tavail_in=%7lu,\tavail_out=%7lu\n", round, status, buffer.size, buffer.free, buffer.used, Z->avail_in, Z->avail_out);
-#endif
-                       PHP_HTTP_INFLATE_BUFFER_SIZE_ALIGN(buffer.size);
-               }
-       } while ((Z_BUF_ERROR == status || (Z_OK == status && Z->avail_in)) && ++round < PHP_HTTP_INFLATE_ROUNDS);
-       
-       if (status == Z_OK || status == Z_STREAM_END) {
-               php_http_buffer_shrink(&buffer);
-               php_http_buffer_fix(&buffer);
-               *buf = buffer.data;
-               *len = buffer.used;
-       } else {
-               php_http_buffer_dtor(&buffer);
-       }
-       
-       return status;
-}
-
-ZEND_RESULT_CODE php_http_encoding_deflate(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len)
-{
-       int status, level, wbits, strategy;
-       z_stream Z;
-       
-       PHP_HTTP_DEFLATE_LEVEL_SET(flags, level);
-       PHP_HTTP_DEFLATE_WBITS_SET(flags, wbits);
-       PHP_HTTP_DEFLATE_STRATEGY_SET(flags, strategy);
-       
-       memset(&Z, 0, sizeof(z_stream));
-       *encoded = NULL;
-       *encoded_len = 0;
-       
-       status = deflateInit2(&Z, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy);
-       if (Z_OK == status) {
-               *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len);
-               *encoded = emalloc(*encoded_len);
-               
-               Z.next_in = (Bytef *) data;
-               Z.next_out = (Bytef *) *encoded;
-               Z.avail_in = data_len;
-               Z.avail_out = *encoded_len;
-               
-               status = deflate(&Z, Z_FINISH);
-               deflateEnd(&Z);
-               
-               if (Z_STREAM_END == status) {
-                       /* size buffer down to actual length */
-                       *encoded = erealloc(*encoded, Z.total_out + 1);
-                       (*encoded)[*encoded_len = Z.total_out] = '\0';
-                       return SUCCESS;
-               } else {
-                       PTR_SET(*encoded, NULL);
-                       *encoded_len = 0;
-               }
-       }
-       
-       php_error_docref(NULL, E_WARNING, "Could not deflate data: %s", zError(status));
-       return FAILURE;
-}
-
-ZEND_RESULT_CODE php_http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len)
-{
-       z_stream Z;
-       int status, wbits = PHP_HTTP_WINDOW_BITS_ANY;
-       
-       memset(&Z, 0, sizeof(z_stream));
-       
-retry_raw_inflate:
-       status = inflateInit2(&Z, wbits);
-       if (Z_OK == status) {
-               Z.next_in = (Bytef *) data;
-               Z.avail_in = data_len + 1; /* include the terminating NULL, see #61287 */
-               
-               switch (status = php_http_inflate_rounds(&Z, Z_NO_FLUSH, decoded, decoded_len)) {
-                       case Z_STREAM_END:
-                               inflateEnd(&Z);
-                               return SUCCESS;
-
-                       case Z_OK:
-                               status = Z_DATA_ERROR;
-                               break;
-                       
-                       case Z_DATA_ERROR:
-                               /* raw deflated data? */
-                               if (PHP_HTTP_WINDOW_BITS_ANY == wbits) {
-                                       inflateEnd(&Z);
-                                       wbits = PHP_HTTP_WINDOW_BITS_RAW;
-                                       goto retry_raw_inflate;
-                               }
-                               break;
-               }
-               inflateEnd(&Z);
-
-               if (*decoded_len && *decoded) {
-                       efree(*decoded);
-               }
-       }
-       
-       php_error_docref(NULL, E_WARNING, "Could not inflate data: %s", zError(status));
-       return FAILURE;
-}
-
 php_http_encoding_stream_t *php_http_encoding_stream_init(php_http_encoding_stream_t *s, php_http_encoding_stream_ops_t *ops, unsigned flags)
 {
        int freeme;
@@ -243,10 +118,10 @@ php_http_encoding_stream_t *php_http_encoding_stream_init(php_http_encoding_stre
 
        s->flags = flags;
 
-       if ((s->ops = ops)) {
+       if (EXPECTED(s->ops = ops)) {
                php_http_encoding_stream_t *ss = s->ops->init(s);
 
-               if (ss) {
+               if (EXPECTED(ss)) {
                        return ss;
                }
        } else {
@@ -286,10 +161,12 @@ ZEND_RESULT_CODE php_http_encoding_stream_reset(php_http_encoding_stream_t **s)
 {
        php_http_encoding_stream_t *ss;
 
-       if ((*s)->ops->dtor) {
+       if (EXPECTED((*s)->ops->dtor)) {
                (*s)->ops->dtor(*s);
        }
-       if ((ss = (*s)->ops->init(*s))) {
+
+       if (EXPECTED(ss = (*s)->ops->init(*s))) {
+               ss->flags &= ~PHP_HTTP_ENCODING_STREAM_DIRTY;
                *s = ss;
                return SUCCESS;
        }
@@ -298,10 +175,15 @@ ZEND_RESULT_CODE php_http_encoding_stream_reset(php_http_encoding_stream_t **s)
 
 ZEND_RESULT_CODE php_http_encoding_stream_update(php_http_encoding_stream_t *s, const char *in_str, size_t in_len, char **out_str, size_t *out_len)
 {
-       if (!s->ops->update) {
-               return FAILURE;
+       ZEND_RESULT_CODE rc = FAILURE;
+
+       if (EXPECTED(s->ops->update)) {
+               rc = s->ops->update(s, in_str, in_len, out_str, out_len);
        }
-       return s->ops->update(s, in_str, in_len, out_str, out_len);
+
+       s->flags |= PHP_HTTP_ENCODING_STREAM_DIRTY;
+
+       return rc;
 }
 
 ZEND_RESULT_CODE php_http_encoding_stream_flush(php_http_encoding_stream_t *s, char **out_str, size_t *out_len)
@@ -317,7 +199,7 @@ ZEND_RESULT_CODE php_http_encoding_stream_flush(php_http_encoding_stream_t *s, c
 zend_bool php_http_encoding_stream_done(php_http_encoding_stream_t *s)
 {
        if (!s->ops->done) {
-               return 0;
+               return !(s->flags & PHP_HTTP_ENCODING_STREAM_DIRTY);
        }
        return s->ops->done(s);
 }
@@ -327,6 +209,9 @@ ZEND_RESULT_CODE php_http_encoding_stream_finish(php_http_encoding_stream_t *s,
        if (!s->ops->finish) {
                *out_str = NULL;
                *out_len = 0;
+
+               s->flags &= ~PHP_HTTP_ENCODING_STREAM_DIRTY;
+
                return SUCCESS;
        }
        return s->ops->finish(s, out_str, out_len);
@@ -334,15 +219,15 @@ ZEND_RESULT_CODE php_http_encoding_stream_finish(php_http_encoding_stream_t *s,
 
 void php_http_encoding_stream_dtor(php_http_encoding_stream_t *s)
 {
-       if (s->ops->dtor) {
+       if (EXPECTED(s->ops->dtor)) {
                s->ops->dtor(s);
        }
 }
 
 void php_http_encoding_stream_free(php_http_encoding_stream_t **s)
 {
-       if (*s) {
-               if ((*s)->ops->dtor) {
+       if (EXPECTED(*s)) {
+               if (EXPECTED((*s)->ops->dtor)) {
                        (*s)->ops->dtor(*s);
                }
                pefree(*s, ((*s)->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT));
@@ -356,48 +241,6 @@ struct dechunk_ctx {
        unsigned zeroed:1;
 };
 
-static php_http_encoding_stream_t *deflate_init(php_http_encoding_stream_t *s)
-{
-       int status, level, wbits, strategy, p = (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT);
-       z_streamp ctx = pecalloc(1, sizeof(z_stream), p);
-       
-       PHP_HTTP_DEFLATE_LEVEL_SET(s->flags, level);
-       PHP_HTTP_DEFLATE_WBITS_SET(s->flags, wbits);
-       PHP_HTTP_DEFLATE_STRATEGY_SET(s->flags, strategy);
-       
-       if (Z_OK == (status = deflateInit2(ctx, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy))) {
-               if ((ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) {
-                       s->ctx = ctx;
-                       return s;
-               }
-               deflateEnd(ctx);
-               status = Z_MEM_ERROR;
-       }
-       pefree(ctx, p);
-       php_error_docref(NULL, E_WARNING, "Failed to initialize deflate encoding stream: %s", zError(status));
-       return NULL;
-}
-
-static php_http_encoding_stream_t *inflate_init(php_http_encoding_stream_t *s)
-{
-       int status, wbits, p = (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT);
-       z_streamp ctx = pecalloc(1, sizeof(z_stream), p);
-       
-       PHP_HTTP_INFLATE_WBITS_SET(s->flags, wbits);
-       
-       if (Z_OK == (status = inflateInit2(ctx, wbits))) {
-               if ((ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) {
-                       s->ctx = ctx;
-                       return s;
-               }
-               inflateEnd(ctx);
-               status = Z_MEM_ERROR;
-       }
-       pefree(ctx, p);
-       php_error_docref(NULL, E_WARNING, "Failed to initialize inflate stream: %s", zError(status));
-       return NULL;
-}
-
 static php_http_encoding_stream_t *dechunk_init(php_http_encoding_stream_t *s)
 {
        struct dechunk_ctx *ctx = pecalloc(1, sizeof(*ctx), (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT));
@@ -413,42 +256,6 @@ static php_http_encoding_stream_t *dechunk_init(php_http_encoding_stream_t *s)
        return s;
 }
 
-static php_http_encoding_stream_t *deflate_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to)
-{
-       int status, p = to->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT;
-       z_streamp from_ctx = from->ctx, to_ctx = pecalloc(1, sizeof(*to_ctx), p);
-
-       if (Z_OK == (status = deflateCopy(to_ctx, from_ctx))) {
-               if ((to_ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) {
-                       php_http_buffer_append(to_ctx->opaque, PHP_HTTP_BUFFER(from_ctx->opaque)->data, PHP_HTTP_BUFFER(from_ctx->opaque)->used);
-                       to->ctx = to_ctx;
-                       return to;
-               }
-               deflateEnd(to_ctx);
-               status = Z_MEM_ERROR;
-       }
-       php_error_docref(NULL, E_WARNING, "Failed to copy deflate encoding stream: %s", zError(status));
-       return NULL;
-}
-
-static php_http_encoding_stream_t *inflate_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to)
-{
-       int status, p = from->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT;
-       z_streamp from_ctx = from->ctx, to_ctx = pecalloc(1, sizeof(*to_ctx), p);
-
-       if (Z_OK == (status = inflateCopy(to_ctx, from_ctx))) {
-               if ((to_ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) {
-                       php_http_buffer_append(to_ctx->opaque, PHP_HTTP_BUFFER(from_ctx->opaque)->data, PHP_HTTP_BUFFER(from_ctx->opaque)->used);
-                       to->ctx = to_ctx;
-                       return to;
-               }
-               inflateEnd(to_ctx);
-               status = Z_MEM_ERROR;
-       }
-       php_error_docref(NULL, E_WARNING, "Failed to copy inflate encoding stream: %s", zError(status));
-       return NULL;
-}
-
 static php_http_encoding_stream_t *dechunk_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to)
 {
        int p = from->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT;
@@ -466,84 +273,6 @@ static php_http_encoding_stream_t *dechunk_copy(php_http_encoding_stream_t *from
        return NULL;
 }
 
-static ZEND_RESULT_CODE deflate_update(php_http_encoding_stream_t *s, const char *data, size_t data_len, char **encoded, size_t *encoded_len)
-{
-       int status;
-       z_streamp ctx = s->ctx;
-       
-       /* append input to our buffer */
-       php_http_buffer_append(PHP_HTTP_BUFFER(ctx->opaque), data, data_len);
-       
-       ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data;
-       ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used;
-       
-       /* deflate */
-       *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len);
-       *encoded = emalloc(*encoded_len);
-       ctx->avail_out = *encoded_len;
-       ctx->next_out = (Bytef *) *encoded;
-       
-       switch (status = deflate(ctx, PHP_HTTP_ENCODING_STREAM_FLUSH_FLAG(s->flags))) {
-               case Z_OK:
-               case Z_STREAM_END:
-                       /* cut processed chunk off the buffer */
-                       if (ctx->avail_in) {
-                               php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in);
-                       } else {
-                               php_http_buffer_reset(PHP_HTTP_BUFFER(ctx->opaque));
-                       }
-                       
-                       /* size buffer down to actual size */
-                       *encoded_len -= ctx->avail_out;
-                       *encoded = erealloc(*encoded, *encoded_len + 1);
-                       (*encoded)[*encoded_len] = '\0';
-                       return SUCCESS;
-       }
-       
-       PTR_SET(*encoded, NULL);
-       *encoded_len = 0;
-       php_error_docref(NULL, E_WARNING, "Failed to update deflate stream: %s", zError(status));
-       return FAILURE;
-}
-
-static ZEND_RESULT_CODE inflate_update(php_http_encoding_stream_t *s, const char *data, size_t data_len, char **decoded, size_t *decoded_len)
-{
-       int status;
-       z_streamp ctx = s->ctx;
-       
-       /* append input to buffer */
-       php_http_buffer_append(PHP_HTTP_BUFFER(ctx->opaque), data, data_len);
-
-retry_raw_inflate:
-       ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data;
-       ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used;
-       
-       switch (status = php_http_inflate_rounds(ctx, PHP_HTTP_ENCODING_STREAM_FLUSH_FLAG(s->flags), decoded, decoded_len)) {
-               case Z_OK:
-               case Z_STREAM_END:
-                       /* cut off */
-                       if (ctx->avail_in) {
-                               php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in);
-                       } else {
-                               php_http_buffer_reset(PHP_HTTP_BUFFER(ctx->opaque));
-                       }
-                       return SUCCESS;
-               
-               case Z_DATA_ERROR:
-                       /* raw deflated data ? */
-                       if (!(s->flags & PHP_HTTP_INFLATE_TYPE_RAW) && !ctx->total_out) {
-                               inflateEnd(ctx);
-                               s->flags |= PHP_HTTP_INFLATE_TYPE_RAW;
-                               inflateInit2(ctx, PHP_HTTP_WINDOW_BITS_RAW);
-                               goto retry_raw_inflate;
-                       }
-                       break;
-       }
-       
-       php_error_docref(NULL, E_WARNING, "Failed to update inflate stream: %s", zError(status));
-       return FAILURE;
-}
-
 static ZEND_RESULT_CODE dechunk_update(php_http_encoding_stream_t *s, const char *data, size_t data_len, char **decoded, size_t *decoded_len)
 {
        php_http_buffer_t tmp;
@@ -671,34 +400,6 @@ static ZEND_RESULT_CODE dechunk_update(php_http_encoding_stream_t *s, const char
        return SUCCESS;
 }
 
-static ZEND_RESULT_CODE deflate_flush(php_http_encoding_stream_t *s, char **encoded, size_t *encoded_len)
-{
-       int status;
-       z_streamp ctx = s->ctx;
-       
-       *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE;
-       *encoded = emalloc(*encoded_len);
-       
-       ctx->avail_in = 0;
-       ctx->next_in = NULL;
-       ctx->avail_out = *encoded_len;
-       ctx->next_out = (Bytef *) *encoded;
-       
-       switch (status = deflate(ctx, Z_FULL_FLUSH)) {
-               case Z_OK:
-               case Z_STREAM_END:
-                       *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE - ctx->avail_out;
-                       *encoded = erealloc(*encoded, *encoded_len + 1);
-                       (*encoded)[*encoded_len] = '\0';
-                       return SUCCESS;
-       }
-       
-       PTR_SET(*encoded, NULL);
-       *encoded_len = 0;
-       php_error_docref(NULL, E_WARNING, "Failed to flush deflate stream: %s", zError(status));
-       return FAILURE;
-}
-
 static ZEND_RESULT_CODE dechunk_flush(php_http_encoding_stream_t *s, char **decoded, size_t *decoded_len)
 {
        struct dechunk_ctx *ctx = s->ctx;
@@ -719,125 +420,13 @@ static ZEND_RESULT_CODE dechunk_flush(php_http_encoding_stream_t *s, char **deco
        return SUCCESS;
 }
 
-static ZEND_RESULT_CODE deflate_finish(php_http_encoding_stream_t *s, char **encoded, size_t *encoded_len)
-{
-       int status;
-       z_streamp ctx = s->ctx;
-       
-       *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE;
-       *encoded = emalloc(*encoded_len);
-       
-       /* deflate remaining input */
-       ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data;
-       ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used;
-       
-       ctx->avail_out = *encoded_len;
-       ctx->next_out = (Bytef *) *encoded;
-       
-       do {
-               status = deflate(ctx, Z_FINISH);
-       } while (Z_OK == status);
-       
-       if (Z_STREAM_END == status) {
-               /* cut processed input off */
-               php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in);
-               
-               /* size down */
-               *encoded_len -= ctx->avail_out;
-               *encoded = erealloc(*encoded, *encoded_len + 1);
-               (*encoded)[*encoded_len] = '\0';
-               return SUCCESS;
-       }
-       
-       PTR_SET(*encoded, NULL);
-       *encoded_len = 0;
-       php_error_docref(NULL, E_WARNING, "Failed to finish deflate stream: %s", zError(status));
-       return FAILURE;
-}
-
-static ZEND_RESULT_CODE inflate_finish(php_http_encoding_stream_t *s, char **decoded, size_t *decoded_len)
-{
-       int status;
-       z_streamp ctx = s->ctx;
-       
-       if (!PHP_HTTP_BUFFER(ctx->opaque)->used) {
-               *decoded = NULL;
-               *decoded_len = 0;
-               return SUCCESS;
-       }
-       
-       *decoded_len = (PHP_HTTP_BUFFER(ctx->opaque)->used + 1) * PHP_HTTP_INFLATE_ROUNDS;
-       *decoded = emalloc(*decoded_len);
-       
-       /* inflate remaining input */
-       ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data;
-       ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used;
-       
-       ctx->avail_out = *decoded_len;
-       ctx->next_out = (Bytef *) *decoded;
-       
-       if (Z_STREAM_END == (status = inflate(ctx, Z_FINISH))) {
-               /* cut processed input off */
-               php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in);
-               
-               /* size down */
-               *decoded_len -= ctx->avail_out;
-               *decoded = erealloc(*decoded, *decoded_len + 1);
-               (*decoded)[*decoded_len] = '\0';
-               return SUCCESS;
-       }
-       
-       PTR_SET(*decoded, NULL);
-       *decoded_len = 0;
-       php_error_docref(NULL, E_WARNING, "Failed to finish inflate stream: %s", zError(status));
-       return FAILURE;
-}
-
-static zend_bool deflate_done(php_http_encoding_stream_t *s)
-{
-       z_streamp ctx = s->ctx;
-       return !ctx->avail_in && !PHP_HTTP_BUFFER(ctx->opaque)->used;
-}
 
-static zend_bool inflate_done(php_http_encoding_stream_t *s)
-{
-       z_streamp ctx = s->ctx;
-       return !ctx->avail_in && !PHP_HTTP_BUFFER(ctx->opaque)->used;
-}
 
 static zend_bool dechunk_done(php_http_encoding_stream_t *s)
 {
        return ((struct dechunk_ctx *) s->ctx)->zeroed;
 }
 
-static void deflate_dtor(php_http_encoding_stream_t *s)
-{
-       if (s->ctx) {
-               z_streamp ctx = s->ctx;
-
-               if (ctx->opaque) {
-                       php_http_buffer_free((php_http_buffer_t **) &ctx->opaque);
-               }
-               deflateEnd(ctx);
-               pefree(ctx, (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT));
-               s->ctx = NULL;
-       }
-}
-
-static void inflate_dtor(php_http_encoding_stream_t *s)
-{
-       if (s->ctx) {
-               z_streamp ctx = s->ctx;
-
-               if (ctx->opaque) {
-                       php_http_buffer_free((php_http_buffer_t **) &ctx->opaque);
-               }
-               inflateEnd(ctx);
-               pefree(ctx, (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT));
-               s->ctx = NULL;
-       }
-}
-
 static void dechunk_dtor(php_http_encoding_stream_t *s)
 {
        if (s->ctx) {
@@ -849,36 +438,6 @@ static void dechunk_dtor(php_http_encoding_stream_t *s)
        }
 }
 
-static php_http_encoding_stream_ops_t php_http_encoding_deflate_ops = {
-       deflate_init,
-       deflate_copy,
-       deflate_update,
-       deflate_flush,
-       deflate_done,
-       deflate_finish,
-       deflate_dtor
-};
-
-php_http_encoding_stream_ops_t *php_http_encoding_stream_get_deflate_ops(void)
-{
-       return &php_http_encoding_deflate_ops;
-}
-
-static php_http_encoding_stream_ops_t php_http_encoding_inflate_ops = {
-       inflate_init,
-       inflate_copy,
-       inflate_update,
-       NULL,
-       inflate_done,
-       inflate_finish,
-       inflate_dtor
-};
-
-php_http_encoding_stream_ops_t *php_http_encoding_stream_get_inflate_ops(void)
-{
-       return &php_http_encoding_inflate_ops;
-}
-
 static php_http_encoding_stream_ops_t php_http_encoding_dechunk_ops = {
        dechunk_init,
        dechunk_copy,
@@ -920,9 +479,13 @@ php_http_encoding_stream_object_t *php_http_encoding_stream_object_new_ex(zend_c
 
 zend_object *php_http_encoding_stream_object_clone(zval *object)
 {
-       php_http_encoding_stream_object_t *new_obj = NULL, *old_obj = PHP_HTTP_OBJ(NULL, object);
+       php_http_encoding_stream_object_t *new_obj, *old_obj = PHP_HTTP_OBJ(NULL, object);
        php_http_encoding_stream_t *cpy = php_http_encoding_stream_copy(old_obj->stream, NULL);
 
+       if (!cpy) {
+               return NULL;
+       }
+
        new_obj = php_http_encoding_stream_object_new_ex(old_obj->zo.ce, cpy);
        zend_objects_clone_members(&new_obj->zo, &old_obj->zo);
 
@@ -944,16 +507,7 @@ zend_class_entry *php_http_get_encoding_stream_class_entry(void)
 {
        return php_http_encoding_stream_class_entry;
 }
-static zend_class_entry *php_http_deflate_stream_class_entry;
-zend_class_entry *php_http_get_deflate_stream_class_entry(void)
-{
-       return php_http_deflate_stream_class_entry;
-}
-static zend_class_entry *php_http_inflate_stream_class_entry;
-zend_class_entry *php_http_get_inflate_stream_class_entry(void)
-{
-       return php_http_inflate_stream_class_entry;
-}
+
 static zend_class_entry *php_http_dechunk_stream_class_entry;
 zend_class_entry *php_http_get_dechunk_stream_class_entry(void)
 {
@@ -973,17 +527,23 @@ static PHP_METHOD(HttpEncodingStream, __construct)
 
        obj = PHP_HTTP_OBJ(NULL, getThis());
 
-       if (obj->stream) {
+       if (UNEXPECTED(obj->stream)) {
                php_http_throw(bad_method_call, "http\\Encoding\\Stream cannot be initialized twice", NULL);
                return;
        }
 
-       if (instanceof_function(obj->zo.ce, php_http_deflate_stream_class_entry)) {
-               ops = &php_http_encoding_deflate_ops;
-       } else if (instanceof_function(obj->zo.ce, php_http_inflate_stream_class_entry)) {
-               ops = &php_http_encoding_inflate_ops;
+       if (instanceof_function(obj->zo.ce, php_http_get_deflate_stream_class_entry())) {
+               ops = php_http_encoding_stream_get_deflate_ops();
+       } else if (instanceof_function(obj->zo.ce, php_http_get_inflate_stream_class_entry())) {
+               ops = php_http_encoding_stream_get_inflate_ops();
        } else if (instanceof_function(obj->zo.ce, php_http_dechunk_stream_class_entry)) {
                ops = &php_http_encoding_dechunk_ops;
+#if PHP_HTTP_HAVE_LIBBROTLI
+       } else if (instanceof_function(obj->zo.ce, php_http_get_enbrotli_stream_class_entry())) {
+               ops = php_http_encoding_stream_get_enbrotli_ops();
+       } else if (instanceof_function(obj->zo.ce, php_http_get_debrotli_stream_class_entry())) {
+               ops = php_http_encoding_stream_get_debrotli_ops();
+#endif
        } else {
                php_http_throw(runtime, "Unknown http\\Encoding\\Stream class '%s'", obj->zo.ce->name->val);
                return;
@@ -1000,15 +560,15 @@ static PHP_METHOD(HttpEncodingStream, update)
        size_t data_len;
        char *data_str;
 
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data_str, &data_len)) {
+       if (EXPECTED(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data_str, &data_len))) {
                php_http_encoding_stream_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
 
-               if (obj->stream) {
+               if (EXPECTED(obj->stream)) {
                        char *encoded_str = NULL;
                        size_t encoded_len;
 
-                       if (SUCCESS == php_http_encoding_stream_update(obj->stream, data_str, data_len, &encoded_str, &encoded_len)) {
-                               if (encoded_str) {
+                       if (EXPECTED(SUCCESS == php_http_encoding_stream_update(obj->stream, data_str, data_len, &encoded_str, &encoded_len))) {
+                               if (EXPECTED(encoded_str)) {
                                        RETURN_STR(php_http_cs2zs(encoded_str, encoded_len));
                                } else {
                                        RETURN_EMPTY_STRING();
@@ -1022,14 +582,14 @@ ZEND_BEGIN_ARG_INFO_EX(ai_HttpEncodingStream_flush, 0, 0, 0)
 ZEND_END_ARG_INFO();
 static PHP_METHOD(HttpEncodingStream, flush)
 {
-       if (SUCCESS == zend_parse_parameters_none()) {
+       if (EXPECTED(SUCCESS == zend_parse_parameters_none())) {
                php_http_encoding_stream_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
 
-               if (obj->stream) {
+               if (EXPECTED(obj->stream)) {
                        char *encoded_str = NULL;
                        size_t encoded_len;
 
-                       if (SUCCESS == php_http_encoding_stream_flush(obj->stream, &encoded_str, &encoded_len)) {
+                       if (EXPECTED(SUCCESS == php_http_encoding_stream_flush(obj->stream, &encoded_str, &encoded_len))) {
                                if (encoded_str) {
                                        RETURN_STR(php_http_cs2zs(encoded_str, encoded_len));
                                } else {
@@ -1044,10 +604,10 @@ ZEND_BEGIN_ARG_INFO_EX(ai_HttpEncodingStream_done, 0, 0, 0)
 ZEND_END_ARG_INFO();
 static PHP_METHOD(HttpEncodingStream, done)
 {
-       if (SUCCESS == zend_parse_parameters_none()) {
+       if (EXPECTED(SUCCESS == zend_parse_parameters_none())) {
                php_http_encoding_stream_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
 
-               if (obj->stream) {
+               if (EXPECTED(obj->stream)) {
                        RETURN_BOOL(php_http_encoding_stream_done(obj->stream));
                }
        }
@@ -1057,15 +617,15 @@ ZEND_BEGIN_ARG_INFO_EX(ai_HttpEncodingStream_finish, 0, 0, 0)
 ZEND_END_ARG_INFO();
 static PHP_METHOD(HttpEncodingStream, finish)
 {
-       if (SUCCESS == zend_parse_parameters_none()) {
+       if (EXPECTED(SUCCESS == zend_parse_parameters_none())) {
                php_http_encoding_stream_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
 
-               if (obj->stream) {
+               if (EXPECTED(obj->stream)) {
                        char *encoded_str = NULL;
                        size_t encoded_len;
 
-                       if (SUCCESS == php_http_encoding_stream_finish(obj->stream, &encoded_str, &encoded_len)) {
-                               if (SUCCESS == php_http_encoding_stream_reset(&obj->stream)) {
+                       if (EXPECTED(SUCCESS == php_http_encoding_stream_finish(obj->stream, &encoded_str, &encoded_len))) {
+                               if (EXPECTED(SUCCESS == php_http_encoding_stream_reset(&obj->stream))) {
                                        if (encoded_str) {
                                                RETURN_STR(php_http_cs2zs(encoded_str, encoded_len));
                                        } else {
@@ -1080,7 +640,7 @@ static PHP_METHOD(HttpEncodingStream, finish)
 }
 
 static zend_function_entry php_http_encoding_stream_methods[] = {
-       PHP_ME(HttpEncodingStream, __construct,  ai_HttpEncodingStream___construct,  ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+       PHP_ME(HttpEncodingStream, __construct,  ai_HttpEncodingStream___construct,  ZEND_ACC_PUBLIC)
        PHP_ME(HttpEncodingStream, update,       ai_HttpEncodingStream_update,       ZEND_ACC_PUBLIC)
        PHP_ME(HttpEncodingStream, flush,        ai_HttpEncodingStream_flush,        ZEND_ACC_PUBLIC)
        PHP_ME(HttpEncodingStream, done,         ai_HttpEncodingStream_done,         ZEND_ACC_PUBLIC)
@@ -1088,64 +648,6 @@ static zend_function_entry php_http_encoding_stream_methods[] = {
        EMPTY_FUNCTION_ENTRY
 };
 
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpDeflateStream_encode, 0, 0, 1)
-       ZEND_ARG_INFO(0, data)
-       ZEND_ARG_INFO(0, flags)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpDeflateStream, encode)
-{
-       char *str;
-       size_t len;
-       zend_long flags = 0;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &str, &len, &flags)) {
-               char *enc_str = NULL;
-               size_t enc_len;
-
-               if (SUCCESS == php_http_encoding_deflate(flags, str, len, &enc_str, &enc_len)) {
-                       if (enc_str) {
-                               RETURN_STR(php_http_cs2zs(enc_str, enc_len));
-                       } else {
-                               RETURN_EMPTY_STRING();
-                       }
-               }
-       }
-       RETURN_FALSE;
-}
-
-static zend_function_entry php_http_deflate_stream_methods[] = {
-       PHP_ME(HttpDeflateStream, encode, ai_HttpDeflateStream_encode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-       EMPTY_FUNCTION_ENTRY
-};
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpInflateStream_decode, 0, 0, 1)
-       ZEND_ARG_INFO(0, data)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpInflateStream, decode)
-{
-       char *str;
-       size_t len;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &len)) {
-               char *enc_str = NULL;
-               size_t enc_len;
-
-               if (SUCCESS == php_http_encoding_inflate(str, len, &enc_str, &enc_len)) {
-                       if (enc_str) {
-                               RETURN_STR(php_http_cs2zs(enc_str, enc_len));
-                       } else {
-                               RETURN_EMPTY_STRING();
-                       }
-               }
-       }
-       RETURN_FALSE;
-}
-
-static zend_function_entry php_http_inflate_stream_methods[] = {
-       PHP_ME(HttpInflateStream, decode, ai_HttpInflateStream_decode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-       EMPTY_FUNCTION_ENTRY
-};
-
 ZEND_BEGIN_ARG_INFO_EX(ai_HttpDechunkStream_decode, 0, 0, 1)
        ZEND_ARG_INFO(0, data)
        ZEND_ARG_INFO(1, decoded_len)
@@ -1156,12 +658,12 @@ static PHP_METHOD(HttpDechunkStream, decode)
        size_t len;
        zval *zlen = NULL;
 
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|z!", &str, &len, &zlen)) {
+       if (EXPECTED(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|z!", &str, &len, &zlen))) {
                const char *end_ptr;
                char *enc_str = NULL;
                size_t enc_len;
 
-               if ((end_ptr = php_http_encoding_dechunk(str, len, &enc_str, &enc_len))) {
+               if (EXPECTED(end_ptr = php_http_encoding_dechunk(str, len, &enc_str, &enc_len))) {
                        if (zlen) {
                                ZVAL_DEREF(zlen);
                                zval_dtor(zlen);
@@ -1199,28 +701,6 @@ PHP_MINIT_FUNCTION(http_encoding)
        zend_declare_class_constant_long(php_http_encoding_stream_class_entry, ZEND_STRL("FLUSH_SYNC"), PHP_HTTP_ENCODING_STREAM_FLUSH_SYNC);
        zend_declare_class_constant_long(php_http_encoding_stream_class_entry, ZEND_STRL("FLUSH_FULL"), PHP_HTTP_ENCODING_STREAM_FLUSH_FULL);
 
-       memset(&ce, 0, sizeof(ce));
-       INIT_NS_CLASS_ENTRY(ce, "http\\Encoding\\Stream", "Deflate", php_http_deflate_stream_methods);
-       php_http_deflate_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_encoding_stream_class_entry);
-       php_http_deflate_stream_class_entry->create_object = php_http_encoding_stream_object_new;
-
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_GZIP"), PHP_HTTP_DEFLATE_TYPE_GZIP);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_ZLIB"), PHP_HTTP_DEFLATE_TYPE_ZLIB);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_RAW"), PHP_HTTP_DEFLATE_TYPE_RAW);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_DEF"), PHP_HTTP_DEFLATE_LEVEL_DEF);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_MIN"), PHP_HTTP_DEFLATE_LEVEL_MIN);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_MAX"), PHP_HTTP_DEFLATE_LEVEL_MAX);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_DEF"), PHP_HTTP_DEFLATE_STRATEGY_DEF);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_FILT"), PHP_HTTP_DEFLATE_STRATEGY_FILT);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_HUFF"), PHP_HTTP_DEFLATE_STRATEGY_HUFF);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_RLE"), PHP_HTTP_DEFLATE_STRATEGY_RLE);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_FIXED"), PHP_HTTP_DEFLATE_STRATEGY_FIXED);
-
-       memset(&ce, 0, sizeof(ce));
-       INIT_NS_CLASS_ENTRY(ce, "http\\Encoding\\Stream", "Inflate", php_http_inflate_stream_methods);
-       php_http_inflate_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_encoding_stream_class_entry);
-       php_http_inflate_stream_class_entry->create_object = php_http_encoding_stream_object_new;
-
        memset(&ce, 0, sizeof(ce));
        INIT_NS_CLASS_ENTRY(ce, "http\\Encoding\\Stream", "Dechunk", php_http_dechunk_stream_methods);
        php_http_dechunk_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_encoding_stream_class_entry);
@@ -1229,7 +709,6 @@ PHP_MINIT_FUNCTION(http_encoding)
        return SUCCESS;
 }
 
-
 /*
  * Local variables:
  * tab-width: 4
index a924eaf..3b59829 100644 (file)
 #ifndef PHP_HTTP_ENCODING_H
 #define PHP_HTTP_ENCODING_H
 
-#include <zlib.h>
-
 extern PHP_MINIT_FUNCTION(http_encoding);
-extern PHP_RINIT_FUNCTION(http_encoding);
-extern PHP_RSHUTDOWN_FUNCTION(http_encoding);
-
-#define PHP_HTTP_INFLATE_ROUNDS 100
-
-#define PHP_HTTP_DEFLATE_BUFFER_SIZE_GUESS(S) \
-       (((size_t) ((double) S * (double) 1.015)) + 10 + 8 + 4 + 1)
-#define PHP_HTTP_INFLATE_BUFFER_SIZE_GUESS(S) \
-       (((S) + 1) << 3)
-#define PHP_HTTP_INFLATE_BUFFER_SIZE_ALIGN(S) \
-       ((S) += (S) >> (3))
-
-#define PHP_HTTP_DEFLATE_BUFFER_SIZE           0x8000
-#define PHP_HTTP_INFLATE_BUFFER_SIZE           0x1000
-
-#define PHP_HTTP_DEFLATE_LEVEL_DEF                     0x00000000
-#define PHP_HTTP_DEFLATE_LEVEL_MIN                     0x00000001
-#define PHP_HTTP_DEFLATE_LEVEL_MAX                     0x00000009
-#define PHP_HTTP_DEFLATE_TYPE_ZLIB                     0x00000000
-#define PHP_HTTP_DEFLATE_TYPE_GZIP                     0x00000010
-#define PHP_HTTP_DEFLATE_TYPE_RAW                      0x00000020
-#define PHP_HTTP_DEFLATE_STRATEGY_DEF          0x00000000
-#define PHP_HTTP_DEFLATE_STRATEGY_FILT         0x00000100
-#define PHP_HTTP_DEFLATE_STRATEGY_HUFF         0x00000200
-#define PHP_HTTP_DEFLATE_STRATEGY_RLE          0x00000300
-#define PHP_HTTP_DEFLATE_STRATEGY_FIXED                0x00000400
-
-#define PHP_HTTP_DEFLATE_LEVEL_SET(flags, level) \
-       switch (flags & 0xf) \
-       { \
-               default: \
-                       if ((flags & 0xf) < 10) { \
-                               level = flags & 0xf; \
-                               break; \
-                       } \
-               case PHP_HTTP_DEFLATE_LEVEL_DEF: \
-                       level = Z_DEFAULT_COMPRESSION; \
-               break; \
-       }
-       
-#define PHP_HTTP_DEFLATE_WBITS_SET(flags, wbits) \
-       switch (flags & 0xf0) \
-       { \
-               case PHP_HTTP_DEFLATE_TYPE_GZIP: \
-                       wbits = PHP_HTTP_WINDOW_BITS_GZIP; \
-               break; \
-               case PHP_HTTP_DEFLATE_TYPE_RAW: \
-                       wbits = PHP_HTTP_WINDOW_BITS_RAW; \
-               break; \
-               default: \
-                       wbits = PHP_HTTP_WINDOW_BITS_ZLIB; \
-               break; \
-       }
-
-#define PHP_HTTP_INFLATE_WBITS_SET(flags, wbits) \
-       if (flags & PHP_HTTP_INFLATE_TYPE_RAW) { \
-               wbits = PHP_HTTP_WINDOW_BITS_RAW; \
-} else { \
-               wbits = PHP_HTTP_WINDOW_BITS_ANY; \
-}
-
-#define PHP_HTTP_DEFLATE_STRATEGY_SET(flags, strategy) \
-       switch (flags & 0xf00) \
-       { \
-               case PHP_HTTP_DEFLATE_STRATEGY_FILT: \
-                       strategy = Z_FILTERED; \
-               break; \
-               case PHP_HTTP_DEFLATE_STRATEGY_HUFF: \
-                       strategy = Z_HUFFMAN_ONLY; \
-               break; \
-               case PHP_HTTP_DEFLATE_STRATEGY_RLE: \
-                       strategy = Z_RLE; \
-               break; \
-               case PHP_HTTP_DEFLATE_STRATEGY_FIXED: \
-                       strategy = Z_FIXED; \
-               break; \
-               default: \
-                       strategy = Z_DEFAULT_STRATEGY; \
-               break; \
-       }
-
-#define PHP_HTTP_WINDOW_BITS_ZLIB      0x0000000f
-#define PHP_HTTP_WINDOW_BITS_GZIP      0x0000001f
-#define PHP_HTTP_WINDOW_BITS_ANY       0x0000002f
-#define PHP_HTTP_WINDOW_BITS_RAW       -0x000000f
-
-#ifndef Z_FIXED
-/* Z_FIXED does not exist prior 1.2.2.2 */
-#      define Z_FIXED 0
-#endif
 
-#define PHP_HTTP_INFLATE_TYPE_ZLIB                     0x00000000
-#define PHP_HTTP_INFLATE_TYPE_GZIP                     0x00000000
-#define PHP_HTTP_INFLATE_TYPE_RAW                      0x00000001
+#define PHP_HTTP_ENCODING_STREAM_PERSISTENT    0x01000000
+#define PHP_HTTP_ENCODING_STREAM_DIRTY         0x02000000
 
 #define PHP_HTTP_ENCODING_STREAM_FLUSH_NONE    0x00000000
 #define PHP_HTTP_ENCODING_STREAM_FLUSH_SYNC 0x00100000
 #define PHP_HTTP_ENCODING_STREAM_FLUSH_FULL 0x00200000
 
-#define PHP_HTTP_ENCODING_STREAM_FLUSH_FLAG(f) \
-       (((f) & PHP_HTTP_ENCODING_STREAM_FLUSH_FULL) ? Z_FULL_FLUSH : \
-       (((f) & PHP_HTTP_ENCODING_STREAM_FLUSH_SYNC) ? Z_SYNC_FLUSH : Z_NO_FLUSH))
-
-#define PHP_HTTP_ENCODING_STREAM_PERSISTENT    0x01000000
+#define PHP_HTTP_ENCODING_STREAM_FLUSH_FLAG(flags, full, sync, none) \
+       (((flags) & PHP_HTTP_ENCODING_STREAM_FLUSH_FULL) ? (full) : \
+       (((flags) & PHP_HTTP_ENCODING_STREAM_FLUSH_SYNC) ? (sync) : (none)))
 
 typedef struct php_http_encoding_stream php_http_encoding_stream_t;
 
@@ -147,9 +52,6 @@ struct php_http_encoding_stream {
        php_http_encoding_stream_ops_t *ops;
 };
 
-PHP_HTTP_API php_http_encoding_stream_ops_t *php_http_encoding_stream_get_deflate_ops(void);
-PHP_HTTP_API php_http_encoding_stream_ops_t *php_http_encoding_stream_get_inflate_ops(void);
-PHP_HTTP_API php_http_encoding_stream_ops_t *php_http_encoding_stream_get_dechunk_ops(void);
 
 PHP_HTTP_API php_http_encoding_stream_t *php_http_encoding_stream_init(php_http_encoding_stream_t *s, php_http_encoding_stream_ops_t *ops, unsigned flags);
 PHP_HTTP_API php_http_encoding_stream_t *php_http_encoding_stream_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to);
@@ -161,9 +63,9 @@ PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_stream_finish(php_http_encoding_
 PHP_HTTP_API void php_http_encoding_stream_dtor(php_http_encoding_stream_t *s);
 PHP_HTTP_API void php_http_encoding_stream_free(php_http_encoding_stream_t **s);
 
+PHP_HTTP_API php_http_encoding_stream_ops_t *php_http_encoding_stream_get_dechunk_ops(void);
 PHP_HTTP_API const char *php_http_encoding_dechunk(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_deflate(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len);
+PHP_HTTP_API zend_class_entry *php_http_get_dechunk_stream_class_entry(void);
 
 typedef struct php_http_encoding_stream_object {
        php_http_encoding_stream_t *stream;
@@ -177,10 +79,6 @@ php_http_encoding_stream_object_t *php_http_encoding_stream_object_new_ex(zend_c
 zend_object *php_http_encoding_stream_object_clone(zval *object);
 void php_http_encoding_stream_object_free(zend_object *object);
 
-PHP_HTTP_API zend_class_entry *php_http_get_deflate_stream_class_entry(void);
-PHP_HTTP_API zend_class_entry *php_http_get_inflate_stream_class_entry(void);
-PHP_HTTP_API zend_class_entry *php_http_get_dechunk_stream_class_entry(void);
-
 #endif
 
 /*
diff --git a/src/php_http_encoding_brotli.c b/src/php_http_encoding_brotli.c
new file mode 100644 (file)
index 0000000..dbea978
--- /dev/null
@@ -0,0 +1,444 @@
+/*
+    +--------------------------------------------------------------------+
+    | PECL :: http                                                       |
+    +--------------------------------------------------------------------+
+    | Redistribution and use in source and binary forms, with or without |
+    | modification, are permitted provided that the conditions mentioned |
+    | in the accompanying LICENSE file are met.                          |
+    +--------------------------------------------------------------------+
+    | Copyright (c) 2004-2014, Michael Wallner <mike@php.net>            |
+    +--------------------------------------------------------------------+
+*/
+
+#include "php_http_api.h"
+
+#if PHP_HTTP_HAVE_LIBBROTLI
+
+#define PHP_HTTP_DEBROTLI_ROUNDS 100
+#define PHP_HTTP_ENBROTLI_ROUNDS 100
+
+#define PHP_HTTP_ENBROTLI_BUFFER_SIZE_GUESS(S) \
+       BrotliEncoderMaxCompressedSize(S)
+#define PHP_HTTP_DEBROTLI_BUFFER_SIZE_ALIGN(S) \
+       ((S) += (S) >> 3)
+
+#define PHP_HTTP_ENBROTLI_FLUSH_FLAG(flags) \
+       PHP_HTTP_ENCODING_STREAM_FLUSH_FLAG((flags), BROTLI_OPERATION_FLUSH, BROTLI_OPERATION_FLUSH, BROTLI_OPERATION_PROCESS)
+
+#define PHP_HTTP_ENBROTLI_BUFFER_SIZE          0x2000
+#define PHP_HTTP_DEBROTLI_BUFFER_SIZE          0x1000
+
+#define PHP_HTTP_ENBROTLI_LEVEL_SET(flags, level) \
+       level = (((flags) & 0xf) ?: PHP_HTTP_ENBROTLI_LEVEL_DEF)
+#define PHP_HTTP_ENBROTLI_WBITS_SET(flags, wbits) \
+       wbits = ((((flags) >> 4) & 0xff) ?: (PHP_HTTP_ENBROTLI_WBITS_DEF >> 4))
+#define PHP_HTTP_ENBROTLI_MODE_SET(flags, mode) \
+       mode = (((flags) >> 12) & 0xf)
+
+
+static php_http_encoding_stream_t *enbrotli_init(php_http_encoding_stream_t *s)
+{
+       BrotliEncoderState *br;
+       int q, win, mode;
+
+       PHP_HTTP_ENBROTLI_LEVEL_SET(s->flags, q);
+       PHP_HTTP_ENBROTLI_WBITS_SET(s->flags, win);
+       PHP_HTTP_ENBROTLI_MODE_SET(s->flags, mode);
+
+       br = BrotliEncoderCreateInstance(NULL, NULL, NULL);
+       if (EXPECTED(br)) {
+               BrotliEncoderSetParameter(br, BROTLI_PARAM_QUALITY, q);
+               BrotliEncoderSetParameter(br, BROTLI_PARAM_LGWIN, win);
+               BrotliEncoderSetParameter(br, BROTLI_PARAM_MODE, mode);
+
+               s->ctx = br;
+               return s;
+       }
+
+       php_error_docref(NULL, E_WARNING, "Failed to initialize brotli encoding stream");
+       return NULL;
+}
+
+static ZEND_RESULT_CODE enbrotli_update(php_http_encoding_stream_t *s, const char *data, size_t data_len, char **encoded, size_t *encoded_len)
+{
+       php_http_buffer_t out;
+       const unsigned char *in_ptr;
+       size_t in_len;
+       BROTLI_BOOL rc;
+
+       php_http_buffer_init_ex(&out, PHP_HTTP_ENBROTLI_BUFFER_SIZE_GUESS(data_len), 0);
+
+       in_len = data_len;
+       in_ptr = (const unsigned char *) data;
+
+       while (in_len) {
+               size_t out_len = 0;
+
+               rc = BrotliEncoderCompressStream(s->ctx, PHP_HTTP_ENBROTLI_FLUSH_FLAG(s->flags), &in_len, &in_ptr, &out_len, NULL, NULL);
+
+               if (!rc) {
+                       break;
+               }
+
+               if (BrotliEncoderHasMoreOutput(s->ctx)) {
+                       const char *out_str = (const char *) BrotliEncoderTakeOutput(s->ctx, &out_len);
+
+                       php_http_buffer_append(&out, out_str, out_len);
+               }
+       }
+
+       if (rc) {
+               if (out.used) {
+                       php_http_buffer_shrink(&out);
+                       php_http_buffer_fix(&out);
+                       *encoded = out.data;
+                       *encoded_len = out.used;
+               } else {
+                       *encoded = NULL;
+                       *encoded_len = 0;
+                       php_http_buffer_dtor(&out);
+               }
+               return SUCCESS;
+       }
+
+       php_http_buffer_dtor(&out);
+
+       *encoded = NULL;
+       *encoded_len = 0;
+
+       php_error_docref(NULL, E_WARNING, "Failed to update brotli encoding stream");
+       return FAILURE;
+}
+
+static inline ZEND_RESULT_CODE enbrotli_flush_ex(php_http_encoding_stream_t *s, BrotliEncoderOperation op, char **encoded, size_t *encoded_len)
+{
+       php_http_buffer_t out;
+       BROTLI_BOOL rc;
+       int round = 0;
+
+       php_http_buffer_init_ex(&out, PHP_HTTP_ENBROTLI_BUFFER_SIZE, 0);
+
+       do {
+               const unsigned char *empty = NULL;
+               size_t unused = 0, out_len = 0;
+
+               rc = BrotliEncoderCompressStream(s->ctx, op, &unused, &empty, &out_len, NULL, NULL);
+
+               if (!rc) {
+                       break;
+               }
+
+               if (BrotliEncoderHasMoreOutput(s->ctx)) {
+                       const char *out_str = (const char *) BrotliEncoderTakeOutput(s->ctx, &out_len);
+
+                       php_http_buffer_append(&out, out_str, out_len);
+               } else {
+                       if (out.used) {
+                               php_http_buffer_shrink(&out);
+                               php_http_buffer_fix(&out);
+
+                               *encoded = out.data;
+                               *encoded_len = out.used;
+                       } else {
+                               *encoded = NULL;
+                               *encoded_len = 0;
+                       }
+                       return SUCCESS;
+               }
+       } while (++round < PHP_HTTP_ENBROTLI_ROUNDS);
+
+       php_http_buffer_dtor(&out);
+
+       *encoded = NULL;
+       *encoded_len = 0;
+
+       php_error_docref(NULL, E_WARNING, "Failed to flush brotli encoding stream");
+       return FAILURE;
+}
+
+static ZEND_RESULT_CODE enbrotli_flush(php_http_encoding_stream_t *s, char **encoded, size_t *encoded_len)
+{
+       return enbrotli_flush_ex(s, BROTLI_OPERATION_FLUSH, encoded, encoded_len);
+}
+
+static ZEND_RESULT_CODE enbrotli_finish(php_http_encoding_stream_t *s, char **encoded, size_t *encoded_len)
+{
+       ZEND_RESULT_CODE rc;
+
+       do {
+               rc = enbrotli_flush_ex(s, BROTLI_OPERATION_FINISH, encoded, encoded_len);
+       } while (SUCCESS == rc && !BrotliEncoderIsFinished(s->ctx));
+
+       return rc;
+}
+
+static zend_bool enbrotli_done(php_http_encoding_stream_t *s)
+{
+       return !(s->flags & PHP_HTTP_ENCODING_STREAM_DIRTY)
+                       || BrotliEncoderIsFinished(s->ctx);
+}
+
+static void enbrotli_dtor(php_http_encoding_stream_t *s)
+{
+       if (s->ctx) {
+               BrotliEncoderDestroyInstance(s->ctx);
+               s->ctx = NULL;
+       }
+}
+
+static php_http_encoding_stream_t *debrotli_init(php_http_encoding_stream_t *s)
+{
+       BrotliDecoderState *br;
+
+       br = BrotliDecoderCreateInstance(NULL, NULL, NULL);
+       if (br) {
+               s->ctx = br;
+               return s;
+       }
+
+       php_error_docref(NULL, E_WARNING, "Failed to initialize brotli decoding stream");
+       return NULL;
+}
+
+static ZEND_RESULT_CODE debrotli_update(php_http_encoding_stream_t *s, const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len)
+{
+       php_http_buffer_t out;
+       BrotliDecoderResult rc;
+       const unsigned char *in_ptr;
+       size_t in_len;
+
+       in_ptr = (const unsigned char *) encoded;
+       in_len = encoded_len;
+
+       php_http_buffer_init_ex(&out, encoded_len, PHP_HTTP_BUFFER_INIT_PREALLOC);
+
+       while (in_len) {
+               size_t out_len = 0;
+
+               rc = BrotliDecoderDecompressStream(s->ctx, &in_len, &in_ptr, &out_len, NULL, NULL);
+
+               if (BROTLI_DECODER_RESULT_ERROR == rc) {
+                       break;
+               }
+
+               if (BrotliDecoderHasMoreOutput(s->ctx)) {
+                       const char *out_str = (const char *) BrotliDecoderTakeOutput(s->ctx, &out_len);
+
+                       php_http_buffer_append(&out, out_str, out_len);
+               }
+       }
+
+       if (BROTLI_DECODER_RESULT_ERROR != rc) {
+               if (out.used) {
+                       php_http_buffer_shrink(&out);
+                       php_http_buffer_fix(&out);
+                       *decoded = out.data;
+                       *decoded_len = out.used;
+               } else {
+                       php_http_buffer_dtor(&out);
+                       *decoded = NULL;
+                       *decoded_len = 0;
+               }
+               return SUCCESS;
+       }
+
+       php_http_buffer_dtor(&out);
+
+       php_error_docref(NULL, E_WARNING, "Could not brotli decode data: %s", BrotliDecoderErrorString(BrotliDecoderGetErrorCode(s->ctx)));
+       return FAILURE;
+}
+
+static zend_bool debrotli_done(php_http_encoding_stream_t *s)
+{
+       return !BrotliDecoderIsUsed(s->ctx) || BrotliDecoderIsFinished(s->ctx);
+}
+
+static void debrotli_dtor(php_http_encoding_stream_t *s)
+{
+       if (s->ctx) {
+               BrotliDecoderDestroyInstance(s->ctx);
+               s->ctx = NULL;
+       }
+}
+
+static php_http_encoding_stream_ops_t php_http_encoding_enbrotli_ops = {
+       enbrotli_init,
+       NULL,
+       enbrotli_update,
+       enbrotli_flush,
+       enbrotli_done,
+       enbrotli_finish,
+       enbrotli_dtor
+};
+
+php_http_encoding_stream_ops_t *php_http_encoding_stream_get_enbrotli_ops(void)
+{
+       return &php_http_encoding_enbrotli_ops;
+}
+
+static php_http_encoding_stream_ops_t php_http_encoding_debrotli_ops = {
+       debrotli_init,
+       NULL,
+       debrotli_update,
+       NULL,
+       debrotli_done,
+       NULL,
+       debrotli_dtor
+};
+
+php_http_encoding_stream_ops_t *php_http_encoding_stream_get_debrotli_ops(void)
+{
+       return &php_http_encoding_debrotli_ops;
+}
+
+ZEND_RESULT_CODE php_http_encoding_enbrotli(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len)
+{
+       BROTLI_BOOL rc;
+       int q, win, mode;
+
+       *encoded_len = PHP_HTTP_ENBROTLI_BUFFER_SIZE_GUESS(data_len);
+       *encoded = emalloc(*encoded_len + 1);
+
+       PHP_HTTP_ENBROTLI_LEVEL_SET(flags, q);
+       PHP_HTTP_ENBROTLI_WBITS_SET(flags, win);
+       PHP_HTTP_ENBROTLI_MODE_SET(flags, mode);
+
+       rc = BrotliEncoderCompress(q, win, mode, data_len, (const unsigned char *) data, encoded_len, (unsigned char *) *encoded);
+       if (rc) {
+               return SUCCESS;
+       }
+
+       PTR_SET(*encoded, NULL);
+       *encoded_len = 0;
+
+       php_error_docref(NULL, E_WARNING, "Could not brotli encode data");
+       return FAILURE;
+}
+
+ZEND_RESULT_CODE php_http_encoding_debrotli(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len)
+{
+       php_http_encoding_stream_t s = {0};
+       ZEND_RESULT_CODE rc = FAILURE;
+
+       if (debrotli_init(&s)) {
+               rc = debrotli_update(&s, encoded, encoded_len, decoded, decoded_len);
+               debrotli_dtor(&s);
+       }
+
+       return rc;
+}
+
+static zend_class_entry *php_http_enbrotli_stream_class_entry;
+zend_class_entry *php_http_get_enbrotli_stream_class_entry(void)
+{
+       return php_http_enbrotli_stream_class_entry;
+}
+
+static zend_class_entry *php_http_debrotli_stream_class_entry;
+zend_class_entry *php_http_get_debrotli_stream_class_entry(void)
+{
+       return php_http_debrotli_stream_class_entry;
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnbrotliStream_encode, 0, 0, 1)
+       ZEND_ARG_INFO(0, data)
+       ZEND_ARG_INFO(0, flags)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnbrotliStream, encode)
+{
+       char *str;
+       size_t len;
+       zend_long flags = PHP_HTTP_ENBROTLI_MODE_GENERIC | PHP_HTTP_ENBROTLI_WBITS_DEF | PHP_HTTP_ENBROTLI_LEVEL_DEF;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &str, &len, &flags)) {
+               char *enc_str = NULL;
+               size_t enc_len;
+
+               if (SUCCESS == php_http_encoding_enbrotli(flags, str, len, &enc_str, &enc_len)) {
+                       if (enc_str) {
+                               RETURN_STR(php_http_cs2zs(enc_str, enc_len));
+                       } else {
+                               RETURN_EMPTY_STRING();
+                       }
+               }
+       }
+       RETURN_FALSE;
+}
+static zend_function_entry php_http_enbrotli_stream_methods[] = {
+       PHP_ME(HttpEnbrotliStream, encode, ai_HttpEnbrotliStream_encode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       EMPTY_FUNCTION_ENTRY
+};
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpDebrotliStream_decode, 0, 0, 1)
+       ZEND_ARG_INFO(0, data)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpDebrotliStream, decode)
+{
+       char *str;
+       size_t len;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &len)) {
+               char *enc_str = NULL;
+               size_t enc_len;
+
+               if (SUCCESS == php_http_encoding_debrotli(str, len, &enc_str, &enc_len)) {
+                       if (enc_str) {
+                               RETURN_STR(php_http_cs2zs(enc_str, enc_len));
+                       } else {
+                               RETURN_EMPTY_STRING();
+                       }
+               }
+       }
+       RETURN_FALSE;
+}
+
+static zend_function_entry php_http_debrotli_stream_methods[] = {
+       PHP_ME(HttpDebrotliStream, decode, ai_HttpDebrotliStream_decode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       EMPTY_FUNCTION_ENTRY
+};
+
+/* POS */
+const void *BrotliGetDictionary();
+const void *(*php_http_brotli_get_dictionary)();
+
+PHP_MINIT_FUNCTION(http_encoding_brotli)
+{
+       zend_class_entry ce;
+
+       /* force link to libbrotlicommon, because their libraries don't */
+       php_http_brotli_get_dictionary = BrotliGetDictionary();
+
+       memset(&ce, 0, sizeof(ce));
+       INIT_NS_CLASS_ENTRY(ce, "http\\Encoding\\Stream", "Enbrotli", php_http_enbrotli_stream_methods);
+       php_http_enbrotli_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_get_encoding_stream_class_entry());
+       php_http_enbrotli_stream_class_entry->create_object = php_http_encoding_stream_object_new;
+
+       zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("LEVEL_MIN"), PHP_HTTP_ENBROTLI_LEVEL_MIN);
+       zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("LEVEL_DEF"), PHP_HTTP_ENBROTLI_LEVEL_DEF);
+       zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("LEVEL_MAX"), PHP_HTTP_ENBROTLI_LEVEL_MAX);
+       zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("WBITS_MIN"), PHP_HTTP_ENBROTLI_WBITS_MIN);
+       zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("WBITS_DEF"), PHP_HTTP_ENBROTLI_WBITS_DEF);
+       zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("WBITS_MAX"), PHP_HTTP_ENBROTLI_WBITS_MAX);
+       zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("MODE_GENERIC"), PHP_HTTP_ENBROTLI_MODE_GENERIC);
+       zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("MODE_TEXT"), PHP_HTTP_ENBROTLI_MODE_TEXT);
+       zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("MODE_FONT"), PHP_HTTP_ENBROTLI_MODE_FONT);
+
+       memset(&ce, 0, sizeof(ce));
+       INIT_NS_CLASS_ENTRY(ce, "http\\Encoding\\Stream", "Debrotli", php_http_debrotli_stream_methods);
+       php_http_debrotli_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_get_encoding_stream_class_entry());
+       php_http_debrotli_stream_class_entry->create_object = php_http_encoding_stream_object_new;
+
+       return SUCCESS;
+}
+
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
+
diff --git a/src/php_http_encoding_brotli.h b/src/php_http_encoding_brotli.h
new file mode 100644 (file)
index 0000000..504430f
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+    +--------------------------------------------------------------------+
+    | PECL :: http                                                       |
+    +--------------------------------------------------------------------+
+    | Redistribution and use in source and binary forms, with or without |
+    | modification, are permitted provided that the conditions mentioned |
+    | in the accompanying LICENSE file are met.                          |
+    +--------------------------------------------------------------------+
+    | Copyright (c) 2004-2014, Michael Wallner <mike@php.net>            |
+    +--------------------------------------------------------------------+
+*/
+
+#ifndef PHP_HTTP_ENCODING_BROTLI_H
+#define PHP_HTTP_ENCODING_BROTLI_H
+#if PHP_HTTP_HAVE_LIBBROTLI
+
+#include <brotli/decode.h>
+#include <brotli/encode.h>
+
+extern PHP_MINIT_FUNCTION(http_encoding_brotli);
+
+PHP_HTTP_API zend_class_entry *php_http_get_enbrotli_stream_class_entry(void);
+PHP_HTTP_API zend_class_entry *php_http_get_debrotli_stream_class_entry(void);
+
+PHP_HTTP_API php_http_encoding_stream_ops_t *php_http_encoding_stream_get_enbrotli_ops(void);
+PHP_HTTP_API php_http_encoding_stream_ops_t *php_http_encoding_stream_get_debrotli_ops(void);
+
+PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_enbrotli(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_debrotli(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len);
+
+#define PHP_HTTP_ENBROTLI_LEVEL_MIN                    0x00000001
+#define PHP_HTTP_ENBROTLI_LEVEL_DEF                    0x00000004
+#define PHP_HTTP_ENBROTLI_LEVEL_MAX                    0x0000000b
+
+#define PHP_HTTP_ENBROTLI_WBITS_MIN                    0x000000a0
+#define PHP_HTTP_ENBROTLI_WBITS_DEF                    0x00000160
+#define PHP_HTTP_ENBROTLI_WBITS_MAX                    0x00000180
+
+#define PHP_HTTP_ENBROTLI_MODE_GENERIC         0x00000000
+#define PHP_HTTP_ENBROTLI_MODE_TEXT                    0x00001000
+#define PHP_HTTP_ENBROTLI_MODE_FONT                    0x00002000
+
+#endif
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
+
diff --git a/src/php_http_encoding_zlib.c b/src/php_http_encoding_zlib.c
new file mode 100644 (file)
index 0000000..5e19cec
--- /dev/null
@@ -0,0 +1,650 @@
+/*
+    +--------------------------------------------------------------------+
+    | PECL :: http                                                       |
+    +--------------------------------------------------------------------+
+    | Redistribution and use in source and binary forms, with or without |
+    | modification, are permitted provided that the conditions mentioned |
+    | in the accompanying LICENSE file are met.                          |
+    +--------------------------------------------------------------------+
+    | Copyright (c) 2004-2014, Michael Wallner <mike@php.net>            |
+    +--------------------------------------------------------------------+
+*/
+
+#include "php_http_api.h"
+
+#define PHP_HTTP_INFLATE_ROUNDS                                100
+#define PHP_HTTP_INFLATE_BUFFER_SIZE           0x1000
+
+#define PHP_HTTP_DEFLATE_BUFFER_SIZE           0x8000
+
+#define PHP_HTTP_DEFLATE_BUFFER_SIZE_GUESS(S) \
+       (((size_t) ((double) S * (double) 1.015)) + 10 + 8 + 4 + 1)
+
+#define PHP_HTTP_INFLATE_BUFFER_SIZE_GUESS(S) \
+       (((S) + 1) << 3)
+#define PHP_HTTP_INFLATE_BUFFER_SIZE_ALIGN(S) \
+       ((S) += (S) >> (3))
+
+#define PHP_HTTP_INFLATE_FLUSH_FLAG(flags) \
+       PHP_HTTP_ENCODING_STREAM_FLUSH_FLAG((flags), Z_FULL_FLUSH, Z_SYNC_FLUSH, Z_NO_FLUSH)
+#define PHP_HTTP_DEFLATE_FLUSH_FLAG(flags) \
+       PHP_HTTP_ENCODING_STREAM_FLUSH_FLAG((flags), Z_FULL_FLUSH, Z_SYNC_FLUSH, Z_NO_FLUSH)
+
+#define PHP_HTTP_WINDOW_BITS_ZLIB                      0x0000000f
+#define PHP_HTTP_WINDOW_BITS_GZIP                      0x0000001f
+#define PHP_HTTP_WINDOW_BITS_ANY                       0x0000002f
+#define PHP_HTTP_WINDOW_BITS_RAW                       -0x000000f
+
+#define PHP_HTTP_DEFLATE_LEVEL_SET(flags, level) \
+       switch (flags & 0xf) \
+       { \
+               default: \
+                       if ((flags & 0xf) < 10) { \
+                               level = flags & 0xf; \
+                               break; \
+                       } \
+               case PHP_HTTP_DEFLATE_LEVEL_DEF: \
+                       level = Z_DEFAULT_COMPRESSION; \
+               break; \
+       }
+
+#define PHP_HTTP_DEFLATE_WBITS_SET(flags, wbits) \
+       switch (flags & 0xf0) \
+       { \
+               case PHP_HTTP_DEFLATE_TYPE_GZIP: \
+                       wbits = PHP_HTTP_WINDOW_BITS_GZIP; \
+               break; \
+               case PHP_HTTP_DEFLATE_TYPE_RAW: \
+                       wbits = PHP_HTTP_WINDOW_BITS_RAW; \
+               break; \
+               default: \
+                       wbits = PHP_HTTP_WINDOW_BITS_ZLIB; \
+               break; \
+       }
+
+#define PHP_HTTP_INFLATE_WBITS_SET(flags, wbits) \
+       if (flags & PHP_HTTP_INFLATE_TYPE_RAW) { \
+               wbits = PHP_HTTP_WINDOW_BITS_RAW; \
+       } else { \
+                       wbits = PHP_HTTP_WINDOW_BITS_ANY; \
+       }
+
+#define PHP_HTTP_DEFLATE_STRATEGY_SET(flags, strategy) \
+       switch (flags & 0xf00) \
+       { \
+               case PHP_HTTP_DEFLATE_STRATEGY_FILT: \
+                       strategy = Z_FILTERED; \
+               break; \
+               case PHP_HTTP_DEFLATE_STRATEGY_HUFF: \
+                       strategy = Z_HUFFMAN_ONLY; \
+               break; \
+               case PHP_HTTP_DEFLATE_STRATEGY_RLE: \
+                       strategy = Z_RLE; \
+               break; \
+               case PHP_HTTP_DEFLATE_STRATEGY_FIXED: \
+                       strategy = Z_FIXED; \
+               break; \
+               default: \
+                       strategy = Z_DEFAULT_STRATEGY; \
+               break; \
+       }
+
+ZEND_RESULT_CODE php_http_encoding_deflate(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len)
+{
+       int status, level, wbits, strategy;
+       z_stream Z;
+
+       PHP_HTTP_DEFLATE_LEVEL_SET(flags, level);
+       PHP_HTTP_DEFLATE_WBITS_SET(flags, wbits);
+       PHP_HTTP_DEFLATE_STRATEGY_SET(flags, strategy);
+
+       memset(&Z, 0, sizeof(z_stream));
+       *encoded = NULL;
+       *encoded_len = 0;
+
+       status = deflateInit2(&Z, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy);
+       if (EXPECTED(Z_OK == status)) {
+               *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len);
+               *encoded = emalloc(*encoded_len);
+
+               Z.next_in = (Bytef *) data;
+               Z.next_out = (Bytef *) *encoded;
+               Z.avail_in = data_len;
+               Z.avail_out = *encoded_len;
+
+               status = deflate(&Z, Z_FINISH);
+               deflateEnd(&Z);
+
+               if (EXPECTED(Z_STREAM_END == status)) {
+                       /* size buffer down to actual length */
+                       *encoded = erealloc(*encoded, Z.total_out + 1);
+                       (*encoded)[*encoded_len = Z.total_out] = '\0';
+                       return SUCCESS;
+               } else {
+                       PTR_SET(*encoded, NULL);
+                       *encoded_len = 0;
+               }
+       }
+
+       php_error_docref(NULL, E_WARNING, "Could not deflate data: %s", zError(status));
+       return FAILURE;
+}
+
+static php_http_encoding_stream_t *deflate_init(php_http_encoding_stream_t *s)
+{
+       int status, level, wbits, strategy, p = (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT);
+       z_streamp ctx = pecalloc(1, sizeof(z_stream), p);
+
+       PHP_HTTP_DEFLATE_LEVEL_SET(s->flags, level);
+       PHP_HTTP_DEFLATE_WBITS_SET(s->flags, wbits);
+       PHP_HTTP_DEFLATE_STRATEGY_SET(s->flags, strategy);
+
+       if (EXPECTED(Z_OK == (status = deflateInit2(ctx, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy)))) {
+               if (EXPECTED(ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) {
+                       s->ctx = ctx;
+                       return s;
+               }
+               deflateEnd(ctx);
+               status = Z_MEM_ERROR;
+       }
+       pefree(ctx, p);
+       php_error_docref(NULL, E_WARNING, "Failed to initialize deflate encoding stream: %s", zError(status));
+       return NULL;
+}
+
+static php_http_encoding_stream_t *deflate_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to)
+{
+       int status, p = to->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT;
+       z_streamp from_ctx = from->ctx, to_ctx = pecalloc(1, sizeof(*to_ctx), p);
+
+       if (Z_OK == (status = deflateCopy(to_ctx, from_ctx))) {
+               if ((to_ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) {
+                       php_http_buffer_append(to_ctx->opaque, PHP_HTTP_BUFFER(from_ctx->opaque)->data, PHP_HTTP_BUFFER(from_ctx->opaque)->used);
+                       to->ctx = to_ctx;
+                       return to;
+               }
+               deflateEnd(to_ctx);
+               status = Z_MEM_ERROR;
+       }
+       php_error_docref(NULL, E_WARNING, "Failed to copy deflate encoding stream: %s", zError(status));
+       return NULL;
+}
+
+static ZEND_RESULT_CODE deflate_update(php_http_encoding_stream_t *s, const char *data, size_t data_len, char **encoded, size_t *encoded_len)
+{
+       int status;
+       z_streamp ctx = s->ctx;
+
+       /* append input to our buffer */
+       php_http_buffer_append(PHP_HTTP_BUFFER(ctx->opaque), data, data_len);
+
+       ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data;
+       ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used;
+
+       /* deflate */
+       *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len);
+       *encoded = emalloc(*encoded_len);
+       ctx->avail_out = *encoded_len;
+       ctx->next_out = (Bytef *) *encoded;
+
+       switch (status = deflate(ctx, PHP_HTTP_DEFLATE_FLUSH_FLAG(s->flags))) {
+               case Z_OK:
+               case Z_STREAM_END:
+                       /* cut processed chunk off the buffer */
+                       if (ctx->avail_in) {
+                               php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in);
+                       } else {
+                               php_http_buffer_reset(PHP_HTTP_BUFFER(ctx->opaque));
+                       }
+
+                       /* size buffer down to actual size */
+                       *encoded_len -= ctx->avail_out;
+                       *encoded = erealloc(*encoded, *encoded_len + 1);
+                       (*encoded)[*encoded_len] = '\0';
+                       return SUCCESS;
+       }
+
+       PTR_SET(*encoded, NULL);
+       *encoded_len = 0;
+       php_error_docref(NULL, E_WARNING, "Failed to update deflate stream: %s", zError(status));
+       return FAILURE;
+}
+
+static ZEND_RESULT_CODE deflate_flush(php_http_encoding_stream_t *s, char **encoded, size_t *encoded_len)
+{
+       int status;
+       z_streamp ctx = s->ctx;
+
+       *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE;
+       *encoded = emalloc(*encoded_len);
+
+       ctx->avail_in = 0;
+       ctx->next_in = NULL;
+       ctx->avail_out = *encoded_len;
+       ctx->next_out = (Bytef *) *encoded;
+
+       status = deflate(ctx, Z_FULL_FLUSH);
+       if (EXPECTED(Z_OK == status || Z_STREAM_END == status)) {
+               *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE - ctx->avail_out;
+               *encoded = erealloc(*encoded, *encoded_len + 1);
+               (*encoded)[*encoded_len] = '\0';
+               return SUCCESS;
+       }
+
+       PTR_SET(*encoded, NULL);
+       *encoded_len = 0;
+       php_error_docref(NULL, E_WARNING, "Failed to flush deflate stream: %s", zError(status));
+       return FAILURE;
+}
+
+static ZEND_RESULT_CODE deflate_finish(php_http_encoding_stream_t *s, char **encoded, size_t *encoded_len)
+{
+       int status;
+       z_streamp ctx = s->ctx;
+
+       *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE;
+       *encoded = emalloc(*encoded_len);
+
+       /* deflate remaining input */
+       ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data;
+       ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used;
+
+       ctx->avail_out = *encoded_len;
+       ctx->next_out = (Bytef *) *encoded;
+
+       do {
+               status = deflate(ctx, Z_FINISH);
+       } while (Z_OK == status);
+
+       if (EXPECTED(Z_STREAM_END == status)) {
+               /* cut processed input off */
+               php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in);
+
+               /* size down */
+               *encoded_len -= ctx->avail_out;
+               *encoded = erealloc(*encoded, *encoded_len + 1);
+               (*encoded)[*encoded_len] = '\0';
+               return SUCCESS;
+       }
+
+       PTR_SET(*encoded, NULL);
+       *encoded_len = 0;
+       php_error_docref(NULL, E_WARNING, "Failed to finish deflate stream: %s", zError(status));
+       return FAILURE;
+}
+
+static zend_bool deflate_done(php_http_encoding_stream_t *s)
+{
+       z_streamp ctx = s->ctx;
+       return !ctx->avail_in && !PHP_HTTP_BUFFER(ctx->opaque)->used;
+}
+
+static void deflate_dtor(php_http_encoding_stream_t *s)
+{
+       if (EXPECTED(s->ctx)) {
+               z_streamp ctx = s->ctx;
+
+               if (EXPECTED(ctx->opaque)) {
+                       php_http_buffer_free((php_http_buffer_t **) &ctx->opaque);
+               }
+               deflateEnd(ctx);
+               pefree(ctx, (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT));
+               s->ctx = NULL;
+       }
+}
+
+static inline int php_http_inflate_rounds(z_stream *Z, int flush, char **buf, size_t *len)
+{
+       int status = 0, round = 0;
+       php_http_buffer_t buffer;
+
+       *buf = NULL;
+       *len = 0;
+
+       php_http_buffer_init_ex(&buffer, Z->avail_in, PHP_HTTP_BUFFER_INIT_PREALLOC);
+
+       do {
+               if (UNEXPECTED(PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize_ex(&buffer, buffer.size, 0, 1))) {
+                       status = Z_MEM_ERROR;
+               } else {
+                       Z->avail_out = buffer.free;
+                       Z->next_out = (Bytef *) buffer.data + buffer.used;
+#if 0
+                       fprintf(stderr, "\n%3d: %3d PRIOR: size=%7lu,\tfree=%7lu,\tused=%7lu,\tavail_in=%7lu,\tavail_out=%7lu\n", round, status, buffer.size, buffer.free, buffer.used, Z->avail_in, Z->avail_out);
+#endif
+                       status = inflate(Z, flush);
+                       php_http_buffer_account(&buffer, buffer.free - Z->avail_out);
+#if 0
+                       fprintf(stderr, "%3d: %3d AFTER: size=%7lu,\tfree=%7lu,\tused=%7lu,\tavail_in=%7lu,\tavail_out=%7lu\n", round, status, buffer.size, buffer.free, buffer.used, Z->avail_in, Z->avail_out);
+#endif
+                       PHP_HTTP_INFLATE_BUFFER_SIZE_ALIGN(buffer.size);
+               }
+       } while ((Z_BUF_ERROR == status || (Z_OK == status && Z->avail_in)) && ++round < PHP_HTTP_INFLATE_ROUNDS);
+
+       if (EXPECTED(status == Z_OK || status == Z_STREAM_END)) {
+               php_http_buffer_shrink(&buffer);
+               php_http_buffer_fix(&buffer);
+               *buf = buffer.data;
+               *len = buffer.used;
+       } else {
+               php_http_buffer_dtor(&buffer);
+       }
+
+       return status;
+}
+
+ZEND_RESULT_CODE php_http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len)
+{
+       z_stream Z;
+       int status, wbits = PHP_HTTP_WINDOW_BITS_ANY;
+
+       memset(&Z, 0, sizeof(z_stream));
+
+retry_raw_inflate:
+       status = inflateInit2(&Z, wbits);
+       if (EXPECTED(Z_OK == status)) {
+               Z.next_in = (Bytef *) data;
+               Z.avail_in = data_len + 1; /* include the terminating NULL, see #61287 */
+
+               switch (status = php_http_inflate_rounds(&Z, Z_NO_FLUSH, decoded, decoded_len)) {
+                       case Z_STREAM_END:
+                               inflateEnd(&Z);
+                               return SUCCESS;
+
+                       case Z_OK:
+                               status = Z_DATA_ERROR;
+                               break;
+
+                       case Z_DATA_ERROR:
+                               /* raw deflated data? */
+                               if (PHP_HTTP_WINDOW_BITS_ANY == wbits) {
+                                       inflateEnd(&Z);
+                                       wbits = PHP_HTTP_WINDOW_BITS_RAW;
+                                       goto retry_raw_inflate;
+                               }
+                               break;
+               }
+               inflateEnd(&Z);
+
+               if (*decoded_len && *decoded) {
+                       efree(*decoded);
+               }
+       }
+
+       php_error_docref(NULL, E_WARNING, "Could not inflate data: %s", zError(status));
+       return FAILURE;
+}
+
+static php_http_encoding_stream_t *inflate_init(php_http_encoding_stream_t *s)
+{
+       int status, wbits, p = (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT);
+       z_streamp ctx = pecalloc(1, sizeof(z_stream), p);
+
+       PHP_HTTP_INFLATE_WBITS_SET(s->flags, wbits);
+
+       if (EXPECTED(Z_OK == (status = inflateInit2(ctx, wbits)))) {
+               if ((ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) {
+                       s->ctx = ctx;
+                       return s;
+               }
+               inflateEnd(ctx);
+               status = Z_MEM_ERROR;
+       }
+       pefree(ctx, p);
+       php_error_docref(NULL, E_WARNING, "Failed to initialize inflate stream: %s", zError(status));
+       return NULL;
+}
+
+static php_http_encoding_stream_t *inflate_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to)
+{
+       int status, p = from->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT;
+       z_streamp from_ctx = from->ctx, to_ctx = pecalloc(1, sizeof(*to_ctx), p);
+
+       if (Z_OK == (status = inflateCopy(to_ctx, from_ctx))) {
+               if ((to_ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) {
+                       php_http_buffer_append(to_ctx->opaque, PHP_HTTP_BUFFER(from_ctx->opaque)->data, PHP_HTTP_BUFFER(from_ctx->opaque)->used);
+                       to->ctx = to_ctx;
+                       return to;
+               }
+               inflateEnd(to_ctx);
+               status = Z_MEM_ERROR;
+       }
+       php_error_docref(NULL, E_WARNING, "Failed to copy inflate encoding stream: %s", zError(status));
+       return NULL;
+}
+
+static ZEND_RESULT_CODE inflate_update(php_http_encoding_stream_t *s, const char *data, size_t data_len, char **decoded, size_t *decoded_len)
+{
+       int status;
+       z_streamp ctx = s->ctx;
+
+       /* append input to buffer */
+       php_http_buffer_append(PHP_HTTP_BUFFER(ctx->opaque), data, data_len);
+
+retry_raw_inflate:
+       ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data;
+       ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used;
+
+       switch (status = php_http_inflate_rounds(ctx, PHP_HTTP_INFLATE_FLUSH_FLAG(s->flags), decoded, decoded_len)) {
+               case Z_OK:
+               case Z_STREAM_END:
+                       /* cut off */
+                       if (ctx->avail_in) {
+                               php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in);
+                       } else {
+                               php_http_buffer_reset(PHP_HTTP_BUFFER(ctx->opaque));
+                       }
+                       return SUCCESS;
+
+               case Z_DATA_ERROR:
+                       /* raw deflated data ? */
+                       if (!(s->flags & PHP_HTTP_INFLATE_TYPE_RAW) && !ctx->total_out) {
+                               inflateEnd(ctx);
+                               s->flags |= PHP_HTTP_INFLATE_TYPE_RAW;
+                               inflateInit2(ctx, PHP_HTTP_WINDOW_BITS_RAW);
+                               goto retry_raw_inflate;
+                       }
+                       break;
+       }
+
+       php_error_docref(NULL, E_WARNING, "Failed to update inflate stream: %s", zError(status));
+       return FAILURE;
+}
+
+static ZEND_RESULT_CODE inflate_finish(php_http_encoding_stream_t *s, char **decoded, size_t *decoded_len)
+{
+       int status;
+       z_streamp ctx = s->ctx;
+
+       if (!PHP_HTTP_BUFFER(ctx->opaque)->used) {
+               *decoded = NULL;
+               *decoded_len = 0;
+               return SUCCESS;
+       }
+
+       *decoded_len = (PHP_HTTP_BUFFER(ctx->opaque)->used + 1) * PHP_HTTP_INFLATE_ROUNDS;
+       *decoded = emalloc(*decoded_len);
+
+       /* inflate remaining input */
+       ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data;
+       ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used;
+
+       ctx->avail_out = *decoded_len;
+       ctx->next_out = (Bytef *) *decoded;
+
+       if (Z_STREAM_END == (status = inflate(ctx, Z_FINISH))) {
+               /* cut processed input off */
+               php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in);
+
+               /* size down */
+               *decoded_len -= ctx->avail_out;
+               *decoded = erealloc(*decoded, *decoded_len + 1);
+               (*decoded)[*decoded_len] = '\0';
+               return SUCCESS;
+       }
+
+       PTR_SET(*decoded, NULL);
+       *decoded_len = 0;
+       php_error_docref(NULL, E_WARNING, "Failed to finish inflate stream: %s", zError(status));
+       return FAILURE;
+}
+
+static zend_bool inflate_done(php_http_encoding_stream_t *s)
+{
+       z_streamp ctx = s->ctx;
+       return !ctx->avail_in && !PHP_HTTP_BUFFER(ctx->opaque)->used;
+}
+
+static void inflate_dtor(php_http_encoding_stream_t *s)
+{
+       if (EXPECTED(s->ctx)) {
+               z_streamp ctx = s->ctx;
+
+               if (EXPECTED(ctx->opaque)) {
+                       php_http_buffer_free((php_http_buffer_t **) &ctx->opaque);
+               }
+               inflateEnd(ctx);
+               pefree(ctx, (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT));
+               s->ctx = NULL;
+       }
+}
+
+static php_http_encoding_stream_ops_t php_http_encoding_deflate_ops = {
+       deflate_init,
+       deflate_copy,
+       deflate_update,
+       deflate_flush,
+       deflate_done,
+       deflate_finish,
+       deflate_dtor
+};
+
+php_http_encoding_stream_ops_t *php_http_encoding_stream_get_deflate_ops(void)
+{
+       return &php_http_encoding_deflate_ops;
+}
+
+static php_http_encoding_stream_ops_t php_http_encoding_inflate_ops = {
+       inflate_init,
+       inflate_copy,
+       inflate_update,
+       NULL,
+       inflate_done,
+       inflate_finish,
+       inflate_dtor
+};
+
+php_http_encoding_stream_ops_t *php_http_encoding_stream_get_inflate_ops(void)
+{
+       return &php_http_encoding_inflate_ops;
+}
+
+static zend_class_entry *php_http_deflate_stream_class_entry;
+zend_class_entry *php_http_get_deflate_stream_class_entry(void)
+{
+       return php_http_deflate_stream_class_entry;
+}
+
+static zend_class_entry *php_http_inflate_stream_class_entry;
+zend_class_entry *php_http_get_inflate_stream_class_entry(void)
+{
+       return php_http_inflate_stream_class_entry;
+}
+
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpDeflateStream_encode, 0, 0, 1)
+       ZEND_ARG_INFO(0, data)
+       ZEND_ARG_INFO(0, flags)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpDeflateStream, encode)
+{
+       char *str;
+       size_t len;
+       zend_long flags = 0;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &str, &len, &flags)) {
+               char *enc_str = NULL;
+               size_t enc_len;
+
+               if (SUCCESS == php_http_encoding_deflate(flags, str, len, &enc_str, &enc_len)) {
+                       if (enc_str) {
+                               RETURN_STR(php_http_cs2zs(enc_str, enc_len));
+                       } else {
+                               RETURN_EMPTY_STRING();
+                       }
+               }
+       }
+       RETURN_FALSE;
+}
+
+static zend_function_entry php_http_deflate_stream_methods[] = {
+       PHP_ME(HttpDeflateStream, encode, ai_HttpDeflateStream_encode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       EMPTY_FUNCTION_ENTRY
+};
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpInflateStream_decode, 0, 0, 1)
+       ZEND_ARG_INFO(0, data)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpInflateStream, decode)
+{
+       char *str;
+       size_t len;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &len)) {
+               char *enc_str = NULL;
+               size_t enc_len;
+
+               if (SUCCESS == php_http_encoding_inflate(str, len, &enc_str, &enc_len)) {
+                       if (enc_str) {
+                               RETURN_STR(php_http_cs2zs(enc_str, enc_len));
+                       } else {
+                               RETURN_EMPTY_STRING();
+                       }
+               }
+       }
+       RETURN_FALSE;
+}
+
+static zend_function_entry php_http_inflate_stream_methods[] = {
+       PHP_ME(HttpInflateStream, decode, ai_HttpInflateStream_decode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       EMPTY_FUNCTION_ENTRY
+};
+
+PHP_MINIT_FUNCTION(http_encoding_zlib)
+{
+       zend_class_entry ce;
+
+       memset(&ce, 0, sizeof(ce));
+       INIT_NS_CLASS_ENTRY(ce, "http\\Encoding\\Stream", "Deflate", php_http_deflate_stream_methods);
+       php_http_deflate_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_get_encoding_stream_class_entry());
+       php_http_deflate_stream_class_entry->create_object = php_http_encoding_stream_object_new;
+
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_GZIP"), PHP_HTTP_DEFLATE_TYPE_GZIP);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_ZLIB"), PHP_HTTP_DEFLATE_TYPE_ZLIB);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_RAW"), PHP_HTTP_DEFLATE_TYPE_RAW);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_DEF"), PHP_HTTP_DEFLATE_LEVEL_DEF);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_MIN"), PHP_HTTP_DEFLATE_LEVEL_MIN);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_MAX"), PHP_HTTP_DEFLATE_LEVEL_MAX);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_DEF"), PHP_HTTP_DEFLATE_STRATEGY_DEF);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_FILT"), PHP_HTTP_DEFLATE_STRATEGY_FILT);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_HUFF"), PHP_HTTP_DEFLATE_STRATEGY_HUFF);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_RLE"), PHP_HTTP_DEFLATE_STRATEGY_RLE);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_FIXED"), PHP_HTTP_DEFLATE_STRATEGY_FIXED);
+
+       memset(&ce, 0, sizeof(ce));
+       INIT_NS_CLASS_ENTRY(ce, "http\\Encoding\\Stream", "Inflate", php_http_inflate_stream_methods);
+       php_http_inflate_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_get_encoding_stream_class_entry());
+       php_http_inflate_stream_class_entry->create_object = php_http_encoding_stream_object_new;
+
+       return SUCCESS;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
+
diff --git a/src/php_http_encoding_zlib.h b/src/php_http_encoding_zlib.h
new file mode 100644 (file)
index 0000000..9d17560
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+    +--------------------------------------------------------------------+
+    | PECL :: http                                                       |
+    +--------------------------------------------------------------------+
+    | Redistribution and use in source and binary forms, with or without |
+    | modification, are permitted provided that the conditions mentioned |
+    | in the accompanying LICENSE file are met.                          |
+    +--------------------------------------------------------------------+
+    | Copyright (c) 2004-2014, Michael Wallner <mike@php.net>            |
+    +--------------------------------------------------------------------+
+*/
+
+#ifndef PHP_HTTP_ENCODING_ZLIB_H
+#define PHP_HTTP_ENCODING_ZLIB_H
+
+#include "php_http_encoding.h"
+
+#include <zlib.h>
+
+#ifndef Z_FIXED
+/* Z_FIXED does not exist prior 1.2.2.2 */
+#      define Z_FIXED 0
+#endif
+
+extern PHP_MINIT_FUNCTION(http_encoding_zlib);
+
+zend_class_entry *php_http_get_deflate_stream_class_entry(void);
+zend_class_entry *php_http_get_inflate_stream_class_entry(void);
+
+PHP_HTTP_API php_http_encoding_stream_ops_t *php_http_encoding_stream_get_deflate_ops(void);
+PHP_HTTP_API php_http_encoding_stream_ops_t *php_http_encoding_stream_get_inflate_ops(void);
+
+PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_deflate(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len);
+
+#define PHP_HTTP_DEFLATE_LEVEL_DEF                     0x00000000
+#define PHP_HTTP_DEFLATE_LEVEL_MIN                     0x00000001
+#define PHP_HTTP_DEFLATE_LEVEL_MAX                     0x00000009
+#define PHP_HTTP_DEFLATE_TYPE_ZLIB                     0x00000000
+#define PHP_HTTP_DEFLATE_TYPE_GZIP                     0x00000010
+#define PHP_HTTP_DEFLATE_TYPE_RAW                      0x00000020
+#define PHP_HTTP_DEFLATE_STRATEGY_DEF          0x00000000
+#define PHP_HTTP_DEFLATE_STRATEGY_FILT         0x00000100
+#define PHP_HTTP_DEFLATE_STRATEGY_HUFF         0x00000200
+#define PHP_HTTP_DEFLATE_STRATEGY_RLE          0x00000300
+#define PHP_HTTP_DEFLATE_STRATEGY_FIXED                0x00000400
+
+#define PHP_HTTP_INFLATE_TYPE_ZLIB                     0x00000000
+#define PHP_HTTP_INFLATE_TYPE_GZIP                     0x00000000
+#define PHP_HTTP_INFLATE_TYPE_RAW                      0x00000001
+
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
+
index b5454ed..7c6eea8 100644 (file)
@@ -245,7 +245,7 @@ static PHP_METHOD(HttpEnvRequest, getFiles)
 }
 
 static zend_function_entry php_http_env_request_methods[] = {
-       PHP_ME(HttpEnvRequest, __construct,  ai_HttpEnvRequest___construct,  ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+       PHP_ME(HttpEnvRequest, __construct,  ai_HttpEnvRequest___construct,  ZEND_ACC_PUBLIC)
        PHP_ME(HttpEnvRequest, getForm,      ai_HttpEnvRequest_getForm,      ZEND_ACC_PUBLIC)
        PHP_ME(HttpEnvRequest, getQuery,     ai_HttpEnvRequest_getQuery,     ZEND_ACC_PUBLIC)
        PHP_ME(HttpEnvRequest, getCookie,    ai_HttpEnvRequest_getCookie,    ZEND_ACC_PUBLIC)
index 3a5a8e8..f5d2da9 100644 (file)
@@ -15,7 +15,7 @@
 static void set_option(zval *options, const char *name_str, size_t name_len, int type, void *value_ptr, size_t value_len)
 {
        if (Z_TYPE_P(options) == IS_OBJECT) {
-               if (value_ptr) {
+               if (EXPECTED(value_ptr)) {
                        switch (type) {
                                case IS_DOUBLE:
                                        zend_update_property_double(Z_OBJCE_P(options), options, name_str, name_len, *(double *)value_ptr);
@@ -36,7 +36,7 @@ static void set_option(zval *options, const char *name_str, size_t name_len, int
                }
        } else {
                convert_to_array(options);
-               if (value_ptr) {
+               if (EXPECTED(value_ptr)) {
                        switch (type) {
                                case IS_DOUBLE:
                                        add_assoc_double_ex(options, name_str, name_len, *(double *)value_ptr);
@@ -64,9 +64,9 @@ static zval *get_option(zval *options, const char *name_str, size_t name_len, zv
 {
        zval *val = NULL;
 
-       if (Z_TYPE_P(options) == IS_OBJECT) {
+       if (EXPECTED(Z_TYPE_P(options) == IS_OBJECT)) {
                val = zend_read_property(Z_OBJCE_P(options), options, name_str, name_len, 0, tmp);
-       } else if (Z_TYPE_P(options) == IS_ARRAY) {
+       } else if (EXPECTED(Z_TYPE_P(options) == IS_ARRAY)) {
                val = zend_symtable_str_find(Z_ARRVAL_P(options), name_str, name_len);
        } else {
                abort();
@@ -81,7 +81,7 @@ static php_http_message_body_t *get_body(zval *options)
        zval zbody_tmp, *zbody;
        php_http_message_body_t *body = NULL;
 
-       if ((zbody = get_option(options, ZEND_STRL("body"), &zbody_tmp))) {
+       if (EXPECTED(zbody = get_option(options, ZEND_STRL("body"), &zbody_tmp))) {
                if ((Z_TYPE_P(zbody) == IS_OBJECT) && instanceof_function(Z_OBJCE_P(zbody), php_http_get_message_body_class_entry())) {
                        php_http_message_body_object_t *body_obj = PHP_HTTP_OBJ(NULL, zbody);
 
@@ -97,8 +97,8 @@ static php_http_message_t *get_request(zval *options)
        zval zrequest_tmp, *zrequest;
        php_http_message_t *request = NULL;
 
-       if ((zrequest = get_option(options, ZEND_STRL("request"), &zrequest_tmp))) {
-               if (Z_TYPE_P(zrequest) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zrequest), php_http_message_get_class_entry())) {
+       if (EXPECTED(zrequest = get_option(options, ZEND_STRL("request"), &zrequest_tmp))) {
+               if (UNEXPECTED(Z_TYPE_P(zrequest) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zrequest), php_http_message_get_class_entry()))) {
                        php_http_message_object_t *request_obj = PHP_HTTP_OBJ(NULL, zrequest);
 
                        request = request_obj->message;
@@ -144,11 +144,11 @@ php_http_cache_status_t php_http_env_is_response_cached_by_etag(zval *options, c
        zval zetag_tmp, *zetag;
 
 
-       if (!(body = get_body(options))) {
+       if (UNEXPECTED(!(body = get_body(options)))) {
                return ret;
        }
 
-       if ((zetag = get_option(options, ZEND_STRL("etag"), &zetag_tmp)) && Z_TYPE_P(zetag) != IS_NULL) {
+       if (EXPECTED(zetag = get_option(options, ZEND_STRL("etag"), &zetag_tmp)) && Z_TYPE_P(zetag) != IS_NULL) {
                zend_string *zs = zval_get_string(zetag);
                etag = estrndup(zs->val, zs->len);
                zend_string_release(zs);
@@ -177,16 +177,16 @@ php_http_cache_status_t php_http_env_is_response_cached_by_last_modified(zval *o
        php_http_message_body_t *body;
        zval zlm_tmp, *zlm;
 
-       if (!(body = get_body(options))) {
+       if (UNEXPECTED(!(body = get_body(options)))) {
                return ret;
        }
 
-       if ((zlm = get_option(options, ZEND_STRL("lastModified"), &zlm_tmp))) {
+       if (EXPECTED(zlm = get_option(options, ZEND_STRL("lastModified"), &zlm_tmp))) {
                lm = zval_get_long(zlm);
                zval_ptr_dtor(zlm);
        }
 
-       if (lm <= 0) {
+       if (EXPECTED(lm <= 0)) {
                lm = php_http_message_body_mtime(body);
                set_option(options, ZEND_STRL("lastModified"), IS_LONG, &lm, 0);
        }
@@ -228,13 +228,13 @@ static size_t output(void *context, char *buf, size_t len)
 {
        php_http_env_response_t *r = context;
 
-       if (SUCCESS != r->ops->write(r, buf, len)) {
+       if (UNEXPECTED(SUCCESS != r->ops->write(r, buf, len))) {
                return (size_t) -1;
        }
 
        /*      we really only need to flush when throttling is enabled,
                because we push the data as fast as possible anyway if not */
-       if (r->throttle.delay >= PHP_HTTP_DIFFSEC) {
+       if (UNEXPECTED(r->throttle.delay >= PHP_HTTP_DIFFSEC)) {
                r->ops->flush(r);
                php_http_sleep(r->throttle.delay);
        }
@@ -339,14 +339,14 @@ static ZEND_RESULT_CODE php_http_env_response_send_head(php_http_env_response_t
                return ret;
        }
 
-       if ((zoption = get_option(options, ZEND_STRL("headers"), &zoption_tmp))) {
-               if (Z_TYPE_P(zoption) == IS_ARRAY) {
+       if (EXPECTED(zoption = get_option(options, ZEND_STRL("headers"), &zoption_tmp))) {
+               if (EXPECTED(Z_TYPE_P(zoption) == IS_ARRAY)) {
                        php_http_header_to_callback(Z_ARRVAL_P(zoption), 0, (php_http_pass_format_callback_t) r->ops->set_header, r);
                }
                zval_ptr_dtor(zoption);
        }
 
-       if ((zoption = get_option(options, ZEND_STRL("responseCode"), &zoption_tmp))) {
+       if (EXPECTED(zoption = get_option(options, ZEND_STRL("responseCode"), &zoption_tmp))) {
                zend_long rc = zval_get_long(zoption);
 
                zval_ptr_dtor(zoption);
@@ -359,12 +359,12 @@ static ZEND_RESULT_CODE php_http_env_response_send_head(php_http_env_response_t
                return ret;
        }
 
-       if ((zoption = get_option(options, ZEND_STRL("httpVersion"), &zoption_tmp))) {
+       if (EXPECTED(zoption = get_option(options, ZEND_STRL("httpVersion"), &zoption_tmp))) {
                php_http_version_t v;
                zend_string *zs = zval_get_string(zoption);
 
                zval_ptr_dtor(zoption);
-               if (zs->len && php_http_version_parse(&v, zs->val)) {
+               if (EXPECTED(zs->len && php_http_version_parse(&v, zs->val))) {
                        ret = r->ops->set_protocol_version(r, &v);
                        php_http_version_dtor(&v);
                }
@@ -375,7 +375,7 @@ static ZEND_RESULT_CODE php_http_env_response_send_head(php_http_env_response_t
                return ret;
        }
 
-       if ((zoption = get_option(options, ZEND_STRL("cookies"), &zoption_tmp))) {
+       if (EXPECTED(zoption = get_option(options, ZEND_STRL("cookies"), &zoption_tmp))) {
                if (Z_TYPE_P(zoption) == IS_ARRAY) {
                        zval *zcookie;
 
@@ -403,7 +403,7 @@ static ZEND_RESULT_CODE php_http_env_response_send_head(php_http_env_response_t
                return ret;
        }
 
-       if ((zoption = get_option(options, ZEND_STRL("contentType"), &zoption_tmp))) {
+       if (EXPECTED(zoption = get_option(options, ZEND_STRL("contentType"), &zoption_tmp))) {
                zend_string *zs = zval_get_string(zoption);
 
                zval_ptr_dtor(zoption);
@@ -419,12 +419,12 @@ static ZEND_RESULT_CODE php_http_env_response_send_head(php_http_env_response_t
                return ret;
        }
 
-       if (r->range.status == PHP_HTTP_RANGE_OK) {
+       if (UNEXPECTED(r->range.status == PHP_HTTP_RANGE_OK)) {
                if (zend_hash_num_elements(&r->range.values) == 1) {
                        zval *range, *begin, *end;
 
-                       if (    1 == php_http_array_list(&r->range.values, 1, &range)
-                               &&      2 == php_http_array_list(Z_ARRVAL_P(range), 2, &begin, &end)
+                       if (EXPECTED(   1 == php_http_array_list(&r->range.values, 1, &range)
+                               &&      2 == php_http_array_list(Z_ARRVAL_P(range), 2, &begin, &end))
                        ) {
                                if (SUCCESS == (ret = r->ops->set_status(r, 206))) {
                                        ret = r->ops->set_header(r, "Content-Range: bytes %ld-%ld/%zu", Z_LVAL_P(begin), Z_LVAL_P(end), r->content.length);
@@ -441,7 +441,7 @@ static ZEND_RESULT_CODE php_http_env_response_send_head(php_http_env_response_t
                        }
                }
        } else {
-               if ((zoption = get_option(options, ZEND_STRL("cacheControl"), &zoption_tmp))) {
+               if (EXPECTED(zoption = get_option(options, ZEND_STRL("cacheControl"), &zoption_tmp))) {
                        zend_string *zs = zval_get_string(zoption);
 
                        zval_ptr_dtor(zoption);
@@ -455,7 +455,7 @@ static ZEND_RESULT_CODE php_http_env_response_send_head(php_http_env_response_t
                        return ret;
                }
 
-               if ((zoption = get_option(options, ZEND_STRL("contentDisposition"), &zoption_tmp))) {
+               if (EXPECTED(zoption = get_option(options, ZEND_STRL("contentDisposition"), &zoption_tmp))) {
 
                        if (Z_TYPE_P(zoption) == IS_ARRAY) {
                                php_http_buffer_t buf;
@@ -476,7 +476,7 @@ static ZEND_RESULT_CODE php_http_env_response_send_head(php_http_env_response_t
                        return ret;
                }
 
-               if ((zoption = get_option(options, ZEND_STRL("contentEncoding"), &zoption_tmp))) {
+               if (EXPECTED(zoption = get_option(options, ZEND_STRL("contentEncoding"), &zoption_tmp))) {
                        zend_long ce = zval_get_long(zoption);
                        zval zsupported;
                        HashTable *result = NULL;
@@ -551,22 +551,22 @@ static ZEND_RESULT_CODE php_http_env_response_send_head(php_http_env_response_t
                                        break;
                        }
 
-                       if ((zoption = get_option(options, ZEND_STRL("etag"), &zoption_tmp))) {
+                       if (EXPECTED(zoption = get_option(options, ZEND_STRL("etag"), &zoption_tmp))) {
                                zend_string *zs = zval_get_string(zoption);
 
                                zval_ptr_dtor(zoption);
-                               if (*zs->val != '"' && strncmp(zs->val, "W/\"", 3)) {
+                               if (EXPECTED(*zs->val != '"' && strncmp(zs->val, "W/\"", 3))) {
                                        ret = r->ops->set_header(r, "ETag: \"%s\"", zs->val);
                                } else {
                                        ret = r->ops->set_header(r, "ETag: %s", zs->val);
                                }
                                zend_string_release(zs);
                        }
-                       if ((zoption = get_option(options, ZEND_STRL("lastModified"), &zoption_tmp))) {
+                       if (EXPECTED(zoption = get_option(options, ZEND_STRL("lastModified"), &zoption_tmp))) {
                                zend_long lm = zval_get_long(zoption);
 
                                zval_ptr_dtor(zoption);
-                               if (lm) {
+                               if (EXPECTED(lm)) {
                                        zend_string *date = php_format_date(ZEND_STRL(PHP_HTTP_DATE_FORMAT), lm, 0);
                                        if (date) {
                                                ret = r->ops->set_header(r, "Last-Modified: %s", date->val);
@@ -590,17 +590,17 @@ static ZEND_RESULT_CODE php_http_env_response_send_body(php_http_env_response_t
                return ret;
        }
 
-       if ((body = get_body(&r->options))) {
-               if ((zoption = get_option(&r->options, ZEND_STRL("throttleDelay"), &zoption_tmp))) {
+       if (EXPECTED(body = get_body(&r->options))) {
+               if (EXPECTED(zoption = get_option(&r->options, ZEND_STRL("throttleDelay"), &zoption_tmp))) {
                        r->throttle.delay = zval_get_double(zoption);
                        zval_ptr_dtor(zoption);
                }
-               if ((zoption = get_option(&r->options, ZEND_STRL("throttleChunk"), &zoption_tmp))) {
+               if (EXPECTED(zoption = get_option(&r->options, ZEND_STRL("throttleChunk"), &zoption_tmp))) {
                        r->throttle.chunk = zval_get_long(zoption);
                        zval_ptr_dtor(zoption);
                }
 
-               if (r->range.status == PHP_HTTP_RANGE_OK) {
+               if (UNEXPECTED(r->range.status == PHP_HTTP_RANGE_OK)) {
                        if (zend_hash_num_elements(&r->range.values) == 1) {
                                /* single range */
                                zval *range, *begin, *end;
@@ -672,10 +672,10 @@ ZEND_RESULT_CODE php_http_env_response_send(php_http_env_response_t *r)
        request = get_request(&r->options);
 
        /* check for ranges */
-       if ((body = get_body(&r->options))) {
+       if (EXPECTED(body = get_body(&r->options))) {
                r->content.length = php_http_message_body_size(body);
 
-               if (SUCCESS != r->ops->set_header(r, "Accept-Ranges: bytes")) {
+               if (UNEXPECTED(SUCCESS != r->ops->set_header(r, "Accept-Ranges: bytes"))) {
                        return FAILURE;
                } else {
                        ZEND_INIT_SYMTABLE_EX(&r->range.values, 0, 0);
@@ -727,17 +727,17 @@ ZEND_RESULT_CODE php_http_env_response_send(php_http_env_response_t *r)
                }
        }
 
-       if (SUCCESS != php_http_env_response_send_head(r, request)) {
+       if (UNEXPECTED(SUCCESS != php_http_env_response_send_head(r, request))) {
                php_error_docref(NULL, E_WARNING, "Failed to send response headers");
                return FAILURE;
        }
 
-       if (SUCCESS != php_http_env_response_send_body(r)) {
+       if (UNEXPECTED(SUCCESS != php_http_env_response_send_body(r))) {
                php_error_docref(NULL, E_WARNING, "Failed to send response body");
                return FAILURE;
        }
 
-       if (SUCCESS != r->ops->finish(r)) {
+       if (UNEXPECTED(SUCCESS != r->ops->finish(r))) {
                php_error_docref(NULL, E_WARNING, "Failed to finish response");
                return FAILURE;
        }
@@ -848,7 +848,7 @@ static ZEND_RESULT_CODE php_http_env_response_stream_init(php_http_env_response_
        ctx = ecalloc(1, sizeof(*ctx));
 
        ctx->stream = init_arg;
-       ++GC_REFCOUNT(ctx->stream->res);
+       GC_ADDREF(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);
@@ -857,7 +857,7 @@ static ZEND_RESULT_CODE php_http_env_response_stream_init(php_http_env_response_
        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) {
+       if (UNEXPECTED(ctx->request && ctx->request->http.version.major == 1 && ctx->request->http.version.minor == 0)) {
                ctx->version.minor = 0;
        }
 
@@ -869,7 +869,7 @@ static void php_http_env_response_stream_dtor(php_http_env_response_t *r)
 {
        php_http_env_response_stream_ctx_t *ctx = r->ctx;
 
-       if (ctx->chunked_filter) {
+       if (UNEXPECTED(ctx->chunked_filter)) {
                ctx->chunked_filter = php_stream_filter_remove(ctx->chunked_filter, 1);
        }
        zend_hash_destroy(&ctx->header);
@@ -883,7 +883,7 @@ static void php_http_env_response_stream_header(php_http_env_response_stream_ctx
 
        ZEND_HASH_FOREACH_VAL(header, val)
        {
-               if (Z_TYPE_P(val) == IS_ARRAY) {
+               if (UNEXPECTED(Z_TYPE_P(val) == IS_ARRAY)) {
                        php_http_env_response_stream_header(ctx, Z_ARRVAL_P(val), buf);
                } else {
                        zend_string *zs = zval_get_string(val);
@@ -913,11 +913,11 @@ static ZEND_RESULT_CODE php_http_env_response_stream_start(php_http_env_response
        php_http_buffer_appendf(&header_buf, "HTTP/%u.%u %ld %s" PHP_HTTP_CRLF, ctx->version.major, ctx->version.minor, ctx->status_code, php_http_env_get_response_status_for_code(ctx->status_code));
 
        /* there are some limitations regarding TE:chunked, see https://tools.ietf.org/html/rfc7230#section-3.3.1 */
-       if (ctx->version.major == 1 && ctx->version.minor == 0) {
+       if (UNEXPECTED(ctx->version.major == 1 && ctx->version.minor == 0)) {
                ctx->chunked = 0;
-       } else if (ctx->status_code == 204 || ctx->status_code/100 == 1) {
+       } else if (UNEXPECTED(ctx->status_code == 204 || ctx->status_code/100 == 1)) {
                ctx->chunked = 0;
-       } else if (ctx->request && ctx->status_code/100 == 2 && !strcasecmp(ctx->request->http.info.request.method, "CONNECT")) {
+       } else if (UNEXPECTED(ctx->request && ctx->status_code/100 == 2 && !strcasecmp(ctx->request->http.info.request.method, "CONNECT"))) {
                ctx->chunked = 0;
        }
 
@@ -938,7 +938,9 @@ static ZEND_RESULT_CODE php_http_env_response_stream_start(php_http_env_response
 
        if (ctx->chunked) {
                ctx->chunked_filter = php_stream_filter_create("http.chunked_encode", NULL, 0);
-               php_stream_filter_append(&ctx->stream->writefilters, ctx->chunked_filter);
+               if (ctx->chunked_filter) {
+                       php_stream_filter_append(&ctx->stream->writefilters, ctx->chunked_filter);
+               }
        }
 
        return ctx->started ? SUCCESS : FAILURE;
@@ -982,13 +984,13 @@ static ZEND_RESULT_CODE php_http_env_response_stream_set_header_ex(php_http_env_
        zend_string *header_key;
        ZEND_RESULT_CODE rv;
 
-       if (stream_ctx->started || stream_ctx->finished) {
+       if (UNEXPECTED(stream_ctx->started || stream_ctx->finished)) {
                return FAILURE;
        }
 
        header_len = vspprintf(&header_str, 0, fmt, argv);
 
-       if (!(header_end = strchr(header_str, ':'))) {
+       if (UNEXPECTED(!(header_end = strchr(header_str, ':')))) {
                efree(header_str);
                return FAILURE;
        }
@@ -1080,10 +1082,10 @@ static ZEND_RESULT_CODE php_http_env_response_stream_finish(php_http_env_respons
 {
        php_http_env_response_stream_ctx_t *ctx = r->ctx;
 
-       if (ctx->finished) {
+       if (UNEXPECTED(ctx->finished)) {
                return FAILURE;
        }
-       if (!ctx->started) {
+       if (UNEXPECTED(!ctx->started)) {
                if (SUCCESS != php_http_env_response_stream_start(ctx)) {
                        return FAILURE;
                }
@@ -1391,7 +1393,7 @@ static PHP_METHOD(HttpEnvResponse, send)
 }
 
 static zend_function_entry php_http_env_response_methods[] = {
-       PHP_ME(HttpEnvResponse, __construct,             ai_HttpEnvResponse___construct,             ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+       PHP_ME(HttpEnvResponse, __construct,             ai_HttpEnvResponse___construct,             ZEND_ACC_PUBLIC)
        PHP_ME(HttpEnvResponse, __invoke,                ai_HttpEnvResponse___invoke,                ZEND_ACC_PUBLIC)
        PHP_ME(HttpEnvResponse, setEnvRequest,           ai_HttpEnvResponse_setEnvRequest,           ZEND_ACC_PUBLIC)
        PHP_ME(HttpEnvResponse, setCookie,               ai_HttpEnvResponse_setCookie,               ZEND_ACC_PUBLIC)
index 5acf280..e3e66f1 100644 (file)
@@ -22,7 +22,7 @@
        do { \
                zend_error_handling __zeh; \
                zend_replace_error_handling(EH_THROW, php_http_get_exception_ ##e## _class_entry(), &__zeh); \
-               if (!(test)) { \
+               if (UNEXPECTED(!(test))) { \
                        zend_restore_error_handling(&__zeh); \
                        fail; \
                } \
index 92bfd15..95ff560 100644 (file)
@@ -75,7 +75,7 @@ typedef struct _http_chunked_decode_filter_buffer_t {
        ulong   hexlen;
 } PHP_HTTP_FILTER_BUFFER(chunked_decode);
 
-typedef php_http_encoding_stream_t PHP_HTTP_FILTER_BUFFER(zlib);
+typedef php_http_encoding_stream_t PHP_HTTP_FILTER_BUFFER(stream);
 
 static PHP_HTTP_FILTER_FUNCTION(chunked_decode)
 {
@@ -266,22 +266,10 @@ static PHP_HTTP_FILTER_FUNCTION(chunked_encode)
        return PSFS_PASS_ON;
 }
 
-static PHP_HTTP_FILTER_OPS(chunked_decode) = {
-       PHP_HTTP_FILTER_FUNC(chunked_decode),
-       PHP_HTTP_FILTER_DTOR(chunked_decode),
-       "http.chunked_decode"
-};
-
-static PHP_HTTP_FILTER_OPS(chunked_encode) = {
-       PHP_HTTP_FILTER_FUNC(chunked_encode),
-       NULL,
-       "http.chunked_encode"
-};
-
-static PHP_HTTP_FILTER_FUNCTION(zlib)
+static PHP_HTTP_FILTER_FUNCTION(stream)
 {
        php_stream_bucket *ptr, *nxt;
-       PHP_HTTP_FILTER_BUFFER(zlib) *buffer = Z_PTR(this->abstract);
+       PHP_HTTP_FILTER_BUFFER(stream) *buffer = Z_PTR(this->abstract);
        
        if (bytes_consumed) {
                *bytes_consumed = 0;
@@ -307,7 +295,7 @@ static PHP_HTTP_FILTER_FUNCTION(zlib)
                }
                
 #if DBG_FILTER
-               fprintf(stderr, "update: deflate (-> %zu) (w: %zu, r: %zu)\n", encoded_len, stream->writepos, stream->readpos);
+               fprintf(stderr, "update: compress (-> %zu) (w: %zu, r: %zu)\n", encoded_len, stream->writepos, stream->readpos);
 #endif
                
                if (encoded) {
@@ -329,7 +317,7 @@ static PHP_HTTP_FILTER_FUNCTION(zlib)
                }
                
 #if DBG_FILTER
-               fprintf(stderr, "flush: deflate (-> %zu)\n", encoded_len);
+               fprintf(stderr, "flush: compress (-> %zu)\n", encoded_len);
 #endif
                
                if (encoded) {
@@ -349,7 +337,7 @@ static PHP_HTTP_FILTER_FUNCTION(zlib)
                }
                
 #if DBG_FILTER
-               fprintf(stderr, "finish: deflate (-> %zu)\n", encoded_len);
+               fprintf(stderr, "finish: compress (-> %zu)\n", encoded_len);
 #endif
                
                if (encoded) {
@@ -362,25 +350,56 @@ static PHP_HTTP_FILTER_FUNCTION(zlib)
        
        return PSFS_PASS_ON;
 }
-static PHP_HTTP_FILTER_DESTRUCTOR(zlib)
+
+static PHP_HTTP_FILTER_DESTRUCTOR(stream)
 {
-       PHP_HTTP_FILTER_BUFFER(zlib) *buffer = Z_PTR(this->abstract);
+       PHP_HTTP_FILTER_BUFFER(stream) *buffer = Z_PTR(this->abstract);
        php_http_encoding_stream_free(&buffer);
 }
 
+static PHP_HTTP_FILTER_OPS(chunked_decode) = {
+       PHP_HTTP_FILTER_FUNC(chunked_decode),
+       PHP_HTTP_FILTER_DTOR(chunked_decode),
+       "http.chunked_decode"
+};
+
+static PHP_HTTP_FILTER_OPS(chunked_encode) = {
+       PHP_HTTP_FILTER_FUNC(chunked_encode),
+       NULL,
+       "http.chunked_encode"
+};
+
 static PHP_HTTP_FILTER_OPS(deflate) = {
-       PHP_HTTP_FILTER_FUNC(zlib),
-       PHP_HTTP_FILTER_DTOR(zlib),
+       PHP_HTTP_FILTER_FUNC(stream),
+       PHP_HTTP_FILTER_DTOR(stream),
        "http.deflate"
 };
 
 static PHP_HTTP_FILTER_OPS(inflate) = {
-       PHP_HTTP_FILTER_FUNC(zlib),
-       PHP_HTTP_FILTER_DTOR(zlib),
+       PHP_HTTP_FILTER_FUNC(stream),
+       PHP_HTTP_FILTER_DTOR(stream),
        "http.inflate"
 };
 
+#if PHP_HTTP_HAVE_LIBBROTLI
+static PHP_HTTP_FILTER_OPS(brotli_encode) = {
+       PHP_HTTP_FILTER_FUNC(stream),
+       PHP_HTTP_FILTER_DTOR(stream),
+       "http.brotli_encode"
+};
+
+static PHP_HTTP_FILTER_OPS(brotli_decode) = {
+       PHP_HTTP_FILTER_FUNC(stream),
+       PHP_HTTP_FILTER_DTOR(stream),
+       "http.brotli_decode"
+};
+#endif
+
+#if PHP_VERSION_ID >= 70200
+static php_stream_filter *http_filter_create(const char *name, zval *params, uint8_t p)
+#else
 static php_stream_filter *http_filter_create(const char *name, zval *params, int p)
+#endif
 {
        zval *tmp = params;
        php_stream_filter *f = NULL;
@@ -416,7 +435,7 @@ static php_stream_filter *http_filter_create(const char *name, zval *params, int
        } else
        
        if (!strcasecmp(name, "http.inflate")) {
-               PHP_HTTP_FILTER_BUFFER(zlib) *b = NULL;
+               PHP_HTTP_FILTER_BUFFER(stream) *b = NULL;
                
                if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), flags))) {
                        if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(inflate), b, p))) {
@@ -426,13 +445,35 @@ static php_stream_filter *http_filter_create(const char *name, zval *params, int
        } else
        
        if (!strcasecmp(name, "http.deflate")) {
-               PHP_HTTP_FILTER_BUFFER(zlib) *b = NULL;
+               PHP_HTTP_FILTER_BUFFER(stream) *b = NULL;
                
                if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), flags))) {
                        if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(deflate), b, p))) {
                                php_http_encoding_stream_free(&b);
                        }
                }
+#if PHP_HTTP_HAVE_LIBBROTLI
+       } else
+
+       if (!strcasecmp(name, "http.brotli_encode")) {
+               PHP_HTTP_FILTER_BUFFER(stream) *b = NULL;
+
+               if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_enbrotli_ops(), flags))) {
+                       if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(brotli_encode), b, p))) {
+                               php_http_encoding_stream_free(&b);
+                       }
+               }
+       } else
+
+       if (!strcasecmp(name, "http.brotli_decode")) {
+               PHP_HTTP_FILTER_BUFFER(stream) *b = NULL;
+
+               if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_debrotli_ops(), flags))) {
+                       if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(brotli_decode), b, p))) {
+                               php_http_encoding_stream_free(&b);
+                       }
+               }
+#endif
        }
        
        return f;
index 8c71cde..4c9a702 100644 (file)
@@ -398,7 +398,7 @@ PHP_METHOD(HttpHeader, parse)
 }
 
 static zend_function_entry php_http_header_methods[] = {
-       PHP_ME(HttpHeader, __construct,   ai_HttpHeader___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+       PHP_ME(HttpHeader, __construct,   ai_HttpHeader___construct, ZEND_ACC_PUBLIC)
        PHP_ME(HttpHeader, serialize,     ai_HttpHeader_serialize, ZEND_ACC_PUBLIC)
        ZEND_MALIAS(HttpHeader, __toString, serialize, ai_HttpHeader_serialize, ZEND_ACC_PUBLIC)
        ZEND_MALIAS(HttpHeader, toString, serialize, ai_HttpHeader_serialize, ZEND_ACC_PUBLIC)
index fb84452..cd76b76 100644 (file)
@@ -40,42 +40,19 @@ php_http_header_parser_t *php_http_header_parser_init(php_http_header_parser_t *
        return parser;
 }
 
-php_http_header_parser_state_t php_http_header_parser_state_push(php_http_header_parser_t *parser, unsigned argc, ...)
-{
-       va_list va_args;
-       unsigned i;
-       php_http_header_parser_state_t state = 0;
-
-       /* short circuit */
-       ZEND_PTR_STACK_RESIZE_IF_NEEDED((&parser->stack), argc);
-
-       va_start(va_args, argc);
-       for (i = 0; i < argc; ++i) {
-               state = va_arg(va_args, php_http_header_parser_state_t);
-               zend_ptr_stack_push(&parser->stack, (void *) state);
-       }
-       va_end(va_args);
-
-       return state;
-}
+#define php_http_header_parser_state_push(parser, state) zend_ptr_stack_push(&(parser)->stack, (void *) (state)), (state)
+#define php_http_header_parser_state_ex(parser) ((parser)->stack.top \
+               ? (php_http_header_parser_state_t) (parser)->stack.elements[(parser)->stack.top - 1] \
+               : PHP_HTTP_HEADER_PARSER_STATE_START)
 
 php_http_header_parser_state_t php_http_header_parser_state_is(php_http_header_parser_t *parser)
 {
-       if (parser->stack.top) {
-               return (php_http_header_parser_state_t) parser->stack.elements[parser->stack.top - 1];
-       }
-
-       return PHP_HTTP_HEADER_PARSER_STATE_START;
+       return php_http_header_parser_state_ex(parser);
 }
 
-php_http_header_parser_state_t php_http_header_parser_state_pop(php_http_header_parser_t *parser)
-{
-       if (parser->stack.top) {
-               return (php_http_header_parser_state_t) zend_ptr_stack_pop(&parser->stack);
-       }
-
-       return PHP_HTTP_HEADER_PARSER_STATE_START;
-}
+#define php_http_header_parser_state_pop(parser) ((parser)->stack.top \
+               ? (php_http_header_parser_state_t) zend_ptr_stack_pop(&(parser)->stack) \
+               : PHP_HTTP_HEADER_PARSER_STATE_START)
 
 void php_http_header_parser_dtor(php_http_header_parser_t *parser)
 {
@@ -97,9 +74,14 @@ void php_http_header_parser_free(php_http_header_parser_t **parser)
 /* 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 )
 {
-       zend_string *escaped_str = zend_string_init(str, len, 0);
+       zend_string *escaped_str, *zstr_str = zend_string_init(str, len, 0);
 
-       escaped_str = php_addcslashes(escaped_str, 1, ZEND_STRL("\x0..\x1F\x7F..\xFF"));
+#if PHP_VERSION_ID < 70300
+       escaped_str = php_addcslashes(zstr_str, 1, ZEND_STRL("\x0..\x1F\x7F..\xFF"));
+#else
+       escaped_str = php_addcslashes(zstr_str, ZEND_STRL("\x0..\x1F\x7F..\xFF"));
+       zend_string_release_ex(zstr_str, 0);
+#endif
 
        if (valid_len != len && (!eol_str || (str+valid_len) != eol_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);
@@ -114,7 +96,7 @@ static void php_http_header_parser_error(size_t valid_len, char *str, size_t len
 
 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)
 {
-       while (buffer->used || !php_http_header_parser_states[php_http_header_parser_state_is(parser)].need_data) {
+       while (buffer->used || !php_http_header_parser_states[php_http_header_parser_state_ex(parser)].need_data) {
 #if DBG_PARSER
                const char *state[] = {"START", "KEY", "VALUE", "VALUE_EX", "HEADER_DONE", "DONE"};
                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);
@@ -123,7 +105,7 @@ php_http_header_parser_state_t php_http_header_parser_parse(php_http_header_pars
                switch (php_http_header_parser_state_pop(parser)) {
                        case PHP_HTTP_HEADER_PARSER_STATE_FAILURE:
                                php_error_docref(NULL, E_WARNING, "Failed to parse headers");
-                               return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
+                               return php_http_header_parser_state_push(parser, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
 
                        case PHP_HTTP_HEADER_PARSER_STATE_START: {
                                char *ptr = buffer->data;
@@ -133,7 +115,7 @@ php_http_header_parser_state_t php_http_header_parser_parse(php_http_header_pars
                                }
 
                                php_http_buffer_cut(buffer, 0, ptr - buffer->data);
-                               php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY);
+                               php_http_header_parser_state_push(parser, PHP_HTTP_HEADER_PARSER_STATE_KEY);
                                break;
                        }
 
@@ -147,7 +129,7 @@ php_http_header_parser_state_t php_http_header_parser_parse(php_http_header_pars
                                if (buffer->data == (eol_str = php_http_locate_bin_eol(buffer->data, buffer->used, &eol_len))) {
                                        /* end of headers */
                                        php_http_buffer_cut(buffer, 0, eol_len);
-                                       php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_DONE);
+                                       php_http_header_parser_state_push(parser, PHP_HTTP_HEADER_PARSER_STATE_DONE);
                                } else if (php_http_info_parse(&parser->info, buffer->data)) {
                                        /* new message starting with request/response line */
                                        if (callback_func) {
@@ -155,7 +137,7 @@ php_http_header_parser_state_t php_http_header_parser_parse(php_http_header_pars
                                        }
                                        php_http_info_dtor(&parser->info);
                                        php_http_buffer_cut(buffer, 0, eol_str + eol_len - buffer->data);
-                                       php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
+                                       php_http_header_parser_state_push(parser, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
                                } else if ((colon = memchr(buffer->data, ':', buffer->used)) && (!eol_str || eol_str > colon)) {
                                        /* header: string */
                                        size_t valid_len;
@@ -167,18 +149,18 @@ php_http_header_parser_state_t php_http_header_parser_parse(php_http_header_pars
                                        if (valid_len != parser->_key.len) {
                                                php_http_header_parser_error(valid_len, parser->_key.str, parser->_key.len, eol_str);
                                                PTR_SET(parser->_key.str, NULL);
-                                               return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
+                                               return php_http_header_parser_state_push(parser, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
                                        }
                                        while (PHP_HTTP_IS_CTYPE(space, *++colon) && *colon != '\n' && *colon != '\r');
                                        php_http_buffer_cut(buffer, 0, colon - buffer->data);
-                                       php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE);
+                                       php_http_header_parser_state_push(parser, PHP_HTTP_HEADER_PARSER_STATE_VALUE);
                                } else if (eol_str || (flags & PHP_HTTP_HEADER_PARSER_CLEANUP)) {
                                        /* neither reqeust/response line nor 'header:' string, or injected new line or NUL etc. */
                                        php_http_header_parser_error(strspn(buffer->data, PHP_HTTP_HEADER_NAME_CHARS), buffer->data, buffer->used, eol_str);
-                                       return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
+                                       return php_http_header_parser_state_push(parser, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
                                } else {
                                        /* keep feeding */
-                                       return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY);
+                                       return php_http_header_parser_state_push(parser, PHP_HTTP_HEADER_PARSER_STATE_KEY);
                                }
                                break;
                        }
@@ -217,26 +199,26 @@ php_http_header_parser_state_t php_http_header_parser_parse(php_http_header_pars
 
                                if ((eol_str = php_http_locate_bin_eol(buffer->data, buffer->used, &eol_len))) {
                                        SET_ADD_VAL(eol_str - buffer->data, eol_len);
-                                       php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX);
+                                       php_http_header_parser_state_push(parser, PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX);
                                } else if (flags & PHP_HTTP_HEADER_PARSER_CLEANUP) {
                                        if (buffer->used) {
                                                SET_ADD_VAL(buffer->used, 0);
                                        }
-                                       php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
+                                       php_http_header_parser_state_push(parser, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
                                } else {
-                                       return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE);
+                                       return php_http_header_parser_state_push(parser, PHP_HTTP_HEADER_PARSER_STATE_VALUE);
                                }
                                break;
                        }
 
                        case PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX:
                                if (buffer->used && (*buffer->data == ' ' || *buffer->data == '\t')) {
-                                       php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE);
+                                       php_http_header_parser_state_push(parser, PHP_HTTP_HEADER_PARSER_STATE_VALUE);
                                } else if (buffer->used || (flags & PHP_HTTP_HEADER_PARSER_CLEANUP)) {
-                                       php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
+                                       php_http_header_parser_state_push(parser, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
                                } else {
                                        /* keep feeding */
-                                       return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX);
+                                       return php_http_header_parser_state_push(parser, PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX);
                                }
                                break;
 
@@ -252,7 +234,7 @@ php_http_header_parser_state_t php_http_header_parser_parse(php_http_header_pars
                                                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);
+                                               return php_http_header_parser_state_push(parser, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
                                        }
 
                                        if (!headers && callback_func) {
@@ -273,7 +255,7 @@ php_http_header_parser_state_t php_http_header_parser_parse(php_http_header_pars
                                PTR_SET(parser->_key.str, NULL);
                                PTR_SET(parser->_val.str, NULL);
 
-                               php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY);
+                               php_http_header_parser_state_push(parser, PHP_HTTP_HEADER_PARSER_STATE_KEY);
                                break;
 
                        case PHP_HTTP_HEADER_PARSER_STATE_DONE:
index c22c6a7..c923998 100644 (file)
@@ -41,9 +41,7 @@ typedef struct php_http_header_parser {
 } php_http_header_parser_t;
 
 PHP_HTTP_API php_http_header_parser_t *php_http_header_parser_init(php_http_header_parser_t *parser);
-PHP_HTTP_API php_http_header_parser_state_t php_http_header_parser_state_push(php_http_header_parser_t *parser, unsigned argc, ...);
 PHP_HTTP_API php_http_header_parser_state_t php_http_header_parser_state_is(php_http_header_parser_t *parser);
-PHP_HTTP_API php_http_header_parser_state_t php_http_header_parser_state_pop(php_http_header_parser_t *parser);
 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);
index 1470f97..48da6bf 100644 (file)
@@ -96,12 +96,12 @@ php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_head
        zend_bool free_info = !info;
 
        /* sane parameter */
-       if ((!pre_header) || (!*pre_header)) {
+       if (UNEXPECTED((!pre_header) || (!*pre_header))) {
                return NULL;
        }
 
        /* where's the end of the line */
-       if (!(end = php_http_locate_eol(pre_header, NULL))) {
+       if (UNEXPECTED(!(end = php_http_locate_eol(pre_header, NULL)))) {
                end = pre_header + strlen(pre_header);
        }
 
@@ -112,7 +112,7 @@ php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_head
 
        info = php_http_info_init(info);
 
-       if (!php_http_version_parse(&info->http.version, http)) {
+       if (UNEXPECTED(!php_http_version_parse(&info->http.version, http))) {
                if (free_info) {
                        php_http_info_free(&info);
                }
@@ -126,7 +126,7 @@ php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_head
        }
 
        /* and nothing than SPACE or NUL after HTTP/X(.x) */
-       if (*off && (!PHP_HTTP_IS_CTYPE(space, *off))) {
+       if (UNEXPECTED(*off && (!PHP_HTTP_IS_CTYPE(space, *off)))) {
                if (free_info) {
                        php_http_info_free(&info);
                }
@@ -147,7 +147,7 @@ php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_head
 
                info->type = PHP_HTTP_RESPONSE;
                while (code < end && ' ' == *code) ++code;
-               if (end > code) {
+               if (EXPECTED(end > code)) {
                        /* rfc7230#3.1.2 The status-code element is a 3-digit integer code */
                        PHP_HTTP_INFO(info).response.code = 100*(*code++ - '0');
                        PHP_HTTP_INFO(info).response.code += 10*(*code++ - '0');
@@ -162,8 +162,8 @@ php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_head
                } else {
                        PHP_HTTP_INFO(info).response.code = 0;
                }
-               if (status && end > status) {
-                       while (' ' == *status) ++status;
+               if (EXPECTED(status && end > status)) {
+                       while (' ' == *status && end > status) ++status;
                        PHP_HTTP_INFO(info).response.status = estrndup(status, end - status);
                } else {
                        PHP_HTTP_INFO(info).response.status = NULL;
@@ -177,17 +177,17 @@ php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_head
                const char *url = strchr(pre_header, ' ');
 
                info->type = PHP_HTTP_REQUEST;
-               if (url && http > url) {
+               if (EXPECTED(url && http > url)) {
                        size_t url_len = url - pre_header;
 
                        PHP_HTTP_INFO(info).request.method = estrndup(pre_header, url_len);
 
-                       while (' ' == *url) ++url;
+                       while (' ' == *url && http > url) ++url;
                        while (' ' == *(http-1)) --http;
 
-                       if (http > url) {
+                       if (EXPECTED(http > url)) {
                                /* CONNECT presents an authority only */
-                               if (strcasecmp(PHP_HTTP_INFO(info).request.method, "CONNECT")) {
+                               if (UNEXPECTED(strcasecmp(PHP_HTTP_INFO(info).request.method, "CONNECT"))) {
                                        PHP_HTTP_INFO(info).request.url = php_http_url_parse(url, http - url, PHP_HTTP_URL_STDFLAGS);
                                } else {
                                        PHP_HTTP_INFO(info).request.url = php_http_url_parse_authority(url, http - url, PHP_HTTP_URL_STDFLAGS);
index 3f08ee4..d70ced4 100644 (file)
@@ -75,7 +75,7 @@ php_http_message_t *php_http_message_init_env(php_http_message_t *message, php_h
                        break;
 
                case PHP_HTTP_RESPONSE:
-                       message = php_http_message_init(NULL, type, NULL);
+                       message = php_http_message_init(message, type, NULL);
                        if (!SG(sapi_headers).http_status_line || !php_http_info_parse((php_http_info_t *) &message->http, SG(sapi_headers).http_status_line)) {
                                if (!(message->http.info.response.code = SG(sapi_headers).http_response_code)) {
                                        message->http.info.response.code = 200;
@@ -177,24 +177,24 @@ zend_bool php_http_message_is_multipart(php_http_message_t *msg, char **boundary
                popts.input.str = ct->val;
                popts.input.len = ct->len;
 
-               if (php_http_params_parse(&params, &popts)) {
+               if (EXPECTED(php_http_params_parse(&params, &popts))) {
                        zval *cur, *arg;
                        zend_string *ct_str;
                        zend_ulong index;
 
                        zend_hash_internal_pointer_reset(&params);
 
-                       if ((cur = zend_hash_get_current_data(&params))
+                       if (EXPECTED((cur = zend_hash_get_current_data(&params))
                        &&      (Z_TYPE_P(cur) == IS_ARRAY)
-                       &&      (HASH_KEY_IS_STRING == zend_hash_get_current_key(&params, &ct_str, &index))
+                       &&      (HASH_KEY_IS_STRING == zend_hash_get_current_key(&params, &ct_str, &index)))
                        ) {
                                if (php_http_match(ct_str->val, "multipart", PHP_HTTP_MATCH_WORD)) {
                                        is_multipart = 1;
 
                                        /* get boundary */
-                                       if (boundary
+                                       if (EXPECTED(boundary
                                        &&      (arg = zend_hash_str_find(Z_ARRVAL_P(cur), ZEND_STRL("arguments")))
-                                       &&      Z_TYPE_P(arg) == IS_ARRAY
+                                       &&      Z_TYPE_P(arg) == IS_ARRAY)
                                        ) {
                                                zval *val;
                                                php_http_arrkey_t key;
@@ -204,7 +204,7 @@ zend_bool php_http_message_is_multipart(php_http_message_t *msg, char **boundary
                                                        if (key.key && key.key->len == lenof("boundary") && !strcasecmp(key.key->val, "boundary")) {
                                                                zend_string *bnd = zval_get_string(val);
 
-                                                               if (bnd->len) {
+                                                               if (EXPECTED(bnd->len)) {
                                                                        *boundary = estrndup(bnd->val, bnd->len);
                                                                }
                                                                zend_string_release(bnd);
@@ -471,7 +471,7 @@ php_http_message_t *php_http_message_copy_ex(php_http_message_t *from, php_http_
 
 void php_http_message_dtor(php_http_message_t *message)
 {
-       if (message) {
+       if (EXPECTED(message)) {
                zend_hash_destroy(&message->hdrs);
                php_http_message_body_free(&message->body);
 
@@ -493,7 +493,7 @@ void php_http_message_dtor(php_http_message_t *message)
 
 void php_http_message_free(php_http_message_t **message)
 {
-       if (*message) {
+       if (EXPECTED(*message)) {
                if ((*message)->parent) {
                        php_http_message_free(&(*message)->parent);
                }
@@ -538,12 +538,14 @@ static php_http_message_object_prophandler_t *php_http_message_object_get_propha
        return zend_hash_str_find_ptr(&php_http_message_object_prophandlers, name_str->val, name_str->len);
 }
 static void php_http_message_object_prophandler_get_type(php_http_message_object_t *obj, zval *return_value) {
+       zval_ptr_dtor(return_value);
        RETVAL_LONG(obj->message->type);
 }
 static void php_http_message_object_prophandler_set_type(php_http_message_object_t *obj, zval *value) {
        php_http_message_set_type(obj->message, zval_get_long(value));
 }
 static void php_http_message_object_prophandler_get_request_method(php_http_message_object_t *obj, zval *return_value) {
+       zval_ptr_dtor(return_value);
        if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message) && obj->message->http.info.request.method) {
                RETVAL_STRING(obj->message->http.info.request.method);
        } else {
@@ -561,6 +563,7 @@ static void php_http_message_object_prophandler_get_request_url(php_http_message
        char *url_str;
        size_t url_len;
 
+       zval_ptr_dtor(return_value);
        if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message) && obj->message->http.info.request.url && php_http_url_to_string(obj->message->http.info.request.url, &url_str, &url_len, 0)) {
                RETVAL_STR(php_http_cs2zs(url_str, url_len));
        } else {
@@ -573,6 +576,7 @@ static void php_http_message_object_prophandler_set_request_url(php_http_message
        }
 }
 static void php_http_message_object_prophandler_get_response_status(php_http_message_object_t *obj, zval *return_value) {
+       zval_ptr_dtor(return_value);
        if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message) && obj->message->http.info.response.status) {
                RETVAL_STRING(obj->message->http.info.response.status);
        } else {
@@ -587,6 +591,7 @@ static void php_http_message_object_prophandler_set_response_status(php_http_mes
        }
 }
 static void php_http_message_object_prophandler_get_response_code(php_http_message_object_t *obj, zval *return_value) {
+       zval_ptr_dtor(return_value);
        if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message)) {
                RETVAL_LONG(obj->message->http.info.response.code);
        } else {
@@ -603,6 +608,7 @@ static void php_http_message_object_prophandler_get_http_version(php_http_messag
        char *version_str;
        size_t version_len;
 
+       zval_ptr_dtor(return_value);
        php_http_version_to_string(&obj->message->http.version, &version_str, &version_len, NULL, NULL);
        RETVAL_STR(php_http_cs2zs(version_str, version_len));
 }
@@ -612,28 +618,41 @@ static void php_http_message_object_prophandler_set_http_version(php_http_messag
        zend_string_release(zs);
 }
 static void php_http_message_object_prophandler_get_headers(php_http_message_object_t *obj, zval *return_value ) {
+       zval tmp;
+
+       ZVAL_COPY_VALUE(&tmp, return_value);
        array_init(return_value);
        array_copy(&obj->message->hdrs, Z_ARRVAL_P(return_value));
+       zval_ptr_dtor(&tmp);
 }
 static void php_http_message_object_prophandler_set_headers(php_http_message_object_t *obj, zval *value) {
-       HashTable *headers;
-       zval *orig_value = value;
+       int converted = 0;
+       HashTable garbage, *src;
 
        if (Z_TYPE_P(value) != IS_ARRAY && Z_TYPE_P(value) != IS_OBJECT) {
-               convert_to_array_ex(value);
+               converted = 1;
+               SEPARATE_ZVAL(value);
+               convert_to_array(value);
        }
-       headers = HASH_OF(value);
+       src = HASH_OF(value);
 
-       zend_hash_clean(&obj->message->hdrs);
-       array_copy(headers, &obj->message->hdrs);
+       garbage = obj->message->hdrs;
+       zend_hash_init(&obj->message->hdrs, zend_hash_num_elements(src), NULL, ZVAL_PTR_DTOR, 0);
+       array_copy(HASH_OF(value), &obj->message->hdrs);
 
-       if (orig_value != value) {
+       zend_hash_destroy(&garbage);
+
+       if (converted) {
                zval_ptr_dtor(value);
        }
 }
 static void php_http_message_object_prophandler_get_body(php_http_message_object_t *obj, zval *return_value) {
        if (obj->body) {
+               zval tmp;
+
+               ZVAL_COPY_VALUE(&tmp, return_value);
                RETVAL_OBJECT(&obj->body->zo, 1);
+               zval_ptr_dtor(&tmp);
        } else {
                RETVAL_NULL();
        }
@@ -643,7 +662,11 @@ static void php_http_message_object_prophandler_set_body(php_http_message_object
 }
 static void php_http_message_object_prophandler_get_parent_message(php_http_message_object_t *obj, zval *return_value) {
        if (obj->message->parent) {
+               zval tmp;
+
+               ZVAL_COPY_VALUE(&tmp, return_value);
                RETVAL_OBJECT(&obj->parent->zo, 1);
+               zval_ptr_dtor(&tmp);
        } else {
                RETVAL_NULL();
        }
@@ -652,10 +675,10 @@ static void php_http_message_object_prophandler_set_parent_message(php_http_mess
        if (Z_TYPE_P(value) == IS_OBJECT && instanceof_function(Z_OBJCE_P(value), php_http_message_class_entry)) {
                php_http_message_object_t *parent_obj = PHP_HTTP_OBJ(NULL, value);
 
+               Z_ADDREF_P(value);
                if (obj->message->parent) {
                        zend_object_release(&obj->parent->zo);
                }
-               Z_ADDREF_P(value);
                obj->parent = parent_obj;
                obj->message->parent = parent_obj->message;
        }
@@ -835,7 +858,7 @@ php_http_message_object_t *php_http_message_object_new_ex(zend_class_entry *ce,
 
 zend_object *php_http_message_object_clone(zval *this_ptr)
 {
-       php_http_message_object_t *new_obj = NULL;
+       php_http_message_object_t *new_obj;
        php_http_message_object_t *old_obj = PHP_HTTP_OBJ(NULL, this_ptr);
 
        new_obj = php_http_message_object_new_ex(old_obj->zo.ce, php_http_message_copy(old_obj->message, NULL));
@@ -877,31 +900,27 @@ static zval *php_http_message_object_read_prop(zval *object, zval *member, int t
        zend_string *member_name = zval_get_string(member);
        php_http_message_object_prophandler_t *handler = php_http_message_object_get_prophandler(member_name);
 
-       if (!handler || type == BP_VAR_R || type == BP_VAR_IS) {
-               return_value = zend_get_std_object_handlers()->read_property(object, member, type, cache_slot, tmp);
+       return_value = zend_get_std_object_handlers()->read_property(object, member, type, cache_slot, tmp);
 
-               if (handler) {
+       if (handler && handler->read) {
+               if (type == BP_VAR_R || type == BP_VAR_IS) {
                        php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, object);
 
-                       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-                       handler->read(obj, tmp);
-
-                       //zval_ptr_dtor(return_value);
-                       ZVAL_COPY_VALUE(return_value, tmp);
-               }
-               zend_string_release(member_name);
-               return return_value;
-       } else {
-               php_property_proxy_t *proxy;
-               php_property_proxy_object_t *proxy_obj;
+                       handler->read(obj, return_value);
+               } else {
+                       php_property_proxy_t *proxy;
+                       php_property_proxy_object_t *proxy_obj;
 
-               proxy = php_property_proxy_init(object, member_name);
-               proxy_obj = php_property_proxy_object_new_ex(NULL, proxy);
+                       proxy = php_property_proxy_init(object, member_name);
+                       proxy_obj = php_property_proxy_object_new_ex(NULL, proxy);
 
-               ZVAL_OBJ(tmp, &proxy_obj->zo);
-               zend_string_release(member_name);
-               return tmp;
+                       ZVAL_OBJ(tmp, &proxy_obj->zo);
+                       return_value = tmp;
+               }
        }
+
+       zend_string_release(member_name);
+       return return_value;
 }
 
 static void php_http_message_object_write_prop(zval *object, zval *member, zval *value, void **cache_slot)
@@ -1102,7 +1121,6 @@ static PHP_METHOD(HttpMessage, getBody)
 
        if (!obj->body) {
                php_http_message_object_init_body_object(obj);
-
        }
        if (obj->body) {
                RETVAL_OBJECT(&obj->body->zo, 1);
@@ -1975,7 +1993,7 @@ static PHP_METHOD(HttpMessage, current)
 }
 
 static zend_function_entry php_http_message_methods[] = {
-       PHP_ME(HttpMessage, __construct,        ai_HttpMessage___construct,        ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+       PHP_ME(HttpMessage, __construct,        ai_HttpMessage___construct,        ZEND_ACC_PUBLIC)
        PHP_ME(HttpMessage, getBody,            ai_HttpMessage_getBody,            ZEND_ACC_PUBLIC)
        PHP_ME(HttpMessage, setBody,            ai_HttpMessage_setBody,            ZEND_ACC_PUBLIC)
        PHP_ME(HttpMessage, addBody,            ai_HttpMessage_addBody,            ZEND_ACC_PUBLIC)
index 18859a3..79c8bf7 100644 (file)
@@ -40,13 +40,13 @@ php_http_message_body_t *php_http_message_body_init(php_http_message_body_t **bo
                php_http_message_body_addref(body);
                return body;
        }
-       
+
        body = ecalloc(1, sizeof(php_http_message_body_t));
        body->refcount = 1;
 
        if (stream) {
                body->res = stream->res;
-               ++GC_REFCOUNT(body->res);
+               GC_ADDREF(body->res);
        } else {
                body->res = php_stream_temp_create(TEMP_STREAM_DEFAULT, 0xffff)->res;
        }
@@ -90,9 +90,9 @@ void php_http_message_body_free(php_http_message_body_t **body_ptr)
 {
        if (*body_ptr) {
                php_http_message_body_t *body = *body_ptr;
-
                if (!--body->refcount) {
                        zend_list_delete(body->res);
+                       body->res = NULL;
                        PTR_FREE(body->boundary);
                        efree(body);
                }
@@ -262,9 +262,14 @@ void php_http_message_body_add_part(php_http_message_body_t *body, php_http_mess
 
 ZEND_RESULT_CODE php_http_message_body_add_form_field(php_http_message_body_t *body, const char *name, const char *value_str, size_t value_len)
 {
-       zend_string *safe_name = zend_string_init(name, strlen(name), 0);
+       zend_string *safe_name, *zstr_name = zend_string_init(name, strlen(name), 0);
 
-       safe_name = php_addslashes(safe_name, 1);
+#if PHP_VERSION_ID < 70300
+       safe_name = php_addslashes(zstr_name, 1);
+#else
+       safe_name = php_addslashes(zstr_name);
+       zend_string_release_ex(zstr_name, 0);
+#endif
 
        BOUNDARY_OPEN(body);
        php_http_message_body_appendf(
@@ -284,9 +289,14 @@ ZEND_RESULT_CODE php_http_message_body_add_form_file(php_http_message_body_t *bo
 {
        size_t path_len = strlen(path);
        char *path_dup = estrndup(path, path_len);
-       zend_string *base_name, *safe_name = zend_string_init(name, strlen(name), 0);
-
-       safe_name = php_addslashes(safe_name, 1);
+       zend_string *base_name, *safe_name, *zstr_name = zend_string_init(name, strlen(name), 0);
+
+#if PHP_VERSION_ID < 70300
+       safe_name = php_addslashes(zstr_name, 1);
+#else
+       safe_name = php_addslashes(zstr_name);
+       zend_string_release_ex(zstr_name, 0);
+#endif
        base_name = php_basename(path_dup, path_len, NULL, 0);
 
        BOUNDARY_OPEN(body);
@@ -339,8 +349,8 @@ static ZEND_RESULT_CODE add_recursive_fields(php_http_message_body_t *body, cons
        zval *val;
        php_http_arrkey_t key;
 
-       if (!ZEND_HASH_GET_APPLY_COUNT(fields)) {
-               ZEND_HASH_INC_APPLY_COUNT(fields);
+       if (!HT_IS_RECURSIVE(fields)) {
+               HT_PROTECT_RECURSION(fields);
                ZEND_HASH_FOREACH_KEY_VAL_IND(fields, key.h, key.key, val)
                {
                        char *str = format_key(&key, name);
@@ -348,18 +358,18 @@ static ZEND_RESULT_CODE add_recursive_fields(php_http_message_body_t *body, cons
                        if (Z_TYPE_P(val) != IS_ARRAY && Z_TYPE_P(val) != IS_OBJECT) {
                                if (SUCCESS != add_recursive_field_value(body, str, val)) {
                                        efree(str);
-                                       ZEND_HASH_DEC_APPLY_COUNT(fields);
+                                       HT_UNPROTECT_RECURSION(fields);
                                        return FAILURE;
                                }
                        } else if (SUCCESS != add_recursive_fields(body, str, HASH_OF(val))) {
                                efree(str);
-                               ZEND_HASH_DEC_APPLY_COUNT(fields);
+                               HT_UNPROTECT_RECURSION(fields);
                                return FAILURE;
                        }
                        efree(str);
                }
                ZEND_HASH_FOREACH_END();
-               ZEND_HASH_DEC_APPLY_COUNT(fields);
+               HT_UNPROTECT_RECURSION(fields);
        }
 
        return SUCCESS;
@@ -377,8 +387,8 @@ static ZEND_RESULT_CODE add_recursive_files(php_http_message_body_t *body, const
                zval *val;
                php_http_arrkey_t key;
 
-               if (!ZEND_HASH_GET_APPLY_COUNT(files)) {
-                       ZEND_HASH_INC_APPLY_COUNT(files);
+               if (!HT_IS_RECURSIVE(files)) {
+                       HT_PROTECT_RECURSION(files);
                        ZEND_HASH_FOREACH_KEY_VAL_IND(files, key.h, key.key, val)
                        {
                                if (Z_TYPE_P(val) == IS_ARRAY || Z_TYPE_P(val) == IS_OBJECT) {
@@ -386,14 +396,14 @@ static ZEND_RESULT_CODE add_recursive_files(php_http_message_body_t *body, const
 
                                        if (SUCCESS != add_recursive_files(body, str, HASH_OF(val))) {
                                                efree(str);
-                                               ZEND_HASH_DEC_APPLY_COUNT(files);
+                                               HT_UNPROTECT_RECURSION(files);
                                                return FAILURE;
                                        }
                                        efree(str);
                                }
                        }
                        ZEND_HASH_FOREACH_END();
-                       ZEND_HASH_DEC_APPLY_COUNT(files);
+                       HT_UNPROTECT_RECURSION(files);
                }
                return SUCCESS;
        } else {
@@ -570,8 +580,6 @@ php_http_message_body_object_t *php_http_message_body_object_new_ex(zend_class_e
 
        if (body) {
                o->body = body;
-               php_stream_to_zval(php_http_message_body_stream(o->body), o->gc);
-
        }
 
        o->zo.handlers = &php_http_message_body_object_handlers;
@@ -581,7 +589,7 @@ php_http_message_body_object_t *php_http_message_body_object_new_ex(zend_class_e
 
 zend_object *php_http_message_body_object_clone(zval *object)
 {
-       php_http_message_body_object_t *new_obj = NULL;
+       php_http_message_body_object_t *new_obj;
        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);
 
@@ -597,12 +605,18 @@ static HashTable *php_http_message_body_object_get_gc(zval *object, zval **table
        HashTable *props = Z_OBJPROP_P(object);
        uint32_t count = zend_hash_num_elements(props);
 
-       *n = 1;
+       obj->gc = erealloc(obj->gc, (1 + count) * sizeof(zval));
+
+       if (php_http_message_body_stream(obj->body)) {
+               *n = 1;
+               php_stream_to_zval(php_http_message_body_stream(obj->body), obj->gc);
+       } else {
+               *n = 0;
+       }
+
        if (count) {
                zval *val;
 
-               obj->gc = erealloc(obj->gc, (*n + count) * sizeof(zval));
-
                ZEND_HASH_FOREACH_VAL(props, val)
                {
                        ZVAL_COPY_VALUE(&obj->gc[(*n)++], val);
@@ -893,7 +907,7 @@ PHP_METHOD(HttpMessageBody, stat)
 }
 
 static zend_function_entry php_http_message_body_methods[] = {
-       PHP_ME(HttpMessageBody, __construct,  ai_HttpMessageBody___construct,  ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+       PHP_ME(HttpMessageBody, __construct,  ai_HttpMessageBody___construct,  ZEND_ACC_PUBLIC)
        PHP_ME(HttpMessageBody, __toString,   ai_HttpMessageBody___toString,   ZEND_ACC_PUBLIC)
        PHP_MALIAS(HttpMessageBody, toString, __toString, ai_HttpMessageBody___toString, ZEND_ACC_PUBLIC)
        PHP_MALIAS(HttpMessageBody, serialize, __toString, ai_HttpMessageBody___toString, ZEND_ACC_PUBLIC)
index db935d9..49a6013 100644 (file)
@@ -57,41 +57,16 @@ php_http_message_parser_t *php_http_message_parser_init(php_http_message_parser_
        return parser;
 }
 
-php_http_message_parser_state_t php_http_message_parser_state_push(php_http_message_parser_t *parser, unsigned argc, ...)
-{
-       php_http_message_parser_state_t state = PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE;
-       va_list va_args;
-       unsigned i;
-
-       if (argc > 0) {
-               /* short circuit */
-               ZEND_PTR_STACK_RESIZE_IF_NEEDED((&parser->stack), argc);
-
-               va_start(va_args, argc);
-               for (i = 0; i < argc; ++i) {
-                       state  = va_arg(va_args, php_http_message_parser_state_t);
-                       zend_ptr_stack_push(&parser->stack, (void *) state);
-               }
-               va_end(va_args);
-       }
-
-       return state;
-}
-
+#define php_http_message_parser_state_push(parser, state) zend_ptr_stack_push(&(parser)->stack, (void *) (state)), (state)
+#define php_http_message_parser_state_pop(parser) ((parser)->stack.top \
+               ? (php_http_message_parser_state_t) zend_ptr_stack_pop(&parser->stack) \
+               : PHP_HTTP_MESSAGE_PARSER_STATE_START)
+#define php_http_message_parser_state_is_ex(parser) ((parser)->stack.top \
+               ? (php_http_message_parser_state_t) (parser)->stack.elements[(parser)->stack.top - 1] \
+               : PHP_HTTP_MESSAGE_PARSER_STATE_START)
 php_http_message_parser_state_t php_http_message_parser_state_is(php_http_message_parser_t *parser)
 {
-       if (parser->stack.top) {
-               return (php_http_message_parser_state_t) parser->stack.elements[parser->stack.top - 1];
-       }
-       return PHP_HTTP_MESSAGE_PARSER_STATE_START;
-}
-
-php_http_message_parser_state_t php_http_message_parser_state_pop(php_http_message_parser_t *parser)
-{
-       if (parser->stack.top) {
-               return (php_http_message_parser_state_t) zend_ptr_stack_pop(&parser->stack);
-       }
-       return PHP_HTTP_MESSAGE_PARSER_STATE_START;
+       return php_http_message_parser_state_is_ex(parser);
 }
 
 void php_http_message_parser_dtor(php_http_message_parser_t *parser)
@@ -188,7 +163,7 @@ php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_me
 
                        case PHP_HTTP_MESSAGE_PARSER_STATE_DONE:
                        case PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE:
-                               return php_http_message_parser_state_is(parser);
+                               return php_http_message_parser_state_is_ex(parser);
                }
 
                if (justread) {
@@ -210,7 +185,7 @@ php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_p
        size_t len = 0;
        size_t cut = 0;
 
-       while (buffer->used || !php_http_message_parser_states[php_http_message_parser_state_is(parser)].need_data) {
+       while (buffer->used || !php_http_message_parser_states[php_http_message_parser_state_is_ex(parser)].need_data) {
 #if DBG_PARSER
                fprintf(stderr, "#MP: %s (f: %u, t:%d, l:%zu)\n", 
                        php_http_message_parser_state_name(php_http_message_parser_state_is(parser)),
@@ -224,7 +199,7 @@ php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_p
                switch (php_http_message_parser_state_pop(parser))
                {
                        case PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE:
-                               return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE);
+                               return php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE);
 
                        case PHP_HTTP_MESSAGE_PARSER_STATE_START:
                        {
@@ -237,7 +212,7 @@ php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_p
                                php_http_buffer_cut(buffer, 0, ptr - buffer->data);
 
                                if (buffer->used) {
-                                       php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER);
+                                       php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER);
                                }
                                break;
                        }
@@ -251,14 +226,14 @@ php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_p
                                                return PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE;
 
                                        case PHP_HTTP_HEADER_PARSER_STATE_DONE:
-                                               php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE);
+                                               php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE);
                                                break;
 
                                        default:
                                                if (buffer->used || !(flags & PHP_HTTP_MESSAGE_PARSER_CLEANUP)) {
-                                                       return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER);
+                                                       return php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER);
                                                } else {
-                                                       php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE);
+                                                       php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE);
                                                }
                                }
                                break;
@@ -313,7 +288,7 @@ php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_p
 
                                        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);
+                                               php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
                                                break;
                                        }
                                        zend_string_release(con);
@@ -339,11 +314,11 @@ php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_p
                                }
 
                                if ((flags & PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES)) {
-                                       php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB);
+                                       php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB);
                                } else {
                                        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);
+                                               php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED);
                                                break;
                                        }
 
@@ -368,7 +343,11 @@ php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_p
 
                                                                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);
+                                                                       if (parser->body_length) {
+                                                                               php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
+                                                                       } else {
+                                                                               php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE);
+                                                                       }
                                                                        zend_string_release(content_range);
                                                                        break;
                                                                }
@@ -380,14 +359,18 @@ php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_p
 
                                        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);
+                                               if (parser->body_length) {
+                                                       php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
+                                               } else {
+                                                       php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE);
+                                               }
                                                break;
                                        }
 
                                        if ((*message)->type == PHP_HTTP_REQUEST) {
-                                               php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
+                                               php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
                                        } else {
-                                               php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB);
+                                               php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB);
                                        }
                                }
                                break;
@@ -401,7 +384,7 @@ php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_p
                                                size_t dec_len;
 
                                                if (SUCCESS != php_http_encoding_stream_update(parser->inflate, str, len, &dec_str, &dec_len)) {
-                                                       return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE);
+                                                       return php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE);
                                                }
 
                                                if (str != buffer->data) {
@@ -434,7 +417,8 @@ php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_p
                                len = buffer->used;
                                cut = len;
 
-                               php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
+                               php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE);
+                               php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
                                break;
                        }
 
@@ -446,7 +430,12 @@ php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_p
 
                                parser->body_length -= len;
 
-                               php_http_message_parser_state_push(parser, 2, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
+                               if (parser->body_length) {
+                                       php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
+                               } else {
+                                       php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE);
+                               }
+                               php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
                                break;
                        }
 
@@ -472,24 +461,26 @@ php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_p
 
                                if (php_http_encoding_stream_done(parser->dechunk)) {
                                        cut = buffer->used - PHP_HTTP_BUFFER(parser->dechunk->ctx)->used;
-                                       php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
+                                       php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE);
+                                       php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
                                } else {
                                        cut = buffer->used;
-                                       php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
+                                       php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED);
+                                       php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
                                }
                                break;
                        }
 
                        case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:
                        {
-                               php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
+                               php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
 
                                if (parser->dechunk && parser->dechunk->ctx) {
                                        char *dec_str = NULL;
                                        size_t dec_len;
 
                                        if (SUCCESS != php_http_encoding_stream_finish(parser->dechunk, &dec_str, &dec_len)) {
-                                               return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE);
+                                               return php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE);
                                        }
                                        php_http_encoding_stream_dtor(parser->dechunk);
 
@@ -497,7 +488,8 @@ php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_p
                                                str = dec_str;
                                                len = dec_len;
                                                cut = 0;
-                                               php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
+                                               php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL);
+                                               php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
                                        }
                                }
 
@@ -531,7 +523,7 @@ php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_p
                }
        }
 
-       return php_http_message_parser_state_is(parser);
+       return php_http_message_parser_state_is_ex(parser);
 }
 
 static zend_class_entry *php_http_message_parser_class_entry;
@@ -584,7 +576,7 @@ static PHP_METHOD(HttpMessageParser, getState)
 
        zend_parse_parameters_none();
        /* always return the real state */
-       RETVAL_LONG(php_http_message_parser_state_is(parser_obj->parser));
+       RETVAL_LONG(php_http_message_parser_state_is_ex(parser_obj->parser));
 }
 
 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_parse, 0, 0, 3)
index 9b6174d..edc6c2b 100644 (file)
@@ -46,9 +46,7 @@ typedef struct php_http_message_parser {
 } php_http_message_parser_t;
 
 PHP_HTTP_API php_http_message_parser_t *php_http_message_parser_init(php_http_message_parser_t *parser);
-PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_state_push(php_http_message_parser_t *parser, unsigned argc, ...);
 PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_state_is(php_http_message_parser_t *parser);
-PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_state_pop(php_http_message_parser_t *parser);
 PHP_HTTP_API void php_http_message_parser_dtor(php_http_message_parser_t *parser);
 PHP_HTTP_API void php_http_message_parser_free(php_http_message_parser_t **parser);
 PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_parser_t *parser, php_http_buffer_t *buffer, unsigned flags, php_http_message_t **message);
index 36e3490..f0cc25f 100644 (file)
@@ -50,11 +50,11 @@ int php_http_match(const char *haystack_str, const char *needle_str, int flags)
 {
        int result = 0;
 
-       if (!haystack_str || !needle_str) {
+       if (UNEXPECTED(!haystack_str || !needle_str)) {
                return result;
        }
 
-       if (flags & PHP_HTTP_MATCH_FULL) {
+       if (UNEXPECTED(flags & PHP_HTTP_MATCH_FULL)) {
                if (flags & PHP_HTTP_MATCH_CASE) {
                        result = !strcmp(haystack_str, needle_str);
                } else {
@@ -64,7 +64,7 @@ int php_http_match(const char *haystack_str, const char *needle_str, int flags)
                const char *found;
                char *haystack = estrdup(haystack_str), *needle = estrdup(needle_str);
 
-               if (flags & PHP_HTTP_MATCH_CASE) {
+               if (UNEXPECTED(flags & PHP_HTTP_MATCH_CASE)) {
                        found = zend_memnstr(haystack, needle, strlen(needle), haystack+strlen(haystack));
                } else {
                        found = php_stristr(haystack, needle, strlen(haystack), strlen(needle));
@@ -73,7 +73,7 @@ int php_http_match(const char *haystack_str, const char *needle_str, int flags)
                if (found) {
                        if (!(flags & PHP_HTTP_MATCH_WORD)
                        ||      (       (found == haystack || !PHP_HTTP_IS_CTYPE(alnum, *(found - 1)))
-                               &&      (!*(found + strlen(needle)) || !PHP_HTTP_IS_CTYPE(alnum, *(found + strlen(needle))))
+                               &&      EXPECTED(!*(found + strlen(needle)) || !PHP_HTTP_IS_CTYPE(alnum, *(found + strlen(needle))))
                                )
                        ) {
                                result = 1;
@@ -87,27 +87,37 @@ int php_http_match(const char *haystack_str, const char *needle_str, int flags)
        return result;
 }
 
-char *php_http_pretty_key(register char *key, size_t key_len, zend_bool uctitle, zend_bool xhyphen)
+char *php_http_pretty_key(char *key, size_t key_len, zend_bool uctitle, zend_bool xhyphen)
 {
-       size_t i = 1;
+       size_t i;
        int wasalpha;
 
        if (key && key_len) {
-               if ((wasalpha = PHP_HTTP_IS_CTYPE(alpha, key[0]))) {
-                       key[0] = (char) (uctitle ? PHP_HTTP_TO_CTYPE(upper, key[0]) : PHP_HTTP_TO_CTYPE(lower, key[0]));
+               if (EXPECTED(wasalpha = PHP_HTTP_IS_CTYPE(alpha, key[0]))) {
+                       if (EXPECTED(uctitle)) {
+                               key[0] = PHP_HTTP_TO_CTYPE(upper, key[0]);
+                       } else {
+                               key[0] = PHP_HTTP_TO_CTYPE(lower, key[0]);
+                       }
                }
-               PHP_HTTP_DUFF(key_len,
-                       if (PHP_HTTP_IS_CTYPE(alpha, key[i])) {
-                               key[i] = (char) (((!wasalpha) && uctitle) ? PHP_HTTP_TO_CTYPE(upper, key[i]) : PHP_HTTP_TO_CTYPE(lower, key[i]));
-                               wasalpha = 1;
+               for (i = 1; i < key_len; ++i) {
+                       if (EXPECTED(PHP_HTTP_IS_CTYPE(alpha, key[i]))) {
+                               if (EXPECTED(wasalpha)) {
+                                       key[i] = PHP_HTTP_TO_CTYPE(lower, key[i]);
+                               } else if (EXPECTED(uctitle)) {
+                                       key[i] = PHP_HTTP_TO_CTYPE(upper, key[i]);
+                                       wasalpha = 1;
+                               } else {
+                                       key[i] = PHP_HTTP_TO_CTYPE(lower, key[i]);
+                                       wasalpha = 1;
+                               }
                        } else {
-                               if (xhyphen && (key[i] == '_')) {
+                               if (EXPECTED(xhyphen && (key[i] == '_'))) {
                                        key[i] = '-';
                                }
                                wasalpha = 0;
                        }
-                       ++i;
-               );
+               }
        }
        return key;
 }
@@ -190,7 +200,7 @@ int php_http_array_apply_append_func(zval *value, int num_args, va_list args, ze
                if (flags & ARRAY_JOIN_STRINGIFY) {
                        convert_to_string_ex(value);
                }
-               Z_ADDREF_P(value);
+               Z_TRY_ADDREF_P(value);
 
                if (data) {
                        if (Z_TYPE_P(data) != IS_ARRAY) {
index ae3359c..35de35e 100644 (file)
@@ -67,47 +67,7 @@ char *php_http_pretty_key(register char *key, size_t key_len, zend_bool uctitle,
 size_t php_http_boundary(char *buf, size_t len);
 int php_http_select_str(const char *cmp, int argc, ...);
 
-/* See "A Reusable Duff Device" By Ralf Holly, August 01, 2005 */
-#define PHP_HTTP_DUFF_BREAK() times_=1
-#define PHP_HTTP_DUFF(c, a) do { \
-       size_t count_ = (c); \
-       size_t times_ = (count_ + 7) >> 3; \
-       switch (count_ & 7){ \
-               case 0: do { \
-                       a; \
-               case 7: \
-                       a; \
-               case 6: \
-                       a; \
-               case 5: \
-                       a; \
-               case 4: \
-                       a; \
-               case 3: \
-                       a; \
-               case 2: \
-                       a; \
-               case 1: \
-                       a; \
-                                       } while (--times_ > 0); \
-       } \
-} while (0)
-
-static inline const char *php_http_locate_str(register const char *h, size_t h_len, const char *n, size_t n_len)
-{
-       if (!n_len || !h_len || h_len < n_len) {
-               return NULL;
-       }
-
-       PHP_HTTP_DUFF(h_len - n_len + 1,
-               if (*h == *n && !strncmp(h + 1, n + 1, n_len - 1)) {
-                       return h;
-               }
-               ++h;
-       );
-
-       return NULL;
-}
+#define php_http_locate_str(h, h_len, n, n_len) zend_memnstr((h), (n), (n_len), (h)+(h_len))
 
 static inline const char *php_http_locate_eol(const char *line, int *eol_len)
 {
@@ -123,16 +83,14 @@ static inline const char *php_http_locate_bin_eol(const char *bin, size_t len, i
 {
        register const char *eol = bin;
 
-       if (len > 0) {
-               PHP_HTTP_DUFF(len,
-                       if (*eol == '\r' || *eol == '\n') {
-                               if (eol_len) {
-                                       *eol_len = ((eol[0] == '\r' && eol[1] == '\n') ? 2 : 1);
-                               }
-                               return eol;
+       while (len--) {
+               if (UNEXPECTED(*eol == '\r' || *eol == '\n')) {
+                       if (EXPECTED(eol_len)) {
+                               *eol_len = (EXPECTED(eol[0] == '\r' && eol[1] == '\n') ? 2 : 1);
                        }
-                       ++eol;
-               );
+                       return eol;
+               }
+               ++eol;
        }
        return NULL;
 }
@@ -144,6 +102,32 @@ static inline const char *php_http_locate_bin_eol(const char *bin, size_t len, i
 #      define HASH_OF(p) ((HashTable*)(Z_TYPE_P(p)==IS_ARRAY ? Z_ARRVAL_P(p) : ((Z_TYPE_P(p)==IS_OBJECT ? Z_OBJ_HT_P(p)->get_properties((p)) : NULL))))
 #endif
 
+#ifndef GC_SET_REFCOUNT
+#      define GC_SET_REFCOUNT(gc, rc) GC_REFCOUNT(gc) = rc
+#endif
+#ifndef GC_ADDREF
+#      define GC_ADDREF(gc) ++GC_REFCOUNT(gc)
+#endif
+#ifndef GC_DELREF
+#      define GC_DELREF(gc) --GC_REFCOUNT(gc)
+#endif
+
+#ifdef ZEND_HASH_GET_APPLY_COUNT
+#      define HT_IS_RECURSIVE(ht) (ZEND_HASH_GET_APPLY_COUNT(ht) > 0)
+#else
+#      define HT_IS_RECURSIVE(ht) GC_IS_RECURSIVE(ht)
+#endif
+#ifdef ZEND_HASH_INC_APPLY_COUNT
+#      define HT_PROTECT_RECURSION(ht) ZEND_HASH_INC_APPLY_COUNT(ht)
+#else
+#      define HT_PROTECT_RECURSION(ht) GC_PROTECT_RECURSION(ht)
+#endif
+#ifdef ZEND_HASH_DEC_APPLY_COUNT
+#      define HT_UNPROTECT_RECURSION(ht) ZEND_HASH_DEC_APPLY_COUNT(ht)
+#else
+#      define HT_UNPROTECT_RECURSION(ht) GC_UNPROTECT_RECURSION(ht)
+#endif
+
 static inline void *PHP_HTTP_OBJ(zend_object *zo, zval *zv)
 {
        if (!zo) {
@@ -161,7 +145,7 @@ static inline zend_string *php_http_cs2zs(char *s, size_t l)
        str->len = l;
        str->h = 0;
 
-       GC_REFCOUNT(str) = 1;
+       GC_SET_REFCOUNT(str, 1);
        GC_TYPE_INFO(str) = IS_STRING;
 
        return str;
index e42998e..2d09860 100644 (file)
@@ -66,7 +66,9 @@ php_http_object_method_t *php_http_object_method_init(php_http_object_method_t *
 
        cb->fci.size = sizeof(cb->fci);
        ZVAL_STRINGL(&cb->fci.function_name, method_str, method_len);
+#if PHP_VERSION_ID < 70300
        cb->fcc.initialized = 1;
+#endif
        cb->fcc.calling_scope = cb->fcc.called_scope = Z_OBJCE_P(zobject);
        cb->fcc.function_handler = Z_OBJ_HT_P(zobject)->get_method(&Z_OBJ_P(zobject), Z_STR(cb->fci.function_name), NULL);
 
index d4be512..4cb6ed3 100644 (file)
@@ -16,7 +16,7 @@ static void php_http_options_hash_dtor(zval *pData)
 {
        php_http_option_t *opt = Z_PTR_P(pData);
 
-       zval_ptr_dtor(&opt->defval);
+       zval_internal_dtor(&opt->defval);
        zend_hash_destroy(&opt->suboptions.options);
        zend_string_release(opt->name);
        pefree(opt, opt->persistent);
index 9b40cfe..92c1441 100644 (file)
@@ -67,7 +67,11 @@ static inline zend_string *quote_string(zend_string *zs, zend_bool force)
 {
        size_t len = (zs)->len;
 
+#if PHP_VERSION_ID < 70300
        zs = php_addcslashes(zs, 0, ZEND_STRL("\0..\37\173\\\""));
+#else
+       zs = php_addcslashes(zs, ZEND_STRL("\0..\37\173\\\""));
+#endif
 
        if (force || len != (zs)->len || strpbrk((zs)->val, "()<>@,;:\"[]?={} ")) {
                int len = (zs)->len + 2;
@@ -136,12 +140,11 @@ static inline void prepare_urlencoded(zval *zv)
 
 static void sanitize_dimension(zval *zv)
 {
-       zval arr, tmp, *cur = NULL;
+       zval arr, tmp, *cur = &arr;
        char *var = NULL, *ptr = Z_STRVAL_P(zv), *end = Z_STRVAL_P(zv) + Z_STRLEN_P(zv);
        long level = 0;
 
        array_init(&arr);
-       cur = &arr;
 
        while (ptr < end) {
                if (!var) {
@@ -200,8 +203,8 @@ static void prepare_dimension(php_http_buffer_t *buf, php_http_buffer_t *keybuf,
        zval *val;
        php_http_buffer_t prefix;
 
-       if (!ZEND_HASH_GET_APPLY_COUNT(ht)) {
-               ZEND_HASH_INC_APPLY_COUNT(ht);
+       if (!HT_IS_RECURSIVE(ht)) {
+               HT_PROTECT_RECURSION(ht);
                php_http_buffer_init(&prefix);
                php_http_buffer_append(&prefix, keybuf->data, keybuf->used);
 
@@ -235,7 +238,7 @@ static void prepare_dimension(php_http_buffer_t *buf, php_http_buffer_t *keybuf,
                        php_http_buffer_cut(&prefix, keybuf->used, prefix.used - keybuf->used);
                }
                ZEND_HASH_FOREACH_END();
-               ZEND_HASH_DEC_APPLY_COUNT(ht);
+               HT_UNPROTECT_RECURSION(ht);
 
                php_http_buffer_dtor(&prefix);
        }
@@ -1287,7 +1290,7 @@ PHP_METHOD(HttpParams, offsetSet)
 }
 
 static zend_function_entry php_http_params_methods[] = {
-       PHP_ME(HttpParams, __construct,   ai_HttpParams___construct,   ZEND_ACC_PUBLIC|ZEND_ACC_CTOR|ZEND_ACC_FINAL)
+       PHP_ME(HttpParams, __construct,   ai_HttpParams___construct,   ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
 
        PHP_ME(HttpParams, toArray,       ai_HttpParams_toArray,       ZEND_ACC_PUBLIC)
        PHP_ME(HttpParams, toString,      ai_HttpParams_toString,      ZEND_ACC_PUBLIC)
index 681d009..a302bbd 100644 (file)
@@ -385,8 +385,6 @@ PHP_METHOD(HttpQueryString, getGlobalInstance)
 
                ZVAL_MAKE_REF(_GET);
                zend_update_property(php_http_querystring_class_entry, return_value, ZEND_STRL("queryArray"), _GET);
-
-               zend_update_static_property(php_http_querystring_class_entry, ZEND_STRL("instance"), return_value);
        } else {
                php_http_throw(unexpected_val, "Could not acquire reference to superglobal GET array", NULL);
        }
@@ -682,7 +680,7 @@ PHP_METHOD(HttpQueryString, offsetUnset)
 }
 
 static zend_function_entry php_http_querystring_methods[] = {
-       PHP_ME(HttpQueryString, __construct, ai_HttpQueryString___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR|ZEND_ACC_FINAL)
+       PHP_ME(HttpQueryString, __construct, ai_HttpQueryString___construct, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
 
        PHP_ME(HttpQueryString, toArray, ai_HttpQueryString_toArray, ZEND_ACC_PUBLIC)
        PHP_ME(HttpQueryString, toString, ai_HttpQueryString_toString, ZEND_ACC_PUBLIC)
index 029e6a8..2055df7 100644 (file)
 
 #include "php_http_api.h"
 
-#if PHP_HTTP_HAVE_LIBIDN2
-#      include <idn2.h>
-#endif
 #if PHP_HTTP_HAVE_LIBIDN
 #      include <idna.h>
 #endif
+#if PHP_HTTP_HAVE_LIBIDN2
+#      include <idn2.h>
+#endif
 #if PHP_HTTP_HAVE_LIBICU
+#      include <unicode/uchar.h>
 #      include <unicode/uidna.h>
 #endif
 #if PHP_HTTP_HAVE_LIBIDNKIT || PHP_HTTP_HAVE_LIBIDNKIT2
@@ -40,7 +41,7 @@
 static inline char *localhostname(void)
 {
        char hostname[1024] = {0};
-       
+
 #if PHP_WIN32
        if (SUCCESS == gethostname(hostname, lenof(hostname))) {
                return estrdup(hostname);
@@ -188,9 +189,9 @@ php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_u
        if (!(flags & PHP_HTTP_URL_STRIP_PASS)) {
                url_copy(pass);
        }
-       
+
        url_copy(host);
-       
+
        if (!(flags & PHP_HTTP_URL_STRIP_PORT)) {
                url(buf)->port = url_isset(new_url, port) ? new_url->port : ((old_url) ? old_url->port : 0);
        }
@@ -199,14 +200,14 @@ php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_u
                if ((flags & PHP_HTTP_URL_JOIN_PATH) && url_isset(old_url, path) && url_isset(new_url, path) && *new_url->path != '/') {
                        size_t old_path_len = strlen(old_url->path), new_path_len = strlen(new_url->path);
                        char *path = ecalloc(1, old_path_len + new_path_len + 1 + 1);
-                       
+
                        strcat(path, old_url->path);
                        if (path[old_path_len - 1] != '/') {
                                php_dirname(path, old_path_len);
                                strcat(path, "/");
                        }
                        strcat(path, new_url->path);
-                       
+
                        url(buf)->path = &buf.data[buf.used];
                        if (path[0] != '/') {
                                url_append(&buf, php_http_buffer_append(&buf, "/", 1));
@@ -235,16 +236,16 @@ php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_u
        if (!(flags & PHP_HTTP_URL_STRIP_QUERY)) {
                if ((flags & PHP_HTTP_URL_JOIN_QUERY) && url_isset(new_url, query) && url_isset(old_url, query)) {
                        zval qarr, qstr;
-                       
+
                        array_init(&qarr);
-                       
+
                        ZVAL_STRING(&qstr, old_url->query);
                        php_http_querystring_update(&qarr, &qstr, NULL);
                        zval_ptr_dtor(&qstr);
                        ZVAL_STRING(&qstr, new_url->query);
                        php_http_querystring_update(&qarr, &qstr, NULL);
                        zval_ptr_dtor(&qstr);
-                       
+
                        ZVAL_NULL(&qstr);
                        php_http_querystring_update(&qarr, NULL, &qstr);
 
@@ -261,7 +262,7 @@ php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_u
        if (!(flags & PHP_HTTP_URL_STRIP_FRAGMENT)) {
                url_copy(fragment);
        }
-       
+
        /* done with copy & combine & strip */
 
        if (flags & PHP_HTTP_URL_FROM_ENV) {
@@ -274,13 +275,13 @@ php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_u
        &&      url(buf)->path
        &&      url(buf)->path[0] && url(buf)->path[1]) {
                char *ptr, *end = url(buf)->path + strlen(url(buf)->path) + 1;
-                       
+
                for (ptr = strchr(url(buf)->path, '/'); ptr; ptr = strchr(ptr, '/')) {
                        switch (ptr[1]) {
                                case '/':
                                        memmove(&ptr[1], &ptr[2], end - &ptr[2]);
                                        break;
-                                       
+
                                case '.':
                                        switch (ptr[2]) {
                                                case '\0':
@@ -328,7 +329,7 @@ php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_u
                        url(buf)->port = 0;
                }
        }
-       
+
        return url(buf);
 }
 
@@ -663,7 +664,7 @@ php_http_url_t *php_http_url_copy(const php_http_url_t *url, zend_bool persisten
        return cpy;
 }
 
-static size_t parse_mb_utf8(unsigned *wc, const char *ptr, const char *end)
+static inline size_t parse_mb_utf8(unsigned *wc, const char *ptr, const char *end)
 {
        unsigned wchar;
        size_t consumed = utf8towc(&wchar, (const unsigned char *) ptr, end - ptr);
@@ -679,7 +680,7 @@ static size_t parse_mb_utf8(unsigned *wc, const char *ptr, const char *end)
 }
 
 #if PHP_HTTP_HAVE_WCHAR
-static size_t parse_mb_loc(unsigned *wc, const char *ptr, const char *end)
+static inline size_t parse_mb_loc(unsigned *wc, const char *ptr, const char *end)
 {
        wchar_t wchar;
        size_t consumed = 0;
@@ -723,7 +724,7 @@ static const char * const parse_what[] = {
 
 static const char parse_xdigits[] = "0123456789ABCDEF";
 
-static size_t parse_mb(struct parse_state *state, parse_mb_what_t what, const char *ptr, const char *end, const char *begin, zend_bool force_silent)
+static inline size_t parse_mb(struct parse_state *state, parse_mb_what_t what, const char *ptr, const char *end, const char *begin, zend_bool force_silent)
 {
        unsigned wchar;
        size_t consumed = 0;
@@ -742,7 +743,11 @@ static size_t parse_mb(struct parse_state *state, parse_mb_what_t what, const ch
                        if (what == PARSE_HOSTINFO && (state->flags & PHP_HTTP_URL_PARSE_TOIDN)) {
                                /* idna */
                        } else if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
+#if PHP_HTTP_HAVE_LIBICU
+                               if (!u_isalnum(wchar)) {
+#else
                                if (!isualnum(wchar)) {
+#endif
                                        break;
                                }
 #if PHP_HTTP_HAVE_WCHAR
@@ -752,16 +757,17 @@ static size_t parse_mb(struct parse_state *state, parse_mb_what_t what, const ch
                                }
 #endif
                        }
-                       PHP_HTTP_DUFF(consumed, state->buffer[state->offset++] = *ptr++);
+
+                       memcpy(&state->buffer[state->offset], ptr, consumed);
+                       state->offset += consumed;
                } else {
-                       int i = 0;
-
-                       PHP_HTTP_DUFF(consumed,
-                                       state->buffer[state->offset++] = '%';
-                                       state->buffer[state->offset++] = parse_xdigits[((unsigned char) ptr[i]) >> 4];
-                                       state->buffer[state->offset++] = parse_xdigits[((unsigned char) ptr[i]) & 0xf];
-                                       ++i;
-                       );
+                       size_t i;
+
+                       for (i = 0; i < consumed; ++i) {
+                               state->buffer[state->offset++] = '%';
+                               state->buffer[state->offset++] = parse_xdigits[((unsigned char) ptr[i]) >> 4];
+                               state->buffer[state->offset++] = parse_xdigits[((unsigned char) ptr[i]) & 0xf];
+                       }
                }
 
                return consumed;
@@ -982,10 +988,10 @@ 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_2003(struct parse_state *state)
+static ZEND_RESULT_CODE parse_uidn_2003(struct parse_state *state, size_t prev_len)
 {
-       char *host_ptr = state->url.host, ebuf[64] = {0}, *error = NULL;
-       uint16_t *uhost_str, ahost_str[256], *ahost_ptr;
+       char ebuf[64] = {0}, *error = NULL;
+       uint16_t *uhost_str, ahost_str[256];
        size_t uhost_len, ahost_len;
        UErrorCode rc = U_ZERO_ERROR;
 
@@ -1019,10 +1025,11 @@ static ZEND_RESULT_CODE parse_uidn_2003(struct parse_state *state)
                goto error;
        }
 
-       ahost_ptr = ahost_str;
-       PHP_HTTP_DUFF(ahost_len, *host_ptr++ = *ahost_ptr++);
-       *host_ptr = '\0';
-       state->offset += host_ptr - state->url.host;
+       state->url.host[ahost_len] = '\0';
+       state->offset += ahost_len - prev_len;
+       while (ahost_len--) {
+               state->url.host[ahost_len] = ahost_str[ahost_len];
+       }
 
        return SUCCESS;
 
@@ -1038,9 +1045,9 @@ static ZEND_RESULT_CODE parse_uidn_2003(struct parse_state *state)
 #endif
 
 #if PHP_HTTP_HAVE_LIBICU && HAVE_UIDNA_NAMETOASCII_UTF8
-static ZEND_RESULT_CODE parse_uidn_2008(struct parse_state *state)
+static ZEND_RESULT_CODE parse_uidn_2008(struct parse_state *state, size_t prev_len)
 {
-       char *host_ptr, *error = NULL, ebuf[64] = {0};
+       char *error = NULL, ebuf[64] = {0};
        UErrorCode rc = U_ZERO_ERROR;
        UIDNAInfo info = UIDNA_INFO_INITIALIZER;
        UIDNA *uidna = uidna_openUTS46(UIDNA_ALLOW_UNASSIGNED, &rc);
@@ -1049,42 +1056,46 @@ static ZEND_RESULT_CODE parse_uidn_2008(struct parse_state *state)
                return FAILURE;
        }
 
-       host_ptr = state->url.host;
-
        if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
-               char ahost_str[256], *ahost_ptr = &ahost_str[0];
-               size_t ahost_len = uidna_nameToASCII_UTF8(uidna, host_ptr, -1, ahost_str, sizeof(ahost_str)-1, &info, &rc);
+               char ahost_str[256];
+               size_t ahost_len = uidna_nameToASCII_UTF8(uidna, state->url.host, -1, ahost_str, sizeof(ahost_str)-1, &info, &rc);
 
                if (U_FAILURE(rc) || info.errors) {
                        goto error;
                }
-               PHP_HTTP_DUFF(ahost_len, *host_ptr++ = *ahost_ptr++);
+
+               memcpy(state->url.host, ahost_str, ahost_len);
+               state->url.host[ahost_len] = '\0';
+               state->offset += ahost_len - prev_len;
+
 #if PHP_HTTP_HAVE_WCHAR
        } else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
-               uint16_t *uhost_str, whost_str[256], *whost_ptr = &whost_str[0];
+               uint16_t *uhost_str, whost_str[256];
                size_t uhost_len, whost_len;
 
-               if (SUCCESS != to_utf16(parse_mb_loc, host_ptr, &uhost_str, &uhost_len)) {
+               if (SUCCESS != to_utf16(parse_mb_loc, state->url.host, &uhost_str, &uhost_len)) {
                        error = "could not convert to UTF-16";
                        goto error;
                }
 
                whost_len = uidna_nameToASCII(uidna, uhost_str, uhost_len, whost_str, sizeof(whost_str)-1, &info, &rc);
-               whost_ptr = whost_str;
                efree(uhost_str);
+
                if (U_FAILURE(rc) || info.errors) {
                        goto error;
                }
-               PHP_HTTP_DUFF(whost_len, *host_ptr++ = *whost_ptr++);
+
+               state->url.host[whost_len] = '\0';
+               state->offset += whost_len - prev_len;
+               while (whost_len--) {
+                       state->url.host[whost_len] = whost_str[whost_len];
+               }
 #endif
        } else {
                error = "codepage not specified";
                goto error;
        }
 
-       *host_ptr = '\0';
-       state->offset += host_ptr - state->url.host;
-
        uidna_close(uidna);
        return SUCCESS;
 
@@ -1111,7 +1122,7 @@ static ZEND_RESULT_CODE parse_uidn_2008(struct parse_state *state)
 #      if __GNUC__
 __attribute__ ((unused))
 #      endif
-static ZEND_RESULT_CODE parse_kidn(struct parse_state *state)
+static ZEND_RESULT_CODE parse_kidn(struct parse_state *state, size_t prev_len)
 {
        idn_result_t rc;
 #if PHP_HTTP_HAVE_LIBIDNKIT
@@ -1119,7 +1130,7 @@ static ZEND_RESULT_CODE parse_kidn(struct parse_state *state)
 #elif PHP_HTTP_HAVE_LIBIDNKIT2
        int actions = IDN_MAP|IDN_ASCLOWER|IDN_RTCONV|IDN_PROHCHECK|IDN_NFCCHECK|IDN_PREFCHECK|IDN_COMBCHECK|IDN_CTXOLITECHECK|IDN_BIDICHECK|IDN_LOCALCHECK|IDN_IDNCONV|IDN_LENCHECK|IDN_RTCHECK;
 #endif
-       char ahost_str[256] = {0}, *ahost_ptr = &ahost_str[0], *host_ptr = state->url.host;
+       char ahost_str[256] = {0};
 
        if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
 #if PHP_HTTP_HAVE_LIBIDNKIT
@@ -1131,10 +1142,10 @@ static ZEND_RESULT_CODE parse_kidn(struct parse_state *state)
 
        rc = idn_encodename(actions, state->url.host, ahost_str, 256);
        if (rc == idn_success) {
-               PHP_HTTP_DUFF(strlen(ahost_str), *host_ptr++ = *ahost_ptr++);
+               size_t ahost_len = strlen(ahost_str);
 
-               *host_ptr = '\0';
-               state->offset += host_ptr - state->url.host;
+               memcpy(state->url.host, ahost_str, ahost_len + 1);
+               state->offset += ahost_len - prev_len;
 
                return SUCCESS;
        } else {
@@ -1145,11 +1156,11 @@ static ZEND_RESULT_CODE parse_kidn(struct parse_state *state)
 #endif
 
 #if 0 && PHP_WIN32
-static ZEND_RESULT_CODE parse_widn_2003(struct parse_state *state)
+static ZEND_RESULT_CODE parse_widn_2003(struct parse_state *state, size_t prev_len)
 {
        char *host_ptr;
-       uint16_t *uhost_str, ahost_str[256], *ahost_ptr;
-       size_t uhost_len;
+       uint16_t *uhost_str, ahost_str[256];
+       size_t uhost_len, ahost_len;
 
        if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
                if (SUCCESS != to_utf16(parse_mb_utf8, state->url.host, &uhost_str, &uhost_len)) {
@@ -1175,12 +1186,12 @@ static ZEND_RESULT_CODE parse_widn_2003(struct parse_state *state)
        }
 
        efree(uhost_str);
-       host_ptr = state->url.host;
-       ahost_ptr = ahost_str;
-       PHP_HTTP_DUFF(wcslen(ahost_str), *host_ptr++ = *ahost_ptr++);
-
-       *host_ptr = '\0';
-       state->offset += host_ptr - state->url.host;
+       ahost_len = wcslen(ahost_str);
+       state->url.host[ahost_len] = '\0';
+       state->offset += ahost_len - prev_len;
+       while (ahost_len--) {
+               state->url.host[ahost_len] = ahost_str[ahost_len];
+       }
 
        return SUCCESS;
 }
@@ -1195,11 +1206,11 @@ static ZEND_RESULT_CODE parse_idna(struct parse_state *state, size_t len)
 #      endif
        ) {
 #if PHP_HTTP_HAVE_LIBICU && HAVE_UIDNA_NAMETOASCII_UTF8
-               return parse_uidn_2008(state);
+               return parse_uidn_2008(state, len);
 #elif PHP_HTTP_HAVE_LIBIDN2
                return parse_gidn_2008(state, len);
 #elif PHP_HTTP_HAVE_LIBIDNKIT2
-               return parse_kidn(state);
+               return parse_kidn(state, len);
 #endif
        }
 #endif
@@ -1211,31 +1222,31 @@ static ZEND_RESULT_CODE parse_idna(struct parse_state *state, size_t len)
 #endif
        ) {
 #if HAVE_UIDNA_IDNTOASCII
-               return parse_uidn_2003(state);
+               return parse_uidn_2003(state, len);
 #elif PHP_HTTP_HAVE_LIBIDN
                return parse_gidn_2003(state, len);
 #elif PHP_HTTP_HAVE_LIBIDNKIT
-               return parse_kidn(state);
+               return parse_kidn(state, len);
 #endif
        }
 #endif
 
 #if 0 && PHP_WIN32
-       return parse_widn_2003(state);
+       return parse_widn_2003(state, len);
 #endif
 
 #if PHP_HTTP_HAVE_LIBICU && HAVE_UIDNA_NAMETOASCII_UTF8
-               return parse_uidn_2008(state);
+               return parse_uidn_2008(state, len);
 #elif PHP_HTTP_HAVE_LIBIDN2
                return parse_gidn_2008(state, len);
 #elif PHP_HTTP_HAVE_LIBIDNKIT2
-               return parse_kidn(state);
+               return parse_kidn(state, len);
 #elif HAVE_UIDNA_IDNTOASCII
-               return parse_uidn_2003(state);
+               return parse_uidn_2003(state, len);
 #elif PHP_HTTP_HAVE_LIBIDN
                return parse_gidn_2003(state, len);
 #elif PHP_HTTP_HAVE_LIBIDNKIT
-               return parse_kidn(state);
+               return parse_kidn(state, len);
 #endif
 
        return SUCCESS;
@@ -1810,7 +1821,9 @@ php_http_url_t *php_http_url_parse(const char *str, size_t len, unsigned flags)
        state->maxlen = maxlen;
 
        if (!parse_scheme(state)) {
-               php_error_docref(NULL, E_WARNING, "Failed to parse URL scheme: '%s'", state->ptr);
+               if (!(flags & PHP_HTTP_URL_SILENT_ERRORS)) {
+                       php_error_docref(NULL, E_WARNING, "Failed to parse URL scheme: '%s'", state->ptr);
+               }
                efree(state);
                return NULL;
        }
@@ -1821,13 +1834,17 @@ php_http_url_t *php_http_url_parse(const char *str, size_t len, unsigned flags)
        }
 
        if (!parse_query(state)) {
-               php_error_docref(NULL, E_WARNING, "Failed to parse URL query: '%s'", state->ptr);
+               if (!(flags & PHP_HTTP_URL_SILENT_ERRORS)) {
+                       php_error_docref(NULL, E_WARNING, "Failed to parse URL query: '%s'", state->ptr);
+               }
                efree(state);
                return NULL;
        }
 
        if (!parse_fragment(state)) {
-               php_error_docref(NULL, E_WARNING, "Failed to parse URL fragment: '%s'", state->ptr);
+               if (!(flags & PHP_HTTP_URL_SILENT_ERRORS)) {
+                       php_error_docref(NULL, E_WARNING, "Failed to parse URL fragment: '%s'", state->ptr);
+               }
                efree(state);
                return NULL;
        }
@@ -1896,9 +1913,7 @@ PHP_METHOD(HttpUrl, __construct)
                flags |= PHP_HTTP_URL_FROM_ENV;
        }
 
-       if (flags & PHP_HTTP_URL_SILENT_ERRORS) {
-               zend_replace_error_handling(EH_SUPPRESS, NULL, &zeh);
-       } else if (flags & PHP_HTTP_URL_IGNORE_ERRORS) {
+       if (flags & (PHP_HTTP_URL_SILENT_ERRORS|PHP_HTTP_URL_IGNORE_ERRORS)) {
                zend_replace_error_handling(EH_NORMAL, NULL, &zeh);
        } else {
                zend_replace_error_handling(EH_THROW, php_http_get_exception_bad_url_class_entry(), &zeh);
@@ -1950,9 +1965,7 @@ PHP_METHOD(HttpUrl, mod)
 
        php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "z!|l", &new_url, &flags), invalid_arg, return);
 
-       if (flags & PHP_HTTP_URL_SILENT_ERRORS) {
-               zend_replace_error_handling(EH_SUPPRESS, NULL, &zeh);
-       } else if (flags & PHP_HTTP_URL_IGNORE_ERRORS) {
+       if (flags & (PHP_HTTP_URL_SILENT_ERRORS|PHP_HTTP_URL_IGNORE_ERRORS)) {
                zend_replace_error_handling(EH_NORMAL, NULL, &zeh);
        } else {
                zend_replace_error_handling(EH_THROW, php_http_get_exception_bad_url_class_entry(), &zeh);
@@ -2022,7 +2035,7 @@ PHP_METHOD(HttpUrl, toArray)
 }
 
 static zend_function_entry php_http_url_methods[] = {
-       PHP_ME(HttpUrl, __construct,  ai_HttpUrl___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+       PHP_ME(HttpUrl, __construct,  ai_HttpUrl___construct, ZEND_ACC_PUBLIC)
        PHP_ME(HttpUrl, mod,          ai_HttpUrl_mod, ZEND_ACC_PUBLIC)
        PHP_ME(HttpUrl, toString,     ai_HttpUrl_toString, ZEND_ACC_PUBLIC)
        ZEND_MALIAS(HttpUrl, __toString, toString, ai_HttpUrl_toString, ZEND_ACC_PUBLIC)
index f900803..82a58d1 100644 (file)
 #ifndef PHP_HTTP_UTF8_H
 #define PHP_HTTP_UTF8_H
 
-typedef struct utf8_range {
-       unsigned int start;
-       unsigned int end;
-       unsigned char step;
-} utf8_range_t;
-
 static const unsigned char utf8_mblen[256] = {
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
@@ -42,515 +36,234 @@ static const unsigned char utf8_mask[] = {
                0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01
 };
 
-static const utf8_range_t utf8_ranges[] = {
+typedef struct utf8_range {
+       const unsigned int start;
+       const unsigned int end;
+} utf8_range_t;
+
 /* BEGIN::UTF8TABLE */
-       {    0x0041,     0x005A, 1},
-       {    0x0061,     0x007A, 1},
-       {    0x00AA,          0, 0},
-       {    0x00B5,          0, 0},
-       {    0x00BA,          0, 0},
-       {    0x00C0,     0x00D6, 1},
-       {    0x00D8,     0x00F6, 1},
-       {    0x00F8,     0x00FF, 1},
-       {    0x0100,     0x017F, 1},
-       {    0x0180,     0x024F, 1},
-       {    0x0250,     0x02AF, 1},
-       {    0x02B0,     0x02C1, 1},
-       {    0x02C6,     0x02D1, 1},
-       {    0x02E0,     0x02E4, 1},
-       {    0x02EE,          0, 0},
-       {    0x0345,          0, 0},
-       {    0x0370,     0x0373, 1},
-       {    0x0376,     0x0377, 1},
-       {    0x037A,     0x037D, 1},
-       {    0x0386,          0, 0},
-       {    0x0388,     0x038A, 1},
-       {    0x038C,          0, 0},
-       {    0x038E,     0x03A1, 1},
-       {    0x03A3,     0x03CE, 1},
-       {    0x03D0,     0x03F5, 1},
-       {    0x03F7,     0x03FF, 1},
-       {    0x0400,     0x0481, 1},
-       {    0x048A,     0x04FF, 1},
-       {    0x0500,     0x0523, 1},
-       {    0x0531,     0x0556, 1},
-       {    0x0559,          0, 0},
-       {    0x0561,     0x0587, 1},
-       {    0x05D0,     0x05EA, 1},
-       {    0x05F0,     0x05F2, 1},
-       {    0x0621,     0x064A, 1},
-       {    0x066E,     0x066F, 1},
-       {    0x0671,     0x06D3, 1},
-       {    0x06D5,          0, 0},
-       {    0x06E5,     0x06E6, 1},
-       {    0x06EE,     0x06EF, 1},
-       {    0x06FA,     0x06FC, 1},
-       {    0x06FF,          0, 0},
-       {    0x0710,          0, 0},
-       {    0x0712,     0x072F, 1},
-       {    0x074D,     0x074F, 1},
-       {    0x0750,     0x077F, 1},
-       {    0x0780,     0x07A5, 1},
-       {    0x07B1,          0, 0},
-       {    0x07C0,     0x07EA, 1},
-       {    0x07F4,     0x07F5, 1},
-       {    0x07FA,          0, 0},
-       {    0x0901,     0x0939, 1},
-       {    0x093C,     0x094D, 1},
-       {    0x0950,     0x0954, 1},
-       {    0x0958,     0x0961, 1},
-       {    0x0962,          0, 0},
-       {    0x0963,          0, 0},
-       {    0x0972,          0, 0},
-       {    0x097B,     0x097F, 1},
-       {    0x0981,     0x0983, 1},
-       {    0x0985,     0x098C, 1},
-       {    0x098F,          0, 0},
-       {    0x0990,          0, 0},
-       {    0x0993,     0x09A8, 1},
-       {    0x09AA,     0x09B0, 1},
-       {    0x09B2,          0, 0},
-       {    0x09B6,     0x09B9, 1},
-       {    0x09BC,     0x09C4, 1},
-       {    0x09C7,          0, 0},
-       {    0x09C8,          0, 0},
-       {    0x09CB,     0x09CE, 1},
-       {    0x09D7,          0, 0},
-       {    0x09DC,          0, 0},
-       {    0x09DD,          0, 0},
-       {    0x09DF,     0x09E3, 1},
-       {    0x09F0,     0x09FA, 1},
-       {    0x0A01,     0x0A03, 1},
-       {    0x0A05,     0x0A0A, 1},
-       {    0x0A0F,          0, 0},
-       {    0x0A10,          0, 0},
-       {    0x0A13,     0x0A28, 1},
-       {    0x0A2A,     0x0A30, 1},
-       {    0x0A32,          0, 0},
-       {    0x0A33,          0, 0},
-       {    0x0A35,          0, 0},
-       {    0x0A36,          0, 0},
-       {    0x0A38,          0, 0},
-       {    0x0A39,          0, 0},
-       {    0x0A3C,          0, 0},
-       {    0x0A3E,     0x0A42, 1},
-       {    0x0A47,          0, 0},
-       {    0x0A48,          0, 0},
-       {    0x0A4B,     0x0A4D, 1},
-       {    0x0A51,          0, 0},
-       {    0x0A59,     0x0A5C, 1},
-       {    0x0A5E,          0, 0},
-       {    0x0A70,     0x0A75, 1},
-       {    0x0A81,     0x0A83, 1},
-       {    0x0A85,     0x0A8D, 1},
-       {    0x0A8F,     0x0A91, 1},
-       {    0x0A93,     0x0AA8, 1},
-       {    0x0AAA,     0x0AB0, 1},
-       {    0x0AB2,          0, 0},
-       {    0x0AB3,          0, 0},
-       {    0x0AB5,     0x0AB9, 1},
-       {    0x0ABC,     0x0AC5, 1},
-       {    0x0AC7,     0x0AC9, 1},
-       {    0x0ACB,     0x0ACD, 1},
-       {    0x0AD0,          0, 0},
-       {    0x0AE0,     0x0AE3, 1},
-       {    0x0AF1,          0, 0},
-       {    0x0B01,     0x0B03, 1},
-       {    0x0B05,     0x0B0C, 1},
-       {    0x0B0F,          0, 0},
-       {    0x0B10,          0, 0},
-       {    0x0B13,     0x0B28, 1},
-       {    0x0B2A,     0x0B30, 1},
-       {    0x0B32,          0, 0},
-       {    0x0B33,          0, 0},
-       {    0x0B35,     0x0B39, 1},
-       {    0x0B3C,     0x0B44, 1},
-       {    0x0B47,     0x0B48, 1},
-       {    0x0B4B,     0x0B4D, 1},
-       {    0x0B56,     0x0B57, 1},
-       {    0x0B5C,          0, 0},
-       {    0x0B5D,          0, 0},
-       {    0x0B5F,     0x0B63, 1},
-       {    0x0B70,          0, 0},
-       {    0x0B71,          0, 0},
-       {    0x0B82,          0, 0},
-       {    0x0B83,          0, 0},
-       {    0x0B85,     0x0B8A, 1},
-       {    0x0B8E,     0x0B90, 1},
-       {    0x0B92,     0x0B95, 1},
-       {    0x0B99,          0, 0},
-       {    0x0B9A,          0, 0},
-       {    0x0B9C,          0, 0},
-       {    0x0B9E,          0, 0},
-       {    0x0B9F,          0, 0},
-       {    0x0BA3,          0, 0},
-       {    0x0BA4,          0, 0},
-       {    0x0BA8,     0x0BAA, 1},
-       {    0x0BAE,     0x0BB9, 1},
-       {    0x0BBE,     0x0BC2, 1},
-       {    0x0BC6,     0x0BC8, 1},
-       {    0x0BCA,     0x0BCD, 1},
-       {    0x0BD0,          0, 0},
-       {    0x0BD7,          0, 0},
-       {    0x0BF0,     0x0BFA, 1},
-       {    0x0C01,     0x0C03, 1},
-       {    0x0C05,     0x0C0C, 1},
-       {    0x0C0E,     0x0C10, 1},
-       {    0x0C12,     0x0C28, 1},
-       {    0x0C2A,     0x0C33, 1},
-       {    0x0C35,     0x0C39, 1},
-       {    0x0C3D,     0x0C44, 1},
-       {    0x0C46,     0x0C48, 1},
-       {    0x0C4A,     0x0C4D, 1},
-       {    0x0C55,     0x0C56, 1},
-       {    0x0C58,     0x0C59, 1},
-       {    0x0C60,     0x0C63, 1},
-       {    0x0C82,     0x0C83, 1},
-       {    0x0C85,     0x0C8C, 1},
-       {    0x0C8E,     0x0C90, 1},
-       {    0x0C92,     0x0CA8, 1},
-       {    0x0CAA,     0x0CB3, 1},
-       {    0x0CB5,     0x0CB9, 1},
-       {    0x0CBC,     0x0CC4, 1},
-       {    0x0CC6,     0x0CC8, 1},
-       {    0x0CCA,  &nb