attempt to implement some personal standards
authorMichael Wallner <mike@php.net>
Mon, 28 Sep 2015 14:08:14 +0000 (16:08 +0200)
committerMichael Wallner <mike@php.net>
Mon, 28 Sep 2015 14:08:14 +0000 (16:08 +0200)
147 files changed:
.editorconfig [new file with mode: 0644]
.gitignore
AUTHORS [new file with mode: 0644]
BUGS [new file with mode: 0644]
CONTRIBUTING.md [new file with mode: 0644]
Exceptions.txt [deleted file]
KnownIssues.txt [deleted file]
LICENSE
Makefile.frag [new file with mode: 0644]
README.md
THANKS [new file with mode: 0644]
ThanksTo.txt [deleted file]
bench_select_vs_event.php [deleted file]
check_package-xml.php [deleted file]
config.w32
config9.m4
gen_curlinfo.php [deleted file]
gen_travis_yml.php [deleted file]
gen_utf8.php [deleted file]
package.xml
php_http.c [deleted file]
php_http_api.h [deleted file]
php_http_buffer.c [deleted file]
php_http_buffer.h [deleted file]
php_http_client.c [deleted file]
php_http_client.h [deleted file]
php_http_client_curl.c [deleted file]
php_http_client_curl.h [deleted file]
php_http_client_request.c [deleted file]
php_http_client_request.h [deleted file]
php_http_client_response.c [deleted file]
php_http_client_response.h [deleted file]
php_http_cookie.c [deleted file]
php_http_cookie.h [deleted file]
php_http_curl.c [deleted file]
php_http_curl.h [deleted file]
php_http_encoding.c [deleted file]
php_http_encoding.h [deleted file]
php_http_env.c [deleted file]
php_http_env.h [deleted file]
php_http_env_request.c [deleted file]
php_http_env_request.h [deleted file]
php_http_env_response.c [deleted file]
php_http_env_response.h [deleted file]
php_http_etag.c [deleted file]
php_http_etag.h [deleted file]
php_http_exception.c [deleted file]
php_http_exception.h [deleted file]
php_http_filter.c [deleted file]
php_http_filter.h [deleted file]
php_http_header.c [deleted file]
php_http_header.h [deleted file]
php_http_header_parser.c [deleted file]
php_http_header_parser.h [deleted file]
php_http_info.c [deleted file]
php_http_info.h [deleted file]
php_http_message.c [deleted file]
php_http_message.h [deleted file]
php_http_message_body.c [deleted file]
php_http_message_body.h [deleted file]
php_http_message_parser.c [deleted file]
php_http_message_parser.h [deleted file]
php_http_misc.c [deleted file]
php_http_misc.h [deleted file]
php_http_negotiate.c [deleted file]
php_http_negotiate.h [deleted file]
php_http_object.c [deleted file]
php_http_object.h [deleted file]
php_http_options.c [deleted file]
php_http_options.h [deleted file]
php_http_params.c [deleted file]
php_http_params.h [deleted file]
php_http_querystring.c [deleted file]
php_http_querystring.h [deleted file]
php_http_response_codes.h [deleted file]
php_http_url.c [deleted file]
php_http_url.h [deleted file]
php_http_utf8.h [deleted file]
php_http_version.c [deleted file]
php_http_version.h [deleted file]
reflection2php.php [deleted file]
scripts/bench_select_vs_event.php [new file with mode: 0644]
scripts/check_package-xml.php [new file with mode: 0755]
scripts/gen_curlinfo.php [new file with mode: 0755]
scripts/gen_stubs.php [new file with mode: 0755]
scripts/gen_travis_yml.php [new file with mode: 0755]
scripts/gen_utf8.php [new file with mode: 0755]
src/php_http.c [new file with mode: 0644]
src/php_http_api.h [new file with mode: 0644]
src/php_http_buffer.c [new file with mode: 0644]
src/php_http_buffer.h [new file with mode: 0644]
src/php_http_client.c [new file with mode: 0644]
src/php_http_client.h [new file with mode: 0644]
src/php_http_client_curl.c [new file with mode: 0644]
src/php_http_client_curl.h [new file with mode: 0644]
src/php_http_client_request.c [new file with mode: 0644]
src/php_http_client_request.h [new file with mode: 0644]
src/php_http_client_response.c [new file with mode: 0644]
src/php_http_client_response.h [new file with mode: 0644]
src/php_http_cookie.c [new file with mode: 0644]
src/php_http_cookie.h [new file with mode: 0644]
src/php_http_curl.c [new file with mode: 0644]
src/php_http_curl.h [new file with mode: 0644]
src/php_http_encoding.c [new file with mode: 0644]
src/php_http_encoding.h [new file with mode: 0644]
src/php_http_env.c [new file with mode: 0644]
src/php_http_env.h [new file with mode: 0644]
src/php_http_env_request.c [new file with mode: 0644]
src/php_http_env_request.h [new file with mode: 0644]
src/php_http_env_response.c [new file with mode: 0644]
src/php_http_env_response.h [new file with mode: 0644]
src/php_http_etag.c [new file with mode: 0644]
src/php_http_etag.h [new file with mode: 0644]
src/php_http_exception.c [new file with mode: 0644]
src/php_http_exception.h [new file with mode: 0644]
src/php_http_filter.c [new file with mode: 0644]
src/php_http_filter.h [new file with mode: 0644]
src/php_http_header.c [new file with mode: 0644]
src/php_http_header.h [new file with mode: 0644]
src/php_http_header_parser.c [new file with mode: 0644]
src/php_http_header_parser.h [new file with mode: 0644]
src/php_http_info.c [new file with mode: 0644]
src/php_http_info.h [new file with mode: 0644]
src/php_http_message.c [new file with mode: 0644]
src/php_http_message.h [new file with mode: 0644]
src/php_http_message_body.c [new file with mode: 0644]
src/php_http_message_body.h [new file with mode: 0644]
src/php_http_message_parser.c [new file with mode: 0644]
src/php_http_message_parser.h [new file with mode: 0644]
src/php_http_misc.c [new file with mode: 0644]
src/php_http_misc.h [new file with mode: 0644]
src/php_http_negotiate.c [new file with mode: 0644]
src/php_http_negotiate.h [new file with mode: 0644]
src/php_http_object.c [new file with mode: 0644]
src/php_http_object.h [new file with mode: 0644]
src/php_http_options.c [new file with mode: 0644]
src/php_http_options.h [new file with mode: 0644]
src/php_http_params.c [new file with mode: 0644]
src/php_http_params.h [new file with mode: 0644]
src/php_http_querystring.c [new file with mode: 0644]
src/php_http_querystring.h [new file with mode: 0644]
src/php_http_response_codes.h [new file with mode: 0644]
src/php_http_url.c [new file with mode: 0644]
src/php_http_url.h [new file with mode: 0644]
src/php_http_utf8.h [new file with mode: 0644]
src/php_http_version.c [new file with mode: 0644]
src/php_http_version.h [new file with mode: 0644]

diff --git a/.editorconfig b/.editorconfig
new file mode 100644 (file)
index 0000000..e0c3b0e
--- /dev/null
@@ -0,0 +1,20 @@
+; see http://editorconfig.org
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+indent_style = tab
+charset = utf-8
+trim_trailing_whitespace = true
+
+[package.xml]
+indent_style = space
+indent_size = 1
+
+[*.md]
+trim_trailing_whitespace = false
+
+[*.json]
+indent_style = space
+indent_size = 4
index c78e2b840ffa57fa6efbf5c83ce721171942ea1f..fc8d76145838b779ebc088d7e0a61e75b757c469 100644 (file)
@@ -40,3 +40,4 @@ tests/*.sh
 lcov_data
 *~
 *.phar
 lcov_data
 *~
 *.phar
+vendor/
diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..67bbd91
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Michael Wallner <mike@php.net>
diff --git a/BUGS b/BUGS
new file mode 100644 (file)
index 0000000..4c2ad80
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,25 @@
+Known Issues
+============
+
+Windows:
+       If you keep getting "SSL connect error" when trying to issue 
+               requests, try another (newer) libeay32.dll/ssleay32.dll pair.
+
+Internals:
+       Inflating raw deflated data causes a re-initialization of the inflate
+               stream where the corresponding window bits are modified to tell libz
+               to not check for zlib header bytes.  This is not preventable AFAICS.
+       LFS dependant parts of libcurl are left out because of off_t,
+               respectively off64_t confusion.
+       Persistent handles and "cookiestore" request option do interfere,
+               as libcurl saves the cookies to the file on curl_easy_destroy(),
+               cookies are not saved until the CURL handle will be recycled.
+                       Thus one would either need to
+                               * run PHP with raphf.persistent_handles.limit = 0
+                               * call raphf\persistent_handles_clean() every request
+                               * call $client->flushCookies(), which is available
+                                 since libcurl v7.17.1 and does not work with the
+                                 procedural API
+       HTTP and Proxy authentication information (username/password) can not be
+               unset with NULL prior libcurl v7.19.6 and separate options for setting
+               username and password--which work--are only available since v7.19.6.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644 (file)
index 0000000..968bd44
--- /dev/null
@@ -0,0 +1,39 @@
+# Contributor Code of Conduct
+
+As contributors and maintainers of this project, and in the interest of
+fostering an open and welcoming community, we pledge to respect all people who
+contribute through reporting issues, posting feature requests, updating
+documentation, submitting pull requests or patches, and other activities.
+
+We are committed to making participation in this project a harassment-free
+experience for everyone, regardless of level of experience, gender, gender
+identity and expression, sexual orientation, disability, personal appearance,
+body size, race, ethnicity, age, religion, or nationality.
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery
+* Personal attacks
+* Trolling or insulting/derogatory comments
+* Public or private harassment
+* Publishing other's private information, such as physical or electronic
+  addresses, without explicit permission
+* Other unethical or unprofessional conduct.
+
+Project maintainers have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct. By adopting this Code of Conduct, project
+maintainers commit themselves to fairly and consistently applying these
+principles to every aspect of managing this project. Project maintainers who do
+not follow or enforce the Code of Conduct may be permanently removed from the
+project team.
+
+This code of conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community.
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by opening an issue or contacting one or more of the project maintainers.
+
+This Code of Conduct is adapted from the
+[Contributor Covenant](http://contributor-covenant.org), version 1.2.0,
+available at http://contributor-covenant.org/version/1/2/0/.
diff --git a/Exceptions.txt b/Exceptions.txt
deleted file mode 100644 (file)
index f666ee9..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-# Throw Exceptions:
-
-* on setters, that return self
-* on getters, that return objects
diff --git a/KnownIssues.txt b/KnownIssues.txt
deleted file mode 100644 (file)
index 4c2ad80..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-Known Issues
-============
-
-Windows:
-       If you keep getting "SSL connect error" when trying to issue 
-               requests, try another (newer) libeay32.dll/ssleay32.dll pair.
-
-Internals:
-       Inflating raw deflated data causes a re-initialization of the inflate
-               stream where the corresponding window bits are modified to tell libz
-               to not check for zlib header bytes.  This is not preventable AFAICS.
-       LFS dependant parts of libcurl are left out because of off_t,
-               respectively off64_t confusion.
-       Persistent handles and "cookiestore" request option do interfere,
-               as libcurl saves the cookies to the file on curl_easy_destroy(),
-               cookies are not saved until the CURL handle will be recycled.
-                       Thus one would either need to
-                               * run PHP with raphf.persistent_handles.limit = 0
-                               * call raphf\persistent_handles_clean() every request
-                               * call $client->flushCookies(), which is available
-                                 since libcurl v7.17.1 and does not work with the
-                                 procedural API
-       HTTP and Proxy authentication information (username/password) can not be
-               unset with NULL prior libcurl v7.19.6 and separate options for setting
-               username and password--which work--are only available since v7.19.6.
diff --git a/LICENSE b/LICENSE
index 786ba27b7e2e8f7fb9db45fdaaa65b1da0746d4e..c7d1587923cf08886a4b0056af00d0d7832cf239 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2014, Michael Wallner <mike@iworks.at>.
+Copyright (c) 2004-2015, Michael Wallner <mike@php.net>.
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without 
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without 
diff --git a/Makefile.frag b/Makefile.frag
new file mode 100644 (file)
index 0000000..3ef9ecb
--- /dev/null
@@ -0,0 +1,16 @@
+# provide headers in builddir, so they do not end up in /usr/include/ext/http/src
+
+PHP_HTTP_HEADERS := $(addprefix $(PHP_HTTP_BUILDDIR)/,$(PHP_HTTP_HEADERS))
+
+$(PHP_HTTP_BUILDDIR)/%.h: $(PHP_HTTP_SRCDIR)/src/%.h
+       @cat >$@ <$<
+
+all: http-build-headers
+clean: http-clean-headers
+
+.PHONY: http-build-headers
+http-build-headers: $(PHP_HTTP_HEADERS)
+
+.PHONY: http-clean-headers
+http-clean-headers:
+       -rm $(PHP_HTTP_HEADERS)
index c0ee4d1b956facb7e60ed9c6b80f6e0b6b76e4e2..19738da8115e14b39d0c3c46939ed55ed5904c23 100644 (file)
--- a/README.md
+++ b/README.md
-# pecl/http v2
+# ext-http
 
 
-[![Build Status](https://travis-ci.org/m6w6/ext-http.svg?branch=R_2_5)](https://travis-ci.org/m6w6/ext-http)
+[![Build Status](https://travis-ci.org/m6w6/ext-http.svg?branch=master)](https://travis-ci.org/m6w6/ext-http)
 
 
-## About:
+Extended HTTP support. Again.
 
 
-Extended HTTP support. Again. 
+## Documentation
 
 
-* Introduces the http namespace.
-* PHP stream based message bodies.
-* Encapsulated env request/response.
-* Modular client support.
+See the [online markdown reference](https://mdref.m6w6.name/http).
 
 
-## Installation:
+Known issues are listed in [BUGS](./BUGS) and future ideas can be found in [TODO](./TODO).
 
 
-This extension is hosted at [PECL](http://pecl.php.net) and can be installed with [PEAR](http://pear.php.net)'s pecl command:
+## Installing
 
 
-    # pecl install pecl_http
+### PECL
 
 
-## Dependencies:
+       pecl install pecl_http
 
 
-pecl/http depends on a number of system libraries and PHP extensions for special features.
+### PHARext
 
 
-#### Required system libraries:
+Watch out for [PECL replicates](https://replicator.pharext.org?pecl_http)
+and pharext packages attached to [releases](./releases).
 
 
-The following system libraries are required to build this extension:
+### Checkout
 
 
-##### zlib
-Provides gzip/zlib/deflate encoding.  
-Minimum version: 1.2.0.4  
-Install on Debian: `apt-get install zlib1g-dev`
+       git clone github.com:m6w6/ext-http
+       cd ext-http
+       /path/to/phpize
+       ./configure --with-php-config=/path/to/php-config
+       make
+       sudo make install
 
 
+## ChangeLog
 
 
-#### Optional system libraries:
+A comprehensive list of changes can be obtained from the
+[PECL website](https://pecl.php.net/package-changelog.php?package=pecl_http).
 
 
-The following system libraries are optional and provide additional features:
+## License
 
 
-##### libidn
-Provides IDNA support in URLs.  
-Minimum version: none  
-Install on Debian: `apt-get install libidn11-dev`
+ext-http is licensed under the 2-Clause-BSD license, which can be found in
+the accompanying [LICENSE](./LICENSE) file.
 
 
-##### libidn2
-Provides IDNA support in URLs (fallback if libidn is not available).  
-Minimum version: none  
-Install on Debian: `apt-get install libidn2-0-dev`
+## Contributing
 
 
-##### libicu
-Provides IDNA support in URLs (fallback if libidn is not available).  
-Minimum version: none  
-Install on Debian: `apt-get install libicu-dev`
+All forms of contribution are welcome! Please see the bundled
+[CONTRIBUTING](./CONTRIBUTING.md) note for the general principles followed.
 
 
-##### libcurl
-Provides HTTP request functionality.  
-Minimum version: 7.18.2  
-Install on Debian: `apt-get install libcurl4-openssl-dev`  
-Note: There are usually different styles of SSL support for libcurl available, so you can replace 'openssl' in the above command f.e. with 'nss' or 'gnutls'.
-
-##### libevent
-Eventloop support for the HTTP client.  
-Minimum version: none  
-Install on Debian: `apt-get install libevent-dev`
-
-### PHP extensions:
-
-This extension unconditionally depends on the pre-loaded presence of the following PHP extensions:
-
-* [raphf](https://github.com/m6w6/ext-raphf)
-* [propro](https://github.com/m6w6/ext-propro)
-* spl
-
-
-If configured ```--with-http-shared-deps``` (default) it depends on the pre-loaded presence of the following extensions, as long as they were available at build time:
-
-* hash
-* iconv
-* json (only until < 2.4.0)
-
-Please ensure that all extension on which pecl/http depends, are loaded before it, e.g in your `php.ini`:
-
-       ; obligatory deps
-       extension = raphf.so
-       extension = propro.so
-       
-       ; if shared deps were enabled
-       extension = hash.so
-       extension = iconv.so
-       extension = json.so
-       
-       ; finally load pecl/http
-       extension = http.so
-
-## Conflicts:
-
-pecl/http-v2 conflicts with the following extensions:
-
-* http-v1
-* event (only until <= 2.0.3)
-
-## INI Directives:
-
-* http.etag.mode = "crc32b"  
-  Default hash method for dynamic response payloads to generate an ETag.
-
-## Stream Filters:
-
-The http extension registers the ```http.*``` namespace for its stream filters. Provided stream filters are:
-
-* http.chunked_decode  
-  Decode a stream encoded with chunked transfer encoding.
-* http.chunked_encode  
-  Encode a stream with chunked transfer encoding.
-* http.inflate  
-  Decode a stream encoded with deflate/zlib/gzip encoding.
-* http.deflate  
-  Encode a stream with deflate/zlib/gzip encoding.
-
-
-## Documentation:
-
-Documentation is available at https://mdref.m6w6.name/http
+The list of past and current contributors is maintained in [THANKS](./THANKS).
diff --git a/THANKS b/THANKS
new file mode 100644 (file)
index 0000000..d5ddcb5
--- /dev/null
+++ b/THANKS
@@ -0,0 +1,15 @@
+Thanks go to the following people, who have contributed to this project:
+
+       Ilia Alshanetsky (ilia at php dot net)
+       Anatol Belski (ab at php dot net)
+       Petr Czaderna (petr at hroch dot info)
+       Remi Collet (remi at php dot net)
+       Benjamin Eberlei (kontakt at beberlei dot de)
+       David James (james82 at gmail dot com)
+       Thomas Landro Johnsen (thomas dot l dot johnsen at gmail dot com)
+       Clay Loveless (clay at killersoft dot com)
+       Felipe Pena (felipe at php dot net)
+       David Sklar (sklar at sklar dot com)
+       Travis Swicegood (travis at mashery dot com)
+       Alexey Zakhlestin (indeyets at gmail dot com)
+       Alexander Zhuravlev (zaa at zaa dot pp dot ru)
diff --git a/ThanksTo.txt b/ThanksTo.txt
deleted file mode 100644 (file)
index 67c5b32..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-Thanks To
-=========
-
-People who repeatedly reported issues with this extension in a manner
-so they could be fixed in a reasonable way, or suggested useful features
-to implement, in alphabetical order:
-
-       Ilia Alshanetsky (ilia at php dot net)
-       Anatol Belski (ab at php dot net)
-       Petr Czaderna (petr at hroch dot info)
-       Remi Collet (remi at php dot net)
-       Benjamin Eberlei (kontakt at beberlei dot de)
-       David James (james82 at gmail dot com)
-       Thomas Landro Johnsen (thomas dot l dot johnsen at gmail dot com)
-       Clay Loveless (clay at killersoft dot com)
-       Felipe Pena (felipe at php dot net)
-       David Sklar (sklar at sklar dot com)
-       Travis Swicegood (travis at mashery dot com)
-       Alexey Zakhlestin (indeyets at gmail dot com)
-       Alexander Zhuravlev (zaa at zaa dot pp dot ru)
-
-Thanks a lot!
diff --git a/bench_select_vs_event.php b/bench_select_vs_event.php
deleted file mode 100644 (file)
index 3bc9aed..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-function usage($e = null) {
-    global $argv;
-    if ($e) {
-        fprintf(STDERR, "ERROR: %s\n\n", $e);
-    }
-       fprintf(STDERR, "Usage: %s -u <URL> -n <requests> -c <concurrency> [-p (enable pipelining)] [-e (use libevent)]\n", $argv[0]);
-       fprintf(STDERR, "\nDefaults: -u http://localhost/ -n 1000 -c 10\n\n");
-       exit(-1);
-}
-
-function push($client, $url, &$n) {
-       if ($n-- > 0) {
-               $req = new http\Client\Request("GET", $url);
-               $client->enqueue($req, function($response) use ($client, $req, $url, &$n) {
-                       global $count; ++$count;
-                       push($client, $url, $n);
-                       return true; // dequeue
-               });
-       }
-}
-
-isset($argv) or $argv = $_SERVER['argv'];
-defined('STDERR') or define('STDERR', fopen('php://stderr', 'w'));
-
-$opts = getopt("u:c:n:e");
-isset($opts["u"]) or $opts["u"] = "http://localhost/";
-isset($opts["c"]) or $opts["c"] = 10;
-isset($opts["n"]) or $opts["n"] = 1000;
-
-$argc > 1 or usage();
-
-$time = microtime(true);
-$count = 0;
-$client = new http\Client;
-
-$client->enablePipelining($opts["p"]===false);
-$client->enableEvents($opts["e"]===false);
-
-for ($i = 0, $x = $opts["n"]; $i < $opts["c"]; ++$i) {
-       push($client, $opts["u"], $x);
-}
-
-try {
-       $client->send();
-} catch (Exception $e) {
-       echo $e;
-}
-
-printf("\n> %10.6fs (%3.2fM)\n", microtime(true)-$time, memory_get_peak_usage(true)/1024/1024);
-
-$count == $opts["n"] or printf("\nOnly %d finished\n", $count);
diff --git a/check_package-xml.php b/check_package-xml.php
deleted file mode 100755 (executable)
index 32a1e73..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-#!/usr/bin/env php
-<?php
-
-ini_set("log_errors", false);
-ini_set("display_errors", true);
-
-if ($argc > 1) {
-       if ($argv[1] === "-") {
-               $file = "php://stdin";
-       } else {
-               $file = $argv[1];
-       }
-} elseif (stdin_is_readable()) {
-       $file = "php://stdin";
-} else {
-       $file = "./package.xml";
-}
-
-if (($xml = simplexml_load_file($file))) {
-       $xml_files = xmllist($xml->contents[0]);
-       $dirs = ["."];
-       while ($dir = array_shift($dirs)) {
-               foreach (dirlist($dir) as $file) {
-                       if (is_gitignored($file)) {
-                               continue;
-                       }
-                       if (!is_dir($file)) {
-                               if (!in_array($file, $xml_files)) {
-                                       echo "Missing file $file\n";
-                               }
-                       } else {
-                               $base = basename($file);
-                               if ($base{0} !== ".") {
-                                       array_push($dirs, $file);
-                               }
-                       }
-               }
-       }
-       foreach ($xml_files as $file) {
-               if (!file_exists($file)) {
-                       echo "Extraneous file $file\n";
-               }
-       }
-}
-
-###
-
-function error($fmt) {
-       trigger_error(call_user_func_array("sprintf", func_get_args()));
-}
-
-function stdin_is_readable() {
-       $r = [STDIN]; $w = $e = [];
-       return stream_select($r, $w, $e, 0);
-}
-
-function is_gitignored($file) {
-       static $gitignore;
-       
-       if (!isset($gitignore)) {
-               if (is_readable(".gitignore")) {
-                       $gitignore = explode("\n", `find | git check-ignore --stdin`);
-               } else {
-                       $gitignore = false;
-               }
-       }
-       if ($gitignore) {
-               return in_array($file, $gitignore);
-       }
-       return false;
-}
-
-function xmllist(SimpleXmlElement $dir, $p = ".", &$a = null) {
-       settype($a, "array");
-       $p = trim($p, "/") . "/" . trim($dir["name"], "/") . "/";
-       foreach ($dir as $file) {
-               switch ($file->getName()) {
-                       case "dir":
-                               xmllist($file, $p, $a);
-                               break;
-                       case "file":
-                               $a[] = sprintf("%s/%s", trim($p, "/"), trim($file["name"]));
-                               break;
-                       default:
-                               error("Unknown content type: %s", $file->getName());
-                               break;
-               }
-       }
-       return $a;
-}
-
-function dirlist($dir, $p = null) {
-       $p = implode("/", array_filter([trim($p, "/"), trim($dir, "/")]));
-       foreach (scandir($p) as $file) {
-               yield $p."/".$file;
-       }
-}
index 13c24ab122f31a7ef203360e6af664c15fdc5317..4bd1f69b39f6e32fab498b9f99fd342250456203 100644 (file)
-// config.w32 for pecl/http\r
-// $Id$\r
-\r
-ARG_ENABLE("http", "whether to enable extended HTTP support", "no");\r
-\r
-function check_for_main_ext(ext, header)\r
-{\r
-       if (!header) {\r
-               header = "php_"+ ext +".h";\r
-       }\r
-\r
-       /* When in configure, we're always in the root of PHP source */\r
-       var ext_path = "ext\\" + ext;\r
-       \r
-       STDOUT.Write("Checking for ext/"+ ext +" ...  ");\r
-\r
-       if (FSO.FileExists(ext_path + "\\" + header)) {\r
-               STDOUT.WriteLine(ext_path);\r
-               return ext_path;\r
-       }\r
-\r
-       STDOUT.WriteLine("<not found>");\r
-       return false;\r
-}\r
-\r
-function check_for_pecl_ext(ext, header)\r
-{\r
-       if (!header) {\r
-               header = "php_"+ ext +".h";\r
-       }\r
-       \r
-       var g;\r
-       var s = ext +"\\"+ header;\r
-       \r
-       STDOUT.Write("Checking for pecl/"+ ext +" ...  ");\r
-       if (    (g = glob(configure_module_dirname +"\\..\\"+ s)) ||\r
-                       (g = glob(configure_module_dirname +"\\..\\..\\..\\pecl\\"+ s))) {\r
-               var f = g[0].substr(0, g[0].length - header.length - 1);\r
-               STDOUT.WriteLine(f);\r
-               return f;\r
-       }\r
-       STDOUT.WriteLine("<not found>");\r
-       return false;\r
-}\r
-\r
-if (PHP_HTTP != "no") {\r
-\r
-       EXTENSION("http",\r
-               "php_http.c php_http_buffer.c php_http_client.c " +\r
-               "php_http_client_request.c php_http_client_response.c " +\r
-               "php_http_cookie.c php_http_curl.c php_http_client_curl.c " +\r
-               "php_http_encoding.c php_http_env.c php_http_env_request.c " +\r
-               "php_http_env_response.c php_http_etag.c php_http_exception.c php_http_filter.c php_http_header_parser.c " +\r
-               "php_http_header.c php_http_info.c php_http_message.c php_http_message_body.c php_http_message_parser.c " +\r
-               "php_http_misc.c php_http_negotiate.c php_http_object.c php_http_options.c php_http_params.c " +\r
-               "php_http_querystring.c php_http_url.c php_http_version.c",\r
-               null,\r
-               null);\r
-       AC_DEFINE("HAVE_HTTP", 1, "Have extended HTTP support");\r
-       AC_DEFINE("HTTP_SHARED_DEPS", 1, "Depend on shared extensions");\r
-       \r
-       AC_DEFINE("HAVE_GETHOSTNAME", 1);\r
-       \r
-       if (PHP_DEBUG != "no") {\r
-               ADD_FLAG("CFLAGS_HTTP", "/W3");\r
-       }\r
-       \r
-       if (CHECK_HEADER_ADD_INCLUDE('zlib.h', 'CFLAGS_HTTP', '..\\zlib;' + php_usual_include_suspects)) {\r
-               AC_DEFINE('HTTP_HAVE_ZLIB', 1, "Have zlib library");\r
-               ADD_FLAG("LDFLAGS_HTTP", "/FORCE:MULTIPLE");\r
-       } else {\r
-               WARNING("zlib encoding functions not enabled; libraries and headers not found");\r
-       }\r
-       \r
-       if (typeof(PHP_HASH) != "undefined" && PHP_HASH != "no") {\r
-               var f;\r
-               \r
-               if ((f = check_for_pecl_ext("hash")) || (f = check_for_main_ext("hash"))) {\r
-                       ADD_FLAG("CFLAGS_HTTP", '/I "' + f + '" /DHTTP_HAVE_PHP_HASH_H=1');\r
-                       ADD_EXTENSION_DEP("http", "hash", true);\r
-               }\r
-       }\r
-       \r
-       if (PHP_SESSION != "no") {\r
-               ADD_EXTENSION_DEP("http", "session", true);\r
-       }\r
-       \r
-       if (PHP_ICONV != "no") {\r
-               ADD_EXTENSION_DEP("http", "iconv", true);\r
-       }\r
-       \r
-       if (PHP_CURL != "no") {\r
-               ADD_EXTENSION_DEP("http", "curl", true);\r
-       }\r
-\r
-       \r
-       CURL_LIB="libcurl_a.lib;libcurl.lib;" + (PHP_DEBUG != "no" ? "libcurld.lib":"libcurl.lib");\r
-       if (CHECK_HEADER_ADD_INCLUDE("curl/curl.h", "CFLAGS_HTTP") &&\r
-                       CHECK_HEADER_ADD_INCLUDE("openssl/crypto.h", "CFLAGS_HTTP") &&\r
-                       CHECK_LIB(CURL_LIB, "http", PHP_HTTP) &&\r
-                       CHECK_LIB("ssleay32.lib", "http", PHP_HTTP) &&\r
-                       CHECK_LIB("libeay32.lib", "http", PHP_HTTP) &&\r
-                       CHECK_LIB("zlib.lib;zlib_a.lib", "http", PHP_HTTP) &&\r
-                       CHECK_LIB("libcurl_a.lib", "http", PHP_HTTP) &&\r
-                       ADD_EXTENSION_DEP("http", "propro", true) &&\r
-                       ADD_EXTENSION_DEP("http", "raphf", true) &&\r
-                       CHECK_LIB("winmm.lib", "http", PHP_HTTP)) {\r
-               AC_DEFINE("PHP_HTTP_HAVE_CURL", 1, "Have CURL library");\r
-               AC_DEFINE("PHP_HTTP_HAVE_SSL", 1, "Have SSL");\r
-               AC_DEFINE("PHP_HAVE_CURL_MULTI_STRERROR", 1, "");\r
-               AC_DEFINE("PHP_HAVE_CURL_SHARE_STRERROR", 1, "");\r
-               AC_DEFINE("PHP_HAVE_CURL_EASY_STRERROR", 1, "");\r
-               AC_DEFINE("PHP_HAVE_CURL_EASY_RESET", 1, "");\r
-               AC_DEFINE("PHP_HAVE_CURL_GETFORMDATA", 1, "");\r
-               AC_DEFINE("PHP_HAVE_CURL_FORMGET", 1, "");\r
-               AC_DEFINE("PHP_HAVE_CURL_MULTI_SETOPT", 1, "");\r
-               AC_DEFINE("PHP_HAVE_CURL_MULTI_TIMEOUT", 1, "");\r
-\r
-               if (CHECK_HEADER_ADD_INCLUDE("event2/event.h", "CFLAGS_HTTP") &&\r
-                       CHECK_LIB("libevent.lib", "http", PHP_HTTP) &&\r
-                       CHECK_LIB("libevent_core.lib", "http", PHP_HTTP) &&\r
-                       CHECK_LIB("libevent_extras.lib", "http", PHP_HTTP)) {\r
-\r
-                       AC_DEFINE("PHP_HTTP_HAVE_EVENT", 1);\r
-                       AC_DEFINE("PHP_HTTP_HAVE_EVENT2", 1);\r
-                       AC_DEFINE("PHP_HTTP_EVENT_VERSION", "2.0.21 or greater");\r
-               }\r
-       } else {\r
-               WARNING("curl convenience functions not enabled; libraries and headers not found");\r
-       }\r
-}\r
+// config.w32 for pecl/http
+// $Id$
+
+ARG_ENABLE("http", "whether to enable extended HTTP support", "no");
+
+function check_for_main_ext(ext, header)
+{
+       if (!header) {
+               header = "php_"+ ext +".h";
+       }
+
+       /* When in configure, we're always in the root of PHP source */
+       var ext_path = "ext\\" + ext;
+
+       STDOUT.Write("Checking for ext/"+ ext +" ...  ");
+
+       if (FSO.FileExists(ext_path + "\\" + header)) {
+               STDOUT.WriteLine(ext_path);
+               return ext_path;
+       }
+
+       STDOUT.WriteLine("<not found>");
+       return false;
+}
+
+function check_for_pecl_ext(ext, header)
+{
+       if (!header) {
+               header = "php_"+ ext +".h";
+       }
+
+       var g;
+       var s = ext +"\\"+ header;
+
+       STDOUT.Write("Checking for pecl/"+ ext +" ...  ");
+       if (    (g = glob(configure_module_dirname +"\\..\\"+ s)) ||
+                       (g = glob(configure_module_dirname +"\\..\\..\\..\\pecl\\"+ s))) {
+               var f = g[0].substr(0, g[0].length - header.length - 1);
+               STDOUT.WriteLine(f);
+               return f;
+       }
+       STDOUT.WriteLine("<not found>");
+       return false;
+}
+
+if (PHP_HTTP != "no") {
+
+       EXTENSION("http",
+               "src/php_http.c src/php_http_buffer.c src/php_http_client.c " +
+               "src/php_http_client_request.c src/php_http_client_response.c " +
+               "src/php_http_cookie.c src/php_http_curl.c src/php_http_client_curl.c " +
+               "src/php_http_encoding.c src/php_http_env.c src/php_http_env_request.c " +
+               "src/php_http_env_response.c src/php_http_etag.c src/php_http_exception.c src/php_http_filter.c src/php_http_header_parser.c " +
+               "src/php_http_header.c 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_misc.c src/php_http_negotiate.c 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_version.c",
+               null,
+               null);
+       AC_DEFINE("HAVE_HTTP", 1, "Have extended HTTP support");
+       AC_DEFINE("HTTP_SHARED_DEPS", 1, "Depend on shared extensions");
+
+       AC_DEFINE("HAVE_GETHOSTNAME", 1);
+
+       if (PHP_DEBUG != "no") {
+               ADD_FLAG("CFLAGS_HTTP", "/W3");
+       }
+
+       if (CHECK_HEADER_ADD_INCLUDE('zlib.h', 'CFLAGS_HTTP', '..\\zlib;' + php_usual_include_suspects)) {
+               AC_DEFINE('HTTP_HAVE_ZLIB', 1, "Have zlib library");
+               ADD_FLAG("LDFLAGS_HTTP", "/FORCE:MULTIPLE");
+       } else {
+               WARNING("zlib encoding functions not enabled; libraries and headers not found");
+       }
+
+       if (typeof(PHP_HASH) != "undefined" && PHP_HASH != "no") {
+               var f;
+
+               if ((f = check_for_pecl_ext("hash")) || (f = check_for_main_ext("hash"))) {
+                       ADD_FLAG("CFLAGS_HTTP", '/I "' + f + '" /DHTTP_HAVE_PHP_HASH_H=1');
+                       ADD_EXTENSION_DEP("http", "hash", true);
+               }
+       }
+
+       if (PHP_SESSION != "no") {
+               ADD_EXTENSION_DEP("http", "session", true);
+       }
+
+       if (PHP_ICONV != "no") {
+               ADD_EXTENSION_DEP("http", "iconv", true);
+       }
+
+       if (PHP_CURL != "no") {
+               ADD_EXTENSION_DEP("http", "curl", true);
+       }
+
+
+       CURL_LIB="libcurl_a.lib;libcurl.lib;" + (PHP_DEBUG != "no" ? "libcurld.lib":"libcurl.lib");
+       if (CHECK_HEADER_ADD_INCLUDE("curl/curl.h", "CFLAGS_HTTP") &&
+                       CHECK_HEADER_ADD_INCLUDE("openssl/crypto.h", "CFLAGS_HTTP") &&
+                       CHECK_LIB(CURL_LIB, "http", PHP_HTTP) &&
+                       CHECK_LIB("ssleay32.lib", "http", PHP_HTTP) &&
+                       CHECK_LIB("libeay32.lib", "http", PHP_HTTP) &&
+                       CHECK_LIB("zlib.lib;zlib_a.lib", "http", PHP_HTTP) &&
+                       CHECK_LIB("libcurl_a.lib", "http", PHP_HTTP) &&
+                       ADD_EXTENSION_DEP("http", "propro", true) &&
+                       ADD_EXTENSION_DEP("http", "raphf", true) &&
+                       CHECK_LIB("winmm.lib", "http", PHP_HTTP)) {
+               AC_DEFINE("PHP_HTTP_HAVE_CURL", 1, "Have CURL library");
+               AC_DEFINE("PHP_HTTP_HAVE_SSL", 1, "Have SSL");
+               AC_DEFINE("PHP_HAVE_CURL_MULTI_STRERROR", 1, "");
+               AC_DEFINE("PHP_HAVE_CURL_SHARE_STRERROR", 1, "");
+               AC_DEFINE("PHP_HAVE_CURL_EASY_STRERROR", 1, "");
+               AC_DEFINE("PHP_HAVE_CURL_EASY_RESET", 1, "");
+               AC_DEFINE("PHP_HAVE_CURL_GETFORMDATA", 1, "");
+               AC_DEFINE("PHP_HAVE_CURL_FORMGET", 1, "");
+               AC_DEFINE("PHP_HAVE_CURL_MULTI_SETOPT", 1, "");
+               AC_DEFINE("PHP_HAVE_CURL_MULTI_TIMEOUT", 1, "");
+
+               if (CHECK_HEADER_ADD_INCLUDE("event2/event.h", "CFLAGS_HTTP") &&
+                       CHECK_LIB("libevent.lib", "http", PHP_HTTP) &&
+                       CHECK_LIB("libevent_core.lib", "http", PHP_HTTP) &&
+                       CHECK_LIB("libevent_extras.lib", "http", PHP_HTTP)) {
+
+                       AC_DEFINE("PHP_HTTP_HAVE_EVENT", 1);
+                       AC_DEFINE("PHP_HTTP_HAVE_EVENT2", 1);
+                       AC_DEFINE("PHP_HTTP_EVENT_VERSION", "2.0.21 or greater");
+               }
+       } else {
+               WARNING("curl convenience functions not enabled; libraries and headers not found");
+       }
+}
index 273f03873dbca17a314cfe755fdaeebb9c61a0e4..da682f7a0975a545b82f51823dc2bea1d1bf44c6 100644 (file)
@@ -31,22 +31,22 @@ if test "$PHP_HTTP" != "no"; then
                        AC_CHECK_PROG(SED, sed, sed)
                ])
        ])
                        AC_CHECK_PROG(SED, sed, sed)
                ])
        ])
-       
+
        AC_PROG_CPP
        AC_PROG_CPP
-       
+
        if test "$PHP_HTTP_SHARED_DEPS" != "no"; then
                AC_DEFINE([PHP_HTTP_SHARED_DEPS], [1], [ ])
        else
                AC_DEFINE([PHP_HTTP_SHARED_DEPS], [0], [ ])
        fi
        if test "$PHP_HTTP_SHARED_DEPS" != "no"; then
                AC_DEFINE([PHP_HTTP_SHARED_DEPS], [1], [ ])
        else
                AC_DEFINE([PHP_HTTP_SHARED_DEPS], [0], [ ])
        fi
-       
+
        dnl
        dnl HTTP_SHARED_DEP(name[, code-if-yes[, code-if-not]])
        dnl
        AC_DEFUN([HTTP_SHARED_DEP], [
                extname=$1
                haveext=$[PHP_HTTP_HAVE_EXT_]translit($1,a-z_-,A-Z__)
        dnl
        dnl HTTP_SHARED_DEP(name[, code-if-yes[, code-if-not]])
        dnl
        AC_DEFUN([HTTP_SHARED_DEP], [
                extname=$1
                haveext=$[PHP_HTTP_HAVE_EXT_]translit($1,a-z_-,A-Z__)
-               
+
                AC_MSG_CHECKING([whether to add a dependency on ext/$extname])
                if test "$PHP_HTTP_SHARED_DEPS" = "no"; then
                        AC_MSG_RESULT([no])
                AC_MSG_CHECKING([whether to add a dependency on ext/$extname])
                if test "$PHP_HTTP_SHARED_DEPS" = "no"; then
                        AC_MSG_RESULT([no])
@@ -63,7 +63,7 @@ if test "$PHP_HTTP" != "no"; then
                        $3
                fi
        ])
                        $3
                fi
        ])
-       
+
        dnl
        dnl HTTP_HAVE_PHP_EXT(name[, code-if-yes[, code-if-not]])
        dnl
        dnl
        dnl HTTP_HAVE_PHP_EXT(name[, code-if-yes[, code-if-not]])
        dnl
@@ -92,7 +92,7 @@ if test "$PHP_HTTP" != "no"; then
                        $3
                fi
        ])
                        $3
                fi
        ])
-       
+
        dnl
        dnl HTTP_CURL_SSL_LIB_CHECK(ssllib[, code-if-yes[, code-if-not])
        dnl
        dnl
        dnl HTTP_CURL_SSL_LIB_CHECK(ssllib[, code-if-yes[, code-if-not])
        dnl
@@ -120,7 +120,7 @@ if test "$PHP_HTTP" != "no"; then
                        $3
                ])
        ])
                        $3
                ])
        ])
-       
+
 
 dnl ----
 dnl STDC
 
 dnl ----
 dnl STDC
@@ -240,7 +240,7 @@ dnl ----
                        PHP_ADD_LIBRARY_WITH_PATH(z, $ZLIB_DIR/$PHP_LIBDIR, HTTP_SHARED_LIBADD)
                fi
        fi
                        PHP_ADD_LIBRARY_WITH_PATH(z, $ZLIB_DIR/$PHP_LIBDIR, HTTP_SHARED_LIBADD)
                fi
        fi
-       
+
 dnl ----
 dnl CURL
 dnl ----
 dnl ----
 dnl CURL
 dnl ----
@@ -260,7 +260,7 @@ dnl ----
                        AC_MSG_RESULT([not found])
                else
                        AC_MSG_RESULT([found in $CURL_DIR])
                        AC_MSG_RESULT([not found])
                else
                        AC_MSG_RESULT([found in $CURL_DIR])
-               
+
                        AC_MSG_CHECKING([for curl-config])
                        CURL_CONFIG=
                        for i in "$CURL_DIR/bin/curl-config" "$CURL_DIR/curl-config" `which curl-config`; do
                        AC_MSG_CHECKING([for curl-config])
                        CURL_CONFIG=
                        for i in "$CURL_DIR/bin/curl-config" "$CURL_DIR/curl-config" `which curl-config`; do
@@ -275,7 +275,7 @@ dnl ----
                        else
                                AC_MSG_RESULT([found: $CURL_CONFIG])
                        fi
                        else
                                AC_MSG_RESULT([found: $CURL_CONFIG])
                        fi
-               
+
                        dnl RHEL6:         7.19.7
                        dnl SUSE11:        7.19.7
                        dnl Debian wheezy: 7.26.0
                        dnl RHEL6:         7.19.7
                        dnl SUSE11:        7.19.7
                        dnl Debian wheezy: 7.26.0
@@ -287,7 +287,7 @@ dnl ----
                        if test `echo $CURL_VERSION | $SED -e 's/[[^0-9]]/ /g' | $AWK '{print $1*10000 + $2*100 + $3}'` -lt 71802; then
                                AC_MSG_ERROR([libcurl version greater or equal to 7.18.2 required])
                        fi
                        if test `echo $CURL_VERSION | $SED -e 's/[[^0-9]]/ /g' | $AWK '{print $1*10000 + $2*100 + $3}'` -lt 71802; then
                                AC_MSG_ERROR([libcurl version greater or equal to 7.18.2 required])
                        fi
-                       
+
                        AC_MSG_CHECKING([for HTTP2 support in libcurl])
                        if $CURL_CONFIG --features | $EGREP -q HTTP2; then
                                AC_MSG_RESULT([yes])
                        AC_MSG_CHECKING([for HTTP2 support in libcurl])
                        if $CURL_CONFIG --features | $EGREP -q HTTP2; then
                                AC_MSG_RESULT([yes])
@@ -295,11 +295,11 @@ dnl ----
                        else
                                AC_MSG_RESULT([no])
                        fi
                        else
                                AC_MSG_RESULT([no])
                        fi
-               
+
                        dnl
                        dnl compile tests
                        dnl
                        dnl
                        dnl compile tests
                        dnl
-               
+
                        save_INCLUDES="$INCLUDES"
                        INCLUDES=
                        save_LIBS="$LIBS"
                        save_INCLUDES="$INCLUDES"
                        INCLUDES=
                        save_LIBS="$LIBS"
@@ -308,14 +308,14 @@ dnl ----
                        CFLAGS="$CFLAGS `$CURL_CONFIG --cflags`"
                        save_LDFLAGS="$LDFLAGS"
                        LDFLAGS="$ld_runpath_switch$CURL_DIR/$PHP_LIBDIR"
                        CFLAGS="$CFLAGS `$CURL_CONFIG --cflags`"
                        save_LDFLAGS="$LDFLAGS"
                        LDFLAGS="$ld_runpath_switch$CURL_DIR/$PHP_LIBDIR"
-               
+
                        AC_MSG_CHECKING([for SSL support in libcurl])
                        CURL_SSL=`$CURL_CONFIG --feature | $EGREP SSL`
                        CURL_SSL_LIBS=""
                        if test "$CURL_SSL" = "SSL"; then
                                AC_MSG_RESULT([yes])
                                AC_DEFINE([PHP_HTTP_HAVE_SSL], [1], [ ])
                        AC_MSG_CHECKING([for SSL support in libcurl])
                        CURL_SSL=`$CURL_CONFIG --feature | $EGREP SSL`
                        CURL_SSL_LIBS=""
                        if test "$CURL_SSL" = "SSL"; then
                                AC_MSG_RESULT([yes])
                                AC_DEFINE([PHP_HTTP_HAVE_SSL], [1], [ ])
-                       
+
                                HTTP_CURL_SSL_LIB_CHECK(OpenSSL, [
                                        AC_CHECK_HEADER([openssl/ssl.h], [
                                                AC_CHECK_HEADER([openssl/crypto.h], [
                                HTTP_CURL_SSL_LIB_CHECK(OpenSSL, [
                                        AC_CHECK_HEADER([openssl/ssl.h], [
                                                AC_CHECK_HEADER([openssl/crypto.h], [
@@ -345,7 +345,7 @@ dnl ----
                                dnl no CURL_SSL
                                AC_MSG_RESULT([no])
                        fi
                                dnl no CURL_SSL
                                AC_MSG_RESULT([no])
                        fi
-                       
+
                        AC_MSG_CHECKING([for ares support in libcurl])
                        AC_TRY_RUN([
                                #include <curl/curl.h>
                        AC_MSG_CHECKING([for ares support in libcurl])
                        AC_TRY_RUN([
                                #include <curl/curl.h>
@@ -364,7 +364,7 @@ dnl ----
                        ], [
                                AC_MSG_RESULT([no])
                        ])
                        ], [
                                AC_MSG_RESULT([no])
                        ])
-                       
+
                        AC_MSG_CHECKING([whether CURLOPT_TLSAUTH_TYPE expects CURL_TLSAUTH_SRP or literal "SRP"])
                        AC_TRY_RUN([
                                #include <curl/curl.h>
                        AC_MSG_CHECKING([whether CURLOPT_TLSAUTH_TYPE expects CURL_TLSAUTH_SRP or literal "SRP"])
                        AC_TRY_RUN([
                                #include <curl/curl.h>
@@ -391,24 +391,24 @@ dnl ----
                                        AC_MSG_RESULT([neither])
                                ], [
                                        AC_MSG_RESULT([neither])
                                        AC_MSG_RESULT([neither])
                                ], [
                                        AC_MSG_RESULT([neither])
-                               ])                      
+                               ])
                        ], [
                                AC_MSG_RESULT([neither])
                        ])
                        ], [
                                AC_MSG_RESULT([neither])
                        ])
-               
+
                        INCLUDES="$save_INCLUDES"
                        LIBS="$save_LIBS"
                        CFLAGS="$save_CFLAGS"
                        LDFLAGS="$save_LDFLAGS"
                        INCLUDES="$save_INCLUDES"
                        LIBS="$save_LIBS"
                        CFLAGS="$save_CFLAGS"
                        LDFLAGS="$save_LDFLAGS"
-                       
+
                        if test -n "$CURL_SSL_LIBS"; then
                                for CURL_SSL_LIB in $CURL_SSL_LIBS; do
                                        PHP_ADD_LIBRARY_WITH_PATH([$CURL_SSL_LIB], $CURL_DIR/$PHP_LIBDIR, PHP_HTTP_SHARED_LIBADD)
                                done
                        fi
                        if test -n "$CURL_SSL_LIBS"; then
                                for CURL_SSL_LIB in $CURL_SSL_LIBS; do
                                        PHP_ADD_LIBRARY_WITH_PATH([$CURL_SSL_LIB], $CURL_DIR/$PHP_LIBDIR, PHP_HTTP_SHARED_LIBADD)
                                done
                        fi
-                       
+
                        dnl end compile tests
                        dnl end compile tests
-               
+
                        AC_MSG_CHECKING([for bundled SSL CA info])
                        CURL_CAINFO=
                        for i in `$CURL_CONFIG --ca` "/etc/ssl/certs/ca-certificates.crt" "/etc/ssl/certs/ca-bundle.crt"; do
                        AC_MSG_CHECKING([for bundled SSL CA info])
                        CURL_CAINFO=
                        for i in `$CURL_CONFIG --ca` "/etc/ssl/certs/ca-certificates.crt" "/etc/ssl/certs/ca-bundle.crt"; do
@@ -423,7 +423,7 @@ dnl ----
                                AC_MSG_RESULT([$CURL_CAINFO])
                                AC_DEFINE_UNQUOTED([PHP_HTTP_CURL_CAINFO], ["$CURL_CAINFO"], [path to bundled SSL CA info])
                        fi
                                AC_MSG_RESULT([$CURL_CAINFO])
                                AC_DEFINE_UNQUOTED([PHP_HTTP_CURL_CAINFO], ["$CURL_CAINFO"], [path to bundled SSL CA info])
                        fi
-               
+
                        PHP_ADD_INCLUDE($CURL_DIR/include)
                        PHP_ADD_LIBRARY_WITH_PATH(curl, $CURL_DIR/$PHP_LIBDIR, HTTP_SHARED_LIBADD)
                        PHP_EVAL_LIBLINE(`$CURL_CONFIG --libs`, HTTP_SHARED_LIBADD)
                        PHP_ADD_INCLUDE($CURL_DIR/include)
                        PHP_ADD_LIBRARY_WITH_PATH(curl, $CURL_DIR/$PHP_LIBDIR, HTTP_SHARED_LIBADD)
                        PHP_EVAL_LIBLINE(`$CURL_CONFIG --libs`, HTTP_SHARED_LIBADD)
@@ -453,9 +453,9 @@ dnl ----
                        AC_DEFINE([PHP_HTTP_HAVE_EVENT], [0], [ ])
                else
                        AC_MSG_RESULT([found in $EVENT_DIR])
                        AC_DEFINE([PHP_HTTP_HAVE_EVENT], [0], [ ])
                else
                        AC_MSG_RESULT([found in $EVENT_DIR])
-                       
+
                        AC_MSG_CHECKING([for libevent version, roughly])
                        AC_MSG_CHECKING([for libevent version, roughly])
-                       
+
                        if test -f "$EVENT_DIR/include/event2/event.h"; then
                                EVENT_VER="`$AWK '/_EVENT_VERSION/ {gsub(/\"/,\"\",$3); print $3}' < $EVENT_DIR/include/event2/event-config.h`"
                                AC_DEFINE([PHP_HTTP_HAVE_EVENT2], [1], [ ])
                        if test -f "$EVENT_DIR/include/event2/event.h"; then
                                EVENT_VER="`$AWK '/_EVENT_VERSION/ {gsub(/\"/,\"\",$3); print $3}' < $EVENT_DIR/include/event2/event-config.h`"
                                AC_DEFINE([PHP_HTTP_HAVE_EVENT2], [1], [ ])
@@ -473,7 +473,7 @@ dnl ----
                        fi
                        AC_DEFINE_UNQUOTED([PHP_HTTP_EVENT_VERSION], ["$EVENT_VER"], [ ])
                        AC_MSG_RESULT([$EVENT_VER])
                        fi
                        AC_DEFINE_UNQUOTED([PHP_HTTP_EVENT_VERSION], ["$EVENT_VER"], [ ])
                        AC_MSG_RESULT([$EVENT_VER])
-                       
+
                        PHP_ADD_INCLUDE($EVENT_DIR/include)
                        PHP_ADD_LIBRARY_WITH_PATH(event, $EVENT_DIR/$PHP_LIBDIR, HTTP_SHARED_LIBADD)
                        AC_DEFINE([PHP_HTTP_HAVE_EVENT], [1], [Have libevent support for cURL])
                        PHP_ADD_INCLUDE($EVENT_DIR/include)
                        PHP_ADD_LIBRARY_WITH_PATH(event, $EVENT_DIR/$PHP_LIBDIR, HTTP_SHARED_LIBADD)
                        AC_DEFINE([PHP_HTTP_HAVE_EVENT], [1], [Have libevent support for cURL])
@@ -573,90 +573,59 @@ dnl ----
 dnl ----
 dnl DONE
 dnl ----
 dnl ----
 dnl DONE
 dnl ----
+       PHP_ADD_INCLUDE(src)
+       PHP_ADD_BUILD_DIR(src)
        PHP_HTTP_SOURCES="\
        PHP_HTTP_SOURCES="\
-               php_http_buffer.c \
-               php_http.c \
-               php_http_client.c \
-               php_http_client_curl.c \
-               php_http_client_request.c \
-               php_http_client_response.c \
-               php_http_cookie.c \
-               php_http_curl.c \
-               php_http_encoding.c \
-               php_http_env.c \
-               php_http_env_request.c \
-               php_http_env_response.c \
-               php_http_etag.c \
-               php_http_exception.c \
-               php_http_filter.c \
-               php_http_header_parser.c \
-               php_http_header.c \
-               php_http_info.c \
-               php_http_message_body.c \
-               php_http_message.c \
-               php_http_message_parser.c \
-               php_http_misc.c \
-               php_http_negotiate.c \
-               php_http_object.c \
-               php_http_options.c \
-               php_http_params.c \
-               php_http_querystring.c \
-               php_http_url.c \
-               php_http_version.c \
+               src/php_http_buffer.c \
+               src/php_http.c \
+               src/php_http_client.c \
+               src/php_http_client_curl.c \
+               src/php_http_client_request.c \
+               src/php_http_client_response.c \
+               src/php_http_cookie.c \
+               src/php_http_curl.c \
+               src/php_http_encoding.c \
+               src/php_http_env.c \
+               src/php_http_env_request.c \
+               src/php_http_env_response.c \
+               src/php_http_etag.c \
+               src/php_http_exception.c \
+               src/php_http_filter.c \
+               src/php_http_header_parser.c \
+               src/php_http_header.c \
+               src/php_http_info.c \
+               src/php_http_message_body.c \
+               src/php_http_message.c \
+               src/php_http_message_parser.c \
+               src/php_http_misc.c \
+               src/php_http_negotiate.c \
+               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_version.c \
        "
        PHP_NEW_EXTENSION([http], $PHP_HTTP_SOURCES, $ext_shared)
        "
        PHP_NEW_EXTENSION([http], $PHP_HTTP_SOURCES, $ext_shared)
-       
+
        dnl shared extension deps
        HTTP_SHARED_DEP([hash])
        HTTP_SHARED_DEP([iconv])
        dnl shared extension deps
        HTTP_SHARED_DEP([hash])
        HTTP_SHARED_DEP([iconv])
-       
+
        dnl extension deps
        PHP_ADD_EXTENSION_DEP([http], [raphf], true)
        PHP_ADD_EXTENSION_DEP([http], [propro], true)
        dnl extension deps
        PHP_ADD_EXTENSION_DEP([http], [raphf], true)
        PHP_ADD_EXTENSION_DEP([http], [propro], true)
-       
+
        PHP_SUBST([HTTP_SHARED_LIBADD])
 
        PHP_SUBST([HTTP_SHARED_LIBADD])
 
-       PHP_HTTP_HEADERS="
-               php_http_api.h \
-               php_http_buffer.h \
-               php_http_curl_client.h \
-               php_http_curl_client_datashare.h \
-               php_http_client_datashare.h \
-               php_http_client_factory.h \
-               php_http_client.h \
-               php_http_client_interface.h \
-               php_http_curl_client_pool.h \
-               php_http_client_pool.h \
-               php_http_client_request.h \
-               php_http_client_response.h \
-               php_http_cookie.h \
-               php_http_curl.h \
-               php_http_encoding.h \
-               php_http_env.h \
-               php_http_env_request.h \
-               php_http_env_response.h \
-               php_http_etag.h \
-               php_http_exception.h \
-               php_http_filter.h \
-               php_http.h \
-               php_http_header_parser.h \
-               php_http_header.h \
-               php_http_info.h \
-               php_http_message_body.h \
-               php_http_message.h \
-               php_http_message_parser.h \
-               php_http_misc.h \
-               php_http_negotiate.h \
-               php_http_object.h \
-               php_http_options.h \
-               php_http_params.h \
-               php_http_querystring.h \
-               php_http_response_codes.h \
-               php_http_url.h \
-               php_http_utf8.h \
-               php_http_version.h \
-       "
-       PHP_INSTALL_HEADERS(ext/http, $PHP_HTTP_HEADERS)
+       PHP_HTTP_HEADERS=`(cd $ext_srcdir/src && echo *.h)`
+       PHP_INSTALL_HEADERS(ext/http, php_http.h $PHP_HTTP_HEADERS)
+       PHP_SUBST([PHP_HTTP_HEADERS])
+       PHP_HTTP_SRCDIR=$ext_srcdir
+       PHP_SUBST([PHP_HTTP_SRCDIR])
+       PHP_HTTP_BUILDDIR=$ext_builddir
+       PHP_SUBST([PHP_HTTP_BUILDDIR])
+       PHP_ADD_MAKEFILE_FRAGMENT
 
        AC_DEFINE([HAVE_HTTP], [1], [Have extended HTTP support])
 fi
 
        AC_DEFINE([HAVE_HTTP], [1], [Have extended HTTP support])
 fi
diff --git a/gen_curlinfo.php b/gen_curlinfo.php
deleted file mode 100755 (executable)
index 076a10d..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/usr/bin/env php
-<?php
-// $Id: gen_curlinfo.php 323304 2012-02-17 21:13:24Z mike $
-
-error_reporting(0);
-
-function failure() {
-       // this is why error_get_last() should return a stdClass object
-       $error = error_get_last();
-       fprintf(STDERR, "FAILURE: %s\n", $error["message"]);
-       exit(-1);
-}
-
-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)) {
-                               return $matches;
-                       }
-               } else {
-                       if (preg_match($pattern, $content, $matches)) {
-                               return $matches;
-                       }
-               }
-               trigger_error("no match in $file for $pattern");
-       }
-       failure();
-}
-
-$ifdefs = array(
-       'PRIMARY_IP' => 'PHP_HTTP_CURL_VERSION(7,19,0)',
-       'APPCONNECT_TIME' => 'PHP_HTTP_CURL_VERSION(7,19,0)',
-    'CONDITION_UNMET' => 'PHP_HTTP_CURL_VERSION(7,19,4)',
-    '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)',
-);
-$exclude = array(
-    'PRIVATE', 'LASTSOCKET', 'FTP_ENTRY_PATH', 'CERTINFO', 'TLS_SESSION',
-    'RTSP_SESSION_ID', 'RTSP_CLIENT_CSEQ', 'RTSP_SERVER_CSEQ', 'RTSP_CSEQ_RECV',
-       'COOKIELIST'
-);
-
-$translate = array(
-       'HTTP_CONNECTCODE' => "connect_code",
-       'COOKIELIST' => 'cookies',
-);
-
-$templates = array(
-'STRING' => 
-'      if (CURLE_OK == curl_easy_getinfo(ch, %s, &c)) {
-               add_assoc_string_ex(&array, "%s", sizeof("%2$s"), c ? c : "", 1);
-       }
-',
-'DOUBLE' => 
-'      if (CURLE_OK == curl_easy_getinfo(ch, %s, &d)) {
-               add_assoc_double_ex(&array, "%s", sizeof("%2$s"), d);
-       }
-',
-'LONG' => 
-'      if (CURLE_OK == curl_easy_getinfo(ch, %s, &l)) {
-               add_assoc_long_ex(&array, "%s", sizeof("%2$s"), l);
-       }
-',
-'SLIST' =>
-'      if (CURLE_OK == curl_easy_getinfo(ch, %s, &s)) {
-               MAKE_STD_ZVAL(subarray);
-               array_init(subarray);
-               for (p = s; p; p = p->next) {
-                       if (p->data) {
-                               add_next_index_string(subarray, p->data, 1);
-                       }
-               }
-               add_assoc_zval_ex(&array, "%s", sizeof("%2$s"), subarray);
-               curl_slist_free_all(s);
-       }
-',
-);
-
-$infos = file_re('curl.h', '/^\s*(CURLINFO_(\w+))\s*=\s*CURLINFO_(STRING|LONG|DOUBLE|SLIST)\s*\+\s*\d+\s*,?\s*$/m');
-
-ob_start();
-foreach ($infos as $info) {
-       list(, $full, $short, $type) = $info;
-       if (in_array($short, $exclude)) continue;
-       if (isset($ifdefs[$short])) printf("#if %s\n", $ifdefs[$short]);
-       printf($templates[$type], $full, strtolower((isset($translate[$short])) ? $translate[$short] : $short));
-       if (isset($ifdefs[$short])) printf("#endif\n");
-}
-
-file_put_contents("php_http_client_curl.c", 
-       preg_replace('/(\/\* BEGIN::CURLINFO \*\/\n).*(\n\s*\/\* END::CURLINFO \*\/)/s', '$1'. ob_get_contents() .'$2',
-               file_get_contents("php_http_client_curl.c")));
-
-?>
diff --git a/gen_travis_yml.php b/gen_travis_yml.php
deleted file mode 100755 (executable)
index 89ab0b2..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env php
-# autogenerated file; do not edit
-language: c
-
-addons:
- apt:
-  packages:
-   - php5-cli
-   - php-pear
-   - libcurl4-openssl-dev
-   - zlib1g-dev
-   - libidn11-dev
-   - libevent-dev
-
-env:
-<?php
-
-$gen = include "./travis/pecl/gen-matrix.php";
-$env = $gen([
-       "PHP" => ["5.4", "5.5", "5.6"],
-       "enable_debug",
-       "enable_maintainer_zts",
-       "enable_json",
-       "enable_hash" => ["yes"],
-       "enable_iconv" => ["yes"]
-]);
-foreach ($env as $e) {
-       printf(" - %s\n", $e);
-}
-
-?>
-
-before_script:
- - make -f travis/pecl/Makefile php
- - make -f travis/pecl/Makefile pecl PECL=raphf
- - make -f travis/pecl/Makefile pecl PECL=propro
-
-script:
- - make -f travis/pecl/Makefile ext PECL=http
- - make -f travis/pecl/Makefile test
-
-after_script:
- - test -e tests/helper/server.log && cat tests/helper/server.log
-
-sudo: false
diff --git a/gen_utf8.php b/gen_utf8.php
deleted file mode 100755 (executable)
index 865a2f5..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/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, 0xffff);
-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;
-                               }
-                               print "\t{";
-                               if ($start >= 0xffff) {
-                                       printf("0x%08X, ", $start);
-                                       if ($end) {
-                                               printf("0x%08X, ", $end);
-                                       } else {
-                                               print("         0, ");
-                                       }
-                               } else {
-                                       printf("    0x%04X, ", $start);
-                                       if ($end) {
-                                               printf("    0x%04X, ", $end);
-                                       } else {
-                                               print("         0, ");
-                                       }
-                               }
-                               printf("%d},\n", $step);
-                       }
-               }
-               break;
-       default:
-               if ($a) {
-                       break 2;
-               } elseif ($line === "alpha /\n") {
-                       $a = true;
-               }
-               break;
-       }
-}
-
-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 df97ad994f44f97582f048f227c98063f197359e..95578eabeea2d475d8da2edbb4894d340164a634 100644 (file)
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1" ?>
+<?xml version="1.0" encoding="UTF-8" ?>
 <package version="2.0" xmlns="http://pear.php.net/dtd/package-2.0"
  xmlns:tasks="http://pear.php.net/dtd/tasks-1.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 <package version="2.0" xmlns="http://pear.php.net/dtd/package-2.0"
  xmlns:tasks="http://pear.php.net/dtd/tasks-1.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  <channel>pecl.php.net</channel>
  <summary>Extended HTTP Support</summary>
  <description><![CDATA[
  <channel>pecl.php.net</channel>
  <summary>Extended HTTP Support</summary>
  <description><![CDATA[
-This HTTP extension aims to provide a convenient and powerful 
+This HTTP extension aims to provide a convenient and powerful
 set of functionality for one of PHPs major applications.
 
 set of functionality for one of PHPs major applications.
 
-It eases handling of HTTP urls, headers and messages, provides 
-means for negotiation of a client's preferred content type, 
-language and charset, as well as a convenient way to send any 
+It eases handling of HTTP urls, headers and messages, provides
+means for negotiation of a client's preferred content type,
+language and charset, as well as a convenient way to send any
 arbitrary data with caching and resuming capabilities.
 
 It provides powerful request functionality with support for
 arbitrary data with caching and resuming capabilities.
 
 It provides powerful request functionality with support for
@@ -44,84 +44,88 @@ http://dev.iworks.at/ext-http/lcov/ext/http/
   <release>stable</release>
   <api>stable</api>
  </stability>
   <release>stable</release>
   <api>stable</api>
  </stability>
- <license>BSD, revised</license>
+ <license uri="http://copyfree.org/content/standard/licenses/2bsd/license.txt">BSD-2-Clause</license>
  <notes><![CDATA[
 * Fixed gh-issue #12: crash on bad url passed to http\Message::setRequestUrl()
  <notes><![CDATA[
 * Fixed gh-issue #12: crash on bad url passed to http\Message::setRequestUrl()
-* The URL parser now fails on empty labels 
+* The URL parser now fails on empty labels
 ]]></notes>
  <contents>
   <dir name="/">
 ]]></notes>
  <contents>
   <dir name="/">
-   <file role="doc" name="LICENSE"/>
+   <file role="doc" name="AUTHORS"/>
+   <file role="doc" name="BUGS"/>
+   <file role="doc" name="CONTRIBUTING.md"/>
    <file role="doc" name="CREDITS"/>
    <file role="doc" name="CREDITS"/>
-   <file role="doc" name="Exceptions.txt"/>
-   <file role="doc" name="KnownIssues.txt"/>
-   <file role="doc" name="ThanksTo.txt"/>
+   <file role="doc" name="LICENSE"/>
+   <file role="doc" name="README.md"/>
+   <file role="doc" name="THANKS"/>
+   <file role="doc" name="TODO"/>
    <file role="src" name="config.m4"/>
    <file role="src" name="config9.m4"/>
    <file role="src" name="config.w32"/>
    <file role="src" name="config.m4"/>
    <file role="src" name="config9.m4"/>
    <file role="src" name="config.w32"/>
-   
-   <file role="src" name="php_http_api.h"/>
-   <file role="src" name="php_http_buffer.c"/>
-   <file role="src" name="php_http_buffer.h"/>
-   <file role="src" name="php_http.c"/>
-   <file role="src" name="php_http_client.c"/>
-   <file role="src" name="php_http_client.h"/>
-   <file role="src" name="php_http_client_curl.c"/>
-   <file role="src" name="php_http_client_curl.h"/>
-   <file role="src" name="php_http_client_request.c"/>
-   <file role="src" name="php_http_client_request.h"/>
-   <file role="src" name="php_http_client_response.c"/>
-   <file role="src" name="php_http_client_response.h"/>
-   <file role="src" name="php_http_cookie.c"/>
-   <file role="src" name="php_http_cookie.h"/>
-   <file role="src" name="php_http_curl.c"/>
-   <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_env.c"/>
-   <file role="src" name="php_http_env.h"/>
-   <file role="src" name="php_http_env_request.c"/>
-   <file role="src" name="php_http_env_request.h"/>
-   <file role="src" name="php_http_env_response.c"/>
-   <file role="src" name="php_http_env_response.h"/>
-   <file role="src" name="php_http_etag.c"/>
-   <file role="src" name="php_http_etag.h"/>
-   <file role="src" name="php_http_exception.c"/>
-   <file role="src" name="php_http_exception.h"/>
-   <file role="src" name="php_http_filter.c"/>
-   <file role="src" name="php_http_filter.h"/>
+   <file role="src" name="Makefile.frag"/>
    <file role="src" name="php_http.h"/>
    <file role="src" name="php_http.h"/>
-   <file role="src" name="php_http_header_parser.c"/>
-   <file role="src" name="php_http_header_parser.h"/>
-   <file role="src" name="php_http_header.c"/>
-   <file role="src" name="php_http_header.h"/>
-   <file role="src" name="php_http_info.c"/>
-   <file role="src" name="php_http_info.h"/>
-   <file role="src" name="php_http_message_body.c"/>
-   <file role="src" name="php_http_message_body.h"/>
-   <file role="src" name="php_http_message.c"/>
-   <file role="src" name="php_http_message.h"/>
-   <file role="src" name="php_http_message_parser.c"/>
-   <file role="src" name="php_http_message_parser.h"/>
-   <file role="src" name="php_http_misc.c"/>
-   <file role="src" name="php_http_misc.h"/>
-   <file role="src" name="php_http_negotiate.c"/>
-   <file role="src" name="php_http_negotiate.h"/>
-   <file role="src" name="php_http_object.c"/>
-   <file role="src" name="php_http_object.h"/>
-   <file role="src" name="php_http_options.c"/>
-   <file role="src" name="php_http_options.h"/>
-   <file role="src" name="php_http_params.c"/>
-   <file role="src" name="php_http_params.h"/>
-   <file role="src" name="php_http_querystring.c"/>
-   <file role="src" name="php_http_querystring.h"/>
-   <file role="src" name="php_http_response_codes.h"/>
-   <file role="src" name="php_http_url.c"/>
-   <file role="src" name="php_http_url.h"/>
-   <file role="src" name="php_http_utf8.h"/>
-   <file role="src" name="php_http_version.c"/>
-   <file role="src" name="php_http_version.h"/>
-
+   <dir name="src">
+    <file role="src" name="php_http_api.h"/>
+    <file role="src" name="php_http_buffer.c"/>
+    <file role="src" name="php_http_buffer.h"/>
+    <file role="src" name="php_http.c"/>
+    <file role="src" name="php_http_client.c"/>
+    <file role="src" name="php_http_client.h"/>
+    <file role="src" name="php_http_client_curl.c"/>
+    <file role="src" name="php_http_client_curl.h"/>
+    <file role="src" name="php_http_client_request.c"/>
+    <file role="src" name="php_http_client_request.h"/>
+    <file role="src" name="php_http_client_response.c"/>
+    <file role="src" name="php_http_client_response.h"/>
+    <file role="src" name="php_http_cookie.c"/>
+    <file role="src" name="php_http_cookie.h"/>
+    <file role="src" name="php_http_curl.c"/>
+    <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_env.c"/>
+    <file role="src" name="php_http_env.h"/>
+    <file role="src" name="php_http_env_request.c"/>
+    <file role="src" name="php_http_env_request.h"/>
+    <file role="src" name="php_http_env_response.c"/>
+    <file role="src" name="php_http_env_response.h"/>
+    <file role="src" name="php_http_etag.c"/>
+    <file role="src" name="php_http_etag.h"/>
+    <file role="src" name="php_http_exception.c"/>
+    <file role="src" name="php_http_exception.h"/>
+    <file role="src" name="php_http_filter.c"/>
+    <file role="src" name="php_http_filter.h"/>
+    <file role="src" name="php_http_header_parser.c"/>
+    <file role="src" name="php_http_header_parser.h"/>
+    <file role="src" name="php_http_header.c"/>
+    <file role="src" name="php_http_header.h"/>
+    <file role="src" name="php_http_info.c"/>
+    <file role="src" name="php_http_info.h"/>
+    <file role="src" name="php_http_message_body.c"/>
+    <file role="src" name="php_http_message_body.h"/>
+    <file role="src" name="php_http_message.c"/>
+    <file role="src" name="php_http_message.h"/>
+    <file role="src" name="php_http_message_parser.c"/>
+    <file role="src" name="php_http_message_parser.h"/>
+    <file role="src" name="php_http_misc.c"/>
+    <file role="src" name="php_http_misc.h"/>
+    <file role="src" name="php_http_negotiate.c"/>
+    <file role="src" name="php_http_negotiate.h"/>
+    <file role="src" name="php_http_object.c"/>
+    <file role="src" name="php_http_object.h"/>
+    <file role="src" name="php_http_options.c"/>
+    <file role="src" name="php_http_options.h"/>
+    <file role="src" name="php_http_params.c"/>
+    <file role="src" name="php_http_params.h"/>
+    <file role="src" name="php_http_querystring.c"/>
+    <file role="src" name="php_http_querystring.h"/>
+    <file role="src" name="php_http_response_codes.h"/>
+    <file role="src" name="php_http_url.c"/>
+    <file role="src" name="php_http_url.h"/>
+    <file role="src" name="php_http_utf8.h"/>
+    <file role="src" name="php_http_version.c"/>
+    <file role="src" name="php_http_version.h"/>
+   </dir>
    <dir name="tests">
     <file role="test" name="skipif.inc"/>
      <dir name="data">
    <dir name="tests">
     <file role="test" name="skipif.inc"/>
      <dir name="data">
@@ -329,6 +333,14 @@ http://dev.iworks.at/ext-http/lcov/ext/http/
      <file role="test" name="urlparser011.phpt"/>
      <file role="test" name="version001.phpt"/>
    </dir>
      <file role="test" name="urlparser011.phpt"/>
      <file role="test" name="version001.phpt"/>
    </dir>
+   <dir name="scripts">
+    <file role="src" name="bench_select_vs_event.php"/>
+    <file role="src" name="check_package-xml.php"/>
+    <file role="src" name="gen_curlinfo.php"/>
+    <file role="src" name="gen_stubs.php"/>
+    <file role="src" name="gen_travis_yml.php"/>
+    <file role="src" name="gen_utf8.php"/>
+   </dir>
   </dir>
  </contents>
  <dependencies>
   </dir>
  </contents>
  <dependencies>
@@ -336,7 +348,7 @@ http://dev.iworks.at/ext-http/lcov/ext/http/
    <php>
     <min>5.3.0</min>
     <max>7.0.0</max>
    <php>
     <min>5.3.0</min>
     <max>7.0.0</max>
-       <exclude>7.0.0</exclude>
+    <exclude>7.0.0</exclude>
    </php>
    <pearinstaller>
     <min>1.4.1</min>
    </php>
    <pearinstaller>
     <min>1.4.1</min>
@@ -345,12 +357,16 @@ http://dev.iworks.at/ext-http/lcov/ext/http/
     <name>raphf</name>
     <channel>pecl.php.net</channel>
     <min>1.1.0</min>
     <name>raphf</name>
     <channel>pecl.php.net</channel>
     <min>1.1.0</min>
+    <max>2.0.0dev</max>
+    <exclude>2.0.0dev</exclude>
     <providesextension>raphf</providesextension>
    </package>
    <package>
     <name>propro</name>
     <channel>pecl.php.net</channel>
     <min>1.0.0</min>
     <providesextension>raphf</providesextension>
    </package>
    <package>
     <name>propro</name>
     <channel>pecl.php.net</channel>
     <min>1.0.0</min>
+    <max>2.0.0dev</max>
+    <exclude>2.0.0dev</exclude>
     <providesextension>propro</providesextension>
    </package>
   </required>
     <providesextension>propro</providesextension>
    </package>
   </required>
diff --git a/php_http.c b/php_http.c
deleted file mode 100644 (file)
index 2ff20f1..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-#include <php_ini.h>
-#include <ext/standard/info.h>
-
-#include <zlib.h>
-
-#if PHP_HTTP_HAVE_CURL
-#      include <curl/curl.h>
-#      if PHP_HTTP_HAVE_EVENT
-#              if PHP_HTTP_HAVE_EVENT2
-#                      include <event2/event.h>
-#                      include <event2/event_struct.h>
-#              else
-#                      include <event.h>
-#              endif
-#      endif
-#endif
-#if PHP_HTTP_HAVE_IDN2
-#      include <idn2.h>
-#elif PHP_HTTP_HAVE_IDN
-#      include <idna.h>
-#endif
-
-ZEND_DECLARE_MODULE_GLOBALS(php_http);
-
-#ifdef COMPILE_DL_HTTP
-ZEND_GET_MODULE(http)
-#endif
-
-zend_function_entry http_functions[] = {
-       EMPTY_FUNCTION_ENTRY
-};
-
-PHP_MINIT_FUNCTION(http);
-PHP_MSHUTDOWN_FUNCTION(http);
-PHP_RSHUTDOWN_FUNCTION(http);
-PHP_MINFO_FUNCTION(http);
-
-static zend_module_dep http_module_deps[] = {
-       ZEND_MOD_REQUIRED("raphf")
-       ZEND_MOD_REQUIRED("propro")
-       ZEND_MOD_REQUIRED("spl")
-#ifdef PHP_HTTP_HAVE_HASH
-       ZEND_MOD_REQUIRED("hash")
-#endif
-#ifdef PHP_HTTP_HAVE_ICONV
-       ZEND_MOD_REQUIRED("iconv")
-#endif
-       {NULL, NULL, NULL, 0}
-};
-
-zend_module_entry http_module_entry = {
-       STANDARD_MODULE_HEADER_EX,
-       NULL,
-       http_module_deps,
-       "http",
-       http_functions,
-       PHP_MINIT(http),
-       PHP_MSHUTDOWN(http),
-       NULL,
-       PHP_RSHUTDOWN(http),
-       PHP_MINFO(http),
-       PHP_PECL_HTTP_VERSION,
-       STANDARD_MODULE_PROPERTIES
-};
-
-int http_module_number;
-
-#if PHP_DEBUG && !HAVE_GCOV
-void _dpf(int type, const char *data, size_t length)
-{
-       static const char _sym[] = "><><><";
-       if (type) {
-               int nwp = 0;
-               for (fprintf(stderr, "%c ", _sym[type-1]); length--; data++) {
-                       int ip = PHP_HTTP_IS_CTYPE(print, *data);
-                       if (!ip && *data != '\r' && *data != '\n') nwp = 1;
-                       fprintf(stderr, ip?"%c":"\\x%02x", (int) (*data & 0xff));
-                       if (!nwp && *data == '\n' && length) {
-                               fprintf(stderr, "\n%c ", _sym[type-1]);
-                       }
-               }
-               fprintf(stderr, "\n");
-       } else {
-               fprintf(stderr, "# %.*s\n", (int) length, data);
-       }
-}
-#endif
-
-static void php_http_globals_init_once(zend_php_http_globals *G)
-{
-       memset(G, 0, sizeof(*G));
-}
-
-#if 0
-static inline void php_http_globals_init(zend_php_http_globals *G TSRMLS_DC)
-{
-}
-
-static inline void php_http_globals_free(zend_php_http_globals *G TSRMLS_DC)
-{
-}
-#endif
-
-#if ZTS && PHP_DEBUG && !HAVE_GCOV
-zend_php_http_globals *php_http_globals(void)
-{
-       TSRMLS_FETCH();
-       return PHP_HTTP_G;
-}
-#endif
-
-PHP_INI_BEGIN()
-       STD_PHP_INI_ENTRY("http.etag.mode", "crc32b", PHP_INI_ALL, OnUpdateString, env.etag_mode, zend_php_http_globals, php_http_globals)
-PHP_INI_END()
-
-PHP_MINIT_FUNCTION(http)
-{
-       http_module_number = module_number;
-       ZEND_INIT_MODULE_GLOBALS(php_http, php_http_globals_init_once, NULL);
-       REGISTER_INI_ENTRIES();
-       
-       if (0
-       || SUCCESS != PHP_MINIT_CALL(http_exception)
-       || SUCCESS != PHP_MINIT_CALL(http_cookie)
-       || SUCCESS != PHP_MINIT_CALL(http_encoding)
-       || SUCCESS != PHP_MINIT_CALL(http_filter)
-       || SUCCESS != PHP_MINIT_CALL(http_header)
-       || SUCCESS != PHP_MINIT_CALL(http_header_parser)
-       || SUCCESS != PHP_MINIT_CALL(http_message)
-       || SUCCESS != PHP_MINIT_CALL(http_message_parser)
-       || SUCCESS != PHP_MINIT_CALL(http_message_body)
-       || SUCCESS != PHP_MINIT_CALL(http_querystring)
-       || SUCCESS != PHP_MINIT_CALL(http_client)
-       || SUCCESS != PHP_MINIT_CALL(http_client_request)
-       || SUCCESS != PHP_MINIT_CALL(http_client_response)
-#if PHP_HTTP_HAVE_CURL
-       || SUCCESS != PHP_MINIT_CALL(http_curl)
-       || SUCCESS != PHP_MINIT_CALL(http_client_curl)
-#endif
-       || SUCCESS != PHP_MINIT_CALL(http_url)
-       || SUCCESS != PHP_MINIT_CALL(http_env)
-       || SUCCESS != PHP_MINIT_CALL(http_env_request)
-       || SUCCESS != PHP_MINIT_CALL(http_env_response)
-       || SUCCESS != PHP_MINIT_CALL(http_params)
-       ) {
-               return FAILURE;
-       }
-       
-       return SUCCESS;
-}
-
-
-
-PHP_MSHUTDOWN_FUNCTION(http)
-{
-       UNREGISTER_INI_ENTRIES();
-       
-       if (0
-       || SUCCESS != PHP_MSHUTDOWN_CALL(http_message)
-#if PHP_HTTP_HAVE_CURL
-       || SUCCESS != PHP_MSHUTDOWN_CALL(http_client_curl)
-       || SUCCESS != PHP_MSHUTDOWN_CALL(http_curl)
-#endif
-       || SUCCESS != PHP_MSHUTDOWN_CALL(http_client)
-       ) {
-               return FAILURE;
-       }
-       
-       return SUCCESS;
-}
-
-PHP_RSHUTDOWN_FUNCTION(http)
-{
-       if (0
-       || SUCCESS != PHP_RSHUTDOWN_CALL(http_env)
-       ) {
-               return FAILURE;
-       }
-       
-       return SUCCESS;
-}
-
-PHP_MINFO_FUNCTION(http)
-{
-       php_http_buffer_t buf;
-
-       php_http_buffer_init(&buf);
-
-       php_info_print_table_start();
-       php_info_print_table_header(2, "HTTP Support", "enabled");
-       php_info_print_table_row(2, "Extension Version", PHP_PECL_HTTP_VERSION);
-       php_info_print_table_end();
-       
-       php_info_print_table_start();
-       php_info_print_table_header(3, "Used Library", "Compiled", "Linked");
-       php_info_print_table_row(3, "libz", ZLIB_VERSION, zlibVersion());
-#if PHP_HTTP_HAVE_CURL
-       {
-               curl_version_info_data *cv = curl_version_info(CURLVERSION_NOW);
-               php_info_print_table_row(3, "libcurl", LIBCURL_VERSION, cv->version);
-       }
-#else
-       php_info_print_table_row(3, "libcurl", "disabled", "disabled");
-#endif
-
-#if PHP_HTTP_HAVE_EVENT
-       php_info_print_table_row(3, "libevent",
-#      ifdef LIBEVENT_VERSION
-                       LIBEVENT_VERSION,
-#      else
-                       PHP_HTTP_EVENT_VERSION,
-#      endif
-                       event_get_version());
-#else
-       php_info_print_table_row(3, "libevent", "disabled", "disabled");
-#endif
-
-#if PHP_HTTP_HAVE_IDN2
-       php_info_print_table_row(3, "libidn2 (IDNA2008)", IDN2_VERSION, idn2_check_version(NULL));
-#elif PHP_HTTP_HAVE_IDN
-       php_info_print_table_row(3, "libidn (IDNA2003)", PHP_HTTP_LIBIDN_VERSION, "unknown");
-#endif
-
-       php_info_print_table_end();
-       
-       DISPLAY_INI_ENTRIES();
-}
-
-
-/*
- * 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/php_http_api.h b/php_http_api.h
deleted file mode 100644 (file)
index 08b6ba8..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_API_H
-#define PHP_HTTP_API_H
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#ifndef PHP_WIN32
-#include <php_config.h>
-#endif
-#include <php.h>
-#include <SAPI.h>
-
-#include <ext/raphf/php_raphf.h>
-#include <ext/propro/php_propro.h>
-#include <ext/standard/php_string.h>
-#include <ext/spl/spl_iterators.h>
-#include <ext/date/php_date.h>
-
-#include <zend_interfaces.h>
-#include <zend_exceptions.h>
-
-
-#ifdef PHP_WIN32
-# define PHP_HTTP_API __declspec(dllexport)
-#elif defined(__GNUC__) && __GNUC__ >= 4
-# define PHP_HTTP_API extern __attribute__ ((visibility("default")))
-#else
-# define PHP_HTTP_API extern
-#endif
-
-#if (defined(HAVE_ICONV) || defined(PHP_HTTP_HAVE_EXT_ICONV)) && (PHP_HTTP_SHARED_DEPS || !defined(COMPILE_DL_ICONV))
-#      define PHP_HTTP_HAVE_ICONV
-#endif
-
-#if (defined(HAVE_HASH_EXT) || defined(PHP_HTTP_HAVE_EXT_HASH)) && (PHP_HTTP_SHARED_DEPS || !defined(COMPILE_DL_HASH)) && defined(PHP_HTTP_HAVE_PHP_HASH_H)
-#      define PHP_HTTP_HAVE_HASH
-#endif
-
-#include <stddef.h>
-
-#ifdef PHP_WIN32
-#      define CURL_STATICLIB
-#      include <winsock2.h>
-#else
-#      ifdef HAVE_NETDB_H
-#              include <netdb.h>
-#      endif
-#      ifdef HAVE_UNISTD_H
-#              include <unistd.h>
-#      endif
-#endif
-
-#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTYPE_H) && defined(HAVE_ISWALNUM) && (defined(HAVE_MBRTOWC) || defined(HAVE_MBTOWC))
-#      define PHP_HTTP_HAVE_WCHAR 1
-#endif
-
-#include <ctype.h>
-#define PHP_HTTP_IS_CTYPE(type, c) is##type((int) (unsigned char) (c))
-#define PHP_HTTP_TO_CTYPE(type, c) to##type((int) (unsigned char) (c))
-
-#include "php_http.h"
-
-#include "php_http_buffer.h"
-#include "php_http_misc.h"
-#include "php_http_options.h"
-
-#include "php_http.h"
-#include "php_http_cookie.h"
-#include "php_http_encoding.h"
-#include "php_http_info.h"
-#include "php_http_message.h"
-#include "php_http_env.h"
-#include "php_http_env_request.h"
-#include "php_http_env_response.h"
-#include "php_http_etag.h"
-#include "php_http_exception.h"
-#include "php_http_filter.h"
-#include "php_http_header_parser.h"
-#include "php_http_header.h"
-#include "php_http_message_body.h"
-#include "php_http_message_parser.h"
-#include "php_http_negotiate.h"
-#include "php_http_object.h"
-#include "php_http_params.h"
-#include "php_http_querystring.h"
-#include "php_http_client.h"
-#include "php_http_curl.h"
-#include "php_http_client_request.h"
-#include "php_http_client_response.h"
-#include "php_http_client_curl.h"
-#include "php_http_url.h"
-#include "php_http_version.h"
-
-ZEND_BEGIN_MODULE_GLOBALS(php_http)
-       struct php_http_env_globals env;
-ZEND_END_MODULE_GLOBALS(php_http)
-
-ZEND_EXTERN_MODULE_GLOBALS(php_http);
-
-#ifdef ZTS
-#      include "TSRM/TSRM.h"
-#      define PHP_HTTP_G ((zend_php_http_globals *) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(php_http_globals_id)])
-#      undef TSRMLS_FETCH_FROM_CTX
-#      define TSRMLS_FETCH_FROM_CTX(ctx) void ***tsrm_ls = ((ctx)?(ctx):ts_resource_ex(0, NULL))
-#else
-#      define PHP_HTTP_G (&php_http_globals)
-#endif
-
-#if PHP_DEBUG
-#      define _DPF_STR 0
-#      define _DPF_IN  1
-#      define _DPF_OUT 2
-extern void _dpf(int type, const char *data, size_t length);
-#else
-#      define _dpf(t,s,l);
-#endif
-
-#endif /* PHP_HTTP_API_H */
-
-
-/*
- * 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/php_http_buffer.c b/php_http_buffer.c
deleted file mode 100644 (file)
index e24a4e1..0000000
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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.h>
-#include "php_http_buffer.h"
-
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_init_ex(php_http_buffer_t *buf, size_t chunk_size, int flags)
-{
-       if (!buf) {
-               buf = pemalloc(sizeof(*buf), flags & PHP_HTTP_BUFFER_INIT_PERSISTENT);
-       }
-
-       if (buf) {
-               buf->size = (chunk_size) ? chunk_size : PHP_HTTP_BUFFER_DEFAULT_SIZE;
-               buf->pmem = (flags & PHP_HTTP_BUFFER_INIT_PERSISTENT) ? 1 : 0;
-               buf->data = (flags & PHP_HTTP_BUFFER_INIT_PREALLOC) ? pemalloc(buf->size, buf->pmem) : NULL;
-               buf->free = (flags & PHP_HTTP_BUFFER_INIT_PREALLOC) ? buf->size : 0;
-               buf->used = 0;
-       }
-       
-       return buf;
-}
-
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_from_string_ex(php_http_buffer_t *buf, const char *string, size_t length)
-{
-       if ((buf = php_http_buffer_init(buf))) {
-               if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(buf, string, length)) {
-                       pefree(buf, buf->pmem);
-                       buf = NULL;
-               }
-       }
-       return buf;
-}
-
-PHP_HTTP_BUFFER_API size_t php_http_buffer_resize_ex(php_http_buffer_t *buf, size_t len, size_t override_size, int allow_error)
-{
-       char *ptr = NULL;
-#if 0
-       fprintf(stderr, "RESIZE: len=%lu, size=%lu, used=%lu, free=%lu, total=%lu\n", len, buf->size, buf->used, buf->free, buf->free+buf->used);
-#endif
-       if (buf->free < len) {
-               size_t size = override_size ? override_size : buf->size;
-               
-               while ((size + buf->free) < len) {
-                       size <<= 1;
-               }
-               
-               if (allow_error) {
-                       ptr = perealloc_recoverable(buf->data, buf->used + buf->free + size, buf->pmem);
-               } else {
-                       ptr = perealloc(buf->data, buf->used + buf->free + size, buf->pmem);
-               }
-               
-               if (ptr) {
-                       buf->data = ptr;
-               } else {
-                       return PHP_HTTP_BUFFER_NOMEM;
-               }
-               
-               buf->free += size;
-               return size;
-       }
-       return 0;
-}
-
-PHP_HTTP_BUFFER_API char *php_http_buffer_account(php_http_buffer_t *buf, size_t to_account)
-{
-       assert(to_account <= buf->free);
-
-       buf->free -= to_account;
-       buf->used += to_account;
-
-       return buf->data + buf->used;
-}
-
-PHP_HTTP_BUFFER_API size_t php_http_buffer_shrink(php_http_buffer_t *buf)
-{
-       /* avoid another realloc on fixation */
-       if (buf->free > 1) {
-               char *ptr = perealloc(buf->data, buf->used + 1, buf->pmem);
-               
-               if (ptr) {
-                       buf->data = ptr;
-               } else {
-                       return PHP_HTTP_BUFFER_NOMEM;
-               }
-               buf->free = 1;
-       }
-       return buf->used;
-}
-
-PHP_HTTP_BUFFER_API size_t php_http_buffer_append(php_http_buffer_t *buf, const char *append, size_t append_len)
-{
-       if (buf->free < append_len && PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, append_len)) {
-               return PHP_HTTP_BUFFER_NOMEM;
-       }
-       memcpy(buf->data + buf->used, append, append_len);
-       buf->used += append_len;
-       buf->free -= append_len;
-       return append_len;
-}
-
-PHP_HTTP_BUFFER_API size_t php_http_buffer_appendf(php_http_buffer_t *buf, const char *format, ...)
-{
-       va_list argv;
-       char *append;
-       size_t append_len, alloc;
-
-       va_start(argv, format);
-       append_len = vspprintf(&append, 0, format, argv);
-       va_end(argv);
-
-       alloc = php_http_buffer_append(buf, append, append_len);
-       efree(append);
-
-       if (PHP_HTTP_BUFFER_NOMEM == alloc) {
-               return PHP_HTTP_BUFFER_NOMEM;
-       }
-       return append_len;
-}
-
-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 (into) {
-               *into = copy;
-       }
-       if (len) {
-               *len = buf->used;
-       }
-       return copy;
-}
-
-PHP_HTTP_BUFFER_API size_t php_http_buffer_cut(php_http_buffer_t *buf, size_t offset, size_t length)
-{
-       if (offset > buf->used) {
-               return 0;
-       }
-       if (offset + length > buf->used) {
-               length = buf->used - offset;
-       }
-       memmove(buf->data + offset, buf->data + offset + length, buf->used - length - offset);
-       buf->used -= length;
-       buf->free += length;
-       return length;
-}
-
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_fix(php_http_buffer_t *buf)
-{
-       if (buf->free < 1 && PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize_ex(buf, 1, 1, 0)) {
-               return NULL;
-       }
-       buf->data[buf->used] = '\0';
-       return buf;
-}
-
-PHP_HTTP_BUFFER_API void php_http_buffer_reset(php_http_buffer_t *buf)
-{
-       buf->free += buf->used;
-       buf->used = 0;
-}
-
-PHP_HTTP_BUFFER_API void php_http_buffer_dtor(php_http_buffer_t *buf)
-{
-       if (buf->data) {
-               pefree(buf->data, buf->pmem);
-               buf->data = NULL;
-       }
-       buf->used = 0;
-       buf->free = 0;
-}
-
-PHP_HTTP_BUFFER_API void php_http_buffer_free(php_http_buffer_t **buf)
-{
-       if (*buf) {
-               php_http_buffer_dtor(*buf);
-               pefree(*buf, (*buf)->pmem);
-               *buf = NULL;
-       }
-}
-
-PHP_HTTP_BUFFER_API size_t php_http_buffer_chunk_buffer(php_http_buffer_t **s, const char *data, size_t data_len, char **chunk, size_t chunk_size)
-{
-       php_http_buffer_t *storage;
-       
-       *chunk = NULL;
-       
-       if (!*s) {
-               *s = php_http_buffer_init_ex(NULL, chunk_size << 1, chunk_size ? PHP_HTTP_BUFFER_INIT_PREALLOC : 0);
-       }
-       storage = *s;
-       
-       if (data_len) {
-               php_http_buffer_append(storage, data, data_len);
-       }
-       
-       if (!chunk_size) {
-               php_http_buffer_data(storage, chunk, &chunk_size);
-               php_http_buffer_free(s);
-               return chunk_size;
-       }
-       
-       if (storage->used >= chunk_size) {
-               *chunk = estrndup(storage->data, chunk_size);
-               php_http_buffer_cut(storage, 0, chunk_size);
-               return chunk_size;
-       }
-       
-       return 0;
-}
-
-PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_output(php_http_buffer_t **s, const char *data, size_t data_len, size_t chunk_len, php_http_buffer_pass_func_t passout, void *opaque TSRMLS_DC)
-{
-       char *chunk = NULL;
-       size_t passed = 0, got = 0;
-
-       while ((got = php_http_buffer_chunk_buffer(s, data, data_len, &chunk, chunk_len))) {
-               if (PHP_HTTP_BUFFER_PASS0 == passout(opaque, chunk, got TSRMLS_CC)) {
-                       PTR_SET(chunk, NULL);
-                       return PHP_HTTP_BUFFER_PASS0;
-               }
-               ++passed;
-               if (!chunk_len) {
-                       /*      we already got the last chunk,
-                               and freed all resources */
-                       break;
-               }
-               data = NULL;
-               data_len = 0;
-               PTR_SET(chunk, NULL);
-       }
-       PTR_FREE(chunk);
-       return passed;
-}
-
-PHP_HTTP_BUFFER_API ssize_t php_http_buffer_passthru(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *passin_arg, php_http_buffer_pass_func_t passon, void *passon_arg TSRMLS_DC)
-{
-       size_t passed_on = 0, passed_in = php_http_buffer_chunked_input(s, chunk_size, passin, passin_arg TSRMLS_CC);
-
-       if (passed_in == PHP_HTTP_BUFFER_PASS0) {
-               return passed_in;
-       }
-       if (passed_in || (*s)->used) {
-               passed_on = passon(passon_arg, (*s)->data, (*s)->used TSRMLS_CC);
-
-               if (passed_on == PHP_HTTP_BUFFER_PASS0) {
-                       return passed_on;
-               }
-
-               if (passed_on) {
-                       php_http_buffer_cut(*s, 0, passed_on);
-               }
-       }
-
-       return passed_on - passed_in;
-}
-
-PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_input(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *opaque TSRMLS_DC)
-{
-       php_http_buffer_t *str;
-       size_t passed;
-
-       if (!*s) {
-               *s = php_http_buffer_init_ex(NULL, chunk_size, chunk_size ? PHP_HTTP_BUFFER_INIT_PREALLOC : 0);
-       }
-       str = *s;
-
-       php_http_buffer_resize(str, chunk_size);
-       passed = passin(opaque, str->data + str->used, chunk_size TSRMLS_CC);
-
-       if (passed != PHP_HTTP_BUFFER_PASS0) {
-               str->used += passed;
-               str->free -= passed;
-       }
-
-       php_http_buffer_fix(str);
-
-       return passed;
-}
-
-#ifdef PHP_HTTP_BUFFER_EXTENDED
-
-PHP_HTTP_BUFFER_API int php_http_buffer_cmp(php_http_buffer_t *left, php_http_buffer_t *right)
-{
-       if (left->used > right->used) {
-               return -1;
-       } else if (right->used > left->used) {
-               return 1;
-       } else {
-               return memcmp(left->data, right->data, left->used);
-       }
-}
-
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_copy(const php_http_buffer_t *from, php_http_buffer_t *to)
-{
-       int free_to = !to;
-
-       to = php_http_buffer_clone(from, to);
-
-       if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(to, from->data, from->used)) {
-               if (free_to) {
-                       php_http_buffer_free(&to);
-               } else {
-                       php_http_buffer_dtor(to);
-               }
-       }
-       return to;
-}
-
-PHP_HTTP_BUFFER_API size_t php_http_buffer_insert(php_http_buffer_t *buf, const char *insert, size_t insert_len, size_t offset)
-{
-       if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, insert_len)) {
-               return PHP_HTTP_BUFFER_NOMEM;
-       }
-       memmove(buf->data + offset + insert_len, buf->data + offset, insert_len);
-       memcpy(buf->data + offset, insert, insert_len);
-       buf->used += insert_len;
-       buf->free -= insert_len;
-       return insert_len;
-}
-
-PHP_HTTP_BUFFER_API size_t php_http_buffer_insertf(php_http_buffer_t *buf, size_t offset, const char *format, ...)
-{
-       va_list argv;
-       char *insert;
-       size_t insert_len, alloc;
-
-       va_start(argv, format);
-       insert_len = vspprintf(&insert, 0, format, argv);
-       va_end(argv);
-
-       alloc = php_http_buffer_insert(buf, insert, insert_len, offset);
-       efree(insert);
-
-       if (PHP_HTTP_BUFFER_NOMEM == alloc) {
-               return PHP_HTTP_BUFFER_NOMEM;
-       }
-       return insert_len;
-}
-
-PHP_HTTP_BUFFER_API size_t php_http_buffer_prepend(php_http_buffer_t *buf, const char *prepend, size_t prepend_len)
-{
-       if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, prepend_len)) {
-               return PHP_HTTP_BUFFER_NOMEM;
-       }
-       memmove(buf->data + prepend_len, buf->data, buf->used);
-       memcpy(buf->data, prepend, prepend_len);
-       buf->used += prepend_len;
-       buf->free -= prepend_len;
-       return prepend_len;
-}
-
-PHP_HTTP_BUFFER_API size_t php_http_buffer_prependf(php_http_buffer_t *buf, const char *format, ...)
-{
-       va_list argv;
-       char *prepend;
-       size_t prepend_len, alloc;
-
-       va_start(argv, format);
-       prepend_len = vspprintf(&prepend, 0, format, argv);
-       va_end(argv);
-
-       alloc = php_http_buffer_prepend(buf, prepend, prepend_len);
-       efree(prepend);
-
-       if (PHP_HTTP_BUFFER_NOMEM == alloc) {
-               return PHP_HTTP_BUFFER_NOMEM;
-       }
-       return prepend_len;
-}
-
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_sub(const php_http_buffer_t *buf, size_t offset, size_t length)
-{
-       if (offset >= buf->used) {
-               return NULL;
-       } else {
-               size_t need = 1 + ((length + offset) > buf->used ? (buf->used - offset) : (length - offset));
-               php_http_buffer_t *sub = php_http_buffer_init_ex(NULL, need, PHP_HTTP_BUFFER_INIT_PREALLOC | (buf->pmem ? PHP_HTTP_BUFFER_INIT_PERSISTENT:0));
-               if (sub) {
-                       if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(sub, buf->data + offset, need)) {
-                               php_http_buffer_free(&sub);
-                       } else {
-                               sub->size = buf->size;
-                       }
-               }
-               return sub;
-       }
-}
-
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_right(const php_http_buffer_t *buf, size_t length)
-{
-       if (length < buf->used) {
-               return php_http_buffer_sub(buf, buf->used - length, length);
-       } else {
-               return php_http_buffer_sub(buf, 0, buf->used);
-       }
-}
-
-
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_va(php_http_buffer_t *buf, unsigned argc, va_list argv)
-{
-       unsigned i = 0;
-       buf = php_http_buffer_init(buf);
-
-       if (buf) {
-               while (argc > i++) {
-                       php_http_buffer_free_t f = va_arg(argv, php_http_buffer_free_t);
-                       php_http_buffer_t *current = va_arg(argv, php_http_buffer_t *);
-                       php_http_buffer_append(buf, current->data, current->used);
-                       FREE_PHP_HTTP_BUFFER(f, current);
-               }
-       }
-
-       return buf;
-}
-
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_ex(php_http_buffer_t *buf, unsigned argc, ...)
-{
-       va_list argv;
-       php_http_buffer_t *ret;
-
-       va_start(argv, argc);
-       ret = php_http_buffer_merge_va(buf, argc, argv);
-       va_end(argv);
-       return ret;
-}
-
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge(unsigned argc, ...)
-{
-       va_list argv;
-       php_http_buffer_t *ret;
-
-       va_start(argv, argc);
-       ret = php_http_buffer_merge_va(NULL, argc, argv);
-       va_end(argv);
-       return ret;
-}
-
-#endif
-
-/*
- * Local variables:
- * tab-width: 4
- * c-basic-offset: 4
- * End:
- * vim600: sw=4 ts=4 fdm=marker
- * vim<600: sw=4 ts=4
- */
-
diff --git a/php_http_buffer.h b/php_http_buffer.h
deleted file mode 100644 (file)
index faf8992..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_BUFFER_H
-#define PHP_HTTP_BUFFER_H
-
-#ifndef PHP_HTTP_BUFFER_DEFAULT_SIZE
-#      define PHP_HTTP_BUFFER_DEFAULT_SIZE 256
-#endif
-
-#define PHP_HTTP_BUFFER_ERROR ((size_t) -1)
-#define PHP_HTTP_BUFFER_NOMEM PHP_HTTP_BUFFER_ERROR
-#define PHP_HTTP_BUFFER_PASS0 PHP_HTTP_BUFFER_ERROR
-
-#ifndef PTR_FREE
-#      define PTR_FREE(PTR) \
-       { \
-               if (PTR) { \
-                       efree(PTR); \
-               } \
-       }
-#endif
-#ifndef PTR_SET
-#      define PTR_SET(PTR, SET) \
-       { \
-               PTR_FREE(PTR); \
-               PTR = SET; \
-       }
-#endif
-#ifndef TSRMLS_D
-#      define TSRMLS_D
-#      define TSRMLS_DC
-#      define TSRMLS_CC
-#      define TSRMLS_C
-#endif
-#ifdef PHP_ATTRIBUTE_FORMAT
-#      define PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(f, a, b) PHP_ATTRIBUTE_FORMAT(f, a, b)
-#else
-#      define PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(f, a, b)
-#endif
-#ifndef pemalloc
-#      define pemalloc(s,p)    malloc(s)
-#      define pefree(x,p)              free(x)
-#      define perealloc(x,s,p) realloc(x,s)
-#      define perealloc_recoverable perealloc
-#      define ecalloc calloc
-static inline void *estrndup(void *p, size_t s)
-{
-       char *r = (char *) malloc(s+1);
-       if (r) memcpy((void *) r, p, s), r[s] = '\0';
-       return (void *) r;
-}
-#endif
-
-#if defined(PHP_WIN32)
-#      if defined(PHP_HTTP_BUFFER_EXPORTS)
-#              define PHP_HTTP_BUFFER_API __declspec(dllexport)
-#      elif defined(COMPILE_DL_PHP_HTTP_BUFFER)
-#              define PHP_HTTP_BUFFER_API __declspec(dllimport)
-#      else
-#              define PHP_HTTP_BUFFER_API
-#      endif
-#else
-#      define PHP_HTTP_BUFFER_API
-#endif
-
-#define PHP_HTTP_BUFFER(p) ((php_http_buffer_t *) (p))
-
-#define FREE_PHP_HTTP_BUFFER_PTR(STR) pefree(STR, STR->pmem)
-#define FREE_PHP_HTTP_BUFFER_VAL(STR) php_http_buffer_dtor(STR)
-#define FREE_PHP_HTTP_BUFFER_ALL(STR) php_http_buffer_free(&(STR))
-#define FREE_PHP_HTTP_BUFFER(free, STR) \
-       switch (free) \
-       { \
-       case PHP_HTTP_BUFFER_FREE_NOT: \
-               break; \
-       case PHP_HTTP_BUFFER_FREE_PTR: \
-               pefree(STR, STR->pmem); break; \
-               break; \
-       case PHP_HTTP_BUFFER_FREE_VAL: \
-               php_http_buffer_dtor(STR); \
-               break; \
-       case PHP_HTTP_BUFFER_FREE_ALL: { \
-               php_http_buffer_t *PTR = (STR); \
-               php_http_buffer_free(&PTR); \
-               break; \
-       } \
-       default:\
-               break; \
-       }
-
-#define RETURN_PHP_HTTP_BUFFER_PTR(STR) RETURN_PHP_HTTP_BUFFER((STR), PHP_HTTP_BUFFER_FREE_PTR, 0)
-#define RETURN_PHP_HTTP_BUFFER_VAL(STR) RETURN_PHP_HTTP_BUFFER((STR), PHP_HTTP_BUFFER_FREE_NOT, 0)
-#define RETURN_PHP_HTTP_BUFFER_DUP(STR) RETURN_PHP_HTTP_BUFFER((STR), PHP_HTTP_BUFFER_FREE_NOT, 1)
-#define RETVAL_PHP_HTTP_BUFFER_PTR(STR) RETVAL_PHP_HTTP_BUFFER((STR), PHP_HTTP_BUFFER_FREE_PTR, 0)
-#define RETVAL_PHP_HTTP_BUFFER_VAL(STR) RETVAL_PHP_HTTP_BUFFER((STR), PHP_HTTP_BUFFER_FREE_NOT, 0)
-#define RETVAL_PHP_HTTP_BUFFER_DUP(STR) RETVAL_PHP_HTTP_BUFFER((STR), PHP_HTTP_BUFFER_FREE_NOT, 1)
-/* RETURN_PHP_HTTP_BUFFER(buf, PHP_HTTP_BUFFER_FREE_PTR, 0) */
-#define RETURN_PHP_HTTP_BUFFER(STR, free, dup) \
-       RETVAL_PHP_HTTP_BUFFER((STR), (free), (dup)); \
-       return;
-
-#define RETVAL_PHP_HTTP_BUFFER(STR, free, dup) \
-       php_http_buffer_fix(STR); \
-       RETVAL_STRINGL((STR)->data, (STR)->used, (dup)); \
-       FREE_PHP_HTTP_BUFFER((free), (STR));
-
-typedef struct php_http_buffer {
-       char  *data;
-       size_t used;
-       size_t free;
-       size_t size;
-       unsigned pmem:1;
-       unsigned reserved:31;
-} php_http_buffer_t;
-
-typedef enum php_http_buffer_free {
-       PHP_HTTP_BUFFER_FREE_NOT = 0,
-       PHP_HTTP_BUFFER_FREE_PTR,       /* pefree() */
-       PHP_HTTP_BUFFER_FREE_VAL,       /* php_http_buffer_dtor() */
-       PHP_HTTP_BUFFER_FREE_ALL        /* php_http_buffer_free() */
-} php_http_buffer_free_t;
-
-#define PHP_HTTP_BUFFER_ALL_FREE(STR) PHP_HTTP_BUFFER_FREE_ALL,(STR)
-#define PHP_HTTP_BUFFER_PTR_FREE(STR) PHP_HTTP_BUFFER_FREE_PTR,(STR)
-#define PHP_HTTP_BUFFER_VAL_FREE(STR) PHP_HTTP_BUFFER_FREE_VAL,(STR)
-#define PHP_HTTP_BUFFER_NOT_FREE(STR) PHP_HTTP_BUFFER_FREE_NOT,(STR)
-
-#define PHP_HTTP_BUFFER_INIT_PREALLOC  0x01
-#define PHP_HTTP_BUFFER_INIT_PERSISTENT        0x02
-
-/* create a new php_http_buffer_t */
-#define php_http_buffer_new() php_http_buffer_init(NULL)
-#define php_http_buffer_init(b) php_http_buffer_init_ex(b, PHP_HTTP_BUFFER_DEFAULT_SIZE, 0)
-#define php_http_buffer_clone(from, to) php_http_buffer_init_ex((to), (from)->size, (from)->pmem ? PHP_HTTP_BUFFER_INIT_PERSISTENT:0)
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_init_ex(php_http_buffer_t *buf, size_t chunk_size, int flags);
-
-/* create a php_http_buffer_t from a zval or c-string */
-#define php_http_buffer_from_zval(z) php_http_buffer_from_string(Z_STRVAL(z), Z_STRLEN(z))
-#define php_http_buffer_from_zval_ex(b, z) php_http_buffer_from_string_ex(b, Z_STRVAL(z), Z_STRLEN(z))
-#define php_http_buffer_from_string(s, l) php_http_buffer_from_string_ex(NULL, (s), (l))
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_from_string_ex(php_http_buffer_t *buf, const char *string, size_t length);
-
-/* usually only called from within the internal functions */
-#define php_http_buffer_resize(b, s) php_http_buffer_resize_ex((b), (s), 0, 0)
-PHP_HTTP_BUFFER_API size_t php_http_buffer_resize_ex(php_http_buffer_t *buf, size_t len, size_t override_size, int allow_error);
-
-PHP_HTTP_BUFFER_API char *php_http_buffer_account(php_http_buffer_t *buf, size_t to_account);
-
-/* shrink memory chunk to actually used size (+1) */
-PHP_HTTP_BUFFER_API size_t php_http_buffer_shrink(php_http_buffer_t *buf);
-
-/* append data to the php_http_buffer_t */
-#define php_http_buffer_appends(b, a) php_http_buffer_append((b), (a), sizeof(a)-1)
-#define php_http_buffer_appendl(b, a) php_http_buffer_append((b), (a), strlen(a))
-PHP_HTTP_BUFFER_API size_t php_http_buffer_append(php_http_buffer_t *buf, const char *append, size_t append_len);
-PHP_HTTP_BUFFER_API size_t php_http_buffer_appendf(php_http_buffer_t *buf, const char *format, ...) PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(printf, 2, 3);
-
-/* get a zero-terminated string */
-PHP_HTTP_BUFFER_API char *php_http_buffer_data(const php_http_buffer_t *buf, char **into, size_t *len);
-
-/* remove a substring */
-PHP_HTTP_BUFFER_API size_t php_http_buffer_cut(php_http_buffer_t *buf, size_t offset, size_t length);
-
-/* sets a trailing NUL byte */
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_fix(php_http_buffer_t *buf);
-
-/* reset php_http_buffer_t object */
-PHP_HTTP_BUFFER_API void php_http_buffer_reset(php_http_buffer_t *buf);
-
-/* free a php_http_buffer_t objects contents */
-PHP_HTTP_BUFFER_API void php_http_buffer_dtor(php_http_buffer_t *buf);
-
-/* free a php_http_buffer_t object completely */
-PHP_HTTP_BUFFER_API void php_http_buffer_free(php_http_buffer_t **buf);
-
-/* stores data in a php_http_buffer_t until it reaches chunk_size */
-PHP_HTTP_BUFFER_API size_t php_http_buffer_chunk_buffer(php_http_buffer_t **s, const char *data, size_t data_len, char **chunk, size_t chunk_size);
-
-typedef size_t (*php_http_buffer_pass_func_t)(void *opaque, char *, size_t TSRMLS_DC);
-
-PHP_HTTP_BUFFER_API ssize_t php_http_buffer_passthru(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *passin_arg, php_http_buffer_pass_func_t passon, void *passon_arg TSRMLS_DC);
-
-/* wrapper around php_http_buffer_chunk_buffer, which passes available chunks to passthru() */
-PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_output(php_http_buffer_t **s, const char *data, size_t data_len, size_t chunk_size, php_http_buffer_pass_func_t passout, void *opaque TSRMLS_DC);
-
-/* write chunks directly into php_http_buffer_t buffer */
-PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_input(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *opaque TSRMLS_DC);
-
-
-#      ifdef PHP_HTTP_BUFFER_EXTENDED
-
-/* memcmp for php_http_buffer_t objects */
-PHP_HTTP_BUFFER_API int php_http_buffer_cmp(php_http_buffer_t *left, php_http_buffer_t *right);
-
-/* get a complete php_http_buffer_t duplicate */
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_copy(const php_http_buffer_t *from, php_http_buffer_t *to);
-
-/* merge several php_http_buffer_t objects
-   use like:
-
-       php_http_buffer_t *final = php_http_buffer_merge(3,
-               PHP_HTTP_BUFFER_NOT_FREE(&keep),
-               PHP_HTTP_BUFFER_ALL_FREE(middle_ptr),
-               PHP_HTTP_BUFFER_VAL_FREE(&local);
-*/
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge(unsigned argc, ...);
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_ex(php_http_buffer_t *buf, unsigned argc, ...);
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_va(php_http_buffer_t *buf, unsigned argc, va_list argv);
-
-/* insert data at a specific position of the php_http_buffer_t */
-#define php_http_buffer_inserts(b, i, o) php_http_buffer_insert((b), (i), sizeof(i)-1, (o))
-#define php_http_buffer_insertl(b, i, o) php_http_buffer_insert((b), (i), strlen(i), (o))
-PHP_HTTP_BUFFER_API size_t php_http_buffer_insert(php_http_buffer_t *buf, const char *insert, size_t insert_len, size_t offset);
-PHP_HTTP_BUFFER_API size_t php_http_buffer_insertf(php_http_buffer_t *buf, size_t offset, const char *format, ...) PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(printf, 3, 4);
-
-/* prepend data */
-#define php_http_buffer_prepends(b, p) php_http_buffer_prepend((b), (p), sizeof(p)-1)
-#define php_http_buffer_prependl(b, p) php_http_buffer_prepend((b), (p), strlen(p))
-PHP_HTTP_BUFFER_API size_t php_http_buffer_prepend(php_http_buffer_t *buf, const char *prepend, size_t prepend_len);
-PHP_HTTP_BUFFER_API size_t php_http_buffer_prependf(php_http_buffer_t *buf, const char *format, ...) PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(printf, 2, 3);
-
-/* get a part of the php_http_buffer_t */
-#define php_http_buffer_mid(b, o, l) php_http_buffer_sub((b), (o), (l))
-#define php_http_buffer_left(b, l) php_http_buffer_sub((b), 0, (l))
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_right(const php_http_buffer_t *buf, size_t length);
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_sub(const php_http_buffer_t *buf, size_t offset, size_t len);
-
-#      endif /* PHP_HTTP_BUFFER_EXTENDED */
-
-#endif /* PHP_HTTP_BUFFER_H */
-
-
-/*
- * Local variables:
- * tab-width: 4
- * c-basic-offset: 4
- * End:
- * vim600: sw=4 ts=4 fdm=marker
- * vim<600: sw=4 ts=4
- */
diff --git a/php_http_client.c b/php_http_client.c
deleted file mode 100644 (file)
index 160e8bb..0000000
+++ /dev/null
@@ -1,1284 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-#include "php_http_client.h"
-
-#include <ext/spl/spl_observer.h>
-
-/*
- * array of name => php_http_client_driver_t*
- */
-static HashTable php_http_client_drivers;
-
-ZEND_RESULT_CODE php_http_client_driver_add(php_http_client_driver_t *driver)
-{
-       return zend_hash_add(&php_http_client_drivers, driver->name_str, driver->name_len + 1, (void *) driver, sizeof(php_http_client_driver_t), NULL);
-}
-
-ZEND_RESULT_CODE php_http_client_driver_get(const char *name_str, size_t name_len, php_http_client_driver_t *driver)
-{
-       php_http_client_driver_t *tmp;
-
-       if ((name_str && SUCCESS == zend_hash_find(&php_http_client_drivers, name_str, name_len + 1, (void *) &tmp))
-       ||      (SUCCESS == zend_hash_get_current_data(&php_http_client_drivers, (void *) &tmp))) {
-               *driver = *tmp;
-               return SUCCESS;
-       }
-       return FAILURE;
-}
-
-static int apply_driver_list(void *p, void *arg TSRMLS_DC)
-{
-       php_http_client_driver_t *d = p;
-       zval *zname;
-
-       MAKE_STD_ZVAL(zname);
-       ZVAL_STRINGL(zname, d->name_str, d->name_len, 1);
-
-       zend_hash_next_index_insert(arg, &zname, sizeof(zval *), NULL);
-       return ZEND_HASH_APPLY_KEEP;
-}
-
-void php_http_client_driver_list(HashTable *ht TSRMLS_DC)
-{
-       zend_hash_apply_with_argument(&php_http_client_drivers, apply_driver_list, ht TSRMLS_CC);
-}
-
-void php_http_client_options_set_subr(zval *this_ptr, char *key, size_t len, zval *opts, int overwrite TSRMLS_DC)
-{
-       if (overwrite || (opts && zend_hash_num_elements(Z_ARRVAL_P(opts)))) {
-               zend_class_entry *this_ce = Z_OBJCE_P(getThis());
-               zval *old_opts, *new_opts, **entry = NULL;
-
-               MAKE_STD_ZVAL(new_opts);
-               array_init(new_opts);
-               old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
-               if (Z_TYPE_P(old_opts) == IS_ARRAY) {
-                       array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL_P(new_opts));
-               }
-
-               if (overwrite) {
-                       if (opts && zend_hash_num_elements(Z_ARRVAL_P(opts))) {
-                               Z_ADDREF_P(opts);
-                               zend_symtable_update(Z_ARRVAL_P(new_opts), key, len, (void *) &opts, sizeof(zval *), NULL);
-                       } else {
-                               zend_symtable_del(Z_ARRVAL_P(new_opts), key, len);
-                       }
-               } else if (opts && zend_hash_num_elements(Z_ARRVAL_P(opts))) {
-                       if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(new_opts), key, len, (void *) &entry)) {
-                               array_join(Z_ARRVAL_P(opts), Z_ARRVAL_PP(entry), 0, 0);
-                       } else {
-                               Z_ADDREF_P(opts);
-                               zend_symtable_update(Z_ARRVAL_P(new_opts), key, len, (void *) &opts, sizeof(zval *), NULL);
-                       }
-               }
-
-               zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC);
-               zval_ptr_dtor(&new_opts);
-       }
-}
-
-void php_http_client_options_set(zval *this_ptr, zval *opts TSRMLS_DC)
-{
-       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-       HashPosition pos;
-       zval *new_opts;
-       zend_class_entry *this_ce = Z_OBJCE_P(getThis());
-       zend_bool is_client = instanceof_function(this_ce, php_http_client_class_entry TSRMLS_CC);
-
-       MAKE_STD_ZVAL(new_opts);
-       array_init(new_opts);
-
-       if (!opts || !zend_hash_num_elements(Z_ARRVAL_P(opts))) {
-               zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC);
-               zval_ptr_dtor(&new_opts);
-       } else {
-               zval *old_opts, *add_opts, **opt;
-
-               MAKE_STD_ZVAL(add_opts);
-               array_init(add_opts);
-               /* some options need extra attention -- thus cannot use array_merge() directly */
-               FOREACH_KEYVAL(pos, opts, key, opt) {
-                       if (key.type == HASH_KEY_IS_STRING) {
-#define KEYMATCH(k, s) ((sizeof(s)==k.len) && !strcasecmp(k.str, s))
-                               if (Z_TYPE_PP(opt) == IS_ARRAY && (KEYMATCH(key, "ssl") || KEYMATCH(key, "cookies"))) {
-                                       php_http_client_options_set_subr(getThis(), key.str, key.len, *opt, 0 TSRMLS_CC);
-                               } else if (is_client && (KEYMATCH(key, "recordHistory") || KEYMATCH(key, "responseMessageClass"))) {
-                                       zend_update_property(this_ce, getThis(), key.str, key.len-1, *opt TSRMLS_CC);
-                               } else if (Z_TYPE_PP(opt) == IS_NULL) {
-                                       old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
-                                       if (Z_TYPE_P(old_opts) == IS_ARRAY) {
-                                               zend_symtable_del(Z_ARRVAL_P(old_opts), key.str, key.len);
-                                       }
-                               } else {
-                                       Z_ADDREF_P(*opt);
-                                       add_assoc_zval_ex(add_opts, key.str, key.len, *opt);
-                               }
-                       }
-               }
-
-               old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
-               if (Z_TYPE_P(old_opts) == IS_ARRAY) {
-                       array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL_P(new_opts));
-               }
-               array_join(Z_ARRVAL_P(add_opts), Z_ARRVAL_P(new_opts), 0, 0);
-               zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC);
-               zval_ptr_dtor(&new_opts);
-               zval_ptr_dtor(&add_opts);
-       }
-}
-
-void php_http_client_options_get_subr(zval *this_ptr, char *key, size_t len, zval *return_value TSRMLS_DC)
-{
-       zend_class_entry *this_ce = Z_OBJCE_P(getThis());
-       zval **options, *opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
-
-       if ((Z_TYPE_P(opts) == IS_ARRAY) && (SUCCESS == zend_symtable_find(Z_ARRVAL_P(opts), key, len, (void *) &options))) {
-               RETVAL_ZVAL(*options, 1, 0);
-       }
-}
-
-static void queue_dtor(void *enqueued)
-{
-       php_http_client_enqueue_t *e = enqueued;
-
-       if (e->dtor) {
-               e->dtor(e);
-       }
-}
-
-php_http_client_t *php_http_client_init(php_http_client_t *h, php_http_client_ops_t *ops, php_resource_factory_t *rf, void *init_arg TSRMLS_DC)
-{
-       php_http_client_t *free_h = NULL;
-
-       if (!h) {
-               free_h = h = emalloc(sizeof(*h));
-       }
-       memset(h, 0, sizeof(*h));
-
-       h->ops = ops;
-       if (rf) {
-               h->rf = rf;
-       } else if (ops->rsrc) {
-               h->rf = php_resource_factory_init(NULL, h->ops->rsrc, h, NULL);
-       }
-       zend_llist_init(&h->requests, sizeof(php_http_client_enqueue_t), queue_dtor, 0);
-       zend_llist_init(&h->responses, sizeof(void *), NULL, 0);
-       TSRMLS_SET_CTX(h->ts);
-
-       if (h->ops->init) {
-               if (!(h = h->ops->init(h, init_arg))) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initialize client");
-                       if (free_h) {
-                               efree(free_h);
-                       }
-               }
-       }
-
-       return h;
-}
-
-php_http_client_t *php_http_client_copy(php_http_client_t *from, php_http_client_t *to)
-{
-       if (from->ops->copy) {
-               return from->ops->copy(from, to);
-       }
-
-       return NULL;
-}
-
-void php_http_client_dtor(php_http_client_t *h)
-{
-       php_http_client_reset(h);
-
-       if (h->ops->dtor) {
-               h->ops->dtor(h);
-       }
-
-       php_resource_factory_free(&h->rf);
-}
-
-void php_http_client_free(php_http_client_t **h) {
-       if (*h) {
-               php_http_client_dtor(*h);
-               efree(*h);
-               *h = NULL;
-       }
-}
-
-ZEND_RESULT_CODE php_http_client_enqueue(php_http_client_t *h, php_http_client_enqueue_t *enqueue)
-{
-       TSRMLS_FETCH_FROM_CTX(h->ts);
-
-       if (h->ops->enqueue) {
-               if (php_http_client_enqueued(h, enqueue->request, NULL)) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enqueue request; request already in queue");
-                       return FAILURE;
-               }
-               return h->ops->enqueue(h, enqueue);
-       }
-
-       return FAILURE;
-}
-
-ZEND_RESULT_CODE php_http_client_dequeue(php_http_client_t *h, php_http_message_t *request)
-{
-       TSRMLS_FETCH_FROM_CTX(h->ts);
-
-       if (h->ops->dequeue) {
-               php_http_client_enqueue_t *enqueue = php_http_client_enqueued(h, request, NULL);
-
-               if (!enqueue) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to dequeue request; request not in queue");
-                       return FAILURE;
-               }
-               return h->ops->dequeue(h, enqueue);
-       }
-       return FAILURE;
-}
-
-php_http_client_enqueue_t *php_http_client_enqueued(php_http_client_t *h, void *compare_arg, php_http_client_enqueue_cmp_func_t compare_func)
-{
-       zend_llist_element *el = NULL;
-
-       if (compare_func) {
-               for (el = h->requests.head; el; el = el->next) {
-                       if (compare_func((php_http_client_enqueue_t *) el->data, compare_arg)) {
-                               break;
-                       }
-               }
-       } else {
-               for (el = h->requests.head; el; el = el->next) {
-                       if (((php_http_client_enqueue_t *) el->data)->request == compare_arg) {
-                               break;
-                       }
-               }
-       }
-       return el ? (php_http_client_enqueue_t *) el->data : NULL;
-}
-
-ZEND_RESULT_CODE php_http_client_wait(php_http_client_t *h, struct timeval *custom_timeout)
-{
-       if (h->ops->wait) {
-               return h->ops->wait(h, custom_timeout);
-       }
-
-       return FAILURE;
-}
-
-int php_http_client_once(php_http_client_t *h)
-{
-       if (h->ops->once) {
-               return h->ops->once(h);
-       }
-
-       return FAILURE;
-}
-
-ZEND_RESULT_CODE php_http_client_exec(php_http_client_t *h)
-{
-       if (h->ops->exec) {
-               return h->ops->exec(h);
-       }
-
-       return FAILURE;
-}
-
-void php_http_client_reset(php_http_client_t *h)
-{
-       if (h->ops->reset) {
-               h->ops->reset(h);
-       }
-
-       zend_llist_clean(&h->requests);
-       zend_llist_clean(&h->responses);
-}
-
-ZEND_RESULT_CODE php_http_client_setopt(php_http_client_t *h, php_http_client_setopt_opt_t opt, void *arg)
-{
-       if (h->ops->setopt) {
-               return h->ops->setopt(h, opt, arg);
-       }
-
-       return FAILURE;
-}
-
-ZEND_RESULT_CODE php_http_client_getopt(php_http_client_t *h, php_http_client_getopt_opt_t opt, void *arg, void *res_ptr)
-{
-       if (h->ops->getopt) {
-               return h->ops->getopt(h, opt, arg, res_ptr);
-       }
-       return FAILURE;
-}
-
-zend_class_entry *php_http_client_class_entry;
-static zend_object_handlers php_http_client_object_handlers;
-
-void php_http_client_object_free(void *object TSRMLS_DC)
-{
-       php_http_client_object_t *o = (php_http_client_object_t *) object;
-
-       php_http_client_free(&o->client);
-       php_http_object_method_dtor(&o->notify);
-       php_http_object_method_free(&o->update);
-       zend_object_std_dtor((zend_object *) o TSRMLS_CC);
-       efree(o);
-}
-
-zend_object_value php_http_client_object_new_ex(zend_class_entry *ce, php_http_client_t *client, php_http_client_object_t **ptr TSRMLS_DC)
-{
-       php_http_client_object_t *o;
-
-       o = ecalloc(1, sizeof(php_http_client_object_t));
-       zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
-       object_properties_init((zend_object *) o, ce);
-
-       o->client = client;
-
-       if (ptr) {
-               *ptr = o;
-       }
-
-       o->zv.handle = zend_objects_store_put(o, NULL, php_http_client_object_free, NULL TSRMLS_CC);
-       o->zv.handlers = &php_http_client_object_handlers;
-
-       return o->zv;
-}
-
-zend_object_value php_http_client_object_new(zend_class_entry *ce TSRMLS_DC)
-{
-       return php_http_client_object_new_ex(ce, NULL, NULL TSRMLS_CC);
-}
-
-static void handle_history(zval *zclient, php_http_message_t *request, php_http_message_t *response TSRMLS_DC)
-{
-       zval *new_hist, *old_hist = zend_read_property(php_http_client_class_entry, zclient, ZEND_STRL("history"), 0 TSRMLS_CC);
-       php_http_message_t *zipped = php_http_message_zip(response, request);
-       zend_object_value ov = php_http_message_object_new_ex(php_http_message_class_entry, zipped, NULL TSRMLS_CC);
-
-       MAKE_STD_ZVAL(new_hist);
-       ZVAL_OBJVAL(new_hist, ov, 0);
-
-       if (Z_TYPE_P(old_hist) == IS_OBJECT) {
-               php_http_message_object_prepend(new_hist, old_hist, 1 TSRMLS_CC);
-       }
-
-       zend_update_property(php_http_client_class_entry, zclient, ZEND_STRL("history"), new_hist TSRMLS_CC);
-       zval_ptr_dtor(&new_hist);
-}
-
-static ZEND_RESULT_CODE handle_response(void *arg, php_http_client_t *client, php_http_client_enqueue_t *e, php_http_message_t **response)
-{
-       zend_bool dequeue = 0;
-       zval zclient;
-       php_http_message_t *msg;
-       php_http_client_progress_state_t *progress;
-       TSRMLS_FETCH_FROM_CTX(client->ts);
-
-       INIT_PZVAL(&zclient);
-       ZVAL_OBJVAL(&zclient, ((php_http_client_object_t*) arg)->zv, 0);
-
-       if ((msg = *response)) {
-               php_http_message_object_t *msg_obj;
-               zval *info, *zresponse, *zrequest;
-               HashTable *info_ht;
-
-               /* ensure the message is of type response (could be uninitialized in case of early error, like DNS) */
-               php_http_message_set_type(msg, PHP_HTTP_RESPONSE);
-
-               if (z_is_true(zend_read_property(php_http_client_class_entry, &zclient, ZEND_STRL("recordHistory"), 0 TSRMLS_CC))) {
-                       handle_history(&zclient, e->request, *response TSRMLS_CC);
-               }
-
-               /* hard detach, redirects etc. are in the history */
-               php_http_message_free(&msg->parent);
-               *response = NULL;
-
-               MAKE_STD_ZVAL(zresponse);
-               ZVAL_OBJVAL(zresponse, php_http_message_object_new_ex(php_http_client_response_class_entry, msg, &msg_obj TSRMLS_CC), 0);
-
-               MAKE_STD_ZVAL(zrequest);
-               ZVAL_OBJVAL(zrequest, ((php_http_message_object_t *) e->opaque)->zv, 1);
-
-               php_http_message_object_prepend(zresponse, zrequest, 1 TSRMLS_CC);
-
-               MAKE_STD_ZVAL(info);
-               object_init(info);
-               info_ht = HASH_OF(info);
-               php_http_client_getopt(client, PHP_HTTP_CLIENT_OPT_TRANSFER_INFO, e->request, &info_ht);
-               zend_update_property(php_http_client_response_class_entry, zresponse, ZEND_STRL("transferInfo"), info TSRMLS_CC);
-               zval_ptr_dtor(&info);
-
-               zend_objects_store_add_ref_by_handle(msg_obj->zv.handle TSRMLS_CC);
-               zend_llist_add_element(&client->responses, &msg_obj);
-
-               if (e->closure.fci.size) {
-                       zval *retval = NULL;
-                       zend_error_handling zeh;
-
-                       zend_fcall_info_argn(&e->closure.fci TSRMLS_CC, 1, &zresponse);
-                       zend_replace_error_handling(EH_NORMAL, NULL, &zeh TSRMLS_CC);
-                       zend_fcall_info_call(&e->closure.fci, &e->closure.fcc, &retval, NULL TSRMLS_CC);
-                       zend_restore_error_handling(&zeh TSRMLS_CC);
-                       zend_fcall_info_argn(&e->closure.fci TSRMLS_CC, 0);
-
-                       if (retval) {
-                               if (Z_TYPE_P(retval) == IS_BOOL) {
-                                       dequeue = Z_BVAL_P(retval);
-                               }
-                               zval_ptr_dtor(&retval);
-                       }
-               }
-
-               zval_ptr_dtor(&zresponse);
-               zval_ptr_dtor(&zrequest);
-       }
-
-       if (SUCCESS == php_http_client_getopt(client, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, e->request, &progress)) {
-               progress->info = "finished";
-               progress->finished = 1;
-               client->callback.progress.func(client->callback.progress.arg, client, e, progress);
-       }
-
-       if (dequeue) {
-               php_http_client_dequeue(client, e->request);
-       }
-
-       return SUCCESS;
-}
-
-static void handle_progress(void *arg, php_http_client_t *client, php_http_client_enqueue_t *e, php_http_client_progress_state_t *progress)
-{
-       zval *zrequest, *zprogress, *zclient, **args[2];
-       php_http_client_object_t *client_obj = arg;
-       zend_error_handling zeh;
-       TSRMLS_FETCH_FROM_CTX(client->ts);
-
-       MAKE_STD_ZVAL(zclient);
-       ZVAL_OBJVAL(zclient, client_obj->zv, 1);
-
-       MAKE_STD_ZVAL(zrequest);
-       ZVAL_OBJVAL(zrequest, ((php_http_message_object_t *) e->opaque)->zv, 1);
-       args[0] = &zrequest;
-
-       MAKE_STD_ZVAL(zprogress);
-       object_init(zprogress);
-       add_property_bool(zprogress, "started", progress->started);
-       add_property_bool(zprogress, "finished", progress->finished);
-       add_property_string(zprogress, "info", STR_PTR(progress->info), 1);
-       add_property_double(zprogress, "dltotal", progress->dl.total);
-       add_property_double(zprogress, "dlnow", progress->dl.now);
-       add_property_double(zprogress, "ultotal", progress->ul.total);
-       add_property_double(zprogress, "ulnow", progress->ul.now);
-       args[1] = &zprogress;
-
-       zend_replace_error_handling(EH_NORMAL, NULL, &zeh TSRMLS_CC);
-       php_http_object_method_call(&client_obj->notify, zclient, NULL, 2, args TSRMLS_CC);
-       zend_restore_error_handling(&zeh TSRMLS_CC);
-
-       zval_ptr_dtor(&zclient);
-       zval_ptr_dtor(&zrequest);
-       zval_ptr_dtor(&zprogress);
-}
-
-static void response_dtor(void *data)
-{
-       php_http_message_object_t *msg_obj = *(php_http_message_object_t **) data;
-       TSRMLS_FETCH_FROM_CTX(msg_obj->message->ts);
-
-       zend_objects_store_del_ref_by_handle_ex(msg_obj->zv.handle, msg_obj->zv.handlers TSRMLS_CC);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_construct, 0, 0, 0)
-       ZEND_ARG_INFO(0, driver)
-       ZEND_ARG_INFO(0, persistent_handle_id)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, __construct)
-{
-               char *driver_str = NULL, *persistent_handle_str = NULL;
-               int driver_len = 0, persistent_handle_len = 0;
-               php_http_client_driver_t driver;
-               php_resource_factory_t *rf = NULL;
-               php_http_client_object_t *obj;
-               zval *os;
-
-               php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ss", &driver_str, &driver_len, &persistent_handle_str, &persistent_handle_len), invalid_arg, return);
-
-               if (SUCCESS != php_http_client_driver_get(driver_str, driver_len, &driver)) {
-                       php_http_throw(unexpected_val, "Failed to locate \"%s\" client request handler", driver_str);
-                       return;
-               }
-
-               MAKE_STD_ZVAL(os);
-               object_init_ex(os, spl_ce_SplObjectStorage);
-               zend_update_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), os TSRMLS_CC);
-               zval_ptr_dtor(&os);
-
-               if (persistent_handle_len) {
-                       char *name_str;
-                       size_t name_len;
-                       php_persistent_handle_factory_t *pf;
-
-                       name_len = spprintf(&name_str, 0, "http\\Client\\%s", driver.name_str);
-                       php_http_pretty_key(name_str + sizeof("http\\Client"), driver.name_len, 1, 1);
-
-                       if ((pf = php_persistent_handle_concede(NULL , name_str, name_len, persistent_handle_str, persistent_handle_len, NULL, NULL TSRMLS_CC))) {
-                               rf = php_persistent_handle_resource_factory_init(NULL, pf);
-                       }
-
-                       efree(name_str);
-               }
-
-               obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               php_http_expect(obj->client = php_http_client_init(NULL, driver.client_ops, rf, NULL TSRMLS_CC), runtime, return);
-
-               php_http_object_method_init(&obj->notify, getThis(), ZEND_STRL("notify") TSRMLS_CC);
-
-               obj->client->callback.response.func = handle_response;
-               obj->client->callback.response.arg = obj;
-               obj->client->callback.progress.func = handle_progress;
-               obj->client->callback.progress.arg = obj;
-
-               obj->client->responses.dtor = response_dtor;
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_reset, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, reset)
-{
-       php_http_client_object_t *obj;
-       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       obj->iterator = 0;
-       php_http_client_reset(obj->client);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-static HashTable *combined_options(zval *client, zval *request TSRMLS_DC)
-{
-       HashTable *options;
-       int num_options = 0;
-       zval *z_roptions = NULL, *z_coptions = zend_read_property(php_http_client_class_entry, client, ZEND_STRL("options"), 0 TSRMLS_CC);
-
-       if (Z_TYPE_P(z_coptions) == IS_ARRAY) {
-               num_options = zend_hash_num_elements(Z_ARRVAL_P(z_coptions));
-       }
-       zend_call_method_with_0_params(&request, NULL, NULL, "getOptions", &z_roptions);
-       if (z_roptions && Z_TYPE_P(z_roptions) == IS_ARRAY) {
-               int num = zend_hash_num_elements(Z_ARRVAL_P(z_roptions));
-               if (num > num_options) {
-                       num_options = num;
-               }
-       }
-       ALLOC_HASHTABLE(options);
-       ZEND_INIT_SYMTABLE_EX(options, num_options, 0);
-       if (Z_TYPE_P(z_coptions) == IS_ARRAY) {
-               array_copy(Z_ARRVAL_P(z_coptions), options);
-       }
-       if (z_roptions) {
-               if (Z_TYPE_P(z_roptions) == IS_ARRAY) {
-                       array_join(Z_ARRVAL_P(z_roptions), options, 0, 0);
-               }
-               zval_ptr_dtor(&z_roptions);
-       }
-       return options;
-}
-
-static void msg_queue_dtor(php_http_client_enqueue_t *e)
-{
-       php_http_message_object_t *msg_obj = e->opaque;
-       TSRMLS_FETCH_FROM_CTX(msg_obj->message->ts);
-
-       zend_objects_store_del_ref_by_handle_ex(msg_obj->zv.handle, msg_obj->zv.handlers TSRMLS_CC);
-       zend_hash_destroy(e->options);
-       FREE_HASHTABLE(e->options);
-
-       if (e->closure.fci.size) {
-               zval_ptr_dtor(&e->closure.fci.function_name);
-               if (e->closure.fci.object_ptr) {
-                       zval_ptr_dtor(&e->closure.fci.object_ptr);
-               }
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_enqueue, 0, 0, 1)
-       ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 0)
-       ZEND_ARG_INFO(0, callable)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, enqueue)
-{
-       zval *request;
-       zend_fcall_info fci = empty_fcall_info;
-       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_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|f", &request, php_http_client_request_class_entry, &fci, &fcc), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-       msg_obj = zend_object_store_get_object(request TSRMLS_CC);
-
-       if (php_http_client_enqueued(obj->client, msg_obj->message, NULL)) {
-               php_http_throw(bad_method_call, "Failed to enqueue request; request already in queue", NULL);
-               return;
-       }
-
-       q.request = msg_obj->message;
-       q.options = combined_options(getThis(), request TSRMLS_CC);
-       q.dtor = msg_queue_dtor;
-       q.opaque = msg_obj;
-       q.closure.fci = fci;
-       q.closure.fcc = fcc;
-
-       if (fci.size) {
-               Z_ADDREF_P(fci.function_name);
-               if (fci.object_ptr) {
-                       Z_ADDREF_P(fci.object_ptr);
-               }
-       }
-
-       zend_objects_store_add_ref_by_handle(msg_obj->zv.handle TSRMLS_CC);
-
-       php_http_expect(SUCCESS == php_http_client_enqueue(obj->client, &q), runtime,
-                       msg_queue_dtor(&q);
-                       return;
-       );
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_dequeue, 0, 0, 1)
-       ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, dequeue)
-{
-       zval *request;
-       php_http_client_object_t *obj;
-       php_http_message_object_t *msg_obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, php_http_client_request_class_entry), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-       msg_obj = zend_object_store_get_object(request TSRMLS_CC);
-
-       if (!php_http_client_enqueued(obj->client, msg_obj->message, NULL)) {
-               php_http_throw(bad_method_call, "Failed to dequeue request; request not in queue", NULL);
-               return;
-       }
-
-       php_http_expect(SUCCESS == php_http_client_dequeue(obj->client, msg_obj->message), runtime, return);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_requeue, 0, 0, 1)
-       ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 0)
-       ZEND_ARG_INFO(0, callable)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, requeue)
-{
-       zval *request;
-       zend_fcall_info fci = empty_fcall_info;
-       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_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|f", &request, php_http_client_request_class_entry, &fci, &fcc), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-       msg_obj = zend_object_store_get_object(request TSRMLS_CC);
-
-       if (php_http_client_enqueued(obj->client, msg_obj->message, NULL)) {
-               php_http_expect(SUCCESS == php_http_client_dequeue(obj->client, msg_obj->message), runtime, return);
-       }
-
-       q.request = msg_obj->message;
-       q.options = combined_options(getThis(), request TSRMLS_CC);
-       q.dtor = msg_queue_dtor;
-       q.opaque = msg_obj;
-       q.closure.fci = fci;
-       q.closure.fcc = fcc;
-
-       if (fci.size) {
-               Z_ADDREF_P(fci.function_name);
-               if (fci.object_ptr) {
-                       Z_ADDREF_P(fci.object_ptr);
-               }
-       }
-
-       zend_objects_store_add_ref_by_handle(msg_obj->zv.handle TSRMLS_CC);
-
-       php_http_expect(SUCCESS == php_http_client_enqueue(obj->client, &q), runtime,
-                       msg_queue_dtor(&q);
-                       return;
-       );
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_count, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, count)
-{
-       long count_mode = -1;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &count_mode)) {
-               php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               RETVAL_LONG(zend_llist_count(&obj->client->requests));
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getResponse, 0, 0, 0)
-       ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 1)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, getResponse)
-{
-       zval *zrequest = NULL;
-       php_http_client_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|O", &zrequest, php_http_client_request_class_entry), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       if (zrequest) {
-               /* lookup the response with the request */
-               zend_llist_element *el = NULL;
-               php_http_message_object_t *req_obj = zend_object_store_get_object(zrequest TSRMLS_CC);
-
-               for (el = obj->client->responses.head; el; el = el->next) {
-                       php_http_message_object_t *response_obj = *(php_http_message_object_t **) el->data;
-
-                       if (response_obj->message->parent == req_obj->message) {
-                               RETURN_OBJVAL(response_obj->zv, 1);
-                       }
-               }
-
-               /* not found for the request! */
-               php_http_throw(unexpected_val, "Could not find response for the request", NULL);
-               return;
-       }
-
-       /* pop off the last response */
-       if (obj->client->responses.tail) {
-               php_http_message_object_t *response_obj = *(php_http_message_object_t **) obj->client->responses.tail->data;
-
-               /* pop off and go */
-               if (response_obj) {
-                       RETVAL_OBJVAL(response_obj->zv, 1);
-                       zend_llist_remove_tail(&obj->client->responses);
-               }
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getHistory, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, getHistory)
-{
-       zval *zhistory;
-
-       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
-
-       zhistory = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("history"), 0 TSRMLS_CC);
-       RETVAL_ZVAL(zhistory, 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_send, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, send)
-{
-       php_http_client_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       php_http_expect(SUCCESS == php_http_client_exec(obj->client), runtime, return);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_once, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, once)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               RETURN_BOOL(0 < php_http_client_once(obj->client));
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_wait, 0, 0, 0)
-       ZEND_ARG_INFO(0, timeout)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, wait)
-{
-       double timeout = 0;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|d", &timeout)) {
-               struct timeval timeout_val;
-               php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               timeout_val.tv_sec = (time_t) timeout;
-               timeout_val.tv_usec = PHP_HTTP_USEC(timeout) % PHP_HTTP_MCROSEC;
-
-               RETURN_BOOL(SUCCESS == php_http_client_wait(obj->client, timeout > 0 ? &timeout_val : NULL));
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_configure, 0, 0, 1)
-       ZEND_ARG_ARRAY_INFO(0, settings, 1)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, configure)
-{
-       HashTable *settings = NULL;
-       php_http_client_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|H!", &settings), invalid_arg, return);
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       php_http_expect(SUCCESS == php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_CONFIGURATION, settings), unexpected_val, return);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_enablePipelining, 0, 0, 0)
-       ZEND_ARG_INFO(0, enable)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, enablePipelining)
-{
-       zend_bool enable = 1;
-       php_http_client_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &enable), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       php_http_expect(SUCCESS == php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_ENABLE_PIPELINING, &enable), unexpected_val, return);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_enableEvents, 0, 0, 0)
-       ZEND_ARG_INFO(0, enable)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, enableEvents)
-{
-       zend_bool enable = 1;
-       php_http_client_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &enable), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       php_http_expect(SUCCESS == php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_USE_EVENTS, &enable), unexpected_val, return);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-struct notify_arg {
-       php_http_object_method_t *cb;
-       zval **args[3];
-       int argc;
-};
-
-static int notify(zend_object_iterator *iter, void *puser TSRMLS_DC)
-{
-       zval **observer = NULL;
-       struct notify_arg *arg = puser;
-
-       iter->funcs->get_current_data(iter, &observer TSRMLS_CC);
-       if (observer) {
-               return php_http_object_method_call(arg->cb, *observer, NULL, arg->argc, arg->args TSRMLS_CC);
-       }
-       return FAILURE;
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_notify, 0, 0, 0)
-       ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 1)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, notify)
-{
-       zval *request = NULL, *zprogress = NULL, *observers;
-       php_http_client_object_t *client_obj;
-       struct notify_arg arg = {NULL};
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|O!o!", &request, php_http_client_request_class_entry, &zprogress), invalid_arg, return);
-
-       client_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-       observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
-
-       if (Z_TYPE_P(observers) != IS_OBJECT) {
-               php_http_throw(unexpected_val, "Observer storage is corrupted", NULL);
-               return;
-       }
-
-       if (client_obj->update) {
-               arg.cb = client_obj->update;
-
-               Z_ADDREF_P(getThis());
-               arg.args[0] = &getThis();
-               arg.argc = 1;
-
-               if (request) {
-                       Z_ADDREF_P(request);
-                       arg.args[1] = &request;
-                       arg.argc += 1;
-               }
-
-               if (zprogress) {
-                       Z_ADDREF_P(zprogress);
-                       arg.args[2] = &zprogress;
-                       arg.argc += 1;
-               }
-
-               spl_iterator_apply(observers, notify, &arg TSRMLS_CC);
-
-               zval_ptr_dtor(&getThis());
-               if (request) {
-                       zval_ptr_dtor(&request);
-               }
-               if (zprogress) {
-                       zval_ptr_dtor(&zprogress);
-               }
-       }
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_attach, 0, 0, 1)
-       ZEND_ARG_OBJ_INFO(0, observer, SplObserver, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, attach)
-{
-       zval *observers, *observer, *retval = NULL;
-       php_http_client_object_t *client_obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &observer, spl_ce_SplObserver), invalid_arg, return);
-
-       client_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-       observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
-
-       if (Z_TYPE_P(observers) != IS_OBJECT) {
-               php_http_throw(unexpected_val, "Observer storage is corrupted", NULL);
-               return;
-       }
-
-       if (!client_obj->update) {
-               client_obj->update = php_http_object_method_init(NULL, observer, ZEND_STRL("update") TSRMLS_CC);
-       }
-
-       zend_call_method_with_1_params(&observers, NULL, NULL, "attach", &retval, observer);
-       if (retval) {
-               zval_ptr_dtor(&retval);
-       }
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_detach, 0, 0, 1)
-       ZEND_ARG_OBJ_INFO(0, observer, SplObserver, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, detach)
-{
-       zval *observers, *observer, *retval = NULL;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &observer, spl_ce_SplObserver), invalid_arg, return);
-
-       observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
-
-       if (Z_TYPE_P(observers) != IS_OBJECT) {
-               php_http_throw(unexpected_val, "Observer storage is corrupted", NULL);
-               return;
-       }
-
-       zend_call_method_with_1_params(&observers, NULL, NULL, "detach", &retval, observer);
-       if (retval) {
-               zval_ptr_dtor(&retval);
-       }
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getObservers, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, getObservers)
-{
-       zval *observers;
-
-       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
-
-       observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
-
-       if (Z_TYPE_P(observers) != IS_OBJECT) {
-               php_http_throw(unexpected_val, "Observer storage is corrupted", NULL);
-               return;
-       }
-
-       RETVAL_ZVAL(observers, 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getProgressInfo, 0, 0, 1)
-       ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, getProgressInfo)
-{
-       zval *request;
-       php_http_client_object_t *obj;
-       php_http_message_object_t *req_obj;
-       php_http_client_progress_state_t *progress;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, php_http_client_request_class_entry), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-       req_obj = zend_object_store_get_object(request TSRMLS_CC);
-
-       php_http_expect(SUCCESS == php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, req_obj->message, &progress), unexpected_val, return);
-
-       object_init(return_value);
-       add_property_bool(return_value, "started", progress->started);
-       add_property_bool(return_value, "finished", progress->finished);
-       add_property_string(return_value, "info", STR_PTR(progress->info), 1);
-       add_property_double(return_value, "dltotal", progress->dl.total);
-       add_property_double(return_value, "dlnow", progress->dl.now);
-       add_property_double(return_value, "ultotal", progress->ul.total);
-       add_property_double(return_value, "ulnow", progress->ul.now);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getTransferInfo, 0, 0, 1)
-       ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, getTransferInfo)
-{
-       zval *request;
-       HashTable *info;
-       php_http_client_object_t *obj;
-       php_http_message_object_t *req_obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, php_http_client_request_class_entry), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-       req_obj = zend_object_store_get_object(request TSRMLS_CC);
-
-       object_init(return_value);
-       info = HASH_OF(return_value);
-       php_http_expect(SUCCESS == php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_TRANSFER_INFO, req_obj->message, &info), unexpected_val, return);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_setOptions, 0, 0, 0)
-       ZEND_ARG_ARRAY_INFO(0, options, 1)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, setOptions)
-{
-       zval *opts = NULL;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return);
-
-       php_http_client_options_set(getThis(), opts TSRMLS_CC);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getOptions, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, getOptions)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               zval *options = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
-               RETVAL_ZVAL(options, 1, 0);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_setSslOptions, 0, 0, 0)
-       ZEND_ARG_ARRAY_INFO(0, ssl_option, 1)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, setSslOptions)
-{
-       zval *opts = NULL;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return);
-
-       php_http_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 1 TSRMLS_CC);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_addSslOptions, 0, 0, 0)
-       ZEND_ARG_ARRAY_INFO(0, ssl_options, 1)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, addSslOptions)
-{
-       zval *opts = NULL;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return);
-
-       php_http_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 0 TSRMLS_CC);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getSslOptions, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, getSslOptions)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_client_options_get_subr(getThis(), ZEND_STRS("ssl"), return_value TSRMLS_CC);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_setCookies, 0, 0, 0)
-       ZEND_ARG_ARRAY_INFO(0, cookies, 1)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, setCookies)
-{
-       zval *opts = NULL;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return);
-
-       php_http_client_options_set_subr(getThis(), ZEND_STRS("cookies"), opts, 1 TSRMLS_CC);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_addCookies, 0, 0, 0)
-       ZEND_ARG_ARRAY_INFO(0, cookies, 1)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, addCookies)
-{
-       zval *opts = NULL;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return);
-
-       php_http_client_options_set_subr(getThis(), ZEND_STRS("cookies"), opts, 0 TSRMLS_CC);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getCookies, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, getCookies)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_client_options_get_subr(getThis(), ZEND_STRS("cookies"), return_value TSRMLS_CC);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getAvailableDrivers, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, getAvailableDrivers) {
-       if (SUCCESS == zend_parse_parameters_none()) {
-               array_init(return_value);
-               php_http_client_driver_list(Z_ARRVAL_P(return_value) TSRMLS_CC);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getAvailableOptions, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, getAvailableOptions)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               array_init(return_value);
-               php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_AVAILABLE_OPTIONS, NULL, &Z_ARRVAL_P(return_value));
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getAvailableConfiguration, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClient, getAvailableConfiguration)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               array_init(return_value);
-               php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_AVAILABLE_CONFIGURATION, NULL, &Z_ARRVAL_P(return_value));
-       }
-}
-
-static zend_function_entry php_http_client_methods[] = {
-       PHP_ME(HttpClient, __construct,          ai_HttpClient_construct,            ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
-       PHP_ME(HttpClient, reset,                ai_HttpClient_reset,                ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, enqueue,              ai_HttpClient_enqueue,              ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, dequeue,              ai_HttpClient_dequeue,              ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, requeue,              ai_HttpClient_requeue,              ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, count,                ai_HttpClient_count,                ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, send,                 ai_HttpClient_send,                 ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, once,                 ai_HttpClient_once,                 ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, wait,                 ai_HttpClient_wait,                 ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, getResponse,          ai_HttpClient_getResponse,          ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, getHistory,           ai_HttpClient_getHistory,           ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, configure,            ai_HttpClient_configure,            ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, enablePipelining,     ai_HttpClient_enablePipelining,     ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
-       PHP_ME(HttpClient, enableEvents,         ai_HttpClient_enableEvents,         ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
-       PHP_ME(HttpClient, notify,               ai_HttpClient_notify,               ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, attach,               ai_HttpClient_attach,               ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, detach,               ai_HttpClient_detach,               ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, getObservers,         ai_HttpClient_getObservers,         ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, getProgressInfo,      ai_HttpClient_getProgressInfo,      ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, getTransferInfo,      ai_HttpClient_getTransferInfo,      ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, setOptions,           ai_HttpClient_setOptions,           ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, getOptions,           ai_HttpClient_getOptions,           ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, setSslOptions,        ai_HttpClient_setSslOptions,        ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, addSslOptions,        ai_HttpClient_addSslOptions,        ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, getSslOptions,        ai_HttpClient_getSslOptions,        ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, setCookies,           ai_HttpClient_setCookies,           ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, addCookies,           ai_HttpClient_addCookies,           ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, getCookies,           ai_HttpClient_getCookies,           ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, getAvailableDrivers,  ai_HttpClient_getAvailableDrivers,  ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-       PHP_ME(HttpClient, getAvailableOptions,  ai_HttpClient_getAvailableOptions,  ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClient, getAvailableConfiguration, ai_HttpClient_getAvailableConfiguration, ZEND_ACC_PUBLIC)
-       EMPTY_FUNCTION_ENTRY
-};
-
-PHP_MINIT_FUNCTION(http_client)
-{
-       zend_class_entry ce = {0};
-
-       INIT_NS_CLASS_ENTRY(ce, "http", "Client", php_http_client_methods);
-       php_http_client_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
-       php_http_client_class_entry->create_object = php_http_client_object_new;
-       zend_class_implements(php_http_client_class_entry TSRMLS_CC, 2, spl_ce_SplSubject, spl_ce_Countable);
-       memcpy(&php_http_client_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
-       php_http_client_object_handlers.clone_obj = NULL;
-       zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("observers"), ZEND_ACC_PRIVATE TSRMLS_CC);
-       zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("options"), ZEND_ACC_PROTECTED TSRMLS_CC);
-       zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("history"), ZEND_ACC_PROTECTED TSRMLS_CC);
-       zend_declare_property_bool(php_http_client_class_entry, ZEND_STRL("recordHistory"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
-
-       zend_hash_init(&php_http_client_drivers, 2, NULL, NULL, 1);
-
-       return SUCCESS;
-}
-
-PHP_MSHUTDOWN_FUNCTION(http_client)
-{
-       zend_hash_destroy(&php_http_client_drivers);
-       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/php_http_client.h b/php_http_client.h
deleted file mode 100644 (file)
index f4a5b59..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_CLIENT_H
-#define PHP_HTTP_CLIENT_H
-
-typedef enum php_http_client_setopt_opt {
-       PHP_HTTP_CLIENT_OPT_ENABLE_PIPELINING,
-       PHP_HTTP_CLIENT_OPT_USE_EVENTS,
-       PHP_HTTP_CLIENT_OPT_CONFIGURATION,
-} php_http_client_setopt_opt_t;
-
-typedef enum php_http_client_getopt_opt {
-       PHP_HTTP_CLIENT_OPT_PROGRESS_INFO,              /* php_http_client_enqueue_t*, php_http_client_progress_state_t** */
-       PHP_HTTP_CLIENT_OPT_TRANSFER_INFO,              /* php_http_client_enqueue_t*, HashTable* */
-       PHP_HTTP_CLIENT_OPT_AVAILABLE_OPTIONS,          /* NULL, HashTable* */
-       PHP_HTTP_CLIENT_OPT_AVAILABLE_CONFIGURATION,/* NULL, HashTable */
-} php_http_client_getopt_opt_t;
-
-typedef struct php_http_client_enqueue {
-       php_http_message_t *request; /* unique */
-       HashTable *options;
-       void (*dtor)(struct php_http_client_enqueue *);
-       void *opaque;
-       struct {
-               zend_fcall_info fci;
-               zend_fcall_info_cache fcc;
-       } closure;
-} php_http_client_enqueue_t;
-
-typedef struct php_http_client *(*php_http_client_init_func_t)(struct php_http_client *p, void *init_arg);
-typedef struct php_http_client *(*php_http_client_copy_func_t)(struct php_http_client *from, struct php_http_client *to);
-typedef void (*php_http_client_dtor_func_t)(struct php_http_client *p);
-typedef void (*php_http_client_reset_func_t)(struct php_http_client *p);
-typedef ZEND_RESULT_CODE (*php_http_client_exec_func_t)(struct php_http_client *p);
-typedef int (*php_http_client_once_func_t)(struct php_http_client *p);
-typedef ZEND_RESULT_CODE (*php_http_client_wait_func_t)(struct php_http_client *p, struct timeval *custom_timeout);
-typedef ZEND_RESULT_CODE (*php_http_client_enqueue_func_t)(struct php_http_client *p, php_http_client_enqueue_t *enqueue);
-typedef ZEND_RESULT_CODE (*php_http_client_dequeue_func_t)(struct php_http_client *p, php_http_client_enqueue_t *enqueue);
-typedef ZEND_RESULT_CODE (*php_http_client_setopt_func_t)(struct php_http_client *p, php_http_client_setopt_opt_t opt, void *arg);
-typedef ZEND_RESULT_CODE (*php_http_client_getopt_func_t)(struct php_http_client *h, php_http_client_getopt_opt_t opt, void *arg, void **res);
-
-typedef struct php_http_client_ops {
-       php_resource_factory_ops_t *rsrc;
-       php_http_client_init_func_t init;
-       php_http_client_copy_func_t copy;
-       php_http_client_dtor_func_t dtor;
-       php_http_client_reset_func_t reset;
-       php_http_client_exec_func_t exec;
-       php_http_client_wait_func_t wait;
-       php_http_client_once_func_t once;
-       php_http_client_enqueue_func_t enqueue;
-       php_http_client_dequeue_func_t dequeue;
-       php_http_client_setopt_func_t setopt;
-       php_http_client_getopt_func_t getopt;
-} php_http_client_ops_t;
-
-typedef struct php_http_client_driver {
-       const char *name_str;
-       size_t name_len;
-       php_http_client_ops_t *client_ops;
-} php_http_client_driver_t;
-
-PHP_HTTP_API ZEND_RESULT_CODE php_http_client_driver_add(php_http_client_driver_t *driver);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_client_driver_get(const char *name_str, size_t name_len, php_http_client_driver_t *driver);
-
-typedef struct php_http_client_progress_state {
-       struct {
-               double now;
-               double total;
-       } ul;
-       struct {
-               double now;
-               double total;
-       } dl;
-       const char *info;
-       unsigned started:1;
-       unsigned finished:1;
-} php_http_client_progress_state_t;
-
-typedef ZEND_RESULT_CODE (*php_http_client_response_callback_t)(void *arg, struct php_http_client *client, php_http_client_enqueue_t *e, php_http_message_t **response);
-typedef void (*php_http_client_progress_callback_t)(void *arg, struct php_http_client *client, php_http_client_enqueue_t *e, php_http_client_progress_state_t *state);
-
-typedef struct php_http_client {
-       void *ctx;
-       php_resource_factory_t *rf;
-       php_http_client_ops_t *ops;
-
-       struct {
-               struct {
-                       php_http_client_response_callback_t func;
-                       void *arg;
-               } response;
-               struct {
-                       php_http_client_progress_callback_t func;
-                       void *arg;
-               } progress;
-       } callback;
-
-       zend_llist requests;
-       zend_llist responses;
-
-#ifdef ZTS
-       void ***ts;
-#endif
-} php_http_client_t;
-
-PHP_HTTP_API zend_class_entry *php_http_client_class_entry;
-
-typedef struct php_http_client_object {
-       zend_object zo;
-       zend_object_value zv;
-       php_http_client_t *client;
-       long iterator;
-       php_http_object_method_t *update;
-       php_http_object_method_t notify;
-} php_http_client_object_t;
-
-PHP_HTTP_API php_http_client_t *php_http_client_init(php_http_client_t *h, php_http_client_ops_t *ops, php_resource_factory_t *rf, void *init_arg TSRMLS_DC);
-PHP_HTTP_API php_http_client_t *php_http_client_copy(php_http_client_t *from, php_http_client_t *to);
-PHP_HTTP_API void php_http_client_dtor(php_http_client_t *h);
-PHP_HTTP_API void php_http_client_free(php_http_client_t **h);
-
-PHP_HTTP_API ZEND_RESULT_CODE php_http_client_enqueue(php_http_client_t *h, php_http_client_enqueue_t *enqueue);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_client_dequeue(php_http_client_t *h, php_http_message_t *request);
-
-PHP_HTTP_API ZEND_RESULT_CODE php_http_client_wait(php_http_client_t *h, struct timeval *custom_timeout);
-PHP_HTTP_API int php_http_client_once(php_http_client_t *h);
-
-PHP_HTTP_API ZEND_RESULT_CODE php_http_client_exec(php_http_client_t *h);
-PHP_HTTP_API void php_http_client_reset(php_http_client_t *h);
-
-PHP_HTTP_API ZEND_RESULT_CODE php_http_client_setopt(php_http_client_t *h, php_http_client_setopt_opt_t opt, void *arg);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_client_getopt(php_http_client_t *h, php_http_client_getopt_opt_t opt, void *arg, void *res_ptr);
-
-typedef int (*php_http_client_enqueue_cmp_func_t)(php_http_client_enqueue_t *cmp, void *arg);
-/* compare with request message pointer if compare_func is NULL */
-PHP_HTTP_API php_http_client_enqueue_t *php_http_client_enqueued(php_http_client_t *h, void *compare_arg, php_http_client_enqueue_cmp_func_t compare_func);
-
-PHP_MINIT_FUNCTION(http_client);
-PHP_MSHUTDOWN_FUNCTION(http_client);
-
-#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/php_http_client_curl.c b/php_http_client_curl.c
deleted file mode 100644 (file)
index fed92fc..0000000
+++ /dev/null
@@ -1,2599 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-#include "php_http_client.h"
-
-#if PHP_HTTP_HAVE_CURL
-
-#if PHP_HTTP_HAVE_EVENT
-#      if !PHP_HTTP_HAVE_EVENT2 && /* just be really sure */ !(LIBEVENT_VERSION_NUMBER >= 0x02000000)
-#              include <event.h>
-#              define event_base_new event_init
-#              define event_assign(e, b, s, a, cb, d) do {\
-                       event_set(e, s, a, cb, d); \
-                       event_base_set(b, e); \
-               } while(0)
-#      else
-#              if PHP_HTTP_HAVE_EVENT2
-#                      include <event2/event.h>
-#                      include <event2/event_struct.h>
-#              else
-#                      error "libevent presence is unknown"
-#              endif
-#      endif
-#      ifndef DBG_EVENTS
-#              define DBG_EVENTS 0
-#      endif
-#endif
-
-#ifdef PHP_HTTP_HAVE_OPENSSL
-#      include <openssl/ssl.h>
-#endif
-#ifdef PHP_HTTP_HAVE_GNUTLS
-#      include <gnutls.h>
-#endif
-
-typedef struct php_http_client_curl {
-       CURLM *handle;
-
-       int unfinished;  /* int because of curl_multi_perform() */
-
-#if PHP_HTTP_HAVE_EVENT
-       struct event_base *evbase;
-       struct event *timeout;
-       unsigned useevents:1;
-#endif
-} php_http_client_curl_t;
-
-typedef struct php_http_client_curl_handler {
-       CURL *handle;
-       php_resource_factory_t *rf;
-       php_http_client_t *client;
-       php_http_client_progress_state_t progress;
-       php_http_client_enqueue_t queue;
-
-       struct {
-               php_http_buffer_t headers;
-               php_http_message_body_t *body;
-       } response;
-
-       struct {
-               HashTable cache;
-
-               struct curl_slist *proxyheaders;
-               struct curl_slist *headers;
-               struct curl_slist *resolve;
-               php_http_buffer_t cookies;
-               php_http_buffer_t ranges;
-
-               long redirects;
-               unsigned range_request:1;
-               unsigned encode_cookies:1;
-
-               struct {
-                       uint count;
-                       double delay;
-               } retry;
-
-       } options;
-
-} php_http_client_curl_handler_t;
-
-typedef struct php_http_curle_storage {
-       char *url;
-       char *cookiestore;
-       CURLcode errorcode;
-       char errorbuffer[0x100];
-} php_http_curle_storage_t;
-
-static inline php_http_curle_storage_t *php_http_curle_get_storage(CURL *ch) {
-       php_http_curle_storage_t *st = NULL;
-
-       curl_easy_getinfo(ch, CURLINFO_PRIVATE, &st);
-
-       if (!st) {
-               st = pecalloc(1, sizeof(*st), 1);
-               curl_easy_setopt(ch, CURLOPT_PRIVATE, st);
-               curl_easy_setopt(ch, CURLOPT_ERRORBUFFER, st->errorbuffer);
-       }
-
-       return st;
-}
-
-static void *php_http_curle_ctor(void *opaque, void *init_arg TSRMLS_DC)
-{
-       void *ch;
-
-       if ((ch = curl_easy_init())) {
-               php_http_curle_get_storage(ch);
-               return ch;
-       }
-       return NULL;
-}
-
-static void *php_http_curle_copy(void *opaque, void *handle TSRMLS_DC)
-{
-       void *ch;
-
-       if ((ch = curl_easy_duphandle(handle))) {
-               curl_easy_reset(ch);
-               php_http_curle_get_storage(ch);
-               return ch;
-       }
-       return NULL;
-}
-
-static void php_http_curle_dtor(void *opaque, void *handle TSRMLS_DC)
-{
-       php_http_curle_storage_t *st = php_http_curle_get_storage(handle);
-
-       curl_easy_cleanup(handle);
-
-       if (st) {
-               if (st->url) {
-                       pefree(st->url, 1);
-               }
-               if (st->cookiestore) {
-                       pefree(st->cookiestore, 1);
-               }
-               pefree(st, 1);
-       }
-}
-
-static php_resource_factory_ops_t php_http_curle_resource_factory_ops = {
-       php_http_curle_ctor,
-       php_http_curle_copy,
-       php_http_curle_dtor
-};
-
-static void *php_http_curlm_ctor(void *opaque, void *init_arg TSRMLS_DC)
-{
-       return curl_multi_init();
-}
-
-static void php_http_curlm_dtor(void *opaque, void *handle TSRMLS_DC)
-{
-       curl_multi_cleanup(handle);
-}
-
-static php_resource_factory_ops_t php_http_curlm_resource_factory_ops = {
-       php_http_curlm_ctor,
-       NULL,
-       php_http_curlm_dtor
-};
-
-/* curl callbacks */
-
-static size_t php_http_curle_read_callback(void *data, size_t len, size_t n, void *ctx)
-{
-       php_http_message_body_t *body = ctx;
-
-       if (body && body->stream_id) {
-               php_stream *s = php_http_message_body_stream(body);
-
-               if (s) {
-                       TSRMLS_FETCH_FROM_CTX(body->ts);
-                       return php_stream_read(s, data, len * n);
-               } else abort();
-       }
-       return 0;
-}
-
-#if PHP_HTTP_CURL_VERSION(7,32,0)
-static int php_http_curle_xferinfo_callback(void *ctx, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
-#else
-static int php_http_curle_progress_callback(void *ctx, double dltotal, double dlnow, double ultotal, double ulnow)
-#endif
-{
-       php_http_client_curl_handler_t *h = ctx;
-       zend_bool update = 0;
-
-       if (h->progress.dl.total != dltotal
-       ||      h->progress.dl.now != dlnow
-       ||      h->progress.ul.total != ultotal
-       ||      h->progress.ul.now != ulnow
-       ) {
-               update = 1;
-
-               h->progress.dl.total = dltotal;
-               h->progress.dl.now = dlnow;
-               h->progress.ul.total = ultotal;
-               h->progress.ul.now = ulnow;
-       }
-
-       if (update && h->client->callback.progress.func) {
-               h->client->callback.progress.func(h->client->callback.progress.arg, h->client, &h->queue, &h->progress);
-       }
-
-       return 0;
-}
-
-static int php_http_curle_seek_callback(void *userdata, curl_off_t offset, int origin)
-{
-       php_http_message_body_t *body = userdata;
-       TSRMLS_FETCH_FROM_CTX(body->ts);
-
-       if (!body) {
-               return 1;
-       }
-       if (0 == php_stream_seek(php_http_message_body_stream(body), offset, origin)) {
-               return 0;
-       }
-       return 2;
-}
-
-static int php_http_curle_raw_callback(CURL *ch, curl_infotype type, char *data, size_t length, void *ctx)
-{
-       php_http_client_curl_handler_t *h = ctx;
-
-       /* catch progress */
-       switch (type) {
-               case CURLINFO_TEXT:
-                       if (data[0] == '-') {
-                       } 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)) {
-                               h->progress.info = "setup";
-                       } else if (php_memnstr(data, ZEND_STRL("About to connect"), data + length)) {
-                               h->progress.info = "resolve";
-                       } else if (php_memnstr(data, ZEND_STRL("Trying"), data + length)) {
-                               h->progress.info = "connect";
-                       } else if (php_memnstr(data, ZEND_STRL("Found bundle for host"), data + length)) {
-                               h->progress.info = "connect";
-                       } else if (php_memnstr(data, ZEND_STRL("Connected"), data + length)) {
-                               h->progress.info = "connected";
-                       } else if (php_memnstr(data, ZEND_STRL("Re-using existing connection!"), data + length)) {
-                               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("SSL"), data + length)) {
-                               h->progress.info = "ssl negotiation";
-                       } 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)) {
-                               h->progress.info = "not disconnected";
-                       } else if (php_memnstr(data, ZEND_STRL("closed"), data + length)) {
-                               h->progress.info = "disconnected";
-                       } else if (php_memnstr(data, ZEND_STRL("Issue another request"), data + length)) {
-                               h->progress.info = "redirect";
-                       } else if (php_memnstr(data, ZEND_STRL("Operation timed out"), data + length)) {
-                               h->progress.info = "timeout";
-                       } else {
-#if 0
-                               h->progress.info = data;
-                               data[length - 1] = '\0';
-#endif
-                       }
-                       if (h->client->callback.progress.func) {
-                               h->client->callback.progress.func(h->client->callback.progress.arg, h->client, &h->queue, &h->progress);
-                       }
-                       break;
-               case CURLINFO_HEADER_OUT:
-               case CURLINFO_DATA_OUT:
-               case CURLINFO_SSL_DATA_OUT:
-                       h->progress.info = "send";
-                       break;
-               case CURLINFO_HEADER_IN:
-               case CURLINFO_DATA_IN:
-               case CURLINFO_SSL_DATA_IN:
-                       h->progress.info = "receive";
-                       break;
-               default:
-                       break;
-       }
-
-#if 0
-       /* debug */
-       _dpf(type, data, length);
-#endif
-
-       return 0;
-}
-
-static int php_http_curle_header_callback(char *data, size_t n, size_t l, void *arg)
-{
-       php_http_client_curl_handler_t *h = arg;
-
-       return php_http_buffer_append(&h->response.headers, data, n * l);
-}
-
-static int php_http_curle_body_callback(char *data, size_t n, size_t l, void *arg)
-{
-       php_http_client_curl_handler_t *h = arg;
-
-       return php_http_message_body_append(h->response.body, data, n*l);
-}
-
-static ZEND_RESULT_CODE php_http_curle_get_info(CURL *ch, HashTable *info)
-{
-       char *c;
-       long l;
-       double d;
-       struct curl_slist *s, *p;
-       zval *subarray, array;
-       INIT_PZVAL_ARRAY(&array, info);
-
-       /* BEGIN::CURLINFO */
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_EFFECTIVE_URL, &c)) {
-               add_assoc_string_ex(&array, "effective_url", sizeof("effective_url"), c ? c : "", 1);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &l)) {
-               add_assoc_long_ex(&array, "response_code", sizeof("response_code"), l);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_TOTAL_TIME, &d)) {
-               add_assoc_double_ex(&array, "total_time", sizeof("total_time"), d);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_NAMELOOKUP_TIME, &d)) {
-               add_assoc_double_ex(&array, "namelookup_time", sizeof("namelookup_time"), d);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONNECT_TIME, &d)) {
-               add_assoc_double_ex(&array, "connect_time", sizeof("connect_time"), d);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PRETRANSFER_TIME, &d)) {
-               add_assoc_double_ex(&array, "pretransfer_time", sizeof("pretransfer_time"), d);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SIZE_UPLOAD, &d)) {
-               add_assoc_double_ex(&array, "size_upload", sizeof("size_upload"), d);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SIZE_DOWNLOAD, &d)) {
-               add_assoc_double_ex(&array, "size_download", sizeof("size_download"), d);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SPEED_DOWNLOAD, &d)) {
-               add_assoc_double_ex(&array, "speed_download", sizeof("speed_download"), d);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SPEED_UPLOAD, &d)) {
-               add_assoc_double_ex(&array, "speed_upload", sizeof("speed_upload"), d);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HEADER_SIZE, &l)) {
-               add_assoc_long_ex(&array, "header_size", sizeof("header_size"), l);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REQUEST_SIZE, &l)) {
-               add_assoc_long_ex(&array, "request_size", sizeof("request_size"), l);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SSL_VERIFYRESULT, &l)) {
-               add_assoc_long_ex(&array, "ssl_verifyresult", sizeof("ssl_verifyresult"), l);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_FILETIME, &l)) {
-               add_assoc_long_ex(&array, "filetime", sizeof("filetime"), l);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d)) {
-               add_assoc_double_ex(&array, "content_length_download", sizeof("content_length_download"), d);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_LENGTH_UPLOAD, &d)) {
-               add_assoc_double_ex(&array, "content_length_upload", sizeof("content_length_upload"), d);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_STARTTRANSFER_TIME, &d)) {
-               add_assoc_double_ex(&array, "starttransfer_time", sizeof("starttransfer_time"), d);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_TYPE, &c)) {
-               add_assoc_string_ex(&array, "content_type", sizeof("content_type"), c ? c : "", 1);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_TIME, &d)) {
-               add_assoc_double_ex(&array, "redirect_time", sizeof("redirect_time"), d);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_COUNT, &l)) {
-               add_assoc_long_ex(&array, "redirect_count", sizeof("redirect_count"), l);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HTTP_CONNECTCODE, &l)) {
-               add_assoc_long_ex(&array, "connect_code", sizeof("connect_code"), l);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HTTPAUTH_AVAIL, &l)) {
-               add_assoc_long_ex(&array, "httpauth_avail", sizeof("httpauth_avail"), l);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PROXYAUTH_AVAIL, &l)) {
-               add_assoc_long_ex(&array, "proxyauth_avail", sizeof("proxyauth_avail"), l);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_OS_ERRNO, &l)) {
-               add_assoc_long_ex(&array, "os_errno", sizeof("os_errno"), l);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_NUM_CONNECTS, &l)) {
-               add_assoc_long_ex(&array, "num_connects", sizeof("num_connects"), l);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SSL_ENGINES, &s)) {
-               MAKE_STD_ZVAL(subarray);
-               array_init(subarray);
-               for (p = s; p; p = p->next) {
-                       if (p->data) {
-                               add_next_index_string(subarray, p->data, 1);
-                       }
-               }
-               add_assoc_zval_ex(&array, "ssl_engines", sizeof("ssl_engines"), subarray);
-               curl_slist_free_all(s);
-       }
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_URL, &c)) {
-               add_assoc_string_ex(&array, "redirect_url", sizeof("redirect_url"), c ? c : "", 1);
-       }
-#if PHP_HTTP_CURL_VERSION(7,19,0)
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PRIMARY_IP, &c)) {
-               add_assoc_string_ex(&array, "primary_ip", sizeof("primary_ip"), c ? c : "", 1);
-       }
-#endif
-#if PHP_HTTP_CURL_VERSION(7,19,0)
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_APPCONNECT_TIME, &d)) {
-               add_assoc_double_ex(&array, "appconnect_time", sizeof("appconnect_time"), d);
-       }
-#endif
-#if PHP_HTTP_CURL_VERSION(7,19,4)
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONDITION_UNMET, &l)) {
-               add_assoc_long_ex(&array, "condition_unmet", sizeof("condition_unmet"), l);
-       }
-#endif
-#if PHP_HTTP_CURL_VERSION(7,21,0)
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PRIMARY_PORT, &l)) {
-               add_assoc_long_ex(&array, "primary_port", sizeof("primary_port"), l);
-       }
-#endif
-#if PHP_HTTP_CURL_VERSION(7,21,0)
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_LOCAL_IP, &c)) {
-               add_assoc_string_ex(&array, "local_ip", sizeof("local_ip"), c ? c : "", 1);
-       }
-#endif
-#if PHP_HTTP_CURL_VERSION(7,21,0)
-       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_LOCAL_PORT, &l)) {
-               add_assoc_long_ex(&array, "local_port", sizeof("local_port"), l);
-       }
-#endif
-
-       /* END::CURLINFO */
-
-#if PHP_HTTP_CURL_VERSION(7,34,0)
-       {
-               zval *ti_array;
-               struct curl_tlssessioninfo *ti;
-
-               if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_TLS_SESSION, &ti)) {
-                       const char *backend;
-
-                       MAKE_STD_ZVAL(subarray);
-                       ZVAL_NULL(subarray);
-                       MAKE_STD_ZVAL(ti_array);
-                       array_init(ti_array);
-
-                       switch (ti->backend) {
-                       case CURLSSLBACKEND_NONE:
-                               backend = "none";
-                               break;
-                       case CURLSSLBACKEND_OPENSSL:
-                               backend = "openssl";
-#ifdef PHP_HTTP_HAVE_OPENSSL
-                               {
-                                       SSL_CTX *ctx = ti->internals;
-
-                                       array_init(subarray);
-                                       add_assoc_long_ex(subarray, ZEND_STRS("number"), SSL_CTX_sess_number(ctx));
-                                       add_assoc_long_ex(subarray, ZEND_STRS("connect"), SSL_CTX_sess_connect(ctx));
-                                       add_assoc_long_ex(subarray, ZEND_STRS("connect_good"), SSL_CTX_sess_connect_good(ctx));
-                                       add_assoc_long_ex(subarray, ZEND_STRS("connect_renegotiate"), SSL_CTX_sess_connect_renegotiate(ctx));
-                                       add_assoc_long_ex(subarray, ZEND_STRS("hits"), SSL_CTX_sess_hits(ctx));
-                                       add_assoc_long_ex(subarray, ZEND_STRS("cache_full"), SSL_CTX_sess_cache_full(ctx));
-                               }
-#endif
-                               break;
-                       case CURLSSLBACKEND_GNUTLS:
-                               backend = "gnutls";
-#ifdef PHP_HTTP_HAVE_GNUTLS
-                               {
-                                       gnutls_session_t sess = ti->internals;
-                                       char *desc;
-
-                                       array_init(subarray);
-                                       if ((desc = gnutls_session_get_desc(sess))) {
-                                               add_assoc_string_ex(subarray, ZEND_STRS("desc"), desc, 1);
-                                               gnutls_free(desc);
-                                       }
-                                       add_assoc_bool_ex(subarray, ZEND_STRS("resumed"), gnutls_session_is_resumed(sess));
-                               }
-#endif
-                               break;
-                       case CURLSSLBACKEND_NSS:
-                               backend = "nss";
-                               break;
-#if !PHP_HTTP_CURL_VERSION(7,39,0)
-                       case CURLSSLBACKEND_QSOSSL:
-                               backend = "qsossl";
-                               break;
-#else
-                       case CURLSSLBACKEND_GSKIT:
-                               backend = "gskit";
-                               break;
-#endif
-                       case CURLSSLBACKEND_POLARSSL:
-                               backend = "polarssl";
-                               break;
-                       case CURLSSLBACKEND_CYASSL:
-                               backend = "cyassl";
-                               break;
-                       case CURLSSLBACKEND_SCHANNEL:
-                               backend = "schannel";
-                               break;
-                       case CURLSSLBACKEND_DARWINSSL:
-                               backend = "darwinssl";
-                               break;
-                       default:
-                               backend = "unknown";
-                       }
-                       add_assoc_string_ex(ti_array, ZEND_STRS("backend"), estrdup(backend), 0);
-                       add_assoc_zval_ex(ti_array, ZEND_STRS("internals"), subarray);
-                       add_assoc_zval_ex(&array, "tls_session", sizeof("tls_session"), ti_array);
-               }
-       }
-#endif
-
-#if (PHP_HTTP_CURL_VERSION(7,19,1) && defined(PHP_HTTP_HAVE_OPENSSL)) || (PHP_HTTP_CURL_VERSION(7,34,0) && defined(PHP_HTTP_HAVE_NSS)) || (PHP_HTTP_CURL_VERSION(7,42,0) && defined(PHP_HTTP_HAVE_GNUTLS)) || (PHP_HTTP_CURL_VERSION(7,39,0) && defined(PHP_HTTP_HAVE_GSKIT))
-       {
-               int i;
-               zval *ci_array;
-               struct curl_certinfo *ci;
-               char *colon, *keyname;
-
-               if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CERTINFO, &ci)) {
-                       MAKE_STD_ZVAL(ci_array);
-                       array_init(ci_array);
-
-                       for (i = 0; i < ci->num_of_certs; ++i) {
-                               s = ci->certinfo[i];
-
-                               MAKE_STD_ZVAL(subarray);
-                               array_init(subarray);
-                               for (p = s; p; p = p->next) {
-                                       if (p->data) {
-                                               if ((colon = strchr(p->data, ':'))) {
-                                                       keyname = estrndup(p->data, colon - p->data);
-                                                       add_assoc_string_ex(subarray, keyname, colon - p->data + 1, colon + 1, 1);
-                                                       efree(keyname);
-                                               } else {
-                                                       add_next_index_string(subarray, p->data, 1);
-                                               }
-                                       }
-                               }
-                               add_next_index_zval(ci_array, subarray);
-                       }
-                       add_assoc_zval_ex(&array, "certinfo", sizeof("certinfo"), ci_array);
-               }
-       }
-#endif
-       {
-               php_http_curle_storage_t *st = php_http_curle_get_storage(ch);
-
-               add_assoc_long_ex(&array, "curlcode", sizeof("curlcode"), st->errorcode);
-               add_assoc_string_ex(&array, "error", sizeof("error"), st->errorbuffer, 1);
-       }
-
-       return SUCCESS;
-}
-
-static int compare_queue(php_http_client_enqueue_t *e, void *handle)
-{
-       return handle == ((php_http_client_curl_handler_t *) e->opaque)->handle;
-}
-
-static php_http_message_t *php_http_curlm_responseparser(php_http_client_curl_handler_t *h TSRMLS_DC)
-{
-       php_http_message_t *response;
-       php_http_header_parser_t parser;
-       zval *zh;
-
-       response = php_http_message_init(NULL, 0, h->response.body TSRMLS_CC);
-       php_http_header_parser_init(&parser TSRMLS_CC);
-       while (h->response.headers.used) {
-               php_http_header_parser_state_t st = php_http_header_parser_parse(&parser,
-                               &h->response.headers, PHP_HTTP_HEADER_PARSER_CLEANUP, &response->hdrs,
-                               (php_http_info_callback_t) php_http_message_info_callback, (void *) &response);
-               if (PHP_HTTP_HEADER_PARSER_STATE_FAILURE == st) {
-                       break;
-               }
-       }
-       php_http_header_parser_dtor(&parser);
-
-       /* move body to right message */
-       if (response->body != h->response.body) {
-               php_http_message_t *ptr = response;
-
-               while (ptr->parent) {
-                       ptr = ptr->parent;
-               }
-               php_http_message_body_free(&response->body);
-               response->body = ptr->body;
-               ptr->body = NULL;
-       }
-       php_http_message_body_addref(h->response.body);
-
-       /* let's update the response headers */
-       if ((zh = php_http_message_header(response, ZEND_STRL("Content-Length"), 1))) {
-               zend_hash_update(&response->hdrs, "X-Original-Content-Length", sizeof("X-Original-Content-Length"), &zh, sizeof(zval *), NULL);
-       }
-       if ((zh = php_http_message_header(response, ZEND_STRL("Transfer-Encoding"), 0))) {
-               zend_hash_update(&response->hdrs, "X-Original-Transfer-Encoding", sizeof("X-Original-Transfer-Encoding"), (void *) &zh, sizeof(zval *), NULL);
-               zend_hash_del(&response->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding"));
-       }
-       if ((zh = php_http_message_header(response, ZEND_STRL("Content-Range"), 0))) {
-               zend_hash_update(&response->hdrs, "X-Original-Content-Range", sizeof("X-Original-Content-Range"), &zh, sizeof(zval *), NULL);
-               zend_hash_del(&response->hdrs, "Content-Range", sizeof("Content-Range"));
-       }
-       if ((zh = php_http_message_header(response, ZEND_STRL("Content-Encoding"), 0))) {
-               zend_hash_update(&response->hdrs, "X-Original-Content-Encoding", sizeof("X-Original-Content-Encoding"), &zh, sizeof(zval *), NULL);
-               zend_hash_del(&response->hdrs, "Content-Encoding", sizeof("Content-Encoding"));
-       }
-       php_http_message_update_headers(response);
-
-       return response;
-}
-
-static void php_http_curlm_responsehandler(php_http_client_t *context)
-{
-       int err_count = 0, remaining = 0;
-       php_http_curle_storage_t *st, *err = NULL;
-       php_http_client_enqueue_t *enqueue;
-       php_http_client_curl_t *curl = context->ctx;
-       TSRMLS_FETCH_FROM_CTX(context->ts);
-
-       do {
-               CURLMsg *msg = curl_multi_info_read(curl->handle, &remaining);
-
-               if (msg && CURLMSG_DONE == msg->msg) {
-                       if (CURLE_OK != msg->data.result) {
-                               st = php_http_curle_get_storage(msg->easy_handle);
-                               st->errorcode = msg->data.result;
-
-                               /* defer the warnings/exceptions, so the callback is still called for this request */
-                               if (!err) {
-                                       err = ecalloc(remaining + 1, sizeof(*err));
-                               }
-                               memcpy(&err[err_count], st, sizeof(*st));
-                               if (st->url) {
-                                       err[err_count].url = estrdup(st->url);
-                               }
-                               err_count++;
-                       }
-
-                       if ((enqueue = php_http_client_enqueued(context, msg->easy_handle, compare_queue))) {
-                               php_http_client_curl_handler_t *handler = enqueue->opaque;
-                               php_http_message_t *response = php_http_curlm_responseparser(handler TSRMLS_CC);
-
-                               if (response) {
-                                       context->callback.response.func(context->callback.response.arg, context, &handler->queue, &response);
-                                       php_http_message_free(&response);
-                               }
-                       }
-               }
-       } while (remaining);
-
-       if (err_count) {
-               int i = 0;
-
-               do {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s; %s (%s)", curl_easy_strerror(err[i].errorcode), err[i].errorbuffer, STR_PTR(err[i].url));
-                       if (err[i].url) {
-                               efree(err[i].url);
-                       }
-               } while (++i < err_count);
-
-               efree(err);
-       }
-}
-
-#if PHP_HTTP_HAVE_EVENT
-
-typedef struct php_http_curlm_event {
-       struct event evnt;
-       php_http_client_t *context;
-} php_http_curlm_event_t;
-
-static inline int etoca(short action) {
-       switch (action & (EV_READ|EV_WRITE)) {
-               case EV_READ:
-                       return CURL_CSELECT_IN;
-                       break;
-               case EV_WRITE:
-                       return CURL_CSELECT_OUT;
-                       break;
-               case EV_READ|EV_WRITE:
-                       return CURL_CSELECT_IN|CURL_CSELECT_OUT;
-                       break;
-               default:
-                       return 0;
-       }
-}
-
-static void php_http_curlm_timeout_callback(int socket, short action, void *event_data)
-{
-       php_http_client_t *context = event_data;
-       php_http_client_curl_t *curl = context->ctx;
-
-#if DBG_EVENTS
-       fprintf(stderr, "T");
-#endif
-       if (curl->useevents) {
-               CURLMcode rc;
-               TSRMLS_FETCH_FROM_CTX(context->ts);
-
-               /* ignore and use -1,0 on timeout */
-               (void) socket;
-               (void) action;
-
-               while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle, CURL_SOCKET_TIMEOUT, 0, &curl->unfinished)));
-
-               if (CURLM_OK != rc) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s",  curl_multi_strerror(rc));
-               }
-
-               php_http_curlm_responsehandler(context);
-       }
-}
-
-static void php_http_curlm_event_callback(int socket, short action, void *event_data)
-{
-       php_http_client_t *context = event_data;
-       php_http_client_curl_t *curl = context->ctx;
-
-#if DBG_EVENTS
-       fprintf(stderr, "E");
-#endif
-       if (curl->useevents) {
-               CURLMcode rc = CURLM_OK;
-               TSRMLS_FETCH_FROM_CTX(context->ts);
-
-               while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle, socket, etoca(action), &curl->unfinished)));
-
-               if (CURLM_OK != rc) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", curl_multi_strerror(rc));
-               }
-
-               php_http_curlm_responsehandler(context);
-
-               /* remove timeout if there are no transfers left */
-               if (!curl->unfinished && event_initialized(curl->timeout) && event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
-                       event_del(curl->timeout);
-               }
-       }
-}
-
-static int php_http_curlm_socket_callback(CURL *easy, curl_socket_t sock, int action, void *socket_data, void *assign_data)
-{
-       php_http_client_t *context = socket_data;
-       php_http_client_curl_t *curl = context->ctx;
-
-#if DBG_EVENTS
-       fprintf(stderr, "S");
-#endif
-       if (curl->useevents) {
-               int events = EV_PERSIST;
-               php_http_curlm_event_t *ev = assign_data;
-               TSRMLS_FETCH_FROM_CTX(context->ts);
-
-               if (!ev) {
-                       ev = ecalloc(1, sizeof(php_http_curlm_event_t));
-                       ev->context = context;
-                       curl_multi_assign(curl->handle, sock, ev);
-               } else {
-                       event_del(&ev->evnt);
-               }
-
-               switch (action) {
-                       case CURL_POLL_IN:
-                               events |= EV_READ;
-                               break;
-                       case CURL_POLL_OUT:
-                               events |= EV_WRITE;
-                               break;
-                       case CURL_POLL_INOUT:
-                               events |= EV_READ|EV_WRITE;
-                               break;
-
-                       case CURL_POLL_REMOVE:
-                               efree(ev);
-                               /* no break */
-                       case CURL_POLL_NONE:
-                               return 0;
-
-                       default:
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown socket action %d", action);
-                               return -1;
-               }
-
-               event_assign(&ev->evnt, curl->evbase, sock, events, php_http_curlm_event_callback, context);
-               event_add(&ev->evnt, NULL);
-       }
-
-       return 0;
-}
-
-static void php_http_curlm_timer_callback(CURLM *multi, long timeout_ms, void *timer_data)
-{
-       php_http_client_t *context = timer_data;
-       php_http_client_curl_t *curl = context->ctx;
-
-#if DBG_EVENTS
-       fprintf(stderr, "\ntimer <- timeout_ms: %ld\n", timeout_ms);
-#endif
-       if (curl->useevents) {
-
-               if (timeout_ms < 0) {
-                       php_http_curlm_timeout_callback(CURL_SOCKET_TIMEOUT, /*EV_READ|EV_WRITE*/0, context);
-               } else if (timeout_ms > 0 || !event_initialized(curl->timeout) || !event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
-                       struct timeval timeout;
-
-                       if (!event_initialized(curl->timeout)) {
-                               event_assign(curl->timeout, curl->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_curlm_timeout_callback, context);
-                       }
-
-                       timeout.tv_sec = timeout_ms / 1000;
-                       timeout.tv_usec = (timeout_ms % 1000) * 1000;
-
-                       event_add(curl->timeout, &timeout);
-               }
-       }
-}
-
-#endif /* HAVE_EVENT */
-
-/* curl options */
-
-static php_http_options_t php_http_curle_options, php_http_curlm_options;
-
-#define PHP_HTTP_CURLE_OPTION_CHECK_STRLEN             0x0001
-#define PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR            0x0002
-#define PHP_HTTP_CURLE_OPTION_TRANSFORM_MS             0x0004
-
-static ZEND_RESULT_CODE php_http_curle_option_set_ssl_verifyhost(php_http_option_t *opt, zval *val, void *userdata)
-{
-       php_http_client_curl_handler_t *curl = userdata;
-       CURL *ch = curl->handle;
-
-       if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_SSL_VERIFYHOST, Z_BVAL_P(val) ? 2 : 0)) {
-               return FAILURE;
-       }
-       return SUCCESS;
-}
-
-static ZEND_RESULT_CODE php_http_curle_option_set_cookiestore(php_http_option_t *opt, zval *val, void *userdata)
-{
-       php_http_client_curl_handler_t *curl = userdata;
-       CURL *ch = curl->handle;
-       php_http_curle_storage_t *storage = php_http_curle_get_storage(curl->handle);
-
-       if (storage->cookiestore) {
-               pefree(storage->cookiestore, 1);
-       }
-       if (val && Z_STRLEN_P(val)) {
-               storage->cookiestore = pestrndup(Z_STRVAL_P(val), Z_STRLEN_P(val), 1);
-       } else {
-               storage->cookiestore = NULL;
-       }
-       if (    CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIEFILE, storage->cookiestore)
-               ||      CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIEJAR, storage->cookiestore)
-       ) {
-               return FAILURE;
-       }
-       return SUCCESS;
-}
-
-static ZEND_RESULT_CODE php_http_curle_option_set_cookies(php_http_option_t *opt, zval *val, void *userdata)
-{
-       php_http_client_curl_handler_t *curl = userdata;
-       CURL *ch = curl->handle;
-       TSRMLS_FETCH_FROM_CTX(curl->client->ts);
-
-       if (val && Z_TYPE_P(val) != IS_NULL) {
-               if (curl->options.encode_cookies) {
-                       if (SUCCESS == php_http_url_encode_hash_ex(HASH_OF(val), &curl->options.cookies, ZEND_STRL(";"), ZEND_STRL("="), NULL, 0 TSRMLS_CC)) {
-                               php_http_buffer_fix(&curl->options.cookies);
-                               if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIE, curl->options.cookies.data)) {
-                                       return FAILURE;
-                               }
-                       } else {
-                               return FAILURE;
-                       }
-               } else {
-                       HashPosition pos;
-                       php_http_array_hashkey_t cookie_key = php_http_array_hashkey_init(0);
-                       zval **cookie_val;
-
-                       FOREACH_KEYVAL(pos, val, cookie_key, cookie_val) {
-                               zval *zv = php_http_ztyp(IS_STRING, *cookie_val);
-
-                               php_http_array_hashkey_stringify(&cookie_key);
-                               php_http_buffer_appendf(&curl->options.cookies, "%s=%s; ", cookie_key.str, Z_STRVAL_P(zv));
-                               php_http_array_hashkey_stringfree(&cookie_key);
-
-                               zval_ptr_dtor(&zv);
-                       }
-
-                       php_http_buffer_fix(&curl->options.cookies);
-                       if (curl->options.cookies.used) {
-                               if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIE, curl->options.cookies.data)) {
-                                       return FAILURE;
-                               }
-                       }
-               }
-       } else {
-               php_http_buffer_reset(&curl->options.cookies);
-               if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIE, NULL)) {
-                       return FAILURE;
-               }
-       }
-       return SUCCESS;
-}
-
-static ZEND_RESULT_CODE php_http_curle_option_set_encodecookies(php_http_option_t *opt, zval *val, void *userdata)
-{
-       php_http_client_curl_handler_t *curl = userdata;
-
-       curl->options.encode_cookies = Z_BVAL_P(val);
-       return SUCCESS;
-}
-
-static ZEND_RESULT_CODE php_http_curle_option_set_lastmodified(php_http_option_t *opt, zval *val, void *userdata)
-{
-       php_http_client_curl_handler_t *curl = userdata;
-       CURL *ch = curl->handle;
-       TSRMLS_FETCH_FROM_CTX(curl->client->ts);
-
-       if (Z_LVAL_P(val)) {
-               if (Z_LVAL_P(val) > 0) {
-                       if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, Z_LVAL_P(val))) {
-                               return FAILURE;
-                       }
-               } else {
-                       if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, (long) sapi_get_request_time(TSRMLS_C) + Z_LVAL_P(val))) {
-                               return FAILURE;
-                       }
-               }
-               if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMECONDITION, (long) (curl->options.range_request ? CURL_TIMECOND_IFUNMODSINCE : CURL_TIMECOND_IFMODSINCE))) {
-                       return FAILURE;
-               }
-       } else {
-               if (    CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, 0)
-                       ||      CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMECONDITION, 0)
-               ) {
-                       return FAILURE;
-               }
-       }
-       return SUCCESS;
-}
-
-static ZEND_RESULT_CODE php_http_curle_option_set_compress(php_http_option_t *opt, zval *val, void *userdata)
-{
-       php_http_client_curl_handler_t *curl = userdata;
-       CURL *ch = curl->handle;
-
-#if !PHP_HTTP_CURL_VERSION(7,21,6)
-#      define CURLOPT_ACCEPT_ENCODING CURLOPT_ENCODING
-#endif
-       if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_ACCEPT_ENCODING, Z_BVAL_P(val) ? "" : NULL)) {
-               return FAILURE;
-       }
-       return SUCCESS;
-}
-
-static ZEND_RESULT_CODE php_http_curle_option_set_etag(php_http_option_t *opt, zval *val, void *userdata)
-{
-       php_http_client_curl_handler_t *curl = userdata;
-       php_http_buffer_t header;
-
-       if (Z_STRLEN_P(val)) {
-               zend_bool is_quoted = !((Z_STRVAL_P(val)[0] != '"') || (Z_STRVAL_P(val)[Z_STRLEN_P(val)-1] != '"'));
-               php_http_buffer_init(&header);
-               php_http_buffer_appendf(&header, is_quoted?"%s: %s":"%s: \"%s\"", curl->options.range_request?"If-Match":"If-None-Match", Z_STRVAL_P(val));
-               php_http_buffer_fix(&header);
-               curl->options.headers = curl_slist_append(curl->options.headers, header.data);
-               php_http_buffer_dtor(&header);
-       }
-       return SUCCESS;
-}
-
-static ZEND_RESULT_CODE php_http_curle_option_set_range(php_http_option_t *opt, zval *val, void *userdata)
-{
-       php_http_client_curl_handler_t *curl = userdata;
-       CURL *ch = curl->handle;
-       TSRMLS_FETCH_FROM_CTX(curl->client->ts);
-
-       php_http_buffer_reset(&curl->options.ranges);
-
-       if (val && Z_TYPE_P(val) != IS_NULL) {
-               HashPosition pos;
-               zval **rr, **rb, **re;
-
-               FOREACH_VAL(pos, val, rr) {
-                       if (Z_TYPE_PP(rr) == IS_ARRAY) {
-                               if (2 == php_http_array_list(Z_ARRVAL_PP(rr) TSRMLS_CC, 2, &rb, &re)) {
-                                       if (    ((Z_TYPE_PP(rb) == IS_LONG) || ((Z_TYPE_PP(rb) == IS_STRING) && is_numeric_string(Z_STRVAL_PP(rb), Z_STRLEN_PP(rb), NULL, NULL, 1))) &&
-                                                       ((Z_TYPE_PP(re) == IS_LONG) || ((Z_TYPE_PP(re) == IS_STRING) && is_numeric_string(Z_STRVAL_PP(re), Z_STRLEN_PP(re), NULL, NULL, 1)))) {
-                                               zval *rbl = php_http_ztyp(IS_LONG, *rb);
-                                               zval *rel = php_http_ztyp(IS_LONG, *re);
-
-                                               if ((Z_LVAL_P(rbl) >= 0) && (Z_LVAL_P(rel) >= 0)) {
-                                                       php_http_buffer_appendf(&curl->options.ranges, "%ld-%ld,", Z_LVAL_P(rbl), Z_LVAL_P(rel));
-                                               }
-                                               zval_ptr_dtor(&rbl);
-                                               zval_ptr_dtor(&rel);
-                                       }
-
-                               }
-                       }
-               }
-
-               if (curl->options.ranges.used) {
-                       curl->options.range_request = 1;
-                       /* ditch last comma */
-                       curl->options.ranges.data[curl->options.ranges.used - 1] = '\0';
-               }
-       }
-
-       if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RANGE, curl->options.ranges.data)) {
-               return FAILURE;
-       }
-       return SUCCESS;
-}
-
-static ZEND_RESULT_CODE php_http_curle_option_set_resume(php_http_option_t *opt, zval *val, void *userdata)
-{
-       php_http_client_curl_handler_t *curl = userdata;
-       CURL *ch = curl->handle;
-
-       if (Z_LVAL_P(val) > 0) {
-               curl->options.range_request = 1;
-       }
-       if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RESUME_FROM, Z_LVAL_P(val))) {
-               return FAILURE;
-       }
-       return SUCCESS;
-}
-
-static ZEND_RESULT_CODE php_http_curle_option_set_retrydelay(php_http_option_t *opt, zval *val, void *userdata)
-{
-       php_http_client_curl_handler_t *curl = userdata;
-
-       curl->options.retry.delay = Z_DVAL_P(val);
-       return SUCCESS;
-}
-
-static ZEND_RESULT_CODE php_http_curle_option_set_retrycount(php_http_option_t *opt, zval *val, void *userdata)
-{
-       php_http_client_curl_handler_t *curl = userdata;
-
-       curl->options.retry.count = Z_LVAL_P(val);
-       return SUCCESS;
-}
-
-static ZEND_RESULT_CODE php_http_curle_option_set_redirect(php_http_option_t *opt, zval *val, void *userdata)
-{
-       php_http_client_curl_handler_t *curl = userdata;
-       CURL *ch = curl->handle;
-
-       if (    CURLE_OK != curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, Z_LVAL_P(val) ? 1L : 0L)
-               ||      CURLE_OK != curl_easy_setopt(ch, CURLOPT_MAXREDIRS, curl->options.redirects = Z_LVAL_P(val))
-       ) {
-               return FAILURE;
-       }
-       return SUCCESS;
-}
-
-static ZEND_RESULT_CODE php_http_curle_option_set_portrange(php_http_option_t *opt, zval *val, void *userdata)
-{
-       php_http_client_curl_handler_t *curl = userdata;
-       CURL *ch = curl->handle;
-       long localport = 0, localportrange = 0;
-       TSRMLS_FETCH_FROM_CTX(curl->client->ts);
-
-       if (val && Z_TYPE_P(val) != IS_NULL) {
-               zval **z_port_start, *zps_copy = NULL, **z_port_end, *zpe_copy = NULL;
-
-               switch (php_http_array_list(Z_ARRVAL_P(val) TSRMLS_CC, 2, &z_port_start, &z_port_end)) {
-               case 2:
-                       zps_copy = php_http_ztyp(IS_LONG, *z_port_start);
-                       zpe_copy = php_http_ztyp(IS_LONG, *z_port_end);
-                       localportrange = labs(Z_LVAL_P(zps_copy)-Z_LVAL_P(zpe_copy))+1L;
-                       /* no break */
-               case 1:
-                       if (!zps_copy) {
-                               zps_copy = php_http_ztyp(IS_LONG, *z_port_start);
-                       }
-                       localport = (zpe_copy && Z_LVAL_P(zpe_copy) > 0) ? MIN(Z_LVAL_P(zps_copy), Z_LVAL_P(zpe_copy)) : Z_LVAL_P(zps_copy);
-                       zval_ptr_dtor(&zps_copy);
-                       if (zpe_copy) {
-                               zval_ptr_dtor(&zpe_copy);
-                       }
-                       break;
-               default:
-                       break;
-               }
-       }
-       if (    CURLE_OK != curl_easy_setopt(ch, CURLOPT_LOCALPORT, localport)
-               ||      CURLE_OK != curl_easy_setopt(ch, CURLOPT_LOCALPORTRANGE, localportrange)
-       ) {
-               return FAILURE;
-       }
-       return SUCCESS;
-}
-
-#if PHP_HTTP_CURL_VERSION(7,37,0)
-static ZEND_RESULT_CODE php_http_curle_option_set_proxyheader(php_http_option_t *opt, zval *val, void *userdata)
-{
-       php_http_client_curl_handler_t *curl = userdata;
-       TSRMLS_FETCH_FROM_CTX(curl->client->ts);
-
-       if (val && Z_TYPE_P(val) != IS_NULL) {
-               php_http_array_hashkey_t header_key = php_http_array_hashkey_init(0);
-               zval **header_val, *header_cpy;
-               HashPosition pos;
-               php_http_buffer_t header;
-
-               php_http_buffer_init(&header);
-               FOREACH_KEYVAL(pos, val, header_key, header_val) {
-                       if (header_key.type == HASH_KEY_IS_STRING) {
-                               header_cpy = php_http_ztyp(IS_STRING, *header_val);
-                               php_http_buffer_appendf(&header, "%s: %s", header_key.str, Z_STRVAL_P(header_cpy));
-                               php_http_buffer_fix(&header);
-                               curl->options.proxyheaders = curl_slist_append(curl->options.proxyheaders, header.data);
-                               php_http_buffer_reset(&header);
-
-                               zval_ptr_dtor(&header_cpy);
-                       }
-               }
-               php_http_buffer_dtor(&header);
-       }
-       if (CURLE_OK != curl_easy_setopt(curl->handle, CURLOPT_PROXYHEADER, curl->options.proxyheaders)) {
-               return FAILURE;
-       }
-       if (CURLE_OK != curl_easy_setopt(curl->handle, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE)) {
-               curl_easy_setopt(curl->handle, CURLOPT_PROXYHEADER, NULL);
-               return FAILURE;
-       }
-       return SUCCESS;
-}
-#endif
-
-#if PHP_HTTP_CURL_VERSION(7,21,3)
-static ZEND_RESULT_CODE php_http_curle_option_set_resolve(php_http_option_t *opt, zval *val, void *userdata)
-{
-       php_http_client_curl_handler_t *curl = userdata;
-       CURL *ch = curl->handle;
-       TSRMLS_FETCH_FROM_CTX(curl->client->ts);
-
-       if (val && Z_TYPE_P(val) != IS_NULL) {
-               php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-               HashPosition pos;
-               zval **data;
-
-               FOREACH_KEYVAL(pos, val, key, data) {
-                       zval *cpy = php_http_ztyp(IS_STRING, *data);
-                       curl->options.resolve = curl_slist_append(curl->options.resolve, Z_STRVAL_P(cpy));
-                       zval_ptr_dtor(&cpy);
-               }
-
-               if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RESOLVE, curl->options.resolve)) {
-                       return FAILURE;
-               }
-       } else {
-               if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RESOLVE, NULL)) {
-                       return FAILURE;
-               }
-       }
-       return SUCCESS;
-}
-#endif
-
-#if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP)
-static ZEND_RESULT_CODE php_http_curle_option_set_ssl_tlsauthtype(php_http_option_t *opt, zval *val, void *userdata)
-{
-       php_http_client_curl_handler_t *curl = userdata;
-       CURL *ch = curl->handle;
-
-       if (val && Z_LVAL_P(val)) {
-               switch (Z_LVAL_P(val)) {
-               case CURL_TLSAUTH_SRP:
-                       if (CURLE_OK == curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, PHP_HTTP_CURL_TLSAUTH_SRP)) {
-                               return SUCCESS;
-                       }
-                       /* no break */
-               default:
-                       return FAILURE;
-               }
-       }
-       if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, PHP_HTTP_CURL_TLSAUTH_DEF)) {
-               return FAILURE;
-       }
-       return SUCCESS;
-}
-#endif
-
-static void php_http_curle_options_init(php_http_options_t *registry TSRMLS_DC)
-{
-       php_http_option_t *opt;
-
-       /* url options */
-#if PHP_HTTP_CURL_VERSION(7,42,0)
-       php_http_option_register(registry, ZEND_STRL("path_as_is"), CURLOPT_PATH_AS_IS, IS_BOOL);
-#endif
-
-       /* proxy */
-       if ((opt = php_http_option_register(registry, ZEND_STRL("proxyhost"), CURLOPT_PROXY, IS_STRING))) {
-               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-       }
-       php_http_option_register(registry, ZEND_STRL("proxytype"), CURLOPT_PROXYTYPE, IS_LONG);
-       php_http_option_register(registry, ZEND_STRL("proxyport"), CURLOPT_PROXYPORT, IS_LONG);
-       if ((opt = php_http_option_register(registry, ZEND_STRL("proxyauth"), CURLOPT_PROXYUSERPWD, IS_STRING))) {
-               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-       }
-       if ((opt = php_http_option_register(registry, ZEND_STRL("proxyauthtype"), CURLOPT_PROXYAUTH, IS_LONG))) {
-               Z_LVAL(opt->defval) = CURLAUTH_ANYSAFE;
-       }
-       php_http_option_register(registry, ZEND_STRL("proxytunnel"), CURLOPT_HTTPPROXYTUNNEL, IS_BOOL);
-#if PHP_HTTP_CURL_VERSION(7,19,4)
-       php_http_option_register(registry, ZEND_STRL("noproxy"), CURLOPT_NOPROXY, IS_STRING);
-#endif
-
-#if PHP_HTTP_CURL_VERSION(7,37,0)
-       if ((opt = php_http_option_register(registry, ZEND_STRL("proxyheader"), CURLOPT_PROXYHEADER, IS_ARRAY))) {
-               opt->setter = php_http_curle_option_set_proxyheader;
-       }
-#endif
-#if PHP_HTTP_CURL_VERSION(7,43,0)
-       if ((opt = php_http_option_register(registry, ZEND_STRL("proxy_service_name"), CURLOPT_PROXY_SERVICE_NAME, IS_STRING))) {
-               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-       }
-#endif
-
-#if PHP_HTTP_CURL_VERSION(7,40,0)
-       if ((opt = php_http_option_register(registry, ZEND_STRL("unix_socket_path"), CURLOPT_UNIX_SOCKET_PATH, IS_STRING))) {
-               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
-       }
-#endif
-
-       /* dns */
-       if ((opt = php_http_option_register(registry, ZEND_STRL("dns_cache_timeout"), CURLOPT_DNS_CACHE_TIMEOUT, IS_LONG))) {
-               Z_LVAL(opt->defval) = 60;
-       }
-       php_http_option_register(registry, ZEND_STRL("ipresolve"), CURLOPT_IPRESOLVE, IS_LONG);
-#if PHP_HTTP_CURL_VERSION(7,21,3)
-       if ((opt = php_http_option_register(registry, ZEND_STRL("resolve"), CURLOPT_RESOLVE, IS_ARRAY))) {
-               opt->setter = php_http_curle_option_set_resolve;
-       }
-#endif
-#if PHP_HTTP_HAVE_ARES
-# if PHP_HTTP_CURL_VERSION(7,24,0)
-       if ((opt = php_http_option_register(registry, ZEND_STRL("dns_servers"), CURLOPT_DNS_SERVERS, IS_STRING))) {
-               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-       }
-# endif
-# if PHP_HTTP_CURL_VERSION(7,33,0)
-       if ((opt = php_http_option_register(registry, ZEND_STRL("dns_interface"), CURLOPT_DNS_INTERFACE, IS_STRING))) {
-               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-       }
-       if ((opt = php_http_option_register(registry, ZEND_STRL("dns_local_ip4"), CURLOPT_DNS_LOCAL_IP4, IS_STRING))) {
-               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-       }
-       if ((opt = php_http_option_register(registry, ZEND_STRL("dns_local_ip6"), CURLOPT_DNS_LOCAL_IP6, IS_STRING))) {
-               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-       }
-# endif
-#endif
-
-       /* limits */
-       php_http_option_register(registry, ZEND_STRL("low_speed_limit"), CURLOPT_LOW_SPEED_LIMIT, IS_LONG);
-       php_http_option_register(registry, ZEND_STRL("low_speed_time"), CURLOPT_LOW_SPEED_TIME, IS_LONG);
-
-       /* LSF weirdance
-       php_http_option_register(registry, ZEND_STRL("max_send_speed"), CURLOPT_MAX_SEND_SPEED_LARGE, IS_LONG);
-       php_http_option_register(registry, ZEND_STRL("max_recv_speed"), CURLOPT_MAX_RECV_SPEED_LARGE, IS_LONG);
-       */
-
-       /* connection handling */
-       /* crashes
-       if ((opt = php_http_option_register(registry, ZEND_STRL("maxconnects"), CURLOPT_MAXCONNECTS, IS_LONG))) {
-               Z_LVAL(opt->defval) = 5;
-       }
-       */
-       php_http_option_register(registry, ZEND_STRL("fresh_connect"), CURLOPT_FRESH_CONNECT, IS_BOOL);
-       php_http_option_register(registry, ZEND_STRL("forbid_reuse"), CURLOPT_FORBID_REUSE, IS_BOOL);
-
-       /* outgoing interface */
-       php_http_option_register(registry, ZEND_STRL("interface"), CURLOPT_INTERFACE, IS_STRING);
-       if ((opt = php_http_option_register(registry, ZEND_STRL("portrange"), CURLOPT_LOCALPORT, IS_ARRAY))) {
-               opt->setter = php_http_curle_option_set_portrange;
-       }
-
-       /* another endpoint port */
-       php_http_option_register(registry, ZEND_STRL("port"), CURLOPT_PORT, IS_LONG);
-
-       /* RFC4007 zone_id */
-#if PHP_HTTP_CURL_VERSION(7,19,0)
-       php_http_option_register(registry, ZEND_STRL("address_scope"), CURLOPT_ADDRESS_SCOPE, IS_LONG);
-#endif
-
-       /* auth */
-       if ((opt = php_http_option_register(registry, ZEND_STRL("httpauth"), CURLOPT_USERPWD, IS_STRING))) {
-               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-       }
-       if ((opt = php_http_option_register(registry, ZEND_STRL("httpauthtype"), CURLOPT_HTTPAUTH, IS_LONG))) {
-               Z_LVAL(opt->defval) = CURLAUTH_ANYSAFE;
-       }
-#if PHP_HTTP_CURL_VERSION(7,43,0)
-       if ((opt = php_http_option_register(registry, ZEND_STRL("service_name"), CURLOPT_SERVICE_NAME, IS_STRING))) {
-               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-       }
-#endif
-
-       /* redirects */
-       if ((opt = php_http_option_register(registry, ZEND_STRL("redirect"), CURLOPT_FOLLOWLOCATION, IS_LONG))) {
-               opt->setter = php_http_curle_option_set_redirect;
-       }
-       php_http_option_register(registry, ZEND_STRL("unrestricted_auth"), CURLOPT_UNRESTRICTED_AUTH, IS_BOOL);
-#if PHP_HTTP_CURL_VERSION(7,19,1)
-       php_http_option_register(registry, ZEND_STRL("postredir"), CURLOPT_POSTREDIR, IS_LONG);
-#endif
-
-       /* retries */
-       if ((opt = php_http_option_register(registry, ZEND_STRL("retrycount"), 0, IS_LONG))) {
-               opt->setter = php_http_curle_option_set_retrycount;
-       }
-       if ((opt = php_http_option_register(registry, ZEND_STRL("retrydelay"), 0, IS_DOUBLE))) {
-               opt->setter = php_http_curle_option_set_retrydelay;
-       }
-
-       /* referer */
-       if ((opt = php_http_option_register(registry, ZEND_STRL("referer"), CURLOPT_REFERER, IS_STRING))) {
-               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-       }
-       if ((opt = php_http_option_register(registry, ZEND_STRL("autoreferer"), CURLOPT_AUTOREFERER, IS_BOOL))) {
-               ZVAL_BOOL(&opt->defval, 1);
-       }
-
-       /* useragent */
-       if ((opt = php_http_option_register(registry, ZEND_STRL("useragent"), CURLOPT_USERAGENT, IS_STRING))) {
-               /* don't check strlen, to allow sending no useragent at all */
-               ZVAL_STRING(&opt->defval,
-                               "PECL_HTTP/" PHP_PECL_HTTP_VERSION " "
-                               "PHP/" PHP_VERSION " "
-                               "libcurl/" LIBCURL_VERSION
-                       , 0);
-       }
-
-       /* resume */
-       if ((opt = php_http_option_register(registry, ZEND_STRL("resume"), CURLOPT_RESUME_FROM, IS_LONG))) {
-               opt->setter = php_http_curle_option_set_resume;
-       }
-       /* ranges */
-       if ((opt = php_http_option_register(registry, ZEND_STRL("range"), CURLOPT_RANGE, IS_ARRAY))) {
-               opt->setter = php_http_curle_option_set_range;
-       }
-
-       /* etag */
-       if ((opt = php_http_option_register(registry, ZEND_STRL("etag"), 0, IS_STRING))) {
-               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-               opt->setter = php_http_curle_option_set_etag;
-       }
-
-       /* compression */
-       if ((opt = php_http_option_register(registry, ZEND_STRL("compress"), 0, IS_BOOL))) {
-               opt->setter = php_http_curle_option_set_compress;
-       }
-
-       /* lastmodified */
-       if ((opt = php_http_option_register(registry, ZEND_STRL("lastmodified"), 0, IS_LONG))) {
-               opt->setter = php_http_curle_option_set_lastmodified;
-       }
-
-       /* cookies */
-       if ((opt = php_http_option_register(registry, ZEND_STRL("encodecookies"), 0, IS_BOOL))) {
-               opt->setter = php_http_curle_option_set_encodecookies;
-               ZVAL_BOOL(&opt->defval, 1);
-       }
-       if ((opt = php_http_option_register(registry, ZEND_STRL("cookies"), 0, IS_ARRAY))) {
-               opt->setter = php_http_curle_option_set_cookies;
-       }
-
-       /* cookiesession, don't load session cookies from cookiestore */
-       php_http_option_register(registry, ZEND_STRL("cookiesession"), CURLOPT_COOKIESESSION, IS_BOOL);
-       /* cookiestore, read initial cookies from that file and store cookies back into that file */
-       if ((opt = php_http_option_register(registry, ZEND_STRL("cookiestore"), 0, IS_STRING))) {
-               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
-               opt->setter = php_http_curle_option_set_cookiestore;
-       }
-
-       /* maxfilesize */
-       php_http_option_register(registry, ZEND_STRL("maxfilesize"), CURLOPT_MAXFILESIZE, IS_LONG);
-
-       /* http protocol version */
-       php_http_option_register(registry, ZEND_STRL("protocol"), CURLOPT_HTTP_VERSION, IS_LONG);
-
-       /* timeouts */
-       if ((opt = php_http_option_register(registry, ZEND_STRL("timeout"), CURLOPT_TIMEOUT_MS, IS_DOUBLE))) {
-               opt->flags |= PHP_HTTP_CURLE_OPTION_TRANSFORM_MS;
-       }
-       if ((opt = php_http_option_register(registry, ZEND_STRL("connecttimeout"), CURLOPT_CONNECTTIMEOUT_MS, IS_DOUBLE))) {
-               opt->flags |= PHP_HTTP_CURLE_OPTION_TRANSFORM_MS;
-               Z_DVAL(opt->defval) = 3;
-       }
-#if PHP_HTTP_CURL_VERSION(7,36,0)
-       if ((opt = php_http_option_register(registry, ZEND_STRL("expect_100_timeout"), CURLOPT_EXPECT_100_TIMEOUT_MS, IS_DOUBLE))) {
-               opt->flags |= PHP_HTTP_CURLE_OPTION_TRANSFORM_MS;
-               Z_DVAL(opt->defval) = 1;
-       }
-#endif
-
-       /* tcp */
-       php_http_option_register(registry, ZEND_STRL("tcp_nodelay"), CURLOPT_TCP_NODELAY, IS_BOOL);
-#if PHP_HTTP_CURL_VERSION(7,25,0)
-       php_http_option_register(registry, ZEND_STRL("tcp_keepalive"), CURLOPT_TCP_KEEPALIVE, IS_BOOL);
-       if ((opt = php_http_option_register(registry, ZEND_STRL("tcp_keepidle"), CURLOPT_TCP_KEEPIDLE, IS_LONG))) {
-               Z_LVAL(opt->defval) = 60;
-       }
-       if ((opt = php_http_option_register(registry, ZEND_STRL("tcp_keepintvl"), CURLOPT_TCP_KEEPINTVL, IS_LONG))) {
-               Z_LVAL(opt->defval) = 60;
-       }
-#endif
-
-       /* ssl */
-       if ((opt = php_http_option_register(registry, ZEND_STRL("ssl"), 0, IS_ARRAY))) {
-               registry = &opt->suboptions;
-
-               if ((opt = php_http_option_register(registry, ZEND_STRL("cert"), CURLOPT_SSLCERT, IS_STRING))) {
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
-               }
-               if ((opt = php_http_option_register(registry, ZEND_STRL("certtype"), CURLOPT_SSLCERTTYPE, IS_STRING))) {
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-                       ZVAL_STRING(&opt->defval, "PEM", 0);
-               }
-               if ((opt = php_http_option_register(registry, ZEND_STRL("key"), CURLOPT_SSLKEY, IS_STRING))) {
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
-               }
-               if ((opt = php_http_option_register(registry, ZEND_STRL("keytype"), CURLOPT_SSLKEYTYPE, IS_STRING))) {
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-                       ZVAL_STRING(&opt->defval, "PEM", 0);
-               }
-               if ((opt = php_http_option_register(registry, ZEND_STRL("keypasswd"), CURLOPT_SSLKEYPASSWD, IS_STRING))) {
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-               }
-               php_http_option_register(registry, ZEND_STRL("engine"), CURLOPT_SSLENGINE, IS_STRING);
-               php_http_option_register(registry, ZEND_STRL("version"), CURLOPT_SSLVERSION, IS_LONG);
-               if ((opt = php_http_option_register(registry, ZEND_STRL("verifypeer"), CURLOPT_SSL_VERIFYPEER, IS_BOOL))) {
-                       ZVAL_BOOL(&opt->defval, 1);
-               }
-               if ((opt = php_http_option_register(registry, ZEND_STRL("verifyhost"), CURLOPT_SSL_VERIFYHOST, IS_BOOL))) {
-                       ZVAL_BOOL(&opt->defval, 1);
-                       opt->setter = php_http_curle_option_set_ssl_verifyhost;
-               }
-#if PHP_HTTP_CURL_VERSION(7,41,0)
-               php_http_option_register(registry, ZEND_STRL("verifystatus"), CURLOPT_SSL_VERIFYSTATUS, IS_BOOL);
-#endif
-               php_http_option_register(registry, ZEND_STRL("cipher_list"), CURLOPT_SSL_CIPHER_LIST, IS_STRING);
-               if ((opt = php_http_option_register(registry, ZEND_STRL("cainfo"), CURLOPT_CAINFO, IS_STRING))) {
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
-#ifdef PHP_HTTP_CURL_CAINFO
-                       ZVAL_STRING(&opt->defval, PHP_HTTP_CURL_CAINFO, 0);
-#endif
-               }
-               if ((opt = php_http_option_register(registry, ZEND_STRL("capath"), CURLOPT_CAPATH, IS_STRING))) {
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
-               }
-               if ((opt = php_http_option_register(registry, ZEND_STRL("random_file"), CURLOPT_RANDOM_FILE, IS_STRING))) {
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
-               }
-               if ((opt = php_http_option_register(registry, ZEND_STRL("egdsocket"), CURLOPT_EGDSOCKET, IS_STRING))) {
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
-               }
-#if PHP_HTTP_CURL_VERSION(7,19,0)
-               if ((opt = php_http_option_register(registry, ZEND_STRL("issuercert"), CURLOPT_ISSUERCERT, IS_STRING))) {
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
-               }
-#      ifdef PHP_HTTP_HAVE_OPENSSL
-               if ((opt = php_http_option_register(registry, ZEND_STRL("crlfile"), CURLOPT_CRLFILE, IS_STRING))) {
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
-               }
-#      endif
-#endif
-#if (PHP_HTTP_CURL_VERSION(7,19,1) && defined(PHP_HTTP_HAVE_OPENSSL)) || (PHP_HTTP_CURL_VERSION(7,34,0) && defined(PHP_HTTP_HAVE_NSS)) || (PHP_HTTP_CURL_VERSION(7,42,0) && defined(PHP_HTTP_HAVE_GNUTLS)) || (PHP_HTTP_CURL_VERSION(7,39,0) && defined(PHP_HTTP_HAVE_GSKIT))
-               php_http_option_register(registry, ZEND_STRL("certinfo"), CURLOPT_CERTINFO, IS_BOOL);
-#endif
-#if PHP_HTTP_CURL_VERSION(7,36,0)
-               if ((opt = php_http_option_register(registry, ZEND_STRL("enable_npn"), CURLOPT_SSL_ENABLE_NPN, IS_BOOL))) {
-                       ZVAL_BOOL(&opt->defval, 1);
-               }
-               if ((opt = php_http_option_register(registry, ZEND_STRL("enable_alpn"), CURLOPT_SSL_ENABLE_ALPN, IS_BOOL))) {
-                       ZVAL_BOOL(&opt->defval, 1);
-               }
-#endif
-#if PHP_HTTP_CURL_VERSION(7,39,0)
-               /* FIXME: see http://curl.haxx.se/libcurl/c/CURLOPT_PINNEDPUBLICKEY.html#AVAILABILITY */
-               if ((opt = php_http_option_register(registry, ZEND_STRL("pinned_publickey"), CURLOPT_PINNEDPUBLICKEY, IS_STRING))) {
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
-               }
-#endif
-#if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP)
-               if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthtype"), CURLOPT_TLSAUTH_TYPE, IS_LONG))) {
-                       opt->setter = php_http_curle_option_set_ssl_tlsauthtype;
-               }
-               if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthuser"), CURLOPT_TLSAUTH_USERNAME, IS_STRING))) {
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-               }
-               if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthpass"), CURLOPT_TLSAUTH_PASSWORD, IS_STRING))) {
-                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
-               }
-#endif
-#if PHP_HTTP_CURL_VERSION(7,42,0) && (defined(PHP_HTTP_HAVE_NSS) || defined(PHP_HTTP_HAVE_DARWINSSL))
-               php_http_option_register(registry, ZEND_STRL("falsestart"), CURLOPT_SSL_FALSESTART, IS_BOOL);
-#endif
-       }
-}
-
-static zval *php_http_curle_get_option(php_http_option_t *opt, HashTable *options, void *userdata)
-{
-       php_http_client_curl_handler_t *curl = userdata;
-       zval *option;
-
-       if ((option = php_http_option_get(opt, options, NULL))) {
-               option = php_http_ztyp(opt->type, option);
-               zend_hash_quick_update(&curl->options.cache, opt->name.s, opt->name.l, opt->name.h, &option, sizeof(zval *), NULL);
-       }
-       return option;
-}
-
-static ZEND_RESULT_CODE php_http_curle_set_option(php_http_option_t *opt, zval *val, void *userdata)
-{
-       php_http_client_curl_handler_t *curl = userdata;
-       CURL *ch = curl->handle;
-       zval tmp;
-       CURLcode rc = CURLE_OK;
-       ZEND_RESULT_CODE rv = SUCCESS;
-       TSRMLS_FETCH_FROM_CTX(curl->client->ts);
-
-       if (!val) {
-               val = &opt->defval;
-       }
-
-       switch (opt->type) {
-       case IS_BOOL:
-               if (opt->setter) {
-                       rv = opt->setter(opt, val, curl);
-               } else if (CURLE_OK != curl_easy_setopt(ch, opt->option, (long) Z_BVAL_P(val))) {
-                       rv = FAILURE;
-               }
-               break;
-
-       case IS_LONG:
-               if (opt->setter) {
-                       rv = opt->setter(opt, val, curl);
-               } else if (CURLE_OK != curl_easy_setopt(ch, opt->option, Z_LVAL_P(val))) {
-                       rv = FAILURE;
-               }
-               break;
-
-       case IS_STRING:
-               if (opt->setter) {
-                       rv = opt->setter(opt, val, curl);
-               } else if ((opt->flags & PHP_HTTP_CURLE_OPTION_CHECK_STRLEN) && !Z_STRLEN_P(val)) {
-                       if (CURLE_OK != (rc = curl_easy_setopt(ch, opt->option, NULL))) {
-                               rv = FAILURE;
-                       }
-               } else if ((opt->flags & PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR) && Z_STRVAL_P(val) && SUCCESS != php_check_open_basedir(Z_STRVAL_P(val) TSRMLS_CC)) {
-                       if (CURLE_OK != (rc = curl_easy_setopt(ch, opt->option, NULL))) {
-                               rv = FAILURE;
-                       }
-               } else if (CURLE_OK != (rc = curl_easy_setopt(ch, opt->option, Z_STRVAL_P(val)))) {
-                       rv = FAILURE;
-               }
-               break;
-
-       case IS_DOUBLE:
-               if (opt->flags & PHP_HTTP_CURLE_OPTION_TRANSFORM_MS) {
-                       tmp = *val;
-                       Z_DVAL(tmp) *= 1000;
-                       val = &tmp;
-               }
-               if (opt->setter) {
-                       rv = opt->setter(opt, val, curl);
-               } else if (CURLE_OK != curl_easy_setopt(ch, opt->option, (long) Z_DVAL_P(val))) {
-                       rv = FAILURE;
-               }
-               break;
-
-       case IS_ARRAY:
-               if (opt->setter) {
-                       rv = opt->setter(opt, val, curl);
-               } else if (Z_TYPE_P(val) != IS_NULL) {
-                       rv = php_http_options_apply(&opt->suboptions, Z_ARRVAL_P(val), curl);
-               }
-               break;
-
-       default:
-               if (opt->setter) {
-                       rv = opt->setter(opt, val, curl);
-               } else {
-                       rv = FAILURE;
-               }
-               break;
-       }
-       if (rv != SUCCESS) {
-               php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s (%s)", opt->name.s, curl_easy_strerror(rc));
-       }
-       return rv;
-}
-
-#if PHP_HTTP_CURL_VERSION(7,30,0)
-static ZEND_RESULT_CODE php_http_curlm_option_set_pipelining_bl(php_http_option_t *opt, zval *value, void *userdata)
-{
-       php_http_client_t *client = userdata;
-       php_http_client_curl_t *curl = client->ctx;
-       CURLM *ch = curl->handle;
-       HashTable tmp_ht;
-       char **bl = NULL;
-       TSRMLS_FETCH_FROM_CTX(client->ts);
-
-       /* array of char *, ending with a NULL */
-       if (value && Z_TYPE_P(value) != IS_NULL) {
-               zval **entry;
-               HashPosition pos;
-               HashTable *ht = HASH_OF(value);
-               int c = zend_hash_num_elements(ht);
-               char **ptr = ecalloc(c + 1, sizeof(char *));
-
-               bl = ptr;
-
-               zend_hash_init(&tmp_ht, c, NULL, ZVAL_PTR_DTOR, 0);
-               array_join(ht, &tmp_ht, 0, ARRAY_JOIN_STRINGIFY);
-
-               FOREACH_HASH_VAL(pos, &tmp_ht, entry) {
-                       *ptr++ = Z_STRVAL_PP(entry);
-               }
-       }
-
-       if (CURLM_OK != curl_multi_setopt(ch, opt->option, bl)) {
-               if (bl) {
-                       efree(bl);
-                       zend_hash_destroy(&tmp_ht);
-               }
-               return FAILURE;
-       }
-
-       if (bl) {
-               efree(bl);
-               zend_hash_destroy(&tmp_ht);
-       }
-       return SUCCESS;
-}
-#endif
-
-#if PHP_HTTP_HAVE_EVENT
-static inline ZEND_RESULT_CODE php_http_curlm_use_eventloop(php_http_client_t *h, zend_bool enable)
-{
-       php_http_client_curl_t *curl = h->ctx;
-
-       if ((curl->useevents = enable)) {
-               if (!curl->evbase) {
-                       curl->evbase = event_base_new();
-               }
-               if (!curl->timeout) {
-                       curl->timeout = ecalloc(1, sizeof(struct event));
-               }
-               curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, h);
-               curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, php_http_curlm_socket_callback);
-               curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, h);
-               curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, php_http_curlm_timer_callback);
-       } else {
-               curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, NULL);
-               curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, NULL);
-               curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, NULL);
-               curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, NULL);
-       }
-
-       return SUCCESS;
-}
-
-static ZEND_RESULT_CODE php_http_curlm_option_set_use_eventloop(php_http_option_t *opt, zval *value, void *userdata)
-{
-       php_http_client_t *client = userdata;
-
-       return php_http_curlm_use_eventloop(client, value && Z_BVAL_P(value));
-}
-#endif
-
-static void php_http_curlm_options_init(php_http_options_t *registry TSRMLS_DC)
-{
-       php_http_option_t *opt;
-
-       /* set size of connection cache */
-       if ((opt = php_http_option_register(registry, ZEND_STRL("maxconnects"), CURLMOPT_MAXCONNECTS, IS_LONG))) {
-               /* -1 == default, 0 == unlimited */
-               ZVAL_LONG(&opt->defval, -1);
-       }
-       /* set max number of connections to a single host */
-#if PHP_HTTP_CURL_VERSION(7,30,0)
-       php_http_option_register(registry, ZEND_STRL("max_host_connections"), CURLMOPT_MAX_HOST_CONNECTIONS, IS_LONG);
-#endif
-       /* maximum number of requests in a pipeline */
-#if PHP_HTTP_CURL_VERSION(7,30,0)
-       if ((opt = php_http_option_register(registry, ZEND_STRL("max_pipeline_length"), CURLMOPT_MAX_PIPELINE_LENGTH, IS_LONG))) {
-               ZVAL_LONG(&opt->defval, 5);
-       }
-#endif
-       /* max simultaneously open connections */
-#if PHP_HTTP_CURL_VERSION(7,30,0)
-       php_http_option_register(registry, ZEND_STRL("max_total_connections"), CURLMOPT_MAX_TOTAL_CONNECTIONS, IS_LONG);
-#endif
-       /* enable/disable HTTP pipelining */
-       php_http_option_register(registry, ZEND_STRL("pipelining"), CURLMOPT_PIPELINING, IS_BOOL);
-       /* chunk length threshold for pipelining */
-#if PHP_HTTP_CURL_VERSION(7,30,0)
-       php_http_option_register(registry, ZEND_STRL("chunk_length_penalty_size"), CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE, IS_LONG);
-#endif
-       /* size threshold for pipelining penalty */
-#if PHP_HTTP_CURL_VERSION(7,30,0)
-       php_http_option_register(registry, ZEND_STRL("content_length_penalty_size"), CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE, IS_LONG);
-#endif
-       /* pipelining server blacklist */
-#if PHP_HTTP_CURL_VERSION(7,30,0)
-       if ((opt = php_http_option_register(registry, ZEND_STRL("pipelining_server_bl"), CURLMOPT_PIPELINING_SERVER_BL, IS_ARRAY))) {
-               opt->setter = php_http_curlm_option_set_pipelining_bl;
-       }
-#endif
-       /* pipelining host blacklist */
-#if PHP_HTTP_CURL_VERSION(7,30,0)
-       if ((opt = php_http_option_register(registry, ZEND_STRL("pipelining_site_bl"), CURLMOPT_PIPELINING_SITE_BL, IS_ARRAY))) {
-               opt->setter = php_http_curlm_option_set_pipelining_bl;
-       }
-#endif
-       /* events */
-#if PHP_HTTP_HAVE_EVENT
-       if ((opt = php_http_option_register(registry, ZEND_STRL("use_eventloop"), 0, IS_BOOL))) {
-               opt->setter = php_http_curlm_option_set_use_eventloop;
-       }
-#endif
-}
-
-static ZEND_RESULT_CODE php_http_curlm_set_option(php_http_option_t *opt, zval *val, void *userdata)
-{
-       php_http_client_t *client = userdata;
-       php_http_client_curl_t *curl = client->ctx;
-       CURLM *ch = curl->handle;
-       zval *orig = val;
-       CURLMcode rc = CURLM_UNKNOWN_OPTION;
-       ZEND_RESULT_CODE rv = SUCCESS;
-       TSRMLS_FETCH_FROM_CTX(client->ts);
-
-       if (!val) {
-               val = &opt->defval;
-       } else if (opt->type && Z_TYPE_P(val) != opt->type && !(Z_TYPE_P(val) == IS_NULL && opt->type == IS_ARRAY)) {
-               val = php_http_ztyp(opt->type, val);
-       }
-
-       if (opt->setter) {
-               rv = opt->setter(opt, val, client);
-       } else {
-               switch (opt->type) {
-               case IS_BOOL:
-                       if (CURLM_OK != (rc = curl_multi_setopt(ch, opt->option, (long) Z_BVAL_P(val)))) {
-                               rv = FAILURE;
-                       }
-                       break;
-               case IS_LONG:
-                       if (CURLM_OK != (rc = curl_multi_setopt(ch, opt->option, Z_LVAL_P(val)))) {
-                               rv = FAILURE;
-                       }
-                       break;
-               default:
-                       rv = FAILURE;
-                       break;
-               }
-       }
-
-       if (val && val != orig && val != &opt->defval) {
-               zval_ptr_dtor(&val);
-       }
-
-       if (rv != SUCCESS) {
-               php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s (%s)", opt->name.s, curl_easy_strerror(rc));
-       }
-       return rv;
-}
-
-/* client ops */
-
-static ZEND_RESULT_CODE php_http_client_curl_handler_reset(php_http_client_curl_handler_t *curl)
-{
-       CURL *ch = curl->handle;
-       php_http_curle_storage_t *st;
-
-       if ((st = php_http_curle_get_storage(ch))) {
-               if (st->url) {
-                       pefree(st->url, 1);
-                       st->url = NULL;
-               }
-               if (st->cookiestore) {
-                       pefree(st->cookiestore, 1);
-                       st->cookiestore = NULL;
-               }
-               st->errorbuffer[0] = '\0';
-       }
-
-       curl_easy_setopt(ch, CURLOPT_URL, NULL);
-       curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, NULL);
-       curl_easy_setopt(ch, CURLOPT_HTTPGET, 1L);
-       curl_easy_setopt(ch, CURLOPT_NOBODY, 0L);
-       /* libcurl < 7.19.6 does not clear auth info with USERPWD set to NULL */
-#if PHP_HTTP_CURL_VERSION(7,19,1)
-       curl_easy_setopt(ch, CURLOPT_PROXYUSERNAME, NULL);
-       curl_easy_setopt(ch, CURLOPT_PROXYPASSWORD, NULL);
-       curl_easy_setopt(ch, CURLOPT_USERNAME, NULL);
-       curl_easy_setopt(ch, CURLOPT_PASSWORD, NULL);
-#endif
-
-#if PHP_HTTP_CURL_VERSION(7,21,3)
-       if (curl->options.resolve) {
-               curl_slist_free_all(curl->options.resolve);
-               curl->options.resolve = NULL;
-       }
-#endif
-       curl->options.retry.count = 0;
-       curl->options.retry.delay = 0;
-       curl->options.redirects = 0;
-       curl->options.encode_cookies = 1;
-
-       if (curl->options.headers) {
-               curl_slist_free_all(curl->options.headers);
-               curl->options.headers = NULL;
-       }
-       if (curl->options.proxyheaders) {
-               curl_slist_free_all(curl->options.proxyheaders);
-               curl->options.proxyheaders = NULL;
-       }
-
-       php_http_buffer_reset(&curl->options.cookies);
-       php_http_buffer_reset(&curl->options.ranges);
-
-       return SUCCESS;
-}
-
-static php_http_client_curl_handler_t *php_http_client_curl_handler_init(php_http_client_t *h, php_resource_factory_t *rf)
-{
-       void *handle;
-       php_http_client_curl_handler_t *handler;
-       TSRMLS_FETCH_FROM_CTX(h->ts);
-
-       if (!(handle = php_resource_factory_handle_ctor(rf, NULL TSRMLS_CC))) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to initialize curl handle");
-               return NULL;
-       }
-
-       handler = ecalloc(1, sizeof(*handler));
-       handler->rf = rf;
-       handler->client = h;
-       handler->handle = handle;
-       handler->response.body = php_http_message_body_init(NULL, NULL TSRMLS_CC);
-       php_http_buffer_init(&handler->response.headers);
-       php_http_buffer_init(&handler->options.cookies);
-       php_http_buffer_init(&handler->options.ranges);
-       zend_hash_init(&handler->options.cache, 0, NULL, ZVAL_PTR_DTOR, 0);
-
-#if defined(ZTS)
-       curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1L);
-#endif
-       curl_easy_setopt(handle, CURLOPT_HEADER, 0L);
-       curl_easy_setopt(handle, CURLOPT_FILETIME, 1L);
-       curl_easy_setopt(handle, CURLOPT_AUTOREFERER, 1L);
-       curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L);
-       curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0L);
-       curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, php_http_curle_header_callback);
-       curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, php_http_curle_body_callback);
-       curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, php_http_curle_raw_callback);
-       curl_easy_setopt(handle, CURLOPT_READFUNCTION, php_http_curle_read_callback);
-       curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, php_http_curle_seek_callback);
-#if PHP_HTTP_CURL_VERSION(7,32,0)
-       curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, php_http_curle_xferinfo_callback);
-       curl_easy_setopt(handle, CURLOPT_XFERINFODATA, handler);
-#else
-       curl_easy_setopt(handle, CURLOPT_PROGRESSFUNCTION, php_http_curle_progress_callback);
-       curl_easy_setopt(handle, CURLOPT_PROGRESSDATA, handler);
-#endif
-       curl_easy_setopt(handle, CURLOPT_DEBUGDATA, handler);
-       curl_easy_setopt(handle, CURLOPT_WRITEDATA, handler);
-       curl_easy_setopt(handle, CURLOPT_HEADERDATA, handler);
-
-       php_http_client_curl_handler_reset(handler);
-
-       return handler;
-}
-
-
-static ZEND_RESULT_CODE php_http_client_curl_handler_prepare(php_http_client_curl_handler_t *curl, php_http_client_enqueue_t *enqueue)
-{
-       size_t body_size;
-       php_http_message_t *msg = enqueue->request;
-       php_http_curle_storage_t *storage = php_http_curle_get_storage(curl->handle);
-       TSRMLS_FETCH_FROM_CTX(curl->client->ts);
-
-       /* request url */
-       if (!PHP_HTTP_INFO(msg).request.url) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot request empty URL");
-               return FAILURE;
-       }
-       storage->errorbuffer[0] = '\0';
-       if (storage->url) {
-               pefree(storage->url, 1);
-       }
-       php_http_url_to_string(PHP_HTTP_INFO(msg).request.url, &storage->url, NULL, 1);
-       curl_easy_setopt(curl->handle, CURLOPT_URL, storage->url);
-
-       /* apply options */
-       php_http_options_apply(&php_http_curle_options, enqueue->options, curl);
-
-       /* request headers */
-       php_http_message_update_headers(msg);
-       if (zend_hash_num_elements(&msg->hdrs)) {
-               php_http_array_hashkey_t header_key = php_http_array_hashkey_init(0);
-               zval **header_val, *header_cpy;
-               HashPosition pos;
-               php_http_buffer_t header;
-#if !PHP_HTTP_CURL_VERSION(7,23,0)
-               zval **ct = NULL;
-
-               zend_hash_find(&msg->hdrs, ZEND_STRS("Content-Length"), (void *) &ct);
-#endif
-
-               php_http_buffer_init(&header);
-               FOREACH_HASH_KEYVAL(pos, &msg->hdrs, header_key, header_val) {
-                       if (header_key.type == HASH_KEY_IS_STRING) {
-#if !PHP_HTTP_CURL_VERSION(7,23,0)
-                               /* avoid duplicate content-length header */
-                               if (ct && *ct == *header_val) {
-                                       continue;
-                               }
-#endif
-                               header_cpy = php_http_ztyp(IS_STRING, *header_val);
-                               php_http_buffer_appendf(&header, "%s: %s", header_key.str, Z_STRVAL_P(header_cpy));
-                               php_http_buffer_fix(&header);
-                               curl->options.headers = curl_slist_append(curl->options.headers, header.data);
-                               php_http_buffer_reset(&header);
-
-                               zval_ptr_dtor(&header_cpy);
-                       }
-               }
-               php_http_buffer_dtor(&header);
-       }
-       curl_easy_setopt(curl->handle, CURLOPT_HTTPHEADER, curl->options.headers);
-
-       /* attach request body */
-       if ((body_size = php_http_message_body_size(msg->body))) {
-               /* RFC2616, section 4.3 (para. 4) states that Â»a message-body MUST NOT be included in a request if the
-                * specification of the request method (section 5.1.1) does not allow sending an entity-body in request.«
-                * Following the clause in section 5.1.1 (para. 2) that request methods Â»MUST be implemented with the
-                * same semantics as those specified in section 9« reveal that not any single defined HTTP/1.1 method
-                * does not allow a request body.
-                */
-               php_stream_rewind(php_http_message_body_stream(msg->body));
-               curl_easy_setopt(curl->handle, CURLOPT_SEEKDATA, msg->body);
-               curl_easy_setopt(curl->handle, CURLOPT_READDATA, msg->body);
-               curl_easy_setopt(curl->handle, CURLOPT_INFILESIZE, body_size);
-               curl_easy_setopt(curl->handle, CURLOPT_POSTFIELDSIZE, body_size);
-               curl_easy_setopt(curl->handle, CURLOPT_POST, 1L);
-       } else {
-               curl_easy_setopt(curl->handle, CURLOPT_SEEKDATA, NULL);
-               curl_easy_setopt(curl->handle, CURLOPT_READDATA, NULL);
-               curl_easy_setopt(curl->handle, CURLOPT_INFILESIZE, 0L);
-               curl_easy_setopt(curl->handle, CURLOPT_POSTFIELDSIZE, 0L);
-       }
-
-       /*
-        * Always use CUSTOMREQUEST, else curl won't send any request body for GET etc.
-        * See e.g. bug #69313.
-        *
-        * Here's what curl does:
-        * - CURLOPT_HTTPGET: ignore request body
-        * - CURLOPT_UPLOAD: set "Expect: 100-continue" header
-        * - CURLOPT_POST: set "Content-Type: application/x-www-form-urlencoded" header
-        * Now select the least bad.
-        *
-        * See also https://tools.ietf.org/html/rfc7231#section-5.1.1
-        */
-       if (PHP_HTTP_INFO(msg).request.method) {
-               switch(php_http_select_str(PHP_HTTP_INFO(msg).request.method, 2, "HEAD", "PUT")) {
-               case 0:
-                       curl_easy_setopt(curl->handle, CURLOPT_NOBODY, 1L);
-                       break;
-               case 1:
-                       curl_easy_setopt(curl->handle, CURLOPT_UPLOAD, 1L);
-                       break;
-               default:
-                       curl_easy_setopt(curl->handle, CURLOPT_CUSTOMREQUEST, PHP_HTTP_INFO(msg).request.method);
-               }
-       } else {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot use empty request method");
-               return FAILURE;
-       }
-
-       return SUCCESS;
-}
-
-static void php_http_client_curl_handler_clear(php_http_client_curl_handler_t *handler)
-{
-       curl_easy_setopt(handler->handle, CURLOPT_NOPROGRESS, 1L);
-#if PHP_HTTP_CURL_VERSION(7,32,0)
-       curl_easy_setopt(handler->handle, CURLOPT_XFERINFOFUNCTION, NULL);
-#else
-       curl_easy_setopt(handler->handle, CURLOPT_PROGRESSFUNCTION, NULL);
-#endif
-       curl_easy_setopt(handler->handle, CURLOPT_VERBOSE, 0L);
-       curl_easy_setopt(handler->handle, CURLOPT_DEBUGFUNCTION, NULL);
-}
-
-static void php_http_client_curl_handler_dtor(php_http_client_curl_handler_t *handler)
-{
-       TSRMLS_FETCH_FROM_CTX(handler->client->ts);
-
-       php_http_client_curl_handler_clear(handler);
-
-       php_resource_factory_handle_dtor(handler->rf, handler->handle TSRMLS_CC);
-       php_resource_factory_free(&handler->rf);
-
-       php_http_message_body_free(&handler->response.body);
-       php_http_buffer_dtor(&handler->response.headers);
-       php_http_buffer_dtor(&handler->options.ranges);
-       php_http_buffer_dtor(&handler->options.cookies);
-       zend_hash_destroy(&handler->options.cache);
-
-       if (handler->options.headers) {
-               curl_slist_free_all(handler->options.headers);
-               handler->options.headers = NULL;
-       }
-
-       efree(handler);
-}
-
-static php_http_client_t *php_http_client_curl_init(php_http_client_t *h, void *handle)
-{
-       php_http_client_curl_t *curl;
-       TSRMLS_FETCH_FROM_CTX(h->ts);
-
-       if (!handle && !(handle = php_resource_factory_handle_ctor(h->rf, NULL TSRMLS_CC))) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to initialize curl handle");
-               return NULL;
-       }
-
-       curl = ecalloc(1, sizeof(*curl));
-       curl->handle = handle;
-       curl->unfinished = 0;
-       h->ctx = curl;
-
-       return h;
-}
-
-static void php_http_client_curl_dtor(php_http_client_t *h)
-{
-       php_http_client_curl_t *curl = h->ctx;
-       TSRMLS_FETCH_FROM_CTX(h->ts);
-
-#if PHP_HTTP_HAVE_EVENT
-       if (curl->timeout) {
-               if (event_initialized(curl->timeout) && event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
-                       event_del(curl->timeout);
-               }
-               efree(curl->timeout);
-               curl->timeout = NULL;
-       }
-       if (curl->evbase) {
-               event_base_free(curl->evbase);
-               curl->evbase = NULL;
-       }
-#endif
-       curl->unfinished = 0;
-
-       php_resource_factory_handle_dtor(h->rf, curl->handle TSRMLS_CC);
-
-       efree(curl);
-       h->ctx = NULL;
-}
-
-static void queue_dtor(php_http_client_enqueue_t *e)
-{
-       php_http_client_curl_handler_t *handler = e->opaque;
-
-       if (handler->queue.dtor) {
-               e->opaque = handler->queue.opaque;
-               handler->queue.dtor(e);
-       }
-       php_http_client_curl_handler_dtor(handler);
-}
-
-static php_resource_factory_t *create_rf(php_http_client_t *h, php_http_client_enqueue_t *enqueue TSRMLS_DC)
-{
-       php_persistent_handle_factory_t *pf = NULL;
-       php_resource_factory_t *rf = NULL;
-       php_http_url_t *url = enqueue->request->http.info.request.url;
-
-       if (!url || (!url->host && !url->path)) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot request empty URL");
-               return NULL;
-       }
-
-       /* only if the client itself is setup for persistence */
-       if (php_resource_factory_is_persistent(h->rf)) {
-               char *id_str = NULL;
-               size_t id_len;
-               int port = url->port ? url->port : 80;
-               zval **zport;
-
-               if (SUCCESS == zend_hash_find(enqueue->options, ZEND_STRS("port"), (void *) &zport)) {
-                       zval *zcpy = php_http_ztyp(IS_LONG, *zport);
-
-                       if (Z_LVAL_P(zcpy)) {
-                               port = Z_LVAL_P(zcpy);
-                       }
-                       zval_ptr_dtor(&zcpy);
-               }
-
-               id_len = spprintf(&id_str, 0, "%s:%d", STR_PTR(url->host), port);
-               pf = php_persistent_handle_concede(NULL, ZEND_STRL("http\\Client\\Curl\\Request"), id_str, id_len, NULL, NULL TSRMLS_CC);
-               efree(id_str);
-       }
-
-       if (pf) {
-               rf = php_persistent_handle_resource_factory_init(NULL, pf);
-       } else {
-               rf = php_resource_factory_init(NULL, &php_http_curle_resource_factory_ops, NULL, NULL);
-       }
-
-       return rf;
-}
-
-static ZEND_RESULT_CODE php_http_client_curl_enqueue(php_http_client_t *h, php_http_client_enqueue_t *enqueue)
-{
-       CURLMcode rs;
-       php_http_client_curl_t *curl = h->ctx;
-       php_http_client_curl_handler_t *handler;
-       php_http_client_progress_state_t *progress;
-       php_resource_factory_t *rf;
-       TSRMLS_FETCH_FROM_CTX(h->ts);
-
-       rf = create_rf(h, enqueue TSRMLS_CC);
-       if (!rf) {
-               return FAILURE;
-       }
-
-       handler = php_http_client_curl_handler_init(h, rf);
-       if (!handler) {
-               return FAILURE;
-       }
-
-       if (SUCCESS != php_http_client_curl_handler_prepare(handler, enqueue)) {
-               php_http_client_curl_handler_dtor(handler);
-               return FAILURE;
-       }
-
-       handler->queue = *enqueue;
-       enqueue->opaque = handler;
-       enqueue->dtor = queue_dtor;
-
-       if (CURLM_OK == (rs = curl_multi_add_handle(curl->handle, handler->handle))) {
-               zend_llist_add_element(&h->requests, enqueue);
-               ++curl->unfinished;
-
-               if (h->callback.progress.func && SUCCESS == php_http_client_getopt(h, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, enqueue->request, &progress)) {
-                       progress->info = "start";
-                       h->callback.progress.func(h->callback.progress.arg, h, &handler->queue, progress);
-                       progress->started = 1;
-               }
-
-               return SUCCESS;
-       } else {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not enqueue request: %s", curl_multi_strerror(rs));
-               return FAILURE;
-       }
-}
-
-static ZEND_RESULT_CODE php_http_client_curl_dequeue(php_http_client_t *h, php_http_client_enqueue_t *enqueue)
-{
-       CURLMcode rs;
-       php_http_client_curl_t *curl = h->ctx;
-       php_http_client_curl_handler_t *handler = enqueue->opaque;
-       TSRMLS_FETCH_FROM_CTX(h->ts);
-
-       php_http_client_curl_handler_clear(handler);
-       if (CURLM_OK == (rs = curl_multi_remove_handle(curl->handle, handler->handle))) {
-               zend_llist_del_element(&h->requests, handler->handle, (int (*)(void *, void *)) compare_queue);
-               return SUCCESS;
-       } else {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not dequeue request: %s", curl_multi_strerror(rs));
-       }
-
-       return FAILURE;
-}
-
-static void php_http_client_curl_reset(php_http_client_t *h)
-{
-       zend_llist_element *next_el, *this_el;
-
-       for (this_el = h->requests.head; this_el; this_el = next_el) {
-               next_el = this_el->next;
-               php_http_client_curl_dequeue(h, (void *) this_el->data);
-       }
-}
-
-static inline void php_http_client_curl_get_timeout(php_http_client_curl_t *curl, long max_tout, struct timeval *timeout)
-{
-       if ((CURLM_OK == curl_multi_timeout(curl->handle, &max_tout)) && (max_tout > 0)) {
-               timeout->tv_sec = max_tout / 1000;
-               timeout->tv_usec = (max_tout % 1000) * 1000;
-       } else {
-               timeout->tv_sec = 0;
-               timeout->tv_usec = 1000;
-       }
-}
-
-#ifdef PHP_WIN32
-#      define SELECT_ERROR SOCKET_ERROR
-#else
-#      define SELECT_ERROR -1
-#endif
-
-static ZEND_RESULT_CODE php_http_client_curl_wait(php_http_client_t *h, struct timeval *custom_timeout)
-{
-       int MAX;
-       fd_set R, W, E;
-       struct timeval timeout;
-       php_http_client_curl_t *curl = h->ctx;
-
-#if PHP_HTTP_HAVE_EVENT
-       if (curl->useevents) {
-               if (!event_initialized(curl->timeout)) {
-                       event_assign(curl->timeout, curl->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_curlm_timeout_callback, h);
-               } else if (custom_timeout && timerisset(custom_timeout)) {
-                       event_add(curl->timeout, custom_timeout);
-               } else if (!event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
-                       php_http_client_curl_get_timeout(curl, 1000, &timeout);
-                       event_add(curl->timeout, &timeout);
-               }
-
-               event_base_loop(curl->evbase, EVLOOP_ONCE);
-
-               return SUCCESS;
-       }
-#endif
-
-       FD_ZERO(&R);
-       FD_ZERO(&W);
-       FD_ZERO(&E);
-
-       if (CURLM_OK == curl_multi_fdset(curl->handle, &R, &W, &E, &MAX)) {
-               if (custom_timeout && timerisset(custom_timeout)) {
-                       timeout = *custom_timeout;
-               } else {
-                       php_http_client_curl_get_timeout(curl, 1000, &timeout);
-               }
-
-               if (MAX == -1) {
-                       php_http_sleep((double) timeout.tv_sec + (double) (timeout.tv_usec / PHP_HTTP_MCROSEC));
-                       return SUCCESS;
-               } else if (SELECT_ERROR != select(MAX + 1, &R, &W, &E, &timeout)) {
-                       return SUCCESS;
-               }
-       }
-       return FAILURE;
-}
-
-static int php_http_client_curl_once(php_http_client_t *h)
-{
-       php_http_client_curl_t *curl = h->ctx;
-
-#if PHP_HTTP_HAVE_EVENT
-       if (curl->useevents) {
-               event_base_loop(curl->evbase, EVLOOP_NONBLOCK);
-       } else
-#endif
-       while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curl->handle, &curl->unfinished));
-
-       php_http_curlm_responsehandler(h);
-
-       return curl->unfinished;
-
-}
-
-static ZEND_RESULT_CODE php_http_client_curl_exec(php_http_client_t *h)
-{
-#if PHP_HTTP_HAVE_EVENT
-       php_http_client_curl_t *curl = h->ctx;
-#endif
-       TSRMLS_FETCH_FROM_CTX(h->ts);
-
-#if PHP_HTTP_HAVE_EVENT
-       if (curl->useevents) {
-               php_http_curlm_timeout_callback(CURL_SOCKET_TIMEOUT, /*EV_READ|EV_WRITE*/0, h);
-               do {
-                       int ev_rc = event_base_dispatch(curl->evbase);
-
-#if DBG_EVENTS
-                       fprintf(stderr, "%c", "X.0"[ev_rc+1]);
-#endif
-
-                       if (ev_rc < 0) {
-                               php_error_docref(NULL TSRMLS_CC, E_ERROR, "Error in event_base_dispatch()");
-                               return FAILURE;
-                       }
-               } while (curl->unfinished && !EG(exception));
-       } else
-#endif
-       {
-               while (php_http_client_curl_once(h) && !EG(exception)) {
-                       if (SUCCESS != php_http_client_curl_wait(h, NULL)) {
-#ifdef PHP_WIN32
-                               /* see http://msdn.microsoft.com/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp */
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "WinSock error: %d", WSAGetLastError());
-#else
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno));
-#endif
-                               return FAILURE;
-                       }
-               }
-       }
-
-       return SUCCESS;
-}
-
-static ZEND_RESULT_CODE php_http_client_curl_setopt(php_http_client_t *h, php_http_client_setopt_opt_t opt, void *arg)
-{
-       php_http_client_curl_t *curl = h->ctx;
-
-       switch (opt) {
-               case PHP_HTTP_CLIENT_OPT_CONFIGURATION:
-                       return php_http_options_apply(&php_http_curlm_options, (HashTable *) arg,  h);
-                       break;
-
-               case PHP_HTTP_CLIENT_OPT_ENABLE_PIPELINING:
-                       if (CURLM_OK != curl_multi_setopt(curl->handle, CURLMOPT_PIPELINING, (long) *((zend_bool *) arg))) {
-                               return FAILURE;
-                       }
-                       break;
-
-               case PHP_HTTP_CLIENT_OPT_USE_EVENTS:
-#if PHP_HTTP_HAVE_EVENT
-                       return php_http_curlm_use_eventloop(h, *(zend_bool *) arg);
-                       break;
-#endif
-
-               default:
-                       return FAILURE;
-       }
-       return SUCCESS;
-}
-
-static int apply_available_options(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
-{
-       php_http_option_t *opt = pDest;
-       HashTable *ht;
-       zval *entry;
-       int c;
-
-       ht = va_arg(args, HashTable*);
-
-       MAKE_STD_ZVAL(entry);
-
-       if ((c = zend_hash_num_elements(&opt->suboptions.options))) {
-               array_init_size(entry, c);
-               zend_hash_apply_with_arguments(&opt->suboptions.options TSRMLS_CC, apply_available_options, 1, Z_ARRVAL_P(entry));
-       } else {
-               /* catch deliberate NULL options */
-               if (Z_TYPE(opt->defval) == IS_STRING && !Z_STRVAL(opt->defval)) {
-                       ZVAL_NULL(entry);
-               } else {
-                       ZVAL_COPY_VALUE(entry, &opt->defval);
-                       zval_copy_ctor(entry);
-               }
-       }
-
-       if (hash_key->nKeyLength) {
-               zend_hash_quick_update(ht, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &entry, sizeof(zval *), NULL);
-       } else {
-               zend_hash_index_update(ht, hash_key->h, (void *) &entry, sizeof(zval *), NULL);
-       }
-
-       return ZEND_HASH_APPLY_KEEP;
-}
-
-static ZEND_RESULT_CODE php_http_client_curl_getopt(php_http_client_t *h, php_http_client_getopt_opt_t opt, void *arg, void **res)
-{
-       php_http_client_enqueue_t *enqueue;
-       TSRMLS_FETCH_FROM_CTX(h->ts);
-
-       switch (opt) {
-       case PHP_HTTP_CLIENT_OPT_PROGRESS_INFO:
-               if ((enqueue = php_http_client_enqueued(h, arg, NULL))) {
-                       php_http_client_curl_handler_t *handler = enqueue->opaque;
-
-                       *((php_http_client_progress_state_t **) res) = &handler->progress;
-                       return SUCCESS;
-               }
-               break;
-
-       case PHP_HTTP_CLIENT_OPT_TRANSFER_INFO:
-               if ((enqueue = php_http_client_enqueued(h, arg, NULL))) {
-                       php_http_client_curl_handler_t *handler = enqueue->opaque;
-
-                       php_http_curle_get_info(handler->handle, *(HashTable **) res);
-                       return SUCCESS;
-               }
-               break;
-
-       case PHP_HTTP_CLIENT_OPT_AVAILABLE_OPTIONS:
-               zend_hash_apply_with_arguments(&php_http_curle_options.options TSRMLS_CC, apply_available_options, 1, *(HashTable **) res);
-               break;
-
-       case PHP_HTTP_CLIENT_OPT_AVAILABLE_CONFIGURATION:
-               zend_hash_apply_with_arguments(&php_http_curlm_options.options TSRMLS_CC, apply_available_options, 1, *(HashTable **) res);
-               break;
-
-       default:
-               break;
-       }
-
-       return FAILURE;
-}
-
-static php_http_client_ops_t php_http_client_curl_ops = {
-       &php_http_curlm_resource_factory_ops,
-       php_http_client_curl_init,
-       NULL /* copy */,
-       php_http_client_curl_dtor,
-       php_http_client_curl_reset,
-       php_http_client_curl_exec,
-       php_http_client_curl_wait,
-       php_http_client_curl_once,
-       php_http_client_curl_enqueue,
-       php_http_client_curl_dequeue,
-       php_http_client_curl_setopt,
-       php_http_client_curl_getopt
-};
-
-php_http_client_ops_t *php_http_client_curl_get_ops(void)
-{
-       return &php_http_client_curl_ops;
-}
-
-PHP_MINIT_FUNCTION(http_client_curl)
-{
-       php_http_options_t *options;
-       php_http_client_driver_t driver = {
-               ZEND_STRL("curl"),
-               &php_http_client_curl_ops
-       };
-
-       if (SUCCESS != php_http_client_driver_add(&driver)) {
-               return FAILURE;
-       }
-
-       if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl"), &php_http_curlm_resource_factory_ops, NULL, NULL TSRMLS_CC)) {
-               return FAILURE;
-       }
-       if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl\\Request"), &php_http_curle_resource_factory_ops, NULL, NULL TSRMLS_CC)) {
-               return FAILURE;
-       }
-
-       if ((options = php_http_options_init(&php_http_curle_options, 1))) {
-               options->getter = php_http_curle_get_option;
-               options->setter = php_http_curle_set_option;
-
-               php_http_curle_options_init(options TSRMLS_CC);
-       }
-       if ((options = php_http_options_init(&php_http_curlm_options, 1))) {
-               options->getter = php_http_option_get;
-               options->setter = php_http_curlm_set_option;
-
-               php_http_curlm_options_init(options TSRMLS_CC);
-       }
-
-       /*
-       * HTTP Protocol Version Constants
-       */
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_1_0", CURL_HTTP_VERSION_1_0, CONST_CS|CONST_PERSISTENT);
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_1_1", CURL_HTTP_VERSION_1_1, CONST_CS|CONST_PERSISTENT);
-#if PHP_HTTP_CURL_VERSION(7,33,0)
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_2_0", CURL_HTTP_VERSION_2_0, CONST_CS|CONST_PERSISTENT);
-#endif
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_ANY", CURL_HTTP_VERSION_NONE, CONST_CS|CONST_PERSISTENT);
-
-       /*
-       * SSL Version Constants
-       */
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1", CURL_SSLVERSION_TLSv1, CONST_CS|CONST_PERSISTENT);
-#if PHP_HTTP_CURL_VERSION(7,34,0)
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1_0", CURL_SSLVERSION_TLSv1_0, CONST_CS|CONST_PERSISTENT);
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1_1", CURL_SSLVERSION_TLSv1_1, CONST_CS|CONST_PERSISTENT);
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1_2", CURL_SSLVERSION_TLSv1_2, CONST_CS|CONST_PERSISTENT);
-#endif
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_SSLv2", CURL_SSLVERSION_SSLv2, CONST_CS|CONST_PERSISTENT);
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_SSLv3", CURL_SSLVERSION_SSLv3, CONST_CS|CONST_PERSISTENT);
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_ANY", CURL_SSLVERSION_DEFAULT, CONST_CS|CONST_PERSISTENT);
-#if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP)
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "TLSAUTH_SRP", CURL_TLSAUTH_SRP, CONST_CS|CONST_PERSISTENT);
-#endif
-
-       /*
-       * DNS IPvX resolving
-       */
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "IPRESOLVE_V4", CURL_IPRESOLVE_V4, CONST_CS|CONST_PERSISTENT);
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "IPRESOLVE_V6", CURL_IPRESOLVE_V6, CONST_CS|CONST_PERSISTENT);
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "IPRESOLVE_ANY", CURL_IPRESOLVE_WHATEVER, CONST_CS|CONST_PERSISTENT);
-
-       /*
-       * Auth Constants
-       */
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_BASIC", CURLAUTH_BASIC, CONST_CS|CONST_PERSISTENT);
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_DIGEST", CURLAUTH_DIGEST, CONST_CS|CONST_PERSISTENT);
-#if PHP_HTTP_CURL_VERSION(7,19,3)
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_DIGEST_IE", CURLAUTH_DIGEST_IE, CONST_CS|CONST_PERSISTENT);
-#endif
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_NTLM", CURLAUTH_NTLM, CONST_CS|CONST_PERSISTENT);
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_GSSNEG", CURLAUTH_GSSNEGOTIATE, CONST_CS|CONST_PERSISTENT);
-#if PHP_HTTP_CURL_VERSION(7,38,0)
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_SPNEGO", CURLAUTH_NEGOTIATE, CONST_CS|CONST_PERSISTENT);
-#endif
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_ANY", CURLAUTH_ANY, CONST_CS|CONST_PERSISTENT);
-
-       /*
-       * Proxy Type Constants
-       */
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS4", CURLPROXY_SOCKS4, CONST_CS|CONST_PERSISTENT);
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS4A", CURLPROXY_SOCKS5, CONST_CS|CONST_PERSISTENT);
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS5_HOSTNAME", CURLPROXY_SOCKS5, CONST_CS|CONST_PERSISTENT);
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS5", CURLPROXY_SOCKS5, CONST_CS|CONST_PERSISTENT);
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_HTTP", CURLPROXY_HTTP, CONST_CS|CONST_PERSISTENT);
-#if PHP_HTTP_CURL_VERSION(7,19,4)
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_HTTP_1_0", CURLPROXY_HTTP_1_0, CONST_CS|CONST_PERSISTENT);
-#endif
-
-       /*
-       * Post Redirection Constants
-       */
-#if PHP_HTTP_CURL_VERSION(7,19,1)
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_301", CURL_REDIR_POST_301, CONST_CS|CONST_PERSISTENT);
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_302", CURL_REDIR_POST_302, CONST_CS|CONST_PERSISTENT);
-#if PHP_HTTP_CURL_VERSION(7,26,0)
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_303", CURL_REDIR_POST_303, CONST_CS|CONST_PERSISTENT);
-#endif
-       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_ALL", CURL_REDIR_POST_ALL, CONST_CS|CONST_PERSISTENT);
-#endif
-
-       return SUCCESS;
-}
-
-PHP_MSHUTDOWN_FUNCTION(http_client_curl)
-{
-       php_persistent_handle_cleanup(ZEND_STRL("http\\Client\\Curl"), NULL, 0 TSRMLS_CC);
-       php_persistent_handle_cleanup(ZEND_STRL("http\\Client\\Curl\\Request"), NULL, 0 TSRMLS_CC);
-
-       php_http_options_dtor(&php_http_curle_options);
-       php_http_options_dtor(&php_http_curlm_options);
-
-       return SUCCESS;
-}
-
-#endif /* PHP_HTTP_HAVE_CURL */
-
-/*
- * 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/php_http_client_curl.h b/php_http_client_curl.h
deleted file mode 100644 (file)
index c82a09c..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_CLIENT_CURL_H
-#define PHP_HTTP_CLIENT_CURL_H
-
-#if PHP_HTTP_HAVE_CURL
-
-PHP_MINIT_FUNCTION(http_client_curl);
-PHP_MSHUTDOWN_FUNCTION(http_client_curl);
-#endif /* PHP_HTTP_HAVE_CURL */
-
-#endif /* PHP_HTTP_CLIENT_CURL_H */
-
-/*
- * 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/php_http_client_request.c b/php_http_client_request.c
deleted file mode 100644 (file)
index 0e40cc5..0000000
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-void php_http_client_options_set_subr(zval *this_ptr, char *key, size_t len, zval *opts, int overwrite TSRMLS_DC);
-void php_http_client_options_set(zval *this_ptr, zval *opts TSRMLS_DC);
-void php_http_client_options_get_subr(zval *this_ptr, char *key, size_t len, zval *return_value TSRMLS_DC);
-
-#define PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj) \
-       do { \
-               if (!obj->message) { \
-                       obj->message = php_http_message_init(NULL, PHP_HTTP_REQUEST, NULL TSRMLS_CC); \
-               } \
-       } while(0)
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest___construct, 0, 0, 0)
-       ZEND_ARG_INFO(0, method)
-       ZEND_ARG_INFO(0, url)
-       ZEND_ARG_ARRAY_INFO(0, headers, 1)
-       ZEND_ARG_OBJ_INFO(0, body, http\\Message\\Body, 1)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClientRequest, __construct)
-{
-       char *meth_str = NULL;
-       int meth_len = 0;
-       zval *zheaders = NULL, *zbody = NULL, *zurl = NULL;
-       php_http_message_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!z!a!O!", &meth_str, &meth_len, &zurl, &zheaders, &zbody, php_http_message_body_class_entry), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       if (obj->message) {
-               php_http_message_set_type(obj->message, PHP_HTTP_REQUEST);
-       } else {
-               obj->message = php_http_message_init(NULL, PHP_HTTP_REQUEST, NULL TSRMLS_CC);
-       }
-
-       if (zbody) {
-               php_http_expect(SUCCESS == php_http_message_object_set_body(obj, zbody TSRMLS_CC), unexpected_val, return);
-       }
-       if (meth_str && meth_len) {
-               PHP_HTTP_INFO(obj->message).request.method = estrndup(meth_str, meth_len);
-       }
-       if (zurl) {
-               PHP_HTTP_INFO(obj->message).request.url = php_http_url_from_zval(zurl, ~0 TSRMLS_CC);
-       }
-       if (zheaders) {
-               array_copy(Z_ARRVAL_P(zheaders), &obj->message->hdrs);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_setContentType, 0, 0, 1)
-       ZEND_ARG_INFO(0, content_type)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClientRequest, setContentType)
-{
-       char *ct_str;
-       int ct_len;
-       php_http_message_object_t *obj;
-       zval *zct;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ct_str, &ct_len), invalid_arg, return);
-
-       if (ct_len && !strchr(ct_str, '/')) {
-               php_http_throw(unexpected_val, "Content type \"%s\" does not seem to contain a primary and a secondary part", ct_str);
-               return;
-       }
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj);
-
-       MAKE_STD_ZVAL(zct);
-       ZVAL_STRINGL(zct, ct_str, ct_len, 1);
-       zend_hash_update(&obj->message->hdrs, "Content-Type", sizeof("Content-Type"), (void *) &zct, sizeof(void *), NULL);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_getContentType, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClientRequest, getContentType)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-               zval *zct;
-
-               PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj);
-
-               php_http_message_update_headers(obj->message);
-               zct = php_http_message_header(obj->message, ZEND_STRL("Content-Type"), 1);
-               if (zct) {
-                       RETURN_ZVAL(zct, 0, 1);
-               }
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_setQuery, 0, 0, 0)
-       ZEND_ARG_INFO(0, query_data)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClientRequest, setQuery)
-{
-       zval *qdata = NULL;
-       php_http_message_object_t *obj;
-       php_http_url_t *old_url = NULL, new_url = {NULL};
-       char empty[] = "";
-       unsigned flags = PHP_HTTP_URL_REPLACE;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z!", &qdata), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj);
-
-       if (qdata) {
-               zval arr, str;
-
-               INIT_PZVAL(&arr);
-               array_init(&arr);
-               INIT_PZVAL(&str);
-               ZVAL_NULL(&str);
-
-               php_http_expect(SUCCESS == php_http_querystring_update(&arr, qdata, &str TSRMLS_CC), bad_querystring,
-                               zval_dtor(&arr);
-                               return;
-               );
-               new_url.query = Z_STRVAL(str);
-               zval_dtor(&arr);
-       } else {
-               flags = PHP_HTTP_URL_STRIP_QUERY;
-       }
-
-       if (obj->message->http.info.request.url) {
-               old_url = obj->message->http.info.request.url;
-       }
-
-       obj->message->http.info.request.url = php_http_url_mod(old_url, &new_url, flags TSRMLS_CC);
-
-       if (old_url) {
-               php_http_url_free(&old_url);
-       }
-       if (new_url.query != &empty[0]) {
-               PTR_FREE(new_url.query);
-       }
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_getQuery, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClientRequest, getQuery)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj);
-
-               if (obj->message->http.info.request.url && obj->message->http.info.request.url->query) {
-                       RETVAL_STRING(obj->message->http.info.request.url->query, 1);
-               }
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_addQuery, 0, 0, 1)
-       ZEND_ARG_INFO(0, query_data)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClientRequest, addQuery)
-{
-       zval *qdata, arr, str;
-       php_http_message_object_t *obj;
-       php_http_url_t *old_url = NULL, new_url = {NULL};
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &qdata), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj);
-
-       INIT_PZVAL(&arr);
-       array_init(&arr);
-       INIT_PZVAL(&str);
-       ZVAL_NULL(&str);
-
-       php_http_expect(SUCCESS == php_http_querystring_update(&arr, qdata, &str TSRMLS_CC), bad_querystring,
-                       zval_dtor(&arr);
-                       return;
-       );
-       new_url.query = Z_STRVAL(str);
-       zval_dtor(&arr);
-
-       if (obj->message->http.info.request.url) {
-               old_url = obj->message->http.info.request.url;
-       }
-
-       obj->message->http.info.request.url = php_http_url_mod(old_url, &new_url, PHP_HTTP_URL_JOIN_QUERY TSRMLS_CC);
-
-       if (old_url) {
-               php_http_url_free(&old_url);
-       }
-       PTR_FREE(new_url.query);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_setOptions, 0, 0, 0)
-       ZEND_ARG_ARRAY_INFO(0, options, 1)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClientRequest, setOptions)
-{
-       zval *opts = NULL;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return);
-
-       php_http_client_options_set(getThis(), opts TSRMLS_CC);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_getOptions, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClientRequest, getOptions)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               zval *zoptions = zend_read_property(php_http_client_request_class_entry, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
-               RETURN_ZVAL(zoptions, 1, 0);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_setSslOptions, 0, 0, 0)
-       ZEND_ARG_ARRAY_INFO(0, ssl_options, 1)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClientRequest, setSslOptions)
-{
-       zval *opts = NULL;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return);
-
-       php_http_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 1 TSRMLS_CC);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_addSslOptions, 0, 0, 0)
-       ZEND_ARG_ARRAY_INFO(0, ssl_options, 1)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClientRequest, addSslOptions)
-{
-       zval *opts = NULL;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return);
-
-       php_http_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 0 TSRMLS_CC);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_getSslOptions, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClientRequest, getSslOptions)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_client_options_get_subr(getThis(), ZEND_STRS("ssl"), return_value TSRMLS_CC);
-       }
-}
-
-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, 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)
-       PHP_ME(HttpClientRequest, getQuery,       ai_HttpClientRequest_getQuery,       ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClientRequest, addQuery,       ai_HttpClientRequest_addQuery,       ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClientRequest, setOptions,     ai_HttpClientRequest_setOptions,     ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClientRequest, getOptions,     ai_HttpClientRequest_getOptions,     ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClientRequest, setSslOptions,  ai_HttpClientRequest_setSslOptions,  ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClientRequest, getSslOptions,  ai_HttpClientRequest_getSslOptions,  ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClientRequest, addSslOptions,  ai_HttpClientRequest_addSslOptions,  ZEND_ACC_PUBLIC)
-       EMPTY_FUNCTION_ENTRY
-};
-
-zend_class_entry *php_http_client_request_class_entry;
-
-PHP_MINIT_FUNCTION(http_client_request)
-{
-       zend_class_entry ce = {0};
-
-       INIT_NS_CLASS_ENTRY(ce, "http\\Client", "Request", php_http_client_request_methods);
-       php_http_client_request_class_entry = zend_register_internal_class_ex(&ce, php_http_message_class_entry, NULL TSRMLS_CC);
-
-       zend_declare_property_null(php_http_client_request_class_entry, ZEND_STRL("options"), ZEND_ACC_PROTECTED TSRMLS_CC);
-
-       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/php_http_client_request.h b/php_http_client_request.h
deleted file mode 100644 (file)
index 474114b..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_CLIENT_REQUEST_H
-#define PHP_HTTP_CLIENT_REQUEST_H
-
-PHP_HTTP_API zend_class_entry *php_http_client_request_class_entry;
-PHP_MINIT_FUNCTION(http_client_request);
-
-#endif /* PHP_HTTP_CLIENT_REQUEST_H */
-
-/*
- * 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/php_http_client_response.c b/php_http_client_response.c
deleted file mode 100644 (file)
index 8d512ec..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientResponse_getCookies, 0, 0, 0)
-       ZEND_ARG_INFO(0, flags)
-       ZEND_ARG_INFO(0, allowed_extras)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClientResponse, getCookies)
-{
-       long flags = 0;
-       zval *allowed_extras_array = NULL;
-       int i = 0;
-       char **allowed_extras = NULL;
-       zval *header = NULL, **entry = NULL;
-       HashPosition pos;
-       php_http_message_object_t *msg;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|la!", &flags, &allowed_extras_array)) {
-               return;
-       }
-
-       msg = zend_object_store_get_object(getThis() TSRMLS_CC);
-       array_init(return_value);
-
-       if (allowed_extras_array) {
-               allowed_extras = ecalloc(zend_hash_num_elements(Z_ARRVAL_P(allowed_extras_array)) + 1, sizeof(char *));
-               FOREACH_VAL(pos, allowed_extras_array, entry) {
-                       zval *data = php_http_ztyp(IS_STRING, *entry);
-                       allowed_extras[i++] = estrndup(Z_STRVAL_P(data), Z_STRLEN_P(data));
-                       zval_ptr_dtor(&data);
-               }
-       }
-
-       if ((header = php_http_message_header(msg->message, ZEND_STRL("Set-Cookie"), 0))) {
-               php_http_cookie_list_t *list;
-
-               if (Z_TYPE_P(header) == IS_ARRAY) {
-                       zval **single_header;
-
-                       FOREACH_VAL(pos, header, single_header) {
-                               zval *data = php_http_ztyp(IS_STRING, *single_header);
-
-                               if ((list = php_http_cookie_list_parse(NULL, Z_STRVAL_P(data), Z_STRLEN_P(data), flags, allowed_extras TSRMLS_CC))) {
-                                       zval *cookie;
-
-                                       MAKE_STD_ZVAL(cookie);
-                                       ZVAL_OBJVAL(cookie, php_http_cookie_object_new_ex(php_http_cookie_class_entry, list, NULL TSRMLS_CC), 0);
-                                       add_next_index_zval(return_value, cookie);
-                               }
-                               zval_ptr_dtor(&data);
-                       }
-               } else {
-                       zval *data = php_http_ztyp(IS_STRING, header);
-                       if ((list = php_http_cookie_list_parse(NULL, Z_STRVAL_P(data), Z_STRLEN_P(data), flags, allowed_extras TSRMLS_CC))) {
-                               zval *cookie;
-
-                               MAKE_STD_ZVAL(cookie);
-                               ZVAL_OBJVAL(cookie, php_http_cookie_object_new_ex(php_http_cookie_class_entry, list, NULL TSRMLS_CC), 0);
-                               add_next_index_zval(return_value, cookie);
-                       }
-                       zval_ptr_dtor(&data);
-               }
-               zval_ptr_dtor(&header);
-       }
-
-       if (allowed_extras) {
-               for (i = 0; allowed_extras[i]; ++i) {
-                       efree(allowed_extras[i]);
-               }
-               efree(allowed_extras);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientResponse_getTransferInfo, 0, 0, 0)
-       ZEND_ARG_INFO(0, element)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpClientResponse, getTransferInfo)
-{
-       char *info_name = NULL;
-       int info_len = 0;
-       zval *info;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &info_name, &info_len), invalid_arg, return);
-
-       info = zend_read_property(php_http_client_response_class_entry, getThis(), ZEND_STRL("transferInfo"), 0 TSRMLS_CC);
-
-       /* request completed? */
-       if (Z_TYPE_P(info) != IS_OBJECT) {
-               php_http_throw(bad_method_call, "Incomplete state", NULL);
-               return;
-       }
-
-       if (info_len && info_name) {
-               info = zend_read_property(NULL, info, php_http_pretty_key(info_name, info_len, 0, 0), info_len, 0 TSRMLS_CC);
-
-               if (!info) {
-                       php_http_throw(unexpected_val, "Could not find transfer info with name '%s'", info_name);
-                       return;
-               }
-       }
-
-       RETURN_ZVAL(info, 1, 0);
-}
-
-static zend_function_entry php_http_client_response_methods[] = {
-       PHP_ME(HttpClientResponse, getCookies,      ai_HttpClientResponse_getCookies,      ZEND_ACC_PUBLIC)
-       PHP_ME(HttpClientResponse, getTransferInfo, ai_HttpClientResponse_getTransferInfo, ZEND_ACC_PUBLIC)
-       EMPTY_FUNCTION_ENTRY
-};
-
-zend_class_entry *php_http_client_response_class_entry;
-
-PHP_MINIT_FUNCTION(http_client_response)
-{
-       zend_class_entry ce = {0};
-
-       INIT_NS_CLASS_ENTRY(ce, "http\\Client", "Response", php_http_client_response_methods);
-       php_http_client_response_class_entry = zend_register_internal_class_ex(&ce, php_http_message_class_entry, NULL TSRMLS_CC);
-       zend_declare_property_null(php_http_client_response_class_entry, ZEND_STRL("transferInfo"), ZEND_ACC_PROTECTED TSRMLS_CC);
-
-       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/php_http_client_response.h b/php_http_client_response.h
deleted file mode 100644 (file)
index 15d004e..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_CLIENT_RESPONSE_H
-#define PHP_HTTP_CLIENT_RESPONSE_H
-
-PHP_HTTP_API zend_class_entry *php_http_client_response_class_entry;
-PHP_MINIT_FUNCTION(http_client_response);
-
-#endif /* PHP_HTTP_CLIENT_RESPONSE_H */
-
-
-/*
- * 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/php_http_cookie.c b/php_http_cookie.c
deleted file mode 100644 (file)
index 354dfa6..0000000
+++ /dev/null
@@ -1,1038 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-php_http_cookie_list_t *php_http_cookie_list_init(php_http_cookie_list_t *list TSRMLS_DC)
-{
-       if (!list) {
-               list = emalloc(sizeof(*list));
-       }
-       
-       zend_hash_init(&list->cookies, 0, NULL, ZVAL_PTR_DTOR, 0);
-       zend_hash_init(&list->extras, 0, NULL, ZVAL_PTR_DTOR, 0);
-       
-       list->path = NULL;
-       list->domain = NULL;
-       list->expires = -1;
-       list->max_age = -1;
-       list->flags = 0;
-       
-       TSRMLS_SET_CTX(list->ts);
-
-       return list;
-}
-
-php_http_cookie_list_t *php_http_cookie_list_copy(php_http_cookie_list_t *from, php_http_cookie_list_t *to)
-{
-       TSRMLS_FETCH_FROM_CTX(from->ts);
-
-       to = php_http_cookie_list_init(to TSRMLS_CC);
-
-       array_copy(&from->cookies, &to->cookies);
-       array_copy(&from->extras, &to->extras);
-
-       PTR_SET(to->path, from->path ? estrdup(from->path) : NULL);
-       PTR_SET(to->domain, from->domain ? estrdup(from->domain) : NULL);
-       to->expires = from->expires;
-       to->max_age = from->max_age;
-       to->flags = from->flags;
-
-       return to;
-}
-
-void php_http_cookie_list_dtor(php_http_cookie_list_t *list)
-{
-       if (list) {
-               zend_hash_destroy(&list->cookies);
-               zend_hash_destroy(&list->extras);
-       
-               PTR_SET(list->path, NULL);
-               PTR_SET(list->domain, NULL);
-       }
-}
-
-
-
-void php_http_cookie_list_free(php_http_cookie_list_t **list)
-{
-       if (*list) {
-               php_http_cookie_list_dtor(*list);
-               efree(*list);
-               *list = NULL;
-       }
-}
-
-const char *php_http_cookie_list_get_cookie(php_http_cookie_list_t *list, const char *name, size_t name_len, zval **zcookie)
-{
-       zval **cookie;
-       if ((SUCCESS != zend_symtable_find(&list->cookies, name, name_len + 1, (void *) &cookie)) || (Z_TYPE_PP(cookie) != IS_STRING)) {
-               return NULL;
-       }
-       if (zcookie) {
-               *zcookie = *cookie;
-       }
-       return Z_STRVAL_PP(cookie);
-}
-
-const char *php_http_cookie_list_get_extra(php_http_cookie_list_t *list, const char *name, size_t name_len, zval **zextra)
-{
-       zval **extra;
-
-       if ((SUCCESS != zend_symtable_find(&list->extras, name, name_len + 1, (void *) &extra)) || (Z_TYPE_PP(extra) != IS_STRING)) {
-               return NULL;
-       }
-       if (zextra) {
-               *zextra = *extra;
-       }
-       return Z_STRVAL_PP(extra);
-}
-
-void php_http_cookie_list_add_cookie(php_http_cookie_list_t *list, const char *name, size_t name_len, const char *value, size_t value_len)
-{
-       zval *cookie_value;
-
-       MAKE_STD_ZVAL(cookie_value);
-       ZVAL_STRINGL(cookie_value, estrndup(value, value_len), value_len, 0);
-       zend_symtable_update(&list->cookies, name, name_len + 1, (void *) &cookie_value, sizeof(zval *), NULL);
-}
-
-void php_http_cookie_list_add_extra(php_http_cookie_list_t *list, const char *name, size_t name_len, const char *value, size_t value_len)
-{
-       zval *cookie_value;
-
-       MAKE_STD_ZVAL(cookie_value);
-       ZVAL_STRINGL(cookie_value, estrndup(value, value_len), value_len, 0);
-       zend_symtable_update(&list->extras, name, name_len + 1, (void *) &cookie_value, sizeof(zval *), NULL);
-}
-
-#define _KEY_IS(s) (key->len == sizeof(s) && !strncasecmp(key->str, (s), key->len))
-static void add_entry(php_http_cookie_list_t *list, char **allowed_extras, long flags, php_http_array_hashkey_t *key, zval *val)
-{
-       zval *arg = php_http_zsep(1, IS_STRING, val);
-
-       if (!(flags & PHP_HTTP_COOKIE_PARSE_RAW)) {
-               Z_STRLEN_P(arg) = php_raw_url_decode(Z_STRVAL_P(arg), Z_STRLEN_P(arg));
-       }
-
-       if _KEY_IS("path") {
-               PTR_SET(list->path, estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(arg)));
-       } else if _KEY_IS("domain") {
-               PTR_SET(list->domain, estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(arg)));
-       } else if _KEY_IS("expires") {
-               char *date = estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(arg));
-               list->expires = php_parse_date(date, NULL);
-               efree(date);
-       } else if _KEY_IS("max-age") {
-               list->max_age = strtol(Z_STRVAL_P(arg), NULL, 10);
-       } else if _KEY_IS("secure") {
-               list->flags |= PHP_HTTP_COOKIE_SECURE;
-       } else if _KEY_IS("httpOnly") {
-               list->flags |= PHP_HTTP_COOKIE_HTTPONLY;
-       } else {
-               /* check for extra */
-               if (allowed_extras) {
-                       char **ae = allowed_extras;
-
-                       php_http_array_hashkey_stringify(key);
-                       for (; *ae; ++ae) {
-                               if (!strncasecmp(key->str, *ae, key->len)) {
-                                       if (key->type == HASH_KEY_IS_LONG) {
-                                               zend_hash_index_update(&list->extras, key->num, (void *) &arg, sizeof(zval *), NULL);
-                                       } else {
-                                               zend_hash_update(&list->extras, key->str, key->len, (void *) &arg, sizeof(zval *), NULL);
-                                       }
-                                       php_http_array_hashkey_stringfree(key);
-                                       return;
-                               }
-                       }
-                       php_http_array_hashkey_stringfree(key);
-               }
-
-               /* cookie */
-               if (key->type == HASH_KEY_IS_LONG) {
-                       zend_hash_index_update(&list->cookies, key->num, (void *) &arg, sizeof(zval *), NULL);
-               } else {
-                       zend_hash_update(&list->cookies, key->str, key->len, (void *) &arg, sizeof(zval *), NULL);
-               }
-               return;
-       }
-       zval_ptr_dtor(&arg);
-}
-
-php_http_cookie_list_t *php_http_cookie_list_parse(php_http_cookie_list_t *list, const char *str, size_t len, long flags, char **allowed_extras TSRMLS_DC)
-{
-       php_http_params_opts_t opts;
-       HashTable params;
-       HashPosition pos1, pos2;
-       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-       zval **param, **val, **args, **arg;
-
-       php_http_params_opts_default_get(&opts);
-       opts.input.str = estrndup(str, len);
-       opts.input.len = len;
-       opts.param = NULL;
-       zend_hash_init(&params, 10, NULL, ZVAL_PTR_DTOR, 0);
-       php_http_params_parse(&params, &opts TSRMLS_CC);
-       efree(opts.input.str);
-
-       list = php_http_cookie_list_init(list TSRMLS_CC);
-       FOREACH_HASH_KEYVAL(pos1, &params, key, param) {
-               if (Z_TYPE_PP(param) == IS_ARRAY) {
-                       if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(param), ZEND_STRS("value"), (void *) &val)) {
-                               add_entry(list, NULL, flags, &key, *val);
-                       }
-                       if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(param), ZEND_STRS("arguments"), (void *) &args) && Z_TYPE_PP(args) == IS_ARRAY) {
-                               FOREACH_KEYVAL(pos2, *args, key, arg) {
-                                       add_entry(list, allowed_extras, flags, &key, *arg);
-                               }
-                       }
-               }
-       }
-       zend_hash_destroy(&params);
-
-       return list;
-}
-
-void php_http_cookie_list_to_struct(php_http_cookie_list_t *list, zval *strct)
-{
-       zval array, *cookies, *extras;
-       TSRMLS_FETCH_FROM_CTX(list->ts);
-       
-       INIT_PZVAL_ARRAY(&array, HASH_OF(strct));
-       
-       MAKE_STD_ZVAL(cookies);
-       array_init(cookies);
-       zend_hash_copy(Z_ARRVAL_P(cookies), &list->cookies, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
-       add_assoc_zval(&array, "cookies", cookies);
-       
-       MAKE_STD_ZVAL(extras);
-       array_init(extras);
-       zend_hash_copy(Z_ARRVAL_P(extras), &list->extras, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
-       add_assoc_zval(&array, "extras", extras);
-       
-       add_assoc_long(&array, "flags", list->flags);
-       add_assoc_long(&array, "expires", (long) list->expires);
-       add_assoc_long(&array, "max-age", (long) list->max_age);
-       add_assoc_string(&array, "path", STR_PTR(list->path), 1);
-       add_assoc_string(&array, "domain", STR_PTR(list->domain), 1);
-}
-
-php_http_cookie_list_t *php_http_cookie_list_from_struct(php_http_cookie_list_t *list, zval *strct TSRMLS_DC)
-{
-       zval **tmp, *cpy;
-       HashTable *ht = HASH_OF(strct);
-       
-       list = php_http_cookie_list_init(list TSRMLS_CC);
-       
-       if (SUCCESS == zend_hash_find(ht, "cookies", sizeof("cookies"), (void *) &tmp) && Z_TYPE_PP(tmp) == IS_ARRAY) {
-               zend_hash_copy(&list->cookies, Z_ARRVAL_PP(tmp), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
-       }
-       if (SUCCESS == zend_hash_find(ht, "extras", sizeof("extras"), (void *) &tmp) && Z_TYPE_PP(tmp) == IS_ARRAY) {
-               zend_hash_copy(&list->extras, Z_ARRVAL_PP(tmp), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
-       }
-       if (SUCCESS == zend_hash_find(ht, "flags", sizeof("flags"), (void *) &tmp)) {
-               cpy = php_http_ztyp(IS_LONG, *tmp);
-               list->flags = Z_LVAL_P(cpy);
-               zval_ptr_dtor(&cpy);
-       }
-       if (SUCCESS == zend_hash_find(ht, "expires", sizeof("expires"), (void *) &tmp)) {
-               if (Z_TYPE_PP(tmp) == IS_LONG) {
-                       list->expires = Z_LVAL_PP(tmp);
-               } else {
-                       long lval;
-
-                       cpy = php_http_ztyp(IS_STRING, *tmp);
-                       if (IS_LONG == is_numeric_string(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy), &lval, NULL, 0)) {
-                               list->expires = lval;
-                       } else {
-                               list->expires = php_parse_date(Z_STRVAL_P(cpy), NULL);
-                       }
-
-                       zval_ptr_dtor(&cpy);
-               }
-       }
-       if (SUCCESS == zend_hash_find(ht, "max-age", sizeof("max-age"), (void *) &tmp)) {
-               if (Z_TYPE_PP(tmp) == IS_LONG) {
-                       list->max_age = Z_LVAL_PP(tmp);
-               } else {
-                       long lval;
-
-                       cpy = php_http_ztyp(IS_STRING, *tmp);
-                       if (IS_LONG == is_numeric_string(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy), &lval, NULL, 0)) {
-                               list->max_age = lval;
-                       }
-
-                       zval_ptr_dtor(&cpy);
-               }
-       }
-       if (SUCCESS == zend_hash_find(ht, "path", sizeof("path"), (void *) &tmp) && Z_TYPE_PP(tmp) == IS_STRING) {
-               list->path = estrndup(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
-       }
-       if (SUCCESS == zend_hash_find(ht, "domain", sizeof("domain"), (void *) &tmp) && Z_TYPE_PP(tmp) == IS_STRING) {
-               list->domain = estrndup(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
-       }
-       
-       return list;
-}
-
-static inline void append_encoded(php_http_buffer_t *buf, const char *key, size_t key_len, const char *val, size_t val_len)
-{
-       char *enc_str[2];
-       int enc_len[2];
-       
-       enc_str[0] = php_raw_url_encode(key, key_len, &enc_len[0]);
-       enc_str[1] = php_raw_url_encode(val, val_len, &enc_len[1]);
-       
-       php_http_buffer_append(buf, enc_str[0], enc_len[0]);
-       php_http_buffer_appends(buf, "=");
-       php_http_buffer_append(buf, enc_str[1], enc_len[1]);
-       php_http_buffer_appends(buf, "; ");
-       
-       efree(enc_str[0]);
-       efree(enc_str[1]);
-}
-
-void php_http_cookie_list_to_string(php_http_cookie_list_t *list, char **str, size_t *len)
-{
-       php_http_buffer_t buf;
-       zval **val;
-       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-       HashPosition pos;
-       TSRMLS_FETCH_FROM_CTX(list->ts);
-       
-       php_http_buffer_init(&buf);
-       
-       FOREACH_HASH_KEYVAL(pos, &list->cookies, key, val) {
-               zval *tmp = php_http_ztyp(IS_STRING, *val);
-
-               php_http_array_hashkey_stringify(&key);
-               append_encoded(&buf, key.str, key.len-1, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
-               php_http_array_hashkey_stringfree(&key);
-
-               zval_ptr_dtor(&tmp);
-       }
-       
-       if (list->domain && *list->domain) {
-               php_http_buffer_appendf(&buf, "domain=%s; ", list->domain);
-       }
-       if (list->path && *list->path) {
-               php_http_buffer_appendf(&buf, "path=%s; ", list->path);
-       }
-       if (list->expires >= 0) {
-               char *date = php_format_date(ZEND_STRL(PHP_HTTP_DATE_FORMAT), list->expires, 0 TSRMLS_CC);
-               php_http_buffer_appendf(&buf, "expires=%s; ", date);
-               efree(date);
-       }
-       if (list->max_age >= 0) {
-               php_http_buffer_appendf(&buf, "max-age=%ld; ", list->max_age);
-       }
-       
-       FOREACH_HASH_KEYVAL(pos, &list->extras, key, val) {
-               zval *tmp = php_http_ztyp(IS_STRING, *val);
-
-               php_http_array_hashkey_stringify(&key);
-               append_encoded(&buf, key.str, key.len-1, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
-               php_http_array_hashkey_stringfree(&key);
-
-               zval_ptr_dtor(&tmp);
-       }
-       
-       if (list->flags & PHP_HTTP_COOKIE_SECURE) {
-               php_http_buffer_appends(&buf, "secure; ");
-       }
-       if (list->flags & PHP_HTTP_COOKIE_HTTPONLY) {
-               php_http_buffer_appends(&buf, "httpOnly; ");
-       }
-       
-       php_http_buffer_fix(&buf);
-       *str = buf.data;
-       *len = buf.used;
-}
-
-
-
-static zend_object_handlers php_http_cookie_object_handlers;
-
-zend_object_value php_http_cookie_object_new(zend_class_entry *ce TSRMLS_DC)
-{
-       return php_http_cookie_object_new_ex(ce, NULL, NULL TSRMLS_CC);
-}
-
-zend_object_value php_http_cookie_object_new_ex(zend_class_entry *ce, php_http_cookie_list_t *list, php_http_cookie_object_t **ptr TSRMLS_DC)
-{
-       php_http_cookie_object_t *o;
-
-       o = ecalloc(sizeof(*o), 1);
-       zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
-       object_properties_init((zend_object *) o, ce);
-
-       if (list) {
-               o->list = list;
-       }
-
-       if (ptr) {
-               *ptr = o;
-       }
-
-       o->zv.handle = zend_objects_store_put(o, NULL, php_http_cookie_object_free, NULL TSRMLS_CC);
-       o->zv.handlers = &php_http_cookie_object_handlers;
-
-       return o->zv;
-}
-
-#define PHP_HTTP_COOKIE_OBJECT_INIT(obj) \
-       do { \
-               if (!obj->list) { \
-                       obj->list = php_http_cookie_list_init(NULL TSRMLS_CC); \
-               } \
-       } while(0)
-
-zend_object_value php_http_cookie_object_clone(zval *this_ptr TSRMLS_DC)
-{
-       php_http_cookie_object_t *new_obj, *old_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-       zend_object_value ov;
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(old_obj);
-
-       ov = php_http_cookie_object_new_ex(old_obj->zo.ce, php_http_cookie_list_copy(old_obj->list, NULL), &new_obj TSRMLS_CC);
-       zend_objects_clone_members((zend_object *) new_obj, ov, (zend_object *) old_obj, Z_OBJ_HANDLE_P(getThis()) TSRMLS_CC);
-
-       return ov;
-}
-
-void php_http_cookie_object_free(void *object TSRMLS_DC)
-{
-       php_http_cookie_object_t *obj = object;
-
-       php_http_cookie_list_free(&obj->list);
-       zend_object_std_dtor((zend_object *) obj TSRMLS_CC);
-       efree(obj);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie___construct, 0, 0, 0)
-       ZEND_ARG_INFO(0, cookie_string)
-       ZEND_ARG_INFO(0, parser_flags)
-       ZEND_ARG_INFO(0, allowed_extras)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpCookie, __construct)
-{
-       php_http_cookie_object_t *obj;
-       zval *zcookie = NULL;
-       long flags = 0;
-       char **ae = NULL;
-       HashTable *allowed_extras = NULL;
-       zend_error_handling zeh;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!lH", &zcookie, &flags, &allowed_extras), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh TSRMLS_CC);
-       if (zcookie) {
-
-               if (allowed_extras && zend_hash_num_elements(allowed_extras)) {
-                       char **ae_ptr = safe_emalloc(zend_hash_num_elements(allowed_extras) + 1, sizeof(char *), 0);
-                       HashPosition pos;
-                       zval **val;
-
-                       ae = ae_ptr;
-                       FOREACH_HASH_VAL(pos, allowed_extras, val) {
-                               zval *cpy = php_http_ztyp(IS_STRING, *val);
-
-                               *ae_ptr++ = estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy));
-                               zval_ptr_dtor(&cpy);
-                       }
-                       *ae_ptr = NULL;
-               }
-
-               switch (Z_TYPE_P(zcookie)) {
-                       case IS_OBJECT:
-                               if (instanceof_function(Z_OBJCE_P(zcookie), php_http_cookie_class_entry TSRMLS_CC)) {
-                                       php_http_cookie_object_t *zco = zend_object_store_get_object(zcookie TSRMLS_CC);
-
-                                       if (zco->list) {
-                                               obj->list = php_http_cookie_list_copy(zco->list, NULL);
-                                       }
-                                       break;
-                               }
-                               /* no break */
-                       case IS_ARRAY:
-                               obj->list = php_http_cookie_list_from_struct(obj->list, zcookie TSRMLS_CC);
-                               break;
-                       default: {
-                               zval *cpy = php_http_ztyp(IS_STRING, zcookie);
-
-                               obj->list = php_http_cookie_list_parse(obj->list, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy), flags, ae TSRMLS_CC);
-                               zval_ptr_dtor(&cpy);
-                               break;
-                       }
-               }
-
-               if (ae) {
-                       char **ae_ptr;
-
-                       for (ae_ptr = ae; *ae_ptr; ++ae_ptr) {
-                               efree(*ae_ptr);
-                       }
-                       efree(ae);
-               }
-       }
-       zend_restore_error_handling(&zeh TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getCookies, 0, 0, 0)
-ZEND_END_ARG_INFO();;
-static PHP_METHOD(HttpCookie, getCookies)
-{
-       php_http_cookie_object_t *obj;
-
-       if (SUCCESS != zend_parse_parameters_none()) {
-               return;
-       }
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       array_init(return_value);
-       array_copy(&obj->list->cookies, Z_ARRVAL_P(return_value));
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setCookies, 0, 0, 0)
-       ZEND_ARG_INFO(0, cookies)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpCookie, setCookies)
-{
-       HashTable *cookies = NULL;
-       php_http_cookie_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|H", &cookies), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       zend_hash_clean(&obj->list->cookies);
-       if (cookies) {
-               array_copy_strings(cookies, &obj->list->cookies);
-       }
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_addCookies, 0, 0, 1)
-       ZEND_ARG_INFO(0, cookies)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpCookie, addCookies)
-{
-       HashTable *cookies = NULL;
-       php_http_cookie_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &cookies), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       array_join(cookies, &obj->list->cookies, 1, ARRAY_JOIN_STRONLY|ARRAY_JOIN_STRINGIFY);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getExtras, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpCookie, getExtras)
-{
-       php_http_cookie_object_t *obj;
-
-       if (SUCCESS != zend_parse_parameters_none()) {
-               return;
-       }
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       array_init(return_value);
-       array_copy(&obj->list->extras, Z_ARRVAL_P(return_value));
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setExtras, 0, 0, 0)
-       ZEND_ARG_INFO(0, extras)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpCookie, setExtras)
-{
-       HashTable *extras = NULL;
-       php_http_cookie_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|H", &extras), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       zend_hash_clean(&obj->list->extras);
-       if (extras) {
-               array_copy_strings(extras, &obj->list->extras);
-       }
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_addExtras, 0, 0, 1)
-       ZEND_ARG_INFO(0, extras)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpCookie, addExtras)
-{
-       HashTable *extras = NULL;
-       php_http_cookie_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &extras), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       array_join(extras, &obj->list->extras, 1, ARRAY_JOIN_STRONLY|ARRAY_JOIN_STRINGIFY);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getCookie, 0, 0, 1)
-       ZEND_ARG_INFO(0, name)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpCookie, getCookie)
-{
-       char *name_str;
-       int name_len;
-       zval *zvalue;
-       php_http_cookie_object_t *obj;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) {
-               return;
-       }
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       if (php_http_cookie_list_get_cookie(obj->list, name_str, name_len, &zvalue)) {
-               RETURN_ZVAL(zvalue, 1, 0);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setCookie, 0, 0, 1)
-       ZEND_ARG_INFO(0, cookie_name)
-       ZEND_ARG_INFO(0, cookie_value)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpCookie, setCookie)
-{
-       char *name_str, *value_str = NULL;
-       int name_len, value_len = 0;
-       php_http_cookie_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s!", &name_str, &name_len, &value_str, &value_len), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       if (!value_str) {
-               php_http_cookie_list_del_cookie(obj->list, name_str, name_len);
-       } else {
-               php_http_cookie_list_add_cookie(obj->list, name_str, name_len, value_str, value_len);
-       }
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_addCookie, 0, 0, 2)
-       ZEND_ARG_INFO(0, cookie_name)
-       ZEND_ARG_INFO(0, cookie_value)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpCookie, addCookie)
-{
-       char *name_str, *value_str;
-       int name_len, value_len;
-       php_http_cookie_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &name_str, &name_len, &value_str, &value_len), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       php_http_cookie_list_add_cookie(obj->list, name_str, name_len, value_str, value_len);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getExtra, 0, 0, 1)
-       ZEND_ARG_INFO(0, name)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpCookie, getExtra)
-{
-       char *name_str;
-       int name_len;
-       zval *zvalue;
-       php_http_cookie_object_t *obj;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) {
-               return;
-       }
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       if (php_http_cookie_list_get_extra(obj->list, name_str, name_len, &zvalue)) {
-               RETURN_ZVAL(zvalue, 1, 0);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setExtra, 0, 0, 1)
-       ZEND_ARG_INFO(0, extra_name)
-       ZEND_ARG_INFO(0, extra_value)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpCookie, setExtra)
-{
-       char *name_str, *value_str = NULL;
-       int name_len, value_len = 0;
-       php_http_cookie_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s!", &name_str, &name_len, &value_str, &value_len), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       if (!value_str) {
-               php_http_cookie_list_del_extra(obj->list, name_str, name_len);
-       } else {
-               php_http_cookie_list_add_extra(obj->list, name_str, name_len, value_str, value_len);
-       }
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_addExtra, 0, 0, 2)
-       ZEND_ARG_INFO(0, extra_name)
-       ZEND_ARG_INFO(0, extra_value)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpCookie, addExtra)
-{
-       char *name_str, *value_str;
-       int name_len, value_len;
-       php_http_cookie_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &name_str, &name_len, &value_str, &value_len), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       php_http_cookie_list_add_extra(obj->list, name_str, name_len, value_str, value_len);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getDomain, 0, 0, 0)
-ZEND_END_ARG_INFO();;
-static PHP_METHOD(HttpCookie, getDomain)
-{
-       php_http_cookie_object_t *obj;
-
-       if (SUCCESS != zend_parse_parameters_none()) {
-               return;
-       }
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       if (obj->list->domain) {
-               RETURN_STRING(obj->list->domain, 1);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setDomain, 0, 0, 0)
-       ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpCookie, setDomain)
-{
-       char *domain_str = NULL;
-       int domain_len = 0;
-       php_http_cookie_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &domain_str, &domain_len), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       PTR_SET(obj->list->domain, domain_str ? estrndup(domain_str, domain_len) : NULL);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getPath, 0, 0, 0)
-ZEND_END_ARG_INFO();;
-static PHP_METHOD(HttpCookie, getPath)
-{
-       php_http_cookie_object_t *obj;
-
-       if (SUCCESS != zend_parse_parameters_none()) {
-               return;
-       }
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       if (obj->list->path) {
-               RETURN_STRING(obj->list->path, 1);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setPath, 0, 0, 0)
-       ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpCookie, setPath)
-{
-       char *path_str = NULL;
-       int path_len = 0;
-       php_http_cookie_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &path_str, &path_len), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       PTR_SET(obj->list->path, path_str ? estrndup(path_str, path_len) : NULL);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getExpires, 0, 0, 0)
-ZEND_END_ARG_INFO();;
-static PHP_METHOD(HttpCookie, getExpires)
-{
-       php_http_cookie_object_t *obj;
-
-       if (SUCCESS != zend_parse_parameters_none()) {
-               return;
-       }
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       RETURN_LONG(obj->list->expires);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setExpires, 0, 0, 0)
-       ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpCookie, setExpires)
-{
-       long ts = -1;
-       php_http_cookie_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &ts), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       obj->list->expires = ts;
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getMaxAge, 0, 0, 0)
-ZEND_END_ARG_INFO();;
-static PHP_METHOD(HttpCookie, getMaxAge)
-{
-       php_http_cookie_object_t *obj;
-
-       if (SUCCESS != zend_parse_parameters_none()) {
-               return;
-       }
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       RETURN_LONG(obj->list->max_age);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setMaxAge, 0, 0, 0)
-       ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpCookie, setMaxAge)
-{
-       long ts = -1;
-       php_http_cookie_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &ts), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       obj->list->max_age = ts;
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getFlags, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpCookie, getFlags)
-{
-       php_http_cookie_object_t *obj;
-
-       if (SUCCESS != zend_parse_parameters_none()) {
-               return;
-       }
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       RETURN_LONG(obj->list->flags);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setFlags, 0, 0, 0)
-       ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpCookie, setFlags)
-{
-       long flags = 0;
-       php_http_cookie_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flags), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       obj->list->flags = flags;
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_toString, 0, 0, 0)
-ZEND_END_ARG_INFO();;
-static PHP_METHOD(HttpCookie, toString)
-{
-       php_http_cookie_object_t *obj;
-       char *str;
-       size_t len;
-
-       if (SUCCESS != zend_parse_parameters_none()) {
-               RETURN_EMPTY_STRING();
-       }
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       php_http_cookie_list_to_string(obj->list, &str, &len);
-
-       RETURN_STRINGL(str, len, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_toArray, 0, 0, 0)
-ZEND_END_ARG_INFO();;
-static PHP_METHOD(HttpCookie, toArray)
-{
-       php_http_cookie_object_t *obj;
-
-       if (SUCCESS != zend_parse_parameters_none()) {
-               return;
-       }
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
-
-       array_init(return_value);
-       php_http_cookie_list_to_struct(obj->list, return_value);
-}
-
-static zend_function_entry php_http_cookie_methods[] = {
-       PHP_ME(HttpCookie, __construct,   ai_HttpCookie___construct,  ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, getCookies,    ai_HttpCookie_getCookies,   ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, setCookies,    ai_HttpCookie_setCookies,   ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, addCookies,    ai_HttpCookie_addCookies,   ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, getCookie,     ai_HttpCookie_getCookie,    ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, setCookie,     ai_HttpCookie_setCookie,    ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, addCookie,     ai_HttpCookie_addCookie,    ZEND_ACC_PUBLIC)
-
-       PHP_ME(HttpCookie, getExtras,     ai_HttpCookie_getExtras,    ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, setExtras,     ai_HttpCookie_setExtras,    ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, addExtras,     ai_HttpCookie_addExtras,    ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, getExtra,      ai_HttpCookie_getExtra,     ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, setExtra,      ai_HttpCookie_setExtra,     ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, addExtra,      ai_HttpCookie_addExtra,     ZEND_ACC_PUBLIC)
-
-       PHP_ME(HttpCookie, getDomain,     ai_HttpCookie_getDomain,    ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, setDomain,     ai_HttpCookie_setDomain,    ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, getPath,       ai_HttpCookie_getPath,      ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, setPath,       ai_HttpCookie_setPath,      ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, getExpires,    ai_HttpCookie_getExpires,   ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, setExpires,    ai_HttpCookie_setExpires,   ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, getMaxAge,     ai_HttpCookie_getMaxAge,    ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, setMaxAge,     ai_HttpCookie_setMaxAge,    ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, getFlags,      ai_HttpCookie_getFlags,     ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, setFlags,      ai_HttpCookie_setFlags,     ZEND_ACC_PUBLIC)
-
-       PHP_ME(HttpCookie, toArray,       ai_HttpCookie_toArray,      ZEND_ACC_PUBLIC)
-       PHP_ME(HttpCookie, toString,      ai_HttpCookie_toString,     ZEND_ACC_PUBLIC)
-       ZEND_MALIAS(HttpCookie, __toString, toString, ai_HttpCookie_toString, ZEND_ACC_PUBLIC)
-
-       EMPTY_FUNCTION_ENTRY
-};
-
-zend_class_entry *php_http_cookie_class_entry;
-
-PHP_MINIT_FUNCTION(http_cookie)
-{
-       zend_class_entry ce = {0};
-
-       INIT_NS_CLASS_ENTRY(ce, "http", "Cookie", php_http_cookie_methods);
-       php_http_cookie_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
-       php_http_cookie_class_entry->create_object = php_http_cookie_object_new;
-       memcpy(&php_http_cookie_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
-       php_http_cookie_object_handlers.clone_obj = php_http_cookie_object_clone;
-
-       zend_declare_class_constant_long(php_http_cookie_class_entry, ZEND_STRL("PARSE_RAW"), PHP_HTTP_COOKIE_PARSE_RAW TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_cookie_class_entry, ZEND_STRL("SECURE"), PHP_HTTP_COOKIE_SECURE TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_cookie_class_entry, ZEND_STRL("HTTPONLY"), PHP_HTTP_COOKIE_HTTPONLY TSRMLS_CC);
-
-       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/php_http_cookie.h b/php_http_cookie.h
deleted file mode 100644 (file)
index 7cf00fe..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_COOKIE_H
-#define PHP_HTTP_COOKIE_H
-
-#define PHP_HTTP_COOKIE_SECURE         0x10L
-#define PHP_HTTP_COOKIE_HTTPONLY       0x20L
-
-#define PHP_HTTP_COOKIE_PARSE_RAW      0x01L
-
-/*
-       generally a netscape cookie compliant struct, recognizing httpOnly attribute, too;
-       cookie params like those from rfc2109 and rfc2965 are just put into extras, if
-       one specifies them in allowed extras, else they're treated like cookies themself
-*/
-typedef struct php_http_cookie_list {
-       HashTable cookies;
-       HashTable extras;
-       long flags;
-       char *path;
-       char *domain;
-       time_t expires;
-       time_t max_age;
-
-#ifdef ZTS
-       void ***ts;
-#endif
-} php_http_cookie_list_t;
-
-PHP_HTTP_API php_http_cookie_list_t *php_http_cookie_list_init(php_http_cookie_list_t *list TSRMLS_DC);
-PHP_HTTP_API php_http_cookie_list_t *php_http_cookie_list_parse(php_http_cookie_list_t *list, const char *str, size_t len, long flags, char **allowed_extras TSRMLS_DC);
-PHP_HTTP_API php_http_cookie_list_t *php_http_cookie_list_copy(php_http_cookie_list_t *from, php_http_cookie_list_t *to);
-PHP_HTTP_API void php_http_cookie_list_dtor(php_http_cookie_list_t *list);
-PHP_HTTP_API void php_http_cookie_list_free(php_http_cookie_list_t **list);
-
-#define php_http_cookie_list_has_cookie(list, name, name_len) zend_symtable_exists(&(list)->cookies, (name), (name_len)+1)
-#define php_http_cookie_list_del_cookie(list, name, name_len) zend_symtable_del(&(list)->cookies, (name), (name_len)+1)
-PHP_HTTP_API void php_http_cookie_list_add_cookie(php_http_cookie_list_t *list, const char *name, size_t name_len, const char *value, size_t value_len);
-PHP_HTTP_API const char *php_http_cookie_list_get_cookie(php_http_cookie_list_t *list, const char *name, size_t name_len, zval **cookie);
-
-#define php_http_cookie_list_has_extra(list, name, name_len) zend_symtable_exists(&(list)->extras, (name), (name_len)+1)
-#define php_http_cookie_list_del_extra(list, name, name_len) zend_symtable_del(&(list)->extras, (name), (name_len)+1)
-PHP_HTTP_API void php_http_cookie_list_add_extra(php_http_cookie_list_t *list, const char *name, size_t name_len, const char *value, size_t value_len);
-PHP_HTTP_API const char *php_http_cookie_list_get_extra(php_http_cookie_list_t *list, const char *name, size_t name_len, zval **extra);
-
-PHP_HTTP_API void php_http_cookie_list_to_string(php_http_cookie_list_t *list, char **str, size_t *len);
-PHP_HTTP_API php_http_cookie_list_t *php_http_cookie_list_from_struct(php_http_cookie_list_t *list, zval *strct TSRMLS_DC);
-PHP_HTTP_API void php_http_cookie_list_to_struct(php_http_cookie_list_t *list, zval *strct);
-
-PHP_HTTP_API zend_class_entry *php_http_cookie_class_entry;
-
-typedef struct php_http_cookie_object {
-       zend_object zo;
-       zend_object_value zv;
-       php_http_cookie_list_t *list;
-} php_http_cookie_object_t;
-
-zend_object_value php_http_cookie_object_new(zend_class_entry *ce TSRMLS_DC);
-zend_object_value php_http_cookie_object_new_ex(zend_class_entry *ce, php_http_cookie_list_t *list, php_http_cookie_object_t **obj TSRMLS_DC);
-zend_object_value php_http_cookie_object_clone(zval *this_ptr TSRMLS_DC);
-void php_http_cookie_object_free(void *object TSRMLS_DC);
-
-PHP_MINIT_FUNCTION(http_cookie);
-
-#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/php_http_curl.c b/php_http_curl.c
deleted file mode 100644 (file)
index a995094..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_CURL
-
-#if defined(ZTS) && defined(PHP_HTTP_HAVE_SSL)
-#      ifdef PHP_WIN32
-#              define PHP_HTTP_NEED_OPENSSL_TSL
-#              include <openssl/crypto.h>
-#      else /* !PHP_WIN32 */
-#              if defined(PHP_HTTP_HAVE_OPENSSL)
-#                      define PHP_HTTP_NEED_OPENSSL_TSL
-#                      include <openssl/crypto.h>
-#              elif defined(PHP_HTTP_HAVE_GNUTLS)
-#                      define PHP_HTTP_NEED_GNUTLS_TSL
-#                      include <gcrypt.h>
-#              endif /* PHP_HTTP_HAVE_OPENSSL || PHP_HTTP_HAVE_GNUTLS */
-#      endif /* PHP_WIN32 */
-#endif /* ZTS && PHP_HTTP_HAVE_SSL */
-
-
-#ifdef PHP_HTTP_NEED_OPENSSL_TSL
-static MUTEX_T *php_http_openssl_tsl = NULL;
-
-static void php_http_openssl_thread_lock(int mode, int n, const char * file, int line)
-{
-       if (mode & CRYPTO_LOCK) {
-               tsrm_mutex_lock(php_http_openssl_tsl[n]);
-       } else {
-               tsrm_mutex_unlock(php_http_openssl_tsl[n]);
-       }
-}
-
-static ulong php_http_openssl_thread_id(void)
-{
-       return (ulong) tsrm_thread_id();
-}
-#endif
-#ifdef PHP_HTTP_NEED_GNUTLS_TSL
-static int php_http_gnutls_mutex_create(void **m)
-{
-       if (*((MUTEX_T *) m) = tsrm_mutex_alloc()) {
-               return SUCCESS;
-       } else {
-               return FAILURE;
-       }
-}
-
-static int php_http_gnutls_mutex_destroy(void **m)
-{
-       tsrm_mutex_free(*((MUTEX_T *) m));
-       return SUCCESS;
-}
-
-static int php_http_gnutls_mutex_lock(void **m)
-{
-       return tsrm_mutex_lock(*((MUTEX_T *) m));
-}
-
-static int php_http_gnutls_mutex_unlock(void **m)
-{
-       return tsrm_mutex_unlock(*((MUTEX_T *) m));
-}
-
-static struct gcry_thread_cbs php_http_gnutls_tsl = {
-       GCRY_THREAD_OPTION_USER,
-       NULL,
-       php_http_gnutls_mutex_create,
-       php_http_gnutls_mutex_destroy,
-       php_http_gnutls_mutex_lock,
-       php_http_gnutls_mutex_unlock
-};
-#endif
-
-
-PHP_MINIT_FUNCTION(http_curl)
-{
-#ifdef PHP_HTTP_NEED_OPENSSL_TSL
-       /* mod_ssl, libpq or ext/curl might already have set thread lock callbacks */
-       if (!CRYPTO_get_id_callback()) {
-               int i, c = CRYPTO_num_locks();
-
-               php_http_openssl_tsl = malloc(c * sizeof(MUTEX_T));
-
-               for (i = 0; i < c; ++i) {
-                       php_http_openssl_tsl[i] = tsrm_mutex_alloc();
-               }
-
-               CRYPTO_set_id_callback(php_http_openssl_thread_id);
-               CRYPTO_set_locking_callback(php_http_openssl_thread_lock);
-       }
-#endif
-#ifdef PHP_HTTP_NEED_GNUTLS_TSL
-       gcry_control(GCRYCTL_SET_THREAD_CBS, &php_http_gnutls_tsl);
-#endif
-
-       if (CURLE_OK != curl_global_init(CURL_GLOBAL_ALL)) {
-               return FAILURE;
-       }
-
-       return SUCCESS;
-}
-
-PHP_MSHUTDOWN_FUNCTION(http_curl)
-{
-       curl_global_cleanup();
-#ifdef PHP_HTTP_NEED_OPENSSL_TSL
-       if (php_http_openssl_tsl) {
-               int i, c = CRYPTO_num_locks();
-
-               CRYPTO_set_id_callback(NULL);
-               CRYPTO_set_locking_callback(NULL);
-
-               for (i = 0; i < c; ++i) {
-                       tsrm_mutex_free(php_http_openssl_tsl[i]);
-               }
-
-               free(php_http_openssl_tsl);
-               php_http_openssl_tsl = NULL;
-       }
-#endif
-       return SUCCESS;
-}
-
-#endif /* PHP_HTTP_HAVE_CURL */
-
-/*
- * 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/php_http_curl.h b/php_http_curl.h
deleted file mode 100644 (file)
index ab8b63c..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_CURL_H
-#define PHP_HTTP_CURL_H
-
-#if PHP_HTTP_HAVE_CURL
-
-#include <curl/curl.h>
-#define PHP_HTTP_CURL_VERSION(x, y, z) (LIBCURL_VERSION_NUM >= (((x)<<16) + ((y)<<8) + (z)))
-
-PHP_MINIT_FUNCTION(http_curl);
-PHP_MSHUTDOWN_FUNCTION(http_curl);
-
-#endif /* PHP_HTTP_HAVE_CURL */
-
-#endif /* PHP_HTTP_CURL_H */
-
-/*
- * 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/php_http_encoding.c b/php_http_encoding.c
deleted file mode 100644 (file)
index 286f2b5..0000000
+++ /dev/null
@@ -1,1227 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-#include <zlib.h>
-
-static inline int eol_match(char **line, int *eol_len)
-{
-       char *ptr = *line;
-       
-       while (' ' == *ptr) ++ptr;
-
-       if (ptr == php_http_locate_eol(*line, eol_len)) {
-               *line = ptr;
-               return 1;
-       } else {
-               return 0;
-       }
-}
-
-const char *php_http_encoding_dechunk(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC)
-{
-       int eol_len = 0;
-       char *n_ptr = NULL;
-       const char *e_ptr = encoded;
-       
-       *decoded_len = 0;
-       *decoded = ecalloc(1, encoded_len + 1);
-
-       while ((encoded + encoded_len - e_ptr) > 0) {
-               ulong chunk_len = 0, rest;
-
-               chunk_len = strtoul(e_ptr, &n_ptr, 16);
-
-               /* we could not read in chunk size */
-               if (n_ptr == e_ptr) {
-                       /*
-                        * if this is the first turn and there doesn't seem to be a chunk
-                        * size at the begining of the body, do not fail on apparently
-                        * not encoded data and return a copy
-                        */
-                       if (e_ptr == encoded) {
-                               php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Data does not seem to be chunked encoded");
-                               memcpy(*decoded, encoded, encoded_len);
-                               *decoded_len = encoded_len;
-                               return encoded + encoded_len;
-                       } else {
-                               efree(*decoded);
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected chunk size at pos %tu of %zu but got trash", n_ptr - encoded, encoded_len);
-                               return NULL;
-                       }
-               }
-               
-               /* reached the end */
-               if (!chunk_len) {
-                       /* move over '0' chunked encoding terminator and any new lines */
-                       do {
-                               switch (*e_ptr) {
-                                       case '0':
-                                       case '\r':
-                                       case '\n':
-                                               ++e_ptr;
-                                               continue;
-                               }
-                       } while (0);
-                       break;
-               }
-
-               /* there should be CRLF after the chunk size, but we'll ignore SP+ too */
-               if (*n_ptr && !eol_match(&n_ptr, &eol_len)) {
-                       if (eol_len == 2) {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected CRLF at pos %tu of %zu but got 0x%02X 0x%02X", n_ptr - encoded, encoded_len, *n_ptr, *(n_ptr + 1));
-                       } else {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected LF at pos %tu of %zu but got 0x%02X", n_ptr - encoded, encoded_len, *n_ptr);
-                       }
-               }
-               n_ptr += eol_len;
-               
-               /* chunk size pretends more data than we actually got, so it's probably a truncated message */
-               if (chunk_len > (rest = encoded + encoded_len - n_ptr)) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Truncated message: chunk size %lu exceeds remaining data size %lu at pos %tu of %zu", chunk_len, rest, n_ptr - encoded, encoded_len);
-                       chunk_len = rest;
-               }
-
-               /* copy the chunk */
-               memcpy(*decoded + *decoded_len, n_ptr, chunk_len);
-               *decoded_len += chunk_len;
-               
-               if (chunk_len == rest) {
-                       e_ptr = n_ptr + chunk_len;
-                       break;
-               } else {
-                       /* advance to next chunk */
-                       e_ptr = n_ptr + chunk_len + eol_len;
-               }
-       }
-
-       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 TSRMLS_DC)
-{
-       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 TSRMLS_CC, 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 TSRMLS_DC)
-{
-       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 TSRMLS_CC, 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 TSRMLS_DC)
-{
-       int freeme;
-
-       if ((freeme = !s)) {
-               s = pemalloc(sizeof(*s), (flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT));
-       }
-       memset(s, 0, sizeof(*s));
-
-       s->flags = flags;
-       TSRMLS_SET_CTX(s->ts);
-
-       if ((s->ops = ops)) {
-               php_http_encoding_stream_t *ss = s->ops->init(s);
-
-               if (ss) {
-                       return ss;
-               }
-       } else {
-               return s;
-       }
-
-       if (freeme) {
-               pefree(s, (flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT));
-       }
-       return NULL;
-}
-
-php_http_encoding_stream_t *php_http_encoding_stream_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to)
-{
-       TSRMLS_FETCH_FROM_CTX(from->ts);
-
-       if (from->ops->copy) {
-               int freeme;
-               php_http_encoding_stream_t *ns;
-
-               if ((freeme = !to)) {
-                       to = pemalloc(sizeof(*to), (from->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT));
-               }
-               memset(to, 0, sizeof(*to));
-
-               to->flags = from->flags;
-               to->ops = from->ops;
-               TSRMLS_SET_CTX(to->ts);
-
-               if ((ns = to->ops->copy(from, to))) {
-                       return ns;
-               } else {
-                       return to;
-               }
-
-               if (freeme) {
-                       pefree(to, (to->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT));
-               }
-       }
-
-       return NULL;
-}
-
-ZEND_RESULT_CODE php_http_encoding_stream_reset(php_http_encoding_stream_t **s)
-{
-       php_http_encoding_stream_t *ss;
-       if ((*s)->ops->dtor) {
-               (*s)->ops->dtor(*s);
-       }
-       if ((ss = (*s)->ops->init(*s))) {
-               *s = ss;
-               return SUCCESS;
-       }
-       return FAILURE;
-}
-
-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;
-       }
-       return s->ops->update(s, in_str, in_len, out_str, out_len);
-}
-
-ZEND_RESULT_CODE php_http_encoding_stream_flush(php_http_encoding_stream_t *s, char **out_str, size_t *out_len)
-{
-       if (!s->ops->flush) {
-               *out_str = NULL;
-               *out_len = 0;
-               return SUCCESS;
-       }
-       return s->ops->flush(s, out_str, out_len);
-}
-
-zend_bool php_http_encoding_stream_done(php_http_encoding_stream_t *s)
-{
-       if (!s->ops->done) {
-               return 0;
-       }
-       return s->ops->done(s);
-}
-
-ZEND_RESULT_CODE php_http_encoding_stream_finish(php_http_encoding_stream_t *s, char **out_str, size_t *out_len)
-{
-       if (!s->ops->finish) {
-               *out_str = NULL;
-               *out_len = 0;
-               return SUCCESS;
-       }
-       return s->ops->finish(s, out_str, out_len);
-}
-
-void php_http_encoding_stream_dtor(php_http_encoding_stream_t *s)
-{
-       if (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) {
-                       (*s)->ops->dtor(*s);
-               }
-               pefree(*s, ((*s)->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT));
-               *s = NULL;
-       }
-}
-
-struct dechunk_ctx {
-       php_http_buffer_t buffer;
-       ulong hexlen;
-       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);
-       TSRMLS_FETCH_FROM_CTX(s->ts);
-       
-       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 TSRMLS_CC, 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);
-       TSRMLS_FETCH_FROM_CTX(s->ts);
-       
-       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 TSRMLS_CC, 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));
-
-       if (!php_http_buffer_init_ex(&ctx->buffer, PHP_HTTP_BUFFER_DEFAULT_SIZE, (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT) ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0)) {
-               return NULL;
-       }
-
-       ctx->hexlen = 0;
-       ctx->zeroed = 0;
-       s->ctx = ctx;
-
-       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);
-       TSRMLS_FETCH_FROM_CTX(from->ts);
-
-       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 TSRMLS_CC, 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);
-       TSRMLS_FETCH_FROM_CTX(from->ts);
-
-       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 TSRMLS_CC, 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;
-       struct dechunk_ctx *from_ctx = from->ctx, *to_ctx = pemalloc(sizeof(*to_ctx), p);
-       TSRMLS_FETCH_FROM_CTX(from->ts);
-
-       if (php_http_buffer_init_ex(&to_ctx->buffer, PHP_HTTP_BUFFER_DEFAULT_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0)) {
-               to_ctx->hexlen = from_ctx->hexlen;
-               to_ctx->zeroed = from_ctx->zeroed;
-               php_http_buffer_append(&to_ctx->buffer, from_ctx->buffer.data, from_ctx->buffer.used);
-               to->ctx = to_ctx;
-               return to;
-       }
-       pefree(to_ctx, p);
-       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to copy inflate encoding stream: out of memory");
-       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;
-       TSRMLS_FETCH_FROM_CTX(s->ts);
-       
-       /* 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 TSRMLS_CC, 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;
-       TSRMLS_FETCH_FROM_CTX(s->ts);
-       
-       /* 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 TSRMLS_CC, 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;
-       struct dechunk_ctx *ctx = s->ctx;
-       TSRMLS_FETCH_FROM_CTX(s->ts);
-
-       if (ctx->zeroed) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Dechunk encoding stream has already reached the end of chunked input");
-               return FAILURE;
-       }
-       if ((PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(&ctx->buffer, data, data_len)) || !php_http_buffer_fix(&ctx->buffer)) {
-               /* OOM */
-               return FAILURE;
-       }
-
-       *decoded = NULL;
-       *decoded_len = 0;
-
-       php_http_buffer_init(&tmp);
-
-       /* we have data in our buffer */
-       while (ctx->buffer.used) {
-
-               /* we already know the size of the chunk and are waiting for data */
-               if (ctx->hexlen) {
-
-                       /* not enough data buffered */
-                       if (ctx->buffer.used < ctx->hexlen) {
-
-                               /* flush anyway? */
-                               if (s->flags & PHP_HTTP_ENCODING_STREAM_FLUSH_FULL) {
-                                       /* flush all data (should only be chunk data) */
-                                       php_http_buffer_append(&tmp, ctx->buffer.data, ctx->buffer.used);
-                                       /* waiting for less data now */
-                                       ctx->hexlen -= ctx->buffer.used;
-                                       /* no more buffered data */
-                                       php_http_buffer_reset(&ctx->buffer);
-                                       /* break */
-                               }
-
-                               /* we have too less data and don't need to flush */
-                               else {
-                                       break;
-                               }
-                       }
-
-                       /* we seem to have all data of the chunk */
-                       else {
-                               php_http_buffer_append(&tmp, ctx->buffer.data, ctx->hexlen);
-                               /* remove outgoing data from the buffer */
-                               php_http_buffer_cut(&ctx->buffer, 0, ctx->hexlen);
-                               /* reset hexlen */
-                               ctx->hexlen = 0;
-                               /* continue */
-                       }
-               }
-
-               /* we don't know the length of the chunk yet */
-               else {
-                       size_t off = 0;
-
-                       /* ignore preceeding CRLFs (too loose?) */
-                       while (off < ctx->buffer.used && (
-                                       ctx->buffer.data[off] == '\n' ||
-                                       ctx->buffer.data[off] == '\r')) {
-                               ++off;
-                       }
-                       if (off) {
-                               php_http_buffer_cut(&ctx->buffer, 0, off);
-                       }
-
-                       /* still data there? */
-                       if (ctx->buffer.used) {
-                               int eollen;
-                               const char *eolstr;
-
-                               /* we need eol, so we can be sure we have all hex digits */
-                               php_http_buffer_fix(&ctx->buffer);
-                               if ((eolstr = php_http_locate_bin_eol(ctx->buffer.data, ctx->buffer.used, &eollen))) {
-                                       char *stop = NULL;
-
-                                       /* read in chunk size */
-                                       ctx->hexlen = strtoul(ctx->buffer.data, &stop, 16);
-
-                                       /*      if strtoul() stops at the beginning of the buffered data
-                                               there's something oddly wrong, i.e. bad input */
-                                       if (stop == ctx->buffer.data) {
-                                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse chunk len from '%.*s'", (int) MIN(16, ctx->buffer.used), ctx->buffer.data);
-                                               php_http_buffer_dtor(&tmp);
-                                               return FAILURE;
-                                       }
-
-                                       /* cut out <chunk size hex><chunk extension><eol> */
-                                       php_http_buffer_cut(&ctx->buffer, 0, eolstr + eollen - ctx->buffer.data);
-                                       /* buffer->hexlen is 0 now or contains the size of the next chunk */
-                                       if (!ctx->hexlen) {
-                                               size_t off = 0;
-
-                                               /* ignore following CRLFs (too loose?) */
-                                               while (off < ctx->buffer.used && (
-                                                               ctx->buffer.data[off] == '\n' ||
-                                                               ctx->buffer.data[off] == '\r')) {
-                                                       ++off;
-                                               }
-                                               if (off) {
-                                                       php_http_buffer_cut(&ctx->buffer, 0, off);
-                                               }
-
-                                               ctx->zeroed = 1;
-                                               break;
-                                       }
-                                       /* continue */
-                               } else {
-                                       /* we have not enough data buffered to read in chunk size */
-                                       break;
-                               }
-                       }
-                       /* break */
-               }
-       }
-
-       php_http_buffer_fix(&tmp);
-       *decoded = tmp.data;
-       *decoded_len = tmp.used;
-
-       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;
-       TSRMLS_FETCH_FROM_CTX(s->ts);
-       
-       *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 TSRMLS_CC, 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;
-
-       if (ctx->hexlen) {
-               /* flush all data (should only be chunk data) */
-               php_http_buffer_fix(&ctx->buffer);
-               php_http_buffer_data(&ctx->buffer, decoded, decoded_len);
-               /* waiting for less data now */
-               ctx->hexlen -= ctx->buffer.used;
-               /* no more buffered data */
-               php_http_buffer_reset(&ctx->buffer);
-       } else {
-               *decoded = NULL;
-               *decoded_len = 0;
-       }
-
-       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;
-       TSRMLS_FETCH_FROM_CTX(s->ts);
-       
-       *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 TSRMLS_CC, 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;
-       TSRMLS_FETCH_FROM_CTX(s->ts);
-       
-       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 TSRMLS_CC, 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) {
-               struct dechunk_ctx *ctx = s->ctx;
-
-               php_http_buffer_dtor(&ctx->buffer);
-               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 php_http_encoding_stream_ops_t php_http_encoding_dechunk_ops = {
-       dechunk_init,
-       dechunk_copy,
-       dechunk_update,
-       dechunk_flush,
-       dechunk_done,
-       NULL,
-       dechunk_dtor
-};
-
-php_http_encoding_stream_ops_t *php_http_encoding_stream_get_dechunk_ops(void)
-{
-       return &php_http_encoding_dechunk_ops;
-}
-
-static zend_object_handlers php_http_encoding_stream_object_handlers;
-
-zend_object_value php_http_encoding_stream_object_new(zend_class_entry *ce TSRMLS_DC)
-{
-       return php_http_encoding_stream_object_new_ex(ce, NULL, NULL TSRMLS_CC);
-}
-
-zend_object_value php_http_encoding_stream_object_new_ex(zend_class_entry *ce, php_http_encoding_stream_t *s, php_http_encoding_stream_object_t **ptr TSRMLS_DC)
-{
-       php_http_encoding_stream_object_t *o;
-
-       o = ecalloc(1, sizeof(*o));
-       zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
-       object_properties_init((zend_object *) o, ce);
-
-       if (ptr) {
-               *ptr = o;
-       }
-
-       if (s) {
-               o->stream = s;
-       }
-
-       o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_encoding_stream_object_free, NULL TSRMLS_CC);
-       o->zv.handlers = &php_http_encoding_stream_object_handlers;
-
-       return o->zv;
-}
-
-zend_object_value php_http_encoding_stream_object_clone(zval *this_ptr TSRMLS_DC)
-{
-       zend_object_value new_ov;
-       php_http_encoding_stream_object_t *new_obj = NULL, *old_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       new_ov = php_http_encoding_stream_object_new_ex(old_obj->zo.ce, php_http_encoding_stream_copy(old_obj->stream, NULL), &new_obj TSRMLS_CC);
-       zend_objects_clone_members(&new_obj->zo, new_ov, &old_obj->zo, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC);
-
-       return new_ov;
-}
-
-void php_http_encoding_stream_object_free(void *object TSRMLS_DC)
-{
-       php_http_encoding_stream_object_t *o = (php_http_encoding_stream_object_t *) object;
-
-       if (o->stream) {
-               php_http_encoding_stream_free(&o->stream);
-       }
-       zend_object_std_dtor((zend_object *) o TSRMLS_CC);
-       efree(o);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEncodingStream___construct, 0, 0, 0)
-       ZEND_ARG_INFO(0, flags)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEncodingStream, __construct)
-{
-       long flags = 0;
-       php_http_encoding_stream_object_t *obj;
-       php_http_encoding_stream_ops_t *ops;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flags), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       if (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 TSRMLS_CC)) {
-               ops = &php_http_encoding_deflate_ops;
-       } else if (instanceof_function(obj->zo.ce, php_http_inflate_stream_class_entry TSRMLS_CC)) {
-               ops = &php_http_encoding_inflate_ops;
-       } else if (instanceof_function(obj->zo.ce, php_http_dechunk_stream_class_entry TSRMLS_CC)) {
-               ops = &php_http_encoding_dechunk_ops;
-       } else {
-               php_http_throw(runtime, "Unknown http\\Encoding\\Stream class '%s'", obj->zo.ce->name);
-               return;
-       }
-
-       php_http_expect(obj->stream = php_http_encoding_stream_init(obj->stream, ops, flags TSRMLS_CC), runtime, return);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEncodingStream_update, 0, 0, 1)
-       ZEND_ARG_INFO(0, data)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEncodingStream, update)
-{
-       int data_len;
-       char *data_str;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data_str, &data_len)) {
-               php_http_encoding_stream_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               if (obj->stream) {
-                       size_t encoded_len;
-                       char *encoded_str;
-
-                       if (SUCCESS == php_http_encoding_stream_update(obj->stream, data_str, data_len, &encoded_str, &encoded_len)) {
-                               RETURN_STRINGL(encoded_str, encoded_len, 0);
-                       }
-               }
-       }
-}
-
-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()) {
-               php_http_encoding_stream_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               if (obj->stream) {
-                       char *encoded_str;
-                       size_t encoded_len;
-
-                       if (SUCCESS == php_http_encoding_stream_flush(obj->stream, &encoded_str, &encoded_len)) {
-                               if (encoded_str) {
-                                       RETURN_STRINGL(encoded_str, encoded_len, 0);
-                               } else {
-                                       RETURN_EMPTY_STRING();
-                               }
-                       }
-               }
-       }
-}
-
-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()) {
-               php_http_encoding_stream_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               if (obj->stream) {
-                       RETURN_BOOL(php_http_encoding_stream_done(obj->stream));
-               }
-       }
-}
-
-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()) {
-               php_http_encoding_stream_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               if (obj->stream) {
-                       char *encoded_str;
-                       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 (encoded_str) {
-                                               RETURN_STRINGL(encoded_str, encoded_len, 0);
-                                       } else {
-                                               RETURN_EMPTY_STRING();
-                                       }
-                               } else {
-                                       PTR_FREE(encoded_str);
-                               }
-                       }
-               }
-       }
-}
-
-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, 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)
-       PHP_ME(HttpEncodingStream, finish,       ai_HttpEncodingStream_finish,       ZEND_ACC_PUBLIC)
-       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;
-       int len;
-       long flags = 0;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &len, &flags)) {
-               char *enc_str;
-               size_t enc_len;
-
-               if (SUCCESS == php_http_encoding_deflate(flags, str, len, &enc_str, &enc_len TSRMLS_CC)) {
-                       RETURN_STRINGL(enc_str, enc_len, 0);
-               }
-       }
-       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;
-       int len;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) {
-               char *enc_str;
-               size_t enc_len;
-
-               if (SUCCESS == php_http_encoding_inflate(str, len, &enc_str, &enc_len TSRMLS_CC)) {
-                       RETURN_STRINGL(enc_str, enc_len, 0);
-               }
-       }
-       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)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpDechunkStream, decode)
-{
-       char *str;
-       int len;
-       zval *zlen = NULL;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z!", &str, &len, &zlen)) {
-               const char *end_ptr;
-               char *enc_str;
-               size_t enc_len;
-
-               if ((end_ptr = php_http_encoding_dechunk(str, len, &enc_str, &enc_len TSRMLS_CC))) {
-                       if (zlen) {
-                               zval_dtor(zlen);
-                               ZVAL_LONG(zlen, str + len - end_ptr);
-                       }
-                       RETURN_STRINGL(enc_str, enc_len, 0);
-               }
-       }
-       RETURN_FALSE;
-}
-
-static zend_function_entry php_http_dechunk_stream_methods[] = {
-       PHP_ME(HttpDechunkStream, decode, ai_HttpDechunkStream_decode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-       EMPTY_FUNCTION_ENTRY
-};
-
-zend_class_entry *php_http_encoding_stream_class_entry;
-zend_class_entry *php_http_deflate_stream_class_entry;
-zend_class_entry *php_http_inflate_stream_class_entry;
-zend_class_entry *php_http_dechunk_stream_class_entry;
-
-PHP_MINIT_FUNCTION(http_encoding)
-{
-       zend_class_entry ce = {0};
-
-       INIT_NS_CLASS_ENTRY(ce, "http\\Encoding", "Stream", php_http_encoding_stream_methods);
-       php_http_encoding_stream_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
-       php_http_encoding_stream_class_entry->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;
-       php_http_encoding_stream_class_entry->create_object = php_http_encoding_stream_object_new;
-       memcpy(&php_http_encoding_stream_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
-       php_http_encoding_stream_object_handlers.clone_obj = php_http_encoding_stream_object_clone;
-
-       zend_declare_class_constant_long(php_http_encoding_stream_class_entry, ZEND_STRL("FLUSH_NONE"), PHP_HTTP_ENCODING_STREAM_FLUSH_NONE TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_encoding_stream_class_entry, ZEND_STRL("FLUSH_SYNC"), PHP_HTTP_ENCODING_STREAM_FLUSH_SYNC TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_encoding_stream_class_entry, ZEND_STRL("FLUSH_FULL"), PHP_HTTP_ENCODING_STREAM_FLUSH_FULL TSRMLS_CC);
-
-       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, NULL TSRMLS_CC);
-
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_GZIP"), PHP_HTTP_DEFLATE_TYPE_GZIP TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_ZLIB"), PHP_HTTP_DEFLATE_TYPE_ZLIB TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_RAW"), PHP_HTTP_DEFLATE_TYPE_RAW TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_DEF"), PHP_HTTP_DEFLATE_LEVEL_DEF TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_MIN"), PHP_HTTP_DEFLATE_LEVEL_MIN TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_MAX"), PHP_HTTP_DEFLATE_LEVEL_MAX TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_DEF"), PHP_HTTP_DEFLATE_STRATEGY_DEF TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_FILT"), PHP_HTTP_DEFLATE_STRATEGY_FILT TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_HUFF"), PHP_HTTP_DEFLATE_STRATEGY_HUFF TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_RLE"), PHP_HTTP_DEFLATE_STRATEGY_RLE TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_FIXED"), PHP_HTTP_DEFLATE_STRATEGY_FIXED TSRMLS_CC);
-
-       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, NULL TSRMLS_CC);
-
-       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, NULL TSRMLS_CC);
-
-       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/php_http_encoding.h b/php_http_encoding.h
deleted file mode 100644 (file)
index cdf7a1a..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_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_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
-
-typedef struct php_http_encoding_stream php_http_encoding_stream_t;
-
-typedef php_http_encoding_stream_t *(*php_http_encoding_stream_init_func_t)(php_http_encoding_stream_t *s);
-typedef php_http_encoding_stream_t *(*php_http_encoding_stream_copy_func_t)(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to);
-typedef ZEND_RESULT_CODE (*php_http_encoding_stream_update_func_t)(php_http_encoding_stream_t *s, const char *in_str, size_t in_len, char **out_str, size_t *out_len);
-typedef ZEND_RESULT_CODE (*php_http_encoding_stream_flush_func_t)(php_http_encoding_stream_t *s, char **out_str, size_t *out_len);
-typedef zend_bool (*php_http_encoding_stream_done_func_t)(php_http_encoding_stream_t *s);
-typedef ZEND_RESULT_CODE (*php_http_encoding_stream_finish_func_t)(php_http_encoding_stream_t *s, char **out_str, size_t *out_len);
-typedef void (*php_http_encoding_stream_dtor_func_t)(php_http_encoding_stream_t *s);
-
-typedef struct php_http_encoding_stream_ops {
-       php_http_encoding_stream_init_func_t init;
-       php_http_encoding_stream_copy_func_t copy;
-       php_http_encoding_stream_update_func_t update;
-       php_http_encoding_stream_flush_func_t flush;
-       php_http_encoding_stream_done_func_t done;
-       php_http_encoding_stream_finish_func_t finish;
-       php_http_encoding_stream_dtor_func_t dtor;
-} php_http_encoding_stream_ops_t;
-
-struct php_http_encoding_stream {
-       unsigned flags;
-       void *ctx;
-       php_http_encoding_stream_ops_t *ops;
-#ifdef ZTS
-       void ***ts;
-#endif
-};
-
-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 TSRMLS_DC);
-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);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_stream_reset(php_http_encoding_stream_t **s);
-PHP_HTTP_API 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);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_stream_flush(php_http_encoding_stream_t *s, char **out_str, size_t *len);
-PHP_HTTP_API zend_bool php_http_encoding_stream_done(php_http_encoding_stream_t *s);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_stream_finish(php_http_encoding_stream_t *s, char **out_str, size_t *len);
-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 const char *php_http_encoding_dechunk(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC);
-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 TSRMLS_DC);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len TSRMLS_DC);
-
-typedef struct php_http_encoding_stream_object {
-       zend_object zo;
-       zend_object_value zv;
-       php_http_encoding_stream_t *stream;
-} php_http_encoding_stream_object_t;
-
-PHP_HTTP_API zend_class_entry *php_http_encoding_stream_class_entry;
-
-zend_object_value php_http_encoding_stream_object_new(zend_class_entry *ce TSRMLS_DC);
-zend_object_value php_http_encoding_stream_object_new_ex(zend_class_entry *ce, php_http_encoding_stream_t *s, php_http_encoding_stream_object_t **ptr TSRMLS_DC);
-zend_object_value php_http_encoding_stream_object_clone(zval *object TSRMLS_DC);
-void php_http_encoding_stream_object_free(void *object TSRMLS_DC);
-
-PHP_HTTP_API zend_class_entry *php_http_deflate_stream_class_entry;
-PHP_HTTP_API zend_class_entry *php_http_inflate_stream_class_entry;
-PHP_HTTP_API zend_class_entry *php_http_dechunk_stream_class_entry;
-
-#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/php_http_env.c b/php_http_env.c
deleted file mode 100644 (file)
index c60d4c0..0000000
+++ /dev/null
@@ -1,819 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-#include "php_variables.h"
-
-PHP_RSHUTDOWN_FUNCTION(http_env)
-{
-       if (PHP_HTTP_G->env.request.headers) {
-               zend_hash_destroy(PHP_HTTP_G->env.request.headers);
-               FREE_HASHTABLE(PHP_HTTP_G->env.request.headers);
-               PHP_HTTP_G->env.request.headers = NULL;
-       }
-       if (PHP_HTTP_G->env.request.body) {
-               php_http_message_body_free(&PHP_HTTP_G->env.request.body);
-       }
-
-       if (PHP_HTTP_G->env.server_var) {
-               zval_ptr_dtor(&PHP_HTTP_G->env.server_var);
-               PHP_HTTP_G->env.server_var = NULL;
-       }
-
-       return SUCCESS;
-}
-
-void php_http_env_get_request_headers(HashTable *headers TSRMLS_DC)
-{
-       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-       zval **hsv, **header;
-       HashPosition pos;
-
-       if (!PHP_HTTP_G->env.request.headers) {
-               ALLOC_HASHTABLE(PHP_HTTP_G->env.request.headers);
-               zend_hash_init(PHP_HTTP_G->env.request.headers, 0, NULL, ZVAL_PTR_DTOR, 0);
-
-               zend_is_auto_global("_SERVER", lenof("_SERVER") TSRMLS_CC);
-
-               if (SUCCESS == zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void *) &hsv) && Z_TYPE_PP(hsv) == IS_ARRAY) {
-                       FOREACH_KEY(pos, *hsv, key) {
-                               if (key.type == HASH_KEY_IS_STRING && key.len > 6 && *key.str == 'H' && !strncmp(key.str, "HTTP_", 5)) {
-                                       key.len -= 5;
-                                       key.str = php_http_pretty_key(estrndup(key.str + 5, key.len - 1), key.len - 1, 1, 1);
-
-                                       zend_hash_get_current_data_ex(Z_ARRVAL_PP(hsv), (void *) &header, &pos);
-                                       Z_ADDREF_P(*header);
-                                       zend_symtable_update(PHP_HTTP_G->env.request.headers, key.str, key.len, (void *) header, sizeof(zval *), NULL);
-
-                                       efree(key.str);
-                               } else if (key.type == HASH_KEY_IS_STRING && key.len > 9 && *key.str == 'C' && !strncmp(key.str, "CONTENT_", 8)) {
-                                       key.str = php_http_pretty_key(estrndup(key.str, key.len - 1), key.len - 1, 1, 1);
-
-                                       zend_hash_get_current_data_ex(Z_ARRVAL_PP(hsv), (void *) &header, &pos);
-                                       Z_ADDREF_P(*header);
-                                       zend_symtable_update(PHP_HTTP_G->env.request.headers, key.str, key.len, (void *) header, sizeof(zval *), NULL);
-
-                                       efree(key.str);
-                               }
-                       }
-               }
-       }
-
-       if (headers) {
-               zend_hash_copy(headers, PHP_HTTP_G->env.request.headers, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
-       }
-}
-
-char *php_http_env_get_request_header(const char *name_str, size_t name_len, size_t *len, php_http_message_t *request TSRMLS_DC)
-{
-       HashTable *request_headers;
-       zval **zvalue = NULL;
-       char *val = NULL, *key = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1);
-
-       if (request) {
-               request_headers = &request->hdrs;
-       } else {
-               php_http_env_get_request_headers(NULL TSRMLS_CC);
-               request_headers = PHP_HTTP_G->env.request.headers;
-       }
-
-       if (SUCCESS == zend_symtable_find(request_headers, key, name_len + 1, (void *) &zvalue)) {
-               zval *zcopy = php_http_ztyp(IS_STRING, *zvalue);
-
-               val = estrndup(Z_STRVAL_P(zcopy), Z_STRLEN_P(zcopy));
-               if (len) {
-                       *len = Z_STRLEN_P(zcopy);
-               }
-               zval_ptr_dtor(&zcopy);
-       }
-
-       efree(key);
-
-       return val;
-}
-
-int php_http_env_got_request_header(const char *name_str, size_t name_len, php_http_message_t *request TSRMLS_DC)
-{
-       HashTable *request_headers;
-       char *key = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1);
-       int got;
-
-       if (request) {
-               request_headers = &request->hdrs;
-       } else {
-               php_http_env_get_request_headers(NULL TSRMLS_CC);
-               request_headers = PHP_HTTP_G->env.request.headers;
-       }
-       got = zend_symtable_exists(request_headers, key, name_len + 1);
-       efree(key);
-
-       return got;
-}
-
-zval *php_http_env_get_superglobal(const char *key, size_t key_len TSRMLS_DC)
-{
-       zval **hsv;
-
-       zend_is_auto_global(key, key_len TSRMLS_CC);
-
-       if ((SUCCESS != zend_hash_find(&EG(symbol_table), key, key_len + 1, (void *) &hsv)) || (Z_TYPE_PP(hsv) != IS_ARRAY)) {
-               return NULL;
-       }
-
-       return *hsv;
-}
-
-zval *php_http_env_get_server_var(const char *key, size_t key_len, zend_bool check TSRMLS_DC)
-{
-       zval *hsv, **var;
-       char *env;
-
-       /* if available, this is a lot faster than accessing $_SERVER */
-       if (sapi_module.getenv) {
-               if ((!(env = sapi_module.getenv((char *) key, key_len TSRMLS_CC))) || (check && !*env)) {
-                       return NULL;
-               }
-               if (PHP_HTTP_G->env.server_var) {
-                       zval_ptr_dtor(&PHP_HTTP_G->env.server_var);
-               }
-               MAKE_STD_ZVAL(PHP_HTTP_G->env.server_var);
-               ZVAL_STRING(PHP_HTTP_G->env.server_var, env, 1);
-               return PHP_HTTP_G->env.server_var;
-       }
-
-       if (!(hsv = php_http_env_get_superglobal(ZEND_STRL("_SERVER") TSRMLS_CC))) {
-               return NULL;
-       }
-       if ((SUCCESS != zend_symtable_find(Z_ARRVAL_P(hsv), key, key_len + 1, (void *) &var))) {
-               return NULL;
-       }
-       if (check && !((Z_TYPE_PP(var) == IS_STRING) && Z_STRVAL_PP(var) && Z_STRLEN_PP(var))) {
-               return NULL;
-       }
-       return *var;
-}
-
-php_http_message_body_t *php_http_env_get_request_body(TSRMLS_D)
-{
-       if (!PHP_HTTP_G->env.request.body) {
-               php_stream *s = php_stream_temp_new();
-#if PHP_VERSION_ID >= 50600
-               php_stream *input = php_stream_open_wrapper("php://input", "r", 0, NULL);
-
-               /* php://input does not support stat */
-               php_stream_copy_to_stream_ex(input, s, -1, NULL);
-               php_stream_close(input);
-#else
-               if (SG(request_info).post_data || SG(request_info).raw_post_data) {
-                       /* php://input does not support seek() in PHP <= 5.5 */
-                       if (SG(request_info).raw_post_data) {
-                               php_stream_write(s, SG(request_info).raw_post_data, SG(request_info).raw_post_data_length);
-                       } else {
-                               php_stream_write(s, SG(request_info).post_data, SG(request_info).post_data_length);
-                       }
-               } else if (sapi_module.read_post && !SG(read_post_bytes)) {
-                       char *buf = emalloc(4096);
-                       int len;
-
-                       while (0 < (len = sapi_module.read_post(buf, 4096 TSRMLS_CC))) {
-                               SG(read_post_bytes) += len;
-                               php_stream_write(s, buf, len);
-
-                               if (len < 4096) {
-                                       break;
-                               }
-                       }
-                       efree(buf);
-               }
-#endif
-               php_stream_rewind(s);
-               PHP_HTTP_G->env.request.body = php_http_message_body_init(NULL, s TSRMLS_CC);
-       }
-
-       return PHP_HTTP_G->env.request.body;
-}
-
-const char *php_http_env_get_request_method(php_http_message_t *request TSRMLS_DC)
-{
-       const char *m;
-
-       if (PHP_HTTP_MESSAGE_TYPE(REQUEST, request)) {
-               m = request->http.info.request.method;
-       } else {
-               m = SG(request_info).request_method;
-       }
-
-       return m ? m : "GET";
-}
-
-php_http_range_status_t php_http_env_get_request_ranges(HashTable *ranges, size_t length, php_http_message_t *request TSRMLS_DC)
-{
-       zval *zentry;
-       char *range, *rp, c;
-       long begin = -1, end = -1, *ptr;
-
-       if (!(range = php_http_env_get_request_header(ZEND_STRL("Range"), NULL, request TSRMLS_CC))) {
-               return PHP_HTTP_RANGE_NO;
-       }
-       if (strncmp(range, "bytes=", lenof("bytes="))) {
-               PTR_FREE(range);
-               return PHP_HTTP_RANGE_NO;
-       }
-
-       rp  = range + lenof("bytes=");
-       ptr = &begin;
-
-       do {
-               switch (c = *(rp++)) {
-                       case '0':
-                               /* allow 000... - shall we? */
-                               if (*ptr != -10) {
-                                       *ptr *= 10;
-                               }
-                               break;
-
-                       case '1': case '2': case '3':
-                       case '4': case '5': case '6':
-                       case '7': case '8': case '9':
-                               /*
-                                * If the value of the pointer is already set (non-negative)
-                                * then multiply its value by ten and add the current value,
-                                * else initialise the pointers value with the current value
-                                * --
-                                * This let us recognize empty fields when validating the
-                                * ranges, i.e. a "-10" for begin and "12345" for the end
-                                * was the following range request: "Range: bytes=0-12345";
-                                * While a "-1" for begin and "12345" for the end would
-                                * have been: "Range: bytes=-12345".
-                                */
-                               if (*ptr > 0) {
-                                       *ptr *= 10;
-                                       *ptr += c - '0';
-                               } else {
-                                       *ptr = c - '0';
-                               }
-                               break;
-
-                       case '-':
-                               ptr = &end;
-                               break;
-
-                       case ' ':
-                               break;
-
-                       case 0:
-                       case ',':
-
-                               if (length) {
-                                       /* validate ranges */
-                                       switch (begin) {
-                                               /* "0-12345" */
-                                               case -10:
-                                                       switch (end) {
-                                                               /* "0-" */
-                                                               case -1:
-                                                                       PTR_FREE(range);
-                                                                       return PHP_HTTP_RANGE_NO;
-
-                                                               /* "0-0" */
-                                                               case -10:
-                                                                       end = 0;
-                                                                       break;
-
-                                                               default:
-                                                                       if (length <= (size_t) end) {
-                                                                               end = length - 1;
-                                                                       }
-                                                                       break;
-                                                       }
-                                                       begin = 0;
-                                                       break;
-
-                                               /* "-12345" */
-                                               case -1:
-                                                       /* "-", "-0" */
-                                                       if (end == -1 || end == -10) {
-                                                               PTR_FREE(range);
-                                                               return PHP_HTTP_RANGE_ERR;
-                                                       }
-                                                       begin = length - end;
-                                                       end = length - 1;
-                                                       break;
-
-                                               /* "12345-(NNN)" */
-                                               default:
-                                                       if (length <= (size_t) begin) {
-                                                               PTR_FREE(range);
-                                                               return PHP_HTTP_RANGE_ERR;
-                                                       }
-                                                       switch (end) {
-                                                               /* "12345-0" */
-                                                               case -10:
-                                                                       PTR_FREE(range);
-                                                                       return PHP_HTTP_RANGE_ERR;
-
-                                                               /* "12345-" */
-                                                               case -1:
-                                                                       end = length - 1;
-                                                                       break;
-
-                                                               /* "12345-67890" */
-                                                               default:
-                                                                       if (length <= (size_t) end) {
-                                                                               end = length - 1;
-                                                                       } else if (end <  begin) {
-                                                                               PTR_FREE(range);
-                                                                               return PHP_HTTP_RANGE_ERR;
-                                                                       }
-                                                                       break;
-                                                       }
-                                                       break;
-                                       }
-                               }
-
-                               MAKE_STD_ZVAL(zentry);
-                               array_init(zentry);
-                               add_index_long(zentry, 0, begin);
-                               add_index_long(zentry, 1, end);
-                               zend_hash_next_index_insert(ranges, &zentry, sizeof(zval *), NULL);
-
-                               begin = -1;
-                               end = -1;
-                               ptr = &begin;
-
-                               break;
-
-                       default:
-                               PTR_FREE(range);
-                               return PHP_HTTP_RANGE_NO;
-               }
-       } while (c != 0);
-
-       PTR_FREE(range);
-       return PHP_HTTP_RANGE_OK;
-}
-
-static void grab_headers(void *data, void *arg TSRMLS_DC)
-{
-       php_http_buffer_appendl(PHP_HTTP_BUFFER(arg), ((sapi_header_struct *)data)->header);
-       php_http_buffer_appends(PHP_HTTP_BUFFER(arg), PHP_HTTP_CRLF);
-}
-
-ZEND_RESULT_CODE php_http_env_get_response_headers(HashTable *headers_ht TSRMLS_DC)
-{
-       ZEND_RESULT_CODE status;
-       php_http_buffer_t headers;
-
-       php_http_buffer_init(&headers);
-       zend_llist_apply_with_argument(&SG(sapi_headers).headers, grab_headers, &headers TSRMLS_CC);
-       php_http_buffer_fix(&headers);
-
-       status = php_http_header_parse(headers.data, headers.used, headers_ht, NULL, NULL TSRMLS_CC);
-       php_http_buffer_dtor(&headers);
-
-       return status;
-}
-
-char *php_http_env_get_response_header(const char *name_str, size_t name_len TSRMLS_DC)
-{
-       char *val = NULL;
-       HashTable headers;
-
-       zend_hash_init(&headers, 0, NULL, ZVAL_PTR_DTOR, 0);
-       if (SUCCESS == php_http_env_get_response_headers(&headers TSRMLS_CC)) {
-               zval **zvalue;
-               char *key = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1);
-
-               if (SUCCESS == zend_symtable_find(&headers, key, name_len + 1, (void *) &zvalue)) {
-                       zval *zcopy = php_http_ztyp(IS_STRING, *zvalue);
-
-                       val = estrndup(Z_STRVAL_P(zcopy), Z_STRLEN_P(zcopy));
-                       zval_ptr_dtor(&zcopy);
-               }
-
-               efree(key);
-       }
-       zend_hash_destroy(&headers);
-
-       return val;
-}
-
-long php_http_env_get_response_code(TSRMLS_D)
-{
-       long code = SG(sapi_headers).http_response_code;
-       return code ? code : 200;
-}
-
-ZEND_RESULT_CODE php_http_env_set_response_code(long http_code TSRMLS_DC)
-{
-       return sapi_header_op(SAPI_HEADER_SET_STATUS, (void *) http_code TSRMLS_CC);
-}
-
-ZEND_RESULT_CODE php_http_env_set_response_status_line(long code, php_http_version_t *v TSRMLS_DC)
-{
-       sapi_header_line h = {NULL, 0, 0};
-       ZEND_RESULT_CODE ret;
-
-       h.line_len = spprintf(&h.line, 0, "HTTP/%u.%u %ld %s", v->major, v->minor, code, php_http_env_get_response_status_for_code(code));
-       ret = sapi_header_op(SAPI_HEADER_REPLACE, (void *) &h TSRMLS_CC);
-       efree(h.line);
-
-       return ret;
-}
-
-ZEND_RESULT_CODE php_http_env_set_response_protocol_version(php_http_version_t *v TSRMLS_DC)
-{
-       return php_http_env_set_response_status_line(php_http_env_get_response_code(TSRMLS_C), v TSRMLS_CC);
-}
-
-ZEND_RESULT_CODE php_http_env_set_response_header(long http_code, const char *header_str, size_t header_len, zend_bool replace TSRMLS_DC)
-{
-       sapi_header_line h = {estrndup(header_str, header_len), header_len, http_code};
-       ZEND_RESULT_CODE ret = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, (void *) &h TSRMLS_CC);
-       efree(h.line);
-       return ret;
-}
-
-ZEND_RESULT_CODE php_http_env_set_response_header_va(long http_code, zend_bool replace, const char *fmt, va_list argv TSRMLS_DC)
-{
-       ZEND_RESULT_CODE ret = FAILURE;
-       sapi_header_line h = {NULL, 0, http_code};
-
-       h.line_len = vspprintf(&h.line, 0, fmt, argv);
-
-       if (h.line) {
-               if (h.line_len) {
-                       ret = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, (void *) &h TSRMLS_CC);
-               }
-               efree(h.line);
-       }
-       return ret;
-}
-
-ZEND_RESULT_CODE php_http_env_set_response_header_format(long http_code, zend_bool replace TSRMLS_DC, const char *fmt, ...)
-{
-       ZEND_RESULT_CODE ret;
-       va_list args;
-
-       va_start(args, fmt);
-       ret = php_http_env_set_response_header_va(http_code, replace, fmt, args TSRMLS_CC);
-       va_end(args);
-
-       return ret;
-}
-
-ZEND_RESULT_CODE php_http_env_set_response_header_value(long http_code, const char *name_str, size_t name_len, zval *value, zend_bool replace TSRMLS_DC)
-{
-       if (!value) {
-               sapi_header_line h = {(char *) name_str, name_len, http_code};
-
-               return sapi_header_op(SAPI_HEADER_DELETE, (void *) &h TSRMLS_CC);
-       }
-
-       if(Z_TYPE_P(value) == IS_ARRAY || Z_TYPE_P(value) == IS_OBJECT) {
-               HashPosition pos;
-               int first = replace;
-               zval **data_ptr;
-
-               FOREACH_HASH_VAL(pos, HASH_OF(value), data_ptr) {
-                       if (SUCCESS != php_http_env_set_response_header_value(http_code, name_str, name_len, *data_ptr, first TSRMLS_CC)) {
-                               return FAILURE;
-                       }
-                       first = 0;
-               }
-
-               return SUCCESS;
-       } else {
-               zval *data = php_http_ztyp(IS_STRING, value);
-
-               if (!Z_STRLEN_P(data)) {
-                       zval_ptr_dtor(&data);
-                       return php_http_env_set_response_header_value(http_code, name_str, name_len, NULL, replace TSRMLS_CC);
-               } else {
-                       sapi_header_line h;
-                       ZEND_RESULT_CODE ret;
-
-                       if (name_len > INT_MAX) {
-                               name_len = INT_MAX;
-                       }
-                       h.response_code = http_code;
-                       h.line_len = spprintf(&h.line, 0, "%.*s: %.*s", (int) name_len, name_str, Z_STRLEN_P(data), Z_STRVAL_P(data));
-
-                       ret = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, (void *) &h TSRMLS_CC);
-
-                       zval_ptr_dtor(&data);
-                       PTR_FREE(h.line);
-
-                       return ret;
-               }
-       }
-}
-
-const char *php_http_env_get_response_status_for_code(unsigned code)
-{
-       switch (code) {
-#define PHP_HTTP_RESPONSE_CODE(c, s) case c: return s;
-#include "php_http_response_codes.h"
-#undef PHP_HTTP_RESPONSE_CODE
-       default:
-               return NULL;
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getRequestHeader, 0, 0, 0)
-       ZEND_ARG_INFO(0, header_name)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnv, getRequestHeader)
-{
-       char *header_name_str = NULL;
-       int header_name_len = 0;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) {
-               return;
-       }
-       if (header_name_str && header_name_len) {
-               size_t header_length;
-               char *header_value = php_http_env_get_request_header(header_name_str, header_name_len, &header_length, NULL TSRMLS_CC);
-
-               if (header_value) {
-                       RETURN_STRINGL(header_value, header_length, 0);
-               }
-       } else {
-               array_init(return_value);
-               php_http_env_get_request_headers(Z_ARRVAL_P(return_value) TSRMLS_CC);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getRequestBody, 0, 0, 0)
-       ZEND_ARG_INFO(0, body_class_name)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnv, getRequestBody)
-{
-       zend_object_value ov;
-       php_http_message_body_t *body;
-       zend_class_entry *class_entry = php_http_message_body_class_entry;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|C", &class_entry), invalid_arg, return);
-
-       body = php_http_env_get_request_body(TSRMLS_C);
-       if (SUCCESS == php_http_new(&ov, class_entry, (php_http_new_t) php_http_message_body_object_new_ex, php_http_message_body_class_entry, body, NULL TSRMLS_CC)) {
-               php_http_message_body_addref(body);
-               RETVAL_OBJVAL(ov, 0);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getResponseStatusForCode, 0, 0, 1)
-       ZEND_ARG_INFO(0, code)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnv, getResponseStatusForCode)
-{
-       long code;
-       const char *status;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) {
-               return;
-       }
-
-       if ((status = php_http_env_get_response_status_for_code(code))) {
-               RETURN_STRING(status, 1);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getResponseStatusForAllCodes, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnv, getResponseStatusForAllCodes)
-{
-       if (SUCCESS != zend_parse_parameters_none()) {
-               return;
-       }
-
-       array_init(return_value);
-#define PHP_HTTP_RESPONSE_CODE(code, status) add_index_string(return_value, code, status, 1);
-#include "php_http_response_codes.h"
-#undef PHP_HTTP_RESPONSE_CODE
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getResponseHeader, 0, 0, 0)
-       ZEND_ARG_INFO(0, header_name)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnv, getResponseHeader)
-{
-       char *header_name_str = NULL;
-       int header_name_len = 0;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) {
-               return;
-       }
-       if (header_name_str && header_name_len) {
-               char *header_value = php_http_env_get_response_header(header_name_str, header_name_len TSRMLS_CC);
-
-               if (header_value) {
-                       RETURN_STRING(header_value, 0);
-               }
-       } else {
-               array_init(return_value);
-               php_http_env_get_response_headers(Z_ARRVAL_P(return_value) TSRMLS_CC);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getResponseCode, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnv, getResponseCode)
-{
-       if (SUCCESS != zend_parse_parameters_none()) {
-               return;
-       }
-       RETURN_LONG(php_http_env_get_response_code(TSRMLS_C));
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_setResponseHeader, 0, 0, 1)
-       ZEND_ARG_INFO(0, header_name)
-       ZEND_ARG_INFO(0, header_value)
-       ZEND_ARG_INFO(0, response_code)
-       ZEND_ARG_INFO(0, replace_header)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnv, setResponseHeader)
-{
-       char *header_name_str;
-       int header_name_len;
-       zval *header_value = NULL;
-       long code = 0;
-       zend_bool replace_header = 1;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z!lb", &header_name_str, &header_name_len, &header_value, &code, &replace_header)) {
-               return;
-       }
-       RETURN_BOOL(SUCCESS == php_http_env_set_response_header_value(code, header_name_str, header_name_len, header_value, replace_header TSRMLS_CC));
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_setResponseCode, 0, 0, 1)
-       ZEND_ARG_INFO(0, code)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnv, setResponseCode)
-{
-       long code;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) {
-               return;
-       }
-       RETURN_BOOL(SUCCESS == php_http_env_set_response_code(code TSRMLS_CC));
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_negotiateLanguage, 0, 0, 1)
-       ZEND_ARG_INFO(0, supported)
-       ZEND_ARG_INFO(1, result_array)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnv, negotiateLanguage)
-{
-       HashTable *supported;
-       zval *rs_array = NULL;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H|z", &supported, &rs_array)) {
-               return;
-       }
-       if (rs_array) {
-               zval_dtor(rs_array);
-               array_init(rs_array);
-       }
-
-       PHP_HTTP_DO_NEGOTIATE(language, supported, rs_array);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_negotiateCharset, 0, 0, 1)
-       ZEND_ARG_INFO(0, supported)
-       ZEND_ARG_INFO(1, result_array)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnv, negotiateCharset)
-{
-       HashTable *supported;
-       zval *rs_array = NULL;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H|z", &supported, &rs_array)) {
-               return;
-       }
-       if (rs_array) {
-               zval_dtor(rs_array);
-               array_init(rs_array);
-       }
-       PHP_HTTP_DO_NEGOTIATE(charset, supported, rs_array);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_negotiateEncoding, 0, 0, 1)
-       ZEND_ARG_INFO(0, supported)
-       ZEND_ARG_INFO(1, result_array)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnv, negotiateEncoding)
-{
-       HashTable *supported;
-       zval *rs_array = NULL;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H|z", &supported, &rs_array)) {
-               return;
-       }
-       if (rs_array) {
-               zval_dtor(rs_array);
-               array_init(rs_array);
-       }
-       PHP_HTTP_DO_NEGOTIATE(encoding, supported, rs_array);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_negotiateContentType, 0, 0, 1)
-       ZEND_ARG_INFO(0, supported)
-       ZEND_ARG_INFO(1, result_array)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnv, negotiateContentType)
-{
-       HashTable *supported;
-       zval *rs_array = NULL;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H|z", &supported, &rs_array)) {
-               return;
-       }
-       if (rs_array) {
-               zval_dtor(rs_array);
-               array_init(rs_array);
-       }
-       PHP_HTTP_DO_NEGOTIATE(content_type, supported, rs_array);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_negotiate, 0, 0, 2)
-       ZEND_ARG_INFO(0, params)
-       ZEND_ARG_INFO(0, supported)
-       ZEND_ARG_INFO(0, primary_type_separator)
-       ZEND_ARG_INFO(1, result_array)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnv, negotiate)
-{
-       HashTable *supported, *rs;
-       zval *rs_array = NULL;
-       char *value_str, *sep_str = NULL;
-       int value_len, sep_len = 0;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sH|s!z", &value_str, &value_len, &supported, &sep_str, &sep_len, &rs_array)) {
-               return;
-       }
-
-
-       if (rs_array) {
-               zval_dtor(rs_array);
-               array_init(rs_array);
-       }
-
-       if ((rs = php_http_negotiate(value_str, value_len, supported, sep_str, sep_len TSRMLS_CC))) {
-               PHP_HTTP_DO_NEGOTIATE_HANDLE_RESULT(rs, supported, rs_array);
-       } else {
-               PHP_HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported, rs_array);
-       }
-}
-
-static zend_function_entry php_http_env_methods[] = {
-       PHP_ME(HttpEnv, getRequestHeader,              ai_HttpEnv_getRequestHeader,              ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-       PHP_ME(HttpEnv, getRequestBody,                ai_HttpEnv_getRequestBody,                ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-
-       PHP_ME(HttpEnv, getResponseStatusForCode,      ai_HttpEnv_getResponseStatusForCode,      ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-       PHP_ME(HttpEnv, getResponseStatusForAllCodes,  ai_HttpEnv_getResponseStatusForAllCodes,  ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-
-       PHP_ME(HttpEnv, getResponseHeader,             ai_HttpEnv_getResponseHeader,             ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-       PHP_ME(HttpEnv, getResponseCode,               ai_HttpEnv_getResponseCode,               ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-       PHP_ME(HttpEnv, setResponseHeader,             ai_HttpEnv_setResponseHeader,             ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-       PHP_ME(HttpEnv, setResponseCode,               ai_HttpEnv_setResponseCode,               ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-
-       PHP_ME(HttpEnv, negotiateLanguage,             ai_HttpEnv_negotiateLanguage,             ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-       PHP_ME(HttpEnv, negotiateContentType,          ai_HttpEnv_negotiateContentType,          ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-       PHP_ME(HttpEnv, negotiateEncoding,             ai_HttpEnv_negotiateEncoding,             ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-       PHP_ME(HttpEnv, negotiateCharset,              ai_HttpEnv_negotiateCharset,              ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-       PHP_ME(HttpEnv, negotiate,                     ai_HttpEnv_negotiate,                     ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-
-       EMPTY_FUNCTION_ENTRY
-};
-
-zend_class_entry *php_http_env_class_entry;
-
-PHP_MINIT_FUNCTION(http_env)
-{
-       zend_class_entry ce = {0};
-
-       INIT_NS_CLASS_ENTRY(ce, "http", "Env", php_http_env_methods);
-       php_http_env_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
-
-       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/php_http_env.h b/php_http_env.h
deleted file mode 100644 (file)
index 3fc80ab..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_ENV_H
-#define PHP_HTTP_ENV_H
-
-#include "php_http_message_body.h"
-#include "php_http_version.h"
-
-struct php_http_env_globals {
-       zval *server_var;
-       char *etag_mode;
-
-       struct {
-               HashTable *headers;
-               php_http_message_body_t *body;
-       } request;
-};
-
-typedef enum php_http_content_encoding {
-       PHP_HTTP_CONTENT_ENCODING_NONE,
-       PHP_HTTP_CONTENT_ENCODING_GZIP
-} php_http_content_encoding_t;
-
-typedef enum php_http_range_status {
-       PHP_HTTP_RANGE_NO,
-       PHP_HTTP_RANGE_OK,
-       PHP_HTTP_RANGE_ERR
-} php_http_range_status_t;
-
-PHP_HTTP_API php_http_range_status_t php_http_env_get_request_ranges(HashTable *ranges, size_t entity_length, php_http_message_t *request TSRMLS_DC);
-PHP_HTTP_API void php_http_env_get_request_headers(HashTable *headers TSRMLS_DC);
-PHP_HTTP_API char *php_http_env_get_request_header(const char *name_str, size_t name_len, size_t *len, php_http_message_t *request TSRMLS_DC);
-PHP_HTTP_API int php_http_env_got_request_header(const char *name_str, size_t name_len, php_http_message_t *request TSRMLS_DC);
-PHP_HTTP_API php_http_message_body_t *php_http_env_get_request_body(TSRMLS_D);
-PHP_HTTP_API const char *php_http_env_get_request_method(php_http_message_t *request TSRMLS_DC);
-
-typedef enum php_http_content_disposition {
-       PHP_HTTP_CONTENT_DISPOSITION_NONE,
-       PHP_HTTP_CONTENT_DISPOSITION_INLINE,
-       PHP_HTTP_CONTENT_DISPOSITION_ATTACHMENT
-} php_http_content_disposition_t;
-
-typedef enum php_http_cache_status {
-       PHP_HTTP_CACHE_NO,
-       PHP_HTTP_CACHE_HIT,
-       PHP_HTTP_CACHE_MISS
-} php_http_cache_status_t;
-
-PHP_HTTP_API long php_http_env_get_response_code(TSRMLS_D);
-PHP_HTTP_API const char *php_http_env_get_response_status_for_code(unsigned code);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_env_get_response_headers(HashTable *headers_ht TSRMLS_DC);
-PHP_HTTP_API char *php_http_env_get_response_header(const char *name_str, size_t name_len TSRMLS_DC);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_code(long http_code TSRMLS_DC);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_protocol_version(php_http_version_t *v TSRMLS_DC);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_header(long http_code, const char *header_str, size_t header_len, zend_bool replace TSRMLS_DC);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_header_value(long http_code, const char *name_str, size_t name_len, zval *value, zend_bool replace TSRMLS_DC);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_header_format(long http_code, zend_bool replace TSRMLS_DC, const char *fmt, ...);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_header_va(long http_code, zend_bool replace, const char *fmt, va_list argv TSRMLS_DC);
-
-PHP_HTTP_API zval *php_http_env_get_server_var(const char *key_str, size_t key_len, zend_bool check TSRMLS_DC);
-#define php_http_env_got_server_var(v) (NULL != php_http_env_get_server_var((v), strlen(v), 1 TSRMLS_CC))
-PHP_HTTP_API zval *php_http_env_get_superglobal(const char *key, size_t key_len TSRMLS_DC);
-
-PHP_HTTP_API zend_class_entry *php_http_env_class_entry;
-PHP_MINIT_FUNCTION(http_env);
-PHP_RSHUTDOWN_FUNCTION(http_env);
-
-#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/php_http_env_request.c b/php_http_env_request.c
deleted file mode 100644 (file)
index a884d2f..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-static int grab_file(void *zpp TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
-{
-       zval *zfiles, **name, **zname, **error, **zerror, **type, **ztype, **size, **zsize, **tmp_name = zpp;
-       zend_hash_key *file_key;
-
-       zfiles = (zval *) va_arg(argv, zval *);
-       file_key = (zend_hash_key *) va_arg(argv, zend_hash_key *);
-       name = (zval **) va_arg(argv, zval **);
-       size = (zval **) va_arg(argv, zval **);
-       type = (zval **) va_arg(argv, zval **);
-       error = (zval **) va_arg(argv, zval **);
-
-       if (SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(name), key->h, (void *) &zname)
-       &&      SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(size), key->h, (void *) &zsize)
-       &&      SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(type), key->h, (void *) &ztype)
-       &&      SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(error), key->h, (void *) &zerror)
-       ) {
-               zval *entry, **array;
-
-               MAKE_STD_ZVAL(entry);
-               array_init(entry);
-
-               Z_ADDREF_PP(tmp_name);
-               add_assoc_zval_ex(entry, ZEND_STRS("file"), *tmp_name);
-               Z_ADDREF_PP(zname);
-               add_assoc_zval_ex(entry, ZEND_STRS("name"), *zname);
-               Z_ADDREF_PP(zsize);
-               add_assoc_zval_ex(entry, ZEND_STRS("size"), *zsize);
-               Z_ADDREF_PP(ztype);
-               add_assoc_zval_ex(entry, ZEND_STRS("type"), *ztype);
-               Z_ADDREF_PP(zerror);
-               add_assoc_zval_ex(entry, ZEND_STRS("error"), *zerror);
-
-               if (SUCCESS == zend_hash_quick_find(Z_ARRVAL_P(zfiles), file_key->arKey, file_key->nKeyLength, file_key->h, (void *) &array)) {
-                       add_next_index_zval(*array, entry);
-               } else {
-                       zval *tmp;
-
-                       MAKE_STD_ZVAL(tmp);
-                       array_init(tmp);
-                       add_next_index_zval(tmp, entry);
-                       zend_hash_quick_update(Z_ARRVAL_P(zfiles), file_key->arKey, file_key->nKeyLength, file_key->h, (void *) &tmp, sizeof(zval *), NULL);
-               }
-       }
-
-       return ZEND_HASH_APPLY_KEEP;
-}
-
-static int grab_files(void *zpp TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
-{
-       zval *zfiles, **name, **tmp_name, **error, **type, **size, **val = zpp;
-
-       zfiles = (zval *) va_arg(argv, zval *);
-
-       if (Z_TYPE_PP(val) == IS_ARRAY
-       &&      SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("tmp_name"), (void *) &tmp_name)
-       &&      SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("name"), (void *) &name)
-       &&      SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("size"), (void *) &size)
-       &&      SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("type"), (void *) &type)
-       &&      SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("error"), (void *) &error)
-       ) {
-               int count;
-
-               if (Z_TYPE_PP(tmp_name) == IS_ARRAY && (count = zend_hash_num_elements(Z_ARRVAL_PP(tmp_name))) > 1) {
-                       if (count == zend_hash_num_elements(Z_ARRVAL_PP(name))
-                       &&      count == zend_hash_num_elements(Z_ARRVAL_PP(size))
-                       &&      count == zend_hash_num_elements(Z_ARRVAL_PP(type))
-                       &&      count == zend_hash_num_elements(Z_ARRVAL_PP(error))
-                       ) {
-                               zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp_name) TSRMLS_CC, grab_file, 6, zfiles, key, name, size, type, error);
-                       } else {
-                               /* wat?! */
-                               return ZEND_HASH_APPLY_STOP;
-                       }
-               } else  {
-                       zval *cpy, **tmp;
-
-                       MAKE_STD_ZVAL(cpy);
-                       MAKE_COPY_ZVAL(val, cpy);
-                       if (SUCCESS == zend_hash_find(Z_ARRVAL_P(cpy), ZEND_STRS("tmp_name"), (void *) &tmp)) {
-                               Z_ADDREF_PP(tmp);
-                               add_assoc_zval_ex(cpy, ZEND_STRS("file"), *tmp);
-                               zend_hash_del_key_or_index(Z_ARRVAL_P(cpy), ZEND_STRS("tmp_name"), 0, HASH_DEL_KEY);
-                       }
-                       if (key->nKeyLength > 0) {
-                               zend_hash_quick_update(Z_ARRVAL_P(zfiles), key->arKey, key->nKeyLength, key->h, (void *) &cpy, sizeof(zval *), NULL);
-                       } else {
-                               zend_hash_index_update(Z_ARRVAL_P(zfiles), key->h, (void *) &cpy, sizeof(zval *), NULL);
-                       }
-               }
-       }
-
-       return ZEND_HASH_APPLY_KEEP;
-}
-
-#define PHP_HTTP_ENV_REQUEST_OBJECT_INIT(obj) \
-       do { \
-               if (!obj->message) { \
-                       obj->message = php_http_message_init_env(NULL, PHP_HTTP_REQUEST TSRMLS_CC); \
-               } \
-       } while(0)
-
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvRequest___construct, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnvRequest, __construct)
-{
-       php_http_message_object_t *obj;
-       zval *zsg, *zqs;
-
-       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-       obj->body = NULL;
-
-       php_http_expect(obj->message = php_http_message_init_env(obj->message, PHP_HTTP_REQUEST TSRMLS_CC), unexpected_val, return);
-
-       zsg = php_http_env_get_superglobal(ZEND_STRL("_GET") TSRMLS_CC);
-       MAKE_STD_ZVAL(zqs);
-       object_init_ex(zqs, php_http_querystring_class_entry);
-       php_http_expect(SUCCESS == php_http_querystring_ctor(zqs, zsg TSRMLS_CC), unexpected_val,
-                       zval_ptr_dtor(&zqs);
-                       return;
-       );
-       zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("query"), zqs TSRMLS_CC);
-       zval_ptr_dtor(&zqs);
-
-       zsg = php_http_env_get_superglobal(ZEND_STRL("_POST") TSRMLS_CC);
-       MAKE_STD_ZVAL(zqs);
-       object_init_ex(zqs, php_http_querystring_class_entry);
-       php_http_expect(SUCCESS == php_http_querystring_ctor(zqs, zsg TSRMLS_CC), unexpected_val,
-                       zval_ptr_dtor(&zqs);
-                       return;
-       );
-       zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("form"), zqs TSRMLS_CC);
-       zval_ptr_dtor(&zqs);
-
-       zsg = php_http_env_get_superglobal(ZEND_STRL("_COOKIE") TSRMLS_CC);
-       MAKE_STD_ZVAL(zqs);
-       object_init_ex(zqs, php_http_querystring_class_entry);
-       php_http_expect(SUCCESS == php_http_querystring_ctor(zqs, zsg TSRMLS_CC), unexpected_val,
-                       zval_ptr_dtor(&zqs);
-                       return;
-       );
-       zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("cookie"), zqs TSRMLS_CC);
-       zval_ptr_dtor(&zqs);
-
-       MAKE_STD_ZVAL(zqs);
-       array_init(zqs);
-       if ((zsg = php_http_env_get_superglobal(ZEND_STRL("_FILES") TSRMLS_CC))) {
-               zend_hash_apply_with_arguments(Z_ARRVAL_P(zsg) TSRMLS_CC, grab_files, 1, zqs);
-       }
-       zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("files"), zqs TSRMLS_CC);
-       zval_ptr_dtor(&zqs);
-}
-
-#define call_querystring_get(prop) \
-       do {\
-               zend_fcall_info fci; \
-               zend_fcall_info_cache fcc; \
-               zval *rv = NULL, mn, ***args = ecalloc(sizeof(zval **), ZEND_NUM_ARGS()); \
-               zval *qs = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL(prop), 0 TSRMLS_CC); \
-                \
-               INIT_PZVAL(&mn); \
-               array_init(&mn); \
-               Z_ADDREF_P(qs); \
-               add_next_index_zval(&mn, qs); \
-               add_next_index_stringl(&mn, ZEND_STRL("get"), 1); \
-               zend_fcall_info_init(&mn, 0, &fci, &fcc, NULL, NULL TSRMLS_CC); \
-               zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args); \
-               zend_fcall_info_argp(&fci TSRMLS_CC, ZEND_NUM_ARGS(), args); \
-               zend_fcall_info_call(&fci, &fcc, &rv, NULL TSRMLS_CC); \
-               zend_fcall_info_args_clear(&fci, 1); \
-               efree(args); \
-               zval_dtor(&mn); \
-               if (rv) { \
-                       RETVAL_ZVAL(rv, 0, 1); \
-               } \
-       } while(0);
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvRequest_getForm, 0, 0, 0)
-       ZEND_ARG_INFO(0, name)
-       ZEND_ARG_INFO(0, type)
-       ZEND_ARG_INFO(0, defval)
-       ZEND_ARG_INFO(0, delete)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnvRequest, getForm)
-{
-       if (ZEND_NUM_ARGS()) {
-               call_querystring_get("form");
-       } else {
-               zval *zform = zend_read_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("form"), 0 TSRMLS_CC);
-               RETURN_ZVAL(zform, 1, 0);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvRequest_getQuery, 0, 0, 0)
-       ZEND_ARG_INFO(0, name)
-       ZEND_ARG_INFO(0, type)
-       ZEND_ARG_INFO(0, defval)
-       ZEND_ARG_INFO(0, delete)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnvRequest, getQuery)
-{
-       if (ZEND_NUM_ARGS()) {
-               call_querystring_get("query");
-       } else {
-               zval *zquery = zend_read_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("query"), 0 TSRMLS_CC);
-               RETURN_ZVAL(zquery, 1, 0);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvRequest_getCookie, 0, 0, 0)
-       ZEND_ARG_INFO(0, name)
-       ZEND_ARG_INFO(0, type)
-       ZEND_ARG_INFO(0, defval)
-       ZEND_ARG_INFO(0, delete)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnvRequest, getCookie)
-{
-       if (ZEND_NUM_ARGS()) {
-               call_querystring_get("cookie");
-       } else {
-               zval *zcookie = zend_read_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("cookie"), 0 TSRMLS_CC);
-               RETURN_ZVAL(zcookie, 1, 0);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvRequest_getFiles, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnvRequest, getFiles)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               zval *zfiles = zend_read_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("files"), 0 TSRMLS_CC);
-               RETURN_ZVAL(zfiles, 1, 0);
-       }
-}
-
-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, 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)
-       PHP_ME(HttpEnvRequest, getFiles,     ai_HttpEnvRequest_getFiles,     ZEND_ACC_PUBLIC)
-       EMPTY_FUNCTION_ENTRY
-};
-
-zend_class_entry *php_http_env_request_class_entry;
-
-PHP_MINIT_FUNCTION(http_env_request)
-{
-       zend_class_entry ce = {0};
-
-       INIT_NS_CLASS_ENTRY(ce, "http\\Env", "Request", php_http_env_request_methods);
-       php_http_env_request_class_entry = zend_register_internal_class_ex(&ce, php_http_message_class_entry, NULL TSRMLS_CC);
-
-       zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("query"), ZEND_ACC_PROTECTED TSRMLS_CC);
-       zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("form"), ZEND_ACC_PROTECTED TSRMLS_CC);
-       zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("cookie"), ZEND_ACC_PROTECTED TSRMLS_CC);
-       zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("files"), ZEND_ACC_PROTECTED TSRMLS_CC);
-
-       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/php_http_env_request.h b/php_http_env_request.h
deleted file mode 100644 (file)
index a97836b..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_ENV_REQUEST_H
-#define PHP_HTTP_ENV_REQUEST_H
-
-PHP_HTTP_API zend_class_entry *php_http_env_request_class_entry;
-PHP_MINIT_FUNCTION(http_env_request);
-
-#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/php_http_env_response.c b/php_http_env_response.c
deleted file mode 100644 (file)
index 20a31eb..0000000
+++ /dev/null
@@ -1,1513 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-static void set_option(zval *options, const char *name_str, size_t name_len, int type, void *value_ptr, size_t value_len TSRMLS_DC)
-{
-       if (Z_TYPE_P(options) == IS_OBJECT) {
-               if (value_ptr) {
-                       switch (type) {
-                               case IS_DOUBLE:
-                                       zend_update_property_double(Z_OBJCE_P(options), options, name_str, name_len, *(double *)value_ptr TSRMLS_CC);
-                                       break;
-                               case IS_LONG:
-                                       zend_update_property_long(Z_OBJCE_P(options), options, name_str, name_len, *(long *)value_ptr TSRMLS_CC);
-                                       break;
-                               case IS_STRING:
-                                       zend_update_property_stringl(Z_OBJCE_P(options), options, name_str, name_len, value_ptr, value_len TSRMLS_CC);
-                                       break;
-                               case IS_ARRAY:
-                               case IS_OBJECT:
-                                       zend_update_property(Z_OBJCE_P(options), options, name_str, name_len, value_ptr TSRMLS_CC);
-                                       break;
-                       }
-               } else {
-                       zend_update_property_null(Z_OBJCE_P(options), options, name_str, name_len TSRMLS_CC);
-               }
-       } else {
-               convert_to_array(options);
-               if (value_ptr) {
-                       switch (type) {
-                               case IS_DOUBLE:
-                                       add_assoc_double_ex(options, name_str, name_len + 1, *(double *)value_ptr);
-                                       break;
-                               case IS_LONG:
-                                       add_assoc_long_ex(options, name_str, name_len + 1, *(long *)value_ptr);
-                                       break;
-                               case IS_STRING: {
-                                       char *value = estrndup(value_ptr, value_len);
-                                       add_assoc_stringl_ex(options, name_str, name_len + 1, value, value_len, 0);
-                                       break;
-                               case IS_ARRAY:
-                               case IS_OBJECT:
-                                       Z_ADDREF_P(value_ptr);
-                                       add_assoc_zval_ex(options, name_str, name_len + 1, value_ptr);
-                                       break;
-                               }
-                       }
-               } else {
-                       add_assoc_null_ex(options, name_str, name_len + 1);
-               }
-       }
-}
-static zval *get_option(zval *options, const char *name_str, size_t name_len TSRMLS_DC)
-{
-       zval *val, **valptr;
-
-       if (Z_TYPE_P(options) == IS_OBJECT) {
-               val = zend_read_property(Z_OBJCE_P(options), options, name_str, name_len, 0 TSRMLS_CC);
-       } else {
-               if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(options), name_str, name_len + 1, (void *) &valptr)) {
-                       val = *valptr;
-               } else {
-                       val = NULL;
-               }
-       }
-       if (val) {
-               Z_ADDREF_P(val);
-       }
-       return val;
-}
-static php_http_message_body_t *get_body(zval *options TSRMLS_DC)
-{
-       zval *zbody;
-       php_http_message_body_t *body = NULL;
-
-       if ((zbody = get_option(options, ZEND_STRL("body") TSRMLS_CC))) {
-               if ((Z_TYPE_P(zbody) == IS_OBJECT) && instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_class_entry TSRMLS_CC)) {
-                       php_http_message_body_object_t *body_obj = zend_object_store_get_object(zbody TSRMLS_CC);
-
-                       body = body_obj->body;
-               }
-               zval_ptr_dtor(&zbody);
-       }
-
-       return body;
-}
-static php_http_message_t *get_request(zval *options TSRMLS_DC)
-{
-       zval *zrequest;
-       php_http_message_t *request = NULL;
-
-       if ((zrequest = get_option(options, ZEND_STRL("request") TSRMLS_CC))) {
-               if (Z_TYPE_P(zrequest) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zrequest), php_http_message_class_entry TSRMLS_CC)) {
-                       php_http_message_object_t *request_obj = zend_object_store_get_object(zrequest TSRMLS_CC);
-
-                       request = request_obj->message;
-               }
-               zval_ptr_dtor(&zrequest);
-       }
-
-       return request;
-}
-static void set_cookie(zval *options, zval *zcookie_new TSRMLS_DC)
-{
-       HashPosition pos;
-       zval *zcookies_set;
-       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-       php_http_cookie_object_t *obj = zend_object_store_get_object(zcookie_new TSRMLS_CC);
-
-       zcookies_set = get_option(options, ZEND_STRL("cookies") TSRMLS_CC);
-       if (!zcookies_set || Z_TYPE_P(zcookies_set) != IS_ARRAY) {
-               if (zcookies_set) {
-                       zval_ptr_dtor(&zcookies_set);
-               }
-               MAKE_STD_ZVAL(zcookies_set);
-               array_init_size(zcookies_set, zend_hash_num_elements(&obj->list->cookies));
-       } else {
-               SEPARATE_ZVAL(&zcookies_set);
-       }
-
-       FOREACH_HASH_KEY(pos, &obj->list->cookies, key) {
-               Z_ADDREF_P(zcookie_new);
-               if (key.type == HASH_KEY_IS_STRING) {
-                       add_assoc_zval_ex(zcookies_set, key.str, key.len, zcookie_new);
-               } else {
-                       add_index_zval(zcookies_set, key.num, zcookie_new);
-               }
-       }
-
-       set_option(options, ZEND_STRL("cookies"), IS_ARRAY, zcookies_set, 0 TSRMLS_CC);
-       zval_ptr_dtor(&zcookies_set);
-}
-
-php_http_cache_status_t php_http_env_is_response_cached_by_etag(zval *options, const char *header_str, size_t header_len, php_http_message_t *request TSRMLS_DC)
-{
-       php_http_cache_status_t ret = PHP_HTTP_CACHE_NO;
-       int free_etag = 0;
-       char *header = NULL, *etag;
-       php_http_message_body_t *body;
-       zval *zetag;
-
-
-       if (!(body = get_body(options TSRMLS_CC))) {
-               return ret;
-       }
-
-       if ((zetag = get_option(options, ZEND_STRL("etag") TSRMLS_CC))) {
-               zval *zetag_copy = php_http_ztyp(IS_STRING, zetag);
-               zval_ptr_dtor(&zetag);
-               zetag = zetag_copy;
-       }
-
-       if (zetag && Z_STRLEN_P(zetag)) {
-               etag = Z_STRVAL_P(zetag);
-       } else if ((etag = php_http_message_body_etag(body))) {
-               set_option(options, ZEND_STRL("etag"), IS_STRING, etag, strlen(etag) TSRMLS_CC);
-               free_etag = 1;
-       }
-
-       if (zetag) {
-               zval_ptr_dtor(&zetag);
-       }
-
-       if (etag && (header = php_http_env_get_request_header(header_str, header_len, NULL, request TSRMLS_CC))) {
-               ret = php_http_match(header, etag, PHP_HTTP_MATCH_WORD)  ? PHP_HTTP_CACHE_HIT : PHP_HTTP_CACHE_MISS;
-       }
-
-       if (free_etag) {
-               efree(etag);
-       }
-
-       PTR_FREE(header);
-       return ret;
-}
-
-php_http_cache_status_t php_http_env_is_response_cached_by_last_modified(zval *options, const char *header_str, size_t header_len, php_http_message_t *request TSRMLS_DC)
-{
-       php_http_cache_status_t ret = PHP_HTTP_CACHE_NO;
-       char *header;
-       time_t ums, lm = 0;
-       php_http_message_body_t *body;
-       zval *zlm;
-
-       if (!(body = get_body(options TSRMLS_CC))) {
-               return ret;
-       }
-
-       if ((zlm = get_option(options, ZEND_STRL("lastModified") TSRMLS_CC))) {
-               zval *zlm_copy = php_http_ztyp(IS_LONG, zlm);
-               zval_ptr_dtor(&zlm);
-               zlm = zlm_copy;
-       }
-
-       if (zlm && Z_LVAL_P(zlm) > 0) {
-               lm = Z_LVAL_P(zlm);
-       } else {
-               lm = php_http_message_body_mtime(body);
-               set_option(options, ZEND_STRL("lastModified"), IS_LONG, &lm, 0 TSRMLS_CC);
-       }
-
-       if (zlm) {
-               zval_ptr_dtor(&zlm);
-       }
-
-       if ((header = php_http_env_get_request_header(header_str, header_len, NULL, request TSRMLS_CC))) {
-               ums = php_parse_date(header, NULL);
-
-               if (ums > 0 && ums >= lm) {
-                       ret = PHP_HTTP_CACHE_HIT;
-               } else {
-                       ret = PHP_HTTP_CACHE_MISS;
-               }
-       }
-
-       PTR_FREE(header);
-       return ret;
-}
-
-static zend_bool php_http_env_response_is_cacheable(php_http_env_response_t *r, php_http_message_t *request)
-{
-       long status = r->ops->get_status(r);
-       TSRMLS_FETCH_FROM_CTX(r->ts);
-
-       if (status && status / 100 != 2) {
-               return 0;
-       }
-
-       if (php_http_env_got_request_header(ZEND_STRL("Authorization"), request TSRMLS_CC)) {
-               return 0;
-       }
-
-       if (-1 == php_http_select_str(php_http_env_get_request_method(request TSRMLS_CC), 2, "HEAD", "GET")) {
-               return 0;
-       }
-
-       return 1;
-}
-
-static size_t output(void *context, char *buf, size_t len TSRMLS_DC)
-{
-       php_http_env_response_t *r = context;
-
-       if (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) {
-               r->ops->flush(r);
-               php_http_sleep(r->throttle.delay);
-       }
-       return len;
-}
-
-#define php_http_env_response_send_done(r) php_http_env_response_send_data((r), NULL, 0)
-static ZEND_RESULT_CODE php_http_env_response_send_data(php_http_env_response_t *r, const char *buf, size_t len)
-{
-       size_t chunks_sent, chunk = r->throttle.chunk ? r->throttle.chunk : PHP_HTTP_SENDBUF_SIZE;
-       TSRMLS_FETCH_FROM_CTX(r->ts);
-
-       if (r->content.encoder) {
-               char *enc_str = NULL;
-               size_t enc_len = 0;
-
-               if (buf) {
-                       if (SUCCESS != php_http_encoding_stream_update(r->content.encoder, buf, len, &enc_str, &enc_len)) {
-                               return FAILURE;
-                       }
-               } else {
-                       if (SUCCESS != php_http_encoding_stream_finish(r->content.encoder, &enc_str, &enc_len)) {
-                               return FAILURE;
-                       }
-               }
-
-               if (!enc_str) {
-                       return SUCCESS;
-               }
-               chunks_sent = php_http_buffer_chunked_output(&r->buffer, enc_str, enc_len, buf ? chunk : 0, output, r TSRMLS_CC);
-               PTR_FREE(enc_str);
-       } else {
-               chunks_sent = php_http_buffer_chunked_output(&r->buffer, buf, len, buf ? chunk : 0, output, r TSRMLS_CC);
-       }
-
-       return chunks_sent != (size_t) -1 ? SUCCESS : FAILURE;
-}
-
-php_http_env_response_t *php_http_env_response_init(php_http_env_response_t *r, zval *options, php_http_env_response_ops_t *ops, void *init_arg TSRMLS_DC)
-{
-       zend_bool free_r;
-
-       if ((free_r = !r)) {
-               r = emalloc(sizeof(*r));
-       }
-       memset(r, 0, sizeof(*r));
-
-       if (ops) {
-               r->ops = ops;
-       } else {
-               r->ops = php_http_env_response_get_sapi_ops();
-       }
-
-       r->buffer = php_http_buffer_init(NULL);
-
-       Z_ADDREF_P(options);
-       r->options = options;
-
-       TSRMLS_SET_CTX(r->ts);
-
-       if (r->ops->init && (SUCCESS != r->ops->init(r, init_arg))) {
-               if (free_r) {
-                       php_http_env_response_free(&r);
-               } else {
-                       php_http_env_response_dtor(r);
-                       r = NULL;
-               }
-       }
-
-       return r;
-}
-
-void php_http_env_response_dtor(php_http_env_response_t *r)
-{
-       if (r->ops->dtor) {
-               r->ops->dtor(r);
-       }
-       php_http_buffer_free(&r->buffer);
-       zval_ptr_dtor(&r->options);
-       PTR_FREE(r->content.type);
-       PTR_FREE(r->content.encoding);
-       if (r->content.encoder) {
-               php_http_encoding_stream_free(&r->content.encoder);
-       }
-}
-
-void php_http_env_response_free(php_http_env_response_t **r)
-{
-       if (*r) {
-               php_http_env_response_dtor(*r);
-               efree(*r);
-               *r = NULL;
-       }
-}
-
-static ZEND_RESULT_CODE php_http_env_response_send_head(php_http_env_response_t *r, php_http_message_t *request)
-{
-       ZEND_RESULT_CODE ret = SUCCESS;
-       zval *zoption, *options = r->options;
-       TSRMLS_FETCH_FROM_CTX(r->ts);
-
-       if (r->done) {
-               return ret;
-       }
-
-       if ((zoption = get_option(options, ZEND_STRL("headers") TSRMLS_CC))) {
-               if (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 TSRMLS_CC);
-               }
-               zval_ptr_dtor(&zoption);
-       }
-
-       if (ret != SUCCESS) {
-               return ret;
-       }
-
-       if ((zoption = get_option(options, ZEND_STRL("responseCode") TSRMLS_CC))) {
-               zval *zoption_copy = php_http_ztyp(IS_LONG, zoption);
-
-               zval_ptr_dtor(&zoption);
-               if (Z_LVAL_P(zoption_copy) > 0) {
-                       ret = r->ops->set_status(r, Z_LVAL_P(zoption_copy));
-               }
-               zval_ptr_dtor(&zoption_copy);
-       }
-
-       if (ret != SUCCESS) {
-               return ret;
-       }
-
-       if ((zoption = get_option(options, ZEND_STRL("httpVersion") TSRMLS_CC))) {
-               php_http_version_t v;
-               zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
-
-               zval_ptr_dtor(&zoption);
-               if (Z_STRLEN_P(zoption_copy) && php_http_version_parse(&v, Z_STRVAL_P(zoption_copy) TSRMLS_CC)) {
-                       ret = r->ops->set_protocol_version(r, &v);
-                       php_http_version_dtor(&v);
-               }
-               zval_ptr_dtor(&zoption_copy);
-       }
-
-       if (ret != SUCCESS) {
-               return ret;
-       }
-
-       if ((zoption = get_option(options, ZEND_STRL("cookies") TSRMLS_CC))) {
-               if (Z_TYPE_P(zoption) == IS_ARRAY) {
-                       HashPosition pos;
-                       zval **zcookie;
-
-                       FOREACH_VAL(pos, zoption, zcookie) {
-                               if (Z_TYPE_PP(zcookie) == IS_OBJECT && instanceof_function(Z_OBJCE_PP(zcookie), php_http_cookie_class_entry TSRMLS_CC)) {
-                                       php_http_cookie_object_t *obj = zend_object_store_get_object(*zcookie TSRMLS_CC);
-                                       char *str;
-                                       size_t len;
-
-                                       php_http_cookie_list_to_string(obj->list, &str, &len);
-                                       if (SUCCESS != (ret = r->ops->add_header(r, "Set-Cookie: %s", str))) {
-                                               efree(str);
-                                               break;
-                                       }
-                                       efree(str);
-                               }
-                       }
-               }
-               zval_ptr_dtor(&zoption);
-       }
-
-       if (ret != SUCCESS) {
-               return ret;
-       }
-
-       if ((zoption = get_option(options, ZEND_STRL("contentType") TSRMLS_CC))) {
-               zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
-
-               zval_ptr_dtor(&zoption);
-               if (Z_STRLEN_P(zoption_copy) && strchr(Z_STRVAL_P(zoption_copy), '/')) {
-                       if (SUCCESS == (ret = r->ops->set_header(r, "Content-Type: %.*s", Z_STRLEN_P(zoption_copy), Z_STRVAL_P(zoption_copy)))) {
-                               r->content.type = estrndup(Z_STRVAL_P(zoption_copy), Z_STRLEN_P(zoption_copy));
-                       }
-               }
-               zval_ptr_dtor(&zoption_copy);
-       }
-
-       if (ret != SUCCESS) {
-               return ret;
-       }
-
-       if (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 TSRMLS_CC, 1, &range)
-                               &&      2 == php_http_array_list(Z_ARRVAL_PP(range) TSRMLS_CC, 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_PP(begin), Z_LVAL_PP(end), r->content.length);
-                               }
-                       } else {
-                               /* this should never happen */
-                               zend_hash_destroy(&r->range.values);
-                               ret = FAILURE;
-                       }
-               } else {
-                       php_http_boundary(r->range.boundary, sizeof(r->range.boundary) TSRMLS_CC);
-                       if (SUCCESS == (ret = r->ops->set_status(r, 206))) {
-                               ret = r->ops->set_header(r, "Content-Type: multipart/byteranges; boundary=%s", r->range.boundary);
-                       }
-               }
-       } else {
-               if ((zoption = get_option(options, ZEND_STRL("cacheControl") TSRMLS_CC))) {
-                       zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
-
-                       zval_ptr_dtor(&zoption);
-                       if (Z_STRLEN_P(zoption_copy)) {
-                               ret = r->ops->set_header(r, "Cache-Control: %.*s", Z_STRLEN_P(zoption_copy), Z_STRVAL_P(zoption_copy));
-                       }
-                       zval_ptr_dtor(&zoption_copy);
-               }
-
-               if (ret != SUCCESS) {
-                       return ret;
-               }
-
-               if ((zoption = get_option(options, ZEND_STRL("contentDisposition") TSRMLS_CC))) {
-                       zval *zoption_copy = php_http_ztyp(IS_ARRAY, zoption);
-                       php_http_buffer_t buf;
-
-                       php_http_buffer_init(&buf);
-                       if (php_http_params_to_string(&buf, Z_ARRVAL_P(zoption_copy), ZEND_STRL(","), ZEND_STRL(";"), ZEND_STRL("="), PHP_HTTP_PARAMS_DEFAULT TSRMLS_CC)) {
-                               if (buf.used) {
-                                       ret = r->ops->set_header(r, "Content-Disposition: %.*s", buf.used, buf.data);
-                               }
-                       }
-
-                       php_http_buffer_dtor(&buf);
-                       zval_ptr_dtor(&zoption_copy);
-                       zval_ptr_dtor(&zoption);
-               }
-
-               if (ret != SUCCESS) {
-                       return ret;
-               }
-
-               if ((zoption = get_option(options, ZEND_STRL("contentEncoding") TSRMLS_CC))) {
-                       zval *zoption_copy = php_http_ztyp(IS_LONG, zoption);
-                       zval zsupported;
-                       HashTable *result = NULL;
-
-                       zval_ptr_dtor(&zoption);
-                       switch (Z_LVAL_P(zoption_copy)) {
-                               case PHP_HTTP_CONTENT_ENCODING_GZIP:
-                                       INIT_PZVAL(&zsupported);
-                                       array_init(&zsupported);
-                                       add_next_index_stringl(&zsupported, ZEND_STRL("none"), 1);
-                                       add_next_index_stringl(&zsupported, ZEND_STRL("gzip"), 1);
-                                       add_next_index_stringl(&zsupported, ZEND_STRL("deflate"), 1);
-
-                                       if ((result = php_http_negotiate_encoding(Z_ARRVAL(zsupported), request TSRMLS_CC))) {
-                                               char *key_str = NULL;
-                                               uint key_len = 0;
-
-                                               zend_hash_internal_pointer_reset(result);
-                                               if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(result, &key_str, &key_len, NULL, 0, NULL)) {
-                                                       if (!strcmp(key_str, "gzip")) {
-                                                               if (!(r->content.encoder = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), PHP_HTTP_DEFLATE_TYPE_GZIP TSRMLS_CC))) {
-                                                                       ret = FAILURE;
-                                                               } else if (SUCCESS == (ret = r->ops->set_header(r, "Content-Encoding: gzip"))) {
-                                                                       r->content.encoding = estrndup(key_str, key_len - 1);
-                                                               }
-                                                       } else if (!strcmp(key_str, "deflate")) {
-                                                               if (!(r->content.encoder = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), PHP_HTTP_DEFLATE_TYPE_ZLIB TSRMLS_CC))) {
-                                                                       ret = FAILURE;
-                                                               } else if (SUCCESS == (ret = r->ops->set_header(r, "Content-Encoding: deflate"))) {
-                                                                       r->content.encoding = estrndup(key_str, key_len - 1);
-                                                               }
-                                                       } else {
-                                                               ret = r->ops->del_header(r, ZEND_STRL("Content-Encoding"));
-                                                       }
-
-                                                       if (SUCCESS == ret) {
-                                                               ret = r->ops->add_header(r, "Vary: Accept-Encoding");
-                                                       }
-                                               }
-
-                                               zend_hash_destroy(result);
-                                               FREE_HASHTABLE(result);
-                                       }
-
-                                       zval_dtor(&zsupported);
-                                       break;
-
-                               case PHP_HTTP_CONTENT_ENCODING_NONE:
-                               default:
-                                       ret = r->ops->del_header(r, ZEND_STRL("Content-Encoding"));
-                                       break;
-                       }
-                       zval_ptr_dtor(&zoption_copy);
-               }
-
-               if (SUCCESS != ret) {
-                       return ret;
-               }
-
-               if (php_http_env_response_is_cacheable(r, request)) {
-                       switch (php_http_env_is_response_cached_by_etag(options, ZEND_STRL("If-None-Match"), request TSRMLS_CC)) {
-                               case PHP_HTTP_CACHE_MISS:
-                                       break;
-
-                               case PHP_HTTP_CACHE_NO:
-                                       if (PHP_HTTP_CACHE_HIT != php_http_env_is_response_cached_by_last_modified(options, ZEND_STRL("If-Modified-Since"), request TSRMLS_CC)) {
-                                               break;
-                                       }
-                                       /*  no break */
-
-                               case PHP_HTTP_CACHE_HIT:
-                                       ret = r->ops->set_status(r, 304);
-                                       r->done = 1;
-                                       break;
-                       }
-
-                       if ((zoption = get_option(options, ZEND_STRL("etag") TSRMLS_CC))) {
-                               zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
-
-                               zval_ptr_dtor(&zoption);
-                               if (*Z_STRVAL_P(zoption_copy) != '"' && strncmp(Z_STRVAL_P(zoption_copy), "W/\"", 3)) {
-                                       ret = r->ops->set_header(r, "ETag: \"%s\"", Z_STRVAL_P(zoption_copy));
-                               } else {
-                                       ret = r->ops->set_header(r, "ETag: %s", Z_STRVAL_P(zoption_copy));
-                               }
-                               zval_ptr_dtor(&zoption_copy);
-                       }
-                       if ((zoption = get_option(options, ZEND_STRL("lastModified") TSRMLS_CC))) {
-                               zval *zoption_copy = php_http_ztyp(IS_LONG, zoption);
-
-                               zval_ptr_dtor(&zoption);
-                               if (Z_LVAL_P(zoption_copy)) {
-                                       char *date = php_format_date(ZEND_STRL(PHP_HTTP_DATE_FORMAT), Z_LVAL_P(zoption_copy), 0 TSRMLS_CC);
-                                       if (date) {
-                                               ret = r->ops->set_header(r, "Last-Modified: %s", date);
-                                               efree(date);
-                                       }
-                               }
-                               zval_ptr_dtor(&zoption_copy);
-                       }
-               }
-       }
-
-       return ret;
-}
-
-static ZEND_RESULT_CODE php_http_env_response_send_body(php_http_env_response_t *r)
-{
-       ZEND_RESULT_CODE ret = SUCCESS;
-       zval *zoption;
-       php_http_message_body_t *body;
-       TSRMLS_FETCH_FROM_CTX(r->ts);
-
-       if (r->done) {
-               return ret;
-       }
-
-       if ((body = get_body(r->options TSRMLS_CC))) {
-               if ((zoption = get_option(r->options, ZEND_STRL("throttleDelay") TSRMLS_CC))) {
-                       if (Z_TYPE_P(zoption) == IS_DOUBLE) {
-                               r->throttle.delay =  Z_DVAL_P(zoption);
-                       }
-                       zval_ptr_dtor(&zoption);
-               }
-               if ((zoption = get_option(r->options, ZEND_STRL("throttleChunk") TSRMLS_CC))) {
-                       if (Z_TYPE_P(zoption) == IS_LONG) {
-                               r->throttle.chunk = Z_LVAL_P(zoption);
-                       }
-                       zval_ptr_dtor(&zoption);
-               }
-
-               if (r->range.status == PHP_HTTP_RANGE_OK) {
-                       if (zend_hash_num_elements(&r->range.values) == 1) {
-                               /* single range */
-                               zval **range, **begin, **end;
-
-                               if (    1 == php_http_array_list(&r->range.values TSRMLS_CC, 1, &range)
-                                       &&      2 == php_http_array_list(Z_ARRVAL_PP(range) TSRMLS_CC, 2, &begin, &end)
-                               ) {
-                                       /* send chunk */
-                                       ret = php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_env_response_send_data, r, Z_LVAL_PP(begin), Z_LVAL_PP(end) - Z_LVAL_PP(begin) + 1);
-                                       if (ret == SUCCESS) {
-                                               ret = php_http_env_response_send_done(r);
-                                       }
-                                       zend_hash_destroy(&r->range.values);
-                               } else {
-                                       /* this should never happen */
-                                       zend_hash_destroy(&r->range.values);
-                                       r->ops->set_status(r, 500);
-                                       ret = FAILURE;
-                               }
-
-                       } else {
-                               /* send multipart/byte-ranges message */
-                               HashPosition pos;
-                               zval **chunk;
-
-                               FOREACH_HASH_VAL(pos, &r->range.values, chunk) {
-                                       zval **begin, **end;
-
-                                       if (2 == php_http_array_list(Z_ARRVAL_PP(chunk) TSRMLS_CC, 2, &begin, &end)) {
-                                               php_http_buffer_appendf(r->buffer,
-                                                               PHP_HTTP_CRLF
-                                                               "--%s" PHP_HTTP_CRLF
-                                                               "Content-Type: %s" PHP_HTTP_CRLF
-                                                               "Content-Range: bytes %ld-%ld/%zu" PHP_HTTP_CRLF PHP_HTTP_CRLF,
-                                                               /* - */
-                                                               r->range.boundary,
-                                                               r->content.type ? r->content.type : "application/octet-stream",
-                                                               Z_LVAL_PP(begin),
-                                                               Z_LVAL_PP(end),
-                                                               r->content.length
-                                               );
-                                               ret = php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_env_response_send_data, r, Z_LVAL_PP(begin), Z_LVAL_PP(end) - Z_LVAL_PP(begin) + 1);
-                                       }
-                               }
-
-                               if (ret == SUCCESS) {
-                                       php_http_buffer_appendf(r->buffer, PHP_HTTP_CRLF "--%s--", r->range.boundary);
-                                       ret = php_http_env_response_send_done(r);
-                               }
-                               zend_hash_destroy(&r->range.values);
-                       }
-
-               } else {
-                       ret = php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_env_response_send_data, r, 0, 0);
-                       if (ret == SUCCESS) {
-                               ret = php_http_env_response_send_done(r);
-                       }
-               }
-       }
-       return ret;
-}
-
-ZEND_RESULT_CODE php_http_env_response_send(php_http_env_response_t *r)
-{
-       php_http_message_t *request;
-       php_http_message_body_t *body;
-       TSRMLS_FETCH_FROM_CTX(r->ts);
-
-       request = get_request(r->options TSRMLS_CC);
-
-       /* check for ranges */
-       if ((body = get_body(r->options TSRMLS_CC))) {
-               r->content.length = php_http_message_body_size(body);
-
-               if (SUCCESS != r->ops->set_header(r, "Accept-Ranges: bytes")) {
-                       return FAILURE;
-               } else {
-                       zend_hash_init(&r->range.values, 0, NULL, ZVAL_PTR_DTOR, 0);
-                       r->range.status = php_http_env_get_request_ranges(&r->range.values, r->content.length, request TSRMLS_CC);
-
-                       switch (r->range.status) {
-                               case PHP_HTTP_RANGE_NO:
-                                       zend_hash_destroy(&r->range.values);
-                                       break;
-
-                               case PHP_HTTP_RANGE_ERR:
-                                       if (php_http_env_got_request_header(ZEND_STRL("If-Range"), request TSRMLS_CC)) {
-                                               r->range.status = PHP_HTTP_RANGE_NO;
-                                               zend_hash_destroy(&r->range.values);
-                                       } else {
-                                               r->done = 1;
-                                               zend_hash_destroy(&r->range.values);
-                                               if (SUCCESS != r->ops->set_status(r, 416)) {
-                                                       return FAILURE;
-                                               }
-                                               if (SUCCESS != r->ops->set_header(r, "Content-Range: bytes */%zu", r->content.length)) {
-                                                       return FAILURE;
-                                               }
-                                       }
-                                       break;
-
-                               case PHP_HTTP_RANGE_OK:
-                                       if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(r->options, ZEND_STRL("If-Range"), request TSRMLS_CC)
-                                       ||      PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("If-Range"), request TSRMLS_CC)
-                                       ) {
-                                               r->range.status = PHP_HTTP_RANGE_NO;
-                                               zend_hash_destroy(&r->range.values);
-                                               break;
-                                       }
-                                       if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(r->options, ZEND_STRL("If-Match"), request TSRMLS_CC)
-                                       ||      PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("If-Unmodified-Since"), request TSRMLS_CC)
-                                       ||      PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("Unless-Modified-Since"), request TSRMLS_CC)
-                                       ) {
-                                               r->done = 1;
-                                               zend_hash_destroy(&r->range.values);
-                                               if (SUCCESS != r->ops->set_status(r, 412)) {
-                                                       return FAILURE;
-                                               }
-                                               break;
-                                       }
-
-                                       break;
-                       }
-               }
-       }
-
-       if (SUCCESS != php_http_env_response_send_head(r, request)) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to send response headers");
-               return FAILURE;
-       }
-
-       if (SUCCESS != php_http_env_response_send_body(r)) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to send response body");
-               return FAILURE;
-       }
-
-       if (SUCCESS != r->ops->finish(r)) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to finish response");
-               return FAILURE;
-       }
-
-       return SUCCESS;
-}
-
-static long php_http_env_response_sapi_get_status(php_http_env_response_t *r)
-{
-       TSRMLS_FETCH_FROM_CTX(r->ts);
-
-       return php_http_env_get_response_code(TSRMLS_C);
-}
-static ZEND_RESULT_CODE php_http_env_response_sapi_set_status(php_http_env_response_t *r, long http_code)
-{
-       TSRMLS_FETCH_FROM_CTX(r->ts);
-
-       return php_http_env_set_response_code(http_code TSRMLS_CC);
-}
-static ZEND_RESULT_CODE php_http_env_response_sapi_set_protocol_version(php_http_env_response_t *r, php_http_version_t *v)
-{
-       TSRMLS_FETCH_FROM_CTX(r->ts);
-
-       return php_http_env_set_response_protocol_version(v TSRMLS_CC);
-}
-static ZEND_RESULT_CODE php_http_env_response_sapi_set_header(php_http_env_response_t *r, const char *fmt, ...)
-{
-       ZEND_RESULT_CODE ret;
-       va_list args;
-       TSRMLS_FETCH_FROM_CTX(r->ts);
-
-       va_start(args, fmt);
-       ret = php_http_env_set_response_header_va(0, 1, fmt, args TSRMLS_CC);
-       va_end(args);
-
-       return ret;
-}
-static ZEND_RESULT_CODE php_http_env_response_sapi_add_header(php_http_env_response_t *r, const char *fmt, ...)
-{
-       ZEND_RESULT_CODE ret;
-       va_list args;
-       TSRMLS_FETCH_FROM_CTX(r->ts);
-
-       va_start(args, fmt);
-       ret = php_http_env_set_response_header_va(0, 0, fmt, args TSRMLS_CC);
-       va_end(args);
-
-       return ret;
-}
-static ZEND_RESULT_CODE php_http_env_response_sapi_del_header(php_http_env_response_t *r, const char *header_str, size_t header_len)
-{
-       TSRMLS_FETCH_FROM_CTX(r->ts);
-
-       return php_http_env_set_response_header_value(0, header_str, header_len, NULL, 1 TSRMLS_CC);
-}
-static ZEND_RESULT_CODE php_http_env_response_sapi_write(php_http_env_response_t *r, const char *data_str, size_t data_len)
-{
-       TSRMLS_FETCH_FROM_CTX(r->ts);
-
-       if (0 < PHPWRITE(data_str, data_len)) {
-               return SUCCESS;
-       }
-       return FAILURE;
-}
-static ZEND_RESULT_CODE php_http_env_response_sapi_flush(php_http_env_response_t *r)
-{
-       TSRMLS_FETCH_FROM_CTX(r->ts);
-
-#if PHP_VERSION_ID >= 50400
-       if (php_output_get_level(TSRMLS_C)) {
-               php_output_flush_all(TSRMLS_C);
-       }
-       if (!(php_output_get_status(TSRMLS_C) & PHP_OUTPUT_IMPLICITFLUSH)) {
-               sapi_flush(TSRMLS_C);
-       }
-#else
-       php_end_ob_buffer(1, 1 TSRMLS_CC);
-       sapi_flush(TSRMLS_C);
-#endif
-
-       return SUCCESS;
-}
-static ZEND_RESULT_CODE php_http_env_response_sapi_finish(php_http_env_response_t *r)
-{
-       return SUCCESS;
-}
-
-static php_http_env_response_ops_t php_http_env_response_sapi_ops = {
-       NULL,
-       NULL,
-       php_http_env_response_sapi_get_status,
-       php_http_env_response_sapi_set_status,
-       php_http_env_response_sapi_set_protocol_version,
-       php_http_env_response_sapi_set_header,
-       php_http_env_response_sapi_add_header,
-       php_http_env_response_sapi_del_header,
-       php_http_env_response_sapi_write,
-       php_http_env_response_sapi_flush,
-       php_http_env_response_sapi_finish
-};
-
-php_http_env_response_ops_t *php_http_env_response_get_sapi_ops(void)
-{
-       return &php_http_env_response_sapi_ops;
-}
-
-typedef struct php_http_env_response_stream_ctx {
-       HashTable header;
-       php_http_version_t version;
-       long status_code;
-
-       php_stream *stream;
-       php_stream_filter *chunked_filter;
-       php_http_message_t *request;
-
-       unsigned started:1;
-       unsigned finished:1;
-       unsigned chunked:1;
-} php_http_env_response_stream_ctx_t;
-
-static ZEND_RESULT_CODE php_http_env_response_stream_init(php_http_env_response_t *r, void *init_arg)
-{
-       php_http_env_response_stream_ctx_t *ctx;
-       size_t buffer_size = 0x1000;
-       TSRMLS_FETCH_FROM_CTX(r->ts);
-
-       ctx = ecalloc(1, sizeof(*ctx));
-
-       ctx->stream = init_arg;
-       if (!ctx->stream || SUCCESS != zend_list_addref(ctx->stream->rsrc_id)) {
-               efree(ctx);
-               return FAILURE;
-       }
-       php_stream_set_option(ctx->stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_FULL, &buffer_size);
-       zend_hash_init(&ctx->header, 0, NULL, ZVAL_PTR_DTOR, 0);
-       php_http_version_init(&ctx->version, 1, 1 TSRMLS_CC);
-       ctx->status_code = 200;
-       ctx->chunked = 1;
-       ctx->request = get_request(r->options TSRMLS_CC);
-
-       /* 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) {
-               ctx->version.minor = 0;
-       }
-
-       r->ctx = ctx;
-
-       return SUCCESS;
-}
-static void php_http_env_response_stream_dtor(php_http_env_response_t *r)
-{
-       php_http_env_response_stream_ctx_t *ctx = r->ctx;
-       TSRMLS_FETCH_FROM_CTX(r->ts);
-
-       if (ctx->chunked_filter) {
-               ctx->chunked_filter = php_stream_filter_remove(ctx->chunked_filter, 1 TSRMLS_CC);
-       }
-       zend_hash_destroy(&ctx->header);
-       zend_list_delete(ctx->stream->rsrc_id);
-       efree(ctx);
-       r->ctx = NULL;
-}
-static void php_http_env_response_stream_header(php_http_env_response_stream_ctx_t *ctx, HashTable *header, php_http_buffer_t *buf TSRMLS_DC)
-{
-       HashPosition pos;
-       zval **val;
-
-       FOREACH_HASH_VAL(pos, header, val) {
-               if (Z_TYPE_PP(val) == IS_ARRAY) {
-                       php_http_env_response_stream_header(ctx, Z_ARRVAL_PP(val), buf TSRMLS_CC);
-               } else {
-                       zval *tmp = php_http_ztyp(IS_STRING, *val);
-
-                       if (ctx->chunked) {
-                               /* disable chunked transfer encoding if we've got an explicit content-length */
-                               if (!strncasecmp(Z_STRVAL_P(tmp), "Content-Length:", lenof("Content-Length:"))) {
-                                       ctx->chunked = 0;
-                               }
-                       }
-                       php_http_buffer_append(buf, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
-                       php_http_buffer_appends(buf, PHP_HTTP_CRLF);
-                       zval_ptr_dtor(&tmp);
-               }
-       }
-}
-static ZEND_RESULT_CODE php_http_env_response_stream_start(php_http_env_response_stream_ctx_t *ctx TSRMLS_DC)
-{
-       php_http_buffer_t header_buf;
-
-       if (ctx->started || ctx->finished) {
-               return FAILURE;
-       }
-
-       php_http_buffer_init(&header_buf);
-       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) {
-               ctx->chunked = 0;
-       } else if (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")) {
-               ctx->chunked = 0;
-       }
-
-       php_http_env_response_stream_header(ctx, &ctx->header, &header_buf TSRMLS_CC);
-
-       /* enable chunked transfer encoding */
-       if (ctx->chunked) {
-               php_http_buffer_appends(&header_buf, "Transfer-Encoding: chunked" PHP_HTTP_CRLF);
-       }
-       php_http_buffer_appends(&header_buf, PHP_HTTP_CRLF);
-
-       if (header_buf.used == php_stream_write(ctx->stream, header_buf.data, header_buf.used)) {
-               ctx->started = 1;
-       }
-       php_http_buffer_dtor(&header_buf);
-       php_stream_flush(ctx->stream);
-
-       if (ctx->chunked) {
-               ctx->chunked_filter = php_stream_filter_create("http.chunked_encode", NULL, 0 TSRMLS_CC);
-               php_stream_filter_append(&ctx->stream->writefilters, ctx->chunked_filter);
-       }
-
-       return ctx->started ? SUCCESS : FAILURE;
-}
-static long php_http_env_response_stream_get_status(php_http_env_response_t *r)
-{
-       php_http_env_response_stream_ctx_t *ctx = r->ctx;
-
-       return ctx->status_code;
-}
-static ZEND_RESULT_CODE php_http_env_response_stream_set_status(php_http_env_response_t *r, long http_code)
-{
-       php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
-
-       if (stream_ctx->started || stream_ctx->finished) {
-               return FAILURE;
-       }
-
-       stream_ctx->status_code = http_code;
-
-       return SUCCESS;
-}
-static ZEND_RESULT_CODE php_http_env_response_stream_set_protocol_version(php_http_env_response_t *r, php_http_version_t *v)
-{
-       php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
-
-       if (stream_ctx->started || stream_ctx->finished) {
-               return FAILURE;
-       }
-
-       memcpy(&stream_ctx->version, v, sizeof(stream_ctx->version));
-
-       return SUCCESS;
-}
-static ZEND_RESULT_CODE php_http_env_response_stream_set_header_ex(php_http_env_response_t *r, zend_bool replace, const char *fmt, va_list argv)
-{
-       php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
-       char *header_end, *header_str = NULL;
-       size_t header_len = 0;
-       zval *zheader, **zheader_ptr;
-
-       if (stream_ctx->started || stream_ctx->finished) {
-               return FAILURE;
-       }
-
-       header_len = vspprintf(&header_str, 0, fmt, argv);
-
-       if (!(header_end = strchr(header_str, ':'))) {
-               efree(header_str);
-               return FAILURE;
-       }
-
-       *header_end = '\0';
-
-       if (!replace && (SUCCESS == zend_hash_find(&stream_ctx->header, header_str, header_end - header_str + 1, (void *) &zheader_ptr))) {
-               convert_to_array(*zheader_ptr);
-               *header_end = ':';
-               return add_next_index_stringl(*zheader_ptr, header_str, header_len, 0);
-       } else {
-               MAKE_STD_ZVAL(zheader);
-               ZVAL_STRINGL(zheader, header_str, header_len, 0);
-
-               if (SUCCESS != zend_hash_update(&stream_ctx->header, header_str, header_end - header_str + 1, (void *) &zheader, sizeof(zval *), NULL)) {
-                       zval_ptr_dtor(&zheader);
-                       return FAILURE;
-               }
-
-               *header_end = ':';
-               return SUCCESS;
-       }
-}
-static ZEND_RESULT_CODE php_http_env_response_stream_set_header(php_http_env_response_t *r, const char *fmt, ...)
-{
-       ZEND_RESULT_CODE ret;
-       va_list argv;
-
-       va_start(argv, fmt);
-       ret = php_http_env_response_stream_set_header_ex(r, 1, fmt, argv);
-       va_end(argv);
-
-       return ret;
-}
-static ZEND_RESULT_CODE php_http_env_response_stream_add_header(php_http_env_response_t *r, const char *fmt, ...)
-{
-       ZEND_RESULT_CODE ret;
-       va_list argv;
-
-       va_start(argv, fmt);
-       ret = php_http_env_response_stream_set_header_ex(r, 0, fmt, argv);
-       va_end(argv);
-
-       return ret;
-}
-static ZEND_RESULT_CODE php_http_env_response_stream_del_header(php_http_env_response_t *r, const char *header_str, size_t header_len)
-{
-       php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
-
-       if (stream_ctx->started || stream_ctx->finished) {
-               return FAILURE;
-       }
-
-       zend_hash_del(&stream_ctx->header, header_str, header_len + 1);
-       return SUCCESS;
-}
-static ZEND_RESULT_CODE php_http_env_response_stream_write(php_http_env_response_t *r, const char *data_str, size_t data_len)
-{
-       php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
-       TSRMLS_FETCH_FROM_CTX(r->ts);
-
-       if (stream_ctx->finished) {
-               return FAILURE;
-       }
-       if (!stream_ctx->started) {
-               if (SUCCESS != php_http_env_response_stream_start(stream_ctx TSRMLS_CC)) {
-                       return FAILURE;
-               }
-       }
-
-       if (data_len != php_stream_write(stream_ctx->stream, data_str, data_len)) {
-               return FAILURE;
-       }
-
-       return SUCCESS;
-}
-static ZEND_RESULT_CODE php_http_env_response_stream_flush(php_http_env_response_t *r)
-{
-       php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
-       TSRMLS_FETCH_FROM_CTX(r->ts);
-
-       if (stream_ctx->finished) {
-               return FAILURE;
-       }
-       if (!stream_ctx->started) {
-               if (SUCCESS != php_http_env_response_stream_start(stream_ctx TSRMLS_CC)) {
-                       return FAILURE;
-               }
-       }
-
-       return php_stream_flush(stream_ctx->stream);
-}
-static ZEND_RESULT_CODE php_http_env_response_stream_finish(php_http_env_response_t *r)
-{
-       php_http_env_response_stream_ctx_t *ctx = r->ctx;
-       TSRMLS_FETCH_FROM_CTX(r->ts);
-
-       if (ctx->finished) {
-               return FAILURE;
-       }
-       if (!ctx->started) {
-               if (SUCCESS != php_http_env_response_stream_start(ctx TSRMLS_CC)) {
-                       return FAILURE;
-               }
-       }
-
-       php_stream_flush(ctx->stream);
-       if (ctx->chunked && ctx->chunked_filter) {
-               php_stream_filter_flush(ctx->chunked_filter, 1);
-               ctx->chunked_filter = php_stream_filter_remove(ctx->chunked_filter, 1 TSRMLS_CC);
-       }
-
-       ctx->finished = 1;
-
-       return SUCCESS;
-}
-
-static php_http_env_response_ops_t php_http_env_response_stream_ops = {
-       php_http_env_response_stream_init,
-       php_http_env_response_stream_dtor,
-       php_http_env_response_stream_get_status,
-       php_http_env_response_stream_set_status,
-       php_http_env_response_stream_set_protocol_version,
-       php_http_env_response_stream_set_header,
-       php_http_env_response_stream_add_header,
-       php_http_env_response_stream_del_header,
-       php_http_env_response_stream_write,
-       php_http_env_response_stream_flush,
-       php_http_env_response_stream_finish
-};
-
-php_http_env_response_ops_t *php_http_env_response_get_stream_ops(void)
-{
-       return &php_http_env_response_stream_ops;
-}
-
-#define PHP_HTTP_ENV_RESPONSE_OBJECT_INIT(obj) \
-       do { \
-               if (!obj->message) { \
-                       obj->message = php_http_message_init_env(NULL, PHP_HTTP_RESPONSE TSRMLS_CC); \
-               } \
-       } while (0)
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse___construct, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnvResponse, __construct)
-{
-       php_http_message_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       php_http_expect(obj->message = php_http_message_init_env(obj->message, PHP_HTTP_RESPONSE TSRMLS_CC), unexpected_val, return);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse___invoke, 0, 0, 1)
-       ZEND_ARG_INFO(0, ob_string)
-       ZEND_ARG_INFO(0, ob_flags)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnvResponse, __invoke)
-{
-       char *ob_str;
-       int ob_len;
-       long ob_flags = 0;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &ob_str, &ob_len, &ob_flags)) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_ENV_RESPONSE_OBJECT_INIT(obj);
-
-               if (!obj->body) {
-                       php_http_message_object_init_body_object(obj);
-               }
-               php_http_message_body_append(obj->message->body, ob_str, ob_len);
-#if PHP_VERSION_ID >= 50400
-               if (ob_flags & PHP_OUTPUT_HANDLER_CLEAN) {
-                       php_stream_truncate_set_size(php_http_message_body_stream(obj->message->body), 0);
-               }
-               RETURN_TRUE;
-#else
-               RETURN_EMPTY_STRING();
-#endif
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setEnvRequest, 0, 0, 1)
-       ZEND_ARG_OBJ_INFO(0, env_request, http\\Message, 1)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnvResponse, setEnvRequest)
-{
-       zval *env_req = NULL;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|O", &env_req, php_http_message_class_entry), invalid_arg, return);
-
-       set_option(getThis(), ZEND_STRL("request"), IS_OBJECT, env_req, 0 TSRMLS_CC);
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setContentType, 0, 0, 1)
-       ZEND_ARG_INFO(0, content_type)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnvResponse, setContentType)
-{
-       char *ct_str = NULL;
-       int ct_len = 0;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &ct_str, &ct_len), invalid_arg, return);
-
-       set_option(getThis(), ZEND_STRL("contentType"), IS_STRING, ct_str, ct_len TSRMLS_CC);
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setContentDisposition, 0, 0, 1)
-       ZEND_ARG_ARRAY_INFO(0, disposition_params, 1)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnvResponse, setContentDisposition)
-{
-       zval *zdisposition;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &zdisposition), invalid_arg, return);
-
-       zend_update_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("contentDisposition"), zdisposition TSRMLS_CC);
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setContentEncoding, 0, 0, 1)
-       ZEND_ARG_INFO(0, content_encoding)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnvResponse, setContentEncoding)
-{
-       long ce;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &ce), invalid_arg, return);
-
-       set_option(getThis(), ZEND_STRL("contentEncoding"), IS_LONG, &ce, 0 TSRMLS_CC);
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setCacheControl, 0, 0, 1)
-       ZEND_ARG_INFO(0, cache_control)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnvResponse, setCacheControl)
-{
-       char *cc_str = NULL;
-       int cc_len = 0;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &cc_str, &cc_len), invalid_arg, return);
-
-       set_option(getThis(), ZEND_STRL("cacheControl"), IS_STRING, cc_str, cc_len TSRMLS_CC);
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setLastModified, 0, 0, 1)
-       ZEND_ARG_INFO(0, last_modified)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnvResponse, setLastModified)
-{
-       long last_modified;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &last_modified), invalid_arg, return);
-
-       set_option(getThis(), ZEND_STRL("lastModified"), IS_LONG, &last_modified, 0 TSRMLS_CC);
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_isCachedByLastModified, 0, 0, 0)
-       ZEND_ARG_INFO(0, header_name)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnvResponse, isCachedByLastModified)
-{
-       char *header_name_str = NULL;
-       int header_name_len = 0;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) {
-               if (!header_name_str || !header_name_len) {
-                       header_name_str = "If-Modified-Since";
-                       header_name_len = lenof("If-Modified-Since");
-               }
-
-               RETURN_LONG(php_http_env_is_response_cached_by_last_modified(getThis(), header_name_str, header_name_len, get_request(getThis() TSRMLS_CC) TSRMLS_CC));
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setEtag, 0, 0, 1)
-       ZEND_ARG_INFO(0, etag)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnvResponse, setEtag)
-{
-       char *etag_str = NULL;
-       int etag_len = 0;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &etag_str, &etag_len), invalid_arg, return);
-
-       set_option(getThis(), ZEND_STRL("etag"), IS_STRING, etag_str, etag_len TSRMLS_CC);
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_isCachedByEtag, 0, 0, 0)
-       ZEND_ARG_INFO(0, header_name)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnvResponse, isCachedByEtag)
-{
-       char *header_name_str = NULL;
-       int header_name_len = 0;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) {
-               if (!header_name_str || !header_name_len) {
-                       header_name_str = "If-None-Match";
-                       header_name_len = lenof("If-None-Match");
-               }
-               RETURN_LONG(php_http_env_is_response_cached_by_etag(getThis(), header_name_str, header_name_len, get_request(getThis() TSRMLS_CC) TSRMLS_CC));
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setThrottleRate, 0, 0, 1)
-       ZEND_ARG_INFO(0, chunk_size)
-       ZEND_ARG_INFO(0, delay)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnvResponse, setThrottleRate)
-{
-       long chunk_size;
-       double delay = 1;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|d", &chunk_size, &delay), invalid_arg, return);
-
-       set_option(getThis(), ZEND_STRL("throttleDelay"), IS_DOUBLE, &delay, 0 TSRMLS_CC);
-       set_option(getThis(), ZEND_STRL("throttleChunk"), IS_LONG, &chunk_size, 0 TSRMLS_CC);
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setCookie, 0, 0, 1)
-       ZEND_ARG_INFO(0, cookie)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnvResponse, setCookie)
-{
-       zval *zcookie_new;
-       zend_error_handling zeh;
-       php_http_cookie_list_t *list = NULL;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zcookie_new), invalid_arg, return);
-
-       zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC);
-       switch (Z_TYPE_P(zcookie_new)) {
-       case IS_OBJECT:
-               if (instanceof_function(Z_OBJCE_P(zcookie_new), php_http_cookie_class_entry TSRMLS_CC)) {
-                       Z_ADDREF_P(zcookie_new);
-                       break;
-               }
-               /* no break */
-       case IS_ARRAY:
-               list = php_http_cookie_list_from_struct(NULL, zcookie_new TSRMLS_CC);
-               MAKE_STD_ZVAL(zcookie_new);
-               ZVAL_OBJVAL(zcookie_new, php_http_cookie_object_new_ex(php_http_cookie_class_entry, list, NULL TSRMLS_CC), 0);
-               break;
-
-       default:
-               zcookie_new = php_http_ztyp(IS_STRING, zcookie_new);
-               list = php_http_cookie_list_parse(NULL, Z_STRVAL_P(zcookie_new), Z_STRLEN_P(zcookie_new), 0, NULL TSRMLS_CC);
-               zval_ptr_dtor(&zcookie_new);
-               MAKE_STD_ZVAL(zcookie_new);
-               ZVAL_OBJVAL(zcookie_new, php_http_cookie_object_new_ex(php_http_cookie_class_entry, list, NULL TSRMLS_CC), 0);
-       }
-       zend_restore_error_handling(&zeh TSRMLS_CC);
-
-       set_cookie(getThis(), zcookie_new TSRMLS_CC);
-       zval_ptr_dtor(&zcookie_new);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_send, 0, 0, 0)
-       ZEND_ARG_INFO(0, stream)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpEnvResponse, send)
-{
-       zval *zstream = NULL;
-       php_stream *s = NULL;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &zstream)) {
-               /* first flush the output layer to avoid conflicting headers and output;
-                * also, ob_start($thisEnvResponse) might have been called */
-#if PHP_VERSION_ID >= 50400
-               php_output_end_all(TSRMLS_C);
-#else
-               php_end_ob_buffers(1 TSRMLS_CC);
-#endif
-
-               if (zstream) {
-                       php_http_env_response_t *r;
-
-                       php_stream_from_zval(s, &zstream);
-                       r = php_http_env_response_init(NULL, getThis(), php_http_env_response_get_stream_ops(), s TSRMLS_CC);
-                       if (!r) {
-                               RETURN_FALSE;
-                       }
-
-                       RETVAL_BOOL(SUCCESS == php_http_env_response_send(r));
-                       php_http_env_response_free(&r);
-               } else {
-                       php_http_env_response_t r;
-
-                       if (!php_http_env_response_init(&r, getThis(), NULL, NULL TSRMLS_CC)) {
-                               RETURN_FALSE;
-                       }
-
-                       RETVAL_BOOL(SUCCESS == php_http_env_response_send(&r));
-                       php_http_env_response_dtor(&r);
-               }
-       }
-}
-
-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, __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)
-       PHP_ME(HttpEnvResponse, setContentType,          ai_HttpEnvResponse_setContentType,          ZEND_ACC_PUBLIC)
-       PHP_ME(HttpEnvResponse, setContentDisposition,   ai_HttpEnvResponse_setContentDisposition,   ZEND_ACC_PUBLIC)
-       PHP_ME(HttpEnvResponse, setContentEncoding,      ai_HttpEnvResponse_setContentEncoding,      ZEND_ACC_PUBLIC)
-       PHP_ME(HttpEnvResponse, setCacheControl,         ai_HttpEnvResponse_setCacheControl,         ZEND_ACC_PUBLIC)
-       PHP_ME(HttpEnvResponse, setLastModified,         ai_HttpEnvResponse_setLastModified,         ZEND_ACC_PUBLIC)
-       PHP_ME(HttpEnvResponse, isCachedByLastModified,  ai_HttpEnvResponse_isCachedByLastModified,  ZEND_ACC_PUBLIC)
-       PHP_ME(HttpEnvResponse, setEtag,                 ai_HttpEnvResponse_setEtag,                 ZEND_ACC_PUBLIC)
-       PHP_ME(HttpEnvResponse, isCachedByEtag,          ai_HttpEnvResponse_isCachedByEtag,          ZEND_ACC_PUBLIC)
-       PHP_ME(HttpEnvResponse, setThrottleRate,         ai_HttpEnvResponse_setThrottleRate,         ZEND_ACC_PUBLIC)
-       PHP_ME(HttpEnvResponse, send,                    ai_HttpEnvResponse_send,                    ZEND_ACC_PUBLIC)
-       EMPTY_FUNCTION_ENTRY
-};
-
-zend_class_entry *php_http_env_response_class_entry;
-
-PHP_MINIT_FUNCTION(http_env_response)
-{
-       zend_class_entry ce = {0};
-
-       INIT_NS_CLASS_ENTRY(ce, "http\\Env", "Response", php_http_env_response_methods);
-       php_http_env_response_class_entry = zend_register_internal_class_ex(&ce, php_http_message_class_entry, NULL TSRMLS_CC);
-
-       zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CONTENT_ENCODING_NONE"), PHP_HTTP_CONTENT_ENCODING_NONE TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CONTENT_ENCODING_GZIP"), PHP_HTTP_CONTENT_ENCODING_GZIP TSRMLS_CC);
-
-       zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_NO"), PHP_HTTP_CACHE_NO TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_HIT"), PHP_HTTP_CACHE_HIT TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_MISS"), PHP_HTTP_CACHE_MISS TSRMLS_CC);
-
-       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("request"), ZEND_ACC_PROTECTED TSRMLS_CC);
-       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("cookies"), ZEND_ACC_PROTECTED TSRMLS_CC);
-       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentType"), ZEND_ACC_PROTECTED TSRMLS_CC);
-       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentDisposition"), ZEND_ACC_PROTECTED TSRMLS_CC);
-       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentEncoding"), ZEND_ACC_PROTECTED TSRMLS_CC);
-       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("cacheControl"), ZEND_ACC_PROTECTED TSRMLS_CC);
-       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("etag"), ZEND_ACC_PROTECTED TSRMLS_CC);
-       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("lastModified"), ZEND_ACC_PROTECTED TSRMLS_CC);
-       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("throttleDelay"), ZEND_ACC_PROTECTED TSRMLS_CC);
-       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("throttleChunk"), ZEND_ACC_PROTECTED TSRMLS_CC);
-
-       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/php_http_env_response.h b/php_http_env_response.h
deleted file mode 100644 (file)
index e6a112f..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_ENV_RESPONSE_H
-#define PHP_HTTP_ENV_RESPONSE_H
-
-typedef struct php_http_env_response php_http_env_response_t;
-
-typedef struct php_http_env_response_ops {
-       ZEND_RESULT_CODE (*init)(php_http_env_response_t *r, void *arg);
-       void (*dtor)(php_http_env_response_t *r);
-       long (*get_status)(php_http_env_response_t *r);
-       ZEND_RESULT_CODE (*set_status)(php_http_env_response_t *r, long http_code);
-       ZEND_RESULT_CODE (*set_protocol_version)(php_http_env_response_t *r, php_http_version_t *v);
-       ZEND_RESULT_CODE (*set_header)(php_http_env_response_t *r, const char *fmt, ...);
-       ZEND_RESULT_CODE (*add_header)(php_http_env_response_t *r, const char *fmt, ...);
-       ZEND_RESULT_CODE (*del_header)(php_http_env_response_t *r, const char *header_str, size_t header_len);
-       ZEND_RESULT_CODE (*write)(php_http_env_response_t *r, const char *data_str, size_t data_len);
-       ZEND_RESULT_CODE (*flush)(php_http_env_response_t *r);
-       ZEND_RESULT_CODE (*finish)(php_http_env_response_t *r);
-} php_http_env_response_ops_t;
-
-PHP_HTTP_API php_http_env_response_ops_t *php_http_env_response_get_sapi_ops(void);
-PHP_HTTP_API php_http_env_response_ops_t *php_http_env_response_get_stream_ops(void);
-
-struct php_http_env_response {
-       void *ctx;
-       php_http_env_response_ops_t *ops;
-
-       php_http_cookie_list_t *cookies;
-       php_http_buffer_t *buffer;
-       zval *options;
-
-       struct {
-               size_t chunk;
-               double delay;
-       } throttle;
-
-       struct {
-               php_http_range_status_t status;
-               HashTable values;
-               char boundary[32];
-       } range;
-
-       struct {
-               size_t length;
-               char *type;
-               char *encoding;
-
-               php_http_encoding_stream_t *encoder;
-       } content;
-
-       zend_bool done;
-
-#ifdef ZTS
-       void ***ts;
-#endif
-};
-
-PHP_HTTP_API php_http_env_response_t *php_http_env_response_init(php_http_env_response_t *r, zval *options, php_http_env_response_ops_t *ops, void *ops_ctx TSRMLS_DC);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_env_response_send(php_http_env_response_t *r);
-PHP_HTTP_API void php_http_env_response_dtor(php_http_env_response_t *r);
-PHP_HTTP_API void php_http_env_response_free(php_http_env_response_t **r);
-
-PHP_HTTP_API php_http_cache_status_t php_http_env_is_response_cached_by_etag(zval *options, const char *header_str, size_t header_len, php_http_message_t *request TSRMLS_DC);
-PHP_HTTP_API php_http_cache_status_t php_http_env_is_response_cached_by_last_modified(zval *options, const char *header_str, size_t header_len, php_http_message_t *request TSRMLS_DC);
-
-PHP_HTTP_API zend_class_entry *php_http_env_response_class_entry;
-PHP_MINIT_FUNCTION(http_env_response);
-
-#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/php_http_etag.c b/php_http_etag.c
deleted file mode 100644 (file)
index 3604ad8..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-#ifdef PHP_HTTP_HAVE_HASH
-#      include "php_hash.h"
-#endif
-
-#include <ext/standard/crc32.h>
-#include <ext/standard/sha1.h>
-#include <ext/standard/md5.h>
-
-php_http_etag_t *php_http_etag_init(const char *mode TSRMLS_DC)
-{
-       void *ctx;
-       php_http_etag_t *e;
-
-       if (mode && (!strcasecmp(mode, "crc32b"))) {
-               ctx = emalloc(sizeof(uint));
-               *((uint *) ctx) = ~0;
-       } else if (mode && !strcasecmp(mode, "sha1")) {
-               PHP_SHA1Init(ctx = emalloc(sizeof(PHP_SHA1_CTX)));
-       } else if (mode && !strcasecmp(mode, "md5")) {
-               PHP_MD5Init(ctx = emalloc(sizeof(PHP_MD5_CTX)));
-       } else {
-#ifdef PHP_HTTP_HAVE_HASH
-               const php_hash_ops *eho = NULL;
-
-               if (mode && (eho = php_hash_fetch_ops(mode, strlen(mode)))) {
-                       ctx = emalloc(eho->context_size);
-                       eho->hash_init(ctx);
-               } else
-#endif
-               return NULL;
-       }
-
-       e = emalloc(sizeof(*e));
-       e->ctx = ctx;
-       e->mode = estrdup(mode);
-       TSRMLS_SET_CTX(e->ts);
-
-       return e;
-}
-
-char *php_http_etag_finish(php_http_etag_t *e)
-{
-       unsigned char digest[128] = {0};
-       char *etag = NULL;
-
-       if (!strcasecmp(e->mode, "crc32b")) {
-               unsigned char buf[4];
-
-               *((uint *) e->ctx) = ~*((uint *) e->ctx);
-#ifdef WORDS_BIGENDIAN
-               etag = php_http_etag_digest((unsigned char *) e->ctx, 4);
-#else
-               buf[0] = ((unsigned char *) e->ctx)[3];
-               buf[1] = ((unsigned char *) e->ctx)[2];
-               buf[2] = ((unsigned char *) e->ctx)[1];
-               buf[3] = ((unsigned char *) e->ctx)[0];
-               etag = php_http_etag_digest(buf, 4);
-#endif
-       } else if ((!strcasecmp(e->mode, "sha1"))) {
-               PHP_SHA1Final(digest, e->ctx);
-               etag = php_http_etag_digest(digest, 20);
-       } else if ((!strcasecmp(e->mode, "md5"))) {
-               PHP_MD5Final(digest, e->ctx);
-               etag = php_http_etag_digest(digest, 16);
-       } else {
-#ifdef PHP_HTTP_HAVE_HASH
-               const php_hash_ops *eho = NULL;
-
-               if (e->mode && (eho = php_hash_fetch_ops(e->mode, strlen(e->mode)))) {
-                       eho->hash_final(digest, e->ctx);
-                       etag = php_http_etag_digest(digest, eho->digest_size);
-               }
-#endif
-       }
-
-       efree(e->ctx);
-       efree(e->mode);
-       efree(e);
-
-       return etag;
-}
-
-size_t php_http_etag_update(php_http_etag_t *e, const char *data_ptr, size_t data_len)
-{
-       if (!strcasecmp(e->mode, "crc32b")) {
-               uint i, c = *((uint *) e->ctx);
-               for (i = 0; i < data_len; ++i) {
-                       CRC32(c, data_ptr[i]);
-               }
-               *((uint *) e->ctx) = c;
-       } else if ((!strcasecmp(e->mode, "sha1"))) {
-               PHP_SHA1Update(e->ctx, (const unsigned char *) data_ptr, data_len);
-       } else if ((!strcasecmp(e->mode, "md5"))) {
-               PHP_MD5Update(e->ctx, (const unsigned char *) data_ptr, data_len);
-       } else {
-#ifdef PHP_HTTP_HAVE_HASH
-               const php_hash_ops *eho = NULL;
-
-               if (e->mode && (eho = php_hash_fetch_ops(e->mode, strlen(e->mode)))) {
-                       eho->hash_update(e->ctx, (const unsigned char *) data_ptr, data_len);
-               }
-#endif
-       }
-
-       return data_len;
-}
-
-
-/*
- * 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/php_http_etag.h b/php_http_etag.h
deleted file mode 100644 (file)
index bf6cf49..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_ETAG_H
-#define PHP_HTTP_ETAG_H
-
-typedef struct php_http_etag {
-       void *ctx;
-       char *mode;
-
-#ifdef ZTS
-       void ***ts;
-#endif
-} php_http_etag_t;
-
-PHP_HTTP_API php_http_etag_t *php_http_etag_init(const char *mode TSRMLS_DC);
-PHP_HTTP_API size_t php_http_etag_update(php_http_etag_t *e, const char *data_ptr, size_t data_len);
-PHP_HTTP_API char *php_http_etag_finish(php_http_etag_t *e);
-
-static inline char *php_http_etag_digest(const unsigned char *digest, int len)
-{
-       static const char hexdigits[17] = "0123456789abcdef";
-       int i;
-       char *hex = emalloc(len * 2 + 1);
-       char *ptr = hex;
-
-       for (i = 0; i < len; ++i) {
-               *ptr++ = hexdigits[digest[i] >> 4];
-               *ptr++ = hexdigits[digest[i] & 0xF];
-       }
-       *ptr = '\0';
-
-       return hex;
-}
-
-#endif /* PHP_HTTP_ETAG_H */
-
-/*
- * 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/php_http_exception.c b/php_http_exception.c
deleted file mode 100644 (file)
index 25a33e6..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-#include <ext/spl/spl_exceptions.h>
-
-#ifndef PHP_HTTP_DBG_EXCEPTIONS
-#      define PHP_HTTP_DBG_EXCEPTIONS 0
-#endif
-
-#if PHP_HTTP_DBG_EXCEPTIONS
-static void php_http_exception_hook(zval *ex TSRMLS_DC)
-{
-       if (ex) {
-               zval *m = zend_read_property(Z_OBJCE_P(ex), ex, "message", lenof("message"), 0 TSRMLS_CC);
-               fprintf(stderr, "*** Threw exception '%s'\n", Z_STRVAL_P(m));
-       } else {
-               fprintf(stderr, "*** Threw NULL exception\n");
-       }
-}
-#endif
-
-zend_class_entry *php_http_exception_interface_class_entry;
-zend_class_entry *php_http_exception_runtime_class_entry;
-zend_class_entry *php_http_exception_unexpected_val_class_entry;
-zend_class_entry *php_http_exception_bad_method_call_class_entry;
-zend_class_entry *php_http_exception_invalid_arg_class_entry;
-zend_class_entry *php_http_exception_bad_header_class_entry;
-zend_class_entry *php_http_exception_bad_url_class_entry;
-zend_class_entry *php_http_exception_bad_message_class_entry;
-zend_class_entry *php_http_exception_bad_conversion_class_entry;
-zend_class_entry *php_http_exception_bad_querystring_class_entry;
-
-PHP_MINIT_FUNCTION(http_exception)
-{
-       zend_class_entry *cep, ce = {0};
-
-       INIT_NS_CLASS_ENTRY(ce, "http", "Exception", NULL);
-       php_http_exception_interface_class_entry = zend_register_internal_interface(&ce TSRMLS_CC);
-       
-       /*
-        * Would be great to only have a few exceptions and rather more identifying
-        * error codes, but zend_replace_error_handling() does not accept any codes.
-        */
-
-       memset(&ce, 0, sizeof(ce));
-       INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "RuntimeException", NULL);
-       cep = zend_register_internal_class_ex(&ce, spl_ce_RuntimeException, NULL TSRMLS_CC);
-       zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry);
-       php_http_exception_runtime_class_entry = cep;
-
-       memset(&ce, 0, sizeof(ce));
-       INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "UnexpectedValueException", NULL);
-       cep = zend_register_internal_class_ex(&ce, spl_ce_UnexpectedValueException, NULL TSRMLS_CC);
-       zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry);
-       php_http_exception_unexpected_val_class_entry = cep;
-
-       memset(&ce, 0, sizeof(ce));
-       INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "BadMethodCallException", NULL);
-       cep = zend_register_internal_class_ex(&ce, spl_ce_BadMethodCallException, NULL TSRMLS_CC);
-       zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry);
-       php_http_exception_bad_method_call_class_entry = cep;
-
-       memset(&ce, 0, sizeof(ce));
-       INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "InvalidArgumentException", NULL);
-       cep = zend_register_internal_class_ex(&ce, spl_ce_InvalidArgumentException, NULL TSRMLS_CC);
-       zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry);
-       php_http_exception_invalid_arg_class_entry = cep;
-
-       memset(&ce, 0, sizeof(ce));
-       INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "BadHeaderException", NULL);
-       cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException, NULL TSRMLS_CC);
-       zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry);
-       php_http_exception_bad_header_class_entry = cep;
-
-       memset(&ce, 0, sizeof(ce));
-       INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "BadUrlException", NULL);
-       cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException, NULL TSRMLS_CC);
-       zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry);
-       php_http_exception_bad_url_class_entry = cep;
-
-       memset(&ce, 0, sizeof(ce));
-       INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "BadMessageException", NULL);
-       cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException, NULL TSRMLS_CC);
-       zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry);
-       php_http_exception_bad_message_class_entry = cep;
-
-       memset(&ce, 0, sizeof(ce));
-       INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "BadConversionException", NULL);
-       cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException, NULL TSRMLS_CC);
-       zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry);
-       php_http_exception_bad_conversion_class_entry = cep;
-
-       memset(&ce, 0, sizeof(ce));
-       INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "BadQueryStringException", NULL);
-       cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException, NULL TSRMLS_CC);
-       zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry);
-       php_http_exception_bad_querystring_class_entry = cep;
-
-#if PHP_HTTP_DBG_EXCEPTIONS
-       zend_throw_exception_hook = php_http_exception_hook;
-#endif
-       
-       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/php_http_exception.h b/php_http_exception.h
deleted file mode 100644 (file)
index 969a351..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_EXCEPTION_H
-#define PHP_HTTP_EXCEPTION_H
-
-/* short hand for zend_throw_exception_ex */
-#define php_http_throw(e, fmt, ...) \
-       zend_throw_exception_ex(php_http_exception_ ##e## _class_entry, 0 TSRMLS_CC, fmt, __VA_ARGS__)
-
-/* wrap a call with replaced zend_error_handling */
-#define php_http_expect(test, e, fail) \
-       do { \
-               zend_error_handling __zeh; \
-               zend_replace_error_handling(EH_THROW, php_http_exception_ ##e## _class_entry, &__zeh TSRMLS_CC); \
-               if (!(test)) { \
-                       zend_restore_error_handling(&__zeh TSRMLS_CC); \
-                       fail; \
-               } \
-               zend_restore_error_handling(&__zeh TSRMLS_CC); \
-       } while(0)
-
-PHP_HTTP_API zend_class_entry *php_http_exception_interface_class_entry;
-PHP_HTTP_API zend_class_entry *php_http_exception_runtime_class_entry;
-PHP_HTTP_API zend_class_entry *php_http_exception_unexpected_val_class_entry;
-PHP_HTTP_API zend_class_entry *php_http_exception_bad_method_call_class_entry;
-PHP_HTTP_API zend_class_entry *php_http_exception_invalid_arg_class_entry;
-PHP_HTTP_API zend_class_entry *php_http_exception_bad_header_class_entry;
-PHP_HTTP_API zend_class_entry *php_http_exception_bad_url_class_entry;
-PHP_HTTP_API zend_class_entry *php_http_exception_bad_message_class_entry;
-PHP_HTTP_API zend_class_entry *php_http_exception_bad_conversion_class_entry;
-PHP_HTTP_API zend_class_entry *php_http_exception_bad_querystring_class_entry;
-
-PHP_MINIT_FUNCTION(http_exception);
-
-#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/php_http_filter.c b/php_http_filter.c
deleted file mode 100644 (file)
index b6d967b..0000000
+++ /dev/null
@@ -1,453 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-#ifndef DBG_FILTER
-#      define DBG_FILTER 0
-#endif
-
-PHP_MINIT_FUNCTION(http_filter)
-{
-       php_stream_filter_register_factory("http.*", &php_http_filter_factory TSRMLS_CC);
-       return SUCCESS;
-}
-
-#define PHP_HTTP_FILTER_PARAMS \
-       php_stream *stream, \
-       php_stream_filter *this, \
-       php_stream_bucket_brigade *buckets_in, \
-       php_stream_bucket_brigade *buckets_out, \
-       size_t *bytes_consumed, int flags \
-       TSRMLS_DC
-#define PHP_HTTP_FILTER_OP(filter) \
-       http_filter_op_ ##filter
-#define PHP_HTTP_FILTER_OPS(filter) \
-       php_stream_filter_ops PHP_HTTP_FILTER_OP(filter)
-#define PHP_HTTP_FILTER_DTOR(filter) \
-       http_filter_ ##filter## _dtor
-#define PHP_HTTP_FILTER_DESTRUCTOR(filter) \
-       void PHP_HTTP_FILTER_DTOR(filter)(php_stream_filter *this TSRMLS_DC)
-#define PHP_HTTP_FILTER_FUNC(filter) \
-       http_filter_ ##filter
-#define PHP_HTTP_FILTER_FUNCTION(filter) \
-       php_stream_filter_status_t PHP_HTTP_FILTER_FUNC(filter)(PHP_HTTP_FILTER_PARAMS)
-#define PHP_HTTP_FILTER_BUFFER(filter) \
-       http_filter_ ##filter## _buffer
-
-#define PHP_HTTP_FILTER_IS_CLOSING(stream, flags) \
-       (       (flags & PSFS_FLAG_FLUSH_CLOSE) \
-       ||      php_stream_eof(stream) \
-       || ((stream->ops == &php_stream_temp_ops || stream->ops == &php_stream_memory_ops) && stream->eof) \
-       )
-
-#define NEW_BUCKET(data, length) \
-       { \
-               char *__data; \
-               php_stream_bucket *__buck; \
-               \
-               __data = pemalloc(length, this->is_persistent); \
-               if (!__data) { \
-                       return PSFS_ERR_FATAL; \
-               } \
-               memcpy(__data, data, length); \
-               \
-               __buck = php_stream_bucket_new(stream, __data, length, 1, this->is_persistent TSRMLS_CC); \
-               if (!__buck) { \
-                       pefree(__data, this->is_persistent); \
-                       return PSFS_ERR_FATAL; \
-               } \
-               \
-               php_stream_bucket_append(buckets_out, __buck TSRMLS_CC); \
-       }
-
-typedef struct _http_chunked_decode_filter_buffer_t {
-       php_http_buffer_t       buffer;
-       ulong   hexlen;
-} PHP_HTTP_FILTER_BUFFER(chunked_decode);
-
-typedef php_http_encoding_stream_t PHP_HTTP_FILTER_BUFFER(zlib);
-
-static PHP_HTTP_FILTER_FUNCTION(chunked_decode)
-{
-       int out_avail = 0;
-       php_stream_bucket *ptr, *nxt;
-       PHP_HTTP_FILTER_BUFFER(chunked_decode) *buffer = (PHP_HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract);
-       
-       if (bytes_consumed) {
-               *bytes_consumed = 0;
-       }
-       
-       /* fetch available bucket data */
-       for (ptr = buckets_in->head; ptr; ptr = nxt) {
-               if (bytes_consumed) {
-                       *bytes_consumed += ptr->buflen;
-               }
-
-               if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(PHP_HTTP_BUFFER(buffer), ptr->buf, ptr->buflen)) {
-                       return PSFS_ERR_FATAL;
-               }
-
-               nxt = ptr->next;
-               php_stream_bucket_unlink(ptr TSRMLS_CC);
-               php_stream_bucket_delref(ptr TSRMLS_CC);
-       }
-       
-       if (!php_http_buffer_fix(PHP_HTTP_BUFFER(buffer))) {
-               return PSFS_ERR_FATAL;
-       }
-
-       /* we have data in our buffer */
-       while (PHP_HTTP_BUFFER(buffer)->used) {
-       
-               /* we already know the size of the chunk and are waiting for data */
-               if (buffer->hexlen) {
-               
-                       /* not enough data buffered */
-                       if (PHP_HTTP_BUFFER(buffer)->used < buffer->hexlen) {
-                       
-                               /* flush anyway? */
-                               if (flags & PSFS_FLAG_FLUSH_INC) {
-                               
-                                       /* flush all data (should only be chunk data) */
-                                       out_avail = 1;
-                                       NEW_BUCKET(PHP_HTTP_BUFFER(buffer)->data, PHP_HTTP_BUFFER(buffer)->used);
-                                       
-                                       /* waiting for less data now */
-                                       buffer->hexlen -= PHP_HTTP_BUFFER(buffer)->used;
-                                       /* no more buffered data */
-                                       php_http_buffer_reset(PHP_HTTP_BUFFER(buffer));
-                                       /* break */
-                               } 
-                               
-                               /* we have too less data and don't need to flush */
-                               else {
-                                       break;
-                               }
-                       } 
-                       
-                       /* we seem to have all data of the chunk */
-                       else {
-                               out_avail = 1;
-                               NEW_BUCKET(PHP_HTTP_BUFFER(buffer)->data, buffer->hexlen);
-                               
-                               /* remove outgoing data from the buffer */
-                               php_http_buffer_cut(PHP_HTTP_BUFFER(buffer), 0, buffer->hexlen);
-                               /* reset hexlen */
-                               buffer->hexlen = 0;
-                               /* continue */
-                       }
-               } 
-               
-               /* we don't know the length of the chunk yet */
-               else {
-                       size_t off = 0;
-                       
-                       /* ignore preceeding CRLFs (too loose?) */
-                       while (off < PHP_HTTP_BUFFER(buffer)->used && (
-                                       PHP_HTTP_BUFFER(buffer)->data[off] == '\n' || 
-                                       PHP_HTTP_BUFFER(buffer)->data[off] == '\r')) {
-                               ++off;
-                       }
-                       if (off) {
-                               php_http_buffer_cut(PHP_HTTP_BUFFER(buffer), 0, off);
-                       }
-                       
-                       /* still data there? */
-                       if (PHP_HTTP_BUFFER(buffer)->used) {
-                               int eollen;
-                               const char *eolstr;
-                               
-                               /* we need eol, so we can be sure we have all hex digits */
-                               php_http_buffer_fix(PHP_HTTP_BUFFER(buffer));
-                               if ((eolstr = php_http_locate_bin_eol(PHP_HTTP_BUFFER(buffer)->data, PHP_HTTP_BUFFER(buffer)->used, &eollen))) {
-                                       char *stop = NULL;
-                                       
-                                       /* read in chunk size */
-                                       buffer->hexlen = strtoul(PHP_HTTP_BUFFER(buffer)->data, &stop, 16);
-                                       
-                                       /*      if strtoul() stops at the beginning of the buffered data
-                                               there's something oddly wrong, i.e. bad input */
-                                       if (stop == PHP_HTTP_BUFFER(buffer)->data) {
-                                               return PSFS_ERR_FATAL;
-                                       }
-                                       
-                                       /* cut out <chunk size hex><chunk extension><eol> */
-                                       php_http_buffer_cut(PHP_HTTP_BUFFER(buffer), 0, eolstr + eollen - PHP_HTTP_BUFFER(buffer)->data);
-                                       /* buffer->hexlen is 0 now or contains the size of the next chunk */
-                                       if (!buffer->hexlen) {
-                                               php_stream_notify_info(stream->context, PHP_STREAM_NOTIFY_COMPLETED, NULL, 0);
-                                               break;
-                                       }
-                                       /* continue */
-                               } else {
-                                       /* we have not enough data buffered to read in chunk size */
-                                       break;
-                               }
-                       }
-                       /* break */
-               }
-       }
-       
-       /* flush before close, but only if we are already waiting for more data */
-       if (PHP_HTTP_FILTER_IS_CLOSING(stream, flags) && buffer->hexlen && PHP_HTTP_BUFFER(buffer)->used) {
-               out_avail = 1;
-               NEW_BUCKET(PHP_HTTP_BUFFER(buffer)->data, PHP_HTTP_BUFFER(buffer)->used);
-               php_http_buffer_reset(PHP_HTTP_BUFFER(buffer));
-               buffer->hexlen = 0;
-       }
-       
-       return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
-}
-
-static PHP_HTTP_FILTER_DESTRUCTOR(chunked_decode)
-{
-       PHP_HTTP_FILTER_BUFFER(chunked_decode) *b = (PHP_HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract);
-       
-       php_http_buffer_dtor(PHP_HTTP_BUFFER(b));
-       pefree(b, this->is_persistent);
-}
-
-static PHP_HTTP_FILTER_FUNCTION(chunked_encode)
-{
-       php_http_buffer_t buf;
-       php_stream_bucket *ptr, *nxt;
-       
-       if (bytes_consumed) {
-               *bytes_consumed = 0;
-       }
-       
-       /* new data available? */
-       php_http_buffer_init(&buf);
-
-       /* fetch available bucket data */
-       for (ptr = buckets_in->head; ptr; ptr = nxt) {
-               if (bytes_consumed) {
-                       *bytes_consumed += ptr->buflen;
-               }
-#if DBG_FILTER
-               fprintf(stderr, "update: chunked (-> %zu) (w: %zu, r: %zu)\n", ptr->buflen, stream->writepos, stream->readpos);
-#endif
-               
-               nxt = ptr->next;
-               php_stream_bucket_unlink(ptr TSRMLS_CC);
-               php_http_buffer_appendf(&buf, "%lx" PHP_HTTP_CRLF, (long unsigned int) ptr->buflen);
-               php_http_buffer_append(&buf, ptr->buf, ptr->buflen);
-               php_http_buffer_appends(&buf, PHP_HTTP_CRLF);
-
-               /* pass through */
-               NEW_BUCKET(buf.data, buf.used);
-               /* reset */
-               php_http_buffer_reset(&buf);
-               php_stream_bucket_delref(ptr TSRMLS_CC);
-       }
-
-       /* free buffer */
-       php_http_buffer_dtor(&buf);
-       
-       /* terminate with "0" */
-       if (PHP_HTTP_FILTER_IS_CLOSING(stream, flags)) {
-#if DBG_FILTER
-               fprintf(stderr, "finish: chunked\n");
-#endif
-               
-               NEW_BUCKET("0" PHP_HTTP_CRLF PHP_HTTP_CRLF, lenof("0" PHP_HTTP_CRLF PHP_HTTP_CRLF));
-       }
-       
-       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)
-{
-       php_stream_bucket *ptr, *nxt;
-       PHP_HTTP_FILTER_BUFFER(zlib) *buffer = (PHP_HTTP_FILTER_BUFFER(zlib) *) this->abstract;
-       
-       if (bytes_consumed) {
-               *bytes_consumed = 0;
-       }
-       
-       /* fetch available bucket data */
-       for (ptr = buckets_in->head; ptr; ptr = nxt) {
-               char *encoded = NULL;
-               size_t encoded_len = 0;
-
-               if (bytes_consumed) {
-                       *bytes_consumed += ptr->buflen;
-               }
-
-#if DBG_FILTER
-               fprintf(stderr, "bucket: b=%p p=%p p=%p\n", ptr->brigade, ptr->prev,  ptr->next);
-#endif
-               
-               nxt = ptr->next;
-               php_stream_bucket_unlink(ptr TSRMLS_CC);
-               php_http_encoding_stream_update(buffer, ptr->buf, ptr->buflen, &encoded, &encoded_len);
-               
-#if DBG_FILTER
-               fprintf(stderr, "update: deflate (-> %zu) (w: %zu, r: %zu)\n", encoded_len, stream->writepos, stream->readpos);
-#endif
-               
-               if (encoded) {
-                       if (encoded_len) {
-                               NEW_BUCKET(encoded, encoded_len);
-                       }
-                       efree(encoded);
-               }
-               php_stream_bucket_delref(ptr TSRMLS_CC);
-       }
-
-       /* flush & close */
-       if (flags & PSFS_FLAG_FLUSH_INC) {
-               char *encoded = NULL;
-               size_t encoded_len = 0;
-               
-               php_http_encoding_stream_flush(buffer, &encoded, &encoded_len);
-               
-#if DBG_FILTER
-               fprintf(stderr, "flush: deflate (-> %zu)\n", encoded_len);
-#endif
-               
-               if (encoded) {
-                       if (encoded_len) {
-                               NEW_BUCKET(encoded, encoded_len);
-                       }
-                       efree(encoded);
-               }
-       }
-       
-       if (PHP_HTTP_FILTER_IS_CLOSING(stream, flags)) {
-               char *encoded = NULL;
-               size_t encoded_len = 0;
-               
-               php_http_encoding_stream_finish(buffer, &encoded, &encoded_len);
-               
-#if DBG_FILTER
-               fprintf(stderr, "finish: deflate (-> %zu)\n", encoded_len);
-#endif
-               
-               if (encoded) {
-                       if (encoded_len) {
-                               NEW_BUCKET(encoded, encoded_len);
-                       }
-                       efree(encoded);
-               }
-       }
-       
-       return PSFS_PASS_ON;
-}
-static PHP_HTTP_FILTER_DESTRUCTOR(zlib)
-{
-       PHP_HTTP_FILTER_BUFFER(zlib) *buffer = (PHP_HTTP_FILTER_BUFFER(zlib) *) this->abstract;
-       php_http_encoding_stream_free(&buffer);
-}
-
-static PHP_HTTP_FILTER_OPS(deflate) = {
-       PHP_HTTP_FILTER_FUNC(zlib),
-       PHP_HTTP_FILTER_DTOR(zlib),
-       "http.deflate"
-};
-
-static PHP_HTTP_FILTER_OPS(inflate) = {
-       PHP_HTTP_FILTER_FUNC(zlib),
-       PHP_HTTP_FILTER_DTOR(zlib),
-       "http.inflate"
-};
-
-static php_stream_filter *http_filter_create(const char *name, zval *params, int p TSRMLS_DC)
-{
-       zval **tmp = &params;
-       php_stream_filter *f = NULL;
-       int flags = p ? PHP_HTTP_ENCODING_STREAM_PERSISTENT : 0;
-       
-       if (params) {
-               switch (Z_TYPE_P(params)) {
-                       case IS_ARRAY:
-                       case IS_OBJECT:
-                               if (SUCCESS != zend_hash_find(HASH_OF(params), "flags", sizeof("flags"), (void *) &tmp)) {
-                                       break;
-                               }
-                               /* no break */
-                       default:
-                       {
-                               zval *num = php_http_ztyp(IS_LONG, *tmp);
-                               
-                               flags |= (Z_LVAL_P(num) & 0x0fffffff);
-                               zval_ptr_dtor(&num);
-
-                       }
-                       break;
-               }
-       }
-
-       if (!strcasecmp(name, "http.chunked_decode")) {
-               PHP_HTTP_FILTER_BUFFER(chunked_decode) *b = NULL;
-               
-               if ((b = pecalloc(1, sizeof(PHP_HTTP_FILTER_BUFFER(chunked_decode)), p))) {
-                       php_http_buffer_init_ex(PHP_HTTP_BUFFER(b), 4096, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0);
-                       if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(chunked_decode), b, p))) {
-                               pefree(b, p);
-                       }
-               }
-       } else
-       
-       if (!strcasecmp(name, "http.chunked_encode")) {
-               f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(chunked_encode), NULL, p);
-       } else
-       
-       if (!strcasecmp(name, "http.inflate")) {
-               PHP_HTTP_FILTER_BUFFER(zlib) *b = NULL;
-               
-               if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), flags TSRMLS_CC))) {
-                       if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(inflate), b, p))) {
-                               php_http_encoding_stream_free(&b);
-                       }
-               }
-       } else
-       
-       if (!strcasecmp(name, "http.deflate")) {
-               PHP_HTTP_FILTER_BUFFER(zlib) *b = NULL;
-               
-               if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), flags TSRMLS_CC))) {
-                       if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(deflate), b, p))) {
-                               php_http_encoding_stream_free(&b);
-                       }
-               }
-       }
-       
-       return f;
-}
-
-php_stream_filter_factory php_http_filter_factory = {
-       http_filter_create
-};
-
-
-/*
- * 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/php_http_filter.h b/php_http_filter.h
deleted file mode 100644 (file)
index 21fe4db..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_FILTER_H
-#define PHP_HTTP_FILTER_H
-
-PHP_HTTP_API php_stream_filter_factory php_http_filter_factory;
-PHP_MINIT_FUNCTION(http_filter);
-
-#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/php_http_header.c b/php_http_header.c
deleted file mode 100644 (file)
index 41601df..0000000
+++ /dev/null
@@ -1,383 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-ZEND_RESULT_CODE php_http_header_parse(const char *header, size_t length, HashTable *headers, php_http_info_callback_t callback_func, void **callback_data TSRMLS_DC)
-{
-       php_http_header_parser_t ctx;
-       php_http_buffer_t buf;
-       php_http_header_parser_state_t rs;
-       
-       if (!php_http_buffer_from_string_ex(&buf, header, length)) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not allocate buffer");
-               return FAILURE;
-       }
-       
-       if (!php_http_header_parser_init(&ctx TSRMLS_CC)) {
-               php_http_buffer_dtor(&buf);
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initialize header parser");
-               return FAILURE;
-       }
-       
-       rs = php_http_header_parser_parse(&ctx, &buf, PHP_HTTP_HEADER_PARSER_CLEANUP, headers, callback_func, callback_data);
-       php_http_header_parser_dtor(&ctx);
-       php_http_buffer_dtor(&buf);
-
-       return rs == PHP_HTTP_HEADER_PARSER_STATE_FAILURE ? FAILURE : SUCCESS;
-}
-
-void php_http_header_to_callback(HashTable *headers, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg TSRMLS_DC)
-{
-       HashPosition pos1, pos2;
-       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-       zval **header, **single_header;
-
-       FOREACH_HASH_KEYVAL(pos1, headers, key, header) {
-               if (key.type == HASH_KEY_IS_STRING) {
-                       if (key.len == sizeof("Set-Cookie") && !strcasecmp(key.str, "Set-Cookie") && Z_TYPE_PP(header) == IS_ARRAY) {
-                               FOREACH_VAL(pos2, *header, single_header) {
-                                       if (Z_TYPE_PP(single_header) == IS_ARRAY) {
-                                               php_http_cookie_list_t *cookie = php_http_cookie_list_from_struct(NULL, *single_header TSRMLS_CC);
-
-                                               if (cookie) {
-                                                       char *buf;
-                                                       size_t len;
-
-                                                       php_http_cookie_list_to_string(cookie, &buf, &len);
-                                                       cb(cb_arg, crlf ? "Set-Cookie: %s" PHP_HTTP_CRLF : "Set-Cookie: %s", buf);
-                                                       php_http_cookie_list_free(&cookie);
-                                                       efree(buf);
-                                               }
-                                       } else {
-                                               zval *strval = php_http_header_value_to_string(*single_header TSRMLS_CC);
-
-                                               cb(cb_arg, crlf ? "Set-Cookie: %s" PHP_HTTP_CRLF : "Set-Cookie: %s", Z_STRVAL_P(strval));
-                                               zval_ptr_dtor(&strval);
-                                       }
-                               }
-                       } else {
-                               zval *strval = php_http_header_value_to_string(*header TSRMLS_CC);
-
-                               cb(cb_arg, crlf ? "%s: %s" PHP_HTTP_CRLF : "%s: %s", key.str, Z_STRVAL_P(strval));
-                               zval_ptr_dtor(&strval);
-                       }
-               }
-       }
-}
-
-void php_http_header_to_string(php_http_buffer_t *str, HashTable *headers TSRMLS_DC)
-{
-       php_http_header_to_callback(headers, 1, (php_http_pass_format_callback_t) php_http_buffer_appendf, str TSRMLS_CC);
-}
-
-zval *php_http_header_value_to_string(zval *header TSRMLS_DC)
-{
-       zval *ret;
-
-       if (Z_TYPE_P(header) == IS_BOOL) {
-               MAKE_STD_ZVAL(ret);
-               ZVAL_STRING(ret, Z_BVAL_P(header) ? "true" : "false", 1);
-       } else if (Z_TYPE_P(header) == IS_ARRAY) {
-               zval **val;
-               HashPosition pos;
-               php_http_buffer_t str;
-
-               php_http_buffer_init(&str);
-               MAKE_STD_ZVAL(ret);
-               FOREACH_VAL(pos,header, val) {
-                       zval *strval = php_http_header_value_to_string(*val TSRMLS_CC);
-
-                       php_http_buffer_appendf(&str, str.used ? ", %s":"%s", Z_STRVAL_P(strval));
-                       zval_ptr_dtor(&strval);
-               }
-               php_http_buffer_fix(&str);
-               ZVAL_STRINGL(ret, str.data, str.used, 0);
-       } else  {
-               ret = php_http_zsep(1, IS_STRING, header);
-       }
-
-       return ret;
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader___construct, 0, 0, 0)
-       ZEND_ARG_INFO(0, name)
-       ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpHeader, __construct)
-{
-       char *name_str = NULL, *value_str = NULL;
-       int name_len = 0, value_len = 0;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!s!", &name_str, &name_len, &value_str, &value_len), invalid_arg, return);
-
-       if (name_str && name_len) {
-               char *pretty_str = estrndup(name_str, name_len);
-               zend_update_property_stringl(php_http_header_class_entry, getThis(), ZEND_STRL("name"), php_http_pretty_key(pretty_str, name_len, 1, 1), name_len TSRMLS_CC);
-               efree(pretty_str);
-       }
-       if (value_str && value_len) {
-               zend_update_property_stringl(php_http_header_class_entry, getThis(), ZEND_STRL("value"), value_str, value_len TSRMLS_CC);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_serialize, 0, 0, 0)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpHeader, serialize)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_buffer_t buf;
-               zval *zname, *zvalue;
-
-               php_http_buffer_init(&buf);
-               zname = php_http_ztyp(IS_STRING, zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("name"), 0 TSRMLS_CC));
-               php_http_buffer_append(&buf, Z_STRVAL_P(zname), Z_STRLEN_P(zname));
-               zval_ptr_dtor(&zname);
-               zvalue = php_http_ztyp(IS_STRING, zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), 0 TSRMLS_CC));
-               if (Z_STRLEN_P(zvalue)) {
-                       php_http_buffer_appends(&buf, ": ");
-                       php_http_buffer_append(&buf, Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue));
-               } else {
-                       php_http_buffer_appends(&buf, ":");
-               }
-               zval_ptr_dtor(&zvalue);
-
-               RETURN_PHP_HTTP_BUFFER_VAL(&buf);
-       }
-       RETURN_EMPTY_STRING();
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_unserialize, 0, 0, 1)
-       ZEND_ARG_INFO(0, serialized)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpHeader, unserialize)
-{
-       char *serialized_str;
-       int serialized_len;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &serialized_str, &serialized_len)) {
-               HashTable ht;
-
-               zend_hash_init(&ht, 1, NULL, ZVAL_PTR_DTOR, 0);
-               if (SUCCESS == php_http_header_parse(serialized_str, serialized_len, &ht, NULL, NULL TSRMLS_CC)) {
-                       if (zend_hash_num_elements(&ht)) {
-                               zval **val, *cpy;
-                               char *str;
-                               uint len;
-                               ulong idx;
-
-                               zend_hash_internal_pointer_reset(&ht);
-                               switch (zend_hash_get_current_key_ex(&ht, &str, &len, &idx, 0, NULL)) {
-                                       case HASH_KEY_IS_STRING:
-                                               zend_update_property_stringl(php_http_header_class_entry, getThis(), ZEND_STRL("name"), str, len - 1 TSRMLS_CC);
-                                               break;
-                                       case HASH_KEY_IS_LONG:
-                                               zend_update_property_long(php_http_header_class_entry, getThis(), ZEND_STRL("name"), idx TSRMLS_CC);
-                                               break;
-                                       default:
-                                               break;
-                               }
-                               zend_hash_get_current_data(&ht, (void *) &val);
-                               cpy = php_http_zsep(1, IS_STRING, *val);
-                               zend_update_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), cpy TSRMLS_CC);
-                               zval_ptr_dtor(&cpy);
-                       }
-               }
-               zend_hash_destroy(&ht);
-       }
-
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_match, 0, 0, 1)
-       ZEND_ARG_INFO(0, value)
-       ZEND_ARG_INFO(0, flags)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpHeader, match)
-{
-       char *val_str;
-       int val_len;
-       long flags = PHP_HTTP_MATCH_LOOSE;
-       zval *zvalue;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sl", &val_str, &val_len, &flags)) {
-               return;
-       }
-
-       zvalue = php_http_ztyp(IS_STRING, zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), 0 TSRMLS_CC));
-       RETVAL_BOOL(php_http_match(Z_STRVAL_P(zvalue), val_str, flags));
-       zval_ptr_dtor(&zvalue);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_negotiate, 0, 0, 1)
-       ZEND_ARG_INFO(0, supported)
-       ZEND_ARG_INFO(1, result)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpHeader, negotiate)
-{
-       HashTable *supported, *rs;
-       zval *zname, *zvalue, *rs_array = NULL;
-       char *sep_str = NULL;
-       size_t sep_len = 0;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H|z", &supported, &rs_array)) {
-               return;
-       }
-       if (rs_array) {
-               zval_dtor(rs_array);
-               array_init(rs_array);
-       }
-
-       zname = php_http_ztyp(IS_STRING, zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("name"), 0 TSRMLS_CC));
-       if (!strcasecmp(Z_STRVAL_P(zname), "Accept")) {
-               sep_str = "/";
-               sep_len = 1;
-       } else if (!strcasecmp(Z_STRVAL_P(zname), "Accept-Language")) {
-               sep_str = "-";
-               sep_len = 1;
-       }
-       zval_ptr_dtor(&zname);
-
-       zvalue = php_http_ztyp(IS_STRING, zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), 0 TSRMLS_CC));
-       if ((rs = php_http_negotiate(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue), supported, sep_str, sep_len TSRMLS_CC))) {
-               PHP_HTTP_DO_NEGOTIATE_HANDLE_RESULT(rs, supported, rs_array);
-       } else {
-               PHP_HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported, rs_array);
-       }
-       zval_ptr_dtor(&zvalue);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_getParams, 0, 0, 0)
-       ZEND_ARG_INFO(0, param_sep)
-       ZEND_ARG_INFO(0, arg_sep)
-       ZEND_ARG_INFO(0, val_sep)
-       ZEND_ARG_INFO(0, flags)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpHeader, getParams)
-{
-       zval zctor, *zparams_obj, **zargs = NULL;
-       
-       INIT_PZVAL(&zctor);
-       ZVAL_STRINGL(&zctor, "__construct", lenof("__construct"), 0);
-       
-       MAKE_STD_ZVAL(zparams_obj);
-       object_init_ex(zparams_obj, php_http_params_class_entry);
-       
-       zargs = (zval **) ecalloc(ZEND_NUM_ARGS()+1, sizeof(zval *));
-       zargs[0] = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("value"), 0 TSRMLS_CC);
-       if (ZEND_NUM_ARGS()) {
-               zend_get_parameters_array(ZEND_NUM_ARGS(), ZEND_NUM_ARGS(), &zargs[1]);
-       }
-       
-       if (SUCCESS == call_user_function(NULL, &zparams_obj, &zctor, return_value, ZEND_NUM_ARGS()+1, zargs TSRMLS_CC)) {
-               RETVAL_ZVAL(zparams_obj, 0, 1);
-       }
-       
-       if (zargs) {
-               efree(zargs);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_parse, 0, 0, 1)
-       ZEND_ARG_INFO(0, string)
-       ZEND_ARG_INFO(0, header_class)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpHeader, parse)
-{
-       char *header_str;
-       int header_len;
-       zend_class_entry *ce = NULL;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|C", &header_str, &header_len, &ce)) {
-               array_init(return_value);
-
-               if (SUCCESS != php_http_header_parse(header_str, header_len, Z_ARRVAL_P(return_value), NULL, NULL TSRMLS_CC)) {
-                       zval_dtor(return_value);
-                       RETURN_FALSE;
-               } else {
-                       if (ce && instanceof_function(ce, php_http_header_class_entry TSRMLS_CC)) {
-                               HashPosition pos;
-                               php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-                               zval **val;
-
-                               FOREACH_KEYVAL(pos, return_value, key, val) {
-                                       zval *zho, *zkey, *zvalue;
-
-                                       Z_ADDREF_PP(val);
-                                       zvalue = *val;
-
-                                       MAKE_STD_ZVAL(zkey);
-                                       if (key.type == HASH_KEY_IS_LONG) {
-                                               ZVAL_LONG(zkey, key.num);
-                                       } else {
-                                               ZVAL_STRINGL(zkey, key.str, key.len - 1, 1);
-                                       }
-
-                                       MAKE_STD_ZVAL(zho);
-                                       object_init_ex(zho, ce);
-                                       zend_call_method_with_2_params(&zho, ce, NULL, "__construct", NULL, zkey, zvalue);
-
-                                       if (key.type == HASH_KEY_IS_LONG) {
-                                               zend_hash_index_update(Z_ARRVAL_P(return_value), key.num, (void *) &zho, sizeof(zval *), NULL);
-                                       } else {
-                                               zend_hash_update(Z_ARRVAL_P(return_value), key.str, key.len, (void *) &zho, sizeof(zval *), NULL);
-                                       }
-
-                                       zval_ptr_dtor(&zvalue);
-                                       zval_ptr_dtor(&zkey);
-                               }
-                       }
-               }
-       }
-}
-
-static zend_function_entry php_http_header_methods[] = {
-       PHP_ME(HttpHeader, __construct,   ai_HttpHeader___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
-       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)
-       PHP_ME(HttpHeader, unserialize,   ai_HttpHeader_unserialize, ZEND_ACC_PUBLIC)
-       PHP_ME(HttpHeader, match,         ai_HttpHeader_match, ZEND_ACC_PUBLIC)
-       PHP_ME(HttpHeader, negotiate,     ai_HttpHeader_negotiate, ZEND_ACC_PUBLIC)
-       PHP_ME(HttpHeader, getParams,     ai_HttpHeader_getParams, ZEND_ACC_PUBLIC)
-       PHP_ME(HttpHeader, parse,         ai_HttpHeader_parse, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-       EMPTY_FUNCTION_ENTRY
-};
-
-zend_class_entry *php_http_header_class_entry;
-
-PHP_MINIT_FUNCTION(http_header)
-{
-       zend_class_entry ce = {0};
-
-       INIT_NS_CLASS_ENTRY(ce, "http", "Header", php_http_header_methods);
-       php_http_header_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
-       zend_class_implements(php_http_header_class_entry TSRMLS_CC, 1, zend_ce_serializable);
-       zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_LOOSE"), PHP_HTTP_MATCH_LOOSE TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_CASE"), PHP_HTTP_MATCH_CASE TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_WORD"), PHP_HTTP_MATCH_WORD TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_FULL"), PHP_HTTP_MATCH_FULL TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_STRICT"), PHP_HTTP_MATCH_STRICT TSRMLS_CC);
-       zend_declare_property_null(php_http_header_class_entry, ZEND_STRL("name"), ZEND_ACC_PUBLIC TSRMLS_CC);
-       zend_declare_property_null(php_http_header_class_entry, ZEND_STRL("value"), ZEND_ACC_PUBLIC TSRMLS_CC);
-
-       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/php_http_header.h b/php_http_header.h
deleted file mode 100644 (file)
index a2baecb..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_HEADERS_H
-#define PHP_HTTP_HEADERS_H
-
-#include "php_http_info.h"
-
-PHP_HTTP_API ZEND_RESULT_CODE php_http_header_parse(const char *header, size_t length, HashTable *headers, php_http_info_callback_t callback_func, void **callback_data TSRMLS_DC);
-
-PHP_HTTP_API void php_http_header_to_callback(HashTable *headers, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg TSRMLS_DC);
-PHP_HTTP_API void php_http_header_to_string(php_http_buffer_t *str, HashTable *headers TSRMLS_DC);
-
-PHP_HTTP_API zval *php_http_header_value_to_string(zval *header TSRMLS_DC);
-
-PHP_HTTP_API zend_class_entry *php_http_header_class_entry;
-PHP_MINIT_FUNCTION(http_header);
-
-#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/php_http_header_parser.c b/php_http_header_parser.c
deleted file mode 100644 (file)
index 46551e2..0000000
+++ /dev/null
@@ -1,489 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-#ifndef DBG_PARSER
-#      define DBG_PARSER 0
-#endif
-
-typedef struct php_http_header_parser_state_spec {
-       php_http_header_parser_state_t state;
-       unsigned need_data:1;
-} php_http_header_parser_state_spec_t;
-
-static const php_http_header_parser_state_spec_t php_http_header_parser_states[] = {
-               {PHP_HTTP_HEADER_PARSER_STATE_START,            1},
-               {PHP_HTTP_HEADER_PARSER_STATE_KEY,                      1},
-               {PHP_HTTP_HEADER_PARSER_STATE_VALUE,            1},
-               {PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX,         0},
-               {PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE,      0},
-               {PHP_HTTP_HEADER_PARSER_STATE_DONE,                     0}
-};
-
-php_http_header_parser_t *php_http_header_parser_init(php_http_header_parser_t *parser TSRMLS_DC)
-{
-       if (!parser) {
-               parser = emalloc(sizeof(*parser));
-       }
-       memset(parser, 0, sizeof(*parser));
-
-       TSRMLS_SET_CTX(parser->ts);
-
-       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;
-}
-
-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;
-}
-
-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;
-}
-
-void php_http_header_parser_dtor(php_http_header_parser_t *parser)
-{
-       zend_ptr_stack_destroy(&parser->stack);
-       php_http_info_dtor(&parser->info);
-       PTR_FREE(parser->_key.str);
-       PTR_FREE(parser->_val.str);
-}
-
-void php_http_header_parser_free(php_http_header_parser_t **parser)
-{
-       if (*parser) {
-               php_http_header_parser_dtor(*parser);
-               efree(*parser);
-               *parser = NULL;
-       }
-}
-
-/* NOTE: 'str' has to be null terminated */
-static void php_http_header_parser_error(size_t valid_len, char *str, size_t len, const char *eol_str TSRMLS_DC)
-{
-       int escaped_len;
-       char *escaped_str;
-
-       escaped_str = php_addcslashes(str, len, &escaped_len, 0, ZEND_STRL("\x0..\x1F\x7F..\xFF") TSRMLS_CC);
-
-       if (valid_len != len && (!eol_str || (str+valid_len) != eol_str)) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse headers: unexpected character '\\%03o' at pos %zu of '%.*s'", str[valid_len], valid_len, escaped_len, escaped_str);
-       } else if (eol_str) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse headers: unexpected end of line at pos %zu of '%.*s'", eol_str - str, escaped_len, escaped_str);
-       } else {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse headers: unexpected end of input at pos %zu of '%.*s'", len, escaped_len, escaped_str);
-       }
-
-       efree(escaped_str);
-}
-
-php_http_header_parser_state_t php_http_header_parser_parse(php_http_header_parser_t *parser, php_http_buffer_t *buffer, unsigned flags, HashTable *headers, php_http_info_callback_t callback_func, void *callback_arg)
-{
-       TSRMLS_FETCH_FROM_CTX(parser->ts);
-
-       while (buffer->used || !php_http_header_parser_states[php_http_header_parser_state_is(parser)].need_data) {
-#if 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);
-               _dpf(0, buffer->data, buffer->used);
-#endif
-               switch (php_http_header_parser_state_pop(parser)) {
-                       case PHP_HTTP_HEADER_PARSER_STATE_FAILURE:
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse headers");
-                               return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
-
-                       case PHP_HTTP_HEADER_PARSER_STATE_START: {
-                               char *ptr = buffer->data;
-
-                               while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) {
-                                       ++ptr;
-                               }
-
-                               php_http_buffer_cut(buffer, 0, ptr - buffer->data);
-                               php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY);
-                               break;
-                       }
-
-                       case PHP_HTTP_HEADER_PARSER_STATE_KEY: {
-                               const char *colon, *eol_str = NULL;
-                               int eol_len = 0;
-
-                               /* fix buffer here, so eol_str pointer doesn't become obsolete afterwards */
-                               php_http_buffer_fix(buffer);
-
-                               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);
-                               } else if (php_http_info_parse(&parser->info, buffer->data TSRMLS_CC)) {
-                                       /* new message starting with request/response line */
-                                       if (callback_func) {
-                                               callback_func(callback_arg, &headers, &parser->info TSRMLS_CC);
-                                       }
-                                       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);
-                               } else if ((colon = memchr(buffer->data, ':', buffer->used)) && (!eol_str || eol_str > colon)) {
-                                       /* header: string */
-                                       size_t valid_len;
-
-                                       parser->_key.len = colon - buffer->data;
-                                       parser->_key.str = estrndup(buffer->data, parser->_key.len);
-
-                                       valid_len = strspn(parser->_key.str, PHP_HTTP_HEADER_NAME_CHARS);
-                                       if (valid_len != parser->_key.len) {
-                                               php_http_header_parser_error(valid_len, parser->_key.str, parser->_key.len, eol_str TSRMLS_CC);
-                                               PTR_SET(parser->_key.str, NULL);
-                                               return php_http_header_parser_state_push(parser, 1, 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);
-                               } 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 TSRMLS_CC);
-                                       return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
-                               } else {
-                                       /* keep feeding */
-                                       return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY);
-                               }
-                               break;
-                       }
-
-                       case PHP_HTTP_HEADER_PARSER_STATE_VALUE: {
-                               const char *eol_str;
-                               int eol_len;
-
-#define SET_ADD_VAL(slen, eol_len) \
-       do { \
-               const char *ptr = buffer->data; \
-               size_t len = slen; \
-                \
-               while (len > 0 && PHP_HTTP_IS_CTYPE(space, *ptr)) { \
-                       ++ptr; \
-                       --len; \
-               } \
-               while (len > 0 && PHP_HTTP_IS_CTYPE(space, ptr[len - 1])) { \
-                       --len; \
-               } \
-                \
-               if (len > 0) { \
-                       if (parser->_val.str) { \
-                               parser->_val.str = erealloc(parser->_val.str, parser->_val.len + len + 2); \
-                               parser->_val.str[parser->_val.len++] = ' '; \
-                               memcpy(&parser->_val.str[parser->_val.len], ptr, len); \
-                               parser->_val.len += len; \
-                               parser->_val.str[parser->_val.len] = '\0'; \
-                       } else { \
-                               parser->_val.len = len; \
-                               parser->_val.str = estrndup(ptr, len); \
-                       } \
-               } \
-               php_http_buffer_cut(buffer, 0, slen + eol_len); \
-       } while (0)
-
-                               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);
-                               } 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);
-                               } else {
-                                       return php_http_header_parser_state_push(parser, 1, 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);
-                               } 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);
-                               } else {
-                                       /* keep feeding */
-                                       return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX);
-                               }
-                               break;
-
-                       case PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE:
-                               if (parser->_key.str && parser->_val.str) {
-                                       zval array, **exist;
-                                       size_t valid_len = strlen(parser->_val.str);
-
-                                       /* check for truncation */
-                                       if (valid_len != parser->_val.len) {
-                                               php_http_header_parser_error(valid_len, parser->_val.str, parser->_val.len, NULL TSRMLS_CC);
-
-                                               PTR_SET(parser->_key.str, NULL);
-                                               PTR_SET(parser->_val.str, NULL);
-
-                                               return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
-                                       }
-
-                                       if (!headers && callback_func) {
-                                               callback_func(callback_arg, &headers, NULL TSRMLS_CC);
-                                       }
-
-                                       INIT_PZVAL_ARRAY(&array, headers);
-                                       php_http_pretty_key(parser->_key.str, parser->_key.len, 1, 1);
-                                       if (SUCCESS == zend_symtable_find(headers, parser->_key.str, parser->_key.len + 1, (void *) &exist)) {
-                                               convert_to_array(*exist);
-                                               add_next_index_stringl(*exist, parser->_val.str, parser->_val.len, 0);
-                                       } else {
-                                               add_assoc_stringl_ex(&array, parser->_key.str, parser->_key.len + 1, parser->_val.str, parser->_val.len, 0);
-                                       }
-                                       parser->_val.str = NULL;
-                               }
-
-                               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);
-                               break;
-
-                       case PHP_HTTP_HEADER_PARSER_STATE_DONE:
-                               return PHP_HTTP_HEADER_PARSER_STATE_DONE;
-               }
-       }
-
-       return php_http_header_parser_state_is(parser);
-}
-
-php_http_header_parser_state_t php_http_header_parser_parse_stream(php_http_header_parser_t *parser, php_http_buffer_t *buf, php_stream *s, unsigned flags, HashTable *headers, php_http_info_callback_t callback_func, void *callback_arg)
-{
-       php_http_header_parser_state_t state = PHP_HTTP_HEADER_PARSER_STATE_START;
-       TSRMLS_FETCH_FROM_CTX(parser->ts);
-
-       if (!buf->data) {
-               php_http_buffer_resize_ex(buf, 0x1000, 1, 0);
-       }
-       while (1) {
-               size_t justread = 0;
-#if DBG_PARSER
-               const char *states[] = {"START", "KEY", "VALUE", "VALUE_EX", "HEADER_DONE", "DONE"};
-               fprintf(stderr, "#SHP: %s (f:%u)\n", states[state], flags);
-#endif
-               /* resize if needed */
-               if (buf->free < 0x1000) {
-                       php_http_buffer_resize_ex(buf, 0x1000, 1, 0);
-               }
-               switch (state) {
-               case PHP_HTTP_HEADER_PARSER_STATE_FAILURE:
-               case PHP_HTTP_HEADER_PARSER_STATE_DONE:
-                       return state;
-
-               default:
-                       /* read line */
-                       php_stream_get_line(s, buf->data + buf->used, buf->free, &justread);
-                       /* if we fail reading a whole line, try a single char */
-                       if (!justread) {
-                               int c = php_stream_getc(s);
-
-                               if (c != EOF) {
-                                       char s[1] = {c};
-                                       justread = php_http_buffer_append(buf, s, 1);
-                               }
-                       }
-                       php_http_buffer_account(buf, justread);
-               }
-
-               if (justread) {
-                       state = php_http_header_parser_parse(parser, buf, flags, headers, callback_func, callback_arg);
-               } else if (php_stream_eof(s)) {
-                       return php_http_header_parser_parse(parser, buf, flags | PHP_HTTP_HEADER_PARSER_CLEANUP, headers, callback_func, callback_arg);
-               } else  {
-                       return state;
-               }
-       }
-
-       return PHP_HTTP_HEADER_PARSER_STATE_DONE;
-}
-
-zend_class_entry *php_http_header_parser_class_entry;
-static zend_object_handlers php_http_header_parser_object_handlers;
-
-zend_object_value php_http_header_parser_object_new(zend_class_entry *ce TSRMLS_DC)
-{
-       return php_http_header_parser_object_new_ex(ce, NULL, NULL TSRMLS_CC);
-}
-
-zend_object_value php_http_header_parser_object_new_ex(zend_class_entry *ce, php_http_header_parser_t *parser, php_http_header_parser_object_t **ptr TSRMLS_DC)
-{
-       php_http_header_parser_object_t *o;
-
-       o = ecalloc(1, sizeof(php_http_header_parser_object_t));
-       zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
-       object_properties_init((zend_object *) o, ce);
-
-       if (ptr) {
-               *ptr = o;
-       }
-
-       if (parser) {
-               o->parser = parser;
-       } else {
-               o->parser = php_http_header_parser_init(NULL TSRMLS_CC);
-       }
-       o->buffer = php_http_buffer_new();
-
-       o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_header_parser_object_free, NULL TSRMLS_CC);
-       o->zv.handlers = &php_http_header_parser_object_handlers;
-
-       return o->zv;
-}
-
-void php_http_header_parser_object_free(void *object TSRMLS_DC)
-{
-       php_http_header_parser_object_t *o = (php_http_header_parser_object_t *) object;
-
-       if (o->parser) {
-               php_http_header_parser_free(&o->parser);
-       }
-       if (o->buffer) {
-               php_http_buffer_free(&o->buffer);
-       }
-       zend_object_std_dtor((zend_object *) o TSRMLS_CC);
-       efree(o);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeaderParser_getState, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpHeaderParser, getState)
-{
-       php_http_header_parser_object_t *parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       zend_parse_parameters_none();
-       /* always return the real state */
-       RETVAL_LONG(php_http_header_parser_state_is(parser_obj->parser));
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeaderParser_parse, 0, 0, 3)
-       ZEND_ARG_INFO(0, data)
-       ZEND_ARG_INFO(0, flags)
-       ZEND_ARG_ARRAY_INFO(1, headers, 1)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpHeaderParser, parse)
-{
-       php_http_header_parser_object_t *parser_obj;
-       zval *zmsg;
-       char *data_str;
-       int data_len;
-       long flags;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slz", &data_str, &data_len, &flags, &zmsg), invalid_arg, return);
-
-       if (Z_TYPE_P(zmsg) != IS_ARRAY) {
-               zval_dtor(zmsg);
-               array_init(zmsg);
-       }
-       parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-       php_http_buffer_append(parser_obj->buffer, data_str, data_len);
-       RETVAL_LONG(php_http_header_parser_parse(parser_obj->parser, parser_obj->buffer, flags, Z_ARRVAL_P(zmsg), NULL, NULL));
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeaderParser_stream, 0, 0, 3)
-       ZEND_ARG_INFO(0, stream)
-       ZEND_ARG_INFO(0, flags)
-       ZEND_ARG_ARRAY_INFO(1, headers, 1)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpHeaderParser, stream)
-{
-       php_http_header_parser_object_t *parser_obj;
-       zend_error_handling zeh;
-       zval *zmsg, *zstream;
-       php_stream *s;
-       long flags;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz", &zstream, &flags, &zmsg), invalid_arg, return);
-
-       zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC);
-       php_stream_from_zval(s, &zstream);
-       zend_restore_error_handling(&zeh TSRMLS_CC);
-
-       if (Z_TYPE_P(zmsg) != IS_ARRAY) {
-               zval_dtor(zmsg);
-               array_init(zmsg);
-       }
-       parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-       RETVAL_LONG(php_http_header_parser_parse_stream(parser_obj->parser, parser_obj->buffer, s, flags, Z_ARRVAL_P(zmsg), NULL, NULL));
-}
-
-static zend_function_entry php_http_header_parser_methods[] = {
-               PHP_ME(HttpHeaderParser, getState, ai_HttpHeaderParser_getState, ZEND_ACC_PUBLIC)
-               PHP_ME(HttpHeaderParser, parse, ai_HttpHeaderParser_parse, ZEND_ACC_PUBLIC)
-               PHP_ME(HttpHeaderParser, stream, ai_HttpHeaderParser_stream, ZEND_ACC_PUBLIC)
-               {NULL, NULL, NULL}
-};
-
-PHP_MINIT_FUNCTION(http_header_parser)
-{
-       zend_class_entry ce;
-
-       INIT_NS_CLASS_ENTRY(ce, "http\\Header", "Parser", php_http_header_parser_methods);
-       php_http_header_parser_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
-       memcpy(&php_http_header_parser_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
-       php_http_header_parser_class_entry->create_object = php_http_header_parser_object_new;
-       php_http_header_parser_object_handlers.clone_obj = NULL;
-
-       zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("CLEANUP"), PHP_HTTP_HEADER_PARSER_CLEANUP TSRMLS_CC);
-
-       zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_FAILURE"), PHP_HTTP_HEADER_PARSER_STATE_FAILURE TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_START"), PHP_HTTP_HEADER_PARSER_STATE_START TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_KEY"), PHP_HTTP_HEADER_PARSER_STATE_KEY TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_VALUE"), PHP_HTTP_HEADER_PARSER_STATE_VALUE TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_VALUE_EX"), PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_HEADER_DONE"), PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_DONE"), PHP_HTTP_HEADER_PARSER_STATE_DONE TSRMLS_CC);
-
-       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/php_http_header_parser.h b/php_http_header_parser.h
deleted file mode 100644 (file)
index ed9ecaf..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_HEADER_PARSER_H
-#define PHP_HTTP_HEADER_PARSER_H
-
-#include "php_http_info.h"
-
-typedef enum php_http_header_parser_state {
-       PHP_HTTP_HEADER_PARSER_STATE_FAILURE = FAILURE,
-       PHP_HTTP_HEADER_PARSER_STATE_START = 0,
-       PHP_HTTP_HEADER_PARSER_STATE_KEY,
-       PHP_HTTP_HEADER_PARSER_STATE_VALUE,
-       PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX,
-       PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE,
-       PHP_HTTP_HEADER_PARSER_STATE_DONE
-} php_http_header_parser_state_t;
-
-#define PHP_HTTP_HEADER_PARSER_CLEANUP 0x1
-
-typedef struct php_http_header_parser {
-       zend_ptr_stack stack;
-       php_http_info_t info;
-       struct {
-               char *str;
-               size_t len;
-       } _key;
-       struct {
-               char *str;
-               size_t len;
-       } _val;
-#ifdef ZTS
-       void ***ts;
-#endif
-} php_http_header_parser_t;
-
-PHP_HTTP_API php_http_header_parser_t *php_http_header_parser_init(php_http_header_parser_t *parser TSRMLS_DC);
-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);
-PHP_HTTP_API php_http_header_parser_state_t php_http_headerparser_parse_stream(php_http_header_parser_t *parser, php_http_buffer_t *buffer, php_stream *s, unsigned flags, HashTable *headers, php_http_info_callback_t callback_func, void *callback_arg);
-
-typedef struct php_http_header_parser_object {
-       zend_object zo;
-       zend_object_value zv;
-       php_http_buffer_t *buffer;
-       php_http_header_parser_t *parser;
-} php_http_header_parser_object_t;
-
-PHP_HTTP_API zend_class_entry *php_http_header_parser_class_entry;
-
-PHP_MINIT_FUNCTION(http_header_parser);
-
-zend_object_value php_http_header_parser_object_new(zend_class_entry *ce TSRMLS_DC);
-zend_object_value php_http_header_parser_object_new_ex(zend_class_entry *ce, php_http_header_parser_t *parser, php_http_header_parser_object_t **ptr TSRMLS_DC);
-void php_http_header_parser_object_free(void *object TSRMLS_DC);
-
-#endif /* PHP_HTTP_HEADER_PARSER_H */
-
-/*
- * 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/php_http_info.c b/php_http_info.c
deleted file mode 100644 (file)
index 4fb067f..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-php_http_info_t *php_http_info_init(php_http_info_t *i TSRMLS_DC)
-{
-       if (!i) {
-               i = emalloc(sizeof(*i));
-       }
-
-       memset(i, 0, sizeof(*i));
-
-       return i;
-}
-
-void php_http_info_dtor(php_http_info_t *i)
-{
-       switch (i->type) {
-               case PHP_HTTP_REQUEST:
-                       PTR_SET(PHP_HTTP_INFO(i).request.method, NULL);
-                       PTR_SET(PHP_HTTP_INFO(i).request.url, NULL);
-                       break;
-               
-               case PHP_HTTP_RESPONSE:
-                       PTR_SET(PHP_HTTP_INFO(i).response.status, NULL);
-                       break;
-               
-               default:
-                       break;
-       }
-}
-
-void php_http_info_free(php_http_info_t **i)
-{
-       if (*i) {
-               php_http_info_dtor(*i);
-               efree(*i);
-               *i = NULL;
-       }
-}
-
-php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_header TSRMLS_DC)
-{
-       const char *end, *http;
-       zend_bool free_info = !info;
-       
-       /* sane parameter */
-       if ((!pre_header) || (!*pre_header)) {
-               return NULL;
-       }
-       
-       /* where's the end of the line */
-       if (!(end = php_http_locate_eol(pre_header, NULL))) {
-               end = pre_header + strlen(pre_header);
-       }
-       
-       /* there must be HTTP/1.x in the line */
-       if (!(http = php_http_locate_str(pre_header, end - pre_header, "HTTP/", lenof("HTTP/")))) {
-               return NULL;
-       }
-       
-       info = php_http_info_init(info TSRMLS_CC);
-
-       /* and nothing than SPACE or NUL after HTTP/X.x */
-       if (!php_http_version_parse(&info->http.version, http TSRMLS_CC)
-       ||      (http[lenof("HTTP/X.x")] && (!PHP_HTTP_IS_CTYPE(space, http[lenof("HTTP/X.x")])))) {
-               if (free_info) {
-                       php_http_info_free(&info);
-               }
-               return NULL;
-       }
-
-#if 0
-       {
-               char *line = estrndup(pre_header, end - pre_header);
-               fprintf(stderr, "http_parse_info('%s')\n", line);
-               efree(line);
-       }
-#endif
-
-       /* is response */
-       if (pre_header == http) {
-               const char *status = NULL, *code = http + sizeof("HTTP/X.x");
-               
-               info->type = PHP_HTTP_RESPONSE;
-               while (' ' == *code) ++code;
-               if (code && 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');
-                       PHP_HTTP_INFO(info).response.code +=     *code++ - '0';
-                       if (PHP_HTTP_INFO(info).response.code < 100 || PHP_HTTP_INFO(info).response.code > 599) {
-                               if (free_info) {
-                                       php_http_info_free(&info);
-                               }
-                               return NULL;
-                       }
-                       status = code;
-               } else {
-                       PHP_HTTP_INFO(info).response.code = 0;
-               }
-               if (status && end > status) {
-                       while (' ' == *status) ++status;
-                       PHP_HTTP_INFO(info).response.status = estrndup(status, end - status);
-               } else {
-                       PHP_HTTP_INFO(info).response.status = NULL;
-               }
-               
-               return info;
-       }
-       
-       /* is request */
-       else if (*(http - 1) == ' ' && (!http[lenof("HTTP/X.x")] || http[lenof("HTTP/X.x")] == '\r' || http[lenof("HTTP/X.x")] == '\n')) {
-               const char *url = strchr(pre_header, ' ');
-               
-               info->type = PHP_HTTP_REQUEST;
-               if (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 (' ' == *(http-1)) --http;
-
-                       if (http > url) {
-                               /* CONNECT presents an authority only */
-                               if (strcasecmp(PHP_HTTP_INFO(info).request.method, "CONNECT")) {
-                                       PHP_HTTP_INFO(info).request.url = php_http_url_parse(url, http - url, ~0 TSRMLS_CC);
-                               } else {
-                                       PHP_HTTP_INFO(info).request.url = php_http_url_parse_authority(url, http - url, ~0 TSRMLS_CC);
-                               }
-                               if (!PHP_HTTP_INFO(info).request.url) {
-                                       PTR_SET(PHP_HTTP_INFO(info).request.method, NULL);
-                                       return NULL;
-                               }
-                       } else {
-                               PTR_SET(PHP_HTTP_INFO(info).request.method, NULL);
-                               return NULL;
-                       }
-               } else {
-                       PHP_HTTP_INFO(info).request.method = NULL;
-                       PHP_HTTP_INFO(info).request.url = NULL;
-               }
-               
-               return info;
-       }
-
-       /* some darn header containing HTTP/X.x */
-       else {
-               if (free_info) {
-                       php_http_info_free(&info);
-               }
-               return NULL;
-       }
-}
-
-/*
- * 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/php_http_info.h b/php_http_info.h
deleted file mode 100644 (file)
index 4f02908..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_INFO_H
-#define PHP_HTTP_INFO_H
-
-#include "php_http_version.h"
-#include "php_http_url.h"
-
-#define PHP_HTTP_INFO_REQUEST_FMT_ARGS(_http_ptr, tmp, eol) "%s %s HTTP/%u.%u" eol, \
-                               (_http_ptr)->info.request.method?(_http_ptr)->info.request.method:"UNKNOWN", \
-                               (_http_ptr)->info.request.method&&!strcasecmp((_http_ptr)->info.request.method,"CONNECT")?( \
-                               (_http_ptr)->info.request.url?php_http_url_authority_to_string((_http_ptr)->info.request.url, &(tmp), NULL):"0"):( \
-                               (_http_ptr)->info.request.url?php_http_url_to_string((_http_ptr)->info.request.url, &(tmp), NULL, 0):"/"), \
-                               (_http_ptr)->version.major||(_http_ptr)->version.major?(_http_ptr)->version.major:1, \
-                               (_http_ptr)->version.major||(_http_ptr)->version.minor?(_http_ptr)->version.minor:1
-
-#define PHP_HTTP_INFO_RESPONSE_FMT_ARGS(_http_ptr, tmp, eol) "HTTP/%u.%u %d%s%s" eol, \
-                               (_http_ptr)->version.major||(_http_ptr)->version.major?(_http_ptr)->version.major:1, \
-                               (_http_ptr)->version.major||(_http_ptr)->version.minor?(_http_ptr)->version.minor:1, \
-                               (_http_ptr)->info.response.code?(_http_ptr)->info.response.code:200, \
-                               (_http_ptr)->info.response.status&&*(_http_ptr)->info.response.status ? " ":"", \
-                               STR_PTR((_http_ptr)->info.response.status)
-
-typedef struct php_http_info_data {
-       union {
-               /* GET /foo/bar */
-               struct { char *method; php_http_url_t *url; } request;
-               /* 200 Ok */
-               struct { unsigned code; char *status; } response;
-       } info;
-       php_http_version_t version;
-} php_http_info_data_t;
-
-typedef enum php_http_info_type {
-       PHP_HTTP_NONE = 0,
-       PHP_HTTP_REQUEST,
-       PHP_HTTP_RESPONSE
-} php_http_info_type_t;
-
-#define PHP_HTTP_INFO(ptr) (ptr)->http.info
-#define PHP_HTTP_INFO_IMPL(_http, _type) \
-       php_http_info_data_t _http; \
-       php_http_info_type_t _type;
-
-typedef struct php_http_info {
-       PHP_HTTP_INFO_IMPL(http, type)
-} php_http_info_t;
-
-typedef zend_bool (*php_http_info_callback_t)(void **callback_data, HashTable **headers, php_http_info_t *info TSRMLS_DC);
-
-PHP_HTTP_API php_http_info_t *php_http_info_init(php_http_info_t *info TSRMLS_DC);
-PHP_HTTP_API php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_header TSRMLS_DC);
-PHP_HTTP_API void php_http_info_dtor(php_http_info_t *info);
-PHP_HTTP_API void php_http_info_free(php_http_info_t **info);
-
-#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/php_http_message.c b/php_http_message.c
deleted file mode 100644 (file)
index c6b03ff..0000000
+++ /dev/null
@@ -1,2085 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-static void message_headers(php_http_message_t *msg, php_http_buffer_t *str);
-
-zend_bool php_http_message_info_callback(php_http_message_t **message, HashTable **headers, php_http_info_t *info TSRMLS_DC)
-{
-       php_http_message_t *old = *message;
-
-       /* advance message */
-       if (!old || old->type || zend_hash_num_elements(&old->hdrs)) {
-               (*message) = php_http_message_init(NULL, 0, NULL TSRMLS_CC);
-               (*message)->parent = old;
-               if (headers) {
-                       (*headers) = &((*message)->hdrs);
-               }
-       }
-
-       if (info) {
-               php_http_message_set_info(*message, info);
-       }
-
-       return old != *message;
-}
-
-php_http_message_t *php_http_message_init(php_http_message_t *message, php_http_message_type_t type, php_http_message_body_t *body TSRMLS_DC)
-{
-       if (!message) {
-               message = emalloc(sizeof(*message));
-       }
-       memset(message, 0, sizeof(*message));
-       TSRMLS_SET_CTX(message->ts);
-
-       php_http_message_set_type(message, type);
-       message->http.version.major = 1;
-       message->http.version.minor = 1;
-       zend_hash_init(&message->hdrs, 0, NULL, ZVAL_PTR_DTOR, 0);
-       message->body = body ? body : php_http_message_body_init(NULL, NULL TSRMLS_CC);
-
-       return message;
-}
-
-php_http_message_t *php_http_message_init_env(php_http_message_t *message, php_http_message_type_t type TSRMLS_DC)
-{
-       int free_msg = !message;
-       zval *sval, tval;
-       php_http_message_body_t *mbody;
-       
-       switch (type) {
-               case PHP_HTTP_REQUEST:
-                       mbody = php_http_env_get_request_body(TSRMLS_C);
-                       php_http_message_body_addref(mbody);
-                       message = php_http_message_init(message, type, mbody TSRMLS_CC);
-                       if ((sval = php_http_env_get_server_var(ZEND_STRL("SERVER_PROTOCOL"), 1 TSRMLS_CC)) && !strncmp(Z_STRVAL_P(sval), "HTTP/", lenof("HTTP/"))) {
-                               php_http_version_parse(&message->http.version, Z_STRVAL_P(sval) TSRMLS_CC);
-                       }
-                       if ((sval = php_http_env_get_server_var(ZEND_STRL("REQUEST_METHOD"), 1 TSRMLS_CC))) {
-                               message->http.info.request.method = estrdup(Z_STRVAL_P(sval));
-                       }
-                       if ((sval = php_http_env_get_server_var(ZEND_STRL("REQUEST_URI"), 1 TSRMLS_CC))) {
-                               message->http.info.request.url = php_http_url_parse(Z_STRVAL_P(sval), Z_STRLEN_P(sval), ~0 TSRMLS_CC);
-                       }
-                       
-                       php_http_env_get_request_headers(&message->hdrs TSRMLS_CC);
-                       break;
-                       
-               case PHP_HTTP_RESPONSE:
-                       message = php_http_message_init(NULL, type, NULL TSRMLS_CC);
-                       if (!SG(sapi_headers).http_status_line || !php_http_info_parse((php_http_info_t *) &message->http, SG(sapi_headers).http_status_line TSRMLS_CC)) {
-                               if (!(message->http.info.response.code = SG(sapi_headers).http_response_code)) {
-                                       message->http.info.response.code = 200;
-                               }
-                               message->http.info.response.status = estrdup(php_http_env_get_response_status_for_code(message->http.info.response.code));
-                       }
-                       
-                       php_http_env_get_response_headers(&message->hdrs TSRMLS_CC);
-#if PHP_VERSION_ID >= 50400
-                       if (php_output_get_level(TSRMLS_C)) {
-                               if (php_output_get_status(TSRMLS_C) & PHP_OUTPUT_SENT) {
-                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not fetch response body, output has already been sent at %s:%d", php_output_get_start_filename(TSRMLS_C), php_output_get_start_lineno(TSRMLS_C));
-                                       goto error;
-                               } else if (SUCCESS != php_output_get_contents(&tval TSRMLS_CC)) {
-                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not fetch response body");
-                                       goto error;
-                               } else {
-                                       php_http_message_body_append(message->body, Z_STRVAL(tval), Z_STRLEN(tval));
-                                       zval_dtor(&tval);
-                               }
-                       }
-#else
-                       if (OG(ob_nesting_level)) {
-                               if (php_get_output_start_filename(TSRMLS_C)) {
-                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not fetch response body, output has already been sent at %s:%d", php_get_output_start_filename(TSRMLS_C), php_get_output_start_lineno(TSRMLS_C));
-                                       goto error;
-                               } else if (SUCCESS != php_ob_get_buffer(&tval TSRMLS_CC)) {
-                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not fetch response body");
-                                       goto error;
-                               } else {
-                                       php_http_message_body_append(message->body, Z_STRVAL(tval), Z_STRLEN(tval));
-                                       zval_dtor(&tval);
-                               }
-                       }
-#endif
-                       break;
-                       
-               default:
-               error:
-                       if (free_msg) {
-                               if (message) {
-                                       php_http_message_free(&message);
-                               }
-                       } else {
-                               message = NULL;
-                       }
-                       break;
-       }
-       
-       return message;
-}
-
-php_http_message_t *php_http_message_parse(php_http_message_t *msg, const char *str, size_t len, zend_bool greedy TSRMLS_DC)
-{
-       php_http_message_parser_t p;
-       php_http_buffer_t buf;
-       unsigned flags = PHP_HTTP_MESSAGE_PARSER_CLEANUP;
-       int free_msg;
-
-       php_http_buffer_from_string_ex(&buf, str, len);
-       php_http_message_parser_init(&p TSRMLS_CC);
-
-       if ((free_msg = !msg)) {
-               msg = php_http_message_init(NULL, 0, NULL TSRMLS_CC);
-       }
-
-       if (greedy) {
-               flags |= PHP_HTTP_MESSAGE_PARSER_GREEDY;
-       }
-       if (PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE == php_http_message_parser_parse(&p, &buf, flags, &msg)) {
-               if (free_msg) {
-                       php_http_message_free(&msg);
-               }
-               msg = NULL;
-       }
-
-       php_http_message_parser_dtor(&p);
-       php_http_buffer_dtor(&buf);
-
-       return msg;
-}
-
-zval *php_http_message_header(php_http_message_t *msg, const char *key_str, size_t key_len, int join)
-{
-       zval *ret = NULL, **header;
-       char *key;
-       ALLOCA_FLAG(free_key);
-
-       key = do_alloca(key_len + 1, free_key);
-       memcpy(key, key_str, key_len);
-       key[key_len] = '\0';
-       php_http_pretty_key(key, key_len, 1, 1);
-
-       if (SUCCESS == zend_symtable_find(&msg->hdrs, key, key_len + 1, (void *) &header)) {
-               if (join && Z_TYPE_PP(header) == IS_ARRAY) {
-                       TSRMLS_FETCH_FROM_CTX(msg->ts);
-
-                       ret = php_http_header_value_to_string(*header TSRMLS_CC);
-               } else {
-                       Z_ADDREF_PP(header);
-                       ret = *header;
-               }
-       }
-
-       free_alloca(key, free_key);
-
-       return ret;
-}
-
-zend_bool php_http_message_is_multipart(php_http_message_t *msg, char **boundary)
-{
-       zval *ct = php_http_message_header(msg, ZEND_STRL("Content-Type"), 1);
-       zend_bool is_multipart = 0;
-       TSRMLS_FETCH_FROM_CTX(msg->ts);
-
-       if (ct) {
-               php_http_params_opts_t popts;
-               HashTable params;
-
-               ZEND_INIT_SYMTABLE(&params);
-               php_http_params_opts_default_get(&popts);
-               popts.input.str = Z_STRVAL_P(ct);
-               popts.input.len = Z_STRLEN_P(ct);
-
-               if (php_http_params_parse(&params, &popts TSRMLS_CC)) {
-                       zval **cur, **arg;
-                       char *ct_str;
-
-                       zend_hash_internal_pointer_reset(&params);
-
-                       if (SUCCESS == zend_hash_get_current_data(&params, (void *) &cur)
-                       &&      Z_TYPE_PP(cur) == IS_ARRAY
-                       &&      HASH_KEY_IS_STRING == zend_hash_get_current_key(&params, &ct_str, NULL, 0)
-                       ) {
-                               if (php_http_match(ct_str, "multipart", PHP_HTTP_MATCH_WORD)) {
-                                       is_multipart = 1;
-
-                                       /* get boundary */
-                                       if (boundary
-                                       &&      SUCCESS == zend_hash_find(Z_ARRVAL_PP(cur), ZEND_STRS("arguments"), (void *) &arg)
-                                       &&      Z_TYPE_PP(arg) == IS_ARRAY
-                                       ) {
-                                               zval **val;
-                                               HashPosition pos;
-                                               php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-
-                                               FOREACH_KEYVAL(pos, *arg, key, val) {
-                                                       if (key.type == HASH_KEY_IS_STRING && !strcasecmp(key.str, "boundary")) {
-                                                               zval *bnd = php_http_ztyp(IS_STRING, *val);
-
-                                                               if (Z_STRLEN_P(bnd)) {
-                                                                       *boundary = estrndup(Z_STRVAL_P(bnd), Z_STRLEN_P(bnd));
-                                                               }
-                                                               zval_ptr_dtor(&bnd);
-                                                       }
-                                               }
-                                       }
-                               }
-                       }
-               }
-               zend_hash_destroy(&params);
-               zval_ptr_dtor(&ct);
-       }
-
-       return is_multipart;
-}
-
-/* */
-void php_http_message_set_type(php_http_message_t *message, php_http_message_type_t type)
-{
-       /* just act if different */
-       if (type != message->type) {
-
-               /* free request info */
-               switch (message->type) {
-                       case PHP_HTTP_REQUEST:
-                               PTR_FREE(message->http.info.request.method);
-                               PTR_FREE(message->http.info.request.url);
-                               break;
-                       
-                       case PHP_HTTP_RESPONSE:
-                               PTR_FREE(message->http.info.response.status);
-                               break;
-                       
-                       default:
-                               break;
-               }
-
-               message->type = type;
-               memset(&message->http, 0, sizeof(message->http));
-       }
-}
-
-void php_http_message_set_info(php_http_message_t *message, php_http_info_t *info)
-{
-       php_http_message_set_type(message, info->type);
-       message->http.version = info->http.version;
-       switch (message->type) {
-               case PHP_HTTP_REQUEST:
-                       PTR_SET(PHP_HTTP_INFO(message).request.url, PHP_HTTP_INFO(info).request.url ? php_http_url_copy(PHP_HTTP_INFO(info).request.url, 0) : NULL);
-                       PTR_SET(PHP_HTTP_INFO(message).request.method, PHP_HTTP_INFO(info).request.method ? estrdup(PHP_HTTP_INFO(info).request.method) : NULL);
-                       break;
-               
-               case PHP_HTTP_RESPONSE:
-                       PHP_HTTP_INFO(message).response.code = PHP_HTTP_INFO(info).response.code;
-                       PTR_SET(PHP_HTTP_INFO(message).response.status, PHP_HTTP_INFO(info).response.status ? estrdup(PHP_HTTP_INFO(info).response.status) : NULL);
-                       break;
-               
-               default:
-                       break;
-       }
-}
-
-void php_http_message_update_headers(php_http_message_t *msg)
-{
-       zval *h;
-       size_t size;
-
-       if (php_http_message_body_stream(msg->body)->readfilters.head) {
-               /* if a read stream filter is attached to the body the caller must also care for the headers */
-       } else if ((h = php_http_message_header(msg, ZEND_STRL("Content-Range"), 0))) {
-               /* don't mess around with a Content-Range message */
-               zval_ptr_dtor(&h);
-       } else if ((size = php_http_message_body_size(msg->body))) {
-               MAKE_STD_ZVAL(h);
-               ZVAL_LONG(h, size);
-               zend_hash_update(&msg->hdrs, "Content-Length", sizeof("Content-Length"), &h, sizeof(zval *), NULL);
-
-               if (msg->body->boundary) {
-                       char *str;
-                       size_t len;
-
-                       if (!(h = php_http_message_header(msg, ZEND_STRL("Content-Type"), 1))) {
-                               len = spprintf(&str, 0, "multipart/form-data; boundary=\"%s\"", msg->body->boundary);
-                               MAKE_STD_ZVAL(h);
-                               ZVAL_STRINGL(h, str, len, 0);
-                               zend_hash_update(&msg->hdrs, "Content-Type", sizeof("Content-Type"), &h, sizeof(zval *), NULL);
-                       } else if (!php_http_match(Z_STRVAL_P(h), "boundary=", PHP_HTTP_MATCH_WORD)) {
-                               zval_dtor(h);
-                               Z_STRLEN_P(h) = spprintf(&Z_STRVAL_P(h), 0, "%s; boundary=\"%s\"", Z_STRVAL_P(h), msg->body->boundary);
-                               zend_hash_update(&msg->hdrs, "Content-Type", sizeof("Content-Type"), &h, sizeof(zval *), NULL);
-                       } else {
-                               zval_ptr_dtor(&h);
-                       }
-               }
-       } else if ((h = php_http_message_header(msg, ZEND_STRL("Content-Length"), 1))) {
-               zval *h_cpy = php_http_ztyp(IS_LONG, h);
-
-               zval_ptr_dtor(&h);
-               if (Z_LVAL_P(h_cpy)) {
-                       /* body->size == 0, so get rid of old Content-Length */
-                       zend_hash_del(&msg->hdrs, "Content-Length", sizeof("Content-Length"));
-               }
-               zval_ptr_dtor(&h_cpy);
-       }
-}
-
-static void message_headers(php_http_message_t *msg, php_http_buffer_t *str)
-{
-       char *tmp = NULL;
-       TSRMLS_FETCH_FROM_CTX(msg->ts);
-
-       switch (msg->type) {
-               case PHP_HTTP_REQUEST:
-                       php_http_buffer_appendf(str, PHP_HTTP_INFO_REQUEST_FMT_ARGS(&msg->http, tmp, PHP_HTTP_CRLF));
-                       PTR_FREE(tmp);
-                       break;
-
-               case PHP_HTTP_RESPONSE:
-                       php_http_buffer_appendf(str, PHP_HTTP_INFO_RESPONSE_FMT_ARGS(&msg->http, tmp, PHP_HTTP_CRLF));
-                       PTR_FREE(tmp);
-                       break;
-
-               default:
-                       break;
-       }
-
-       php_http_message_update_headers(msg);
-       php_http_header_to_string(str, &msg->hdrs TSRMLS_CC);
-}
-
-void php_http_message_to_callback(php_http_message_t *msg, php_http_pass_callback_t cb, void *cb_arg)
-{
-       php_http_buffer_t str;
-
-       php_http_buffer_init_ex(&str, 0x1000, 0);
-       message_headers(msg, &str);
-       cb(cb_arg, str.data, str.used);
-       php_http_buffer_dtor(&str);
-
-       if (php_http_message_body_size(msg->body)) {
-               cb(cb_arg, ZEND_STRL(PHP_HTTP_CRLF));
-               php_http_message_body_to_callback(msg->body, cb, cb_arg, 0, 0);
-       }
-}
-
-void php_http_message_to_string(php_http_message_t *msg, char **string, size_t *length)
-{
-       php_http_buffer_t str;
-       char *data;
-
-       php_http_buffer_init_ex(&str, 0x1000, 0);
-       message_headers(msg, &str);
-       if (php_http_message_body_size(msg->body)) {
-               php_http_buffer_appends(&str, PHP_HTTP_CRLF);
-               php_http_message_body_to_callback(msg->body, (php_http_pass_callback_t) php_http_buffer_append, &str, 0, 0);
-       }
-
-       data = php_http_buffer_data(&str, string, length);
-       if (!string) {
-               efree(data);
-       }
-
-       php_http_buffer_dtor(&str);
-}
-
-void php_http_message_serialize(php_http_message_t *message, char **string, size_t *length)
-{
-       char *buf;
-       php_http_buffer_t str;
-       php_http_message_t *msg;
-
-       php_http_buffer_init(&str);
-
-       msg = message = php_http_message_reverse(message);
-       do {
-               php_http_message_to_callback(message, (php_http_pass_callback_t) php_http_buffer_append, &str);
-               php_http_buffer_appends(&str, PHP_HTTP_CRLF);
-       } while ((message = message->parent));
-       php_http_message_reverse(msg);
-
-       buf = php_http_buffer_data(&str, string, length);
-       if (!string) {
-               efree(buf);
-       }
-
-       php_http_buffer_dtor(&str);
-}
-
-php_http_message_t *php_http_message_reverse(php_http_message_t *msg)
-{
-       int i, c = 0;
-       
-       php_http_message_count(c, msg);
-       
-       if (c > 1) {
-               php_http_message_t *tmp = msg, **arr;
-
-               arr = ecalloc(c, sizeof(**arr));
-               for (i = 0; i < c; ++i) {
-                       arr[i] = tmp;
-                       tmp = tmp->parent;
-               }
-               arr[0]->parent = NULL;
-               for (i = 0; i < c-1; ++i) {
-                       arr[i+1]->parent = arr[i];
-               }
-               
-               msg = arr[c-1];
-               efree(arr);
-       }
-       
-       return msg;
-}
-
-php_http_message_t *php_http_message_zip(php_http_message_t *one, php_http_message_t *two)
-{
-       php_http_message_t *dst = php_http_message_copy(one, NULL), *src = php_http_message_copy(two, NULL), *tmp_dst, *tmp_src, *ret = dst;
-
-       while(dst && src) {
-               tmp_dst = dst->parent;
-               tmp_src = src->parent;
-               dst->parent = src;
-               if (tmp_dst) {
-                       src->parent = tmp_dst;
-               }
-               src = tmp_src;
-               dst = tmp_dst;
-       }
-
-       return ret;
-}
-
-php_http_message_t *php_http_message_copy_ex(php_http_message_t *from, php_http_message_t *to, zend_bool parents)
-{
-       php_http_message_t *temp, *copy = NULL;
-       php_http_info_t info;
-       TSRMLS_FETCH_FROM_CTX(from->ts);
-       
-       if (from) {
-               info.type = from->type;
-               info.http = from->http;
-               
-               copy = temp = php_http_message_init(to, 0, php_http_message_body_copy(from->body, NULL) TSRMLS_CC);
-               php_http_message_set_info(temp, &info);
-               zend_hash_copy(&temp->hdrs, &from->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
-       
-               if (parents) while (from->parent) {
-                       info.type = from->parent->type;
-                       info.http = from->parent->http;
-               
-                       temp->parent = php_http_message_init(NULL, 0, php_http_message_body_copy(from->parent->body, NULL) TSRMLS_CC);
-                       php_http_message_set_info(temp->parent, &info);
-                       zend_hash_copy(&temp->parent->hdrs, &from->parent->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
-               
-                       temp = temp->parent;
-                       from = from->parent;
-               }
-       }
-       
-       return copy;
-}
-
-php_http_message_t *php_http_message_copy(php_http_message_t *from, php_http_message_t *to)
-{
-       return php_http_message_copy_ex(from, to, 1);
-}
-
-void php_http_message_dtor(php_http_message_t *message)
-{
-       if (message) {
-               zend_hash_destroy(&message->hdrs);
-               php_http_message_body_free(&message->body);
-               
-               switch (message->type) {
-                       case PHP_HTTP_REQUEST:
-                               PTR_SET(message->http.info.request.method, NULL);
-                               PTR_SET(message->http.info.request.url, NULL);
-                               break;
-                       
-                       case PHP_HTTP_RESPONSE:
-                               PTR_SET(message->http.info.response.status, NULL);
-                               break;
-                       
-                       default:
-                               break;
-               }
-       }
-}
-
-void php_http_message_free(php_http_message_t **message)
-{
-       if (*message) {
-               if ((*message)->parent) {
-                       php_http_message_free(&(*message)->parent);
-               }
-               php_http_message_dtor(*message);
-               efree(*message);
-               *message = NULL;
-       }
-}
-
-static zval *php_http_message_object_read_prop(zval *object, zval *member, int type PHP_HTTP_ZEND_LITERAL_DC TSRMLS_DC);
-static void php_http_message_object_write_prop(zval *object, zval *member, zval *value PHP_HTTP_ZEND_LITERAL_DC TSRMLS_DC);
-static HashTable *php_http_message_object_get_props(zval *object TSRMLS_DC);
-
-static zend_object_handlers php_http_message_object_handlers;
-static HashTable php_http_message_object_prophandlers;
-
-typedef void (*php_http_message_object_prophandler_func_t)(php_http_message_object_t *o, zval *v TSRMLS_DC);
-
-typedef struct php_http_message_object_prophandler {
-       php_http_message_object_prophandler_func_t read;
-       php_http_message_object_prophandler_func_t write;
-} php_http_message_object_prophandler_t;
-
-static ZEND_RESULT_CODE php_http_message_object_add_prophandler(const char *prop_str, size_t prop_len, php_http_message_object_prophandler_func_t read, php_http_message_object_prophandler_func_t write) {
-       php_http_message_object_prophandler_t h = { read, write };
-       return zend_hash_add(&php_http_message_object_prophandlers, prop_str, prop_len + 1, (void *) &h, sizeof(h), NULL);
-}
-static ZEND_RESULT_CODE php_http_message_object_get_prophandler(const char *prop_str, size_t prop_len, php_http_message_object_prophandler_t **handler) {
-       return zend_hash_find(&php_http_message_object_prophandlers, prop_str, prop_len + 1, (void *) handler);
-}
-static void php_http_message_object_prophandler_get_type(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
-       RETVAL_LONG(obj->message->type);
-}
-static void php_http_message_object_prophandler_set_type(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
-       zval *cpy = php_http_ztyp(IS_LONG, value);
-       php_http_message_set_type(obj->message, Z_LVAL_P(cpy));
-       zval_ptr_dtor(&cpy);
-}
-static void php_http_message_object_prophandler_get_request_method(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
-       if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message) && obj->message->http.info.request.method) {
-               RETVAL_STRING(obj->message->http.info.request.method, 1);
-       } else {
-               RETVAL_NULL();
-       }
-}
-static void php_http_message_object_prophandler_set_request_method(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
-       if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message)) {
-               zval *cpy = php_http_ztyp(IS_STRING, value);
-               PTR_SET(obj->message->http.info.request.method, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
-               zval_ptr_dtor(&cpy);
-       }
-}
-static void php_http_message_object_prophandler_get_request_url(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
-       char *url_str;
-       size_t url_len;
-
-       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_STRINGL(url_str, url_len, 0);
-       } else {
-               RETVAL_NULL();
-       }
-}
-static void php_http_message_object_prophandler_set_request_url(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
-       if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message)) {
-               PTR_SET(obj->message->http.info.request.url, php_http_url_from_zval(value, ~0 TSRMLS_CC));
-       }
-}
-static void php_http_message_object_prophandler_get_response_status(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
-       if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message) && obj->message->http.info.response.status) {
-               RETVAL_STRING(obj->message->http.info.response.status, 1);
-       } else {
-               RETVAL_NULL();
-       }
-}
-static void php_http_message_object_prophandler_set_response_status(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
-       if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message)) {
-               zval *cpy = php_http_ztyp(IS_STRING, value);
-               PTR_SET(obj->message->http.info.response.status, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
-               zval_ptr_dtor(&cpy);
-       }
-}
-static void php_http_message_object_prophandler_get_response_code(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
-       if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message)) {
-               RETVAL_LONG(obj->message->http.info.response.code);
-       } else {
-               RETVAL_NULL();
-       }
-}
-static void php_http_message_object_prophandler_set_response_code(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
-       if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message)) {
-               zval *cpy = php_http_ztyp(IS_LONG, value);
-               obj->message->http.info.response.code = Z_LVAL_P(cpy);
-               PTR_SET(obj->message->http.info.response.status, estrdup(php_http_env_get_response_status_for_code(obj->message->http.info.response.code)));
-               zval_ptr_dtor(&cpy);
-       }
-}
-static void php_http_message_object_prophandler_get_http_version(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
-       char *version_str;
-       size_t version_len;
-
-       php_http_version_to_string(&obj->message->http.version, &version_str, &version_len, NULL, NULL TSRMLS_CC);
-       RETVAL_STRINGL(version_str, version_len, 0);
-}
-static void php_http_message_object_prophandler_set_http_version(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
-       zval *cpy = php_http_ztyp(IS_STRING, value);
-       php_http_version_parse(&obj->message->http.version, Z_STRVAL_P(cpy) TSRMLS_CC);
-       zval_ptr_dtor(&cpy);
-}
-static void php_http_message_object_prophandler_get_headers(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
-       array_init(return_value);
-       zend_hash_copy(Z_ARRVAL_P(return_value), &obj->message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
-}
-static void php_http_message_object_prophandler_set_headers(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
-       zval *cpy = php_http_ztyp(IS_ARRAY, value);
-
-       zend_hash_clean(&obj->message->hdrs);
-       zend_hash_copy(&obj->message->hdrs, Z_ARRVAL_P(cpy), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
-       zval_ptr_dtor(&cpy);
-}
-static void php_http_message_object_prophandler_get_body(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
-       if (obj->body) {
-               RETVAL_OBJVAL(obj->body->zv, 1);
-       } else {
-               RETVAL_NULL();
-       }
-}
-static void php_http_message_object_prophandler_set_body(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
-       php_http_message_object_set_body(obj, value TSRMLS_CC);
-}
-static void php_http_message_object_prophandler_get_parent_message(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
-       if (obj->message->parent) {
-               RETVAL_OBJVAL(obj->parent->zv, 1);
-       } else {
-               RETVAL_NULL();
-       }
-}
-static void php_http_message_object_prophandler_set_parent_message(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
-       if (Z_TYPE_P(value) == IS_OBJECT && instanceof_function(Z_OBJCE_P(value), php_http_message_class_entry TSRMLS_CC)) {
-               php_http_message_object_t *parent_obj = zend_object_store_get_object(value TSRMLS_CC);
-
-               if (obj->message->parent) {
-                       zend_objects_store_del_ref_by_handle(obj->parent->zv.handle TSRMLS_CC);
-               }
-               Z_OBJ_ADDREF_P(value);
-               obj->parent = parent_obj;
-               obj->message->parent = parent_obj->message;
-       }
-}
-
-#define PHP_HTTP_MESSAGE_OBJECT_INIT(obj) \
-       do { \
-               if (!obj->message) { \
-                       obj->message = php_http_message_init(NULL, 0, NULL TSRMLS_CC); \
-               } \
-       } while(0)
-
-
-void php_http_message_object_reverse(zval *this_ptr, zval *return_value TSRMLS_DC)
-{
-       int i = 0;
-       php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-       /* count */
-       php_http_message_count(i, obj->message);
-
-       if (i > 1) {
-               php_http_message_object_t **objects;
-               int last;
-
-               objects = ecalloc(i, sizeof(**objects));
-
-               /* we are the first message */
-               objects[0] = obj;
-
-               /* fetch parents */
-               for (i = 1; obj->parent; ++i) {
-                        objects[i] = obj = obj->parent;
-               }
-
-               /* reorder parents */
-               for (last = --i; i; --i) {
-                       objects[i]->message->parent = objects[i-1]->message;
-                       objects[i]->parent = objects[i-1];
-               }
-
-               objects[0]->message->parent = NULL;
-               objects[0]->parent = NULL;
-
-               /* add ref, because we previously have not been a parent message */
-               Z_OBJ_ADDREF_P(getThis());
-               RETVAL_OBJVAL(objects[last]->zv, 0);
-
-               efree(objects);
-       } else {
-               RETURN_ZVAL(getThis(), 1, 0);
-       }
-}
-
-void php_http_message_object_prepend(zval *this_ptr, zval *prepend, zend_bool top TSRMLS_DC)
-{
-       zval m;
-       php_http_message_t *save_parent_msg = NULL;
-       php_http_message_object_t *save_parent_obj = NULL, *obj = zend_object_store_get_object(this_ptr TSRMLS_CC);
-       php_http_message_object_t *prepend_obj = zend_object_store_get_object(prepend TSRMLS_CC);
-
-       INIT_PZVAL(&m);
-       m.type = IS_OBJECT;
-
-       if (!top) {
-               save_parent_obj = obj->parent;
-               save_parent_msg = obj->message->parent;
-       } else {
-               /* iterate to the most parent object */
-               while (obj->parent) {
-                       obj = obj->parent;
-               }
-       }
-
-       /* prepend */
-       obj->parent = prepend_obj;
-       obj->message->parent = prepend_obj->message;
-
-       /* add ref */
-       zend_objects_store_add_ref(prepend TSRMLS_CC);
-
-       if (!top) {
-               prepend_obj->parent = save_parent_obj;
-               prepend_obj->message->parent = save_parent_msg;
-       }
-}
-
-ZEND_RESULT_CODE php_http_message_object_set_body(php_http_message_object_t *msg_obj, zval *zbody TSRMLS_DC)
-{
-       zval *tmp = NULL;
-       php_stream *s;
-       zend_object_value ov;
-       php_http_message_body_t *body;
-       php_http_message_body_object_t *body_obj;
-
-       switch (Z_TYPE_P(zbody)) {
-               case IS_RESOURCE:
-                       php_stream_from_zval_no_verify(s, &zbody);
-                       if (!s) {
-                               php_http_throw(unexpected_val, "The stream is not a valid resource", NULL);
-                               return FAILURE;
-                       }
-
-                       is_resource:
-
-                       body = php_http_message_body_init(NULL, s TSRMLS_CC);
-                       if (SUCCESS != php_http_new(&ov, php_http_message_body_class_entry, (php_http_new_t) php_http_message_body_object_new_ex, NULL, body, NULL TSRMLS_CC)) {
-                               php_http_message_body_free(&body);
-                               return FAILURE;
-                       }
-                       MAKE_STD_ZVAL(tmp);
-                       ZVAL_OBJVAL(tmp, ov, 0);
-                       zbody = tmp;
-                       break;
-
-               case IS_OBJECT:
-                       if (instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_class_entry TSRMLS_CC)) {
-                               Z_OBJ_ADDREF_P(zbody);
-                               break;
-                       }
-                       /* no break */
-
-               default:
-                       tmp = php_http_ztyp(IS_STRING, zbody);
-                       s = php_stream_temp_new();
-                       php_stream_write(s, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
-                       zval_ptr_dtor(&tmp);
-                       tmp = NULL;
-                       goto is_resource;
-
-       }
-
-       body_obj = zend_object_store_get_object(zbody TSRMLS_CC);
-       if (!body_obj->body) {
-               body_obj->body = php_http_message_body_init(NULL, NULL TSRMLS_CC);
-       }
-       if (msg_obj->body) {
-               zend_objects_store_del_ref_by_handle(msg_obj->body->zv.handle TSRMLS_CC);
-       }
-       if (msg_obj->message) {
-               php_http_message_body_free(&msg_obj->message->body);
-               msg_obj->message->body = php_http_message_body_init(&body_obj->body, NULL TSRMLS_CC);
-       } else {
-               msg_obj->message = php_http_message_init(NULL, 0, php_http_message_body_init(&body_obj->body, NULL TSRMLS_CC) TSRMLS_CC);
-       }
-       msg_obj->body = body_obj;
-
-       if (tmp) {
-               FREE_ZVAL(tmp);
-       }
-       return SUCCESS;
-}
-
-ZEND_RESULT_CODE php_http_message_object_init_body_object(php_http_message_object_t *obj)
-{
-       TSRMLS_FETCH_FROM_CTX(obj->message->ts);
-
-       php_http_message_body_addref(obj->message->body);
-       return php_http_new(NULL, php_http_message_body_class_entry, (php_http_new_t) php_http_message_body_object_new_ex, NULL, obj->message->body, (void *) &obj->body TSRMLS_CC);
-}
-
-zend_object_value php_http_message_object_new(zend_class_entry *ce TSRMLS_DC)
-{
-       return php_http_message_object_new_ex(ce, NULL, NULL TSRMLS_CC);
-}
-
-zend_object_value php_http_message_object_new_ex(zend_class_entry *ce, php_http_message_t *msg, php_http_message_object_t **ptr TSRMLS_DC)
-{
-       php_http_message_object_t *o;
-
-       o = ecalloc(1, sizeof(php_http_message_object_t));
-       zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
-       object_properties_init((zend_object *) o, ce);
-
-       if (ptr) {
-               *ptr = o;
-       }
-
-       if (msg) {
-               o->message = msg;
-               if (msg->parent) {
-                       php_http_message_object_new_ex(ce, msg->parent, &o->parent TSRMLS_CC);
-               }
-               php_http_message_body_object_new_ex(php_http_message_body_class_entry, php_http_message_body_init(&msg->body, NULL TSRMLS_CC), &o->body TSRMLS_CC);
-       }
-
-       o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_message_object_free, NULL TSRMLS_CC);
-       o->zv.handlers = &php_http_message_object_handlers;
-
-       return o->zv;
-}
-
-zend_object_value php_http_message_object_clone(zval *this_ptr TSRMLS_DC)
-{
-       zend_object_value new_ov;
-       php_http_message_object_t *new_obj = NULL;
-       php_http_message_object_t *old_obj = zend_object_store_get_object(this_ptr TSRMLS_CC);
-
-       new_ov = php_http_message_object_new_ex(old_obj->zo.ce, php_http_message_copy(old_obj->message, NULL), &new_obj TSRMLS_CC);
-       zend_objects_clone_members(&new_obj->zo, new_ov, &old_obj->zo, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC);
-
-       return new_ov;
-}
-
-void php_http_message_object_free(void *object TSRMLS_DC)
-{
-       php_http_message_object_t *o = (php_http_message_object_t *) object;
-
-       if (o->iterator) {
-               zval_ptr_dtor(&o->iterator);
-               o->iterator = NULL;
-       }
-       if (o->message) {
-               /* do NOT free recursivly */
-               php_http_message_dtor(o->message);
-               efree(o->message);
-               o->message = NULL;
-       }
-       if (o->parent) {
-               zend_objects_store_del_ref_by_handle(o->parent->zv.handle TSRMLS_CC);
-               o->parent = NULL;
-       }
-       if (o->body) {
-               zend_objects_store_del_ref_by_handle(o->body->zv.handle TSRMLS_CC);
-               o->body = NULL;
-       }
-       zend_object_std_dtor((zend_object *) o TSRMLS_CC);
-       efree(o);
-}
-
-static zval *php_http_message_object_read_prop(zval *object, zval *member, int type PHP_HTTP_ZEND_LITERAL_DC TSRMLS_DC)
-{
-       php_http_message_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
-       php_http_message_object_prophandler_t *handler;
-       zval *return_value, *copy = php_http_ztyp(IS_STRING, member);
-
-       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-       if (SUCCESS == php_http_message_object_get_prophandler(Z_STRVAL_P(copy), Z_STRLEN_P(copy), &handler)) {
-               ALLOC_ZVAL(return_value);
-               Z_SET_REFCOUNT_P(return_value, 0);
-               Z_UNSET_ISREF_P(return_value);
-
-               if (type == BP_VAR_R) {
-                       handler->read(obj, return_value TSRMLS_CC);
-               } else {
-                       php_property_proxy_t *proxy = php_property_proxy_init(object, Z_STRVAL_P(copy), Z_STRLEN_P(copy) TSRMLS_CC);
-                       RETVAL_OBJVAL(php_property_proxy_object_new_ex(php_property_proxy_get_class_entry(), proxy, NULL TSRMLS_CC), 0);
-               }
-       } else {
-               return_value = zend_get_std_object_handlers()->read_property(object, member, type PHP_HTTP_ZEND_LITERAL_CC TSRMLS_CC);
-       }
-
-       zval_ptr_dtor(&copy);
-
-       return return_value;
-}
-
-static void php_http_message_object_write_prop(zval *object, zval *member, zval *value PHP_HTTP_ZEND_LITERAL_DC TSRMLS_DC)
-{
-       php_http_message_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
-       php_http_message_object_prophandler_t *handler;
-       zval *copy = php_http_ztyp(IS_STRING, member);
-
-       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-       if (SUCCESS == php_http_message_object_get_prophandler(Z_STRVAL_P(copy), Z_STRLEN_P(copy), &handler)) {
-               handler->write(obj, value TSRMLS_CC);
-       } else {
-               zend_get_std_object_handlers()->write_property(object, member, value PHP_HTTP_ZEND_LITERAL_CC TSRMLS_CC);
-       }
-
-       zval_ptr_dtor(&copy);
-}
-
-static HashTable *php_http_message_object_get_props(zval *object TSRMLS_DC)
-{
-       zval *headers;
-       php_http_message_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
-       HashTable *props = zend_get_std_object_handlers()->get_properties(object TSRMLS_CC);
-       zval array, *parent, *body;
-       char *ver_str, *url_str = NULL;
-       size_t ver_len, url_len = 0;
-
-       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-       INIT_PZVAL_ARRAY(&array, props);
-       
-#define ASSOC_PROP(ptype, n, val) \
-       do { \
-               zend_property_info *pi; \
-               if (SUCCESS == zend_hash_find(&obj->zo.ce->properties_info, n, sizeof(n), (void *) &pi)) { \
-                       add_assoc_ ##ptype## _ex(&array, pi->name, pi->name_length + 1, val); \
-               } \
-       } while(0) \
-
-#define ASSOC_STRING(name, val) ASSOC_STRINGL(name, val, strlen(val))
-#define ASSOC_STRINGL(name, val, len) ASSOC_STRINGL_EX(name, val, len, 1)
-#define ASSOC_STRINGL_EX(n, val, len, cpy) \
-       do { \
-               zend_property_info *pi; \
-               if (SUCCESS == zend_hash_find(&obj->zo.ce->properties_info, n, sizeof(n), (void *) &pi)) { \
-                       add_assoc_stringl_ex(&array, pi->name, pi->name_length + 1, val, len, cpy); \
-               } \
-       } while(0)
-
-       ASSOC_PROP(long, "type", obj->message->type);
-       ver_len = spprintf(&ver_str, 0, "%u.%u", obj->message->http.version.major, obj->message->http.version.minor);
-       ASSOC_STRINGL_EX("httpVersion", ver_str, ver_len, 0);
-
-       switch (obj->message->type) {
-               case PHP_HTTP_REQUEST:
-                       ASSOC_PROP(long, "responseCode", 0);
-                       ASSOC_STRINGL("responseStatus", "", 0);
-                       ASSOC_STRING("requestMethod", STR_PTR(obj->message->http.info.request.method));
-                       if (obj->message->http.info.request.url) {
-                               php_http_url_to_string(obj->message->http.info.request.url, &url_str, &url_len, 0);
-                               ASSOC_STRINGL_EX("requestUrl", url_str, url_len, 0);
-                       } else {
-                               ASSOC_STRINGL("requestUrl", "", 0);
-                       }
-
-                       break;
-
-               case PHP_HTTP_RESPONSE:
-                       ASSOC_PROP(long, "responseCode", obj->message->http.info.response.code);
-                       ASSOC_STRING("responseStatus", STR_PTR(obj->message->http.info.response.status));
-                       ASSOC_STRINGL("requestMethod", "", 0);
-                       ASSOC_STRINGL("requestUrl", "", 0);
-                       break;
-
-               case PHP_HTTP_NONE:
-               default:
-                       ASSOC_PROP(long, "responseCode", 0);
-                       ASSOC_STRINGL("responseStatus", "", 0);
-                       ASSOC_STRINGL("requestMethod", "", 0);
-                       ASSOC_STRINGL("requestUrl", "", 0);
-                       break;
-       }
-
-       MAKE_STD_ZVAL(headers);
-       array_init(headers);
-       zend_hash_copy(Z_ARRVAL_P(headers), &obj->message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
-       ASSOC_PROP(zval, "headers", headers);
-
-       MAKE_STD_ZVAL(body);
-       if (obj->body) {
-               ZVAL_OBJVAL(body, obj->body->zv, 1);
-       } else {
-               ZVAL_NULL(body);
-       }
-       ASSOC_PROP(zval, "body", body);
-
-       MAKE_STD_ZVAL(parent);
-       if (obj->message->parent) {
-               ZVAL_OBJVAL(parent, obj->parent->zv, 1);
-       } else {
-               ZVAL_NULL(parent);
-       }
-       ASSOC_PROP(zval, "parentMessage", parent);
-
-       return props;
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage___construct, 0, 0, 0)
-       ZEND_ARG_INFO(0, message)
-       ZEND_ARG_INFO(0, greedy)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, __construct)
-{
-       zend_bool greedy = 1;
-       zval *zmessage = NULL;
-       php_http_message_t *msg = NULL;
-       php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-       zend_error_handling zeh;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!b", &zmessage, &greedy), invalid_arg, return);
-
-       zend_replace_error_handling(EH_THROW, php_http_exception_bad_message_class_entry, &zeh TSRMLS_CC);
-       if (zmessage && Z_TYPE_P(zmessage) == IS_RESOURCE) {
-               php_stream *s;
-               php_http_message_parser_t p;
-               zend_error_handling zeh;
-
-               zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC);
-               php_stream_from_zval(s, &zmessage);
-               zend_restore_error_handling(&zeh TSRMLS_CC);
-
-               if (s && php_http_message_parser_init(&p TSRMLS_CC)) {
-                       unsigned flags = (greedy ? PHP_HTTP_MESSAGE_PARSER_GREEDY : 0);
-                       php_http_buffer_t buf;
-
-                       php_http_buffer_init_ex(&buf, 0x1000, PHP_HTTP_BUFFER_INIT_PREALLOC);
-                       if (PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE == php_http_message_parser_parse_stream(&p, &buf, s, flags, &msg)) {
-                               if (!EG(exception)) {
-                                       php_http_throw(bad_message, "Could not parse message from stream", NULL);
-                               }
-                       }
-                       php_http_buffer_dtor(&buf);
-                       php_http_message_parser_dtor(&p);
-               }
-
-               if (!msg && !EG(exception)) {
-                       php_http_throw(bad_message, "Empty message received from stream", NULL);
-               }
-       } else if (zmessage) {
-               zmessage = php_http_ztyp(IS_STRING, zmessage);
-               msg = php_http_message_parse(NULL, Z_STRVAL_P(zmessage), Z_STRLEN_P(zmessage), greedy TSRMLS_CC);
-
-               if (!msg && !EG(exception)) {
-                       php_http_throw(bad_message, "Could not parse message: %.*s", MIN(25, Z_STRLEN_P(zmessage)), Z_STRVAL_P(zmessage));
-               }
-               zval_ptr_dtor(&zmessage);
-       }
-
-       if (msg) {
-               php_http_message_dtor(obj->message);
-               obj->message = msg;
-               if (obj->message->parent) {
-                       php_http_message_object_new_ex(Z_OBJCE_P(getThis()), obj->message->parent, &obj->parent TSRMLS_CC);
-               }
-       }
-       zend_restore_error_handling(&zeh TSRMLS_CC);
-       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getBody, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, getBody)
-{
-       php_http_message_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-       if (!obj->body) {
-               php_http_message_object_init_body_object(obj);
-
-       }
-       if (obj->body) {
-               RETVAL_OBJVAL(obj->body->zv, 1);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setBody, 0, 0, 1)
-       ZEND_ARG_OBJ_INFO(0, body, http\\Message\\Body, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, setBody)
-{
-       zval *zbody;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zbody, php_http_message_body_class_entry)) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-               php_http_message_object_prophandler_set_body(obj, zbody TSRMLS_CC);
-       }
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_addBody, 0, 0, 1)
-       ZEND_ARG_OBJ_INFO(0, body, http\\Message\\Body, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, addBody)
-{
-       zval *new_body;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &new_body, php_http_message_body_class_entry)) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-               php_http_message_body_object_t *new_obj = zend_object_store_get_object(new_body TSRMLS_CC);
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-               php_http_message_body_to_callback(new_obj->body, (php_http_pass_callback_t) php_http_message_body_append, obj->message->body, 0, 0);
-       }
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getHeader, 0, 0, 1)
-       ZEND_ARG_INFO(0, header)
-       ZEND_ARG_INFO(0, into_class)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, getHeader)
-{
-       char *header_str;
-       int header_len;
-       zend_class_entry *header_ce = NULL;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|C!", &header_str, &header_len, &header_ce)) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-               zval *header;
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               if ((header = php_http_message_header(obj->message, header_str, header_len, 0))) {
-                       if (!header_ce) {
-                               RETURN_ZVAL(header, 1, 1);
-                       } else if (instanceof_function(header_ce, php_http_header_class_entry TSRMLS_CC)) {
-                               php_http_object_method_t cb;
-                               zval *header_name, **argv[2];
-
-                               MAKE_STD_ZVAL(header_name);
-                               ZVAL_STRINGL(header_name, header_str, header_len, 1);
-
-                               argv[0] = &header_name;
-                               argv[1] = &header;
-
-                               object_init_ex(return_value, header_ce);
-                               php_http_object_method_init(&cb, return_value, ZEND_STRL("__construct") TSRMLS_CC);
-                               php_http_object_method_call(&cb, return_value, NULL, 2, argv TSRMLS_CC);
-                               php_http_object_method_dtor(&cb);
-
-                               zval_ptr_dtor(&header_name);
-                               zval_ptr_dtor(&header);
-
-                               return;
-                       } else {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Class '%s' is not as descendant of http\\Header", header_ce->name);
-                       }
-               }
-       }
-       RETURN_FALSE;
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getHeaders, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, getHeaders)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               array_init(return_value);
-               array_copy(&obj->message->hdrs, Z_ARRVAL_P(return_value));
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setHeader, 0, 0, 1)
-       ZEND_ARG_INFO(0, header)
-       ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, setHeader)
-{
-       zval *zvalue = NULL;
-       char *name_str;
-       int name_len;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z!", &name_str, &name_len, &zvalue)) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-               char *name = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1);
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               if (!zvalue) {
-                       zend_symtable_del(&obj->message->hdrs, name, name_len + 1);
-               } else {
-                       Z_ADDREF_P(zvalue);
-                       zend_symtable_update(&obj->message->hdrs, name, name_len + 1, &zvalue, sizeof(void *), NULL);
-               }
-               efree(name);
-       }
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setHeaders, 0, 0, 1)
-       ZEND_ARG_ARRAY_INFO(0, headers, 1)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, setHeaders)
-{
-       zval *new_headers = NULL;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/!", &new_headers)) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               zend_hash_clean(&obj->message->hdrs);
-               if (new_headers) {
-                       array_join(Z_ARRVAL_P(new_headers), &obj->message->hdrs, 0, ARRAY_JOIN_PRETTIFY|ARRAY_JOIN_STRONLY);
-               }
-       }
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_addHeader, 0, 0, 2)
-       ZEND_ARG_INFO(0, header)
-       ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, addHeader)
-{
-       zval *zvalue;
-       char *name_str;
-       int name_len;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &name_str, &name_len, &zvalue)) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-               char *name = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1);
-               zval *header;
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               Z_ADDREF_P(zvalue);
-               if ((header = php_http_message_header(obj->message, name, name_len, 0))) {
-                       convert_to_array(header);
-                       zend_hash_next_index_insert(Z_ARRVAL_P(header), &zvalue, sizeof(void *), NULL);
-                       zval_ptr_dtor(&header);
-               } else {
-                       zend_symtable_update(&obj->message->hdrs, name, name_len + 1, &zvalue, sizeof(void *), NULL);
-               }
-               efree(name);
-       }
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_addHeaders, 0, 0, 1)
-       ZEND_ARG_ARRAY_INFO(0, headers, 0)
-       ZEND_ARG_INFO(0, append)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, addHeaders)
-{
-       zval *new_headers;
-       zend_bool append = 0;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &new_headers, &append)) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               array_join(Z_ARRVAL_P(new_headers), &obj->message->hdrs, append, ARRAY_JOIN_STRONLY|ARRAY_JOIN_PRETTIFY);
-       }
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getType, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, getType)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               RETURN_LONG(obj->message->type);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setType, 0, 0, 1)
-       ZEND_ARG_INFO(0, type)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, setType)
-{
-       long type;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type)) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               php_http_message_set_type(obj->message, type);
-       }
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getInfo, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, getInfo)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               char *tmp = NULL;
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               switch (obj->message->type) {
-                       case PHP_HTTP_REQUEST:
-                               Z_STRLEN_P(return_value) = spprintf(&Z_STRVAL_P(return_value), 0, PHP_HTTP_INFO_REQUEST_FMT_ARGS(&obj->message->http, tmp, ""));
-                               PTR_FREE(tmp);
-                               break;
-                       case PHP_HTTP_RESPONSE:
-                               Z_STRLEN_P(return_value) = spprintf(&Z_STRVAL_P(return_value), 0, PHP_HTTP_INFO_RESPONSE_FMT_ARGS(&obj->message->http, tmp, ""));
-                               PTR_FREE(tmp);
-                               break;
-                       default:
-                               RETURN_NULL();
-                               break;
-               }
-               Z_TYPE_P(return_value) = IS_STRING;
-               return;
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setInfo, 0, 0, 1)
-       ZEND_ARG_INFO(0, http_info)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, setInfo)
-{
-       char *str;
-       int len;
-       php_http_message_object_t *obj;
-       php_http_info_t inf;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-       if (!php_http_info_parse(&inf, str TSRMLS_CC)) {
-               php_http_throw(bad_header, "Could not parse message info '%s'", str);
-               return;
-       }
-
-       php_http_message_set_info(obj->message, &inf);
-       php_http_info_dtor(&inf);
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getHttpVersion, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, getHttpVersion)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               char *str;
-               size_t len;
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               php_http_version_to_string(&obj->message->http.version, &str, &len, NULL, NULL TSRMLS_CC);
-               RETURN_STRINGL(str, len, 0);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setHttpVersion, 0, 0, 1)
-       ZEND_ARG_INFO(0, http_version)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, setHttpVersion)
-{
-       char *v_str;
-       int v_len;
-       php_http_version_t version;
-       php_http_message_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &v_str, &v_len), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-       php_http_expect(php_http_version_parse(&version, v_str TSRMLS_CC), unexpected_val, return);
-
-       obj->message->http.version = version;
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getResponseCode, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, getResponseCode)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               if (obj->message->type != PHP_HTTP_RESPONSE) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "http\\Message is not if type response");
-                       RETURN_FALSE;
-               }
-
-               RETURN_LONG(obj->message->http.info.response.code);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setResponseCode, 0, 0, 1)
-       ZEND_ARG_INFO(0, response_code)
-       ZEND_ARG_INFO(0, strict)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, setResponseCode)
-{
-       long code;
-       zend_bool strict = 1;
-       php_http_message_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|b", &code, &strict), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-       if (obj->message->type != PHP_HTTP_RESPONSE) {
-               php_http_throw(bad_method_call, "http\\Message is not of type response", NULL);
-               return;
-       }
-
-       if (strict && (code < 100 || code > 599)) {
-               php_http_throw(invalid_arg, "Invalid response code (100-599): %ld", code);
-               return;
-       }
-
-       obj->message->http.info.response.code = code;
-       PTR_SET(obj->message->http.info.response.status, estrdup(php_http_env_get_response_status_for_code(code)));
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getResponseStatus, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, getResponseStatus)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               if (obj->message->type != PHP_HTTP_RESPONSE) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "http\\Message is not of type response");
-               }
-
-               if (obj->message->http.info.response.status) {
-                       RETURN_STRING(obj->message->http.info.response.status, 1);
-               } else {
-                       RETURN_EMPTY_STRING();
-               }
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setResponseStatus, 0, 0, 1)
-       ZEND_ARG_INFO(0, response_status)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, setResponseStatus)
-{
-       char *status;
-       int status_len;
-       php_http_message_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &status, &status_len), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-       if (obj->message->type != PHP_HTTP_RESPONSE) {
-               php_http_throw(bad_method_call, "http\\Message is not of type response", NULL);
-       }
-
-       PTR_SET(obj->message->http.info.response.status, estrndup(status, status_len));
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getRequestMethod, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, getRequestMethod)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               if (obj->message->type != PHP_HTTP_REQUEST) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "http\\Message is not of type request");
-                       RETURN_FALSE;
-               }
-
-               if (obj->message->http.info.request.method) {
-                       RETURN_STRING(obj->message->http.info.request.method, 1);
-               } else {
-                       RETURN_EMPTY_STRING();
-               }
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setRequestMethod, 0, 0, 1)
-       ZEND_ARG_INFO(0, request_method)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, setRequestMethod)
-{
-       char *method;
-       int method_len;
-       php_http_message_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-       if (obj->message->type != PHP_HTTP_REQUEST) {
-               php_http_throw(bad_method_call, "http\\Message is not of type request", NULL);
-               return;
-       }
-
-       if (method_len < 1) {
-               php_http_throw(invalid_arg, "Cannot set http\\Message's request method to an empty string", NULL);
-               return;
-       }
-
-       PTR_SET(obj->message->http.info.request.method, estrndup(method, method_len));
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getRequestUrl, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, getRequestUrl)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               if (obj->message->type != PHP_HTTP_REQUEST) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "http\\Message is not of type request");
-                       RETURN_FALSE;
-               }
-
-               if (obj->message->http.info.request.url) {
-                       char *url_str;
-                       size_t url_len;
-
-                       php_http_url_to_string(obj->message->http.info.request.url, &url_str, &url_len, 0);
-                       RETURN_STRINGL(url_str, url_len, 0);
-               } else {
-                       RETURN_EMPTY_STRING();
-               }
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setRequestUrl, 0, 0, 1)
-       ZEND_ARG_INFO(0, url)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, setRequestUrl)
-{
-       zval *zurl;
-       php_http_url_t *url;
-       php_http_message_object_t *obj;
-       zend_error_handling zeh;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zurl), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-       if (obj->message->type != PHP_HTTP_REQUEST) {
-               php_http_throw(bad_method_call, "http\\Message is not of type request", NULL);
-               return;
-       }
-
-       zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC);
-       url = php_http_url_from_zval(zurl, ~0 TSRMLS_CC);
-       zend_restore_error_handling(&zeh TSRMLS_CC);
-
-       if (url && php_http_url_is_empty(url)) {
-               php_http_url_free(&url);
-               php_http_throw(invalid_arg, "Cannot set http\\Message's request url to an empty string", NULL);
-       } else if (url) {
-               PTR_SET(obj->message->http.info.request.url, url);
-       }
-
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getParentMessage, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, getParentMessage)
-{
-       php_http_message_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-       if (!obj->message->parent) {
-               php_http_throw(unexpected_val, "http\\Message has not parent message", NULL);
-               return;
-       }
-
-       RETVAL_OBJVAL(obj->parent->zv, 1);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage___toString, 0, 0, 0)
-ZEND_END_ARG_INFO();
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_toString, 0, 0, 0)
-       ZEND_ARG_INFO(0, include_parent)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, toString)
-{
-       zend_bool include_parent = 0;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &include_parent)) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-               char *string;
-               size_t length;
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               if (include_parent) {
-                       php_http_message_serialize(obj->message, &string, &length);
-               } else {
-                       php_http_message_to_string(obj->message, &string, &length);
-               }
-               if (string) {
-                       RETURN_STRINGL(string, length, 0);
-               }
-       }
-       RETURN_EMPTY_STRING();
-}
-
-#ifdef ZTS
-static size_t write_to_stream(void *s, const char *str, size_t len)
-{
-       TSRMLS_FETCH();
-       return php_stream_write(s, str, len);
-}
-#else
-#      define write_to_stream (php_http_pass_callback_t)_php_stream_write
-#endif
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_toStream, 0, 0, 1)
-       ZEND_ARG_INFO(0, stream)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, toStream)
-{
-       zval *zstream;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zstream)) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-               php_stream *s;
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               php_stream_from_zval(s, &zstream);
-               php_http_message_to_callback(obj->message, write_to_stream, s);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_toCallback, 0, 0, 1)
-       ZEND_ARG_INFO(0, callback)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, toCallback)
-{
-       php_http_pass_fcall_arg_t fcd;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f", &fcd.fci, &fcd.fcc)) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               fcd.fcz = getThis();
-               Z_ADDREF_P(fcd.fcz);
-               TSRMLS_SET_CTX(fcd.ts);
-
-               php_http_message_to_callback(obj->message, php_http_pass_fcall_callback, &fcd);
-               zend_fcall_info_args_clear(&fcd.fci, 1);
-
-               zval_ptr_dtor(&fcd.fcz);
-               RETURN_ZVAL(getThis(), 1, 0);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_serialize, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, serialize)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-               char *string;
-               size_t length;
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               php_http_message_serialize(obj->message, &string, &length);
-               RETURN_STRINGL(string, length, 0);
-       }
-       RETURN_EMPTY_STRING();
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_unserialize, 0, 0, 1)
-       ZEND_ARG_INFO(0, serialized)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, unserialize)
-{
-       int length;
-       char *serialized;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &serialized, &length)) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-               php_http_message_t *msg;
-
-               if (obj->message) {
-                       php_http_message_dtor(obj->message);
-                       efree(obj->message);
-               }
-               if ((msg = php_http_message_parse(NULL, serialized, (size_t) length, 1 TSRMLS_CC))) {
-                       obj->message = msg;
-               } else {
-                       obj->message = php_http_message_init(NULL, 0, NULL TSRMLS_CC);
-                       php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not unserialize http\\Message");
-               }
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_detach, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, detach)
-{
-       php_http_message_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-       RETVAL_OBJVAL(php_http_message_object_new_ex(obj->zo.ce, php_http_message_copy_ex(obj->message, NULL, 0), NULL TSRMLS_CC), 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_prepend, 0, 0, 1)
-       ZEND_ARG_OBJ_INFO(0, message, http\\Message, 0)
-       ZEND_ARG_INFO(0, top)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, prepend)
-{
-       zval *prepend;
-       zend_bool top = 1;
-       php_http_message_t *msg[2];
-       php_http_message_object_t *obj, *prepend_obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|b", &prepend, php_http_message_class_entry, &top), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-       prepend_obj = zend_object_store_get_object(prepend TSRMLS_CC);
-       PHP_HTTP_MESSAGE_OBJECT_INIT(prepend_obj);
-
-       /* safety check */
-       for (msg[0] = obj->message; msg[0]; msg[0] = msg[0]->parent) {
-               for (msg[1] = prepend_obj->message; msg[1]; msg[1] = msg[1]->parent) {
-                       if (msg[0] == msg[1]) {
-                               php_http_throw(unexpected_val, "Cannot prepend a message located within the same message chain", NULL);
-                               return;
-                       }
-               }
-       }
-
-       php_http_message_object_prepend(getThis(), prepend, top TSRMLS_CC);
-       RETURN_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_reverse, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, reverse)
-{
-       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
-
-       php_http_message_object_reverse(getThis(), return_value TSRMLS_CC);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_isMultipart, 0, 0, 0)
-       ZEND_ARG_INFO(1, boundary)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, isMultipart)
-{
-       zval *zboundary = NULL;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z", &zboundary)) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-               char *boundary = NULL;
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               RETVAL_BOOL(php_http_message_is_multipart(obj->message, zboundary ? &boundary : NULL));
-
-               if (zboundary && boundary) {
-                       zval_dtor(zboundary);
-                       ZVAL_STRING(zboundary, boundary, 0);
-               }
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_splitMultipartBody, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, splitMultipartBody)
-{
-       php_http_message_object_t *obj;
-       php_http_message_t *msg;
-       char *boundary = NULL;
-
-       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-       if (!php_http_message_is_multipart(obj->message, &boundary)) {
-               php_http_throw(bad_method_call, "http\\Message is not a multipart message", NULL);
-               return;
-       }
-
-       php_http_expect(msg = php_http_message_body_split(obj->message->body, boundary), bad_message, return);
-
-       PTR_FREE(boundary);
-
-       RETURN_OBJVAL(php_http_message_object_new_ex(php_http_message_class_entry, msg, NULL TSRMLS_CC), 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_count, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, count)
-{
-       long count_mode = -1;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &count_mode)) {
-               long i = 0;
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
-               php_http_message_count(i, obj->message);
-               RETURN_LONG(i);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_rewind, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, rewind)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               zval *zobj = getThis();
-               php_http_message_object_t *obj = zend_object_store_get_object(zobj TSRMLS_CC);
-
-               if (obj->iterator) {
-                       zval_ptr_dtor(&obj->iterator);
-               }
-               Z_ADDREF_P(zobj);
-               obj->iterator = zobj;
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_valid, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, valid)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               RETURN_BOOL(obj->iterator != NULL);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_next, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, next)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               if (obj->iterator) {
-                       php_http_message_object_t *itr = zend_object_store_get_object(obj->iterator TSRMLS_CC);
-
-                       if (itr && itr->parent) {
-                               zval *old = obj->iterator;
-                               MAKE_STD_ZVAL(obj->iterator);
-                               ZVAL_OBJVAL(obj->iterator, itr->parent->zv, 1);
-                               zval_ptr_dtor(&old);
-                       } else {
-                               zval_ptr_dtor(&obj->iterator);
-                               obj->iterator = NULL;
-                       }
-               }
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_key, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, key)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               RETURN_LONG(obj->iterator ? obj->iterator->value.obj.handle:0);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_current, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessage, current)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               if (obj->iterator) {
-                       RETURN_ZVAL(obj->iterator, 1, 0);
-               }
-       }
-}
-
-static zend_function_entry php_http_message_methods[] = {
-       PHP_ME(HttpMessage, __construct,        ai_HttpMessage___construct,        ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
-       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)
-       PHP_ME(HttpMessage, getHeader,          ai_HttpMessage_getHeader,          ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, setHeader,          ai_HttpMessage_setHeader,          ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, addHeader,          ai_HttpMessage_addHeader,          ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, getHeaders,         ai_HttpMessage_getHeaders,         ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, setHeaders,         ai_HttpMessage_setHeaders,         ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, addHeaders,         ai_HttpMessage_addHeaders,         ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, getType,            ai_HttpMessage_getType,            ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, setType,            ai_HttpMessage_setType,            ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, getInfo,            ai_HttpMessage_getInfo,            ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, setInfo,            ai_HttpMessage_setInfo,            ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, getResponseCode,    ai_HttpMessage_getResponseCode,    ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, setResponseCode,    ai_HttpMessage_setResponseCode,    ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, getResponseStatus,  ai_HttpMessage_getResponseStatus,  ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, setResponseStatus,  ai_HttpMessage_setResponseStatus,  ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, getRequestMethod,   ai_HttpMessage_getRequestMethod,   ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, setRequestMethod,   ai_HttpMessage_setRequestMethod,   ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, getRequestUrl,      ai_HttpMessage_getRequestUrl,      ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, setRequestUrl,      ai_HttpMessage_setRequestUrl,      ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, getHttpVersion,     ai_HttpMessage_getHttpVersion,     ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, setHttpVersion,     ai_HttpMessage_setHttpVersion,     ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, getParentMessage,   ai_HttpMessage_getParentMessage,   ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, toString,           ai_HttpMessage_toString,           ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, toCallback,         ai_HttpMessage_toCallback,         ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, toStream,           ai_HttpMessage_toStream,           ZEND_ACC_PUBLIC)
-
-       /* implements Countable */
-       PHP_ME(HttpMessage, count,              ai_HttpMessage_count,              ZEND_ACC_PUBLIC)
-
-       /* implements Serializable */
-       PHP_ME(HttpMessage, serialize,          ai_HttpMessage_serialize,          ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, unserialize,        ai_HttpMessage_unserialize,        ZEND_ACC_PUBLIC)
-
-       /* implements Iterator */
-       PHP_ME(HttpMessage, rewind,             ai_HttpMessage_rewind,             ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, valid,              ai_HttpMessage_valid,              ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, current,            ai_HttpMessage_current,            ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, key,                ai_HttpMessage_key,                ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, next,               ai_HttpMessage_next,               ZEND_ACC_PUBLIC)
-
-       ZEND_MALIAS(HttpMessage, __toString, toString, ai_HttpMessage___toString,  ZEND_ACC_PUBLIC)
-
-       PHP_ME(HttpMessage, detach,             ai_HttpMessage_detach,             ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, prepend,            ai_HttpMessage_prepend,            ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, reverse,            ai_HttpMessage_reverse,            ZEND_ACC_PUBLIC)
-
-       PHP_ME(HttpMessage, isMultipart,        ai_HttpMessage_isMultipart,        ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessage, splitMultipartBody, ai_HttpMessage_splitMultipartBody, ZEND_ACC_PUBLIC)
-
-       EMPTY_FUNCTION_ENTRY
-};
-
-zend_class_entry *php_http_message_class_entry;
-
-PHP_MINIT_FUNCTION(http_message)
-{
-       zend_class_entry ce = {0};
-
-       INIT_NS_CLASS_ENTRY(ce, "http", "Message", php_http_message_methods);
-       php_http_message_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
-       php_http_message_class_entry->create_object = php_http_message_object_new;
-       memcpy(&php_http_message_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
-       php_http_message_object_handlers.clone_obj = php_http_message_object_clone;
-       php_http_message_object_handlers.read_property = php_http_message_object_read_prop;
-       php_http_message_object_handlers.write_property = php_http_message_object_write_prop;
-       php_http_message_object_handlers.get_properties = php_http_message_object_get_props;
-       php_http_message_object_handlers.get_property_ptr_ptr = NULL;
-
-       zend_class_implements(php_http_message_class_entry TSRMLS_CC, 3, spl_ce_Countable, zend_ce_serializable, zend_ce_iterator);
-
-       zend_hash_init(&php_http_message_object_prophandlers, 9, NULL, NULL, 1);
-       zend_declare_property_long(php_http_message_class_entry, ZEND_STRL("type"), PHP_HTTP_NONE, ZEND_ACC_PROTECTED TSRMLS_CC);
-       php_http_message_object_add_prophandler(ZEND_STRL("type"), php_http_message_object_prophandler_get_type, php_http_message_object_prophandler_set_type);
-       zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("body"), ZEND_ACC_PROTECTED TSRMLS_CC);
-       php_http_message_object_add_prophandler(ZEND_STRL("body"), php_http_message_object_prophandler_get_body, php_http_message_object_prophandler_set_body);
-       zend_declare_property_string(php_http_message_class_entry, ZEND_STRL("requestMethod"), "", ZEND_ACC_PROTECTED TSRMLS_CC);
-       php_http_message_object_add_prophandler(ZEND_STRL("requestMethod"), php_http_message_object_prophandler_get_request_method, php_http_message_object_prophandler_set_request_method);
-       zend_declare_property_string(php_http_message_class_entry, ZEND_STRL("requestUrl"), "", ZEND_ACC_PROTECTED TSRMLS_CC);
-       php_http_message_object_add_prophandler(ZEND_STRL("requestUrl"), php_http_message_object_prophandler_get_request_url, php_http_message_object_prophandler_set_request_url);
-       zend_declare_property_string(php_http_message_class_entry, ZEND_STRL("responseStatus"), "", ZEND_ACC_PROTECTED TSRMLS_CC);
-       php_http_message_object_add_prophandler(ZEND_STRL("responseStatus"), php_http_message_object_prophandler_get_response_status, php_http_message_object_prophandler_set_response_status);
-       zend_declare_property_long(php_http_message_class_entry, ZEND_STRL("responseCode"), 0, ZEND_ACC_PROTECTED TSRMLS_CC);
-       php_http_message_object_add_prophandler(ZEND_STRL("responseCode"), php_http_message_object_prophandler_get_response_code, php_http_message_object_prophandler_set_response_code);
-       zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("httpVersion"), ZEND_ACC_PROTECTED TSRMLS_CC);
-       php_http_message_object_add_prophandler(ZEND_STRL("httpVersion"), php_http_message_object_prophandler_get_http_version, php_http_message_object_prophandler_set_http_version);
-       zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("headers"), ZEND_ACC_PROTECTED TSRMLS_CC);
-       php_http_message_object_add_prophandler(ZEND_STRL("headers"), php_http_message_object_prophandler_get_headers, php_http_message_object_prophandler_set_headers);
-       zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("parentMessage"), ZEND_ACC_PROTECTED TSRMLS_CC);
-       php_http_message_object_add_prophandler(ZEND_STRL("parentMessage"), php_http_message_object_prophandler_get_parent_message, php_http_message_object_prophandler_set_parent_message);
-
-       zend_declare_class_constant_long(php_http_message_class_entry, ZEND_STRL("TYPE_NONE"), PHP_HTTP_NONE TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_message_class_entry, ZEND_STRL("TYPE_REQUEST"), PHP_HTTP_REQUEST TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_message_class_entry, ZEND_STRL("TYPE_RESPONSE"), PHP_HTTP_RESPONSE TSRMLS_CC);
-
-       return SUCCESS;
-}
-
-PHP_MSHUTDOWN_FUNCTION(http_message)
-{
-       zend_hash_destroy(&php_http_message_object_prophandlers);
-
-       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/php_http_message.h b/php_http_message.h
deleted file mode 100644 (file)
index 780ea68..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_MESSAGE_H
-#define PHP_HTTP_MESSAGE_H
-
-#include "php_http_message_body.h"
-
-/* required minimum length of an HTTP message "HTTP/1.1" */
-#define PHP_HTTP_MESSAGE_MIN_SIZE 8
-#define PHP_HTTP_MESSAGE_TYPE(TYPE, msg) ((msg) && ((msg)->type == PHP_HTTP_ ##TYPE))
-
-typedef php_http_info_type_t php_http_message_type_t;
-typedef struct php_http_message php_http_message_t;
-
-struct php_http_message {
-       PHP_HTTP_INFO_IMPL(http, type)
-       HashTable hdrs;
-       php_http_message_body_t *body;
-       php_http_message_t *parent;
-       void *opaque;
-#ifdef ZTS
-       void ***ts;
-#endif
-};
-
-PHP_HTTP_API zend_bool php_http_message_info_callback(php_http_message_t **message, HashTable **headers, php_http_info_t *info TSRMLS_DC);
-
-PHP_HTTP_API php_http_message_t *php_http_message_init(php_http_message_t *m, php_http_message_type_t t, php_http_message_body_t *body TSRMLS_DC);
-PHP_HTTP_API php_http_message_t *php_http_message_init_env(php_http_message_t *m, php_http_message_type_t t TSRMLS_DC);
-PHP_HTTP_API php_http_message_t *php_http_message_copy(php_http_message_t *from, php_http_message_t *to);
-PHP_HTTP_API php_http_message_t *php_http_message_copy_ex(php_http_message_t *from, php_http_message_t *to, zend_bool parents);
-PHP_HTTP_API void php_http_message_dtor(php_http_message_t *message);
-PHP_HTTP_API void php_http_message_free(php_http_message_t **message);
-
-PHP_HTTP_API void php_http_message_set_type(php_http_message_t *m, php_http_message_type_t t);
-PHP_HTTP_API void php_http_message_set_info(php_http_message_t *message, php_http_info_t *info);
-
-PHP_HTTP_API void php_http_message_update_headers(php_http_message_t *msg);
-
-PHP_HTTP_API zval *php_http_message_header(php_http_message_t *msg, const char *key_str, size_t key_len, int join);
-PHP_HTTP_API zend_bool php_http_message_is_multipart(php_http_message_t *msg, char **boundary);
-
-PHP_HTTP_API void php_http_message_to_string(php_http_message_t *msg, char **string, size_t *length);
-PHP_HTTP_API void php_http_message_to_struct(php_http_message_t *msg, zval *strct);
-PHP_HTTP_API void php_http_message_to_callback(php_http_message_t *msg, php_http_pass_callback_t cb, void *cb_arg);
-
-PHP_HTTP_API void php_http_message_serialize(php_http_message_t *message, char **string, size_t *length);
-PHP_HTTP_API php_http_message_t *php_http_message_reverse(php_http_message_t *msg);
-PHP_HTTP_API php_http_message_t *php_http_message_zip(php_http_message_t *one, php_http_message_t *two);
-
-#define php_http_message_count(c, m) \
-{ \
-       php_http_message_t *__tmp_msg = (m); \
-       for (c = 0; __tmp_msg; __tmp_msg = __tmp_msg->parent, ++(c)); \
-}
-
-PHP_HTTP_API php_http_message_t *php_http_message_parse(php_http_message_t *msg, const char *str, size_t len, zend_bool greedy TSRMLS_DC);
-
-typedef struct php_http_message_object {
-       zend_object zo;
-       zend_object_value zv;
-       php_http_message_t *message;
-       struct php_http_message_object *parent;
-       php_http_message_body_object_t *body;
-       zval *iterator;
-} php_http_message_object_t;
-
-PHP_HTTP_API zend_class_entry *php_http_message_class_entry;
-
-PHP_MINIT_FUNCTION(http_message);
-PHP_MSHUTDOWN_FUNCTION(http_message);
-
-void php_http_message_object_prepend(zval *this_ptr, zval *prepend, zend_bool top /* = 1 */ TSRMLS_DC);
-void php_http_message_object_reverse(zval *this_ptr, zval *return_value TSRMLS_DC);
-ZEND_RESULT_CODE php_http_message_object_set_body(php_http_message_object_t *obj, zval *zbody TSRMLS_DC);
-ZEND_RESULT_CODE php_http_message_object_init_body_object(php_http_message_object_t *obj);
-
-zend_object_value php_http_message_object_new(zend_class_entry *ce TSRMLS_DC);
-zend_object_value php_http_message_object_new_ex(zend_class_entry *ce, php_http_message_t *msg, php_http_message_object_t **ptr TSRMLS_DC);
-zend_object_value php_http_message_object_clone(zval *object TSRMLS_DC);
-void php_http_message_object_free(void *object TSRMLS_DC);
-
-#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/php_http_message_body.c b/php_http_message_body.c
deleted file mode 100644 (file)
index c80c238..0000000
+++ /dev/null
@@ -1,934 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-#include <ext/standard/php_lcg.h>
-
-#define BOUNDARY_OPEN(body) \
-       do {\
-               size_t size = php_http_message_body_size(body); \
-               if (size) { \
-                       php_stream_truncate_set_size(php_http_message_body_stream(body), size - lenof("--" PHP_HTTP_CRLF)); \
-                       php_http_message_body_append(body, ZEND_STRL(PHP_HTTP_CRLF)); \
-               } else { \
-                       php_http_message_body_appendf(body, "--%s" PHP_HTTP_CRLF, php_http_message_body_boundary(body)); \
-               } \
-       } while(0)
-
-#define BOUNDARY_CLOSE(body) \
-               php_http_message_body_appendf(body, PHP_HTTP_CRLF "--%s--" PHP_HTTP_CRLF, php_http_message_body_boundary(body))
-
-static ZEND_RESULT_CODE add_recursive_fields(php_http_message_body_t *body, const char *name, zval *value);
-static ZEND_RESULT_CODE add_recursive_files(php_http_message_body_t *body, const char *name, zval *value);
-
-php_http_message_body_t *php_http_message_body_init(php_http_message_body_t **body_ptr, php_stream *stream TSRMLS_DC)
-{
-       php_http_message_body_t *body;
-
-       if (body_ptr && *body_ptr) {
-               body = *body_ptr;
-               ++body->refcount;
-               return body;
-       }
-       
-       body = ecalloc(1, sizeof(php_http_message_body_t));
-       body->refcount = 1;
-
-       if (stream) {
-               php_stream_auto_cleanup(stream);
-               body->stream_id = php_stream_get_resource_id(stream);
-               zend_list_addref(body->stream_id);
-       } else {
-               stream = php_stream_temp_create(TEMP_STREAM_DEFAULT, 0xffff);
-               php_stream_auto_cleanup(stream);
-               body->stream_id = php_stream_get_resource_id(stream);
-       }
-       TSRMLS_SET_CTX(body->ts);
-
-       if (body_ptr) {
-               *body_ptr = body;
-       }
-
-       return body;
-}
-
-unsigned php_http_message_body_addref(php_http_message_body_t *body)
-{
-       return ++body->refcount;
-}
-
-php_http_message_body_t *php_http_message_body_copy(php_http_message_body_t *from, php_http_message_body_t *to)
-{
-       if (from) {
-               TSRMLS_FETCH_FROM_CTX(from->ts);
-               
-               if (to) {
-                       php_stream_truncate_set_size(php_http_message_body_stream(to), 0);
-               } else {
-                       to = php_http_message_body_init(NULL, NULL TSRMLS_CC);
-               }
-               php_http_message_body_to_stream(from, php_http_message_body_stream(to), 0, 0);
-
-               if (to->boundary) {
-                       efree(to->boundary);
-               }
-               if (from->boundary) {
-                       to->boundary = estrdup(from->boundary);
-               }
-       } else {
-               to = NULL;
-       }
-       return to;
-}
-
-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) {
-                       TSRMLS_FETCH_FROM_CTX(body->ts);
-                       /* NOFIXME: shows leakinfo in DEBUG mode */
-                       zend_list_delete(body->stream_id);
-                       PTR_FREE(body->boundary);
-                       efree(body);
-               }
-               *body_ptr = NULL;
-       }
-}
-
-const php_stream_statbuf *php_http_message_body_stat(php_http_message_body_t *body)
-{
-       TSRMLS_FETCH_FROM_CTX(body->ts);
-       php_stream_stat(php_http_message_body_stream(body), &body->ssb);
-       return &body->ssb;
-}
-
-const char *php_http_message_body_boundary(php_http_message_body_t *body)
-{
-       if (!body->boundary) {
-               union { double dbl; int num[2]; } data;
-               TSRMLS_FETCH_FROM_CTX(body->ts);
-
-               data.dbl = php_combined_lcg(TSRMLS_C);
-               spprintf(&body->boundary, 0, "%x.%x", data.num[0], data.num[1]);
-       }
-       return body->boundary;
-}
-
-char *php_http_message_body_etag(php_http_message_body_t *body)
-{
-       php_http_etag_t *etag;
-       php_stream *s = php_http_message_body_stream(body);
-       TSRMLS_FETCH_FROM_CTX(body->ts);
-
-       /* real file or temp buffer ? */
-       if (s->ops != &php_stream_temp_ops && s->ops != &php_stream_memory_ops) {
-               php_stream_stat(php_http_message_body_stream(body), &body->ssb);
-
-               if (body->ssb.sb.st_mtime) {
-                       char *etag;
-
-                       spprintf(&etag, 0, "%lx-%lx-%lx", body->ssb.sb.st_ino, body->ssb.sb.st_mtime, body->ssb.sb.st_size);
-                       return etag;
-               }
-       }
-
-       /* content based */
-       if ((etag = php_http_etag_init(PHP_HTTP_G->env.etag_mode TSRMLS_CC))) {
-               php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_etag_update, etag, 0, 0);
-               return php_http_etag_finish(etag);
-       }
-
-       return NULL;
-}
-
-void php_http_message_body_to_string(php_http_message_body_t *body, char **buf, size_t *len, off_t offset, size_t forlen)
-{
-       php_stream *s = php_http_message_body_stream(body);
-       TSRMLS_FETCH_FROM_CTX(body->ts);
-
-       php_stream_seek(s, offset, SEEK_SET);
-       if (!forlen) {
-               forlen = -1;
-       }
-       *len = php_stream_copy_to_mem(s, buf, forlen, 0);
-}
-
-ZEND_RESULT_CODE php_http_message_body_to_stream(php_http_message_body_t *body, php_stream *dst, off_t offset, size_t forlen)
-{
-       php_stream *s = php_http_message_body_stream(body);
-       TSRMLS_FETCH_FROM_CTX(body->ts);
-
-       php_stream_seek(s, offset, SEEK_SET);
-
-       if (!forlen) {
-               forlen = -1;
-       }
-       return php_stream_copy_to_stream_ex(s, dst, forlen, NULL);
-}
-
-ZEND_RESULT_CODE php_http_message_body_to_callback(php_http_message_body_t *body, php_http_pass_callback_t cb, void *cb_arg, off_t offset, size_t forlen)
-{
-       php_stream *s = php_http_message_body_stream(body);
-       char *buf = emalloc(0x1000);
-       TSRMLS_FETCH_FROM_CTX(body->ts);
-
-       php_stream_seek(s, offset, SEEK_SET);
-
-       if (!forlen) {
-               forlen = -1;
-       }
-       while (!php_stream_eof(s)) {
-               size_t read = php_stream_read(s, buf, MIN(forlen, 0x1000));
-
-               if (read) {
-                       if (-1 == cb(cb_arg, buf, read)) {
-                               return FAILURE;
-                       }
-               }
-
-               if (read < MIN(forlen, sizeof(buf))) {
-                       break;
-               }
-
-               if (forlen && !(forlen -= read)) {
-                       break;
-               }
-       }
-       efree(buf);
-
-       return SUCCESS;
-}
-
-size_t php_http_message_body_append(php_http_message_body_t *body, const char *buf, size_t len)
-{
-       php_stream *s;
-       size_t written;
-       TSRMLS_FETCH_FROM_CTX(body->ts);
-
-       if (!(s = php_http_message_body_stream(body))) {
-               return -1;
-       }
-
-       if (s->ops->seek) {
-               php_stream_seek(s, 0, SEEK_END);
-       }
-
-       written = php_stream_write(s, buf, len);
-
-       if (written != len) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to append %zu bytes to body; wrote %zu", len, written);
-       }
-
-       return len;
-}
-
-size_t php_http_message_body_appendf(php_http_message_body_t *body, const char *fmt, ...)
-{
-       va_list argv;
-       char *print_str;
-       size_t print_len;
-
-       va_start(argv, fmt);
-       print_len = vspprintf(&print_str, 0, fmt, argv);
-       va_end(argv);
-
-       print_len = php_http_message_body_append(body, print_str, print_len);
-       efree(print_str);
-
-       return print_len;
-}
-
-ZEND_RESULT_CODE php_http_message_body_add_form(php_http_message_body_t *body, HashTable *fields, HashTable *files)
-{
-       zval tmp;
-
-       if (fields) {
-               INIT_PZVAL_ARRAY(&tmp, fields);
-               if (SUCCESS != add_recursive_fields(body, NULL, &tmp)) {
-                       return FAILURE;
-               }
-       }
-       if (files) {
-               INIT_PZVAL_ARRAY(&tmp, files);
-               if (SUCCESS != add_recursive_files(body, NULL, &tmp)) {
-                       return FAILURE;
-               }
-       }
-
-       return SUCCESS;
-}
-
-void php_http_message_body_add_part(php_http_message_body_t *body, php_http_message_t *part)
-{
-       TSRMLS_FETCH_FROM_CTX(body->ts);
-
-       BOUNDARY_OPEN(body);
-       php_http_message_to_callback(part, (php_http_pass_callback_t) php_http_message_body_append, body);
-       BOUNDARY_CLOSE(body);
-}
-
-
-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)
-{
-       char *safe_name;
-       TSRMLS_FETCH_FROM_CTX(body->ts);
-
-       safe_name = php_addslashes(estrdup(name), strlen(name), NULL, 1 TSRMLS_CC);
-
-       BOUNDARY_OPEN(body);
-       php_http_message_body_appendf(
-               body,
-               "Content-Disposition: form-data; name=\"%s\"" PHP_HTTP_CRLF
-               "" PHP_HTTP_CRLF,
-               safe_name
-       );
-       php_http_message_body_append(body, value_str, value_len);
-       BOUNDARY_CLOSE(body);
-
-       efree(safe_name);
-       return SUCCESS;
-}
-
-ZEND_RESULT_CODE php_http_message_body_add_form_file(php_http_message_body_t *body, const char *name, const char *ctype, const char *path, php_stream *in)
-{
-       char *safe_name, *path_dup = estrdup(path), *bname;
-       size_t bname_len;
-       TSRMLS_FETCH_FROM_CTX(body->ts);
-
-       safe_name = php_addslashes(estrdup(name), strlen(name), NULL, 1 TSRMLS_CC);
-       
-       php_basename(path_dup, strlen(path_dup), NULL, 0, &bname, &bname_len TSRMLS_CC); 
-
-       BOUNDARY_OPEN(body);
-       php_http_message_body_appendf(
-               body,
-               "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"" PHP_HTTP_CRLF
-               "Content-Transfer-Encoding: binary" PHP_HTTP_CRLF
-               "Content-Type: %s" PHP_HTTP_CRLF
-               PHP_HTTP_CRLF,
-               safe_name, bname, ctype
-       );
-       php_stream_copy_to_stream_ex(in, php_http_message_body_stream(body), PHP_STREAM_COPY_ALL, NULL);
-       BOUNDARY_CLOSE(body);
-
-       efree(safe_name);
-       efree(path_dup);
-       efree(bname);
-
-       return SUCCESS;
-}
-
-static inline char *format_key(uint type, char *str, ulong num, const char *prefix) {
-       char *new_key = NULL;
-
-       if (prefix && *prefix) {
-               if (type == HASH_KEY_IS_STRING) {
-                       spprintf(&new_key, 0, "%s[%s]", prefix, str);
-               } else {
-                       spprintf(&new_key, 0, "%s[%lu]", prefix, num);
-               }
-       } else if (type == HASH_KEY_IS_STRING) {
-               new_key = estrdup(str);
-       } else {
-               new_key = estrdup("");
-       }
-
-       return new_key;
-}
-
-static ZEND_RESULT_CODE add_recursive_fields(php_http_message_body_t *body, const char *name, zval *value)
-{
-       if (Z_TYPE_P(value) == IS_ARRAY || Z_TYPE_P(value) == IS_OBJECT) {
-               zval **val;
-               HashTable *ht;
-               HashPosition pos;
-               php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-               TSRMLS_FETCH_FROM_CTX(body->ts);
-
-               ht = HASH_OF(value);
-               if (!ht->nApplyCount) {
-                       ++ht->nApplyCount;
-                       FOREACH_KEYVAL(pos, value, key, val) {
-                               char *str = format_key(key.type, key.str, key.num, name);
-                               if (SUCCESS != add_recursive_fields(body, str, *val)) {
-                                       efree(str);
-                                       ht->nApplyCount--;
-                                       return FAILURE;
-                               }
-                               efree(str);
-                       }
-                       --ht->nApplyCount;
-               }
-       } else {
-               zval *cpy = php_http_ztyp(IS_STRING, value);
-               php_http_message_body_add_form_field(body, name, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy));
-               zval_ptr_dtor(&cpy);
-       }
-
-       return SUCCESS;
-}
-
-static ZEND_RESULT_CODE add_recursive_files(php_http_message_body_t *body, const char *name, zval *value)
-{
-       zval **zdata = NULL, **zfile, **zname, **ztype;
-       HashTable *ht;
-       TSRMLS_FETCH_FROM_CTX(body->ts);
-
-       if (Z_TYPE_P(value) != IS_ARRAY && Z_TYPE_P(value) != IS_OBJECT) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected array or object (name, type, file) for message body file to add");
-               return FAILURE;
-       }
-
-       ht = HASH_OF(value);
-
-       if ((SUCCESS != zend_hash_find(ht, ZEND_STRS("name"), (void *) &zname))
-       ||      (SUCCESS != zend_hash_find(ht, ZEND_STRS("type"), (void *) &ztype))
-       ||      (SUCCESS != zend_hash_find(ht, ZEND_STRS("file"), (void *) &zfile))
-       ) {
-               zval **val;
-               HashPosition pos;
-               php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-
-               if (!ht->nApplyCount) {
-                       ++ht->nApplyCount;
-                       FOREACH_HASH_KEYVAL(pos, ht, key, val) {
-                               if (Z_TYPE_PP(val) == IS_ARRAY || Z_TYPE_PP(val) == IS_OBJECT) {
-                                       char *str = format_key(key.type, key.str, key.num, name);
-
-                                       if (SUCCESS != add_recursive_files(body, str, *val)) {
-                                               efree(str);
-                                               --ht->nApplyCount;
-                                               return FAILURE;
-                                       }
-                                       efree(str);
-                               }
-                       }
-                       --ht->nApplyCount;
-               }
-               return SUCCESS;
-       } else {
-               php_stream *stream;
-               zval *zfc = php_http_ztyp(IS_STRING, *zfile);
-
-               if (SUCCESS == zend_hash_find(ht, ZEND_STRS("data"), (void *) &zdata)) {
-                       if (Z_TYPE_PP(zdata) == IS_RESOURCE) {
-                               php_stream_from_zval_no_verify(stream, zdata);
-                       } else {
-                               zval *tmp = php_http_ztyp(IS_STRING, *zdata);
-
-                               stream = php_stream_memory_open(TEMP_STREAM_READONLY, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
-                               zval_ptr_dtor(&tmp);
-                       }
-               } else {
-                       stream = php_stream_open_wrapper(Z_STRVAL_P(zfc), "r", REPORT_ERRORS|USE_PATH, NULL);
-               }
-
-               if (!stream) {
-                       zval_ptr_dtor(&zfc);
-                       return FAILURE;
-               } else {
-                       zval *znc = php_http_ztyp(IS_STRING, *zname), *ztc = php_http_ztyp(IS_STRING, *ztype);
-                       char *key = format_key(HASH_KEY_IS_STRING, Z_STRVAL_P(znc), 0, name);
-                       ZEND_RESULT_CODE ret =  php_http_message_body_add_form_file(body, key, Z_STRVAL_P(ztc), Z_STRVAL_P(zfc), stream);
-
-                       efree(key);
-                       zval_ptr_dtor(&znc);
-                       zval_ptr_dtor(&ztc);
-                       zval_ptr_dtor(&zfc);
-                       if (!zdata || Z_TYPE_PP(zdata) != IS_RESOURCE) {
-                               php_stream_close(stream);
-                       }
-                       return ret;
-               }
-
-       }
-}
-
-struct splitbody_arg {
-       php_http_buffer_t buf;
-       php_http_message_parser_t *parser;
-       char *boundary_str;
-       size_t boundary_len;
-       size_t consumed;
-};
-
-static size_t splitbody(void *opaque, char *buf, size_t len TSRMLS_DC)
-{
-       struct splitbody_arg *arg = opaque;
-       const char *boundary = NULL;
-       size_t consumed = 0;
-       int first_boundary;
-
-       do {
-               first_boundary = !(consumed || arg->consumed);
-
-               if ((boundary = php_http_locate_str(buf, len, arg->boundary_str + first_boundary, arg->boundary_len - first_boundary))) {
-                       size_t real_boundary_len = arg->boundary_len - 1, cut;
-                       const char *real_boundary = boundary + !first_boundary;
-                       int eol_len = 0;
-
-                       if (buf + len <= real_boundary + real_boundary_len) {
-                               /* if we just have enough data for the boundary, it's just a byte too less */
-                               arg->consumed += consumed;
-                               return consumed;
-                       }
-
-                       if (!first_boundary) {
-                               /* this is not the first boundary, read rest of this message */
-                               php_http_buffer_append(&arg->buf, buf, real_boundary - buf);
-                               php_http_message_parser_parse(arg->parser, &arg->buf, 0, &arg->parser->message);
-                       }
-
-                       /* move after the boundary */
-                       cut = real_boundary - buf + real_boundary_len;
-                       buf += cut;
-                       len -= cut;
-                       consumed += cut;
-
-                       if (buf == php_http_locate_bin_eol(buf, len, &eol_len)) {
-                               /* skip CRLF */
-                               buf += eol_len;
-                               len -= eol_len;
-                               consumed += eol_len;
-
-                               if (!first_boundary) {
-                                       /* advance messages */
-                                       php_http_message_t *msg;
-
-                                       msg = php_http_message_init(NULL, 0, NULL TSRMLS_CC);
-                                       msg->parent = arg->parser->message;
-                                       arg->parser->message = msg;
-                               }
-                       } else {
-                               /* is this the last boundary? */
-                               if (*buf == '-') {
-                                       /* ignore the rest */
-                                       consumed += len;
-                                       len = 0;
-                               } else {
-                                       /* let this be garbage */
-                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Malformed multipart boundary at pos %zu", consumed);
-                                       return -1;
-                               }
-                       }
-               }
-       } while (boundary && len);
-
-       /* let there be room for the next boundary */
-       if (len > arg->boundary_len) {
-               consumed += len - arg->boundary_len;
-               php_http_buffer_append(&arg->buf, buf, len - arg->boundary_len);
-               php_http_message_parser_parse(arg->parser, &arg->buf, 0, &arg->parser->message);
-       }
-
-       arg->consumed += consumed;
-       return consumed;
-}
-
-php_http_message_t *php_http_message_body_split(php_http_message_body_t *body, const char *boundary)
-{
-       php_stream *s = php_http_message_body_stream(body);
-       php_http_buffer_t *tmp = NULL;
-       php_http_message_t *msg = NULL;
-       struct splitbody_arg arg;
-       TSRMLS_FETCH_FROM_CTX(body->ts);
-
-       php_http_buffer_init(&arg.buf);
-       arg.parser = php_http_message_parser_init(NULL TSRMLS_CC);
-       arg.boundary_len = spprintf(&arg.boundary_str, 0, "\n--%s", boundary);
-       arg.consumed = 0;
-
-       php_stream_rewind(s);
-       while (!php_stream_eof(s)) {
-               php_http_buffer_passthru(&tmp, 0x1000, (php_http_buffer_pass_func_t) _php_stream_read, s, splitbody, &arg TSRMLS_CC);
-       }
-
-       msg = arg.parser->message;
-       arg.parser->message = NULL;
-
-       php_http_buffer_free(&tmp);
-       php_http_message_parser_free(&arg.parser);
-       php_http_buffer_dtor(&arg.buf);
-       PTR_FREE(arg.boundary_str);
-
-       return msg;
-}
-
-static zend_object_handlers php_http_message_body_object_handlers;
-
-zend_object_value php_http_message_body_object_new(zend_class_entry *ce TSRMLS_DC)
-{
-       return php_http_message_body_object_new_ex(ce, NULL, NULL TSRMLS_CC);
-}
-
-zend_object_value php_http_message_body_object_new_ex(zend_class_entry *ce, php_http_message_body_t *body, php_http_message_body_object_t **ptr TSRMLS_DC)
-{
-       php_http_message_body_object_t *o;
-
-       o = ecalloc(1, sizeof(php_http_message_body_object_t));
-       zend_object_std_init((zend_object *) o, php_http_message_body_class_entry TSRMLS_CC);
-       object_properties_init((zend_object *) o, ce);
-
-       if (ptr) {
-               *ptr = o;
-       }
-
-       if (body) {
-               o->body = body;
-       }
-
-       o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_message_body_object_free, NULL TSRMLS_CC);
-       o->zv.handlers = &php_http_message_body_object_handlers;
-
-       return o->zv;
-}
-
-zend_object_value php_http_message_body_object_clone(zval *object TSRMLS_DC)
-{
-       zend_object_value new_ov;
-       php_http_message_body_object_t *new_obj = NULL;
-       php_http_message_body_object_t *old_obj = zend_object_store_get_object(object TSRMLS_CC);
-       php_http_message_body_t *body = php_http_message_body_copy(old_obj->body, NULL);
-
-       new_ov = php_http_message_body_object_new_ex(old_obj->zo.ce, body, &new_obj TSRMLS_CC);
-       zend_objects_clone_members(&new_obj->zo, new_ov, &old_obj->zo, Z_OBJ_HANDLE_P(object) TSRMLS_CC);
-
-       return new_ov;
-}
-
-void php_http_message_body_object_free(void *object TSRMLS_DC)
-{
-       php_http_message_body_object_t *obj = object;
-
-       php_http_message_body_free(&obj->body);
-       zend_object_std_dtor((zend_object *) obj TSRMLS_CC);
-       efree(obj);
-}
-
-#define PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj) \
-       do { \
-               if (!obj->body) { \
-                       obj->body = php_http_message_body_init(NULL, NULL TSRMLS_CC); \
-               } \
-       } while(0)
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody___construct, 0, 0, 0)
-       ZEND_ARG_INFO(0, stream)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpMessageBody, __construct)
-{
-       php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-       zval *zstream = NULL;
-       php_stream *stream;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r!", &zstream), invalid_arg, return);
-
-       if (zstream) {
-               php_http_expect(php_stream_from_zval_no_verify(stream, &zstream), unexpected_val, return);
-
-               if (obj->body) {
-                       php_http_message_body_free(&obj->body);
-               }
-               obj->body = php_http_message_body_init(NULL, stream TSRMLS_CC);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody___toString, 0, 0, 0)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpMessageBody, __toString)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-               char *str;
-               size_t len;
-
-               PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
-
-               php_http_message_body_to_string(obj->body, &str, &len, 0, 0);
-               if (str) {
-                       RETURN_STRINGL(str, len, 0);
-               }
-       }
-       RETURN_EMPTY_STRING();
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_unserialize, 0, 0, 1)
-       ZEND_ARG_INFO(0, serialized)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpMessageBody, unserialize)
-{
-       char *us_str;
-       int us_len;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &us_str, &us_len)) {
-               php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-               php_stream *s = php_stream_memory_open(0, us_str, us_len);
-
-               obj->body = php_http_message_body_init(NULL, s TSRMLS_CC);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_toStream, 0, 0, 1)
-       ZEND_ARG_INFO(0, stream)
-       ZEND_ARG_INFO(0, offset)
-       ZEND_ARG_INFO(0, maxlen)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpMessageBody, toStream)
-{
-       zval *zstream;
-       long offset = 0, forlen = 0;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|ll", &zstream, &offset, &forlen)) {
-               php_stream *stream;
-               php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
-
-               php_stream_from_zval(stream, &zstream);
-               php_http_message_body_to_stream(obj->body, stream, offset, forlen);
-               RETURN_ZVAL(getThis(), 1, 0);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_toCallback, 0, 0, 1)
-       ZEND_ARG_INFO(0, callback)
-       ZEND_ARG_INFO(0, offset)
-       ZEND_ARG_INFO(0, maxlen)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpMessageBody, toCallback)
-{
-       php_http_pass_fcall_arg_t fcd;
-       long offset = 0, forlen = 0;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f|ll", &fcd.fci, &fcd.fcc, &offset, &forlen)) {
-               php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
-
-               fcd.fcz = getThis();
-               Z_ADDREF_P(fcd.fcz);
-               TSRMLS_SET_CTX(fcd.ts);
-
-               php_http_message_body_to_callback(obj->body, php_http_pass_fcall_callback, &fcd, offset, forlen);
-               zend_fcall_info_args_clear(&fcd.fci, 1);
-
-               zval_ptr_dtor(&fcd.fcz);
-               RETURN_ZVAL(getThis(), 1, 0);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_getResource, 0, 0, 0)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpMessageBody, getResource)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
-
-               zend_list_addref(obj->body->stream_id);
-               RETVAL_RESOURCE(obj->body->stream_id);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_getBoundary, 0, 0, 0)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpMessageBody, getBoundary)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_message_body_object_t * obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-               PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
-
-               if (obj->body->boundary) {
-                       RETURN_STRING(obj->body->boundary, 1);
-               }
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_append, 0, 0, 1)
-       ZEND_ARG_INFO(0, string)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpMessageBody, append)
-{
-       char *str;
-       int len;
-       php_http_message_body_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
-
-       php_http_expect(len == php_http_message_body_append(obj->body, str, len), runtime, return);
-
-       RETURN_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_addForm, 0, 0, 0)
-       ZEND_ARG_ARRAY_INFO(0, fields, 1)
-       ZEND_ARG_ARRAY_INFO(0, files, 1)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpMessageBody, addForm)
-{
-       HashTable *fields = NULL, *files = NULL;
-       php_http_message_body_object_t *obj;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|h!h!", &fields, &files), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
-
-       php_http_expect(SUCCESS == php_http_message_body_add_form(obj->body, fields, files), runtime, return);
-
-       RETURN_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_addPart, 0, 0, 1)
-       ZEND_ARG_OBJ_INFO(0, message, http\\Message, 0)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpMessageBody, addPart)
-{
-       zval *zobj;
-       php_http_message_body_object_t *obj;
-       php_http_message_object_t *mobj;
-       zend_error_handling zeh;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zobj, php_http_message_class_entry), invalid_arg, return);
-
-       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-       mobj = zend_object_store_get_object(zobj TSRMLS_CC);
-
-       PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
-
-       zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh TSRMLS_CC);
-       php_http_message_body_add_part(obj->body, mobj->message);
-       zend_restore_error_handling(&zeh TSRMLS_CC);
-
-       if (!EG(exception)) {
-               RETURN_ZVAL(getThis(), 1, 0);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_etag, 0, 0, 0)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpMessageBody, etag)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-               char *etag;
-
-               PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
-
-               if ((etag = php_http_message_body_etag(obj->body))) {
-                       RETURN_STRING(etag, 0);
-               } else {
-                       RETURN_FALSE;
-               }
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_stat, 0, 0, 0)
-       ZEND_ARG_INFO(0, field)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpMessageBody, stat)
-{
-       char *field_str = NULL;
-       int field_len = 0;
-
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &field_str, &field_len)) {
-               php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-               const php_stream_statbuf *sb;
-
-               PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
-
-               if ((sb = php_http_message_body_stat(obj->body))) {
-                       if (field_str && field_len) {
-                                       switch (*field_str) {
-                                               case 's':
-                                               case 'S':
-                                                       RETURN_LONG(sb->sb.st_size);
-                                                       break;
-                                               case 'a':
-                                               case 'A':
-                                                       RETURN_LONG(sb->sb.st_atime);
-                                                       break;
-                                               case 'm':
-                                               case 'M':
-                                                       RETURN_LONG(sb->sb.st_mtime);
-                                                       break;
-                                               case 'c':
-                                               case 'C':
-                                                       RETURN_LONG(sb->sb.st_ctime);
-                                                       break;
-                                               default:
-                                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown stat field: '%s' (should be one of [s]ize, [a]time, [m]time or [c]time)", field_str);
-                                                       break;
-                                       }
-                       } else {
-                               object_init(return_value);
-                               add_property_long_ex(return_value, ZEND_STRS("size"), sb->sb.st_size TSRMLS_CC);
-                               add_property_long_ex(return_value, ZEND_STRS("atime"), sb->sb.st_atime TSRMLS_CC);
-                               add_property_long_ex(return_value, ZEND_STRS("mtime"), sb->sb.st_mtime TSRMLS_CC);
-                               add_property_long_ex(return_value, ZEND_STRS("ctime"), sb->sb.st_ctime TSRMLS_CC);
-                       }
-               }
-       }
-}
-
-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, __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)
-       PHP_ME(HttpMessageBody, unserialize,  ai_HttpMessageBody_unserialize,  ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessageBody, toStream,     ai_HttpMessageBody_toStream,     ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessageBody, toCallback,   ai_HttpMessageBody_toCallback,   ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessageBody, getResource,  ai_HttpMessageBody_getResource,  ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessageBody, getBoundary,  ai_HttpMessageBody_getBoundary,  ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessageBody, append,       ai_HttpMessageBody_append,       ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessageBody, addForm,      ai_HttpMessageBody_addForm,      ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessageBody, addPart,      ai_HttpMessageBody_addPart,      ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessageBody, etag,         ai_HttpMessageBody_etag,         ZEND_ACC_PUBLIC)
-       PHP_ME(HttpMessageBody, stat,         ai_HttpMessageBody_stat,         ZEND_ACC_PUBLIC)
-       EMPTY_FUNCTION_ENTRY
-};
-
-zend_class_entry *php_http_message_body_class_entry;
-
-PHP_MINIT_FUNCTION(http_message_body)
-{
-       zend_class_entry ce = {0};
-
-       INIT_NS_CLASS_ENTRY(ce, "http\\Message", "Body", php_http_message_body_methods);
-       php_http_message_body_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
-       php_http_message_body_class_entry->create_object = php_http_message_body_object_new;
-       memcpy(&php_http_message_body_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
-       php_http_message_body_object_handlers.clone_obj = php_http_message_body_object_clone;
-       zend_class_implements(php_http_message_body_class_entry TSRMLS_CC, 1, zend_ce_serializable);
-
-       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/php_http_message_body.h b/php_http_message_body.h
deleted file mode 100644 (file)
index dc2c7a2..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_MESSAGE_BODY_H
-#define PHP_HTTP_MESSAGE_BODY_H
-
-typedef struct php_http_message_body {
-       int stream_id;
-       php_stream_statbuf ssb;
-       char *boundary;
-       unsigned refcount;
-#ifdef ZTS
-       void ***ts;
-#endif
-} php_http_message_body_t;
-
-struct php_http_message;
-
-PHP_HTTP_API php_http_message_body_t *php_http_message_body_init(php_http_message_body_t **body, php_stream *stream TSRMLS_DC);
-PHP_HTTP_API unsigned php_http_message_body_addref(php_http_message_body_t *body);
-PHP_HTTP_API php_http_message_body_t *php_http_message_body_copy(php_http_message_body_t *from, php_http_message_body_t *to);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_message_body_add_form(php_http_message_body_t *body, HashTable *fields, HashTable *files);
-PHP_HTTP_API 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);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_message_body_add_form_file(php_http_message_body_t *body, const char *name, const char *ctype, const char *file, php_stream *stream);
-PHP_HTTP_API void php_http_message_body_add_part(php_http_message_body_t *body, struct php_http_message *part);
-PHP_HTTP_API size_t php_http_message_body_append(php_http_message_body_t *body, const char *buf, size_t len);
-PHP_HTTP_API size_t php_http_message_body_appendf(php_http_message_body_t *body, const char *fmt, ...);
-PHP_HTTP_API void php_http_message_body_to_string(php_http_message_body_t *body, char **buf, size_t *len, off_t offset, size_t forlen);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_message_body_to_stream(php_http_message_body_t *body, php_stream *s, off_t offset, size_t forlen);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_message_body_to_callback(php_http_message_body_t *body, php_http_pass_callback_t cb, void *cb_arg, off_t offset, size_t forlen);
-PHP_HTTP_API void php_http_message_body_free(php_http_message_body_t **body);
-PHP_HTTP_API const php_stream_statbuf *php_http_message_body_stat(php_http_message_body_t *body);
-#define php_http_message_body_size(b) (php_http_message_body_stat((b))->sb.st_size)
-#define php_http_message_body_mtime(b) (php_http_message_body_stat((b))->sb.st_mtime)
-PHP_HTTP_API char *php_http_message_body_etag(php_http_message_body_t *body);
-PHP_HTTP_API const char *php_http_message_body_boundary(php_http_message_body_t *body);
-PHP_HTTP_API struct php_http_message *php_http_message_body_split(php_http_message_body_t *body, const char *boundary);
-
-static inline php_stream *php_http_message_body_stream(php_http_message_body_t *body)
-{
-       TSRMLS_FETCH_FROM_CTX(body->ts);
-       return zend_fetch_resource(NULL TSRMLS_CC, body->stream_id, "stream", NULL, 2, php_file_le_stream(), php_file_le_pstream());
-}
-
-typedef struct php_http_message_body_object {
-       zend_object zo;
-       zend_object_value zv;
-       php_http_message_body_t *body;
-} php_http_message_body_object_t;
-
-PHP_HTTP_API zend_class_entry *php_http_message_body_class_entry;
-PHP_MINIT_FUNCTION(http_message_body);
-
-zend_object_value php_http_message_body_object_new(zend_class_entry *ce TSRMLS_DC);
-zend_object_value php_http_message_body_object_new_ex(zend_class_entry *ce, php_http_message_body_t *body, php_http_message_body_object_t **ptr TSRMLS_DC);
-zend_object_value php_http_message_body_object_clone(zval *object TSRMLS_DC);
-void php_http_message_body_object_free(void *object TSRMLS_DC);
-
-#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/php_http_message_parser.c b/php_http_message_parser.c
deleted file mode 100644 (file)
index fae16f1..0000000
+++ /dev/null
@@ -1,705 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-#ifndef DBG_PARSER
-#      define DBG_PARSER 0
-#endif
-
-typedef struct php_http_message_parser_state_spec {
-       php_http_message_parser_state_t state;
-       unsigned need_data:1;
-} php_http_message_parser_state_spec_t;
-
-static const php_http_message_parser_state_spec_t php_http_message_parser_states[] = {
-               {PHP_HTTP_MESSAGE_PARSER_STATE_START,                   1},
-               {PHP_HTTP_MESSAGE_PARSER_STATE_HEADER,                  0},
-               {PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE,             0},
-               {PHP_HTTP_MESSAGE_PARSER_STATE_BODY,                    0},
-               {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB,               1},
-               {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH,             1},
-               {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED,    1},
-               {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE,               0},
-               {PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL,               0},
-               {PHP_HTTP_MESSAGE_PARSER_STATE_DONE,                    0}
-};
-
-#if DBG_PARSER
-const char *php_http_message_parser_state_name(php_http_message_parser_state_t state) {
-       const char *states[] = {"START", "HEADER", "HEADER_DONE", "BODY", "BODY_DUMB", "BODY_LENGTH", "BODY_CHUNK", "BODY_DONE", "UPDATE_CL", "DONE"};
-       
-       if (state < 0 || state > (sizeof(states)/sizeof(char*))-1) {
-               return "FAILURE";
-       }
-       return states[state];
-}
-#endif
-
-php_http_message_parser_t *php_http_message_parser_init(php_http_message_parser_t *parser TSRMLS_DC)
-{
-       if (!parser) {
-               parser = emalloc(sizeof(*parser));
-       }
-       memset(parser, 0, sizeof(*parser));
-
-       TSRMLS_SET_CTX(parser->ts);
-
-       php_http_header_parser_init(&parser->header TSRMLS_CC);
-
-       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;
-}
-
-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;
-}
-
-void php_http_message_parser_dtor(php_http_message_parser_t *parser)
-{
-       php_http_header_parser_dtor(&parser->header);
-       zend_ptr_stack_destroy(&parser->stack);
-       php_http_message_free(&parser->message);
-       if (parser->dechunk) {
-               php_http_encoding_stream_free(&parser->dechunk);
-       }
-       if (parser->inflate) {
-               php_http_encoding_stream_free(&parser->inflate);
-       }
-}
-
-void php_http_message_parser_free(php_http_message_parser_t **parser)
-{
-       if (*parser) {
-               php_http_message_parser_dtor(*parser);
-               efree(*parser);
-               *parser = NULL;
-       }
-}
-
-php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_message_parser_t *parser, php_http_buffer_t *buf, php_stream *s, unsigned flags, php_http_message_t **message)
-{
-       php_http_message_parser_state_t state = PHP_HTTP_MESSAGE_PARSER_STATE_START;
-       TSRMLS_FETCH_FROM_CTX(parser->ts);
-
-       if (!buf->data) {
-               php_http_buffer_resize_ex(buf, 0x1000, 1, 0);
-       }
-       while (1) {
-               size_t justread = 0;
-#if DBG_PARSER
-               fprintf(stderr, "#SP: %s (f:%u)\n", php_http_message_parser_state_name(state), flags);
-#endif
-               /* resize if needed */
-               if (buf->free < 0x1000) {
-                       php_http_buffer_resize_ex(buf, 0x1000, 1, 0);
-               }
-               switch (state) {
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_START:
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER:
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE:
-                               /* read line */
-                               php_stream_get_line(s, buf->data + buf->used, buf->free, &justread);
-                               /* if we fail reading a whole line, try a single char */
-                               if (!justread) {
-                                       int c = php_stream_getc(s);
-
-                                       if (c != EOF) {
-                                               char s[1] = {c};
-                                               justread = php_http_buffer_append(buf, s, 1);
-                                       }
-                               }
-                               php_http_buffer_account(buf, justread);
-                               break;
-
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB:
-                               /* read all */
-                               justread = php_stream_read(s, buf->data + buf->used, buf->free);
-                               php_http_buffer_account(buf, justread);
-                               break;
-
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH:
-                               /* read body_length */
-                               justread = php_stream_read(s, buf->data + buf->used, MIN(buf->free, parser->body_length));
-                               php_http_buffer_account(buf, justread);
-                               break;
-
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED:
-                               /* duh, this is very naive */
-                               if (parser->body_length) {
-                                       justread = php_stream_read(s, buf->data + buf->used, MIN(parser->body_length, buf->free));
-
-                                       php_http_buffer_account(buf, justread);
-
-                                       parser->body_length -= justread;
-                               } else {
-                                       php_http_buffer_resize(buf, 24);
-                                       php_stream_get_line(s, buf->data, buf->free, &justread);
-                                       php_http_buffer_account(buf, justread);
-
-                                       parser->body_length = strtoul(buf->data + buf->used - justread, NULL, 16);
-                               }
-                               break;
-
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY:
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL:
-                               /* should not occur */
-                               abort();
-                               break;
-
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_DONE:
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE:
-                               return php_http_message_parser_state_is(parser);
-               }
-
-               if (justread) {
-                       state = php_http_message_parser_parse(parser, buf, flags, message);
-               } else if (php_stream_eof(s)) {
-                       return php_http_message_parser_parse(parser, buf, flags | PHP_HTTP_MESSAGE_PARSER_CLEANUP, message);
-               } else {
-                       return state;
-               }
-       }
-
-       return PHP_HTTP_MESSAGE_PARSER_STATE_DONE;
-}
-
-
-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)
-{
-       char *str = NULL;
-       size_t len = 0;
-       size_t cut = 0;
-       TSRMLS_FETCH_FROM_CTX(parser->ts);
-
-       while (buffer->used || !php_http_message_parser_states[php_http_message_parser_state_is(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)),
-                       flags, 
-                       message && *message ? (*message)->type : -1, 
-                       buffer->used
-               );
-               _dpf(0, buffer->data, buffer->used);
-#endif
-
-               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);
-
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_START:
-                       {
-                               char *ptr = buffer->data;
-
-                               while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) {
-                                       ++ptr;
-                               }
-
-                               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);
-                               }
-                               break;
-                       }
-
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER:
-                       {
-                               unsigned header_parser_flags = (flags & PHP_HTTP_MESSAGE_PARSER_CLEANUP) ? PHP_HTTP_HEADER_PARSER_CLEANUP : 0;
-
-                               switch (php_http_header_parser_parse(&parser->header, buffer, header_parser_flags, *message ? &(*message)->hdrs : NULL, (php_http_info_callback_t) php_http_message_info_callback, message)) {
-                                       case PHP_HTTP_HEADER_PARSER_STATE_FAILURE:
-                                               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);
-                                               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);
-                                               } else {
-                                                       php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE);
-                                               }
-                               }
-                               break;
-                       }
-
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE:
-                       {
-                               zval *h, *h_loc = NULL, *h_con = NULL, **h_cl = NULL, **h_cr = NULL, **h_te = NULL;
-
-                               /* Content-Range has higher precedence than Content-Length,
-                                * and content-length denotes the original length of the entity,
-                                * so let's *NOT* remove CR/CL, because that would fundamentally
-                                * change the meaning of the whole message
-                                */
-                               if ((h = php_http_message_header(*message, ZEND_STRL("Transfer-Encoding"), 1))) {
-                                       zend_hash_update(&(*message)->hdrs, "X-Original-Transfer-Encoding", sizeof("X-Original-Transfer-Encoding"), (void *) &h, sizeof(zval *), (void *) &h_te);
-                                       zend_hash_del(&(*message)->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding"));
-
-                                       /* reset */
-                                       MAKE_STD_ZVAL(h);
-                                       ZVAL_LONG(h, 0);
-                                       zend_hash_update(&(*message)->hdrs, "Content-Length", sizeof("Content-Length"), (void *) &h, sizeof(zval *), NULL);
-                               } else if ((h = php_http_message_header(*message, ZEND_STRL("Content-Length"), 1))) {
-                                       zend_hash_update(&(*message)->hdrs, "X-Original-Content-Length", sizeof("X-Original-Content-Length"), (void *) &h, sizeof(zval *), (void *) &h_cl);
-                               }
-
-                               if ((h = php_http_message_header(*message, ZEND_STRL("Content-Range"), 1))) {
-                                       zend_hash_find(&(*message)->hdrs, ZEND_STRS("Content-Range"), (void *) &h_cr);
-                                       if (h != *h_cr) {
-                                               zend_hash_update(&(*message)->hdrs, "Content-Range", sizeof("Content-Range"), &h, sizeof(zval *), (void *) &h_cr);
-                                       } else {
-                                               zval_ptr_dtor(&h);
-                                       }
-                               }
-
-                               /* so, if curl sees a 3xx code, a Location header and a Connection:close header
-                                * it decides not to read the response body.
-                                */
-                               if ((flags & PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS)
-                               &&      (*message)->type == PHP_HTTP_RESPONSE
-                               &&      (*message)->http.info.response.code/100 == 3
-                               &&      (h_loc = php_http_message_header(*message, ZEND_STRL("Location"), 1))
-                               &&      (h_con = php_http_message_header(*message, ZEND_STRL("Connection"), 1))
-                               ) {
-                                       if (php_http_match(Z_STRVAL_P(h_con), "close", PHP_HTTP_MATCH_WORD)) {
-                                               php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
-                                               zval_ptr_dtor(&h_loc);
-                                               zval_ptr_dtor(&h_con);
-                                               break;
-                                       }
-                               }
-                               if (h_loc) {
-                                       zval_ptr_dtor(&h_loc);
-                               }
-                               if (h_con) {
-                                       zval_ptr_dtor(&h_con);
-                               }
-
-                               if ((h = php_http_message_header(*message, ZEND_STRL("Content-Encoding"), 1))) {
-                                       if (php_http_match(Z_STRVAL_P(h), "gzip", PHP_HTTP_MATCH_WORD)
-                                       ||      php_http_match(Z_STRVAL_P(h), "x-gzip", PHP_HTTP_MATCH_WORD)
-                                       ||      php_http_match(Z_STRVAL_P(h), "deflate", PHP_HTTP_MATCH_WORD)
-                                       ) {
-                                               if (parser->inflate) {
-                                                       php_http_encoding_stream_reset(&parser->inflate);
-                                               } else {
-                                                       parser->inflate = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), 0 TSRMLS_CC);
-                                               }
-                                               zend_hash_update(&(*message)->hdrs, "X-Original-Content-Encoding", sizeof("X-Original-Content-Encoding"), &h, sizeof(zval *), NULL);
-                                               zend_hash_del(&(*message)->hdrs, "Content-Encoding", sizeof("Content-Encoding"));
-                                       } else {
-                                               zval_ptr_dtor(&h);
-                                       }
-                               }
-
-                               if ((flags & PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES)) {
-                                       php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB);
-                               } else {
-                                       if (h_te) {
-                                               if (strstr(Z_STRVAL_PP(h_te), "chunked")) {
-                                                       parser->dechunk = php_http_encoding_stream_init(parser->dechunk, php_http_encoding_stream_get_dechunk_ops(), 0 TSRMLS_CC);
-                                                       php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED);
-                                                       break;
-                                               }
-                                       }
-
-                                       if (h_cr) {
-                                               ulong total = 0, start = 0, end = 0;
-
-                                               if (!strncasecmp(Z_STRVAL_PP(h_cr), "bytes", lenof("bytes"))
-                                               && (    Z_STRVAL_PP(h_cr)[lenof("bytes")] == ':'
-                                                       ||      Z_STRVAL_PP(h_cr)[lenof("bytes")] == ' '
-                                                       ||      Z_STRVAL_PP(h_cr)[lenof("bytes")] == '='
-                                                       )
-                                               ) {
-                                                       char *total_at = NULL, *end_at = NULL;
-                                                       char *start_at = Z_STRVAL_PP(h_cr) + sizeof("bytes");
-
-                                                       start = strtoul(start_at, &end_at, 10);
-                                                       if (end_at) {
-                                                               end = strtoul(end_at + 1, &total_at, 10);
-                                                               if (total_at && strncmp(total_at + 1, "*", 1)) {
-                                                                       total = strtoul(total_at + 1, NULL, 10);
-                                                               }
-
-                                                               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);
-                                                                       break;
-                                                               }
-                                                       }
-                                               }
-                                       }
-
-                                       if (h_cl) {
-                                               char *stop;
-
-                                               if (Z_TYPE_PP(h_cl) == IS_STRING) {
-                                                       parser->body_length = strtoul(Z_STRVAL_PP(h_cl), &stop, 10);
-
-                                                       if (stop != Z_STRVAL_PP(h_cl)) {
-                                                               php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
-                                                               break;
-                                                       }
-                                               } else if (Z_TYPE_PP(h_cl) == IS_LONG) {
-                                                       parser->body_length = Z_LVAL_PP(h_cl);
-                                                       php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
-                                                       break;
-                                               }
-                                       }
-
-                                       if ((*message)->type == PHP_HTTP_REQUEST) {
-                                               php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
-                                       } else {
-                                               php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB);
-                                       }
-                               }
-                               break;
-                       }
-
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY:
-                       {
-                               if (len) {
-                                       /* FIXME: what if we re-use the parser? */
-                                       if (parser->inflate) {
-                                               char *dec_str = NULL;
-                                               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);
-                                               }
-
-                                               if (str != buffer->data) {
-                                                       PTR_FREE(str);
-                                               }
-                                               str = dec_str;
-                                               len = dec_len;
-                                       }
-
-                                       php_stream_write(php_http_message_body_stream((*message)->body), str, len);
-
-                               }
-
-                               if (cut) {
-                                       php_http_buffer_cut(buffer, 0, cut);
-                               }
-
-                               if (str != buffer->data) {
-                                       PTR_FREE(str);
-                               }
-
-                               str = NULL;
-                               len = 0;
-                               cut = 0;
-                               break;
-                       }
-
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB:
-                       {
-                               str = buffer->data;
-                               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);
-                               break;
-                       }
-
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH:
-                       {
-                               len = MIN(parser->body_length, buffer->used);
-                               str = buffer->data;
-                               cut = len;
-
-                               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);
-                               break;
-                       }
-
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED:
-                       {
-                               /*
-                                * - pass available data through the dechunk stream
-                                * - pass decoded data along
-                                * - if stream zeroed:
-                                *      Y:      - cut processed string out of buffer, but leave length of unprocessed dechunk stream data untouched
-                                *              - body done
-                                *      N:      - parse ahaed
-                                */
-                               char *dec_str = NULL;
-                               size_t dec_len;
-
-                               if (SUCCESS != php_http_encoding_stream_update(parser->dechunk, buffer->data, buffer->used, &dec_str, &dec_len)) {
-                                       return PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE;
-                               }
-
-                               str = dec_str;
-                               len = dec_len;
-
-                               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);
-                               } 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);
-                               }
-                               break;
-                       }
-
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:
-                       {
-                               php_http_message_parser_state_push(parser, 1, 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);
-                                       }
-                                       php_http_encoding_stream_dtor(parser->dechunk);
-
-                                       if (dec_str && dec_len) {
-                                               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);
-                                       }
-                               }
-
-                               break;
-                       }
-
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL:
-                       {
-                               zval *zcl;
-                               MAKE_STD_ZVAL(zcl);
-                               ZVAL_LONG(zcl, php_http_message_body_size((*message)->body));
-                               zend_hash_update(&(*message)->hdrs, "Content-Length", sizeof("Content-Length"), &zcl, sizeof(zval *), NULL);
-                               break;
-                       }
-
-                       case PHP_HTTP_MESSAGE_PARSER_STATE_DONE:
-                       {
-                               char *ptr = buffer->data;
-
-                               while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) {
-                                       ++ptr;
-                               }
-
-                               php_http_buffer_cut(buffer, 0, ptr - buffer->data);
-                               
-                               if (!(flags & PHP_HTTP_MESSAGE_PARSER_GREEDY)) {
-                                       return PHP_HTTP_MESSAGE_PARSER_STATE_DONE;
-                               }
-                               break;
-                       }
-               }
-       }
-
-       return php_http_message_parser_state_is(parser);
-}
-
-zend_class_entry *php_http_message_parser_class_entry;
-static zend_object_handlers php_http_message_parser_object_handlers;
-
-zend_object_value php_http_message_parser_object_new(zend_class_entry *ce TSRMLS_DC)
-{
-       return php_http_message_parser_object_new_ex(ce, NULL, NULL TSRMLS_CC);
-}
-
-zend_object_value php_http_message_parser_object_new_ex(zend_class_entry *ce, php_http_message_parser_t *parser, php_http_message_parser_object_t **ptr TSRMLS_DC)
-{
-       php_http_message_parser_object_t *o;
-
-       o = ecalloc(1, sizeof(php_http_message_parser_object_t));
-       zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
-       object_properties_init((zend_object *) o, ce);
-
-       if (ptr) {
-               *ptr = o;
-       }
-
-       if (parser) {
-               o->parser = parser;
-       } else {
-               o->parser = php_http_message_parser_init(NULL TSRMLS_CC);
-       }
-       o->buffer = php_http_buffer_new();
-
-       o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_message_parser_object_free, NULL TSRMLS_CC);
-       o->zv.handlers = &php_http_message_parser_object_handlers;
-
-       return o->zv;
-}
-
-void php_http_message_parser_object_free(void *object TSRMLS_DC)
-{
-       php_http_message_parser_object_t *o = (php_http_message_parser_object_t *) object;
-
-       if (o->parser) {
-               php_http_message_parser_free(&o->parser);
-       }
-       if (o->buffer) {
-               php_http_buffer_free(&o->buffer);
-       }
-       zend_object_std_dtor((zend_object *) o TSRMLS_CC);
-       efree(o);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_getState, 0, 0, 0)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessageParser, getState)
-{
-       php_http_message_parser_object_t *parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-
-       zend_parse_parameters_none();
-       /* always return the real state */
-       RETVAL_LONG(php_http_message_parser_state_is(parser_obj->parser));
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_parse, 0, 0, 3)
-       ZEND_ARG_INFO(0, data)
-       ZEND_ARG_INFO(0, flags)
-       ZEND_ARG_INFO(1, message)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessageParser, parse)
-{
-       php_http_message_parser_object_t *parser_obj;
-       zval *zmsg;
-       char *data_str;
-       int data_len;
-       long flags;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slz", &data_str, &data_len, &flags, &zmsg), invalid_arg, return);
-
-       parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-       php_http_buffer_append(parser_obj->buffer, data_str, data_len);
-       RETVAL_LONG(php_http_message_parser_parse(parser_obj->parser, parser_obj->buffer, flags, &parser_obj->parser->message));
-
-       zval_dtor(zmsg);
-       if (parser_obj->parser->message) {
-               ZVAL_OBJVAL(zmsg, php_http_message_object_new_ex(php_http_message_class_entry, php_http_message_copy(parser_obj->parser->message, NULL), NULL TSRMLS_CC), 0);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_stream, 0, 0, 3)
-       ZEND_ARG_INFO(0, stream)
-       ZEND_ARG_INFO(0, flags)
-       ZEND_ARG_INFO(1, message)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(HttpMessageParser, stream)
-{
-       php_http_message_parser_object_t *parser_obj;
-       zend_error_handling zeh;
-       zval *zmsg, *zstream;
-       php_stream *s;
-       long flags;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz", &zstream, &flags, &zmsg), invalid_arg, return);
-
-       zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC);
-       php_stream_from_zval(s, &zstream);
-       zend_restore_error_handling(&zeh TSRMLS_CC);
-
-       parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
-       RETVAL_LONG(php_http_message_parser_parse_stream(parser_obj->parser, parser_obj->buffer, s, flags, &parser_obj->parser->message));
-
-       zval_dtor(zmsg);
-       if (parser_obj->parser->message) {
-               ZVAL_OBJVAL(zmsg, php_http_message_object_new_ex(php_http_message_class_entry, php_http_message_copy(parser_obj->parser->message, NULL), NULL TSRMLS_CC), 0);
-       }
-}
-
-static zend_function_entry php_http_message_parser_methods[] = {
-               PHP_ME(HttpMessageParser, getState, ai_HttpMessageParser_getState, ZEND_ACC_PUBLIC)
-               PHP_ME(HttpMessageParser, parse, ai_HttpMessageParser_parse, ZEND_ACC_PUBLIC)
-               PHP_ME(HttpMessageParser, stream, ai_HttpMessageParser_stream, ZEND_ACC_PUBLIC)
-               {NULL, NULL, NULL}
-};
-
-PHP_MINIT_FUNCTION(http_message_parser)
-{
-       zend_class_entry ce;
-
-       INIT_NS_CLASS_ENTRY(ce, "http\\Message", "Parser", php_http_message_parser_methods);
-       php_http_message_parser_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
-       memcpy(&php_http_message_parser_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
-       php_http_message_parser_class_entry->create_object = php_http_message_parser_object_new;
-       php_http_message_parser_object_handlers.clone_obj = NULL;
-
-       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("CLEANUP"), PHP_HTTP_MESSAGE_PARSER_CLEANUP TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("DUMB_BODIES"), PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("EMPTY_REDIRECTS"), PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("GREEDY"), PHP_HTTP_MESSAGE_PARSER_GREEDY TSRMLS_CC);
-
-       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_FAILURE"), PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_START"), PHP_HTTP_MESSAGE_PARSER_STATE_START TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DUMB"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_LENGTH"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_CHUNKED"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_UPDATE_CL"), PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_DONE TSRMLS_CC);
-
-       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/php_http_message_parser.h b/php_http_message_parser.h
deleted file mode 100644 (file)
index 0bac9da..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_MESSAGE_PARSER_H
-#define PHP_HTTP_MESSAGE_PARSER_H
-
-#include "php_http_header_parser.h"
-#include "php_http_encoding.h"
-#include "php_http_message.h"
-
-typedef enum php_http_message_parser_state {
-       PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE = FAILURE,
-       PHP_HTTP_MESSAGE_PARSER_STATE_START = 0,
-       PHP_HTTP_MESSAGE_PARSER_STATE_HEADER,
-       PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE,
-       PHP_HTTP_MESSAGE_PARSER_STATE_BODY,
-       PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB,
-       PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH,
-       PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED,
-       PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE,
-       PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL,
-       PHP_HTTP_MESSAGE_PARSER_STATE_DONE
-} php_http_message_parser_state_t;
-
-#define PHP_HTTP_MESSAGE_PARSER_CLEANUP                        0x1
-#define PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES            0x2
-#define PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS        0x4
-#define PHP_HTTP_MESSAGE_PARSER_GREEDY                 0x8
-
-typedef struct php_http_message_parser {
-       php_http_header_parser_t header;
-       zend_ptr_stack stack;
-       size_t body_length;
-       php_http_message_t *message;
-       php_http_encoding_stream_t *dechunk;
-       php_http_encoding_stream_t *inflate;
-#ifdef ZTS
-       void ***ts;
-#endif
-} php_http_message_parser_t;
-
-PHP_HTTP_API php_http_message_parser_t *php_http_message_parser_init(php_http_message_parser_t *parser TSRMLS_DC);
-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);
-PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_message_parser_t *parser, php_http_buffer_t *buffer, php_stream *s, unsigned flags, php_http_message_t **message);
-
-typedef struct php_http_message_parser_object {
-       zend_object zo;
-       zend_object_value zv;
-       php_http_buffer_t *buffer;
-       php_http_message_parser_t *parser;
-} php_http_message_parser_object_t;
-
-PHP_HTTP_API zend_class_entry *php_http_message_parser_class_entry;
-
-PHP_MINIT_FUNCTION(http_message_parser);
-
-zend_object_value php_http_message_parser_object_new(zend_class_entry *ce TSRMLS_DC);
-zend_object_value php_http_message_parser_object_new_ex(zend_class_entry *ce, php_http_message_parser_t *parser, php_http_message_parser_object_t **ptr TSRMLS_DC);
-void php_http_message_parser_object_free(void *object TSRMLS_DC);
-
-#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/php_http_misc.c b/php_http_misc.c
deleted file mode 100644 (file)
index 8e2227d..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-#include <ext/standard/php_lcg.h>
-#include <zend_exceptions.h>
-
-/* SLEEP */
-
-void php_http_sleep(double s)
-{
-#if defined(PHP_WIN32)
-       Sleep((DWORD) PHP_HTTP_MSEC(s));
-#elif defined(HAVE_USLEEP)
-       usleep(PHP_HTTP_USEC(s));
-#elif defined(HAVE_NANOSLEEP)
-       struct timespec req, rem;
-
-       req.tv_sec = (time_t) s;
-       req.tv_nsec = PHP_HTTP_NSEC(s) % PHP_HTTP_NANOSEC;
-
-       while (nanosleep(&req, &rem) && (errno == EINTR) && (PHP_HTTP_NSEC(rem.tv_sec) + rem.tv_nsec) > PHP_HTTP_NSEC(PHP_HTTP_DIFFSEC))) {
-               req.tv_sec = rem.tv_sec;
-               req.tv_nsec = rem.tv_nsec;
-       }
-#else
-       struct timeval timeout;
-
-       timeout.tv_sec = (time_t) s;
-       timeout.tv_usec = PHP_HTTP_USEC(s) % PHP_HTTP_MCROSEC;
-
-       select(0, NULL, NULL, NULL, &timeout);
-#endif
-}
-
-
-/* STRING UTILITIES */
-
-int php_http_match(const char *haystack_str, const char *needle_str, int flags)
-{
-       int result = 0;
-
-       if (!haystack_str || !needle_str) {
-               return result;
-       }
-
-       if (flags & PHP_HTTP_MATCH_FULL) {
-               if (flags & PHP_HTTP_MATCH_CASE) {
-                       result = !strcmp(haystack_str, needle_str);
-               } else {
-                       result = !strcasecmp(haystack_str, needle_str);
-               }
-       } else {
-               const char *found;
-               char *haystack = estrdup(haystack_str), *needle = estrdup(needle_str);
-
-               if (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));
-               }
-
-               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))))
-                               )
-                       ) {
-                               result = 1;
-                       }
-               }
-
-               PTR_FREE(haystack);
-               PTR_FREE(needle);
-       }
-
-       return result;
-}
-
-char *php_http_pretty_key(register char *key, size_t key_len, zend_bool uctitle, zend_bool xhyphen)
-{
-       size_t i = 1;
-       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]));
-               }
-               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;
-                       } else {
-                               if (xhyphen && (key[i] == '_')) {
-                                       key[i] = '-';
-                               }
-                               wasalpha = 0;
-                       }
-                       ++i;
-               );
-       }
-       return key;
-}
-
-
-size_t php_http_boundary(char *buf, size_t buf_len TSRMLS_DC)
-{
-       return snprintf(buf, buf_len, "%15.15F", sapi_get_request_time(TSRMLS_C) * php_combined_lcg(TSRMLS_C));
-}
-
-int php_http_select_str(const char *cmp, int argc, ...)
-{
-       va_list argv;
-       int match = -1;
-
-       if (cmp && argc > 0) {
-               int i;
-
-               va_start(argv, argc);
-               for (i = 0; i < argc; ++i) {
-                       const char *test = va_arg(argv, const char *);
-
-                       if (!strcasecmp(cmp, test)) {
-                               match = i;
-                               break;
-                       }
-               }
-               va_end(argv);
-       }
-
-       return match;
-}
-
-
-/* ARRAYS */
-
-unsigned php_http_array_list(HashTable *ht TSRMLS_DC, unsigned argc, ...)
-{
-       HashPosition pos;
-       unsigned argl = 0;
-       va_list argv;
-
-       va_start(argv, argc);
-       for (   zend_hash_internal_pointer_reset_ex(ht, &pos);
-                       SUCCESS == zend_hash_has_more_elements_ex(ht, &pos) && (argl < argc);
-                       zend_hash_move_forward_ex(ht, &pos))
-       {
-               zval **data, ***argp = (zval ***) va_arg(argv, zval ***);
-
-               if (SUCCESS == zend_hash_get_current_data_ex(ht, (void *) &data, &pos)) {
-                       *argp = data;
-                       ++argl;
-               }
-       }
-       va_end(argv);
-
-       return argl;
-}
-
-void php_http_array_copy_strings(void *zpp)
-{
-       zval **zvpp = ((zval **) zpp);
-
-       *zvpp = php_http_zsep(1, IS_STRING, *zvpp);
-}
-
-int php_http_array_apply_append_func(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
-{
-       int flags;
-       char *key = NULL;
-       HashTable *dst;
-       zval **data = NULL, *value = *((zval **) pDest);
-
-       dst = va_arg(args, HashTable *);
-       flags = va_arg(args, int);
-
-       if ((!(flags & ARRAY_JOIN_STRONLY)) || hash_key->nKeyLength) {
-               if ((flags & ARRAY_JOIN_PRETTIFY) && hash_key->nKeyLength) {
-                       key = php_http_pretty_key(estrndup(hash_key->arKey, hash_key->nKeyLength - 1), hash_key->nKeyLength - 1, 1, 1);
-                       zend_hash_find(dst, key, hash_key->nKeyLength, (void *) &data);
-               } else if (hash_key->nKeyLength) {
-                       zend_hash_quick_find(dst, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &data);
-               } else {
-                       zend_hash_index_find(dst, hash_key->h, (void *) &data);
-               }
-
-               if (flags & ARRAY_JOIN_STRINGIFY) {
-                       value = php_http_zsep(1, IS_STRING, value);
-               } else {
-                       Z_ADDREF_P(value);
-               }
-
-               if (data) {
-                       if (Z_TYPE_PP(data) != IS_ARRAY) {
-                               convert_to_array(*data);
-                       }
-                       add_next_index_zval(*data, value);
-               } else if (key) {
-                       zend_symtable_update(dst, key, hash_key->nKeyLength, &value, sizeof(zval *), NULL);
-               } else if (hash_key->nKeyLength) {
-                       zend_hash_quick_add(dst, hash_key->arKey, hash_key->nKeyLength, hash_key->h, &value, sizeof(zval *), NULL);
-               } else {
-                       zend_hash_index_update(dst, hash_key->h, (void *) &value, sizeof(zval *), NULL);
-               }
-
-               if (key) {
-                       efree(key);
-               }
-       }
-
-       return ZEND_HASH_APPLY_KEEP;
-}
-
-int php_http_array_apply_merge_func(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
-{
-       int flags;
-       char *key = NULL;
-       HashTable *dst;
-       zval *value = *((zval **) pDest);
-
-       dst = va_arg(args, HashTable *);
-       flags = va_arg(args, int);
-
-       if ((!(flags & ARRAY_JOIN_STRONLY)) || hash_key->nKeyLength) {
-               if (flags & ARRAY_JOIN_STRINGIFY) {
-                       value = php_http_zsep(1, IS_STRING, value);
-               } else {
-                       Z_ADDREF_P(value);
-               }
-
-               if ((flags & ARRAY_JOIN_PRETTIFY) && hash_key->nKeyLength) {
-                       key = php_http_pretty_key(estrndup(hash_key->arKey, hash_key->nKeyLength - 1), hash_key->nKeyLength - 1, 1, 1);
-                       zend_hash_update(dst, key, hash_key->nKeyLength, (void *) &value, sizeof(zval *), NULL);
-                       efree(key);
-               } else if (hash_key->nKeyLength) {
-                       zend_hash_quick_update(dst, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &value, sizeof(zval *), NULL);
-               } else {
-                       zend_hash_index_update(dst, hash_key->h, (void *) &value, sizeof(zval *), NULL);
-               }
-       }
-
-       return ZEND_HASH_APPLY_KEEP;
-}
-
-/* PASS CALLBACK */
-
-size_t php_http_pass_fcall_callback(void *cb_arg, const char *str, size_t len)
-{
-       php_http_pass_fcall_arg_t *fcd = cb_arg;
-       zval *zdata;
-       TSRMLS_FETCH_FROM_CTX(fcd->ts);
-
-       MAKE_STD_ZVAL(zdata);
-       ZVAL_STRINGL(zdata, str, len, 1);
-       if (SUCCESS == zend_fcall_info_argn(&fcd->fci TSRMLS_CC, 2, &fcd->fcz, &zdata)) {
-               zend_fcall_info_call(&fcd->fci, &fcd->fcc, NULL, NULL TSRMLS_CC);
-               zend_fcall_info_args_clear(&fcd->fci, 0);
-       }
-       zval_ptr_dtor(&zdata);
-       return len;
-}
-
-
-/* ZEND */
-
-/*
- * 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/php_http_misc.h b/php_http_misc.h
deleted file mode 100644 (file)
index a4f579d..0000000
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_MISC_H
-#define PHP_HTTP_MISC_H
-
-/* DEFAULTS */
-
-/* DATE FORMAT RFC1123 */
-#define PHP_HTTP_DATE_FORMAT "D, d M Y H:i:s \\G\\M\\T"
-
-/* CR LF */
-#define PHP_HTTP_CRLF "\r\n"
-
-/* def URL arg separator */
-#define PHP_HTTP_URL_ARGSEP "&"
-
-/* send buffer size */
-#define PHP_HTTP_SENDBUF_SIZE 40960
-
-/* allowed characters of header field names */
-#define PHP_HTTP_HEADER_NAME_CHARS "!#$%&'*+-.^_`|~1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
-
-/* SLEEP */
-
-#define PHP_HTTP_DIFFSEC (0.001)
-#define PHP_HTTP_MLLISEC (1000)
-#define PHP_HTTP_MCROSEC (1000 * 1000)
-#define PHP_HTTP_NANOSEC (1000 * 1000 * 1000)
-#define PHP_HTTP_MSEC(s) ((long)(s * PHP_HTTP_MLLISEC))
-#define PHP_HTTP_USEC(s) ((long)(s * PHP_HTTP_MCROSEC))
-#define PHP_HTTP_NSEC(s) ((long)(s * PHP_HTTP_NANOSEC))
-
-PHP_HTTP_API void php_http_sleep(double s);
-
-/* STRING UTILITIES */
-
-#ifndef PTR_SET
-#      define PTR_SET(STR, SET) \
-       { \
-               PTR_FREE(STR); \
-               STR = SET; \
-       }
-#endif
-
-#define STR_PTR(s) (s?s:"")
-
-#define lenof(S) (sizeof(S) - 1)
-
-#define PHP_HTTP_MATCH_LOOSE   0
-#define PHP_HTTP_MATCH_CASE            0x01
-#define PHP_HTTP_MATCH_WORD            0x10
-#define PHP_HTTP_MATCH_FULL            0x20
-#define PHP_HTTP_MATCH_STRICT  (PHP_HTTP_MATCH_CASE|PHP_HTTP_MATCH_FULL)
-
-int php_http_match(const char *haystack, const char *needle, int flags);
-char *php_http_pretty_key(register char *key, size_t key_len, zend_bool uctitle, zend_bool xhyphen);
-size_t php_http_boundary(char *buf, size_t len TSRMLS_DC);
-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;
-}
-
-static inline const char *php_http_locate_eol(const char *line, int *eol_len)
-{
-       const char *eol = strpbrk(line, "\r\n");
-
-       if (eol_len) {
-               *eol_len = eol ? ((eol[0] == '\r' && eol[1] == '\n') ? 2 : 1) : 0;
-       }
-       return eol;
-}
-
-static inline const char *php_http_locate_bin_eol(const char *bin, size_t len, int *eol_len)
-{
-       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;
-                       }
-                       ++eol;
-               );
-       }
-       return NULL;
-}
-
-/* ZEND */
-
-#if PHP_VERSION_ID < 50400
-#      define object_properties_init(o, ce) zend_hash_copy(((zend_object *) o)->properties, &(ce->default_properties), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*))
-#      define PHP_HTTP_ZEND_LITERAL_DC 
-#      define PHP_HTTP_ZEND_LITERAL_CC
-#      define PHP_HTTP_ZEND_LITERAL_CCN
-#      define ZVAL_COPY_VALUE(zv, arr) do { \
-               (zv)->value = (arr)->value; \
-               Z_TYPE_P(zv) = Z_TYPE_P(arr); \
-       } while (0)
-#else
-#      define PHP_HTTP_ZEND_LITERAL_DC , const zend_literal *literal_key
-#      define PHP_HTTP_ZEND_LITERAL_CC , (literal_key)
-#      define PHP_HTTP_ZEND_LITERAL_CCN , NULL
-#endif
-
-#if PHP_VERSION_ID < 50500
-#undef SUCCESS
-#undef FAILURE
-typedef enum {
-       SUCCESS = 0,
-       FAILURE = -1
-} ZEND_RESULT_CODE;
-#endif
-
-#if PHP_VERSION_ID < 50700
-#      define z_is_true zend_is_true
-#else
-#      define z_is_true(z) zend_is_true(z TSRMLS_CC)
-#endif
-
-#define INIT_PZVAL_ARRAY(zv, ht) \
-       { \
-               INIT_PZVAL((zv)); \
-               Z_TYPE_P(zv) = IS_ARRAY; \
-               Z_ARRVAL_P(zv) = (ht); \
-       }
-
-static inline zval *php_http_zconv(int type, zval *z)
-{
-       switch (type) {
-               case IS_NULL:   convert_to_null_ex(&z);         break;
-               case IS_BOOL:   convert_to_boolean_ex(&z);      break;
-               case IS_LONG:   convert_to_long_ex(&z);         break;
-               case IS_DOUBLE: convert_to_double_ex(&z);       break;
-               case IS_STRING: convert_to_string_ex(&z);       break;
-               case IS_ARRAY:  convert_to_array_ex(&z);        break;
-               case IS_OBJECT: convert_to_object_ex(&z);       break;
-       }
-       return z;
-}
-
-static inline zval *php_http_ztyp(int type, zval *z)
-{
-       SEPARATE_ARG_IF_REF(z);
-       return (Z_TYPE_P(z) == type) ? z : php_http_zconv(type, z);
-}
-
-static inline zval *php_http_zsep(zend_bool add_ref, int type, zval *z)
-{
-       if (add_ref) {
-               Z_ADDREF_P(z);
-       }
-       if (Z_TYPE_P(z) != type) {
-               return php_http_zconv(type, z);
-       } else {
-               SEPARATE_ZVAL_IF_NOT_REF(&z);
-               return z;
-       }
-}
-
-static inline ZEND_RESULT_CODE php_http_ini_entry(const char *name_str, size_t name_len, const char **value_str, size_t *value_len, zend_bool orig TSRMLS_DC)
-{
-       zend_ini_entry *ini_entry;
-
-       if (SUCCESS == zend_hash_find(EG(ini_directives), name_str, name_len + 1, (void *) &ini_entry)) {
-               if (orig && ini_entry->modified) {
-                       *value_str = ini_entry->orig_value;
-                       *value_len = (size_t) ini_entry->orig_value_length;
-               } else {
-                       *value_str = ini_entry->value;
-                       *value_len = (size_t) ini_entry->value_length;
-               }
-               return SUCCESS;
-       }
-       return FAILURE;
-}
-
-/* return object(values) */
-#define RETVAL_OBJECT(o, addref) \
-       RETVAL_OBJVAL((o)->value.obj, addref)
-#define RETURN_OBJECT(o, addref) \
-       RETVAL_OBJECT(o, addref); \
-       return
-#define RETVAL_OBJVAL(ov, addref) \
-       ZVAL_OBJVAL(return_value, ov, addref)
-#define RETURN_OBJVAL(ov, addref) \
-       RETVAL_OBJVAL(ov, addref); \
-       return
-#define ZVAL_OBJVAL(zv, ov, addref) \
-       (zv)->type = IS_OBJECT; \
-       (zv)->value.obj = (ov);\
-       if (addref && Z_OBJ_HT_P(zv)->add_ref) { \
-               Z_OBJ_HT_P(zv)->add_ref((zv) TSRMLS_CC); \
-       }
-
-#define Z_OBJ_DELREF(z) \
-       if (Z_OBJ_HT(z)->del_ref) { \
-               Z_OBJ_HT(z)->del_ref(&(z) TSRMLS_CC); \
-       }
-#define Z_OBJ_ADDREF(z) \
-       if (Z_OBJ_HT(z)->add_ref) { \
-               Z_OBJ_HT(z)->add_ref(&(z) TSRMLS_CC); \
-       }
-#define Z_OBJ_DELREF_P(z) \
-       if (Z_OBJ_HT_P(z)->del_ref) { \
-               Z_OBJ_HT_P(z)->del_ref((z) TSRMLS_CC); \
-       }
-#define Z_OBJ_ADDREF_P(z) \
-       if (Z_OBJ_HT_P(z)->add_ref) { \
-               Z_OBJ_HT_P(z)->add_ref((z) TSRMLS_CC); \
-       }
-#define Z_OBJ_DELREF_PP(z) \
-       if (Z_OBJ_HT_PP(z)->del_ref) { \
-               Z_OBJ_HT_PP(z)->del_ref(*(z) TSRMLS_CC); \
-       }
-#define Z_OBJ_ADDREF_PP(z) \
-       if (Z_OBJ_HT_PP(z)->add_ref) { \
-               Z_OBJ_HT_PP(z)->add_ref(*(z) TSRMLS_CC); \
-       }
-
-#define EMPTY_FUNCTION_ENTRY {NULL, NULL, NULL, 0, 0}
-
-#define PHP_MINIT_CALL(func) PHP_MINIT(func)(INIT_FUNC_ARGS_PASSTHRU)
-#define PHP_RINIT_CALL(func) PHP_RINIT(func)(INIT_FUNC_ARGS_PASSTHRU)
-#define PHP_MSHUTDOWN_CALL(func) PHP_MSHUTDOWN(func)(SHUTDOWN_FUNC_ARGS_PASSTHRU)
-#define PHP_RSHUTDOWN_CALL(func) PHP_RSHUTDOWN(func)(SHUTDOWN_FUNC_ARGS_PASSTHRU)
-
-/* ARRAYS */
-
-#ifndef HASH_KEY_NON_EXISTENT
-#      define HASH_KEY_NON_EXISTENT HASH_KEY_NON_EXISTANT
-#endif
-
-PHP_HTTP_API unsigned php_http_array_list(HashTable *ht TSRMLS_DC, unsigned argc, ...);
-
-typedef struct php_http_array_hashkey {
-       char *str;
-       uint len;
-       ulong num;
-       uint dup:1;
-       uint type:31;
-} php_http_array_hashkey_t;
-#define php_http_array_hashkey_init(dup) {NULL, 0, 0, (dup), 0}
-
-static inline void php_http_array_hashkey_stringify(php_http_array_hashkey_t *key)
-{
-       if (key->type != HASH_KEY_IS_STRING) {
-               key->len = spprintf(&key->str, 0, "%lu", key->num) + 1;
-       }
-}
-
-static inline void php_http_array_hashkey_stringfree(php_http_array_hashkey_t *key)
-{
-       if (key->type != HASH_KEY_IS_STRING || key->dup) {
-               PTR_FREE(key->str);
-       }
-}
-
-#define FOREACH_VAL(pos, array, val) FOREACH_HASH_VAL(pos, HASH_OF(array), val)
-#define FOREACH_HASH_VAL(pos, hash, val) \
-       for (   zend_hash_internal_pointer_reset_ex(hash, &pos); \
-                       zend_hash_get_current_data_ex(hash, (void *) &val, &pos) == SUCCESS; \
-                       zend_hash_move_forward_ex(hash, &pos))
-
-#define FOREACH_KEY(pos, array, key) FOREACH_HASH_KEY(pos, HASH_OF(array), key)
-#define FOREACH_HASH_KEY(pos, hash, _key) \
-       for (   zend_hash_internal_pointer_reset_ex(hash, &pos); \
-                       ((_key).type = zend_hash_get_current_key_ex(hash, &(_key).str, &(_key).len, &(_key).num, (zend_bool) (_key).dup, &pos)) != HASH_KEY_NON_EXISTANT; \
-                       zend_hash_move_forward_ex(hash, &pos)) \
-
-#define FOREACH_KEYVAL(pos, array, key, val) FOREACH_HASH_KEYVAL(pos, HASH_OF(array), key, val)
-#define FOREACH_HASH_KEYVAL(pos, hash, _key, val) \
-       for (   zend_hash_internal_pointer_reset_ex(hash, &pos); \
-                       ((_key).type = zend_hash_get_current_key_ex(hash, &(_key).str, &(_key).len, &(_key).num, (zend_bool) (_key).dup, &pos)) != HASH_KEY_NON_EXISTANT && \
-                       zend_hash_get_current_data_ex(hash, (void *) &val, &pos) == SUCCESS; \
-                       zend_hash_move_forward_ex(hash, &pos))
-
-#define array_copy(src, dst) zend_hash_copy(dst, src, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *))
-#define array_copy_strings(src, dst) zend_hash_copy(dst, src, php_http_array_copy_strings, NULL, sizeof(zval *))
-#define ARRAY_JOIN_STRONLY   0x01
-#define ARRAY_JOIN_PRETTIFY  0x02
-#define ARRAY_JOIN_STRINGIFY 0x04
-#define array_join(src, dst, append, flags) zend_hash_apply_with_arguments(src TSRMLS_CC, (append)?php_http_array_apply_append_func:php_http_array_apply_merge_func, 2, dst, (int)flags)
-
-void php_http_array_copy_strings(void *zpp);
-int php_http_array_apply_append_func(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key);
-int php_http_array_apply_merge_func(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key);
-
-/* PASS CALLBACK */
-
-typedef size_t (*php_http_pass_callback_t)(void *cb_arg, const char *str, size_t len);
-typedef size_t (*php_http_pass_php_http_buffer_callback_t)(void *cb_arg, php_http_buffer_t *str);
-typedef size_t (*php_http_pass_format_callback_t)(void *cb_arg, const char *fmt, ...);
-
-typedef struct php_http_pass_fcall_arg {
-       zval *fcz;
-       zend_fcall_info fci;
-       zend_fcall_info_cache fcc;
-#ifdef ZTS
-       void ***ts;
-#endif
-} php_http_pass_fcall_arg_t;
-
-PHP_HTTP_API size_t php_http_pass_fcall_callback(void *cb_arg, const char *str, size_t len);
-
-#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/php_http_negotiate.c b/php_http_negotiate.c
deleted file mode 100644 (file)
index a74875b..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-static int php_http_negotiate_sort(const void *a, const void *b TSRMLS_DC)
-{
-       zval result, *first, *second;
-
-       first = *((zval **) (*((Bucket **) a))->pData);
-       second= *((zval **) (*((Bucket **) b))->pData);
-
-       if (numeric_compare_function(&result, first, second TSRMLS_CC) != SUCCESS) {
-               return 0;
-       }
-       return (Z_LVAL(result) > 0 ? -1 : (Z_LVAL(result) < 0 ? 1 : 0));
-}
-
-#define M_PRI 5
-#define M_SEC 2
-#define M_ANY 1
-#define M_NOT 0
-#define M_ALL -1
-static inline unsigned php_http_negotiate_match(const char *param_str, size_t param_len, const char *supported_str, size_t supported_len, const char *sep_str, size_t sep_len)
-{
-       int match = M_NOT;
-
-       if (param_len == supported_len && !strncasecmp(param_str, supported_str, param_len)) {
-               /* that was easy */
-               match = M_ALL;
-       } else if (sep_str && sep_len) {
-               const char *param_sec = php_http_locate_str(param_str, param_len, sep_str, sep_len);
-               size_t param_pri_len = param_sec ? param_sec - param_str : param_len;
-               const char *supported_sec = php_http_locate_str(supported_str, supported_len, sep_str, sep_len);
-               size_t supported_pri_len = supported_sec ? supported_sec - supported_str : supported_len;
-               size_t cmp_len = MIN(param_pri_len, supported_pri_len);
-
-               if (((*param_str == '*') || (*supported_str == '*'))
-               ||      ((param_pri_len == supported_pri_len) && !strncasecmp(param_str, supported_str, param_pri_len))
-               ||      ((!param_sec || !supported_sec) && cmp_len && !strncasecmp(param_str, supported_str, cmp_len))
-               ) {
-                       match += M_PRI;
-               }
-
-               if (param_sec && supported_sec && !strcasecmp(param_sec, supported_sec)) {
-                       match += M_SEC;
-               }
-
-               if ((param_sec && *(param_sec + sep_len) == '*')
-               ||      (supported_sec && *(supported_sec + sep_len) == '*')
-               ||      ((*param_str == '*') || (*supported_str == '*'))
-               ) {
-                       match += M_ANY;
-               }
-       }
-#if 0
-       fprintf(stderr, "match: %s == %s => %u\n", supported_str, param_str, match);
-#endif
-       return match;
-}
-
-static int php_http_negotiate_reduce(void *p TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
-{
-       unsigned best_match = 0;
-       HashPosition pos;
-       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-       zval **q = NULL, **val, *supported = php_http_ztyp(IS_STRING, *(zval **)p);
-       HashTable *params = va_arg(args, HashTable *);
-       HashTable *result = va_arg(args, HashTable *);
-       const char *sep_str = va_arg(args, const char *);
-       size_t sep_len = va_arg(args, size_t);
-
-       FOREACH_HASH_KEYVAL(pos, params, key, val) {
-               if (key.type == HASH_KEY_IS_STRING) {
-                       unsigned match = php_http_negotiate_match(key.str, key.len-1, Z_STRVAL_P(supported), Z_STRLEN_P(supported), sep_str, sep_len);
-
-                       if (match > best_match) {
-                               best_match = match;
-                               q = val;
-                       }
-               }
-       }
-
-       if (q && Z_DVAL_PP(q) > 0) {
-               Z_ADDREF_PP(q);
-               zend_hash_update(result, Z_STRVAL_P(supported), Z_STRLEN_P(supported) + 1, (void *) q, sizeof(zval *), NULL);
-       }
-
-       zval_ptr_dtor(&supported);
-       return ZEND_HASH_APPLY_KEEP;
-}
-
-HashTable *php_http_negotiate(const char *value_str, size_t value_len, HashTable *supported, const char *primary_sep_str, size_t primary_sep_len TSRMLS_DC)
-{
-       HashTable *result = NULL;
-
-       if (value_str && value_len) {
-               unsigned i = 0;
-               zval arr, **val, **arg, **zq;
-               HashPosition pos;
-               HashTable params;
-               php_http_array_hashkey_t key = php_http_array_hashkey_init(1);
-               php_http_params_opts_t opts;
-
-               zend_hash_init(&params, 10, NULL, ZVAL_PTR_DTOR, 0);
-               php_http_params_opts_default_get(&opts);
-               opts.input.str = estrndup(value_str, value_len);
-               opts.input.len = value_len;
-               php_http_params_parse(&params, &opts TSRMLS_CC);
-               efree(opts.input.str);
-
-               INIT_PZVAL(&arr);
-               array_init(&arr);
-
-               FOREACH_HASH_KEYVAL(pos, &params, key, val) {
-                       double q;
-
-                       if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("arguments"), (void *) &arg)
-                       &&      IS_ARRAY == Z_TYPE_PP(arg)
-                       &&      SUCCESS == zend_hash_find(Z_ARRVAL_PP(arg), ZEND_STRS("q"), (void *) &zq)) {
-                               zval *tmp = php_http_ztyp(IS_DOUBLE, *zq);
-
-                               q = Z_DVAL_P(tmp);
-                               zval_ptr_dtor(&tmp);
-                       } else {
-                               q = 1.0 - ++i / 100.0;
-                       }
-
-                       if (key.type == HASH_KEY_IS_STRING) {
-                               add_assoc_double_ex(&arr, key.str, key.len, q);
-                       } else {
-                               add_index_double(&arr, key.num, q);
-                       }
-
-                       PTR_FREE(key.str);
-               }
-
-#if 0
-               zend_print_zval_r(&arr, 1 TSRMLS_CC);
-#endif
-
-               ALLOC_HASHTABLE(result);
-               zend_hash_init(result, zend_hash_num_elements(supported), NULL, ZVAL_PTR_DTOR, 0);
-               zend_hash_apply_with_arguments(supported TSRMLS_CC, php_http_negotiate_reduce, 4, Z_ARRVAL(arr), result, primary_sep_str, primary_sep_len);
-               zend_hash_destroy(&params);
-               zval_dtor(&arr);
-               zend_hash_sort(result, zend_qsort, php_http_negotiate_sort, 0 TSRMLS_CC);
-       }
-       
-       return result;
-}
-
-
-
-/*
- * 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/php_http_negotiate.h b/php_http_negotiate.h
deleted file mode 100644 (file)
index f7405b5..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_NEGOTIATE_H
-#define PHP_HTTP_NEGOTIATE_H
-
-PHP_HTTP_API HashTable *php_http_negotiate(const char *value_str, size_t value_len, HashTable *supported, const char *primary_sep_str, size_t primary_sep_len TSRMLS_DC);
-
-static inline HashTable *php_http_negotiate_language(HashTable *supported, php_http_message_t *request TSRMLS_DC)
-{
-       HashTable *result = NULL;
-       size_t length;
-       char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Language"), &length, request TSRMLS_CC);
-
-       if (value) {
-               result = php_http_negotiate(value, length, supported, "-", 1 TSRMLS_CC);
-       }
-       PTR_FREE(value);
-
-       return result;
-}
-
-static inline HashTable *php_http_negotiate_encoding(HashTable *supported, php_http_message_t *request TSRMLS_DC)
-{
-       HashTable *result = NULL;
-       size_t length;
-       char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Encoding"), &length, request TSRMLS_CC);
-
-       if (value) {
-               result = php_http_negotiate(value, length, supported, NULL, 0 TSRMLS_CC);
-       }
-       PTR_FREE(value);
-
-       return result;
-}
-
-static inline HashTable *php_http_negotiate_charset(HashTable *supported, php_http_message_t *request TSRMLS_DC)
-{
-       HashTable *result = NULL;
-       size_t length;
-       char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Charset"), &length, request TSRMLS_CC);
-
-       if (value) {
-               result = php_http_negotiate(value, length, supported, NULL, 0 TSRMLS_CC);
-       }
-       PTR_FREE(value);
-
-       return result;
-}
-
-static inline HashTable *php_http_negotiate_content_type(HashTable *supported, php_http_message_t *request TSRMLS_DC)
-{
-       HashTable *result = NULL;
-       size_t length;
-       char *value = php_http_env_get_request_header(ZEND_STRL("Accept"), &length, request TSRMLS_CC);
-
-       if (value) {
-               result = php_http_negotiate(value, length, supported, "/", 1 TSRMLS_CC);
-       }
-       PTR_FREE(value);
-
-       return result;
-}
-
-#define PHP_HTTP_DO_NEGOTIATE_DEFAULT(supported) \
-       { \
-               zval **value; \
-                \
-               zend_hash_internal_pointer_reset((supported)); \
-               if (SUCCESS == zend_hash_get_current_data((supported), (void *) &value)) { \
-                       RETVAL_ZVAL(*value, 1, 0); \
-               } else { \
-                       RETVAL_NULL(); \
-               } \
-       }
-
-#define PHP_HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported, rs_array) \
-       PHP_HTTP_DO_NEGOTIATE_DEFAULT(supported); \
-       if (rs_array) { \
-               HashPosition pos; \
-               zval **value_ptr; \
-                \
-               FOREACH_HASH_VAL(pos, supported, value_ptr) { \
-                       zval *value = php_http_ztyp(IS_STRING, *value_ptr); \
-                       add_assoc_double(rs_array, Z_STRVAL_P(value), 1.0); \
-                       zval_ptr_dtor(&value); \
-               } \
-       }
-
-#define PHP_HTTP_DO_NEGOTIATE_HANDLE_RESULT(result, supported, rs_array) \
-       { \
-               char *key; \
-               uint key_len; \
-               ulong idx; \
-                \
-               if (zend_hash_num_elements(result) && HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(result, &key, &key_len, &idx, 1, NULL)) { \
-                       RETVAL_STRINGL(key, key_len-1, 0); \
-               } else { \
-                       PHP_HTTP_DO_NEGOTIATE_DEFAULT(supported); \
-               } \
-               \
-               if (rs_array) { \
-                       zend_hash_copy(Z_ARRVAL_P(rs_array), result, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); \
-               } \
-               \
-               zend_hash_destroy(result); \
-               FREE_HASHTABLE(result); \
-       }
-
-#define PHP_HTTP_DO_NEGOTIATE(type, supported, rs_array) \
-       { \
-               HashTable *result; \
-               if ((result = php_http_negotiate_ ##type(supported, NULL TSRMLS_CC))) { \
-                       PHP_HTTP_DO_NEGOTIATE_HANDLE_RESULT(result, supported, rs_array); \
-               } else { \
-                       PHP_HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported, rs_array); \
-               } \
-       }
-
-
-#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/php_http_object.c b/php_http_object.c
deleted file mode 100644 (file)
index d57388d..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-zend_object_value php_http_object_new(zend_class_entry *ce TSRMLS_DC)
-{
-       return php_http_object_new_ex(ce, NULL, NULL TSRMLS_CC);
-}
-
-zend_object_value php_http_object_new_ex(zend_class_entry *ce, void *nothing, php_http_object_t **ptr TSRMLS_DC)
-{
-       php_http_object_t *o;
-
-       o = ecalloc(1, sizeof(php_http_object_t));
-       zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
-       object_properties_init((zend_object *) o, ce);
-
-       if (ptr) {
-               *ptr = o;
-       }
-
-       o->zv.handle = zend_objects_store_put(o, NULL, (zend_objects_free_object_storage_t) zend_objects_free_object_storage, NULL TSRMLS_CC);
-       o->zv.handlers = zend_get_std_object_handlers();
-
-       return o->zv;
-}
-
-ZEND_RESULT_CODE php_http_new(zend_object_value *ovp, zend_class_entry *ce, php_http_new_t create, zend_class_entry *parent_ce, void *intern_ptr, void **obj_ptr TSRMLS_DC)
-{
-       zend_object_value ov;
-
-       if (!ce) {
-               ce = parent_ce;
-       } else if (parent_ce && !instanceof_function(ce, parent_ce TSRMLS_CC)) {
-               php_http_throw(unexpected_val, "Class %s does not extend %s", ce->name, parent_ce->name);
-               return FAILURE;
-       }
-
-       ov = create(ce, intern_ptr, obj_ptr TSRMLS_CC);
-       if (ovp) {
-               *ovp = ov;
-       }
-       return SUCCESS;
-}
-
-static inline zend_function *get_object_method(zval *zobject, zval *zmeth TSRMLS_DC)
-{
-#if PHP_VERSION_ID >= 50400
-       return Z_OBJ_HT_P(zobject)->get_method(&zobject, Z_STRVAL_P(zmeth), Z_STRLEN_P(zmeth), NULL TSRMLS_CC);
-#else
-       return Z_OBJ_HT_P(zobject)->get_method(&zobject, Z_STRVAL_P(zmeth), Z_STRLEN_P(zmeth) TSRMLS_CC);
-#endif
-}
-
-php_http_object_method_t *php_http_object_method_init(php_http_object_method_t *cb, zval *zobject, const char *method_str, size_t method_len TSRMLS_DC)
-{
-       zval *zfn;
-
-       if (!cb) {
-               cb = ecalloc(1, sizeof(*cb));
-       } else {
-               memset(cb, 0, sizeof(*cb));
-       }
-
-       MAKE_STD_ZVAL(zfn);
-       ZVAL_STRINGL(zfn, method_str, method_len, 1);
-
-       cb->fci.size = sizeof(cb->fci);
-       cb->fci.function_name = zfn;
-       cb->fcc.initialized = 1;
-       cb->fcc.calling_scope = cb->fcc.called_scope = Z_OBJCE_P(zobject);
-       cb->fcc.function_handler = get_object_method(zobject, cb->fci.function_name TSRMLS_CC);
-
-       return cb;
-}
-
-void php_http_object_method_dtor(php_http_object_method_t *cb)
-{
-       if (cb->fci.function_name) {
-               zval_ptr_dtor(&cb->fci.function_name);
-               cb->fci.function_name = NULL;
-       }
-}
-
-void php_http_object_method_free(php_http_object_method_t **cb)
-{
-       if (*cb) {
-               php_http_object_method_dtor(*cb);
-               efree(*cb);
-               *cb = NULL;
-       }
-}
-
-ZEND_RESULT_CODE php_http_object_method_call(php_http_object_method_t *cb, zval *zobject, zval **retval_ptr, int argc, zval ***args TSRMLS_DC)
-{
-       ZEND_RESULT_CODE rv;
-       zval *retval = NULL;
-
-       Z_ADDREF_P(zobject);
-       cb->fci.object_ptr = zobject;
-       cb->fcc.object_ptr = zobject;
-
-       cb->fci.retval_ptr_ptr = retval_ptr ? retval_ptr : &retval;
-
-       cb->fci.param_count = argc;
-       cb->fci.params = args;
-
-       if (cb->fcc.called_scope != Z_OBJCE_P(zobject)) {
-               cb->fcc.called_scope = Z_OBJCE_P(zobject);
-               cb->fcc.function_handler = get_object_method(zobject, cb->fci.function_name TSRMLS_CC);
-       }
-
-       rv = zend_call_function(&cb->fci, &cb->fcc TSRMLS_CC);
-
-       zval_ptr_dtor(&zobject);
-       if (!retval_ptr && retval) {
-               zval_ptr_dtor(&retval);
-       }
-
-       return rv;
-}
-
-/*
- * 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/php_http_object.h b/php_http_object.h
deleted file mode 100644 (file)
index 3f75932..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_OBJECT_H
-#define PHP_HTTP_OBJECT_H
-
-typedef struct php_http_object {
-       zend_object zo;
-       zend_object_value zv;
-} php_http_object_t;
-
-zend_object_value php_http_object_new(zend_class_entry *ce TSRMLS_DC);
-zend_object_value php_http_object_new_ex(zend_class_entry *ce, void *nothing, php_http_object_t **ptr TSRMLS_DC);
-
-typedef zend_object_value (*php_http_new_t)(zend_class_entry *ce, void *, void ** TSRMLS_DC);
-
-ZEND_RESULT_CODE php_http_new(zend_object_value *ov, zend_class_entry *ce, php_http_new_t create, zend_class_entry *parent_ce, void *intern_ptr, void **obj_ptr TSRMLS_DC);
-
-typedef struct php_http_method {
-       zend_fcall_info fci;
-       zend_fcall_info_cache fcc;
-} php_http_object_method_t;
-
-php_http_object_method_t *php_http_object_method_init(php_http_object_method_t *cb, zval *zobject, const char *method_str, size_t method_len TSRMLS_DC);
-ZEND_RESULT_CODE php_http_object_method_call(php_http_object_method_t *cb, zval *zobject, zval **retval, int argc, zval ***args TSRMLS_DC);
-void php_http_object_method_dtor(php_http_object_method_t *cb);
-void php_http_object_method_free(php_http_object_method_t **cb);
-
-#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/php_http_options.c b/php_http_options.c
deleted file mode 100644 (file)
index 59b9c5f..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-php_http_options_t *php_http_options_init(php_http_options_t *registry, zend_bool persistent)
-{
-       if (!registry) {
-               registry = pecalloc(1, sizeof(*registry), persistent);
-       } else {
-               memset(registry, 0, sizeof(*registry));
-       }
-
-       registry->persistent = persistent;
-       zend_hash_init(&registry->options, 0, NULL, (dtor_func_t) zend_hash_destroy, persistent);
-
-       return registry;
-}
-
-ZEND_RESULT_CODE php_http_options_apply(php_http_options_t *registry, HashTable *options, void *userdata)
-{
-       HashPosition pos;
-       zval *val;
-       php_http_option_t *opt;
-
-       FOREACH_HASH_VAL(pos, &registry->options, opt) {
-               if (!(val = registry->getter(opt, options, userdata))) {
-                       val = &opt->defval;
-               }
-               if (registry->setter) {
-                       if (SUCCESS != registry->setter(opt, val, userdata)) {
-                               return FAILURE;
-                       }
-               } else if (!opt->setter || SUCCESS != opt->setter(opt, val, userdata)) {
-                       return FAILURE;
-               }
-       }
-       return SUCCESS;
-}
-
-void php_http_options_dtor(php_http_options_t *registry)
-{
-       zend_hash_destroy(&registry->options);
-}
-
-void php_http_options_free(php_http_options_t **registry)
-{
-       if (*registry) {
-               php_http_options_dtor(*registry);
-               pefree(*registry, (*registry)->persistent);
-               *registry = NULL;
-       }
-}
-
-php_http_option_t *php_http_option_register(php_http_options_t *registry, const char *name_str, size_t name_len, ulong option, zend_uchar type)
-{
-       php_http_option_t opt, *dst = NULL;
-
-       memset(&opt, 0, sizeof(opt));
-
-       php_http_options_init(&opt.suboptions, registry->persistent);
-       opt.suboptions.getter = registry->getter;
-       opt.suboptions.setter = registry->setter;
-
-       opt.name.h = zend_hash_func(opt.name.s = name_str, opt.name.l = name_len + 1);
-       opt.type = type;
-       opt.option = option;
-
-       INIT_ZVAL(opt.defval);
-       switch ((opt.type = type)) {
-       case IS_BOOL:
-               ZVAL_BOOL(&opt.defval, 0);
-               break;
-
-       case IS_LONG:
-               ZVAL_LONG(&opt.defval, 0);
-               break;
-
-       case IS_STRING:
-               ZVAL_STRINGL(&opt.defval, NULL, 0, 0);
-               break;
-
-       case IS_DOUBLE:
-               ZVAL_DOUBLE(&opt.defval, 0);
-               break;
-
-       default:
-               ZVAL_NULL(&opt.defval);
-               break;
-       }
-
-       zend_hash_quick_update(&registry->options, opt.name.s, opt.name.l, opt.name.h, (void *) &opt, sizeof(opt), (void *) &dst);
-       return dst;
-}
-
-zval *php_http_option_get(php_http_option_t *opt, HashTable *options, void *userdata)
-{
-       if (options) {
-               zval **zoption;
-
-               if (SUCCESS == zend_hash_quick_find(options, opt->name.s, opt->name.l, opt->name.h, (void *) &zoption)) {
-                       return *zoption;
-               }
-       }
-
-       return NULL;
-}
-
-
-/*
- * 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/php_http_options.h b/php_http_options.h
deleted file mode 100644 (file)
index 2475383..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_OPTIONS_H
-#define PHP_HTTP_OPTIONS_H
-
-typedef struct php_http_option php_http_option_t;
-typedef struct php_http_options php_http_options_t;
-
-typedef ZEND_RESULT_CODE (*php_http_option_set_callback_t)(php_http_option_t *opt, zval *val, void *userdata);
-typedef zval *(*php_http_option_get_callback_t)(php_http_option_t *opt, HashTable *options, void *userdata);
-
-struct php_http_options {
-       HashTable options;
-
-       php_http_option_get_callback_t getter;
-       php_http_option_set_callback_t setter;
-
-       unsigned persistent:1;
-};
-
-struct php_http_option {
-       php_http_options_t suboptions;
-
-       struct {
-               const char *s;
-               size_t l;
-               ulong h;
-       } name;
-
-       ulong option;
-       zend_uchar type;
-       unsigned flags;
-       zval defval;
-
-       php_http_option_set_callback_t setter;
-};
-
-PHP_HTTP_API php_http_options_t *php_http_options_init(php_http_options_t *registry, zend_bool persistent);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_options_apply(php_http_options_t *registry, HashTable *options, void *userdata);
-PHP_HTTP_API void php_http_options_dtor(php_http_options_t *registry);
-PHP_HTTP_API void php_http_options_free(php_http_options_t **registry);
-
-PHP_HTTP_API php_http_option_t *php_http_option_register(php_http_options_t *registry, const char *name_str, size_t name_len, ulong option, zend_uchar type);
-PHP_HTTP_API zval *php_http_option_get(php_http_option_t *opt, HashTable *options, void *userdata);
-
-#endif /* PHP_HTTP_OPTIONS_H */
-
-/*
- * 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/php_http_params.c b/php_http_params.c
deleted file mode 100644 (file)
index 5adeb91..0000000
+++ /dev/null
@@ -1,1283 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-static php_http_params_token_t def_param_sep = {",", 1}, *def_param_sep_ptr[] = {&def_param_sep, NULL};
-static php_http_params_token_t def_arg_sep = {";", 1}, *def_arg_sep_ptr[] = {&def_arg_sep, NULL};
-static php_http_params_token_t def_val_sep = {"=", 1}, *def_val_sep_ptr[] = {&def_val_sep, NULL};
-static php_http_params_opts_t def_opts = {
-       {NULL, 0},
-       def_param_sep_ptr,
-       def_arg_sep_ptr,
-       def_val_sep_ptr,
-       NULL,
-       PHP_HTTP_PARAMS_DEFAULT
-};
-
-php_http_params_opts_t *php_http_params_opts_default_get(php_http_params_opts_t *opts)
-{
-       if (!opts) {
-               opts = emalloc(sizeof(*opts));
-       }
-
-       memcpy(opts, &def_opts, sizeof(def_opts));
-
-       return opts;
-}
-
-typedef struct php_http_params_state {
-       php_http_params_token_t input;
-       php_http_params_token_t param;
-       php_http_params_token_t arg;
-       php_http_params_token_t val;
-       struct {
-               zval **param;
-               zval **args;
-               zval **val;
-       } current;
-       unsigned quotes:1;
-       unsigned escape:1;
-       unsigned rfc5987:1;
-} php_http_params_state_t;
-
-static inline void sanitize_escaped(zval *zv TSRMLS_DC)
-{
-       if (Z_STRVAL_P(zv)[0] == '"' && Z_STRVAL_P(zv)[Z_STRLEN_P(zv) - 1] == '"') {
-               size_t deq_len = Z_STRLEN_P(zv) - 2;
-               char *deq = estrndup(Z_STRVAL_P(zv) + 1, deq_len);
-
-               zval_dtor(zv);
-               ZVAL_STRINGL(zv, deq, deq_len, 0);
-       }
-
-       php_stripcslashes(Z_STRVAL_P(zv), &Z_STRLEN_P(zv));
-}
-
-static inline void quote_string(zval *zv, zend_bool force TSRMLS_DC)
-{
-       int len = Z_STRLEN_P(zv);
-
-       Z_STRVAL_P(zv) = php_addcslashes(Z_STRVAL_P(zv), Z_STRLEN_P(zv), &Z_STRLEN_P(zv), 1,
-                       ZEND_STRL("\0..\37\173\\\"") TSRMLS_CC);
-
-       if (force || len != Z_STRLEN_P(zv) || strpbrk(Z_STRVAL_P(zv), "()<>@,;:\"[]?={} ")) {
-               zval tmp = *zv;
-               int len = Z_STRLEN_P(zv) + 2;
-               char *str = emalloc(len + 1);
-
-               str[0] = '"';
-               memcpy(&str[1], Z_STRVAL_P(zv), Z_STRLEN_P(zv));
-               str[len-1] = '"';
-               str[len] = '\0';
-
-               zval_dtor(&tmp);
-               ZVAL_STRINGL(zv, str, len, 0);
-       }
-}
-
-static inline void prepare_escaped(zval *zv TSRMLS_DC)
-{
-       if (Z_TYPE_P(zv) == IS_STRING) {
-               quote_string(zv, 0 TSRMLS_CC);
-       } else {
-               zval_dtor(zv);
-               ZVAL_EMPTY_STRING(zv);
-       }
-}
-
-static inline void sanitize_urlencoded(zval *zv TSRMLS_DC)
-{
-       Z_STRLEN_P(zv) = php_raw_url_decode(Z_STRVAL_P(zv), Z_STRLEN_P(zv));
-}
-
-static inline void prepare_urlencoded(zval *zv TSRMLS_DC)
-{
-       int len;
-       char *str =     php_raw_url_encode(Z_STRVAL_P(zv), Z_STRLEN_P(zv), &len);
-
-       zval_dtor(zv);
-       ZVAL_STRINGL(zv, str, len, 0);
-}
-
-static void sanitize_dimension(zval *zv TSRMLS_DC)
-{
-       zval *arr = NULL, *tmp = NULL, **cur = NULL;
-       char *var = NULL, *ptr = Z_STRVAL_P(zv), *end = Z_STRVAL_P(zv) + Z_STRLEN_P(zv);
-       long level = 0;
-
-       MAKE_STD_ZVAL(arr);
-       array_init(arr);
-       cur = &arr;
-
-       while (ptr < end) {
-               if (!var) {
-                       var = ptr;
-               }
-
-               switch (*ptr) {
-                       case '[':
-                               if (++level > PG(max_input_nesting_level)) {
-                                       zval_ptr_dtor(&arr);
-                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Max input nesting level of %ld exceeded", (long) PG(max_input_nesting_level));
-                                       return;
-                               }
-                               if (ptr - var == 0) {
-                                       ++var;
-                                       break;
-                               }
-                               /* no break */
-
-                       case ']':
-
-                               MAKE_STD_ZVAL(tmp);
-                               ZVAL_NULL(tmp);
-                               convert_to_array(*cur);
-
-                               if (ptr - var) {
-                                       char chr = *ptr;
-                                       *ptr = '\0';
-                                       zend_symtable_update(Z_ARRVAL_PP(cur), var, ptr - var + 1, (void *) &tmp, sizeof(zval *), (void *) &cur);
-                                       *ptr = chr;
-                               } else {
-                                       zend_hash_next_index_insert(Z_ARRVAL_PP(cur), (void *) &tmp, sizeof(zval *), (void *) &cur);
-                               }
-
-                               var = NULL;
-                               break;
-               }
-
-               ++ptr;
-       }
-
-       if (zend_hash_num_elements(Z_ARRVAL_P(arr))) {
-               zval_dtor(zv);
-#if PHP_VERSION_ID >= 50400
-               ZVAL_COPY_VALUE(zv, arr);
-#else
-               zv->value = arr->value;
-               Z_TYPE_P(zv) = Z_TYPE_P(arr);
-#endif
-               FREE_ZVAL(arr);
-       } else {
-               zval_ptr_dtor(&arr);
-       }
-}
-
-static inline void shift_key(php_http_buffer_t *buf, char *key_str, size_t key_len, const char *ass, size_t asl, unsigned flags TSRMLS_DC);
-static inline void shift_val(php_http_buffer_t *buf, zval *zvalue, const char *vss, size_t vsl, unsigned flags TSRMLS_DC);
-
-static void prepare_dimension(php_http_buffer_t *buf, php_http_buffer_t *keybuf, zval *zvalue, const char *pss, size_t psl, const char *vss, size_t vsl, unsigned flags TSRMLS_DC)
-{
-       HashTable *ht = HASH_OF(zvalue);
-       HashPosition pos;
-       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-       zval **val;
-       php_http_buffer_t prefix;
-
-       if (!ht->nApplyCount++) {
-               php_http_buffer_init(&prefix);
-               php_http_buffer_append(&prefix, keybuf->data, keybuf->used);
-
-               FOREACH_HASH_KEYVAL(pos, ht, key, val) {
-                       if (key.type == HASH_KEY_IS_STRING && !*key.str) {
-                               /* only public properties */
-                               continue;
-                       }
-
-                       php_http_buffer_appends(&prefix, "[");
-                       if (key.type == HASH_KEY_IS_STRING) {
-                               php_http_buffer_append(&prefix, key.str, key.len - 1);
-                       } else {
-                               php_http_buffer_appendf(&prefix, "%lu", key.num);
-                       }
-                       php_http_buffer_appends(&prefix, "]");
-
-                       if (Z_TYPE_PP(val) == IS_ARRAY || Z_TYPE_PP(val) == IS_OBJECT) {
-                               prepare_dimension(buf, &prefix, *val, pss, psl, vss, vsl, flags TSRMLS_CC);
-                       } else {
-                               zval *cpy = php_http_ztyp(IS_STRING, *val);
-
-                               shift_key(buf, prefix.data, prefix.used, pss, psl, flags TSRMLS_CC);
-                               shift_val(buf, cpy, vss, vsl, flags TSRMLS_CC);
-                               zval_ptr_dtor(&cpy);
-                       }
-
-                       php_http_buffer_cut(&prefix, keybuf->used, prefix.used - keybuf->used);
-               }
-               php_http_buffer_dtor(&prefix);
-       }
-       --ht->nApplyCount;
-}
-
-static inline void sanitize_key(unsigned flags, char *str, size_t len, zval *zv, zend_bool *rfc5987 TSRMLS_DC)
-{
-       char *eos;
-
-       zval_dtor(zv);
-       php_trim(str, len, NULL, 0, zv, 3 TSRMLS_CC);
-
-       if (flags & PHP_HTTP_PARAMS_ESCAPED) {
-               sanitize_escaped(zv TSRMLS_CC);
-       }
-       
-       if (!Z_STRLEN_P(zv)) {
-               return;
-       }
-
-       eos = &Z_STRVAL_P(zv)[Z_STRLEN_P(zv)-1];
-       if (*eos == '*') {
-               *eos = '\0';
-               *rfc5987 = 1;
-               Z_STRLEN_P(zv) -= 1;
-       }
-
-       if (flags & PHP_HTTP_PARAMS_URLENCODED) {
-               sanitize_urlencoded(zv TSRMLS_CC);
-       }
-
-       if (flags & PHP_HTTP_PARAMS_DIMENSION) {
-               sanitize_dimension(zv TSRMLS_CC);
-       }
-}
-
-static inline void sanitize_rfc5987(zval *zv, char **language, zend_bool *latin1 TSRMLS_DC)
-{
-       char *ptr;
-
-       /* examples:
-        * iso-8850-1'de'bl%f6der%20schei%df%21
-        * utf-8'de-DE'bl%c3%b6der%20schei%c3%9f%21
-        */
-
-       switch (Z_STRVAL_P(zv)[0]) {
-       case 'I':
-       case 'i':
-               if (!strncasecmp(Z_STRVAL_P(zv), "iso-8859-1", lenof("iso-8859-1"))) {
-                       *latin1 = 1;
-                       ptr = Z_STRVAL_P(zv) + lenof("iso-8859-1");
-                       break;
-               }
-               /* no break */
-       case 'U':
-       case 'u':
-               if (!strncasecmp(Z_STRVAL_P(zv), "utf-8", lenof("utf-8"))) {
-                       *latin1 = 0;
-                       ptr = Z_STRVAL_P(zv) + lenof("utf-8");
-                       break;
-               }
-               /* no break */
-       default:
-               return;
-       }
-
-       /* extract language */
-       if (*ptr == '\'') {
-               for (*language = ++ptr; *ptr && *ptr != '\''; ++ptr);
-               if (!*ptr) {
-                       *language = NULL;
-                       return;
-               }
-               *language = estrndup(*language, ptr - *language);
-
-               /* remainder */
-               ptr = estrdup(++ptr);
-               zval_dtor(zv);
-               ZVAL_STRING(zv, ptr, 0);
-       }
-}
-
-static inline void sanitize_rfc5988(char *str, size_t len, zval *zv TSRMLS_DC)
-{
-       zval_dtor(zv);
-       php_trim(str, len, " ><", 3, zv, 3 TSRMLS_CC);
-}
-
-static inline void prepare_rfc5988(zval *zv TSRMLS_DC)
-{
-       if (Z_TYPE_P(zv) != IS_STRING) {
-               zval_dtor(zv);
-               ZVAL_EMPTY_STRING(zv);
-       }
-}
-
-static void utf8encode(zval *zv)
-{
-       size_t pos, len = 0;
-       unsigned char *ptr = (unsigned char *) Z_STRVAL_P(zv);
-
-       while (*ptr) {
-               if (*ptr++ >= 0x80) {
-                       ++len;
-               }
-               ++len;
-       }
-
-       ptr = safe_emalloc(1, len, 1);
-       for (len = 0, pos = 0; len <= Z_STRLEN_P(zv); ++len, ++pos) {
-               ptr[pos] = Z_STRVAL_P(zv)[len];
-               if ((ptr[pos]) >= 0x80) {
-                       ptr[pos + 1] = 0x80 | (ptr[pos] & 0x3f);
-                       ptr[pos] = 0xc0 | ((ptr[pos] >> 6) & 0x1f);
-                       ++pos;
-               }
-       }
-       zval_dtor(zv);
-       ZVAL_STRINGL(zv, (char *) ptr, pos-1, 0);
-}
-
-static inline void sanitize_value(unsigned flags, char *str, size_t len, zval *zv, zend_bool rfc5987 TSRMLS_DC)
-{
-       char *language = NULL;
-       zend_bool latin1 = 0;
-
-       zval_dtor(zv);
-       php_trim(str, len, NULL, 0, zv, 3 TSRMLS_CC);
-
-       if (rfc5987) {
-               sanitize_rfc5987(zv, &language, &latin1 TSRMLS_CC);
-       }
-
-       if (flags & PHP_HTTP_PARAMS_ESCAPED) {
-               sanitize_escaped(zv TSRMLS_CC);
-       }
-
-       if ((flags & PHP_HTTP_PARAMS_URLENCODED) || (rfc5987 && language)) {
-               sanitize_urlencoded(zv TSRMLS_CC);
-       }
-
-       if (rfc5987 && language) {
-               zval *tmp;
-
-               if (latin1) {
-                       utf8encode(zv);
-               }
-
-               MAKE_STD_ZVAL(tmp);
-               ZVAL_COPY_VALUE(tmp, zv);
-               array_init(zv);
-               add_assoc_zval(zv, language, tmp);
-               PTR_FREE(language);
-       }
-}
-
-static inline void prepare_key(unsigned flags, char *old_key, size_t old_len, char **new_key, size_t *new_len TSRMLS_DC)
-{
-       zval zv;
-
-       INIT_PZVAL(&zv);
-       ZVAL_STRINGL(&zv, old_key, old_len, 1);
-
-       if (flags & PHP_HTTP_PARAMS_URLENCODED) {
-               prepare_urlencoded(&zv TSRMLS_CC);
-       }
-
-       if (flags & PHP_HTTP_PARAMS_ESCAPED) {
-               if (flags & PHP_HTTP_PARAMS_RFC5988) {
-                       prepare_rfc5988(&zv TSRMLS_CC);
-               } else {
-                       prepare_escaped(&zv TSRMLS_CC);
-               }
-       }
-
-       *new_key = Z_STRVAL(zv);
-       *new_len = Z_STRLEN(zv);
-}
-
-static inline void prepare_value(unsigned flags, zval *zv TSRMLS_DC)
-{
-       if (flags & PHP_HTTP_PARAMS_URLENCODED) {
-               prepare_urlencoded(zv TSRMLS_CC);
-       }
-
-       if (flags & PHP_HTTP_PARAMS_ESCAPED) {
-               prepare_escaped(zv TSRMLS_CC);
-       }
-}
-
-static void merge_param(HashTable *params, zval *zdata, zval ***current_param, zval ***current_args TSRMLS_DC)
-{
-       zval **ptr, **zdata_ptr;
-       php_http_array_hashkey_t hkey = php_http_array_hashkey_init(0);
-
-#if 0
-       {
-               zval tmp;
-               INIT_PZVAL_ARRAY(&tmp, params);
-               fprintf(stderr, "params = ");
-               zend_print_zval_r(&tmp, 1 TSRMLS_CC);
-               fprintf(stderr, "\n");
-       }
-#endif
-
-       hkey.type = zend_hash_get_current_key_ex(Z_ARRVAL_P(zdata), &hkey.str, &hkey.len, &hkey.num, hkey.dup, NULL);
-
-       if ((hkey.type == HASH_KEY_IS_STRING && !zend_hash_exists(params, hkey.str, hkey.len))
-       ||      (hkey.type == HASH_KEY_IS_LONG && !zend_hash_index_exists(params, hkey.num))
-       ) {
-               zval *tmp, *arg, **args;
-
-               /* create the entry if it doesn't exist */
-               zend_hash_get_current_data(Z_ARRVAL_P(zdata), (void *) &ptr);
-               Z_ADDREF_PP(ptr);
-               MAKE_STD_ZVAL(tmp);
-               array_init(tmp);
-               add_assoc_zval_ex(tmp, ZEND_STRS("value"), *ptr);
-
-               MAKE_STD_ZVAL(arg);
-               array_init(arg);
-               zend_hash_update(Z_ARRVAL_P(tmp), "arguments", sizeof("arguments"), (void *) &arg, sizeof(zval *), (void *) &args);
-               *current_args = args;
-
-               if (hkey.type == HASH_KEY_IS_STRING) {
-                       zend_hash_update(params, hkey.str, hkey.len, (void *) &tmp, sizeof(zval *), (void *) &ptr);
-               } else {
-                       zend_hash_index_update(params, hkey.num, (void *) &tmp, sizeof(zval *), (void *) &ptr);
-               }
-       } else {
-               /* merge */
-               if (hkey.type == HASH_KEY_IS_STRING) {
-                       zend_hash_find(params, hkey.str, hkey.len, (void *) &ptr);
-               } else {
-                       zend_hash_index_find(params, hkey.num, (void *) &ptr);
-               }
-
-               zdata_ptr = &zdata;
-
-               if (Z_TYPE_PP(ptr) == IS_ARRAY
-               &&      SUCCESS == zend_hash_find(Z_ARRVAL_PP(ptr), "value", sizeof("value"), (void *) &ptr)
-               &&      SUCCESS == zend_hash_get_current_data(Z_ARRVAL_PP(zdata_ptr), (void *) &zdata_ptr)
-               ) {
-                       /*
-                        * params = [arr => [value => [0 => 1]]]
-                        *                            ^- ptr
-                        * zdata  = [arr => [0 => NULL]]
-                        *                  ^- zdata_ptr
-                        */
-                       zval **test_ptr;
-
-                       while (Z_TYPE_PP(zdata_ptr) == IS_ARRAY
-                       &&      SUCCESS == zend_hash_get_current_data(Z_ARRVAL_PP(zdata_ptr), (void *) &test_ptr)
-                       ) {
-                               if (Z_TYPE_PP(test_ptr) == IS_ARRAY) {
-
-                                       /* now find key in ptr */
-                                       if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(Z_ARRVAL_PP(zdata_ptr), &hkey.str, &hkey.len, &hkey.num, hkey.dup, NULL)) {
-                                               if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(ptr), hkey.str, hkey.len, (void *) &ptr)) {
-                                                       zdata_ptr = test_ptr;
-                                               } else {
-                                                       Z_ADDREF_PP(test_ptr);
-                                                       zend_hash_update(Z_ARRVAL_PP(ptr), hkey.str, hkey.len, (void *) test_ptr, sizeof(zval *), (void *) &ptr);
-                                                       break;
-                                               }
-                                       } else {
-                                               if (SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(ptr), hkey.num, (void *) &ptr)) {
-                                                       zdata_ptr = test_ptr;
-                                               } else if (hkey.num) {
-                                                       Z_ADDREF_PP(test_ptr);
-                                                       zend_hash_index_update(Z_ARRVAL_PP(ptr), hkey.num, (void *) test_ptr, sizeof(zval *), (void *) &ptr);
-                                                       break;
-                                               } else {
-                                                       Z_ADDREF_PP(test_ptr);
-                                                       zend_hash_next_index_insert(Z_ARRVAL_PP(ptr), (void *) test_ptr, sizeof(zval *), (void *) &ptr);
-                                                       break;
-                                               }
-                                       }
-                               } else {
-                                       /* this is the leaf */
-                                       Z_ADDREF_PP(test_ptr);
-                                       if (Z_TYPE_PP(ptr) != IS_ARRAY) {
-                                               zval_dtor(*ptr);
-                                               array_init(*ptr);
-                                       }
-                                       if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(Z_ARRVAL_PP(zdata_ptr), &hkey.str, &hkey.len, &hkey.num, hkey.dup, NULL)) {
-                                               zend_hash_update(Z_ARRVAL_PP(ptr), hkey.str, hkey.len, (void *) test_ptr, sizeof(zval *), (void *) &ptr);
-                                       } else if (hkey.num) {
-                                               zend_hash_index_update(Z_ARRVAL_PP(ptr), hkey.num, (void *) test_ptr, sizeof(zval *), (void *) &ptr);
-                                       } else {
-                                               zend_hash_next_index_insert(Z_ARRVAL_PP(ptr), (void *) test_ptr, sizeof(zval *), (void *) &ptr);
-                                       }
-                                       break;
-                               }
-                       }
-
-               }
-       }
-
-       /* bubble up */
-       while (Z_TYPE_PP(ptr) == IS_ARRAY && SUCCESS == zend_hash_get_current_data(Z_ARRVAL_PP(ptr), (void *) &ptr));
-       *current_param = ptr;
-}
-
-static void push_param(HashTable *params, php_http_params_state_t *state, const php_http_params_opts_t *opts TSRMLS_DC)
-{
-       if (state->val.str) {
-               if (0 < (state->val.len = state->input.str - state->val.str)) {
-                       sanitize_value(opts->flags, state->val.str, state->val.len, *(state->current.val), state->rfc5987 TSRMLS_CC);
-               }
-               state->rfc5987 = 0;
-       } else if (state->arg.str) {
-               if (0 < (state->arg.len = state->input.str - state->arg.str)) {
-                       zval *val, key;
-                       zend_bool rfc5987 = 0;
-
-                       INIT_PZVAL(&key);
-                       ZVAL_NULL(&key);
-                       sanitize_key(opts->flags, state->arg.str, state->arg.len, &key, &rfc5987 TSRMLS_CC);
-                       state->rfc5987 = rfc5987;
-                       if (Z_TYPE(key) == IS_STRING && Z_STRLEN(key)) {
-                               MAKE_STD_ZVAL(val);
-                               ZVAL_TRUE(val);
-
-                               if (rfc5987) {
-                                       zval **rfc;
-
-                                       if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(state->current.args), ZEND_STRS("*rfc5987*"), (void *) &rfc)) {
-                                               zend_symtable_update(Z_ARRVAL_PP(rfc), Z_STRVAL(key), Z_STRLEN(key) + 1, (void *) &val, sizeof(zval *), (void *) &state->current.val);
-                                       } else {
-                                               zval *tmp;
-
-                                               MAKE_STD_ZVAL(tmp);
-                                               array_init_size(tmp, 1);
-                                               zend_symtable_update(Z_ARRVAL_P(tmp), Z_STRVAL(key), Z_STRLEN(key) + 1, (void *) &val, sizeof(zval *), (void *) &state->current.val);
-                                               zend_symtable_update(Z_ARRVAL_PP(state->current.args), ZEND_STRS("*rfc5987*"), (void *) &tmp, sizeof(zval *), NULL);
-                                       }
-                               } else {
-                                       zend_symtable_update(Z_ARRVAL_PP(state->current.args), Z_STRVAL(key), Z_STRLEN(key) + 1, (void *) &val, sizeof(zval *), (void *) &state->current.val);
-                               }
-                       }
-                       zval_dtor(&key);
-               }
-       } else if (state->param.str) {
-               if (0 < (state->param.len = state->input.str - state->param.str)) {
-                       zval *prm, *arg, *val, *key;
-                       zend_bool rfc5987 = 0;
-
-                       MAKE_STD_ZVAL(key);
-                       ZVAL_NULL(key);
-                       if (opts->flags & PHP_HTTP_PARAMS_RFC5988) {
-                               sanitize_rfc5988(state->param.str, state->param.len, key TSRMLS_CC);
-                       } else {
-                               sanitize_key(opts->flags, state->param.str, state->param.len, key, &rfc5987 TSRMLS_CC);
-                               state->rfc5987 = rfc5987;
-                       }
-                       if (Z_TYPE_P(key) != IS_STRING) {
-                               merge_param(params, key, &state->current.val, &state->current.args TSRMLS_CC);
-                       } else if (Z_STRLEN_P(key)) {
-                               MAKE_STD_ZVAL(prm);
-                               array_init_size(prm, 2);
-
-                               MAKE_STD_ZVAL(val);
-                               if (opts->defval) {
-                                       ZVAL_COPY_VALUE(val, opts->defval);
-                                       zval_copy_ctor(val);
-                               } else {
-                                       ZVAL_TRUE(val);
-                               }
-                               if (rfc5987 && (opts->flags & PHP_HTTP_PARAMS_RFC5987)) {
-                                       zend_hash_update(Z_ARRVAL_P(prm), "*rfc5987*", sizeof("*rfc5987*"), (void *) &val, sizeof(zval *), (void *) &state->current.val);
-                               } else {
-                                       zend_hash_update(Z_ARRVAL_P(prm), "value", sizeof("value"), (void *) &val, sizeof(zval *), (void *) &state->current.val);
-                               }
-
-                               MAKE_STD_ZVAL(arg);
-                               array_init_size(arg, 3);
-                               zend_hash_update(Z_ARRVAL_P(prm), "arguments", sizeof("arguments"), (void *) &arg, sizeof(zval *), (void *) &state->current.args);
-
-                               zend_symtable_update(params, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, (void *) &prm, sizeof(zval *), (void *) &state->current.param);
-                       }
-                       zval_ptr_dtor(&key);
-               }
-       }
-}
-
-static inline zend_bool check_str(const char *chk_str, size_t chk_len, const char *sep_str, size_t sep_len) {
-       return 0 < sep_len && chk_len >= sep_len && *chk_str == *sep_str && !memcmp(chk_str + 1, sep_str + 1, sep_len - 1);
-}
-
-static size_t check_sep(php_http_params_state_t *state, php_http_params_token_t **separators)
-{
-       php_http_params_token_t **sep = separators;
-
-       if (state->quotes || state->escape) {
-               return 0;
-       }
-       
-       if (sep) while (*sep) {
-               if (check_str(state->input.str, state->input.len, (*sep)->str, (*sep)->len)) {
-                       return (*sep)->len;
-               }
-               ++sep;
-       }
-       return 0;
-}
-
-static void skip_sep(size_t skip, php_http_params_state_t *state, php_http_params_token_t **param, php_http_params_token_t **arg, php_http_params_token_t **val TSRMLS_DC)
-{
-       size_t sep_len;
-
-       state->input.str += skip;
-       state->input.len -= skip;
-
-       while ( (param && (sep_len = check_sep(state, param)))
-       ||              (arg && (sep_len = check_sep(state, arg)))
-       ||              (val && (sep_len = check_sep(state, val)))
-       ) {
-               state->input.str += sep_len;
-               state->input.len -= sep_len;
-       }
-}
-
-HashTable *php_http_params_parse(HashTable *params, const php_http_params_opts_t *opts TSRMLS_DC)
-{
-       php_http_params_state_t state = {{NULL,0}, {NULL,0}, {NULL,0}, {NULL,0}, {NULL,NULL,NULL}, 0, 0};
-
-       state.input.str = opts->input.str;
-       state.input.len = opts->input.len;
-
-       if (!params) {
-               ALLOC_HASHTABLE(params);
-               ZEND_INIT_SYMTABLE(params);
-       }
-
-       while (state.input.len) {
-               if ((opts->flags & PHP_HTTP_PARAMS_RFC5988) && !state.arg.str) {
-                       if (*state.input.str == '<') {
-                               state.quotes = 1;
-                       } else if (*state.input.str == '>') {
-                               state.quotes = 0;
-                       }
-               } else if (*state.input.str == '"' && !state.escape) {
-                       state.quotes = !state.quotes;
-               } else {
-                       state.escape = (*state.input.str == '\\');
-               }
-               
-               if (!state.param.str) {
-                       /* initialize */
-                       skip_sep(0, &state, opts->param, opts->arg, opts->val TSRMLS_CC);
-                       state.param.str = state.input.str;
-               } else {
-                       size_t sep_len;
-                       /* are we at a param separator? */
-                       if (0 < (sep_len = check_sep(&state, opts->param))) {
-                               push_param(params, &state, opts TSRMLS_CC);
-
-                               skip_sep(sep_len, &state, opts->param, opts->arg, opts->val TSRMLS_CC);
-
-                               /* start off with a new param */
-                               state.param.str = state.input.str;
-                               state.param.len = 0;
-                               state.arg.str = NULL;
-                               state.arg.len = 0;
-                               state.val.str = NULL;
-                               state.val.len = 0;
-
-                               continue;
-
-                       } else
-                       /* are we at an arg separator? */
-                       if (0 < (sep_len = check_sep(&state, opts->arg))) {
-                               push_param(params, &state, opts TSRMLS_CC);
-
-                               skip_sep(sep_len, &state, NULL, opts->arg, opts->val TSRMLS_CC);
-
-                               /* continue with a new arg */
-                               state.arg.str = state.input.str;
-                               state.arg.len = 0;
-                               state.val.str = NULL;
-                               state.val.len = 0;
-
-                               continue;
-
-                       } else
-                       /* are we at a val separator? */
-                       if (0 < (sep_len = check_sep(&state, opts->val))) {
-                               /* only handle separator if we're not already reading in a val */
-                               if (!state.val.str) {
-                                       push_param(params, &state, opts TSRMLS_CC);
-
-                                       skip_sep(sep_len, &state, NULL, NULL, opts->val TSRMLS_CC);
-
-                                       state.val.str = state.input.str;
-                                       state.val.len = 0;
-
-                                       continue;
-                               }
-                       }
-               }
-               
-               if (state.input.len) {
-                       ++state.input.str;
-                       --state.input.len;
-               }
-       }
-       /* finalize */
-       push_param(params, &state, opts TSRMLS_CC);
-
-       return params;
-}
-
-static inline void shift_key(php_http_buffer_t *buf, char *key_str, size_t key_len, const char *ass, size_t asl, unsigned flags TSRMLS_DC)
-{
-       char *str;
-       size_t len;
-
-       if (buf->used) {
-               php_http_buffer_append(buf, ass, asl);
-       }
-
-       prepare_key(flags, key_str, key_len, &str, &len TSRMLS_CC);
-       php_http_buffer_append(buf, str, len);
-       efree(str);
-}
-
-static inline void shift_rfc5987(php_http_buffer_t *buf, zval *zvalue, const char *vss, size_t vsl, unsigned flags TSRMLS_DC)
-{
-       HashTable *ht = HASH_OF(zvalue);
-       zval **zdata, *tmp;
-       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-
-       if (SUCCESS == zend_hash_get_current_data(ht, (void *) &zdata)
-       &&      HASH_KEY_NON_EXISTENT != (key.type = zend_hash_get_current_key_ex(ht, &key.str, &key.len, &key.num, key.dup, NULL))
-       ) {
-               php_http_array_hashkey_stringify(&key);
-               php_http_buffer_appendf(buf, "*%.*sutf-8'%.*s'",
-                               (int) (vsl > INT_MAX ? INT_MAX : vsl), vss,
-                               (int) (key.len > INT_MAX ? INT_MAX : key.len), key.str);
-               php_http_array_hashkey_stringfree(&key);
-
-               tmp = php_http_zsep(1, IS_STRING, *zdata);
-               prepare_value(flags | PHP_HTTP_PARAMS_URLENCODED, tmp TSRMLS_CC);
-               php_http_buffer_append(buf, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
-               zval_ptr_dtor(&tmp);
-       }
-}
-
-static inline void shift_rfc5988(php_http_buffer_t *buf, char *key_str, size_t key_len, const char *ass, size_t asl, unsigned flags TSRMLS_DC)
-{
-       char *str;
-       size_t len;
-
-       if (buf->used) {
-               php_http_buffer_append(buf, ass, asl);
-       }
-
-       prepare_key(flags, key_str, key_len, &str, &len TSRMLS_CC);
-       php_http_buffer_appends(buf, "<");
-       php_http_buffer_append(buf, str, len);
-       php_http_buffer_appends(buf, ">");
-       efree(str);
-}
-
-static inline void shift_rfc5988_val(php_http_buffer_t *buf, zval *zv, const char *vss, size_t vsl, unsigned flags TSRMLS_DC)
-{
-       zval *tmp = php_http_zsep(1, IS_STRING, zv);
-
-       quote_string(tmp, 1 TSRMLS_CC);
-       php_http_buffer_append(buf, vss, vsl);
-       php_http_buffer_append(buf, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
-
-       zval_ptr_dtor(&tmp);
-}
-
-static inline void shift_val(php_http_buffer_t *buf, zval *zvalue, const char *vss, size_t vsl, unsigned flags TSRMLS_DC)
-{
-       if (Z_TYPE_P(zvalue) != IS_BOOL) {
-               zval *tmp = php_http_zsep(1, IS_STRING, zvalue);
-
-               prepare_value(flags, tmp TSRMLS_CC);
-               php_http_buffer_append(buf, vss, vsl);
-               php_http_buffer_append(buf, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
-
-               zval_ptr_dtor(&tmp);
-       } else if (!Z_BVAL_P(zvalue)) {
-               php_http_buffer_append(buf, vss, vsl);
-               php_http_buffer_appends(buf, "0");
-       }
-}
-
-static void shift_arg(php_http_buffer_t *buf, char *key_str, size_t key_len, zval *zvalue, const char *ass, size_t asl, const char *vss, size_t vsl, unsigned flags TSRMLS_DC)
-{
-       if (Z_TYPE_P(zvalue) == IS_ARRAY || Z_TYPE_P(zvalue) == IS_OBJECT) {
-               HashPosition pos;
-               php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-               zval **val;
-               zend_bool rfc5987 = !strcmp(key_str, "*rfc5987*");
-
-               if (!rfc5987) {
-                       shift_key(buf, key_str, key_len, ass, asl, flags TSRMLS_CC);
-               }
-               FOREACH_KEYVAL(pos, zvalue, key, val) {
-                       /* did you mean recursion? */
-                       php_http_array_hashkey_stringify(&key);
-                       if (rfc5987 && (Z_TYPE_PP(val) == IS_ARRAY || Z_TYPE_PP(val) == IS_OBJECT)) {
-                               shift_key(buf, key.str, key.len-1, ass, asl, flags TSRMLS_CC);
-                               shift_rfc5987(buf, *val, vss, vsl, flags TSRMLS_CC);
-                       } else {
-                               shift_arg(buf, key.str, key.len-1, *val, ass, asl, vss, vsl, flags TSRMLS_CC);
-                       }
-                       php_http_array_hashkey_stringfree(&key);
-               }
-       } else {
-               shift_key(buf, key_str, key_len, ass, asl, flags TSRMLS_CC);
-
-               if (flags & PHP_HTTP_PARAMS_RFC5988) {
-                       switch (key_len) {
-                       case lenof("rel"):
-                       case lenof("title"):
-                       case lenof("anchor"):
-                               /* some args must be quoted */
-                               if (0 <= php_http_select_str(key_str, 3, "rel", "title", "anchor")) {
-                                       shift_rfc5988_val(buf, zvalue, vss, vsl, flags TSRMLS_CC);
-                                       return;
-                               }
-                               break;
-                       }
-               }
-
-               shift_val(buf, zvalue, vss, vsl, flags TSRMLS_CC);
-       }
-}
-
-static void shift_param(php_http_buffer_t *buf, char *key_str, size_t key_len, zval *zvalue, const char *pss, size_t psl, const char *ass, size_t asl, const char *vss, size_t vsl, unsigned flags, zend_bool rfc5987 TSRMLS_DC)
-{
-       if (Z_TYPE_P(zvalue) == IS_ARRAY || Z_TYPE_P(zvalue) == IS_OBJECT) {
-               /* treat as arguments, unless we care for dimensions or rfc5987 */
-               if (flags & PHP_HTTP_PARAMS_DIMENSION) {
-                       php_http_buffer_t *keybuf = php_http_buffer_from_string(key_str, key_len);
-                       prepare_dimension(buf, keybuf, zvalue, pss, psl, vss, vsl, flags TSRMLS_CC);
-                       php_http_buffer_free(&keybuf);
-               } else if (rfc5987) {
-                       shift_key(buf, key_str, key_len, pss, psl, flags TSRMLS_CC);
-                       shift_rfc5987(buf, zvalue, vss, vsl, flags TSRMLS_CC);
-               } else {
-                       shift_arg(buf, key_str, key_len, zvalue, ass, asl, vss, vsl, flags TSRMLS_CC);
-               }
-       } else {
-               if (flags & PHP_HTTP_PARAMS_RFC5988) {
-                       shift_rfc5988(buf, key_str, key_len, pss, psl, flags TSRMLS_CC);
-               } else {
-                       shift_key(buf, key_str, key_len, pss, psl, flags TSRMLS_CC);
-               }
-               shift_val(buf, zvalue, vss, vsl, flags TSRMLS_CC);
-       }
-}
-
-php_http_buffer_t *php_http_params_to_string(php_http_buffer_t *buf, HashTable *params, const char *pss, size_t psl, const char *ass, size_t asl, const char *vss, size_t vsl, unsigned flags TSRMLS_DC)
-{
-       zval **zparam;
-       HashPosition pos, pos1;
-       php_http_array_hashkey_t key = php_http_array_hashkey_init(0), key1 = php_http_array_hashkey_init(0);
-       zend_bool rfc5987 = 0;
-
-       if (!buf) {
-               buf = php_http_buffer_init(NULL);
-       }
-
-       FOREACH_HASH_KEYVAL(pos, params, key, zparam) {
-               zval **zvalue, **zargs;
-
-               if (Z_TYPE_PP(zparam) != IS_ARRAY) {
-                       zvalue = zparam;
-               } else {
-                       if (SUCCESS != zend_hash_find(Z_ARRVAL_PP(zparam), ZEND_STRS("value"), (void *) &zvalue)) {
-                               if (SUCCESS != zend_hash_find(Z_ARRVAL_PP(zparam), ZEND_STRS("*rfc5987*"), (void *) &zvalue)) {
-                                       zvalue = zparam;
-                               } else {
-                                       rfc5987 = 1;
-                               }
-                       }
-               }
-
-               php_http_array_hashkey_stringify(&key);
-               shift_param(buf, key.str, key.len - 1, *zvalue, pss, psl, ass, asl, vss, vsl, flags, rfc5987 TSRMLS_CC);
-               php_http_array_hashkey_stringfree(&key);
-
-               if (Z_TYPE_PP(zparam) == IS_ARRAY && SUCCESS != zend_hash_find(Z_ARRVAL_PP(zparam), ZEND_STRS("arguments"), (void *) &zvalue)) {
-                       if (zvalue == zparam) {
-                               continue;
-                       }
-                       zvalue = zparam;
-               }
-
-               if (Z_TYPE_PP(zvalue) == IS_ARRAY) {
-                       FOREACH_KEYVAL(pos1, *zvalue, key1, zargs) {
-                               if (zvalue == zparam && key1.type == HASH_KEY_IS_STRING && !strcmp(key1.str, "value")) {
-                                       continue;
-                               }
-
-                               php_http_array_hashkey_stringify(&key1);
-                               shift_arg(buf, key1.str, key1.len - 1, *zargs, ass, asl, vss, vsl, flags TSRMLS_CC);
-                               php_http_array_hashkey_stringfree(&key1);
-                       }
-               }
-       }
-
-       php_http_buffer_shrink(buf);
-       php_http_buffer_fix(buf);
-
-       return buf;
-}
-
-php_http_params_token_t **php_http_params_separator_init(zval *zv TSRMLS_DC)
-{
-       zval **sep;
-       HashPosition pos;
-       php_http_params_token_t **ret, **tmp;
-
-       if (!zv) {
-               return NULL;
-       }
-
-       zv = php_http_ztyp(IS_ARRAY, zv);
-       ret = ecalloc(zend_hash_num_elements(Z_ARRVAL_P(zv)) + 1, sizeof(*ret));
-
-       tmp = ret;
-       FOREACH_VAL(pos, zv, sep) {
-               zval *zt = php_http_ztyp(IS_STRING, *sep);
-
-               if (Z_STRLEN_P(zt)) {
-                       *tmp = emalloc(sizeof(**tmp));
-                       (*tmp)->str = estrndup(Z_STRVAL_P(zt), (*tmp)->len = Z_STRLEN_P(zt));
-                       ++tmp;
-               }
-               zval_ptr_dtor(&zt);
-       }
-       zval_ptr_dtor(&zv);
-
-       *tmp = NULL;
-       return ret;
-}
-
-void php_http_params_separator_free(php_http_params_token_t **separator)
-{
-       php_http_params_token_t **sep = separator;
-       if (sep) {
-               while (*sep) {
-                       PTR_FREE((*sep)->str);
-                       efree(*sep);
-                       ++sep;
-               }
-               efree(separator);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams___construct, 0, 0, 0)
-       ZEND_ARG_INFO(0, params)
-       ZEND_ARG_INFO(0, param_sep)
-       ZEND_ARG_INFO(0, arg_sep)
-       ZEND_ARG_INFO(0, val_sep)
-       ZEND_ARG_INFO(0, flags)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpParams, __construct)
-{
-       zval *zcopy, *zparams = NULL, *param_sep = NULL, *arg_sep = NULL, *val_sep = NULL;
-       long flags = PHP_HTTP_PARAMS_DEFAULT;
-       zend_error_handling zeh;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!/z/z/z/l", &zparams, &param_sep, &arg_sep, &val_sep, &flags), invalid_arg, return);
-
-       zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh TSRMLS_CC);
-       {
-               switch (ZEND_NUM_ARGS()) {
-                       case 5:
-                               zend_update_property_long(php_http_params_class_entry, getThis(), ZEND_STRL("flags"), flags TSRMLS_CC);
-                               /* no break */
-                       case 4:
-                               zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("val_sep"), val_sep TSRMLS_CC);
-                               /* no break */
-                       case 3:
-                               zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("arg_sep"), arg_sep TSRMLS_CC);
-                               /* no break */
-                       case 2:
-                               zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("param_sep"), param_sep TSRMLS_CC);
-                               /* no break */
-               }
-
-               if (zparams) {
-                       switch (Z_TYPE_P(zparams)) {
-                               case IS_OBJECT:
-                               case IS_ARRAY:
-                                       zcopy = php_http_zsep(1, IS_ARRAY, zparams);
-                                       zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), zcopy TSRMLS_CC);
-                                       zval_ptr_dtor(&zcopy);
-                                       break;
-                               default:
-                                       zcopy = php_http_ztyp(IS_STRING, zparams);
-                                       if (Z_STRLEN_P(zcopy)) {
-                                               php_http_params_opts_t opts = {
-                                                       {Z_STRVAL_P(zcopy), Z_STRLEN_P(zcopy)},
-                                                       php_http_params_separator_init(zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("param_sep"), 0 TSRMLS_CC) TSRMLS_CC),
-                                                       php_http_params_separator_init(zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("arg_sep"), 0 TSRMLS_CC) TSRMLS_CC),
-                                                       php_http_params_separator_init(zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("val_sep"), 0 TSRMLS_CC) TSRMLS_CC),
-                                                       NULL, flags
-                                               };
-
-                                               MAKE_STD_ZVAL(zparams);
-                                               array_init(zparams);
-                                               php_http_params_parse(Z_ARRVAL_P(zparams), &opts TSRMLS_CC);
-                                               zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), zparams TSRMLS_CC);
-                                               zval_ptr_dtor(&zparams);
-
-                                               php_http_params_separator_free(opts.param);
-                                               php_http_params_separator_free(opts.arg);
-                                               php_http_params_separator_free(opts.val);
-                                       }
-                                       zval_ptr_dtor(&zcopy);
-                                       break;
-                       }
-               } else {
-                       MAKE_STD_ZVAL(zparams);
-                       array_init(zparams);
-                       zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), zparams TSRMLS_CC);
-                       zval_ptr_dtor(&zparams);
-               }
-       }
-       zend_restore_error_handling(&zeh TSRMLS_CC);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_toArray, 0, 0, 0)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpParams, toArray)
-{
-       zval *zparams;
-
-       if (SUCCESS != zend_parse_parameters_none()) {
-               return;
-       }
-       zparams = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC);
-       RETURN_ZVAL(zparams, 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_toString, 0, 0, 0)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpParams, toString)
-{
-       zval **tmp, *zparams, *zpsep, *zasep, *zvsep, *zflags;
-       php_http_buffer_t buf;
-
-       zparams = php_http_zsep(1, IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC));
-       zflags = php_http_ztyp(IS_LONG, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("flags"), 0 TSRMLS_CC));
-
-       zpsep = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("param_sep"), 0 TSRMLS_CC);
-       if (Z_TYPE_P(zpsep) == IS_ARRAY && SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(zpsep), (void *) &tmp)) {
-               zpsep = php_http_ztyp(IS_STRING, *tmp);
-       } else {
-               zpsep = php_http_ztyp(IS_STRING, zpsep);
-       }
-       zasep = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("arg_sep"), 0 TSRMLS_CC);
-       if (Z_TYPE_P(zasep) == IS_ARRAY && SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(zasep), (void *) &tmp)) {
-               zasep = php_http_ztyp(IS_STRING, *tmp);
-       } else {
-               zasep = php_http_ztyp(IS_STRING, zasep);
-       }
-       zvsep = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("val_sep"), 0 TSRMLS_CC);
-       if (Z_TYPE_P(zvsep) == IS_ARRAY && SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(zvsep), (void *) &tmp)) {
-               zvsep = php_http_ztyp(IS_STRING, *tmp);
-       } else {
-               zvsep = php_http_ztyp(IS_STRING, zvsep);
-       }
-
-       php_http_buffer_init(&buf);
-       php_http_params_to_string(&buf, Z_ARRVAL_P(zparams), Z_STRVAL_P(zpsep), Z_STRLEN_P(zpsep), Z_STRVAL_P(zasep), Z_STRLEN_P(zasep), Z_STRVAL_P(zvsep), Z_STRLEN_P(zvsep), Z_LVAL_P(zflags) TSRMLS_CC);
-
-       zval_ptr_dtor(&zparams);
-       zval_ptr_dtor(&zpsep);
-       zval_ptr_dtor(&zasep);
-       zval_ptr_dtor(&zvsep);
-       zval_ptr_dtor(&zflags);
-
-       RETVAL_PHP_HTTP_BUFFER_VAL(&buf);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_offsetExists, 0, 0, 1)
-       ZEND_ARG_INFO(0, name)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpParams, offsetExists)
-{
-       char *name_str;
-       int name_len;
-       zval **zparam, *zparams;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) {
-               return;
-       }
-
-       zparams = php_http_ztyp(IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC));
-
-       if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(zparams), name_str, name_len + 1, (void *) &zparam)) {
-               RETVAL_BOOL(Z_TYPE_PP(zparam) != IS_NULL);
-       } else {
-               RETVAL_FALSE;
-       }
-       zval_ptr_dtor(&zparams);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_offsetGet, 0, 0, 1)
-       ZEND_ARG_INFO(0, name)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpParams, offsetGet)
-{
-       char *name_str;
-       int name_len;
-       zval **zparam, *zparams;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) {
-               return;
-       }
-
-       zparams = php_http_ztyp(IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC));
-
-       if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(zparams), name_str, name_len + 1, (void *) &zparam)) {
-               RETVAL_ZVAL(*zparam, 1, 0);
-       }
-
-       zval_ptr_dtor(&zparams);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_offsetUnset, 0, 0, 1)
-       ZEND_ARG_INFO(0, name)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpParams, offsetUnset)
-{
-       char *name_str;
-       int name_len;
-       zval *zparams;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) {
-               return;
-       }
-
-       zparams = php_http_zsep(1, IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC));
-
-       zend_symtable_del(Z_ARRVAL_P(zparams), name_str, name_len + 1);
-       zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), zparams TSRMLS_CC);
-
-       zval_ptr_dtor(&zparams);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_offsetSet, 0, 0, 2)
-       ZEND_ARG_INFO(0, name)
-       ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpParams, offsetSet)
-{
-       zval *nvalue;
-       char *name_str;
-       int name_len;
-       zval **zparam, *zparams;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &name_str, &name_len, &nvalue)) {
-               return;
-       }
-
-       zparams = php_http_zsep(1, IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC));
-
-       if (name_len) {
-               if (Z_TYPE_P(nvalue) == IS_ARRAY) {
-                       zval *new_zparam;
-
-                       if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(zparams), name_str, name_len + 1, (void *) &zparam)) {
-                               new_zparam = php_http_zsep(1, IS_ARRAY, *zparam);
-                               array_join(Z_ARRVAL_P(nvalue), Z_ARRVAL_P(new_zparam), 0, 0);
-                       } else {
-                               new_zparam = nvalue;
-                               Z_ADDREF_P(new_zparam);
-                       }
-                       add_assoc_zval_ex(zparams, name_str, name_len + 1, new_zparam);
-               } else {
-                       zval *tmp;
-
-                       if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(zparams), name_str, name_len + 1, (void *) &zparam)) {
-                               tmp = php_http_zsep(1, IS_ARRAY, *zparam);
-                       } else {
-                               MAKE_STD_ZVAL(tmp);
-                               array_init(tmp);
-                       }
-
-                       Z_ADDREF_P(nvalue);
-                       add_assoc_zval_ex(tmp, ZEND_STRS("value"), nvalue);
-                       add_assoc_zval_ex(zparams, name_str, name_len + 1, tmp);
-               }
-       } else {
-               zval *tmp = php_http_ztyp(IS_STRING, nvalue), *arr;
-
-               MAKE_STD_ZVAL(arr);
-               array_init(arr);
-               add_assoc_bool_ex(arr, ZEND_STRS("value"), 1);
-               add_assoc_zval_ex(zparams, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp) + 1, arr);
-               zval_ptr_dtor(&tmp);
-       }
-
-       zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), zparams TSRMLS_CC);
-       zval_ptr_dtor(&zparams);
-}
-
-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, toArray,       ai_HttpParams_toArray,       ZEND_ACC_PUBLIC)
-       PHP_ME(HttpParams, toString,      ai_HttpParams_toString,      ZEND_ACC_PUBLIC)
-       ZEND_MALIAS(HttpParams, __toString, toString, ai_HttpParams_toString, ZEND_ACC_PUBLIC)
-
-       PHP_ME(HttpParams, offsetExists,  ai_HttpParams_offsetExists,  ZEND_ACC_PUBLIC)
-       PHP_ME(HttpParams, offsetUnset,   ai_HttpParams_offsetUnset,   ZEND_ACC_PUBLIC)
-       PHP_ME(HttpParams, offsetSet,     ai_HttpParams_offsetSet,     ZEND_ACC_PUBLIC)
-       PHP_ME(HttpParams, offsetGet,     ai_HttpParams_offsetGet,     ZEND_ACC_PUBLIC)
-
-       EMPTY_FUNCTION_ENTRY
-};
-
-zend_class_entry *php_http_params_class_entry;
-
-PHP_MINIT_FUNCTION(http_params)
-{
-       zend_class_entry ce = {0};
-
-       INIT_NS_CLASS_ENTRY(ce, "http", "Params", php_http_params_methods);
-       php_http_params_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
-       php_http_params_class_entry->create_object = php_http_params_object_new;
-       zend_class_implements(php_http_params_class_entry TSRMLS_CC, 1, zend_ce_arrayaccess);
-
-       zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("DEF_PARAM_SEP"), ZEND_STRL(",") TSRMLS_CC);
-       zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("DEF_ARG_SEP"), ZEND_STRL(";") TSRMLS_CC);
-       zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("DEF_VAL_SEP"), ZEND_STRL("=") TSRMLS_CC);
-       zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("COOKIE_PARAM_SEP"), ZEND_STRL("") TSRMLS_CC);
-
-       zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_RAW"), PHP_HTTP_PARAMS_RAW TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_ESCAPED"), PHP_HTTP_PARAMS_ESCAPED TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_URLENCODED"), PHP_HTTP_PARAMS_URLENCODED TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_DIMENSION"), PHP_HTTP_PARAMS_DIMENSION TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_RFC5987"), PHP_HTTP_PARAMS_RFC5987 TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_RFC5988"), PHP_HTTP_PARAMS_RFC5988 TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_DEFAULT"), PHP_HTTP_PARAMS_DEFAULT TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_QUERY"), PHP_HTTP_PARAMS_QUERY TSRMLS_CC);
-
-       zend_declare_property_null(php_http_params_class_entry, ZEND_STRL("params"), ZEND_ACC_PUBLIC TSRMLS_CC);
-       zend_declare_property_stringl(php_http_params_class_entry, ZEND_STRL("param_sep"), ZEND_STRL(","), ZEND_ACC_PUBLIC TSRMLS_CC);
-       zend_declare_property_stringl(php_http_params_class_entry, ZEND_STRL("arg_sep"), ZEND_STRL(";"), ZEND_ACC_PUBLIC TSRMLS_CC);
-       zend_declare_property_stringl(php_http_params_class_entry, ZEND_STRL("val_sep"), ZEND_STRL("="), ZEND_ACC_PUBLIC TSRMLS_CC);
-       zend_declare_property_long(php_http_params_class_entry, ZEND_STRL("flags"), PHP_HTTP_PARAMS_DEFAULT, ZEND_ACC_PUBLIC TSRMLS_CC);
-
-       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/php_http_params.h b/php_http_params.h
deleted file mode 100644 (file)
index e1ebe27..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_PARAMS_H
-#define PHP_HTTP_PARAMS_H
-
-typedef struct php_http_params_token {
-       char *str;
-       size_t len;
-} php_http_params_token_t;
-
-#define PHP_HTTP_PARAMS_RAW                    0x00
-#define PHP_HTTP_PARAMS_ESCAPED                0x01
-#define PHP_HTTP_PARAMS_URLENCODED     0x04
-#define PHP_HTTP_PARAMS_DIMENSION      0x08
-#define PHP_HTTP_PARAMS_RFC5987                0x10
-#define PHP_HTTP_PARAMS_RFC5988                0x20
-#define PHP_HTTP_PARAMS_QUERY          (PHP_HTTP_PARAMS_URLENCODED|PHP_HTTP_PARAMS_DIMENSION)
-#define PHP_HTTP_PARAMS_DEFAULT                (PHP_HTTP_PARAMS_ESCAPED|PHP_HTTP_PARAMS_RFC5987)
-
-typedef struct php_http_params_opts {
-       php_http_params_token_t input;
-       php_http_params_token_t **param;
-       php_http_params_token_t **arg;
-       php_http_params_token_t **val;
-       zval *defval;
-       unsigned flags;
-} php_http_params_opts_t;
-
-PHP_HTTP_API php_http_params_opts_t *php_http_params_opts_default_get(php_http_params_opts_t *opts);
-PHP_HTTP_API HashTable *php_http_params_parse(HashTable *params, const php_http_params_opts_t *opts TSRMLS_DC);
-PHP_HTTP_API php_http_buffer_t *php_http_params_to_string(php_http_buffer_t *buf, HashTable *params, const char *pss, size_t psl, const char *ass, size_t asl, const char *vss, size_t vsl, unsigned flags TSRMLS_DC);
-
-PHP_HTTP_API php_http_params_token_t **php_http_params_separator_init(zval *zv TSRMLS_DC);
-PHP_HTTP_API void php_http_params_separator_free(php_http_params_token_t **separator);
-
-typedef php_http_object_t php_http_params_object_t;
-
-PHP_HTTP_API zend_class_entry *php_http_params_class_entry;
-
-PHP_MINIT_FUNCTION(http_params);
-
-#define php_http_params_object_new php_http_object_new
-#define php_http_params_object_new_ex php_http_object_new_ex
-
-#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/php_http_querystring.c b/php_http_querystring.c
deleted file mode 100644 (file)
index d72337f..0000000
+++ /dev/null
@@ -1,740 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-#include <php_variables.h>
-#include <ext/spl/spl_array.h>
-
-#ifdef PHP_HTTP_HAVE_ICONV
-#      ifndef HAVE_ICONV
-#              define HAVE_ICONV 1
-#      endif
-#      undef PHP_ATOM_INC
-#      include <ext/iconv/php_iconv.h>
-#endif
-
-#define QS_MERGE 1
-
-static inline void php_http_querystring_set(zval *instance, zval *params, int flags TSRMLS_DC)
-{
-       zval *qa;
-
-       if (flags & QS_MERGE) {
-               qa = php_http_zsep(1, IS_ARRAY, zend_read_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), 0 TSRMLS_CC));
-       } else {
-               MAKE_STD_ZVAL(qa);
-               array_init(qa);
-       }
-
-       php_http_querystring_update(qa, params, NULL TSRMLS_CC);
-       zend_update_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), qa TSRMLS_CC);
-       zval_ptr_dtor(&qa);
-}
-
-static inline void php_http_querystring_str(zval *instance, zval *return_value TSRMLS_DC)
-{
-       zval *qa = zend_read_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), 0 TSRMLS_CC);
-
-       if (Z_TYPE_P(qa) == IS_ARRAY) {
-               php_http_querystring_update(qa, NULL, return_value TSRMLS_CC);
-       } else {
-               RETURN_EMPTY_STRING();
-       }
-}
-
-static inline void php_http_querystring_get(zval *this_ptr, int type, char *name, uint name_len, zval *defval, zend_bool del, zval *return_value TSRMLS_DC)
-{
-       zval **arrval, *qarray = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC);
-
-       if ((Z_TYPE_P(qarray) == IS_ARRAY) && (SUCCESS == zend_symtable_find(Z_ARRVAL_P(qarray), name, name_len + 1, (void *) &arrval))) {
-               if (type) {
-                       zval *value = php_http_ztyp(type, *arrval);
-                       RETVAL_ZVAL(value, 1, 1);
-               } else {
-                       RETVAL_ZVAL(*arrval, 1, 0);
-               }
-
-               if (del) {
-                       zval *delarr;
-
-                       MAKE_STD_ZVAL(delarr);
-                       array_init(delarr);
-                       add_assoc_null_ex(delarr, name, name_len + 1);
-                       php_http_querystring_set(this_ptr, delarr, QS_MERGE TSRMLS_CC);
-                       zval_ptr_dtor(&delarr);
-               }
-       } else if(defval) {
-               RETURN_ZVAL(defval, 1, 0);
-       }
-}
-
-#ifdef PHP_HTTP_HAVE_ICONV
-ZEND_RESULT_CODE php_http_querystring_xlate(zval *dst, zval *src, const char *ie, const char *oe TSRMLS_DC)
-{
-       HashPosition pos;
-       zval **entry = NULL;
-       char *xlate_str = NULL, *xkey;
-       size_t xlate_len = 0, xlen;
-       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-       
-       FOREACH_KEYVAL(pos, src, key, entry) {
-               if (key.type == HASH_KEY_IS_STRING) {
-                       if (PHP_ICONV_ERR_SUCCESS != php_iconv_string(key.str, key.len-1, &xkey, &xlen, oe, ie)) {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to convert '%.*s' from '%s' to '%s'", key.len-1, key.str, ie, oe);
-                               return FAILURE;
-                       }
-               }
-               
-               if (Z_TYPE_PP(entry) == IS_STRING) {
-                       if (PHP_ICONV_ERR_SUCCESS != php_iconv_string(Z_STRVAL_PP(entry), Z_STRLEN_PP(entry), &xlate_str, &xlate_len, oe, ie)) {
-                               if (key.type == HASH_KEY_IS_STRING) {
-                                       efree(xkey);
-                               }
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to convert '%.*s' from '%s' to '%s'", Z_STRLEN_PP(entry), Z_STRVAL_PP(entry), ie, oe);
-                               return FAILURE;
-                       }
-                       if (key.type == HASH_KEY_IS_STRING) {
-                               add_assoc_stringl_ex(dst, xkey, xlen+1, xlate_str, xlate_len, 0);
-                       } else {
-                               add_index_stringl(dst, key.num, xlate_str, xlate_len, 0);
-                       }
-               } else if (Z_TYPE_PP(entry) == IS_ARRAY) {
-                       zval *subarray;
-                       
-                       MAKE_STD_ZVAL(subarray);
-                       array_init(subarray);
-                       if (key.type == HASH_KEY_IS_STRING) {
-                               add_assoc_zval_ex(dst, xkey, xlen+1, subarray);
-                       } else {
-                               add_index_zval(dst, key.num, subarray);
-                       }
-                       if (SUCCESS != php_http_querystring_xlate(subarray, *entry, ie, oe TSRMLS_CC)) {
-                               if (key.type == HASH_KEY_IS_STRING) {
-                                       efree(xkey);
-                               }
-                               return FAILURE;
-                       }
-               }
-               
-               if (key.type == HASH_KEY_IS_STRING) {
-                       efree(xkey);
-               }
-       }
-       return SUCCESS;
-}
-#endif /* HAVE_ICONV */
-
-ZEND_RESULT_CODE php_http_querystring_ctor(zval *instance, zval *params TSRMLS_DC)
-{
-       php_http_querystring_set(instance, params, 0 TSRMLS_CC);
-       return SUCCESS;
-}
-
-static int apply_querystring(void *pData TSRMLS_DC)
-{
-       zval **val = pData;
-
-       if (Z_TYPE_PP(val) == IS_ARRAY) {
-               zval **zvalue;
-
-               if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("value"), (void *) &zvalue)) {
-                       zval *tmp = *val;
-
-                       Z_ADDREF_PP(zvalue);
-                       *val = *zvalue;
-                       zval_dtor(tmp);
-                       Z_TYPE_P(tmp) = IS_NULL;
-                       zval_ptr_dtor(&tmp);
-               }
-       }
-
-       return ZEND_HASH_APPLY_KEEP;
-}
-
-ZEND_RESULT_CODE php_http_querystring_parse(HashTable *ht, const char *str, size_t len TSRMLS_DC)
-{
-       ZEND_RESULT_CODE rv = FAILURE;
-       php_http_params_opts_t opts;
-       php_http_params_token_t psep = { ZEND_STRL("&") }, *psepp[] = { &psep, NULL };
-       php_http_params_token_t vsep = { ZEND_STRL("=") }, *vsepp[] = { &vsep, NULL };
-       const char *asi_str = NULL;
-       size_t asi_len = 0;
-
-       opts.input.str = estrndup(str, len);
-       opts.input.len = len;
-       opts.param = psepp;
-       opts.arg = NULL;
-       opts.val = vsepp;
-       opts.flags = PHP_HTTP_PARAMS_QUERY;
-
-       if (SUCCESS == php_http_ini_entry(ZEND_STRL("arg_separator.input"), &asi_str, &asi_len, 0 TSRMLS_CC) && asi_len) {
-               zval *arr;
-
-               MAKE_STD_ZVAL(arr);
-               array_init_size(arr, asi_len);
-
-               do {
-                       add_next_index_stringl(arr, asi_str++, 1, 1);
-               } while (*asi_str);
-
-               opts.param = php_http_params_separator_init(arr TSRMLS_CC);
-
-               zval_ptr_dtor(&arr);
-       }
-
-       MAKE_STD_ZVAL(opts.defval);
-       ZVAL_NULL(opts.defval);
-
-       if (php_http_params_parse(ht, &opts TSRMLS_CC)) {
-               zend_hash_apply(ht, apply_querystring TSRMLS_CC);
-               rv = SUCCESS;
-       }
-
-       if (asi_len) {
-               php_http_params_separator_free(opts.param);
-       }
-
-       zval_ptr_dtor(&opts.defval);
-       efree(opts.input.str);
-       return rv;
-}
-
-ZEND_RESULT_CODE php_http_querystring_update(zval *qarray, zval *params, zval *outstring TSRMLS_DC)
-{
-       /* enforce proper type */
-       if (Z_TYPE_P(qarray) != IS_ARRAY) {
-               convert_to_array(qarray);
-       }
-
-       /* modify qarray */
-       if (params) {
-               HashPosition pos;
-               HashTable *ptr;
-               php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-               zval **params_entry,  **qarray_entry;
-               zval zv, *zv_ptr = NULL;
-
-               INIT_PZVAL(&zv);
-               ZVAL_NULL(&zv);
-
-               /* squeeze the hash out of the zval */
-               if (Z_TYPE_P(params) == IS_OBJECT && instanceof_function(Z_OBJCE_P(params), php_http_querystring_class_entry TSRMLS_CC)) {
-                       zv_ptr = php_http_ztyp(IS_ARRAY, zend_read_property(php_http_querystring_class_entry, params, ZEND_STRL("queryArray"), 0 TSRMLS_CC));
-                       ptr = Z_ARRVAL_P(zv_ptr);
-               } else if (Z_TYPE_P(params) == IS_OBJECT || Z_TYPE_P(params) == IS_ARRAY) {
-                       ptr = HASH_OF(params);
-               } else {
-                       zv_ptr = php_http_ztyp(IS_STRING, params);
-                       array_init(&zv);
-                       php_http_querystring_parse(Z_ARRVAL(zv), Z_STRVAL_P(zv_ptr), Z_STRLEN_P(zv_ptr) TSRMLS_CC);
-                       zval_ptr_dtor(&zv_ptr);
-                       zv_ptr = NULL;
-                       ptr = Z_ARRVAL(zv);
-               }
-
-               FOREACH_HASH_KEYVAL(pos, ptr, key, params_entry) {
-                       /* only public properties */
-                       if (key.type != HASH_KEY_IS_STRING || *key.str) {
-                               if (Z_TYPE_PP(params_entry) == IS_NULL) {
-                                       /*
-                                        * delete
-                                        */
-                                       if (key.type == HASH_KEY_IS_STRING) {
-                                               zend_hash_del(Z_ARRVAL_P(qarray), key.str, key.len);
-                                       } else {
-                                               zend_hash_index_del(Z_ARRVAL_P(qarray), key.num);
-                                       }
-                               } else if (     ((key.type == HASH_KEY_IS_STRING) && (SUCCESS == zend_hash_find(Z_ARRVAL_P(qarray), key.str, key.len, (void *) &qarray_entry)))
-                                               ||      ((key.type == HASH_KEY_IS_LONG) && (SUCCESS == zend_hash_index_find(Z_ARRVAL_P(qarray), key.num, (void *) &qarray_entry)))) {
-                                       /*
-                                        * update
-                                        */
-                                       zval equal, *entry = NULL;
-
-                                       /* recursive */
-                                       if (Z_TYPE_PP(params_entry) == IS_ARRAY || Z_TYPE_PP(params_entry) == IS_OBJECT) {
-                                               entry = php_http_zsep(1, IS_ARRAY, *qarray_entry);
-                                               php_http_querystring_update(entry, *params_entry, NULL TSRMLS_CC);
-                                       } else if ((FAILURE == is_equal_function(&equal, *qarray_entry, *params_entry TSRMLS_CC)) || !Z_BVAL(equal)) {
-                                               Z_ADDREF_PP(params_entry);
-                                               entry = *params_entry;
-                                       }
-
-                                       if (entry) {
-                                               if (key.type == HASH_KEY_IS_STRING) {
-                                                       zend_hash_update(Z_ARRVAL_P(qarray), key.str, key.len, (void *) &entry, sizeof(zval *), NULL);
-                                               } else {
-                                                       zend_hash_index_update(Z_ARRVAL_P(qarray), key.num, (void *) &entry, sizeof(zval *), NULL);
-                                               }
-                                       }
-                               } else {
-                                       zval *entry;
-                                       /*
-                                        * add
-                                        */
-                                       if (Z_TYPE_PP(params_entry) == IS_OBJECT) {
-                                               MAKE_STD_ZVAL(entry);
-                                               array_init(entry);
-                                               php_http_querystring_update(entry, *params_entry, NULL TSRMLS_CC);
-                                       } else {
-                                               Z_ADDREF_PP(params_entry);
-                                               entry = *params_entry;
-                                       }
-                                       if (key.type == HASH_KEY_IS_STRING) {
-                                               add_assoc_zval_ex(qarray, key.str, key.len, entry);
-                                       } else {
-                                               add_index_zval(qarray, key.num, entry);
-                                       }
-                               }
-                       }
-               }
-               /* clean up */
-               if (zv_ptr) {
-                       zval_ptr_dtor(&zv_ptr);
-               }
-               zval_dtor(&zv);
-       }
-
-       /* serialize to string */
-       if (outstring) {
-               char *s;
-               size_t l;
-
-               if (SUCCESS == php_http_url_encode_hash(Z_ARRVAL_P(qarray), NULL, 0, &s, &l TSRMLS_CC)) {
-                       zval_dtor(outstring);
-                       ZVAL_STRINGL(outstring, s, l, 0);
-               } else {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to encode query string");
-                       return FAILURE;
-               }
-       }
-
-       return SUCCESS;
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString___construct, 0, 0, 0)
-       ZEND_ARG_INFO(0, params)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpQueryString, __construct)
-{
-       zval *params = NULL;
-       zend_error_handling zeh;
-       
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z", &params), invalid_arg, return);
-
-       zend_replace_error_handling(EH_THROW, php_http_exception_bad_querystring_class_entry, &zeh TSRMLS_CC);
-       php_http_querystring_set(getThis(), params, 0 TSRMLS_CC);
-       zend_restore_error_handling(&zeh TSRMLS_CC);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_getGlobalInstance, 0, 0, 0)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpQueryString, getGlobalInstance)
-{
-       zval *instance;
-
-       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
-
-       instance = *zend_std_get_static_property(php_http_querystring_class_entry, ZEND_STRL("instance"), 0 PHP_HTTP_ZEND_LITERAL_CCN TSRMLS_CC);
-
-       if (Z_TYPE_P(instance) != IS_OBJECT) {
-               zval **_GET = NULL;
-
-               zend_is_auto_global("_GET", lenof("_GET") TSRMLS_CC);
-
-               if ((SUCCESS == zend_hash_find(&EG(symbol_table), "_GET", sizeof("_GET"), (void *) &_GET))
-               &&      (Z_TYPE_PP(_GET) == IS_ARRAY)
-               ) {
-                       MAKE_STD_ZVAL(instance);
-                       ZVAL_OBJVAL(instance, php_http_querystring_object_new(php_http_querystring_class_entry TSRMLS_CC), 0);
-
-                       SEPARATE_ZVAL_TO_MAKE_IS_REF(_GET);
-                       convert_to_array(*_GET);
-                       zend_update_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), *_GET TSRMLS_CC);
-
-                       zend_update_static_property(php_http_querystring_class_entry, ZEND_STRL("instance"), instance TSRMLS_CC);
-                       zval_ptr_dtor(&instance);
-               } else {
-                       php_http_throw(unexpected_val, "Could not acquire reference to superglobal GET array", NULL);
-               }
-       }
-
-       RETVAL_ZVAL(instance, 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_getIterator, 0, 0, 0)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpQueryString, getIterator)
-{
-       zval *retval = NULL, *qa;
-
-       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
-
-       qa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC);
-
-       object_init_ex(return_value, spl_ce_RecursiveArrayIterator);
-       zend_call_method_with_1_params(&return_value, spl_ce_RecursiveArrayIterator, NULL, "__construct", &retval, qa);
-       if (retval) {
-               zval_ptr_dtor(&retval);
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_toString, 0, 0, 0)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpQueryString, toString)
-{
-       if (SUCCESS != zend_parse_parameters_none()) {
-               return;
-       }
-       php_http_querystring_str(getThis(), return_value TSRMLS_CC);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_toArray, 0, 0, 0)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpQueryString, toArray)
-{
-       zval *zqa;
-
-       if (SUCCESS != zend_parse_parameters_none()) {
-               return;
-       }
-
-       zqa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC);
-       RETURN_ZVAL(zqa, 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_get, 0, 0, 0)
-       ZEND_ARG_INFO(0, name)
-       ZEND_ARG_INFO(0, type)
-       ZEND_ARG_INFO(0, defval)
-       ZEND_ARG_INFO(0, delete)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpQueryString, get)
-{
-       char *name_str = NULL;
-       int name_len = 0;
-       long type = 0;
-       zend_bool del = 0;
-       zval *ztype = NULL, *defval = NULL;
-       
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|szzb", &name_str, &name_len, &ztype, &defval, &del)) {
-               if (name_str && name_len) {
-                       if (ztype) {
-                               if (Z_TYPE_P(ztype) == IS_LONG) {
-                                       type = Z_LVAL_P(ztype);
-                               } else if(Z_TYPE_P(ztype) == IS_STRING) {
-                                       switch (Z_STRVAL_P(ztype)[0]) {
-                                               case 'B': 
-                                               case 'b':       type = PHP_HTTP_QUERYSTRING_TYPE_BOOL;          break;
-                                               case 'L':
-                                               case 'l':
-                                               case 'I':
-                                               case 'i':       type = PHP_HTTP_QUERYSTRING_TYPE_INT;           break;
-                                               case 'd':
-                                               case 'D':
-                                               case 'F':
-                                               case 'f':       type = PHP_HTTP_QUERYSTRING_TYPE_FLOAT;         break;  
-                                               case 'S':
-                                               case 's':       type = PHP_HTTP_QUERYSTRING_TYPE_STRING;        break;
-                                               case 'A':
-                                               case 'a':       type = PHP_HTTP_QUERYSTRING_TYPE_ARRAY;         break;
-                                               case 'O':
-                                               case 'o':       type = PHP_HTTP_QUERYSTRING_TYPE_OBJECT;        break;
-                                       }
-                               }
-                       }
-                       php_http_querystring_get(getThis(), type, name_str, name_len, defval, del, return_value TSRMLS_CC);
-               } else {
-                       php_http_querystring_str(getThis(), return_value TSRMLS_CC);
-               }
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_set, 0, 0, 1)
-       ZEND_ARG_INFO(0, params)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpQueryString, set)
-{
-       zval *params;
-       
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &params)) {
-               return;
-       }
-       
-       php_http_querystring_set(getThis(), params, QS_MERGE TSRMLS_CC);
-       RETVAL_ZVAL(getThis(), 1, 0);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_mod, 0, 0, 0)
-       ZEND_ARG_INFO(0, params)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpQueryString, mod)
-{
-       zval *params;
-       zend_error_handling zeh;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &params), invalid_arg, return);
-       
-       zend_replace_error_handling(EH_THROW, php_http_exception_bad_querystring_class_entry, &zeh TSRMLS_CC);
-       ZVAL_OBJVAL(return_value, Z_OBJ_HT_P(getThis())->clone_obj(getThis() TSRMLS_CC), 0);
-       php_http_querystring_set(return_value, params, QS_MERGE TSRMLS_CC);
-       zend_restore_error_handling(&zeh TSRMLS_CC);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString___getter, 0, 0, 1)
-       ZEND_ARG_INFO(0, name)
-       ZEND_ARG_INFO(0, defval)
-       ZEND_ARG_INFO(0, delete)
-ZEND_END_ARG_INFO();
-#define PHP_HTTP_QUERYSTRING_GETTER(method, TYPE) \
-PHP_METHOD(HttpQueryString, method) \
-{ \
-       char *name; \
-       int name_len; \
-       zval *defval = NULL; \
-       zend_bool del = 0; \
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|zb", &name, &name_len, &defval, &del)) { \
-               php_http_querystring_get(getThis(), TYPE, name, name_len, defval, del, return_value TSRMLS_CC); \
-       } \
-}
-PHP_HTTP_QUERYSTRING_GETTER(getBool, IS_BOOL);
-PHP_HTTP_QUERYSTRING_GETTER(getInt, IS_LONG);
-PHP_HTTP_QUERYSTRING_GETTER(getFloat, IS_DOUBLE);
-PHP_HTTP_QUERYSTRING_GETTER(getString, IS_STRING);
-PHP_HTTP_QUERYSTRING_GETTER(getArray, IS_ARRAY);
-PHP_HTTP_QUERYSTRING_GETTER(getObject, IS_OBJECT);
-
-#ifdef PHP_HTTP_HAVE_ICONV
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_xlate, 0, 0, 2)
-       ZEND_ARG_INFO(0, from_encoding)
-       ZEND_ARG_INFO(0, to_encoding)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpQueryString, xlate)
-{
-       char *ie, *oe;
-       int ie_len, oe_len;
-       zval *na, *qa;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &ie, &ie_len, &oe, &oe_len), invalid_arg, return);
-
-       MAKE_STD_ZVAL(na);
-       array_init(na);
-       qa = php_http_ztyp(IS_ARRAY, zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC));
-
-       php_http_expect(SUCCESS == php_http_querystring_xlate(na, qa, ie, oe TSRMLS_CC), bad_conversion,
-                       zval_ptr_dtor(&na);
-                       zval_ptr_dtor(&qa);
-                       return;
-       );
-
-       php_http_querystring_set(getThis(), na, 0 TSRMLS_CC);
-       RETVAL_ZVAL(getThis(), 1, 0);
-       
-       zval_ptr_dtor(&na);
-       zval_ptr_dtor(&qa);
-}
-#endif /* HAVE_ICONV */
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_serialize, 0, 0, 0)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpQueryString, serialize)
-{
-       if (SUCCESS != zend_parse_parameters_none()) {
-               return;
-       }
-       php_http_querystring_str(getThis(), return_value TSRMLS_CC);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_unserialize, 0, 0, 1)
-       ZEND_ARG_INFO(0, serialized)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpQueryString, unserialize)
-{
-       zval *serialized;
-       
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &serialized)) {
-               return;
-       }
-
-       if (Z_TYPE_P(serialized) == IS_STRING) {
-               php_http_querystring_set(getThis(), serialized, 0 TSRMLS_CC);
-       } else {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected a string as parameter");
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_offsetGet, 0, 0, 1)
-       ZEND_ARG_INFO(0, offset)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpQueryString, offsetGet)
-{
-       char *offset_str;
-       int offset_len;
-       zval **value, *qa;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset_str, &offset_len)) {
-               return;
-       }
-       
-       qa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC);
-
-       if (Z_TYPE_P(qa) == IS_ARRAY) {
-               if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(qa), offset_str, offset_len + 1, (void *) &value)) {
-                       RETVAL_ZVAL(*value, 1, 0);
-               }
-       }
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_offsetSet, 0, 0, 2)
-       ZEND_ARG_INFO(0, offset)
-       ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpQueryString, offsetSet)
-{
-       char *offset_str;
-       int offset_len;
-       zval *value, *param;
-       
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &offset_str, &offset_len, &value)) {
-               return;
-       }
-
-       param = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC);
-
-       if (Z_TYPE_P(param) == IS_ARRAY && zend_symtable_exists(Z_ARRVAL_P(param), offset_str, offset_len + 1)) {
-               Z_ADDREF_P(value);
-               zend_symtable_update(Z_ARRVAL_P(param), offset_str, offset_len + 1, (void *) &value, sizeof(zval *), NULL);
-               Z_ADDREF_P(param);
-       } else {
-               MAKE_STD_ZVAL(param);
-               array_init(param);
-               Z_ADDREF_P(value);
-               add_assoc_zval_ex(param, offset_str, offset_len + 1, value);
-       }
-       php_http_querystring_set(getThis(), param, QS_MERGE TSRMLS_CC);
-       zval_ptr_dtor(&param);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_offsetExists, 0, 0, 1)
-       ZEND_ARG_INFO(0, offset)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpQueryString, offsetExists)
-{
-       char *offset_str;
-       int offset_len;
-       zval **value, *qa;
-
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset_str, &offset_len)) {
-               return;
-       }
-       
-       qa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC);
-
-       if (Z_TYPE_P(qa) == IS_ARRAY) {
-               if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(qa), offset_str, offset_len + 1, (void *) &value)) {
-                       RETURN_BOOL(Z_TYPE_PP(value) != IS_NULL);
-               }
-       }
-       RETURN_FALSE;
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_offsetUnset, 0, 0, 1)
-       ZEND_ARG_INFO(0, offset)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpQueryString, offsetUnset)
-{
-       char *offset_str;
-       int offset_len;
-       zval *param;
-       
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset_str, &offset_len)) {
-               return;
-       }
-
-       MAKE_STD_ZVAL(param);
-       array_init(param);
-       add_assoc_null_ex(param, offset_str, offset_len + 1);
-       php_http_querystring_set(getThis(), param, QS_MERGE TSRMLS_CC);
-       zval_ptr_dtor(&param);
-}
-
-zend_class_entry *php_http_querystring_class_entry;
-
-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, toArray, ai_HttpQueryString_toArray, ZEND_ACC_PUBLIC)
-       PHP_ME(HttpQueryString, toString, ai_HttpQueryString_toString, ZEND_ACC_PUBLIC)
-       ZEND_MALIAS(HttpQueryString, __toString, toString, ai_HttpQueryString_toString, ZEND_ACC_PUBLIC)
-
-       PHP_ME(HttpQueryString, get, ai_HttpQueryString_get, ZEND_ACC_PUBLIC)
-       PHP_ME(HttpQueryString, set, ai_HttpQueryString_set, ZEND_ACC_PUBLIC)
-       PHP_ME(HttpQueryString, mod, ai_HttpQueryString_mod, ZEND_ACC_PUBLIC)
-
-       PHP_ME(HttpQueryString, getBool, ai_HttpQueryString___getter, ZEND_ACC_PUBLIC)
-       PHP_ME(HttpQueryString, getInt, ai_HttpQueryString___getter, ZEND_ACC_PUBLIC)
-       PHP_ME(HttpQueryString, getFloat, ai_HttpQueryString___getter, ZEND_ACC_PUBLIC)
-       PHP_ME(HttpQueryString, getString, ai_HttpQueryString___getter, ZEND_ACC_PUBLIC)
-       PHP_ME(HttpQueryString, getArray, ai_HttpQueryString___getter, ZEND_ACC_PUBLIC)
-       PHP_ME(HttpQueryString, getObject, ai_HttpQueryString___getter, ZEND_ACC_PUBLIC)
-
-       PHP_ME(HttpQueryString, getIterator, ai_HttpQueryString_getIterator, ZEND_ACC_PUBLIC)
-
-       PHP_ME(HttpQueryString, getGlobalInstance, ai_HttpQueryString_getGlobalInstance, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-#ifdef PHP_HTTP_HAVE_ICONV
-       PHP_ME(HttpQueryString, xlate, ai_HttpQueryString_xlate, ZEND_ACC_PUBLIC)
-#endif
-
-       /* Implements Serializable */
-       PHP_ME(HttpQueryString, serialize, ai_HttpQueryString_serialize, ZEND_ACC_PUBLIC)
-       PHP_ME(HttpQueryString, unserialize, ai_HttpQueryString_unserialize, ZEND_ACC_PUBLIC)
-
-       /* Implements ArrayAccess */
-       PHP_ME(HttpQueryString, offsetGet, ai_HttpQueryString_offsetGet, ZEND_ACC_PUBLIC)
-       PHP_ME(HttpQueryString, offsetSet, ai_HttpQueryString_offsetSet, ZEND_ACC_PUBLIC)
-       PHP_ME(HttpQueryString, offsetExists, ai_HttpQueryString_offsetExists, ZEND_ACC_PUBLIC)
-       PHP_ME(HttpQueryString, offsetUnset, ai_HttpQueryString_offsetUnset, ZEND_ACC_PUBLIC)
-
-       EMPTY_FUNCTION_ENTRY
-};
-
-PHP_MINIT_FUNCTION(http_querystring)
-{
-       zend_class_entry ce = {0};
-
-       INIT_NS_CLASS_ENTRY(ce, "http", "QueryString", php_http_querystring_methods);
-       php_http_querystring_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
-       php_http_querystring_class_entry->create_object = php_http_querystring_object_new;
-       zend_class_implements(php_http_querystring_class_entry TSRMLS_CC, 3, zend_ce_serializable, zend_ce_arrayaccess, zend_ce_aggregate);
-
-       zend_declare_property_null(php_http_querystring_class_entry, ZEND_STRL("instance"), (ZEND_ACC_STATIC|ZEND_ACC_PRIVATE) TSRMLS_CC);
-       zend_declare_property_null(php_http_querystring_class_entry, ZEND_STRL("queryArray"), ZEND_ACC_PRIVATE TSRMLS_CC);
-
-       zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_BOOL"), PHP_HTTP_QUERYSTRING_TYPE_BOOL TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_INT"), PHP_HTTP_QUERYSTRING_TYPE_INT TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_FLOAT"), PHP_HTTP_QUERYSTRING_TYPE_FLOAT TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_STRING"), PHP_HTTP_QUERYSTRING_TYPE_STRING TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_ARRAY"), PHP_HTTP_QUERYSTRING_TYPE_ARRAY TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_OBJECT"), PHP_HTTP_QUERYSTRING_TYPE_OBJECT TSRMLS_CC);
-
-       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/php_http_querystring.h b/php_http_querystring.h
deleted file mode 100644 (file)
index 3391e91..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_QUERYSTRING_H
-#define PHP_HTTP_QUERYSTRING_H
-
-#ifdef PHP_HTTP_HAVE_ICONV
-PHP_HTTP_API ZEND_RESULT_CODE php_http_querystring_xlate(zval *dst, zval *src, const char *ie, const char *oe TSRMLS_DC);
-#endif /* PHP_HTTP_HAVE_ICONV */
-PHP_HTTP_API ZEND_RESULT_CODE php_http_querystring_update(zval *qarray, zval *params, zval *qstring TSRMLS_DC);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_querystring_ctor(zval *instance, zval *params TSRMLS_DC);
-
-typedef php_http_object_t php_http_querystring_object_t;
-
-#define PHP_HTTP_QUERYSTRING_TYPE_BOOL         IS_BOOL
-#define PHP_HTTP_QUERYSTRING_TYPE_INT          IS_LONG
-#define PHP_HTTP_QUERYSTRING_TYPE_FLOAT                IS_DOUBLE
-#define PHP_HTTP_QUERYSTRING_TYPE_STRING       IS_STRING
-#define PHP_HTTP_QUERYSTRING_TYPE_ARRAY                IS_ARRAY
-#define PHP_HTTP_QUERYSTRING_TYPE_OBJECT       IS_OBJECT
-
-PHP_HTTP_API zend_class_entry *php_http_querystring_class_entry;
-
-PHP_MINIT_FUNCTION(http_querystring);
-
-#define php_http_querystring_object_new php_http_object_new
-#define php_http_querystring_object_new_ex php_http_object_new_ex
-
-#endif /* PHP_HTTP_QUERYSTRING_H */
-
-/*
- * 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/php_http_response_codes.h b/php_http_response_codes.h
deleted file mode 100644 (file)
index 82157f9..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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-2015, Michael Wallner <mike@php.net>            |
-    +--------------------------------------------------------------------+
-*/
-
-#ifndef PHP_HTTP_RESPONSE_CODE
-#      define PHP_HTTP_RESPONSE_CODE(code, status)
-#endif
-
-PHP_HTTP_RESPONSE_CODE(100, "Continue")
-PHP_HTTP_RESPONSE_CODE(101, "Switching Protocols")
-PHP_HTTP_RESPONSE_CODE(102, "Processing")
-PHP_HTTP_RESPONSE_CODE(200, "OK")
-PHP_HTTP_RESPONSE_CODE(201, "Created")
-PHP_HTTP_RESPONSE_CODE(202, "Accepted")
-PHP_HTTP_RESPONSE_CODE(203, "Non-Authoritative Information")
-PHP_HTTP_RESPONSE_CODE(204, "No Content")
-PHP_HTTP_RESPONSE_CODE(205, "Reset Content")
-PHP_HTTP_RESPONSE_CODE(206, "Partial Content")
-PHP_HTTP_RESPONSE_CODE(207, "Multi-Status")
-PHP_HTTP_RESPONSE_CODE(208, "Already Reported")
-PHP_HTTP_RESPONSE_CODE(226, "IM Used")
-PHP_HTTP_RESPONSE_CODE(300, "Multiple Choices")
-PHP_HTTP_RESPONSE_CODE(301, "Moved Permanently")
-PHP_HTTP_RESPONSE_CODE(302, "Found")
-PHP_HTTP_RESPONSE_CODE(303, "See Other")
-PHP_HTTP_RESPONSE_CODE(304, "Not Modified")
-PHP_HTTP_RESPONSE_CODE(305, "Use Proxy")
-PHP_HTTP_RESPONSE_CODE(307, "Temporary Redirect")
-PHP_HTTP_RESPONSE_CODE(308, "Permanent Redirect")
-PHP_HTTP_RESPONSE_CODE(400, "Bad Request")
-PHP_HTTP_RESPONSE_CODE(401, "Unauthorized")
-PHP_HTTP_RESPONSE_CODE(402, "Payment Required")
-PHP_HTTP_RESPONSE_CODE(403, "Forbidden")
-PHP_HTTP_RESPONSE_CODE(404, "Not Found")
-PHP_HTTP_RESPONSE_CODE(405, "Method Not Allowed")
-PHP_HTTP_RESPONSE_CODE(406, "Not Acceptable")
-PHP_HTTP_RESPONSE_CODE(407, "Proxy Authentication Required")
-PHP_HTTP_RESPONSE_CODE(408, "Request Timeout")
-PHP_HTTP_RESPONSE_CODE(409, "Conflict")
-PHP_HTTP_RESPONSE_CODE(410, "Gone")
-PHP_HTTP_RESPONSE_CODE(411, "Length Required")
-PHP_HTTP_RESPONSE_CODE(412, "Precondition Failed")
-PHP_HTTP_RESPONSE_CODE(413, "Request Entity Too Large")
-PHP_HTTP_RESPONSE_CODE(414, "Request URI Too Long")
-PHP_HTTP_RESPONSE_CODE(415, "Unsupported Media Type")
-PHP_HTTP_RESPONSE_CODE(416, "Requested Range Not Satisfiable")
-PHP_HTTP_RESPONSE_CODE(417, "Expectation Failed")
-PHP_HTTP_RESPONSE_CODE(422, "Unprocessible Entity")
-PHP_HTTP_RESPONSE_CODE(423, "Locked")
-PHP_HTTP_RESPONSE_CODE(424, "Failed Dependency")
-PHP_HTTP_RESPONSE_CODE(426, "Upgrade Required")
-PHP_HTTP_RESPONSE_CODE(428, "Precondition Required")
-PHP_HTTP_RESPONSE_CODE(429, "Too Many Requests")
-PHP_HTTP_RESPONSE_CODE(431, "Request Header Fields Too Large")
-PHP_HTTP_RESPONSE_CODE(500, "Internal Server Error")
-PHP_HTTP_RESPONSE_CODE(501, "Not Implemented")
-PHP_HTTP_RESPONSE_CODE(502, "Bad Gateway")
-PHP_HTTP_RESPONSE_CODE(503, "Service Unavailable")
-PHP_HTTP_RESPONSE_CODE(504, "Gateway Timeout")
-PHP_HTTP_RESPONSE_CODE(505, "HTTP Version Not Supported")
-PHP_HTTP_RESPONSE_CODE(506, "Variant Also Negotiates")
-PHP_HTTP_RESPONSE_CODE(507, "Insufficient Storage")
-PHP_HTTP_RESPONSE_CODE(508, "Loop Detected")
-PHP_HTTP_RESPONSE_CODE(510, "Not Extended")
-PHP_HTTP_RESPONSE_CODE(511, "Network Authentication Required")
-
-
-/*
- * 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/php_http_url.c b/php_http_url.c
deleted file mode 100644 (file)
index 81b2d95..0000000
+++ /dev/null
@@ -1,1749 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_IDN2
-#      include <idn2.h>
-#elif PHP_HTTP_HAVE_IDN
-#      include <idna.h>
-#endif
-
-#ifdef PHP_HTTP_HAVE_WCHAR
-#      include <wchar.h>
-#      include <wctype.h>
-#endif
-
-#ifdef HAVE_ARPA_INET_H
-#      include <arpa/inet.h>
-#endif
-
-#include "php_http_utf8.h"
-
-static inline char *localhostname(void)
-{
-       char hostname[1024] = {0};
-       
-#ifdef PHP_WIN32
-       if (SUCCESS == gethostname(hostname, lenof(hostname))) {
-               return estrdup(hostname);
-       }
-#elif defined(HAVE_GETHOSTNAME)
-       if (SUCCESS == gethostname(hostname, lenof(hostname))) {
-#      if defined(HAVE_GETDOMAINNAME)
-               size_t hlen = strlen(hostname);
-               if (hlen <= lenof(hostname) - lenof("(none)")) {
-                       hostname[hlen++] = '.';
-                       if (SUCCESS == getdomainname(&hostname[hlen], lenof(hostname) - hlen)) {
-                               if (!strcmp(&hostname[hlen], "(none)")) {
-                                       hostname[hlen - 1] = '\0';
-                               }
-                               return estrdup(hostname);
-                       }
-               }
-#      endif
-               if (strcmp(hostname, "(none)")) {
-                       return estrdup(hostname);
-               }
-       }
-#endif
-       return estrndup("localhost", lenof("localhost"));
-}
-
-#define url(buf) ((php_http_url_t *) (buf).data)
-
-static php_http_url_t *php_http_url_from_env(TSRMLS_D)
-{
-       zval *https, *zhost, *zport;
-       long port;
-       php_http_buffer_t buf;
-
-       php_http_buffer_init_ex(&buf, MAX(PHP_HTTP_BUFFER_DEFAULT_SIZE, sizeof(php_http_url_t)<<2), PHP_HTTP_BUFFER_INIT_PREALLOC);
-       php_http_buffer_account(&buf, sizeof(php_http_url_t));
-       memset(buf.data, 0, buf.used);
-
-       /* scheme */
-       url(buf)->scheme = &buf.data[buf.used];
-       https = php_http_env_get_server_var(ZEND_STRL("HTTPS"), 1 TSRMLS_CC);
-       if (https && !strcasecmp(Z_STRVAL_P(https), "ON")) {
-               php_http_buffer_append(&buf, "https", sizeof("https"));
-       } else {
-               php_http_buffer_append(&buf, "http", sizeof("http"));
-       }
-
-       /* host */
-       url(buf)->host = &buf.data[buf.used];
-       if ((((zhost = php_http_env_get_server_var(ZEND_STRL("HTTP_HOST"), 1 TSRMLS_CC)) ||
-                       (zhost = php_http_env_get_server_var(ZEND_STRL("SERVER_NAME"), 1 TSRMLS_CC)) ||
-                       (zhost = php_http_env_get_server_var(ZEND_STRL("SERVER_ADDR"), 1 TSRMLS_CC)))) && Z_STRLEN_P(zhost)) {
-               size_t stop_at = strspn(Z_STRVAL_P(zhost), "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-.");
-
-               php_http_buffer_append(&buf, Z_STRVAL_P(zhost), stop_at);
-               php_http_buffer_append(&buf, "", 1);
-       } else {
-               char *host_str = localhostname();
-
-               php_http_buffer_append(&buf, host_str, strlen(host_str) + 1);
-               efree(host_str);
-       }
-
-       /* port */
-       zport = php_http_env_get_server_var(ZEND_STRL("SERVER_PORT"), 1 TSRMLS_CC);
-       if (zport && IS_LONG == is_numeric_string(Z_STRVAL_P(zport), Z_STRLEN_P(zport), &port, NULL, 0)) {
-               url(buf)->port = port;
-       }
-
-       /* path */
-       if (SG(request_info).request_uri && SG(request_info).request_uri[0]) {
-               const char *q = strchr(SG(request_info).request_uri, '?');
-
-               url(buf)->path = &buf.data[buf.used];
-
-               if (q) {
-                       php_http_buffer_append(&buf, SG(request_info).request_uri, q - SG(request_info).request_uri);
-                       php_http_buffer_append(&buf, "", 1);
-               } else {
-                       php_http_buffer_append(&buf, SG(request_info).request_uri, strlen(SG(request_info).request_uri) + 1);
-               }
-       }
-
-       /* query */
-       if (SG(request_info).query_string && SG(request_info).query_string[0]) {
-               url(buf)->query = &buf.data[buf.used];
-               php_http_buffer_append(&buf, SG(request_info).query_string, strlen(SG(request_info).query_string) + 1);
-       }
-
-       return url(buf);
-}
-
-#define url_isset(u,n) \
-       ((u)&&(u)->n)
-#define url_append(buf, append) do { \
-       char *_ptr = (buf)->data; \
-       php_http_url_t *_url = (php_http_url_t *) _ptr, _mem = *_url; \
-       append; \
-       /* relocate */ \
-       if (_ptr != (buf)->data) { \
-               ptrdiff_t diff = (buf)->data - _ptr; \
-               _url = (php_http_url_t *) (buf)->data; \
-               if (_mem.scheme)        _url->scheme += diff; \
-               if (_mem.user)          _url->user += diff; \
-               if (_mem.pass)          _url->pass += diff; \
-               if (_mem.host)          _url->host += diff; \
-               if (_mem.path)          _url->path += diff; \
-               if (_mem.query)         _url->query += diff; \
-               if (_mem.fragment)      _url->fragment += diff; \
-       } \
-} while (0)
-#define url_copy(n) do { \
-       if (url_isset(new_url, n)) { \
-               url(buf)->n = &buf.data[buf.used]; \
-               url_append(&buf, php_http_buffer_append(&buf, new_url->n, strlen(new_url->n) + 1)); \
-       } else if (url_isset(old_url, n)) { \
-               url(buf)->n = &buf.data[buf.used]; \
-               url_append(&buf, php_http_buffer_append(&buf, old_url->n, strlen(old_url->n) + 1)); \
-       } \
-} while (0)
-
-php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_url_t *new_url, unsigned flags TSRMLS_DC)
-{
-       php_http_url_t *tmp_url = NULL;
-       php_http_buffer_t buf;
-
-       php_http_buffer_init_ex(&buf, MAX(PHP_HTTP_BUFFER_DEFAULT_SIZE, sizeof(php_http_url_t)<<2), PHP_HTTP_BUFFER_INIT_PREALLOC);
-       php_http_buffer_account(&buf, sizeof(php_http_url_t));
-       memset(buf.data, 0, buf.used);
-
-       /* set from env if requested */
-       if (flags & PHP_HTTP_URL_FROM_ENV) {
-               php_http_url_t *env_url = php_http_url_from_env(TSRMLS_C);
-
-               old_url = tmp_url = php_http_url_mod(env_url, old_url, flags ^ PHP_HTTP_URL_FROM_ENV TSRMLS_CC);
-               php_http_url_free(&env_url);
-       }
-
-       url_copy(scheme);
-
-       if (!(flags & PHP_HTTP_URL_STRIP_USER)) {
-               url_copy(user);
-       }
-
-       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);
-       }
-
-       if (!(flags & PHP_HTTP_URL_STRIP_PATH)) {
-               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));
-                       }
-                       url_append(&buf, php_http_buffer_append(&buf, path, strlen(path) + 1));
-                       efree(path);
-               } else {
-                       const char *path = NULL;
-
-                       if (url_isset(new_url, path)) {
-                               path = new_url->path;
-                       } else if (url_isset(old_url, path)) {
-                               path = old_url->path;
-                       }
-
-                       if (path) {
-                               url(buf)->path = &buf.data[buf.used];
-
-                               url_append(&buf, php_http_buffer_append(&buf, path, strlen(path) + 1));
-                       }
-
-
-               }
-       }
-
-       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;
-                       
-                       INIT_PZVAL(&qstr);
-                       INIT_PZVAL(&qarr);
-                       array_init(&qarr);
-                       
-                       ZVAL_STRING(&qstr, old_url->query, 0);
-                       php_http_querystring_update(&qarr, &qstr, NULL TSRMLS_CC);
-                       ZVAL_STRING(&qstr, new_url->query, 0);
-                       php_http_querystring_update(&qarr, &qstr, NULL TSRMLS_CC);
-                       
-                       ZVAL_NULL(&qstr);
-                       php_http_querystring_update(&qarr, NULL, &qstr TSRMLS_CC);
-
-                       url(buf)->query = &buf.data[buf.used];
-                       url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL(qstr), Z_STRLEN(qstr) + 1));
-
-                       zval_dtor(&qstr);
-                       zval_dtor(&qarr);
-               } else {
-                       url_copy(query);
-               }
-       }
-
-       if (!(flags & PHP_HTTP_URL_STRIP_FRAGMENT)) {
-               url_copy(fragment);
-       }
-       
-       /* done with copy & combine & strip */
-
-       if (flags & PHP_HTTP_URL_FROM_ENV) {
-               /* free old_url we tainted above */
-               php_http_url_free(&tmp_url);
-       }
-
-       /* replace directory references if path is not a single slash */
-       if ((flags & PHP_HTTP_URL_SANITIZE_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':
-                                                       ptr[1] = '\0';
-                                                       break;
-
-                                               case '/':
-                                                       memmove(&ptr[1], &ptr[3], end - &ptr[3]);
-                                                       break;
-
-                                               case '.':
-                                                       if (ptr[3] == '/') {
-                                                               char *pos = &ptr[4];
-                                                               while (ptr != url(buf)->path) {
-                                                                       if (*--ptr == '/') {
-                                                                               break;
-                                                                       }
-                                                               }
-                                                               memmove(&ptr[1], pos, end - pos);
-                                                               break;
-                                                       } else if (!ptr[3]) {
-                                                               /* .. at the end */
-                                                               ptr[1] = '\0';
-                                                       }
-                                                       /* no break */
-
-                                               default:
-                                                       /* something else */
-                                                       ++ptr;
-                                                       break;
-                                       }
-                                       break;
-
-                               default:
-                                       ++ptr;
-                                       break;
-                       }
-               }
-       }
-       /* unset default ports */
-       if (url(buf)->port) {
-               if (    ((url(buf)->port == 80) && url(buf)->scheme && !strcmp(url(buf)->scheme, "http"))
-                       ||      ((url(buf)->port ==443) && url(buf)->scheme && !strcmp(url(buf)->scheme, "https"))
-               ) {
-                       url(buf)->port = 0;
-               }
-       }
-       
-       return url(buf);
-}
-
-char *php_http_url_to_string(const php_http_url_t *url, char **url_str, size_t *url_len, zend_bool persistent)
-{
-       php_http_buffer_t buf;
-
-       php_http_buffer_init_ex(&buf, PHP_HTTP_BUFFER_DEFAULT_SIZE, persistent ?
-                       PHP_HTTP_BUFFER_INIT_PERSISTENT : 0);
-
-       if (url->scheme && *url->scheme) {
-               php_http_buffer_appendl(&buf, url->scheme);
-               php_http_buffer_appends(&buf, "://");
-       } else if ((url->user && *url->user) || (url->host && *url->host)) {
-               php_http_buffer_appends(&buf, "//");
-       }
-
-       if (url->user && *url->user) {
-               php_http_buffer_appendl(&buf, url->user);
-               if (url->pass && *url->pass) {
-                       php_http_buffer_appends(&buf, ":");
-                       php_http_buffer_appendl(&buf, url->pass);
-               }
-               php_http_buffer_appends(&buf, "@");
-       }
-
-       if (url->host && *url->host) {
-               php_http_buffer_appendl(&buf, url->host);
-               if (url->port) {
-                       php_http_buffer_appendf(&buf, ":%hu", url->port);
-               }
-       }
-
-       if (url->path && *url->path) {
-               if (*url->path != '/') {
-                       php_http_buffer_appends(&buf, "/");
-               }
-               php_http_buffer_appendl(&buf, url->path);
-       } else if (buf.used) {
-               php_http_buffer_appends(&buf, "/");
-       }
-
-       if (url->query && *url->query) {
-               php_http_buffer_appends(&buf, "?");
-               php_http_buffer_appendl(&buf, url->query);
-       }
-
-       if (url->fragment && *url->fragment) {
-               php_http_buffer_appends(&buf, "#");
-               php_http_buffer_appendl(&buf, url->fragment);
-       }
-
-       php_http_buffer_shrink(&buf);
-       php_http_buffer_fix(&buf);
-
-       if (url_len) {
-               *url_len = buf.used;
-       }
-
-       if (url_str) {
-               *url_str = buf.data;
-       }
-
-       return buf.data;
-}
-
-char *php_http_url_authority_to_string(const php_http_url_t *url, char **url_str, size_t *url_len)
-{
-       php_http_buffer_t buf;
-
-       php_http_buffer_init(&buf);
-
-       if (url->user && *url->user) {
-               php_http_buffer_appendl(&buf, url->user);
-               if (url->pass && *url->pass) {
-                       php_http_buffer_appends(&buf, ":");
-                       php_http_buffer_appendl(&buf, url->pass);
-               }
-               php_http_buffer_appends(&buf, "@");
-       }
-
-       if (url->host && *url->host) {
-               php_http_buffer_appendl(&buf, url->host);
-               if (url->port) {
-                       php_http_buffer_appendf(&buf, ":%hu", url->port);
-               }
-       }
-
-       php_http_buffer_shrink(&buf);
-       php_http_buffer_fix(&buf);
-
-       if (url_len) {
-               *url_len = buf.used;
-       }
-
-       if (url_str) {
-               *url_str = buf.data;
-       }
-
-       return buf.data;
-}
-
-php_http_url_t *php_http_url_from_zval(zval *value, unsigned flags TSRMLS_DC)
-{
-       zval *zcpy;
-       php_http_url_t *purl;
-
-       switch (Z_TYPE_P(value)) {
-       case IS_ARRAY:
-       case IS_OBJECT:
-               purl = php_http_url_from_struct(HASH_OF(value));
-               break;
-
-       default:
-               zcpy = php_http_ztyp(IS_STRING, value);
-               purl = php_http_url_parse(Z_STRVAL_P(zcpy), Z_STRLEN_P(zcpy), flags TSRMLS_CC);
-               zval_ptr_dtor(&zcpy);
-       }
-
-       return purl;
-}
-
-php_http_url_t *php_http_url_from_struct(HashTable *ht)
-{
-       zval **e;
-       php_http_buffer_t buf;
-
-       php_http_buffer_init_ex(&buf, MAX(PHP_HTTP_BUFFER_DEFAULT_SIZE, sizeof(php_http_url_t)<<2), PHP_HTTP_BUFFER_INIT_PREALLOC);
-       php_http_buffer_account(&buf, sizeof(php_http_url_t));
-       memset(buf.data, 0, buf.used);
-
-       if (SUCCESS == zend_hash_find(ht, "scheme", sizeof("scheme"), (void *) &e)) {
-               zval *cpy = php_http_ztyp(IS_STRING, *e);
-               url(buf)->scheme = &buf.data[buf.used];
-               url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
-               zval_ptr_dtor(&cpy);
-       }
-       if (SUCCESS == zend_hash_find(ht, "user", sizeof("user"), (void *) &e)) {
-               zval *cpy = php_http_ztyp(IS_STRING, *e);
-               url(buf)->user = &buf.data[buf.used];
-               url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
-               zval_ptr_dtor(&cpy);
-       }
-       if (SUCCESS == zend_hash_find(ht, "pass", sizeof("pass"), (void *) &e)) {
-               zval *cpy = php_http_ztyp(IS_STRING, *e);
-               url(buf)->pass = &buf.data[buf.used];
-               url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
-               zval_ptr_dtor(&cpy);
-       }
-       if (SUCCESS == zend_hash_find(ht, "host", sizeof("host"), (void *) &e)) {
-               zval *cpy = php_http_ztyp(IS_STRING, *e);
-               url(buf)->host = &buf.data[buf.used];
-               url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
-               zval_ptr_dtor(&cpy);
-       }
-       if (SUCCESS == zend_hash_find(ht, "port", sizeof("port"), (void *) &e)) {
-               zval *cpy = php_http_ztyp(IS_LONG, *e);
-               url(buf)->port = (unsigned short) Z_LVAL_P(cpy);
-               zval_ptr_dtor(&cpy);
-       }
-       if (SUCCESS == zend_hash_find(ht, "path", sizeof("path"), (void *) &e)) {
-               zval *cpy = php_http_ztyp(IS_STRING, *e);
-               url(buf)->path = &buf.data[buf.used];
-               url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
-               zval_ptr_dtor(&cpy);
-       }
-       if (SUCCESS == zend_hash_find(ht, "query", sizeof("query"), (void *) &e)) {
-               zval *cpy = php_http_ztyp(IS_STRING, *e);
-               url(buf)->query = &buf.data[buf.used];
-               url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
-               zval_ptr_dtor(&cpy);
-       }
-       if (SUCCESS == zend_hash_find(ht, "fragment", sizeof("fragment"), (void *) &e)) {
-               zval *cpy = php_http_ztyp(IS_STRING, *e);
-               url(buf)->fragment = &buf.data[buf.used];
-               url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
-               zval_ptr_dtor(&cpy);
-       }
-
-       return url(buf);
-}
-
-HashTable *php_http_url_to_struct(const php_http_url_t *url, zval *strct TSRMLS_DC)
-{
-       zval arr;
-
-       if (strct) {
-               switch (Z_TYPE_P(strct)) {
-                       default:
-                               zval_dtor(strct);
-                               array_init(strct);
-                               /* no break */
-                       case IS_ARRAY:
-                       case IS_OBJECT:
-                               INIT_PZVAL_ARRAY((&arr), HASH_OF(strct));
-                               break;
-               }
-       } else {
-               INIT_PZVAL(&arr);
-               array_init(&arr);
-       }
-
-       if (url) {
-               if (url->scheme) {
-                       add_assoc_string(&arr, "scheme", url->scheme, 1);
-               }
-               if (url->user) {
-                       add_assoc_string(&arr, "user", url->user, 1);
-               }
-               if (url->pass) {
-                       add_assoc_string(&arr, "pass", url->pass, 1);
-               }
-               if (url->host) {
-                       add_assoc_string(&arr, "host", url->host, 1);
-               }
-               if (url->port) {
-                       add_assoc_long(&arr, "port", (long) url->port);
-               }
-               if (url->path) {
-                       add_assoc_string(&arr, "path", url->path, 1);
-               }
-               if (url->query) {
-                       add_assoc_string(&arr, "query", url->query, 1);
-               }
-               if (url->fragment) {
-                       add_assoc_string(&arr, "fragment", url->fragment, 1);
-               }
-       }
-
-       return Z_ARRVAL(arr);
-}
-
-ZEND_RESULT_CODE php_http_url_encode_hash(HashTable *hash, const char *pre_encoded_str, size_t pre_encoded_len, char **encoded_str, size_t *encoded_len TSRMLS_DC)
-{
-       const char *arg_sep_str = "&";
-       size_t arg_sep_len = 1;
-       php_http_buffer_t *qstr = php_http_buffer_new();
-
-       php_http_url_argsep(&arg_sep_str, &arg_sep_len TSRMLS_CC);
-
-       if (SUCCESS != php_http_url_encode_hash_ex(hash, qstr, arg_sep_str, arg_sep_len, "=", 1, pre_encoded_str, pre_encoded_len TSRMLS_CC)) {
-               php_http_buffer_free(&qstr);
-               return FAILURE;
-       }
-
-       php_http_buffer_data(qstr, encoded_str, encoded_len);
-       php_http_buffer_free(&qstr);
-
-       return SUCCESS;
-}
-
-ZEND_RESULT_CODE php_http_url_encode_hash_ex(HashTable *hash, php_http_buffer_t *qstr, const char *arg_sep_str, size_t arg_sep_len, const char *val_sep_str, size_t val_sep_len, const char *pre_encoded_str, size_t pre_encoded_len TSRMLS_DC)
-{
-       if (pre_encoded_len && pre_encoded_str) {
-               php_http_buffer_append(qstr, pre_encoded_str, pre_encoded_len);
-       }
-
-       if (!php_http_params_to_string(qstr, hash, arg_sep_str, arg_sep_len, "", 0, val_sep_str, val_sep_len, PHP_HTTP_PARAMS_QUERY TSRMLS_CC)) {
-               return FAILURE;
-       }
-
-       return SUCCESS;
-}
-
-struct parse_state {
-       php_http_url_t url;
-#ifdef ZTS
-       void ***ts;
-#endif
-       const char *ptr;
-       const char *end;
-       size_t maxlen;
-       off_t offset;
-       unsigned flags;
-       char buffer[1]; /* last member */
-};
-
-void php_http_url_free(php_http_url_t **url)
-{
-       if (*url) {
-               efree(*url);
-               *url = NULL;
-       }
-}
-
-php_http_url_t *php_http_url_copy(const php_http_url_t *url, zend_bool persistent)
-{
-       php_http_url_t *cpy;
-       const char *end = NULL, *url_ptr = (const char *) url;
-       char *cpy_ptr;
-
-       end = MAX(url->scheme, end);
-       end = MAX(url->pass, end);
-       end = MAX(url->user, end);
-       end = MAX(url->host, end);
-       end = MAX(url->path, end);
-       end = MAX(url->query, end);
-       end = MAX(url->fragment, end);
-
-       if (end) {
-               end += strlen(end) + 1;
-               cpy_ptr = pecalloc(1, end - url_ptr, persistent);
-               cpy = (php_http_url_t *) cpy_ptr;
-
-               memcpy(cpy_ptr + sizeof(*cpy), url_ptr + sizeof(*url), end - url_ptr - sizeof(*url));
-
-               cpy->scheme = url->scheme ? cpy_ptr + (url->scheme - url_ptr) : NULL;
-               cpy->pass = url->pass ? cpy_ptr + (url->pass - url_ptr) : NULL;
-               cpy->user = url->user ? cpy_ptr + (url->user - url_ptr) : NULL;
-               cpy->host = url->host ? cpy_ptr + (url->host - url_ptr) : NULL;
-               cpy->path = url->path ? cpy_ptr + (url->path - url_ptr) : NULL;
-               cpy->query = url->query ? cpy_ptr + (url->query - url_ptr) : NULL;
-               cpy->fragment = url->fragment ? cpy_ptr + (url->fragment - url_ptr) : NULL;
-       } else {
-               cpy = ecalloc(1, sizeof(*url));
-       }
-
-       cpy->port = url->port;
-
-       return cpy;
-}
-
-static 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);
-
-       if (!consumed || consumed == (size_t) -1) {
-               return 0;
-       }
-
-       if (wc) {
-               *wc = wchar;
-       }
-       return consumed;
-}
-
-#ifdef PHP_HTTP_HAVE_WCHAR
-static size_t parse_mb_loc(unsigned *wc, const char *ptr, const char *end)
-{
-       wchar_t wchar;
-       size_t consumed = 0;
-#if defined(HAVE_MBRTOWC)
-       mbstate_t ps;
-
-       memset(&ps, 0, sizeof(ps));
-       consumed = mbrtowc(&wchar, ptr, end - ptr, &ps);
-#elif defined(HAVE_MBTOWC)
-       consumed = mbtowc(&wchar, ptr, end - ptr);
-#endif
-
-       if (!consumed || consumed == (size_t) -1) {
-               return 0;
-       }
-
-       if (wc) {
-               *wc = wchar;
-       }
-       return consumed;
-}
-#endif
-
-typedef enum parse_mb_what {
-       PARSE_SCHEME,
-       PARSE_USERINFO,
-       PARSE_HOSTINFO,
-       PARSE_PATH,
-       PARSE_QUERY,
-       PARSE_FRAGMENT
-} parse_mb_what_t;
-
-static const char * const parse_what[] = {
-       "scheme",
-       "userinfo",
-       "hostinfo",
-       "path",
-       "query",
-       "fragment"
-};
-
-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 silent)
-{
-       unsigned wchar;
-       size_t consumed = 0;
-
-       if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
-               consumed = parse_mb_utf8(&wchar, ptr, end);
-       }
-#ifdef PHP_HTTP_HAVE_WCHAR
-       else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
-               consumed = parse_mb_loc(&wchar, ptr, end);
-       }
-#endif
-
-       while (consumed) {
-               if (!(state->flags & PHP_HTTP_URL_PARSE_TOPCT) || what == PARSE_HOSTINFO || what == PARSE_SCHEME) {
-                       if (what == PARSE_HOSTINFO && (state->flags & PHP_HTTP_URL_PARSE_TOIDN)) {
-                               /* idna */
-                       } else if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
-                               if (!isualnum(wchar)) {
-                                       break;
-                               }
-#ifdef PHP_HTTP_HAVE_WCHAR
-                       } else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
-                               if (!iswalnum(wchar)) {
-                                       break;
-                               }
-#endif
-                       }
-                       PHP_HTTP_DUFF(consumed, state->buffer[state->offset++] = *ptr++);
-               } 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;
-                       );
-               }
-
-               return consumed;
-       }
-
-       if (!silent) {
-               TSRMLS_FETCH_FROM_CTX(state->ts);
-               if (consumed) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING,
-                                       "Failed to parse %s; unexpected multibyte sequence 0x%x at pos %u in '%s'",
-                                       parse_what[what], wchar, (unsigned) (ptr - begin), begin);
-               } else {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING,
-                                       "Failed to parse %s; unexpected byte 0x%02x at pos %u in '%s'",
-                                       parse_what[what], (unsigned char) *ptr, (unsigned) (ptr - begin), begin);
-               }
-       }
-
-       return 0;
-}
-
-static ZEND_RESULT_CODE parse_userinfo(struct parse_state *state, const char *ptr)
-{
-       size_t mb;
-       const char *password = NULL, *end = state->ptr, *tmp = ptr;
-       TSRMLS_FETCH_FROM_CTX(state->ts);
-
-       state->url.user = &state->buffer[state->offset];
-
-       do {
-               switch (*ptr) {
-               case ':':
-                       if (password) {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
-                                               "Failed to parse password; duplicate ':' at pos %u in '%s'",
-                                               (unsigned) (ptr - tmp), tmp);
-                               return FAILURE;
-                       }
-                       password = ptr + 1;
-                       state->buffer[state->offset++] = 0;
-                       state->url.pass = &state->buffer[state->offset];
-                       break;
-
-               case '%':
-                       if (ptr[1] != '%' && (end - ptr <= 2 || !isxdigit(*(ptr+1)) || !isxdigit(*(ptr+2)))) {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
-                                               "Failed to parse userinfo; invalid percent encoding at pos %u in '%s'",
-                                               (unsigned) (ptr - tmp), tmp);
-                               return FAILURE;
-                       }
-                       state->buffer[state->offset++] = *ptr++;
-                       state->buffer[state->offset++] = *ptr++;
-                       state->buffer[state->offset++] = *ptr;
-                       break;
-
-               case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
-               case '+': case ',': case ';': case '=': /* sub-delims */
-               case '-': case '.': case '_': case '~': /* unreserved */
-               case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
-               case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
-               case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
-               case 'V': case 'W': case 'X': case 'Y': case 'Z':
-               case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
-               case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
-               case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
-               case 'v': case 'w': case 'x': case 'y': case 'z':
-               case '0': case '1': case '2': case '3': case '4': case '5': case '6':
-               case '7': case '8': case '9':
-                       /* allowed */
-                       state->buffer[state->offset++] = *ptr;
-                       break;
-
-               default:
-                       if (!(mb = parse_mb(state, PARSE_USERINFO, ptr, end, tmp, 0))) {
-                               return FAILURE;
-                       }
-                       ptr += mb - 1;
-               }
-       } while(++ptr != end);
-
-
-       state->buffer[state->offset++] = 0;
-
-       return SUCCESS;
-}
-
-#if defined(PHP_WIN32) || defined(HAVE_UIDNA_IDNTOASCII)
-typedef size_t (*parse_mb_func)(unsigned *wc, const char *ptr, const char *end);
-static ZEND_RESULT_CODE to_utf16(parse_mb_func fn, const char *u8, uint16_t **u16, size_t *len TSRMLS_DC)
-{
-       size_t offset = 0, u8_len = strlen(u8);
-
-       *u16 = ecalloc(4 * sizeof(uint16_t), u8_len + 1);
-       *len = 0;
-
-       while (offset < u8_len) {
-               unsigned wc;
-               uint16_t buf[2], *ptr = buf;
-               size_t consumed = fn(&wc, &u8[offset], &u8[u8_len]);
-
-               if (!consumed) {
-                       efree(*u16);
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse UTF-8 at pos %zu of '%s'", offset, u8);
-                       return FAILURE;
-               } else {
-                       offset += consumed;
-               }
-
-               switch (wctoutf16(buf, wc)) {
-               case 2:
-                       (*u16)[(*len)++] = *ptr++;
-                       /* no break */
-               case 1:
-                       (*u16)[(*len)++] = *ptr++;
-                       break;
-               case 0:
-               default:
-                       efree(*u16);
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to convert UTF-32 'U+%X' to UTF-16", wc);
-                       return FAILURE;
-               }
-       }
-
-       return SUCCESS;
-}
-#endif
-
-#ifndef MAXHOSTNAMELEN
-#      define MAXHOSTNAMELEN 256
-#endif
-
-#if PHP_HTTP_HAVE_IDN2
-static ZEND_RESULT_CODE parse_idn2(struct parse_state *state, size_t prev_len)
-{
-       char *idn = NULL;
-       int rv = -1;
-       TSRMLS_FETCH_FROM_CTX(state->ts);
-
-       if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
-               rv = idn2_lookup_u8((const unsigned char *) state->url.host, (unsigned char **) &idn, IDN2_NFC_INPUT);
-       }
-#      ifdef PHP_HTTP_HAVE_WCHAR
-       else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
-               rv = idn2_lookup_ul(state->url.host, &idn, 0);
-       }
-#      endif
-       if (rv != IDN2_OK) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN; %s", idn2_strerror(rv));
-               return FAILURE;
-       } else {
-               size_t idnlen = strlen(idn);
-               memcpy(state->url.host, idn, idnlen + 1);
-               free(idn);
-               state->offset += idnlen - prev_len;
-               return SUCCESS;
-       }
-}
-#elif PHP_HTTP_HAVE_IDN
-static ZEND_RESULT_CODE parse_idn(struct parse_state *state, size_t prev_len)
-{
-       char *idn = NULL;
-       int rv = -1;
-       TSRMLS_FETCH_FROM_CTX(state->ts);
-
-       if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
-               rv = idna_to_ascii_8z(state->url.host, &idn, IDNA_ALLOW_UNASSIGNED|IDNA_USE_STD3_ASCII_RULES);
-       }
-#      ifdef PHP_HTTP_HAVE_WCHAR
-       else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
-               rv = idna_to_ascii_lz(state->url.host, &idn, IDNA_ALLOW_UNASSIGNED|IDNA_USE_STD3_ASCII_RULES);
-       }
-#      endif
-       if (rv != IDNA_SUCCESS) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN; %s", idna_strerror(rv));
-               return FAILURE;
-       } else {
-               size_t idnlen = strlen(idn);
-               memcpy(state->url.host, idn, idnlen + 1);
-               free(idn);
-               state->offset += idnlen - prev_len;
-               return SUCCESS;
-       }
-}
-#endif
-
-#ifdef HAVE_UIDNA_IDNTOASCII
-#      if HAVE_UNICODE_UIDNA_H
-#              include <unicode/uidna.h>
-#      else
-typedef uint16_t UChar;
-typedef enum { U_ZERO_ERROR = 0 } UErrorCode;
-int32_t uidna_IDNToASCII(const UChar *src, int32_t srcLength, UChar *dest, int32_t destCapacity, int32_t options, void *parseError, UErrorCode *status);
-#      endif
-static ZEND_RESULT_CODE parse_uidn(struct parse_state *state)
-{
-       char *host_ptr;
-       uint16_t *uhost_str, ahost_str[MAXHOSTNAMELEN], *ahost_ptr;
-       size_t uhost_len, ahost_len;
-       UErrorCode error = U_ZERO_ERROR;
-       TSRMLS_FETCH_FROM_CTX(state->ts);
-
-       if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
-               if (SUCCESS != to_utf16(parse_mb_utf8, state->url.host, &uhost_str, &uhost_len TSRMLS_CC)) {
-                       return FAILURE;
-               }
-#ifdef PHP_HTTP_HAVE_WCHAR
-       } else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
-               if (SUCCESS != to_utf16(parse_mb_loc, state->url.host, &uhost_str, &uhost_len TSRMLS_CC)) {
-                       return FAILURE;
-               }
-#endif
-       } else {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN; codepage not specified");
-               return FAILURE;
-       }
-
-       ahost_len = uidna_IDNToASCII(uhost_str, uhost_len, ahost_str, MAXHOSTNAMELEN, 3, NULL, &error);
-       efree(uhost_str);
-
-       if (error != U_ZERO_ERROR) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN; ICU error %d", error);
-               return FAILURE;
-       }
-
-       host_ptr = state->url.host;
-       ahost_ptr = ahost_str;
-       PHP_HTTP_DUFF(ahost_len, *host_ptr++ = *ahost_ptr++);
-
-       *host_ptr = '\0';
-       state->offset += host_ptr - state->url.host;
-
-       return SUCCESS;
-}
-#endif
-
-#if 0 && defined(PHP_WIN32)
-static ZEND_RESULT_CODE parse_widn(struct parse_state *state)
-{
-       char *host_ptr;
-       uint16_t *uhost_str, ahost_str[MAXHOSTNAMELEN], *ahost_ptr;
-       size_t uhost_len;
-       TSRMLS_FETCH_FROM_CTX(state->ts);
-
-       if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
-               if (SUCCESS != to_utf16(parse_mb_utf8, state->url.host, &uhost_str, &uhost_len)) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN");
-                       return FAILURE;
-               }
-#ifdef PHP_HTTP_HAVE_WCHAR
-       } else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
-               if (SUCCESS != to_utf16(parse_mb_loc, state->url.host, &uhost_str, &uhost_len)) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN");
-                       return FAILURE;
-               }
-#endif
-       } else {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN");
-               return FAILURE;
-       }
-
-       if (!IdnToAscii(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES, uhost_str, uhost_len, ahost_str, MAXHOSTNAMELEN)) {
-               efree(uhost_str);
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN");
-               return FAILURE;
-       }
-
-       efree(uhost_str);
-       host_ptr = state->url.host;
-       ahost_ptr = ahost_str;
-       PHP_HTTP_DUFF(wcslen(ahost_str), *host_ptr++ = *ahost_ptr++);
-       efree(ahost_str);
-
-       *host_ptr = '\0';
-       state->offset += host_ptr - state->url.host;
-
-       return SUCCESS;
-}
-#endif
-
-#ifdef HAVE_INET_PTON
-static const char *parse_ip6(struct parse_state *state, const char *ptr)
-{
-       const char *error = NULL, *end = state->ptr, *tmp = memchr(ptr, ']', end - ptr);
-       TSRMLS_FETCH_FROM_CTX(state->ts);
-
-       if (tmp) {
-               size_t addrlen = tmp - ptr + 1;
-               char buf[16], *addr = estrndup(ptr + 1, addrlen - 2);
-               int rv = inet_pton(AF_INET6, addr, buf);
-
-               if (rv == 1) {
-                       state->buffer[state->offset] = '[';
-                       state->url.host = &state->buffer[state->offset];
-                       inet_ntop(AF_INET6, buf, state->url.host + 1, state->maxlen - state->offset);
-                       state->offset += strlen(state->url.host);
-                       state->buffer[state->offset++] = ']';
-                       state->buffer[state->offset++] = 0;
-                       ptr = tmp + 1;
-               } else if (rv == -1) {
-                       error = strerror(errno);
-               } else {
-                       error = "unexpected '['";
-               }
-               efree(addr);
-       } else {
-               error = "expected ']'";
-       }
-
-       if (error) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse hostinfo; %s", error);
-               return NULL;
-       }
-
-       return ptr;
-}
-#endif
-
-static ZEND_RESULT_CODE parse_hostinfo(struct parse_state *state, const char *ptr)
-{
-       size_t mb, len;
-       const char *end = state->ptr, *tmp = ptr, *port = NULL, *label = NULL;
-       TSRMLS_FETCH_FROM_CTX(state->ts);
-
-#ifdef HAVE_INET_PTON
-       if (*ptr == '[' && !(ptr = parse_ip6(state, ptr))) {
-               return FAILURE;
-       }
-#endif
-
-       if (ptr != end) do {
-               switch (*ptr) {
-               case ':':
-                       if (port) {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
-                                               "Failed to parse port; unexpected ':' at pos %u in '%s'",
-                                               (unsigned) (ptr - tmp), tmp);
-                               return FAILURE;
-                       }
-                       port = ptr + 1;
-                       break;
-
-               case '%':
-                       if (ptr[1] != '%' && (end - ptr <= 2 || !isxdigit(*(ptr+1)) || !isxdigit(*(ptr+2)))) {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
-                                               "Failed to parse hostinfo; invalid percent encoding at pos %u in '%s'",
-                                               (unsigned) (ptr - tmp), tmp);
-                               return FAILURE;
-                       }
-                       state->buffer[state->offset++] = *ptr++;
-                       state->buffer[state->offset++] = *ptr++;
-                       state->buffer[state->offset++] = *ptr;
-                       break;
-
-               case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
-               case '+': case ',': case ';': case '=': /* sub-delims */
-               case '-': case '.': case '_': case '~': /* unreserved */
-                       if (port || !label) {
-                               /* sort of a compromise, just ensure we don't end up
-                                * with a dot at the beginning or two consecutive dots
-                                */
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
-                                               "Failed to parse %s; unexpected '%c' at pos %u in '%s'",
-                                               port ? "port" : "host",
-                                               (unsigned char) *ptr, (unsigned) (ptr - tmp), tmp);
-                               return FAILURE;
-                       }
-                       state->buffer[state->offset++] = *ptr;
-                       label = NULL;
-                       break;
-
-               case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
-               case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
-               case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
-               case 'V': case 'W': case 'X': case 'Y': case 'Z':
-               case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
-               case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
-               case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
-               case 'v': case 'w': case 'x': case 'y': case 'z':
-                       if (port) {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
-                                               "Failed to parse port; unexpected char '%c' at pos %u in '%s'",
-                                               (unsigned char) *ptr, (unsigned) (ptr - tmp), tmp);
-                               return FAILURE;
-                       }
-                       /* no break */
-               case '0': case '1': case '2': case '3': case '4': case '5': case '6':
-               case '7': case '8': case '9':
-                       /* allowed */
-                       if (port) {
-                               state->url.port *= 10;
-                               state->url.port += *ptr - '0';
-                       } else {
-                               label = ptr;
-                               state->buffer[state->offset++] = *ptr;
-                       }
-                       break;
-
-               default:
-                       if (ptr == end) {
-                               break;
-                       } else if (port) {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
-                                               "Failed to parse port; unexpected byte 0x%02x at pos %u in '%s'",
-                                               (unsigned char) *ptr, (unsigned) (ptr - tmp), tmp);
-                               return FAILURE;
-                       } else if (!(mb = parse_mb(state, PARSE_HOSTINFO, ptr, end, tmp, 0))) {
-                               return FAILURE;
-                       }
-                       label = ptr;
-                       ptr += mb - 1;
-               }
-       } while (++ptr != end);
-
-       if (!state->url.host) {
-               len = (port ? port - tmp - 1 : end - tmp);
-               state->url.host = &state->buffer[state->offset - len];
-               state->buffer[state->offset++] = 0;
-       }
-
-       if (state->flags & PHP_HTTP_URL_PARSE_TOIDN) {
-#if PHP_HTTP_HAVE_IDN2
-               return parse_idn2(state, len);
-#elif PHP_HTTP_HAVE_IDN
-               return parse_idn(state, len);
-#endif
-#ifdef HAVE_UIDNA_IDNTOASCII
-               return parse_uidn(state);
-#endif
-#if 0 && defined(PHP_WIN32)
-               return parse_widn(state);
-#endif
-       }
-
-       return SUCCESS;
-}
-
-static const char *parse_authority(struct parse_state *state)
-{
-       const char *tmp = state->ptr, *host = NULL;
-
-       do {
-               switch (*state->ptr) {
-               case '@':
-                       /* userinfo delimiter */
-                       if (host) {
-                               TSRMLS_FETCH_FROM_CTX(state->ts);
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
-                                               "Failed to parse userinfo; unexpected '@'");
-                               return NULL;
-                       }
-                       host = state->ptr + 1;
-                       if (tmp != state->ptr && SUCCESS != parse_userinfo(state, tmp)) {
-                               return NULL;
-                       }
-                       tmp = state->ptr + 1;
-                       break;
-
-               case '/':
-               case '?':
-               case '#':
-               case '\0':
-                       EOD:
-                       /* host delimiter */
-                       if (tmp != state->ptr && SUCCESS != parse_hostinfo(state, tmp)) {
-                               return NULL;
-                       }
-                       return state->ptr;
-               }
-       } while (++state->ptr <= state->end);
-
-       --state->ptr;
-       goto EOD;
-}
-
-static const char *parse_path(struct parse_state *state)
-{
-       size_t mb;
-       const char *tmp;
-       TSRMLS_FETCH_FROM_CTX(state->ts);
-
-       /* is there actually a path to parse? */
-       if (!*state->ptr) {
-               return state->ptr;
-       }
-       tmp = state->ptr;
-       state->url.path = &state->buffer[state->offset];
-
-       do {
-               switch (*state->ptr) {
-               case '#':
-               case '?':
-                       goto done;
-
-               case '%':
-                       if (state->ptr[1] != '%' && (state->end - state->ptr <= 2 || !isxdigit(*(state->ptr+1)) || !isxdigit(*(state->ptr+2)))) {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
-                                               "Failed to parse path; invalid percent encoding at pos %u in '%s'",
-                                               (unsigned) (state->ptr - tmp), tmp);
-                               return NULL;
-                       }
-                       state->buffer[state->offset++] = *state->ptr++;
-                       state->buffer[state->offset++] = *state->ptr++;
-                       state->buffer[state->offset++] = *state->ptr;
-                       break;
-
-               case '/': /* yeah, well */
-               case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
-               case '+': case ',': case ';': case '=': /* sub-delims */
-               case '-': case '.': case '_': case '~': /* unreserved */
-               case ':': case '@': /* pchar */
-               case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
-               case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
-               case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
-               case 'V': case 'W': case 'X': case 'Y': case 'Z':
-               case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
-               case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
-               case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
-               case 'v': case 'w': case 'x': case 'y': case 'z':
-               case '0': case '1': case '2': case '3': case '4': case '5': case '6':
-               case '7': case '8': case '9':
-                       /* allowed */
-                       state->buffer[state->offset++] = *state->ptr;
-                       break;
-
-               default:
-                       if (!(mb = parse_mb(state, PARSE_PATH, state->ptr, state->end, tmp, 0))) {
-                               return NULL;
-                       }
-                       state->ptr += mb - 1;
-               }
-       } while (++state->ptr < state->end);
-
-       done:
-       /* did we have any path component ? */
-       if (tmp != state->ptr) {
-               state->buffer[state->offset++] = 0;
-       } else {
-               state->url.path = NULL;
-       }
-       return state->ptr;
-}
-
-static const char *parse_query(struct parse_state *state)
-{
-       size_t mb;
-       const char *tmp = state->ptr + !!*state->ptr;
-       TSRMLS_FETCH_FROM_CTX(state->ts);
-
-       /* is there actually a query to parse? */
-       if (*state->ptr != '?') {
-               return state->ptr;
-       }
-
-       /* skip initial '?' */
-       tmp = ++state->ptr;
-       state->url.query = &state->buffer[state->offset];
-
-       while (state->ptr < state->end) {
-               switch (*state->ptr) {
-               case '#':
-                       goto done;
-
-               case '%':
-                       if (state->ptr[1] != '%' && (state->end - state->ptr <= 2 || !isxdigit(*(state->ptr+1)) || !isxdigit(*(state->ptr+2)))) {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
-                                               "Failed to parse query; invalid percent encoding at pos %u in '%s'",
-                                               (unsigned) (state->ptr - tmp), tmp);
-                               return NULL;
-                       }
-                       state->buffer[state->offset++] = *state->ptr++;
-                       state->buffer[state->offset++] = *state->ptr++;
-                       state->buffer[state->offset++] = *state->ptr;
-                       break;
-
-               /* RFC1738 unsafe */
-               case '{': case '}':
-               case '<': case '>':
-               case '[': case ']':
-               case '|': case '\\': case '^': case '`': case '"': case ' ':
-                       if (state->flags & PHP_HTTP_URL_PARSE_TOPCT) {
-                               state->buffer[state->offset++] = '%';
-                               state->buffer[state->offset++] = parse_xdigits[((unsigned char) *state->ptr) >> 4];
-                               state->buffer[state->offset++] = parse_xdigits[((unsigned char) *state->ptr) & 0xf];
-                               break;
-                       }
-                       /* no break */
-
-               case '?': case '/': /* yeah, well */
-               case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
-               case '+': case ',': case ';': case '=': /* sub-delims */
-               case '-': case '.': case '_': case '~': /* unreserved */
-               case ':': case '@': /* pchar */
-               case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
-               case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
-               case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
-               case 'V': case 'W': case 'X': case 'Y': case 'Z':
-               case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
-               case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
-               case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
-               case 'v': case 'w': case 'x': case 'y': case 'z':
-               case '0': case '1': case '2': case '3': case '4': case '5': case '6':
-               case '7': case '8': case '9':
-                       /* allowed */
-                       state->buffer[state->offset++] = *state->ptr;
-                       break;
-
-               default:
-                       if (!(mb = parse_mb(state, PARSE_QUERY, state->ptr, state->end, tmp, 0))) {
-                               return NULL;
-                       }
-                       state->ptr += mb - 1;
-               }
-
-               ++state->ptr;
-       }
-
-       done:
-       state->buffer[state->offset++] = 0;
-       return state->ptr;
-}
-
-static const char *parse_fragment(struct parse_state *state)
-{
-       size_t mb;
-       const char *tmp;
-       TSRMLS_FETCH_FROM_CTX(state->ts);
-
-       /* is there actually a fragment to parse? */
-       if (*state->ptr != '#') {
-               return state->ptr;
-       }
-
-       /* skip initial '#' */
-       tmp = ++state->ptr;
-       state->url.fragment = &state->buffer[state->offset];
-
-       do {
-               switch (*state->ptr) {
-               case '%':
-                       if (state->ptr[1] != '%' && (state->end - state->ptr <= 2 || !isxdigit(*(state->ptr+1)) || !isxdigit(*(state->ptr+2)))) {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
-                                               "Failed to parse fragment; invalid percent encoding at pos %u in '%s'",
-                                               (unsigned) (state->ptr - tmp), tmp);
-                               return NULL;
-                       }
-                       state->buffer[state->offset++] = *state->ptr++;
-                       state->buffer[state->offset++] = *state->ptr++;
-                       state->buffer[state->offset++] = *state->ptr;
-                       break;
-
-               /* RFC1738 unsafe */
-               case '{': case '}':
-               case '<': case '>':
-               case '[': case ']':
-               case '|': case '\\': case '^': case '`': case '"': case ' ':
-                       if (state->flags & PHP_HTTP_URL_PARSE_TOPCT) {
-                               state->buffer[state->offset++] = '%';
-                               state->buffer[state->offset++] = parse_xdigits[((unsigned char) *state->ptr) >> 4];
-                               state->buffer[state->offset++] = parse_xdigits[((unsigned char) *state->ptr) & 0xf];
-                               break;
-                       }
-                       /* no break */
-
-               case '?': case '/':
-               case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
-               case '+': case ',': case ';': case '=': /* sub-delims */
-               case '-': case '.': case '_': case '~': /* unreserved */
-               case ':': case '@': /* pchar */
-               case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
-               case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
-               case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
-               case 'V': case 'W': case 'X': case 'Y': case 'Z':
-               case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
-               case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
-               case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
-               case 'v': case 'w': case 'x': case 'y': case 'z':
-               case '0': case '1': case '2': case '3': case '4': case '5': case '6':
-               case '7': case '8': case '9':
-                       /* allowed */
-                       state->buffer[state->offset++] = *state->ptr;
-                       break;
-
-               default:
-                       if (!(mb = parse_mb(state, PARSE_FRAGMENT, state->ptr, state->end, tmp, 0))) {
-                               return NULL;
-                       }
-                       state->ptr += mb - 1;
-               }
-       } while (++state->ptr < state->end);
-
-       state->buffer[state->offset++] = 0;
-       return state->ptr;
-}
-
-static const char *parse_hier(struct parse_state *state)
-{
-       if (*state->ptr == '/') {
-               if (state->end - state->ptr > 1) {
-                       if (*(state->ptr + 1) == '/') {
-                               state->ptr += 2;
-                               if (!(state->ptr = parse_authority(state))) {
-                                       return NULL;
-                               }
-                       }
-               }
-       }
-       return parse_path(state);
-}
-
-static const char *parse_scheme(struct parse_state *state)
-{
-       size_t mb;
-       const char *tmp = state->ptr;
-
-       do {
-               switch (*state->ptr) {
-               case ':':
-                       /* scheme delimiter */
-                       state->url.scheme = &state->buffer[0];
-                       state->buffer[state->offset++] = 0;
-                       return ++state->ptr;
-
-               case '0': case '1': case '2': case '3': case '4': case '5': case '6':
-               case '7': case '8': case '9':
-               case '+': case '-': case '.':
-                       if (state->ptr == tmp) {
-                               return tmp;
-                       }
-                       /* no break */
-               case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
-               case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
-               case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
-               case 'V': case 'W': case 'X': case 'Y': case 'Z':
-               case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
-               case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
-               case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
-               case 'v': case 'w': case 'x': case 'y': case 'z':
-                       /* scheme part */
-                       state->buffer[state->offset++] = *state->ptr;
-                       break;
-
-               default:
-                       if (!(mb = parse_mb(state, PARSE_SCHEME, state->ptr, state->end, tmp, 1))) {
-                               /* soft fail; parse path next */
-                               return tmp;
-                       }
-                       state->ptr += mb - 1;
-               }
-       } while (++state->ptr != state->end);
-
-       return state->ptr = tmp;
-}
-
-php_http_url_t *php_http_url_parse(const char *str, size_t len, unsigned flags TSRMLS_DC)
-{
-       size_t maxlen = 3 * len;
-       struct parse_state *state = ecalloc(1, sizeof(*state) + maxlen);
-
-       state->end = str + len;
-       state->ptr = str;
-       state->flags = flags;
-       state->maxlen = maxlen;
-       TSRMLS_SET_CTX(state->ts);
-
-       if (!parse_scheme(state)) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse URL scheme: '%s'", state->ptr);
-               efree(state);
-               return NULL;
-       }
-
-       if (!parse_hier(state)) {
-               efree(state);
-               return NULL;
-       }
-
-       if (!parse_query(state)) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse URL query: '%s'", state->ptr);
-               efree(state);
-               return NULL;
-       }
-
-       if (!parse_fragment(state)) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse URL fragment: '%s'", state->ptr);
-               efree(state);
-               return NULL;
-       }
-
-       return (php_http_url_t *) state;
-}
-
-php_http_url_t *php_http_url_parse_authority(const char *str, size_t len, unsigned flags TSRMLS_DC)
-{
-       size_t maxlen = 3 * len;
-       struct parse_state *state = ecalloc(1, sizeof(*state) + maxlen);
-
-       state->end = str + len;
-       state->ptr = str;
-       state->flags = flags;
-       state->maxlen = maxlen;
-       TSRMLS_SET_CTX(state->ts);
-
-       if (!(state->ptr = parse_authority(state))) {
-               efree(state);
-               return NULL;
-       }
-
-       if (state->ptr != state->end) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING,
-                               "Failed to parse URL authority, unexpected character at pos %u in '%s'",
-                               (unsigned) (state->ptr - str), str);
-               efree(state);
-               return NULL;
-       }
-
-       return (php_http_url_t *) state;
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl___construct, 0, 0, 0)
-       ZEND_ARG_INFO(0, old_url)
-       ZEND_ARG_INFO(0, new_url)
-       ZEND_ARG_INFO(0, flags)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpUrl, __construct)
-{
-       zval *new_url = NULL, *old_url = NULL;
-       long flags = PHP_HTTP_URL_FROM_ENV;
-       zend_error_handling zeh;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!z!l", &old_url, &new_url, &flags), invalid_arg, return);
-
-       zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC);
-       {
-               php_http_url_t *res_purl, *new_purl = NULL, *old_purl = NULL;
-
-               if (new_url) {
-                       new_purl = php_http_url_from_zval(new_url, flags TSRMLS_CC);
-                       if (!new_purl) {
-                               zend_restore_error_handling(&zeh TSRMLS_CC);
-                               return;
-                       }
-               }
-               if (old_url) {
-                       old_purl = php_http_url_from_zval(old_url, flags TSRMLS_CC);
-                       if (!old_purl) {
-                               if (new_purl) {
-                                       php_http_url_free(&new_purl);
-                               }
-                               zend_restore_error_handling(&zeh TSRMLS_CC);
-                               return;
-                       }
-               }
-
-               res_purl = php_http_url_mod(old_purl, new_purl, flags TSRMLS_CC);
-               php_http_url_to_struct(res_purl, getThis() TSRMLS_CC);
-
-               php_http_url_free(&res_purl);
-               if (old_purl) {
-                       php_http_url_free(&old_purl);
-               }
-               if (new_purl) {
-                       php_http_url_free(&new_purl);
-               }
-       }
-       zend_restore_error_handling(&zeh TSRMLS_CC);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_mod, 0, 0, 1)
-       ZEND_ARG_INFO(0, more_url_parts)
-       ZEND_ARG_INFO(0, flags)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpUrl, mod)
-{
-       zval *new_url = NULL;
-       long flags = PHP_HTTP_URL_JOIN_PATH | PHP_HTTP_URL_JOIN_QUERY | PHP_HTTP_URL_SANITIZE_PATH;
-       zend_error_handling zeh;
-
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z!|l", &new_url, &flags), invalid_arg, return);
-
-       zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC);
-       {
-               php_http_url_t *new_purl = NULL, *old_purl = NULL;
-
-               if (new_url) {
-                       new_purl = php_http_url_from_zval(new_url, flags TSRMLS_CC);
-                       if (!new_purl) {
-                               zend_restore_error_handling(&zeh TSRMLS_CC);
-                               return;
-                       }
-               }
-
-               if ((old_purl = php_http_url_from_struct(HASH_OF(getThis())))) {
-                       php_http_url_t *res_purl;
-
-                       ZVAL_OBJVAL(return_value, zend_objects_clone_obj(getThis() TSRMLS_CC), 0);
-
-                       res_purl = php_http_url_mod(old_purl, new_purl, flags TSRMLS_CC);
-                       php_http_url_to_struct(res_purl, return_value TSRMLS_CC);
-
-                       php_http_url_free(&res_purl);
-                       php_http_url_free(&old_purl);
-               }
-               if (new_purl) {
-                       php_http_url_free(&new_purl);
-               }
-       }
-       zend_restore_error_handling(&zeh TSRMLS_CC);
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_toString, 0, 0, 0)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpUrl, toString)
-{
-       if (SUCCESS == zend_parse_parameters_none()) {
-               php_http_url_t *purl;
-
-               if ((purl = php_http_url_from_struct(HASH_OF(getThis())))) {
-                       char *str;
-                       size_t len;
-
-                       php_http_url_to_string(purl, &str, &len, 0);
-                       php_http_url_free(&purl);
-                       RETURN_STRINGL(str, len, 0);
-               }
-       }
-       RETURN_EMPTY_STRING();
-}
-
-ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_toArray, 0, 0, 0)
-ZEND_END_ARG_INFO();
-PHP_METHOD(HttpUrl, toArray)
-{
-       php_http_url_t *purl;
-
-       if (SUCCESS != zend_parse_parameters_none()) {
-               return;
-       }
-
-       /* strip any non-URL properties */
-       purl = php_http_url_from_struct(HASH_OF(getThis()));
-       php_http_url_to_struct(purl, return_value TSRMLS_CC);
-       php_http_url_free(&purl);
-}
-
-static zend_function_entry php_http_url_methods[] = {
-       PHP_ME(HttpUrl, __construct,  ai_HttpUrl___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
-       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)
-       PHP_ME(HttpUrl, toArray,      ai_HttpUrl_toArray, ZEND_ACC_PUBLIC)
-       EMPTY_FUNCTION_ENTRY
-};
-
-zend_class_entry *php_http_url_class_entry;
-
-PHP_MINIT_FUNCTION(http_url)
-{
-       zend_class_entry ce = {0};
-
-       INIT_NS_CLASS_ENTRY(ce, "http", "Url", php_http_url_methods);
-       php_http_url_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
-
-       zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("scheme"), ZEND_ACC_PUBLIC TSRMLS_CC);
-       zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("user"), ZEND_ACC_PUBLIC TSRMLS_CC);
-       zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("pass"), ZEND_ACC_PUBLIC TSRMLS_CC);
-       zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("host"), ZEND_ACC_PUBLIC TSRMLS_CC);
-       zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("port"), ZEND_ACC_PUBLIC TSRMLS_CC);
-       zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("path"), ZEND_ACC_PUBLIC TSRMLS_CC);
-       zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("query"), ZEND_ACC_PUBLIC TSRMLS_CC);
-       zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("fragment"), ZEND_ACC_PUBLIC TSRMLS_CC);
-
-       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("REPLACE"), PHP_HTTP_URL_REPLACE TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("JOIN_PATH"), PHP_HTTP_URL_JOIN_PATH TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("JOIN_QUERY"), PHP_HTTP_URL_JOIN_QUERY TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_USER"), PHP_HTTP_URL_STRIP_USER TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_PASS"), PHP_HTTP_URL_STRIP_PASS TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_AUTH"), PHP_HTTP_URL_STRIP_AUTH TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_PORT"), PHP_HTTP_URL_STRIP_PORT TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_PATH"), PHP_HTTP_URL_STRIP_PATH TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_QUERY"), PHP_HTTP_URL_STRIP_QUERY TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_FRAGMENT"), PHP_HTTP_URL_STRIP_FRAGMENT TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_ALL"), PHP_HTTP_URL_STRIP_ALL TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("FROM_ENV"), PHP_HTTP_URL_FROM_ENV TSRMLS_CC);
-       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("SANITIZE_PATH"), PHP_HTTP_URL_SANITIZE_PATH TSRMLS_CC);
-
-#ifdef PHP_HTTP_HAVE_WCHAR
-       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_MBLOC"), PHP_HTTP_URL_PARSE_MBLOC TSRMLS_CC);
-#endif
-       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_MBUTF8"), PHP_HTTP_URL_PARSE_MBUTF8 TSRMLS_CC);
-#if defined(PHP_HTTP_HAVE_IDN2) || defined(PHP_HTTP_HAVE_IDN) || defined(HAVE_UIDNA_IDNTOASCII)
-       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_TOIDN"), PHP_HTTP_URL_PARSE_TOIDN TSRMLS_CC);
-#endif
-       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_TOPCT"), PHP_HTTP_URL_PARSE_TOPCT TSRMLS_CC);
-
-       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/php_http_url.h b/php_http_url.h
deleted file mode 100644 (file)
index 636efb5..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_URL_H
-#define PHP_HTTP_URL_H
-
-#include <ext/standard/url.h>
-
-/* php_http_url_mod() */
-#define PHP_HTTP_URL_REPLACE           0x000
-#define PHP_HTTP_URL_JOIN_PATH         0x001
-#define PHP_HTTP_URL_JOIN_QUERY                0x002
-#define PHP_HTTP_URL_STRIP_USER                0x004
-#define PHP_HTTP_URL_STRIP_PASS                0x008
-#define PHP_HTTP_URL_STRIP_AUTH                (PHP_HTTP_URL_STRIP_USER|PHP_HTTP_URL_STRIP_PASS)
-#define PHP_HTTP_URL_STRIP_PORT                0x020
-#define PHP_HTTP_URL_STRIP_PATH                0x040
-#define PHP_HTTP_URL_STRIP_QUERY       0x080
-#define PHP_HTTP_URL_STRIP_FRAGMENT    0x100
-#define PHP_HTTP_URL_STRIP_ALL ( \
-       PHP_HTTP_URL_STRIP_AUTH | \
-       PHP_HTTP_URL_STRIP_PORT | \
-       PHP_HTTP_URL_STRIP_PATH | \
-       PHP_HTTP_URL_STRIP_QUERY | \
-       PHP_HTTP_URL_STRIP_FRAGMENT \
-)
-#define PHP_HTTP_URL_FROM_ENV          0x1000
-#define PHP_HTTP_URL_SANITIZE_PATH     0x2000
-
-/* parse multibyte according to locale */
-#define PHP_HTTP_URL_PARSE_MBLOC       0x10000
-/* parse utf8 multibyte sequences */
-#define PHP_HTTP_URL_PARSE_MBUTF8      0x20000
-/* convert multibyte hostnames to IDNA */
-#define PHP_HTTP_URL_PARSE_TOIDN       0x100000
-/* percent encode multibyte sequences in userinfo, path, query and fragment */
-#define PHP_HTTP_URL_PARSE_TOPCT       0x200000
-
-typedef struct php_http_url {
-       /* compatible to php_url, but do not use php_url_free() */
-       char *scheme;
-       char *user;
-       char *pass;
-       char *host;
-       unsigned short port;
-       char *path;
-       char *query;
-       char *fragment;
-} php_http_url_t;
-
-PHP_HTTP_API php_http_url_t *php_http_url_parse(const char *str, size_t len, unsigned flags TSRMLS_DC);
-PHP_HTTP_API php_http_url_t *php_http_url_parse_authority(const char *str, size_t len, unsigned flags TSRMLS_DC);
-PHP_HTTP_API php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_url_t *new_url, unsigned flags TSRMLS_DC);
-PHP_HTTP_API php_http_url_t *php_http_url_copy(const php_http_url_t *url, zend_bool persistent);
-PHP_HTTP_API php_http_url_t *php_http_url_from_struct(HashTable *ht);
-PHP_HTTP_API php_http_url_t *php_http_url_from_zval(zval *value, unsigned flags TSRMLS_DC);
-PHP_HTTP_API HashTable *php_http_url_to_struct(const php_http_url_t *url, zval *strct TSRMLS_DC);
-PHP_HTTP_API char *php_http_url_to_string(const php_http_url_t *url, char **url_str, size_t *url_len, zend_bool persistent);
-PHP_HTTP_API char *php_http_url_authority_to_string(const php_http_url_t *url, char **url_str, size_t *url_len);
-PHP_HTTP_API void php_http_url_free(php_http_url_t **url);
-
-PHP_HTTP_API ZEND_RESULT_CODE php_http_url_encode_hash(HashTable *hash, const char *pre_encoded_str, size_t pre_encoded_len, char **encoded_str, size_t *encoded_len TSRMLS_DC);
-PHP_HTTP_API ZEND_RESULT_CODE php_http_url_encode_hash_ex(HashTable *hash, php_http_buffer_t *qstr, const char *arg_sep_str, size_t arg_sep_len, const char *val_sep_str, size_t val_sep_len, const char *pre_encoded_str, size_t pre_encoded_len TSRMLS_DC);
-
-static inline void php_http_url_argsep(const char **str, size_t *len TSRMLS_DC)
-{
-       php_http_ini_entry(ZEND_STRL("arg_separator.output"), str, len, 0 TSRMLS_CC);
-}
-
-static inline zend_bool php_http_url_is_empty(const php_http_url_t *url) {
-       return !(url->scheme || url->pass || url->user || url->host || url->port ||     url->path || url->query || url->fragment);
-}
-
-PHP_HTTP_API zend_class_entry *php_http_url_class_entry;
-PHP_MINIT_FUNCTION(http_url);
-
-#define php_http_url_object_new php_http_object_new
-#define php_http_url_object_new_ex php_http_object_new_ex
-
-#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/php_http_utf8.h b/php_http_utf8.h
deleted file mode 100644 (file)
index f900803..0000000
+++ /dev/null
@@ -1,670 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_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,
-    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,
-    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,
-    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,
-    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,
-    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,
-    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-    3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
-    4,4,4,4,4,4,4,4,5,5,5,5,6,6,6,6
-};
-
-static const unsigned char utf8_mask[] = {
-               0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01
-};
-
-static const utf8_range_t utf8_ranges[] = {
-/* 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,     0x0CCD, 1},
-       {    0x0CD5,     0x0CD6, 1},
-       {    0x0CDE,          0, 0},
-       {    0x0CE0,     0x0CE3, 1},
-       {    0x0CF1,          0, 0},
-       {    0x0CF2,          0, 0},
-       {    0x0D02,     0x0D03, 1},
-       {    0x0D05,     0x0D0C, 1},
-       {    0x0D0E,     0x0D10, 1},
-       {    0x0D12,     0x0D28, 1},
-       {    0x0D2A,     0x0D39, 1},
-       {    0x0D3D,     0x0D44, 1},
-       {    0x0D46,     0x0D48, 1},
-       {    0x0D4A,     0x0D4D, 1},
-       {    0x0D57,          0, 0},
-       {    0x0D60,     0x0D63, 1},
-       {    0x0D79,     0x0D7F, 1},
-       {    0x0D82,     0x0D83, 1},
-       {    0x0D85,     0x0D96, 1},
-       {    0x0D9A,     0x0DB1, 1},
-       {    0x0DB3,     0x0DBB, 1},
-       {    0x0DBD,          0, 0},
-       {    0x0DC0,     0x0DC6, 1},
-       {    0x0DCA,          0, 0},
-       {    0x0DCF,     0x0DD4, 1},
-       {    0x0DD6,          0, 0},
-       {    0x0DD8,     0x0DDF, 1},
-       {    0x0DF2,     0x0DF4, 1},
-       {    0x0E01,     0x0E2E, 1},
-       {    0x0E30,     0x0E3A, 1},
-       {    0x0E40,     0x0E45, 1},
-       {    0x0E47,     0x0E4E, 1},
-       {    0x0E81,     0x0E82, 1},
-       {    0x0E84,          0, 0},
-       {    0x0E87,     0x0E88, 1},
-       {    0x0E8A,          0, 0},
-       {    0x0E8D,          0, 0},
-       {    0x0E94,     0x0E97, 1},
-       {    0x0E99,     0x0E9F, 1},
-       {    0x0EA1,     0x0EA3, 1},
-       {    0x0EA5,          0, 0},
-       {    0x0EA7,          0, 0},
-       {    0x0EAA,     0x0EAB, 1},
-       {    0x0EAD,     0x0EB0, 1},
-       {    0x0EB2,     0x0EB3, 1},
-       {    0x0EBD,          0, 0},
-       {    0x0EC0,     0x0EC4, 1},
-       {    0x0EC6,          0, 0},
-       {    0x0EDC,     0x0EDD, 1},
-       {    0x0F00,          0, 0},
-       {    0x0F40,     0x0F47, 1},
-       {    0x0F49,     0x0F6C, 1},
-       {    0x0F88,     0x0F8B, 1},
-       {    0x1000,     0x102A, 1},
-       {    0x1050,     0x1055, 1},
-       {    0x105A,     0x105D, 1},
-       {    0x1061,          0, 0},
-       {    0x0165,          0, 0},
-       {    0x1066,          0, 0},
-       {    0x106E,     0x1070, 1},
-       {    0x1075,     0x1081, 1},
-       {    0x108E,          0, 0},
-       {    0x10A0,     0x10C5, 1},
-       {    0x10D0,     0x10FA, 1},
-       {    0x10FC,          0, 0},
-       {    0x1100,     0x1159, 1},
-       {    0x115F,     0x11A2, 1},
-       {    0x11A8,     0x11F9, 1},
-       {    0x1200,     0x1248, 1},
-       {    0x124A,     0x124D, 1},
-       {    0x1250,     0x1256, 1},
-       {    0x1258,          0, 0},
-       {    0x125A,     0x125D, 1},
-       {    0x1260,     0x1288, 1},
-       {    0x128A,     0x128D, 1},
-       {    0x1290,     0x12B0, 1},
-       {    0x12B2,     0x12B5, 1},
-       {    0x12B8,     0x12BE, 1},
-       {    0x12C0,          0, 0},
-       {    0x12C2,     0x12C5, 1},
-       {    0x12C8,     0x12D6, 1},
-       {    0x12D8,     0x1310, 1},
-       {    0x1312,     0x1315, 1},
-       {    0x1318,     0x135A, 1},
-       {    0x1380,     0x138F, 1},
-       {    0x13A0,     0x13F4, 1},
-       {    0x1401,     0x166C, 1},
-       {    0x166F,     0x1676, 1},
-       {    0x1681,     0x169A, 1},
-       {    0x16A0,     0x16EA, 1},
-       {    0x16EE,     0x16F0, 1},
-       {    0x1700,     0x170C, 1},
-       {    0x170E,     0x1711, 1},
-       {    0x1720,     0x1731, 1},
-       {    0x1740,     0x1751, 1},
-       {    0x1760,     0x176C, 1},
-       {    0x176E,     0x1770, 1},
-       {    0x1780,     0x17B3, 1},
-       {    0x17D7,          0, 0},
-       {    0x17DC,          0, 0},
-       {    0x1820,     0x1877, 1},
-       {    0x1880,     0x18A8, 1},
-       {    0x18AA,          0, 0},
-       {    0x1900,     0x191C, 1},
-       {    0x1946,     0x194F, 1},
-       {    0x1950,     0x196D, 1},
-       {    0x1970,     0x1974, 1},
-       {    0x1980,     0x19A9, 1},
-       {    0x19C1,     0x19C7, 1},
-       {    0x19D0,     0x19D9, 1},
-       {    0x1A00,     0x1A16, 1},
-       {    0x1B05,     0x1B33, 1},
-       {    0x1B45,     0x1B4B, 1},
-       {    0x1B50,     0x1B59, 1},
-       {    0x1B83,     0x1BA0, 1},
-       {    0x1BAE,     0x1BAF, 1},
-       {    0x1C00,     0x1C23, 1},
-       {    0x1C4D,     0x1C4F, 1},
-       {    0x1C5A,     0x1C7D, 1},
-       {    0x1D00,     0x1DBF, 1},
-       {    0x1E00,     0x1E9F, 1},
-       {    0x1EA0,     0x1EFF, 1},
-       {    0x1F00,     0x1F15, 1},
-       {    0x1F18,     0x1F1D, 1},
-       {    0x1F20,     0x1F45, 1},
-       {    0x1F48,     0x1F4D, 1},
-       {    0x1F50,     0x1F57, 1},
-       {    0x1F59,          0, 0},
-       {    0x1F5B,          0, 0},
-       {    0x1F5D,          0, 0},
-       {    0x1F5F,     0x1F7D, 1},
-       {    0x1F80,     0x1FB4, 1},
-       {    0x1FB6,     0x1FBC, 1},
-       {    0x1FBE,          0, 0},
-       {    0x1FC2,     0x1FC4, 1},
-       {    0x1FC6,     0x1FCC, 1},
-       {    0x1FD0,     0x1FD3, 1},
-       {    0x1FD6,     0x1FDB, 1},
-       {    0x1FE0,     0x1FEC, 1},
-       {    0x1FF2,     0x1FF4, 1},
-       {    0x1FF6,     0x1FFC, 1},
-       {    0x2071,          0, 0},
-       {    0x207F,          0, 0},
-       {    0x2090,     0x2094, 1},
-       {    0x2102,          0, 0},
-       {    0x2107,          0, 0},
-       {    0x210A,     0x2113, 1},
-       {    0x2115,          0, 0},
-       {    0x2119,     0x211D, 1},
-       {    0x2124,          0, 0},
-       {    0x2126,          0, 0},
-       {    0x2128,     0x212D, 1},
-       {    0x212F,     0x2139, 1},
-       {    0x213C,     0x213F, 1},
-       {    0x2145,     0x2149, 1},
-       {    0x214E,          0, 0},
-       {    0x2160,     0x2188, 1},
-       {    0x249C,     0x24E9, 1},
-       {    0x2C00,     0x2C2E, 1},
-       {    0x2C30,     0x2C5E, 1},
-       {    0x2C60,     0x2C6F, 1},
-       {    0x2C71,     0x2C7D, 1},
-       {    0x2C80,     0x2CE4, 1},
-       {    0x2D00,     0x2D25, 1},
-       {    0x2D30,     0x2D65, 1},
-       {    0x2D6F,          0, 0},
-       {    0x2D80,     0x2D96, 1},
-       {    0x2DA0,     0x2DA6, 1},
-       {    0x2DA8,     0x2DAE, 1},
-       {    0x2DB0,     0x2DB6, 1},
-       {    0x2DB8,     0x2DBE, 1},
-       {    0x2DC0,     0x2DC6, 1},
-       {    0x2DC8,     0x2DCE, 1},
-       {    0x2DD0,     0x2DD6, 1},
-       {    0x2DD8,     0x2DDE, 1},
-       {    0x3005,     0x3007, 1},
-       {    0x3021,     0x3029, 1},
-       {    0x3031,     0x3035, 1},
-       {    0x3038,     0x303C, 1},
-       {    0x3041,     0x3096, 1},
-       {    0x309D,     0x309F, 1},
-       {    0x30A1,     0x30FA, 1},
-       {    0x30FC,     0x30FF, 1},
-       {    0x3105,     0x312D, 1},
-       {    0x3131,     0x318E, 1},
-       {    0x31A0,     0x31B7, 1},
-       {    0x31F0,     0x31FF, 1},
-       {    0x3400,     0x4DB5, 1},
-       {    0x4E00,     0x9FBB, 1},
-       {    0xA000,     0xA48C, 1},
-       {    0xA500,     0xA60B, 1},
-       {    0xA610,     0xA61F, 1},
-       {    0xA62A,     0xA62B, 1},
-       {    0xA640,     0xA65F, 1},
-       {    0xA662,     0xA66E, 1},
-       {    0xA680,     0xA697, 1},
-       {    0xA717,     0xA71F, 1},
-       {    0xA722,     0xA78C, 1},
-       {    0xA7FB,     0xA7FF, 1},
-       {    0xA800,          0, 0},
-       {    0xA801,          0, 0},
-       {    0xA803,     0xA805, 1},
-       {    0xA807,     0xA80A, 1},
-       {    0xA80C,     0xA822, 1},
-       {    0xA840,     0xA873, 1},
-       {    0xA882,     0xA8B3, 1},
-       {    0xA90A,     0xA92D, 1},
-       {    0xA930,     0xA946, 1},
-       {    0xAA00,     0xAA28, 1},
-       {    0xAA40,     0xAA42, 1},
-       {    0xAA44,     0xAA4B, 1},
-       {    0xAC00,     0xD7A3, 1},
-       {    0xF900,     0xFA2D, 1},
-       {    0xFA30,     0xFA6A, 1},
-       {    0xFA70,     0xFAD9, 1},
-       {    0xFB00,     0xFB06, 1},
-       {    0xFB13,     0xFB17, 1},
-       {    0xFB1D,          0, 0},
-       {    0xFB1F,     0xFB28, 1},
-       {    0xFB2A,     0xFB36, 1},
-       {    0xFB38,     0xFB3C, 1},
-       {    0xFB3E,          0, 0},
-       {    0xFB40,          0, 0},
-       {    0xFB41,          0, 0},
-       {    0xFB43,          0, 0},
-       {    0xFB44,          0, 0},
-       {    0xFB46,     0xFB4F, 1},
-       {    0xFB50,     0xFBB1, 1},
-       {    0xFBD3,     0xFD3D, 1},
-       {    0xFD50,     0xFD8F, 1},
-       {    0xFD92,     0xFDC7, 1},
-       {    0xFDF0,     0xFDFB, 1},
-       {    0xFE70,     0xFE74, 1},
-       {    0xFE76,     0xFEFC, 1},
-       {    0xFF21,     0xFF3A, 1},
-       {    0xFF41,     0xFF5A, 1},
-       {    0xFF66,     0xFFBE, 1},
-       {    0xFFC2,     0xFFC7, 1},
-       {    0xFFCA,     0xFFCF, 1},
-       {    0xFFD2,     0xFFD7, 1},
-       {    0xFFDA,     0xFFDC, 1},
-       {0x00010000, 0x0001000B, 1},
-       {0x0001000D, 0x00010026, 1},
-       {0x00010028, 0x0001003A, 1},
-       {0x0001003C, 0x0001003D, 1},
-       {0x0001003F, 0x0001004D, 1},
-       {0x00010050, 0x0001005D, 1},
-       {0x00010080, 0x000100FA, 1},
-       {0x00010140, 0x00010174, 1},
-       {0x00010280, 0x0001029C, 1},
-       {0x000102A0, 0x000102D0, 1},
-       {0x00010300, 0x0001031E, 1},
-       {0x00010330, 0x0001034A, 1},
-       {0x00010380, 0x0001039D, 1},
-       {0x000103A0, 0x000103C3, 1},
-       {0x000103C8, 0x000103CF, 1},
-       {0x000103D1, 0x000103D5, 1},
-       {0x00010400, 0x0001044F, 1},
-       {0x00010450, 0x0001047F, 1},
-       {0x00010480, 0x0001049D, 1},
-       {0x000104A0, 0x000104A9, 1},
-       {0x00010800, 0x00010805, 1},
-       {0x00010808,          0, 0},
-       {0x0001080A, 0x00010835, 1},
-       {0x00010837, 0x00010838, 1},
-       {0x0001083C,          0, 0},
-       {0x0001083F,          0, 0},
-       {0x00010900, 0x00010915, 1},
-       {0x00010A00,          0, 0},
-       {0x00010A10, 0x00010A13, 1},
-       {0x00010A15, 0x00010A17, 1},
-       {0x00010A19, 0x00010A33, 1},
-       {0x00012000, 0x0001236E, 1},
-       {0x00012400, 0x00012462, 1},
-       {0x0001D400, 0x0001D454, 1},
-       {0x0001D456, 0x0001D49C, 1},
-       {0x0001D49E, 0x0001D49F, 1},
-       {0x0001D4A2,          0, 0},
-       {0x0001D4A5, 0x0001D4A6, 1},
-       {0x0001D4A9, 0x0001D4AC, 1},
-       {0x0001D4AE, 0x0001D4B9, 1},
-       {0x0001D4BB,          0, 0},
-       {0x0001D4BD, 0x0001D4C3, 1},
-       {0x0001D4C5, 0x0001D505, 1},
-       {0x0001D507, 0x0001D50A, 1},
-       {0x0001D50D, 0x0001D514, 1},
-       {0x0001D516, 0x0001D51C, 1},
-       {0x0001D51E, 0x0001D539, 1},
-       {0x0001D53B, 0x0001D53E, 1},
-       {0x0001D540, 0x0001D544, 1},
-       {0x0001D546,          0, 0},
-       {0x0001D54A, 0x0001D550, 1},
-       {0x0001D552, 0x0001D6A5, 1},
-       {0x0001D6A8, 0x0001D6C0, 1},
-       {0x0001D6C2, 0x0001D6DA, 1},
-       {0x0001D6DC, 0x0001D6FA, 1},
-       {0x0001D6FC, 0x0001D714, 1},
-       {0x0001D716, 0x0001D734, 1},
-       {0x0001D736, 0x0001D74E, 1},
-       {0x0001D750, 0x0001D76E, 1},
-       {0x0001D770, 0x0001D788, 1},
-       {0x0001D78A, 0x0001D7A8, 1},
-       {0x0001D7AA, 0x0001D7C2, 1},
-       {0x0001D7C4, 0x0001D7CB, 1},
-       {0x0001D7CE, 0x0001D7FF, 1},
-       {0x00020000, 0x0002A6D6, 1},
-       {0x0002F800, 0x0002FA1D, 1},
-       {    0x0660,     0x0669, 1},
-       {    0x06F0,     0x06F9, 1},
-       {    0x0966,     0x096F, 1},
-       {    0x09E6,     0x09EF, 1},
-       {    0x0A66,     0x0A6F, 1},
-       {    0x0AE6,     0x0AEF, 1},
-       {    0x0B66,     0x0B6F, 1},
-       {    0x0BE6,     0x0BEF, 1},
-       {    0x0C66,     0x0C6F, 1},
-       {    0x0C78,     0x0C7F, 1},
-       {    0x0CE6,     0x0CEF, 1},
-       {    0x0D66,     0x0D75, 1},
-       {    0x0D70,     0x0D75, 1},
-       {    0x0E50,     0x0E59, 1},
-       {    0x0ED0,     0x0ED9, 1},
-       {    0x0F20,     0x0F29, 1},
-       {    0x1040,     0x1049, 1},
-       {    0x17E0,     0x17E9, 1},
-       {    0x1810,     0x1819, 1},
-       {    0x1BB0,     0x1BB9, 1},
-       {    0x1C40,     0x1C49, 1},
-       {    0x1C50,     0x1C59, 1},
-       {    0xA620,     0xA629, 1},
-       {    0xA8D0,     0xA8D9, 1},
-       {    0xA900,     0xA909, 1},
-       {    0xAA50,     0xAA59, 1},
-       {    0xFF10,     0xFF19, 1},
-
-/* END::UTF8TABLE */
-};
-
-static inline size_t utf8towc(unsigned *wc, const unsigned char *uc, size_t len)
-{
-       unsigned char ub = utf8_mblen[*uc];
-
-       if (!ub || ub > len || ub > 4) {
-               return 0;
-       }
-
-       *wc = *uc & utf8_mask[ub];
-
-       switch (ub) {
-       case 4:
-               if ((uc[1] & 0xc0) != 0x80) {
-                       return 0;
-               }
-               *wc <<= 6;
-               *wc += *++uc & 0x3f;
-               /* no break */
-       case 3:
-               if ((uc[1] & 0xc0) != 0x80) {
-                       return 0;
-               }
-               *wc <<= 6;
-               *wc += *++uc & 0x3f;
-               /* no break */
-       case 2:
-               if ((uc[1] & 0xc0) != 0x80) {
-                       return 0;
-               }
-               *wc <<= 6;
-               *wc += *++uc & 0x3f;
-               /* no break */
-       case 1:
-               break;
-
-       default:
-               return 0;
-       }
-
-       return ub;
-}
-
-static inline zend_bool isualpha(unsigned ch)
-{
-       unsigned i = 0, j;
-
-       PHP_HTTP_DUFF(sizeof(utf8_ranges)/sizeof(utf8_range_t),
-               if (utf8_ranges[i].start == ch) {
-                       return 1;
-               } else if (utf8_ranges[i].start <= ch && utf8_ranges[i].end >= ch) {
-                       if (utf8_ranges[i].step == 1) {
-                               return 1;
-                       }
-                       for (j = utf8_ranges[i].start; j <= utf8_ranges[i].end; j+= utf8_ranges[i].step) {
-                               if (ch == j) {
-                                       return 1;
-                               }
-                       }
-                       return 0;
-               }
-               ++i;
-       );
-       return 0;
-}
-
-static inline zend_bool isualnum(unsigned ch)
-{
-       /* digits */
-       if (ch >= 0x30 && ch <= 0x39) {
-               return 1;
-       }
-       return isualpha(ch);
-}
-
-static inline size_t wctoutf16(unsigned short u16[2], unsigned wc)
-{
-       if (wc > 0x10ffff || (wc >= 0xd800 && wc <= 0xdfff)) {
-               return 0;
-       }
-
-       if (wc <= 0xffff) {
-               u16[0] = (unsigned short) wc;
-               return 1;
-       }
-
-       wc -= 0x10000;
-       u16[0] = (unsigned short) ((wc >> 10) + 0xd800);
-       u16[1] = (unsigned short) ((wc & 0x3ff) + 0xdc00);
-       return 2;
-}
-
-static inline size_t utf16towc(unsigned *wc, unsigned short *u16_str, size_t u16_len)
-{
-       if (u16_len < 1) {
-               return 0;
-       }
-       if (u16_str[0] - 0xd800 >= 0x800) {
-               *wc = u16_str[0];
-               return 1;
-       }
-       if (u16_len < 2 || (u16_str[0] & 0xfffffc00) != 0xd800 || (u16_str[1] & 0xfffffc00) != 0xdc00) {
-               return 0;
-       }
-       *wc = (u16_str[0] << 10) + u16_str[1] - 0x35fdc00;
-       return 2;
-}
-
-#endif /* PHP_HTTP_UTF8_H */
-
-/*
- * 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/php_http_version.c b/php_http_version.c
deleted file mode 100644 (file)
index 7adef9d..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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"
-
-php_http_version_t *php_http_version_init(php_http_version_t *v, unsigned major, unsigned minor TSRMLS_DC)
-{
-       if (!v) {
-               v = emalloc(sizeof(*v));
-       }
-
-       v->major = major;
-       v->minor = minor;
-
-       return v;
-}
-
-php_http_version_t *php_http_version_parse(php_http_version_t *v, const char *str TSRMLS_DC)
-{
-       long major, minor;
-       char separator = 0;
-       register const char *ptr = str;
-
-       switch (*ptr) {
-       case 'h':
-       case 'H':
-               ++ptr; if (*ptr != 't' && *ptr != 'T') break;
-               ++ptr; if (*ptr != 't' && *ptr != 'T') break;
-               ++ptr; if (*ptr != 'p' && *ptr != 'P') break;
-               ++ptr; if (*ptr != '/') break;
-               ++ptr;
-               /* no break */
-       default:
-               /* rfc7230#2.6 The HTTP version number consists of two decimal digits separated by a "." (period or decimal point) */
-               major = *ptr++ - '0';
-               if (major >= 0 && major <= 9) {
-                       separator = *ptr++;
-                       if (separator) {
-                               if (separator != '.' && separator != ',') {
-                                       php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Non-standard version separator '%c' in HTTP protocol version '%s'", separator, ptr - 2);
-                               }
-                               minor = *ptr - '0';
-                               if (minor >= 0 && minor <= 9) {
-                                       return php_http_version_init(v, major, minor TSRMLS_CC);
-                               }
-                       }
-               }
-       }
-
-       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not parse HTTP protocol version '%s'", str);
-       return NULL;
-}
-
-void php_http_version_to_string(php_http_version_t *v, char **str, size_t *len, const char *pre, const char *post TSRMLS_DC)
-{
-       *len = spprintf(str, 0, "%s%u.%u%s", pre ? pre : "", v->major, v->minor, post ? post : "");
-}
-
-void php_http_version_dtor(php_http_version_t *v)
-{
-       (void) v;
-}
-
-void php_http_version_free(php_http_version_t **v)
-{
-       if (*v) {
-               php_http_version_dtor(*v);
-               efree(*v);
-               *v = NULL;
-       }
-}
-
-/*
- * 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/php_http_version.h b/php_http_version.h
deleted file mode 100644 (file)
index 40b833e..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-    +--------------------------------------------------------------------+
-    | 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_VERSION_H
-#define        PHP_HTTP_VERSION_H
-
-typedef struct php_http_version {
-       unsigned major;
-       unsigned minor;
-} php_http_version_t;
-
-PHP_HTTP_API php_http_version_t *php_http_version_init(php_http_version_t *v, unsigned major, unsigned minor TSRMLS_DC);
-PHP_HTTP_API php_http_version_t *php_http_version_parse(php_http_version_t *v, const char *str TSRMLS_DC);
-PHP_HTTP_API void php_http_version_to_string(php_http_version_t *v, char **str, size_t *len, const char *pre, const char *post TSRMLS_DC);
-PHP_HTTP_API void php_http_version_dtor(php_http_version_t *v);
-PHP_HTTP_API void php_http_version_free(php_http_version_t **v);
-
-#endif /* PHP_HTTP_VERSION_H */
-
-
-/*
- * 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/reflection2php.php b/reflection2php.php
deleted file mode 100755 (executable)
index 20a1f0f..0000000
+++ /dev/null
@@ -1,205 +0,0 @@
-#!/usr/bin/env php
-<?php
-
-function m($m) {
-       $n = "";
-       foreach (Reflection::getModifierNames($m) as $mn) {
-               $n .= $mn . " ";
-       }
-       return $n;
-}
-function t($p) {
-       if ($c = $p->getClass()) return "\\" . $c->getName() . " ";
-       if ($p->isArray()) return "array ";
-}
-function c($n, $c) {
-    $_=$c;
-    while ($c = $c->getParentClass()) {
-        if (array_key_exists($n, $c->getConstants())) {
-            return false;
-        }
-    }
-    $c=$_;
-    foreach ((array) $c->getInterfaces() as $i) {
-        if (array_key_exists($n, $i->getConstants()) || !c($n, $i)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-ob_start(function($s) {
-    // redirect any output to stderr
-    fwrite(STDERR, $s);
-    return true;
-});
-
-$out = STDOUT;
-switch ($argc) {
-    default:
-    case 3:
-        $out = fopen($argv[2], "w") or die;
-    case 2:
-        $ext = $argv[1];
-        break;
-    
-    case 1:
-        die(sprintf($out, "Usage: %s <ext>\n", $argv[0]));
-}
-
-fprintf($out, "<?php\n\n");
-
-$ext = new ReflectionExtension($ext);
-
-$constants  = array();
-$functions  = array();
-$structures = array();
-
-// split up by namespace first
-foreach ($ext->getConstants() as $constant => $value) {
-    $ns = ($nsend = strrpos($constant, "\\")) ? substr($constant, 0, $nsend++) : "";
-    $cn = substr($constant, $nsend);
-    $constants[$ns][$cn] = $value;
-}
-foreach ($ext->getFunctions() as $f) {
-    /* @var $f ReflectionFunction */
-    $ns = $f->inNamespace() ? $f->getNamespaceName() : "";
-    $functions[$ns][$f->getShortName()] = $f;
-}
-foreach ($ext->getClasses() as $c) {
-    /* @var $c ReflectionClass */
-    $ns = $c->inNamespace() ? $c->getNamespaceName() : "";
-    $structures[$ns][$c->getShortName()] = $c;
-}
-
-$namespaces = array_unique(array_merge(
-        array_keys($constants),
-        array_keys($functions),
-        array_keys($structures)
-));
-
-// simple sort
-natsort($namespaces);
-
-foreach ($namespaces as $ns) {
-    fprintf($out, "namespace %s%s\n{\n", $ns, strlen($ns) ? " " : "");
-    //
-    if (isset($constants[$ns])) {
-        ksort($constants[$ns], SORT_NATURAL);
-        foreach ($constants[$ns] as $cn => $value) {
-            fprintf($out, "\tconst %s = %s;\n", $cn, var_export($value, true));
-        }
-    }
-    //
-    if (isset($functions[$ns])) {
-        ksort($functions[$ns], SORT_NATURAL);
-        foreach ($functions[$ns] as $fn => $f) {
-            /* @var $f ReflectionFunction */
-            fprintf($out, "\n\tfunction %s(", $fn);
-            $ps = array();
-            foreach ($f->getParameters() as $p) {
-                $p1 = sprintf("%s%s\$%s", t($p), 
-                        $p->isPassedByReference()?"&":"", trim($p->getName(), "\""));
-                if ($p->isOptional()) {
-                    if ($p->isDefaultValueAvailable()) {
-                        $p1 .= sprintf(" = %s", 
-                                var_export($p->getDefaultValue(), true));
-                    } elseif (!($p->isArray() || $p->getClass()) || $p->allowsNull()) {
-                        $p1 .= " = NULL";
-                    } elseif ($p->isArray()) {
-                        $p1 .= " = array()";
-                    }
-                }
-                $ps[] = $p1;
-            }
-            fprintf($out, "%s) {\n\t}\n", implode(", ", $ps));
-        }
-    }
-    //
-    if (isset($structures[$ns])) {
-        uasort($structures[$ns], function ($a, $b) {
-            /* @var $a ReflectionClass */
-            /* @var $b ReflectionClass */
-            $score = array_sum([
-                -!$a->isInterface()+
-                -!$a->isAbstract()+
-                -!$a->isTrait()+
-                -!substr_compare($a->getShortName(), "Exception", -strlen("Exception")),
-                +!$b->isInterface()+
-                +!$b->isAbstract()+
-                +!$b->isTrait()+
-                -!substr_compare($b->getShortName(), "Exception", -strlen("Exception")),
-            ]);
-            
-            if ($score) {
-                return -$score;
-            }
-            return strnatcmp($a->getShortName(), $b->getShortName());
-        });
-        foreach ($structures[$ns] as $cn => $c) {
-            fprintf($out, "\n\t%s%s %s ", m($c->getModifiers()), 
-                    $c->isInterface() ? "interface":"class", $c->getShortName());
-            if ($p = $c->getParentClass()) {
-                fprintf($out, "extends \\%s ", $p->getName());
-            }
-            if ($i = $c->getInterfaceNames()) {
-                fprintf($out, "implements \\%s ", 
-                        implode(", \\", array_filter($i, function($v) {
-                            return $v != "Traversable";
-                            
-                        }))
-                );
-            }
-            fprintf($out, "\n\t{\n");
-
-            $_=0;
-            foreach ($c->getConstants() as $n => $v) {
-                c($n, $c) and $_+=fprintf($out, "\t\tconst %s = %s;\n", $n, 
-                        var_export($v, true));
-            }
-            $_ and fprintf($out, "\n");
-            $_=0;
-            foreach ($c->getProperties() as $p) {
-                if ($p->getDeclaringClass()->getName() == $c->getName()) {
-                    $_+=fprintf($out, "\t\t%s\$%s;\n", m($p->getModifiers()), 
-                            $p->getName());
-                }
-            }
-            $_ and fprintf($out, "\n");
-
-            foreach ($c->getMethods() as $m) {
-                if ($m->getDeclaringClass()->getName() == $c->getName()) {
-                    fprintf($out, "\t\t%sfunction %s(", m($m->getModifiers()), 
-                            $m->getName());
-                    $ps = array();
-                    foreach ($m->getParameters() as $p) {
-                        $p1 = sprintf("%s%s\$%s", t($p), 
-                                $p->isPassedByReference()?"&":"", $p->getName());
-                        if ($p->isOptional()) {
-                            if ($p->isDefaultValueAvailable()) {
-                                $p1 .= sprintf(" = %s", 
-                                        var_export($p->getDefaultValue(), true));
-                            } elseif (!($p->isArray() || $p->getClass()) || $p->allowsNull()) {
-                                $p1 .= sprintf(" = NULL");
-                            } elseif ($p->isArray()) {
-                                $p1 .= " = array()";
-                            }
-                        }
-                        $ps[] = $p1;
-                    }
-                    fprintf($out, "%s)", implode(", ", $ps));
-                    if ($m->isAbstract()) {
-                        fprintf($out, ";\n\n");
-                    } else {
-                        fprintf($out, " {\n\t\t}\n\n");
-                    }
-                }
-            }
-
-            fprintf($out, "\t}\n");
-            
-        }
-    }
-    //
-    fprintf($out, "}\n\n");
-}
diff --git a/scripts/bench_select_vs_event.php b/scripts/bench_select_vs_event.php
new file mode 100644 (file)
index 0000000..3bc9aed
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+
+function usage($e = null) {
+    global $argv;
+    if ($e) {
+        fprintf(STDERR, "ERROR: %s\n\n", $e);
+    }
+       fprintf(STDERR, "Usage: %s -u <URL> -n <requests> -c <concurrency> [-p (enable pipelining)] [-e (use libevent)]\n", $argv[0]);
+       fprintf(STDERR, "\nDefaults: -u http://localhost/ -n 1000 -c 10\n\n");
+       exit(-1);
+}
+
+function push($client, $url, &$n) {
+       if ($n-- > 0) {
+               $req = new http\Client\Request("GET", $url);
+               $client->enqueue($req, function($response) use ($client, $req, $url, &$n) {
+                       global $count; ++$count;
+                       push($client, $url, $n);
+                       return true; // dequeue
+               });
+       }
+}
+
+isset($argv) or $argv = $_SERVER['argv'];
+defined('STDERR') or define('STDERR', fopen('php://stderr', 'w'));
+
+$opts = getopt("u:c:n:e");
+isset($opts["u"]) or $opts["u"] = "http://localhost/";
+isset($opts["c"]) or $opts["c"] = 10;
+isset($opts["n"]) or $opts["n"] = 1000;
+
+$argc > 1 or usage();
+
+$time = microtime(true);
+$count = 0;
+$client = new http\Client;
+
+$client->enablePipelining($opts["p"]===false);
+$client->enableEvents($opts["e"]===false);
+
+for ($i = 0, $x = $opts["n"]; $i < $opts["c"]; ++$i) {
+       push($client, $opts["u"], $x);
+}
+
+try {
+       $client->send();
+} catch (Exception $e) {
+       echo $e;
+}
+
+printf("\n> %10.6fs (%3.2fM)\n", microtime(true)-$time, memory_get_peak_usage(true)/1024/1024);
+
+$count == $opts["n"] or printf("\nOnly %d finished\n", $count);
diff --git a/scripts/check_package-xml.php b/scripts/check_package-xml.php
new file mode 100755 (executable)
index 0000000..e422c6a
--- /dev/null
@@ -0,0 +1,122 @@
+#!/usr/bin/env php
+<?php
+
+ini_set("log_errors", false);
+ini_set("display_errors", true);
+
+if ($argc > 1) {
+       if ($argv[1] === "-") {
+               $file = "php://stdin";
+       } else {
+               $file = $argv[1];
+       }
+} elseif (stdin_is_readable()) {
+       $file = "php://stdin";
+} else {
+       $file = "./package.xml";
+}
+
+if (($xml = simplexml_load_file($file))) {
+       $xml_files = xmllist($xml->contents[0]);
+       $dirs = ["."];
+       while ($dir = array_shift($dirs)) {
+               foreach (dirlist($dir) as $file) {
+                       if (is_gitignored($file)) {
+                               continue;
+                       }
+                       if (is_commonly_ignored($file)) {
+                               continue;
+                       }
+                       if (!is_dir($file)) {
+                               if (!in_array($file, $xml_files)) {
+                                       echo "Missing file $file\n";
+                               }
+                       } else {
+                               $base = basename($file);
+                               if ($base{0} !== ".") {
+                                       array_push($dirs, $file);
+                               }
+                       }
+               }
+       }
+       foreach ($xml_files as $file) {
+               if (!file_exists($file)) {
+                       echo "Extraneous file $file\n";
+               }
+       }
+}
+
+###
+
+function error($fmt) {
+       trigger_error(call_user_func_array("sprintf", func_get_args()));
+}
+
+function stdin_is_readable() {
+       $r = [STDIN]; $w = $e = [];
+       return stream_select($r, $w, $e, 0);
+}
+
+function is_gitignored($file) {
+       static $gitignore, $gitmodules;
+
+       if (!isset($gitmodules)) {
+               if (is_readable("./.gitmodules")) {
+                       $gitmodules = explode("\n", `git submodule status | awk '{printf$2}'`);
+               } else {
+                       $gitmodules = false;
+               }
+       }
+       if (!isset($gitignore)) {
+               if (is_readable("./.gitignore")) {
+                       $ignore_submodules = $gitmodules ? " ! -path './".implode("/*' ! -path './", $gitmodules)."/*'" : "";
+                       $gitignore = explode("\n", `find . $ignore_submodules | git check-ignore --stdin`);
+               } else {
+                       $gitignore = false;
+               }
+       }
+       if ($gitignore) {
+               if (in_array($file, $gitignore)) {
+                       return true;
+               }
+       }
+       if ($gitmodules) {
+               foreach ($gitmodules as $module) {
+                       if (fnmatch("./$module/*", $file)) {
+                               return true;
+                       }
+               }
+       }
+       return false;
+}
+
+function is_commonly_ignored($file) {
+       return fnmatch("./.git*", $file)
+               || in_array($file, ["./package.xml", "./package2.xml", "./.travis.yml", "./.editorconfig"], true);
+}
+
+function xmllist(SimpleXmlElement $dir, $p = ".", &$a = null) {
+       settype($a, "array");
+       $p = trim($p, "/") . "/" . trim($dir["name"], "/") . "/";
+       foreach ($dir as $file) {
+               switch ($file->getName()) {
+                       case "dir":
+                               xmllist($file, $p, $a);
+                               break;
+                       case "file":
+                               $a[] = sprintf("%s/%s", trim($p, "/"), trim($file["name"]));
+                               break;
+                       default:
+                               error("Unknown content type: %s", $file->getName());
+                               break;
+               }
+       }
+       return $a;
+}
+
+function dirlist($dir, $p = null) {
+       $p = implode("/", array_filter([trim($p, "/"), trim($dir, "/")]));
+       foreach (scandir($p) as $file) {
+               yield $p."/".$file;
+       }
+}
diff --git a/scripts/gen_curlinfo.php b/scripts/gen_curlinfo.php
new file mode 100755 (executable)
index 0000000..076a10d
--- /dev/null
@@ -0,0 +1,99 @@
+#!/usr/bin/env php
+<?php
+// $Id: gen_curlinfo.php 323304 2012-02-17 21:13:24Z mike $
+
+error_reporting(0);
+
+function failure() {
+       // this is why error_get_last() should return a stdClass object
+       $error = error_get_last();
+       fprintf(STDERR, "FAILURE: %s\n", $error["message"]);
+       exit(-1);
+}
+
+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)) {
+                               return $matches;
+                       }
+               } else {
+                       if (preg_match($pattern, $content, $matches)) {
+                               return $matches;
+                       }
+               }
+               trigger_error("no match in $file for $pattern");
+       }
+       failure();
+}
+
+$ifdefs = array(
+       'PRIMARY_IP' => 'PHP_HTTP_CURL_VERSION(7,19,0)',
+       'APPCONNECT_TIME' => 'PHP_HTTP_CURL_VERSION(7,19,0)',
+    'CONDITION_UNMET' => 'PHP_HTTP_CURL_VERSION(7,19,4)',
+    '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)',
+);
+$exclude = array(
+    'PRIVATE', 'LASTSOCKET', 'FTP_ENTRY_PATH', 'CERTINFO', 'TLS_SESSION',
+    'RTSP_SESSION_ID', 'RTSP_CLIENT_CSEQ', 'RTSP_SERVER_CSEQ', 'RTSP_CSEQ_RECV',
+       'COOKIELIST'
+);
+
+$translate = array(
+       'HTTP_CONNECTCODE' => "connect_code",
+       'COOKIELIST' => 'cookies',
+);
+
+$templates = array(
+'STRING' => 
+'      if (CURLE_OK == curl_easy_getinfo(ch, %s, &c)) {
+               add_assoc_string_ex(&array, "%s", sizeof("%2$s"), c ? c : "", 1);
+       }
+',
+'DOUBLE' => 
+'      if (CURLE_OK == curl_easy_getinfo(ch, %s, &d)) {
+               add_assoc_double_ex(&array, "%s", sizeof("%2$s"), d);
+       }
+',
+'LONG' => 
+'      if (CURLE_OK == curl_easy_getinfo(ch, %s, &l)) {
+               add_assoc_long_ex(&array, "%s", sizeof("%2$s"), l);
+       }
+',
+'SLIST' =>
+'      if (CURLE_OK == curl_easy_getinfo(ch, %s, &s)) {
+               MAKE_STD_ZVAL(subarray);
+               array_init(subarray);
+               for (p = s; p; p = p->next) {
+                       if (p->data) {
+                               add_next_index_string(subarray, p->data, 1);
+                       }
+               }
+               add_assoc_zval_ex(&array, "%s", sizeof("%2$s"), subarray);
+               curl_slist_free_all(s);
+       }
+',
+);
+
+$infos = file_re('curl.h', '/^\s*(CURLINFO_(\w+))\s*=\s*CURLINFO_(STRING|LONG|DOUBLE|SLIST)\s*\+\s*\d+\s*,?\s*$/m');
+
+ob_start();
+foreach ($infos as $info) {
+       list(, $full, $short, $type) = $info;
+       if (in_array($short, $exclude)) continue;
+       if (isset($ifdefs[$short])) printf("#if %s\n", $ifdefs[$short]);
+       printf($templates[$type], $full, strtolower((isset($translate[$short])) ? $translate[$short] : $short));
+       if (isset($ifdefs[$short])) printf("#endif\n");
+}
+
+file_put_contents("php_http_client_curl.c", 
+       preg_replace('/(\/\* BEGIN::CURLINFO \*\/\n).*(\n\s*\/\* END::CURLINFO \*\/)/s', '$1'. ob_get_contents() .'$2',
+               file_get_contents("php_http_client_curl.c")));
+
+?>
diff --git a/scripts/gen_stubs.php b/scripts/gen_stubs.php
new file mode 100755 (executable)
index 0000000..20a1f0f
--- /dev/null
@@ -0,0 +1,205 @@
+#!/usr/bin/env php
+<?php
+
+function m($m) {
+       $n = "";
+       foreach (Reflection::getModifierNames($m) as $mn) {
+               $n .= $mn . " ";
+       }
+       return $n;
+}
+function t($p) {
+       if ($c = $p->getClass()) return "\\" . $c->getName() . " ";
+       if ($p->isArray()) return "array ";
+}
+function c($n, $c) {
+    $_=$c;
+    while ($c = $c->getParentClass()) {
+        if (array_key_exists($n, $c->getConstants())) {
+            return false;
+        }
+    }
+    $c=$_;
+    foreach ((array) $c->getInterfaces() as $i) {
+        if (array_key_exists($n, $i->getConstants()) || !c($n, $i)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+ob_start(function($s) {
+    // redirect any output to stderr
+    fwrite(STDERR, $s);
+    return true;
+});
+
+$out = STDOUT;
+switch ($argc) {
+    default:
+    case 3:
+        $out = fopen($argv[2], "w") or die;
+    case 2:
+        $ext = $argv[1];
+        break;
+    
+    case 1:
+        die(sprintf($out, "Usage: %s <ext>\n", $argv[0]));
+}
+
+fprintf($out, "<?php\n\n");
+
+$ext = new ReflectionExtension($ext);
+
+$constants  = array();
+$functions  = array();
+$structures = array();
+
+// split up by namespace first
+foreach ($ext->getConstants() as $constant => $value) {
+    $ns = ($nsend = strrpos($constant, "\\")) ? substr($constant, 0, $nsend++) : "";
+    $cn = substr($constant, $nsend);
+    $constants[$ns][$cn] = $value;
+}
+foreach ($ext->getFunctions() as $f) {
+    /* @var $f ReflectionFunction */
+    $ns = $f->inNamespace() ? $f->getNamespaceName() : "";
+    $functions[$ns][$f->getShortName()] = $f;
+}
+foreach ($ext->getClasses() as $c) {
+    /* @var $c ReflectionClass */
+    $ns = $c->inNamespace() ? $c->getNamespaceName() : "";
+    $structures[$ns][$c->getShortName()] = $c;
+}
+
+$namespaces = array_unique(array_merge(
+        array_keys($constants),
+        array_keys($functions),
+        array_keys($structures)
+));
+
+// simple sort
+natsort($namespaces);
+
+foreach ($namespaces as $ns) {
+    fprintf($out, "namespace %s%s\n{\n", $ns, strlen($ns) ? " " : "");
+    //
+    if (isset($constants[$ns])) {
+        ksort($constants[$ns], SORT_NATURAL);
+        foreach ($constants[$ns] as $cn => $value) {
+            fprintf($out, "\tconst %s = %s;\n", $cn, var_export($value, true));
+        }
+    }
+    //
+    if (isset($functions[$ns])) {
+        ksort($functions[$ns], SORT_NATURAL);
+        foreach ($functions[$ns] as $fn => $f) {
+            /* @var $f ReflectionFunction */
+            fprintf($out, "\n\tfunction %s(", $fn);
+            $ps = array();
+            foreach ($f->getParameters() as $p) {
+                $p1 = sprintf("%s%s\$%s", t($p), 
+                        $p->isPassedByReference()?"&":"", trim($p->getName(), "\""));
+                if ($p->isOptional()) {
+                    if ($p->isDefaultValueAvailable()) {
+                        $p1 .= sprintf(" = %s", 
+                                var_export($p->getDefaultValue(), true));
+                    } elseif (!($p->isArray() || $p->getClass()) || $p->allowsNull()) {
+                        $p1 .= " = NULL";
+                    } elseif ($p->isArray()) {
+                        $p1 .= " = array()";
+                    }
+                }
+                $ps[] = $p1;
+            }
+            fprintf($out, "%s) {\n\t}\n", implode(", ", $ps));
+        }
+    }
+    //
+    if (isset($structures[$ns])) {
+        uasort($structures[$ns], function ($a, $b) {
+            /* @var $a ReflectionClass */
+            /* @var $b ReflectionClass */
+            $score = array_sum([
+                -!$a->isInterface()+
+                -!$a->isAbstract()+
+                -!$a->isTrait()+
+                -!substr_compare($a->getShortName(), "Exception", -strlen("Exception")),
+                +!$b->isInterface()+
+                +!$b->isAbstract()+
+                +!$b->isTrait()+
+                -!substr_compare($b->getShortName(), "Exception", -strlen("Exception")),
+            ]);
+            
+            if ($score) {
+                return -$score;
+            }
+            return strnatcmp($a->getShortName(), $b->getShortName());
+        });
+        foreach ($structures[$ns] as $cn => $c) {
+            fprintf($out, "\n\t%s%s %s ", m($c->getModifiers()), 
+                    $c->isInterface() ? "interface":"class", $c->getShortName());
+            if ($p = $c->getParentClass()) {
+                fprintf($out, "extends \\%s ", $p->getName());
+            }
+            if ($i = $c->getInterfaceNames()) {
+                fprintf($out, "implements \\%s ", 
+                        implode(", \\", array_filter($i, function($v) {
+                            return $v != "Traversable";
+                            
+                        }))
+                );
+            }
+            fprintf($out, "\n\t{\n");
+
+            $_=0;
+            foreach ($c->getConstants() as $n => $v) {
+                c($n, $c) and $_+=fprintf($out, "\t\tconst %s = %s;\n", $n, 
+                        var_export($v, true));
+            }
+            $_ and fprintf($out, "\n");
+            $_=0;
+            foreach ($c->getProperties() as $p) {
+                if ($p->getDeclaringClass()->getName() == $c->getName()) {
+                    $_+=fprintf($out, "\t\t%s\$%s;\n", m($p->getModifiers()), 
+                            $p->getName());
+                }
+            }
+            $_ and fprintf($out, "\n");
+
+            foreach ($c->getMethods() as $m) {
+                if ($m->getDeclaringClass()->getName() == $c->getName()) {
+                    fprintf($out, "\t\t%sfunction %s(", m($m->getModifiers()), 
+                            $m->getName());
+                    $ps = array();
+                    foreach ($m->getParameters() as $p) {
+                        $p1 = sprintf("%s%s\$%s", t($p), 
+                                $p->isPassedByReference()?"&":"", $p->getName());
+                        if ($p->isOptional()) {
+                            if ($p->isDefaultValueAvailable()) {
+                                $p1 .= sprintf(" = %s", 
+                                        var_export($p->getDefaultValue(), true));
+                            } elseif (!($p->isArray() || $p->getClass()) || $p->allowsNull()) {
+                                $p1 .= sprintf(" = NULL");
+                            } elseif ($p->isArray()) {
+                                $p1 .= " = array()";
+                            }
+                        }
+                        $ps[] = $p1;
+                    }
+                    fprintf($out, "%s)", implode(", ", $ps));
+                    if ($m->isAbstract()) {
+                        fprintf($out, ";\n\n");
+                    } else {
+                        fprintf($out, " {\n\t\t}\n\n");
+                    }
+                }
+            }
+
+            fprintf($out, "\t}\n");
+            
+        }
+    }
+    //
+    fprintf($out, "}\n\n");
+}
diff --git a/scripts/gen_travis_yml.php b/scripts/gen_travis_yml.php
new file mode 100755 (executable)
index 0000000..89ab0b2
--- /dev/null
@@ -0,0 +1,45 @@
+#!/usr/bin/env php
+# autogenerated file; do not edit
+language: c
+
+addons:
+ apt:
+  packages:
+   - php5-cli
+   - php-pear
+   - libcurl4-openssl-dev
+   - zlib1g-dev
+   - libidn11-dev
+   - libevent-dev
+
+env:
+<?php
+
+$gen = include "./travis/pecl/gen-matrix.php";
+$env = $gen([
+       "PHP" => ["5.4", "5.5", "5.6"],
+       "enable_debug",
+       "enable_maintainer_zts",
+       "enable_json",
+       "enable_hash" => ["yes"],
+       "enable_iconv" => ["yes"]
+]);
+foreach ($env as $e) {
+       printf(" - %s\n", $e);
+}
+
+?>
+
+before_script:
+ - make -f travis/pecl/Makefile php
+ - make -f travis/pecl/Makefile pecl PECL=raphf
+ - make -f travis/pecl/Makefile pecl PECL=propro
+
+script:
+ - make -f travis/pecl/Makefile ext PECL=http
+ - make -f travis/pecl/Makefile test
+
+after_script:
+ - test -e tests/helper/server.log && cat tests/helper/server.log
+
+sudo: false
diff --git a/scripts/gen_utf8.php b/scripts/gen_utf8.php
new file mode 100755 (executable)
index 0000000..865a2f5
--- /dev/null
@@ -0,0 +1,90 @@
+#!/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, 0xffff);
+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;
+                               }
+                               print "\t{";
+                               if ($start >= 0xffff) {
+                                       printf("0x%08X, ", $start);
+                                       if ($end) {
+                                               printf("0x%08X, ", $end);
+                                       } else {
+                                               print("         0, ");
+                                       }
+                               } else {
+                                       printf("    0x%04X, ", $start);
+                                       if ($end) {
+                                               printf("    0x%04X, ", $end);
+                                       } else {
+                                               print("         0, ");
+                                       }
+                               }
+                               printf("%d},\n", $step);
+                       }
+               }
+               break;
+       default:
+               if ($a) {
+                       break 2;
+               } elseif ($line === "alpha /\n") {
+                       $a = true;
+               }
+               break;
+       }
+}
+
+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")));
diff --git a/src/php_http.c b/src/php_http.c
new file mode 100644 (file)
index 0000000..2ff20f1
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+#include <php_ini.h>
+#include <ext/standard/info.h>
+
+#include <zlib.h>
+
+#if PHP_HTTP_HAVE_CURL
+#      include <curl/curl.h>
+#      if PHP_HTTP_HAVE_EVENT
+#              if PHP_HTTP_HAVE_EVENT2
+#                      include <event2/event.h>
+#                      include <event2/event_struct.h>
+#              else
+#                      include <event.h>
+#              endif
+#      endif
+#endif
+#if PHP_HTTP_HAVE_IDN2
+#      include <idn2.h>
+#elif PHP_HTTP_HAVE_IDN
+#      include <idna.h>
+#endif
+
+ZEND_DECLARE_MODULE_GLOBALS(php_http);
+
+#ifdef COMPILE_DL_HTTP
+ZEND_GET_MODULE(http)
+#endif
+
+zend_function_entry http_functions[] = {
+       EMPTY_FUNCTION_ENTRY
+};
+
+PHP_MINIT_FUNCTION(http);
+PHP_MSHUTDOWN_FUNCTION(http);
+PHP_RSHUTDOWN_FUNCTION(http);
+PHP_MINFO_FUNCTION(http);
+
+static zend_module_dep http_module_deps[] = {
+       ZEND_MOD_REQUIRED("raphf")
+       ZEND_MOD_REQUIRED("propro")
+       ZEND_MOD_REQUIRED("spl")
+#ifdef PHP_HTTP_HAVE_HASH
+       ZEND_MOD_REQUIRED("hash")
+#endif
+#ifdef PHP_HTTP_HAVE_ICONV
+       ZEND_MOD_REQUIRED("iconv")
+#endif
+       {NULL, NULL, NULL, 0}
+};
+
+zend_module_entry http_module_entry = {
+       STANDARD_MODULE_HEADER_EX,
+       NULL,
+       http_module_deps,
+       "http",
+       http_functions,
+       PHP_MINIT(http),
+       PHP_MSHUTDOWN(http),
+       NULL,
+       PHP_RSHUTDOWN(http),
+       PHP_MINFO(http),
+       PHP_PECL_HTTP_VERSION,
+       STANDARD_MODULE_PROPERTIES
+};
+
+int http_module_number;
+
+#if PHP_DEBUG && !HAVE_GCOV
+void _dpf(int type, const char *data, size_t length)
+{
+       static const char _sym[] = "><><><";
+       if (type) {
+               int nwp = 0;
+               for (fprintf(stderr, "%c ", _sym[type-1]); length--; data++) {
+                       int ip = PHP_HTTP_IS_CTYPE(print, *data);
+                       if (!ip && *data != '\r' && *data != '\n') nwp = 1;
+                       fprintf(stderr, ip?"%c":"\\x%02x", (int) (*data & 0xff));
+                       if (!nwp && *data == '\n' && length) {
+                               fprintf(stderr, "\n%c ", _sym[type-1]);
+                       }
+               }
+               fprintf(stderr, "\n");
+       } else {
+               fprintf(stderr, "# %.*s\n", (int) length, data);
+       }
+}
+#endif
+
+static void php_http_globals_init_once(zend_php_http_globals *G)
+{
+       memset(G, 0, sizeof(*G));
+}
+
+#if 0
+static inline void php_http_globals_init(zend_php_http_globals *G TSRMLS_DC)
+{
+}
+
+static inline void php_http_globals_free(zend_php_http_globals *G TSRMLS_DC)
+{
+}
+#endif
+
+#if ZTS && PHP_DEBUG && !HAVE_GCOV
+zend_php_http_globals *php_http_globals(void)
+{
+       TSRMLS_FETCH();
+       return PHP_HTTP_G;
+}
+#endif
+
+PHP_INI_BEGIN()
+       STD_PHP_INI_ENTRY("http.etag.mode", "crc32b", PHP_INI_ALL, OnUpdateString, env.etag_mode, zend_php_http_globals, php_http_globals)
+PHP_INI_END()
+
+PHP_MINIT_FUNCTION(http)
+{
+       http_module_number = module_number;
+       ZEND_INIT_MODULE_GLOBALS(php_http, php_http_globals_init_once, NULL);
+       REGISTER_INI_ENTRIES();
+       
+       if (0
+       || SUCCESS != PHP_MINIT_CALL(http_exception)
+       || SUCCESS != PHP_MINIT_CALL(http_cookie)
+       || SUCCESS != PHP_MINIT_CALL(http_encoding)
+       || SUCCESS != PHP_MINIT_CALL(http_filter)
+       || SUCCESS != PHP_MINIT_CALL(http_header)
+       || SUCCESS != PHP_MINIT_CALL(http_header_parser)
+       || SUCCESS != PHP_MINIT_CALL(http_message)
+       || SUCCESS != PHP_MINIT_CALL(http_message_parser)
+       || SUCCESS != PHP_MINIT_CALL(http_message_body)
+       || SUCCESS != PHP_MINIT_CALL(http_querystring)
+       || SUCCESS != PHP_MINIT_CALL(http_client)
+       || SUCCESS != PHP_MINIT_CALL(http_client_request)
+       || SUCCESS != PHP_MINIT_CALL(http_client_response)
+#if PHP_HTTP_HAVE_CURL
+       || SUCCESS != PHP_MINIT_CALL(http_curl)
+       || SUCCESS != PHP_MINIT_CALL(http_client_curl)
+#endif
+       || SUCCESS != PHP_MINIT_CALL(http_url)
+       || SUCCESS != PHP_MINIT_CALL(http_env)
+       || SUCCESS != PHP_MINIT_CALL(http_env_request)
+       || SUCCESS != PHP_MINIT_CALL(http_env_response)
+       || SUCCESS != PHP_MINIT_CALL(http_params)
+       ) {
+               return FAILURE;
+       }
+       
+       return SUCCESS;
+}
+
+
+
+PHP_MSHUTDOWN_FUNCTION(http)
+{
+       UNREGISTER_INI_ENTRIES();
+       
+       if (0
+       || SUCCESS != PHP_MSHUTDOWN_CALL(http_message)
+#if PHP_HTTP_HAVE_CURL
+       || SUCCESS != PHP_MSHUTDOWN_CALL(http_client_curl)
+       || SUCCESS != PHP_MSHUTDOWN_CALL(http_curl)
+#endif
+       || SUCCESS != PHP_MSHUTDOWN_CALL(http_client)
+       ) {
+               return FAILURE;
+       }
+       
+       return SUCCESS;
+}
+
+PHP_RSHUTDOWN_FUNCTION(http)
+{
+       if (0
+       || SUCCESS != PHP_RSHUTDOWN_CALL(http_env)
+       ) {
+               return FAILURE;
+       }
+       
+       return SUCCESS;
+}
+
+PHP_MINFO_FUNCTION(http)
+{
+       php_http_buffer_t buf;
+
+       php_http_buffer_init(&buf);
+
+       php_info_print_table_start();
+       php_info_print_table_header(2, "HTTP Support", "enabled");
+       php_info_print_table_row(2, "Extension Version", PHP_PECL_HTTP_VERSION);
+       php_info_print_table_end();
+       
+       php_info_print_table_start();
+       php_info_print_table_header(3, "Used Library", "Compiled", "Linked");
+       php_info_print_table_row(3, "libz", ZLIB_VERSION, zlibVersion());
+#if PHP_HTTP_HAVE_CURL
+       {
+               curl_version_info_data *cv = curl_version_info(CURLVERSION_NOW);
+               php_info_print_table_row(3, "libcurl", LIBCURL_VERSION, cv->version);
+       }
+#else
+       php_info_print_table_row(3, "libcurl", "disabled", "disabled");
+#endif
+
+#if PHP_HTTP_HAVE_EVENT
+       php_info_print_table_row(3, "libevent",
+#      ifdef LIBEVENT_VERSION
+                       LIBEVENT_VERSION,
+#      else
+                       PHP_HTTP_EVENT_VERSION,
+#      endif
+                       event_get_version());
+#else
+       php_info_print_table_row(3, "libevent", "disabled", "disabled");
+#endif
+
+#if PHP_HTTP_HAVE_IDN2
+       php_info_print_table_row(3, "libidn2 (IDNA2008)", IDN2_VERSION, idn2_check_version(NULL));
+#elif PHP_HTTP_HAVE_IDN
+       php_info_print_table_row(3, "libidn (IDNA2003)", PHP_HTTP_LIBIDN_VERSION, "unknown");
+#endif
+
+       php_info_print_table_end();
+       
+       DISPLAY_INI_ENTRIES();
+}
+
+
+/*
+ * 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_api.h b/src/php_http_api.h
new file mode 100644 (file)
index 0000000..08b6ba8
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_API_H
+#define PHP_HTTP_API_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef PHP_WIN32
+#include <php_config.h>
+#endif
+#include <php.h>
+#include <SAPI.h>
+
+#include <ext/raphf/php_raphf.h>
+#include <ext/propro/php_propro.h>
+#include <ext/standard/php_string.h>
+#include <ext/spl/spl_iterators.h>
+#include <ext/date/php_date.h>
+
+#include <zend_interfaces.h>
+#include <zend_exceptions.h>
+
+
+#ifdef PHP_WIN32
+# define PHP_HTTP_API __declspec(dllexport)
+#elif defined(__GNUC__) && __GNUC__ >= 4
+# define PHP_HTTP_API extern __attribute__ ((visibility("default")))
+#else
+# define PHP_HTTP_API extern
+#endif
+
+#if (defined(HAVE_ICONV) || defined(PHP_HTTP_HAVE_EXT_ICONV)) && (PHP_HTTP_SHARED_DEPS || !defined(COMPILE_DL_ICONV))
+#      define PHP_HTTP_HAVE_ICONV
+#endif
+
+#if (defined(HAVE_HASH_EXT) || defined(PHP_HTTP_HAVE_EXT_HASH)) && (PHP_HTTP_SHARED_DEPS || !defined(COMPILE_DL_HASH)) && defined(PHP_HTTP_HAVE_PHP_HASH_H)
+#      define PHP_HTTP_HAVE_HASH
+#endif
+
+#include <stddef.h>
+
+#ifdef PHP_WIN32
+#      define CURL_STATICLIB
+#      include <winsock2.h>
+#else
+#      ifdef HAVE_NETDB_H
+#              include <netdb.h>
+#      endif
+#      ifdef HAVE_UNISTD_H
+#              include <unistd.h>
+#      endif
+#endif
+
+#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTYPE_H) && defined(HAVE_ISWALNUM) && (defined(HAVE_MBRTOWC) || defined(HAVE_MBTOWC))
+#      define PHP_HTTP_HAVE_WCHAR 1
+#endif
+
+#include <ctype.h>
+#define PHP_HTTP_IS_CTYPE(type, c) is##type((int) (unsigned char) (c))
+#define PHP_HTTP_TO_CTYPE(type, c) to##type((int) (unsigned char) (c))
+
+#include "php_http.h"
+
+#include "php_http_buffer.h"
+#include "php_http_misc.h"
+#include "php_http_options.h"
+
+#include "php_http.h"
+#include "php_http_cookie.h"
+#include "php_http_encoding.h"
+#include "php_http_info.h"
+#include "php_http_message.h"
+#include "php_http_env.h"
+#include "php_http_env_request.h"
+#include "php_http_env_response.h"
+#include "php_http_etag.h"
+#include "php_http_exception.h"
+#include "php_http_filter.h"
+#include "php_http_header_parser.h"
+#include "php_http_header.h"
+#include "php_http_message_body.h"
+#include "php_http_message_parser.h"
+#include "php_http_negotiate.h"
+#include "php_http_object.h"
+#include "php_http_params.h"
+#include "php_http_querystring.h"
+#include "php_http_client.h"
+#include "php_http_curl.h"
+#include "php_http_client_request.h"
+#include "php_http_client_response.h"
+#include "php_http_client_curl.h"
+#include "php_http_url.h"
+#include "php_http_version.h"
+
+ZEND_BEGIN_MODULE_GLOBALS(php_http)
+       struct php_http_env_globals env;
+ZEND_END_MODULE_GLOBALS(php_http)
+
+ZEND_EXTERN_MODULE_GLOBALS(php_http);
+
+#ifdef ZTS
+#      include "TSRM/TSRM.h"
+#      define PHP_HTTP_G ((zend_php_http_globals *) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(php_http_globals_id)])
+#      undef TSRMLS_FETCH_FROM_CTX
+#      define TSRMLS_FETCH_FROM_CTX(ctx) void ***tsrm_ls = ((ctx)?(ctx):ts_resource_ex(0, NULL))
+#else
+#      define PHP_HTTP_G (&php_http_globals)
+#endif
+
+#if PHP_DEBUG
+#      define _DPF_STR 0
+#      define _DPF_IN  1
+#      define _DPF_OUT 2
+extern void _dpf(int type, const char *data, size_t length);
+#else
+#      define _dpf(t,s,l);
+#endif
+
+#endif /* PHP_HTTP_API_H */
+
+
+/*
+ * 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_buffer.c b/src/php_http_buffer.c
new file mode 100644 (file)
index 0000000..e24a4e1
--- /dev/null
@@ -0,0 +1,459 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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.h>
+#include "php_http_buffer.h"
+
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_init_ex(php_http_buffer_t *buf, size_t chunk_size, int flags)
+{
+       if (!buf) {
+               buf = pemalloc(sizeof(*buf), flags & PHP_HTTP_BUFFER_INIT_PERSISTENT);
+       }
+
+       if (buf) {
+               buf->size = (chunk_size) ? chunk_size : PHP_HTTP_BUFFER_DEFAULT_SIZE;
+               buf->pmem = (flags & PHP_HTTP_BUFFER_INIT_PERSISTENT) ? 1 : 0;
+               buf->data = (flags & PHP_HTTP_BUFFER_INIT_PREALLOC) ? pemalloc(buf->size, buf->pmem) : NULL;
+               buf->free = (flags & PHP_HTTP_BUFFER_INIT_PREALLOC) ? buf->size : 0;
+               buf->used = 0;
+       }
+       
+       return buf;
+}
+
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_from_string_ex(php_http_buffer_t *buf, const char *string, size_t length)
+{
+       if ((buf = php_http_buffer_init(buf))) {
+               if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(buf, string, length)) {
+                       pefree(buf, buf->pmem);
+                       buf = NULL;
+               }
+       }
+       return buf;
+}
+
+PHP_HTTP_BUFFER_API size_t php_http_buffer_resize_ex(php_http_buffer_t *buf, size_t len, size_t override_size, int allow_error)
+{
+       char *ptr = NULL;
+#if 0
+       fprintf(stderr, "RESIZE: len=%lu, size=%lu, used=%lu, free=%lu, total=%lu\n", len, buf->size, buf->used, buf->free, buf->free+buf->used);
+#endif
+       if (buf->free < len) {
+               size_t size = override_size ? override_size : buf->size;
+               
+               while ((size + buf->free) < len) {
+                       size <<= 1;
+               }
+               
+               if (allow_error) {
+                       ptr = perealloc_recoverable(buf->data, buf->used + buf->free + size, buf->pmem);
+               } else {
+                       ptr = perealloc(buf->data, buf->used + buf->free + size, buf->pmem);
+               }
+               
+               if (ptr) {
+                       buf->data = ptr;
+               } else {
+                       return PHP_HTTP_BUFFER_NOMEM;
+               }
+               
+               buf->free += size;
+               return size;
+       }
+       return 0;
+}
+
+PHP_HTTP_BUFFER_API char *php_http_buffer_account(php_http_buffer_t *buf, size_t to_account)
+{
+       assert(to_account <= buf->free);
+
+       buf->free -= to_account;
+       buf->used += to_account;
+
+       return buf->data + buf->used;
+}
+
+PHP_HTTP_BUFFER_API size_t php_http_buffer_shrink(php_http_buffer_t *buf)
+{
+       /* avoid another realloc on fixation */
+       if (buf->free > 1) {
+               char *ptr = perealloc(buf->data, buf->used + 1, buf->pmem);
+               
+               if (ptr) {
+                       buf->data = ptr;
+               } else {
+                       return PHP_HTTP_BUFFER_NOMEM;
+               }
+               buf->free = 1;
+       }
+       return buf->used;
+}
+
+PHP_HTTP_BUFFER_API size_t php_http_buffer_append(php_http_buffer_t *buf, const char *append, size_t append_len)
+{
+       if (buf->free < append_len && PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, append_len)) {
+               return PHP_HTTP_BUFFER_NOMEM;
+       }
+       memcpy(buf->data + buf->used, append, append_len);
+       buf->used += append_len;
+       buf->free -= append_len;
+       return append_len;
+}
+
+PHP_HTTP_BUFFER_API size_t php_http_buffer_appendf(php_http_buffer_t *buf, const char *format, ...)
+{
+       va_list argv;
+       char *append;
+       size_t append_len, alloc;
+
+       va_start(argv, format);
+       append_len = vspprintf(&append, 0, format, argv);
+       va_end(argv);
+
+       alloc = php_http_buffer_append(buf, append, append_len);
+       efree(append);
+
+       if (PHP_HTTP_BUFFER_NOMEM == alloc) {
+               return PHP_HTTP_BUFFER_NOMEM;
+       }
+       return append_len;
+}
+
+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 (into) {
+               *into = copy;
+       }
+       if (len) {
+               *len = buf->used;
+       }
+       return copy;
+}
+
+PHP_HTTP_BUFFER_API size_t php_http_buffer_cut(php_http_buffer_t *buf, size_t offset, size_t length)
+{
+       if (offset > buf->used) {
+               return 0;
+       }
+       if (offset + length > buf->used) {
+               length = buf->used - offset;
+       }
+       memmove(buf->data + offset, buf->data + offset + length, buf->used - length - offset);
+       buf->used -= length;
+       buf->free += length;
+       return length;
+}
+
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_fix(php_http_buffer_t *buf)
+{
+       if (buf->free < 1 && PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize_ex(buf, 1, 1, 0)) {
+               return NULL;
+       }
+       buf->data[buf->used] = '\0';
+       return buf;
+}
+
+PHP_HTTP_BUFFER_API void php_http_buffer_reset(php_http_buffer_t *buf)
+{
+       buf->free += buf->used;
+       buf->used = 0;
+}
+
+PHP_HTTP_BUFFER_API void php_http_buffer_dtor(php_http_buffer_t *buf)
+{
+       if (buf->data) {
+               pefree(buf->data, buf->pmem);
+               buf->data = NULL;
+       }
+       buf->used = 0;
+       buf->free = 0;
+}
+
+PHP_HTTP_BUFFER_API void php_http_buffer_free(php_http_buffer_t **buf)
+{
+       if (*buf) {
+               php_http_buffer_dtor(*buf);
+               pefree(*buf, (*buf)->pmem);
+               *buf = NULL;
+       }
+}
+
+PHP_HTTP_BUFFER_API size_t php_http_buffer_chunk_buffer(php_http_buffer_t **s, const char *data, size_t data_len, char **chunk, size_t chunk_size)
+{
+       php_http_buffer_t *storage;
+       
+       *chunk = NULL;
+       
+       if (!*s) {
+               *s = php_http_buffer_init_ex(NULL, chunk_size << 1, chunk_size ? PHP_HTTP_BUFFER_INIT_PREALLOC : 0);
+       }
+       storage = *s;
+       
+       if (data_len) {
+               php_http_buffer_append(storage, data, data_len);
+       }
+       
+       if (!chunk_size) {
+               php_http_buffer_data(storage, chunk, &chunk_size);
+               php_http_buffer_free(s);
+               return chunk_size;
+       }
+       
+       if (storage->used >= chunk_size) {
+               *chunk = estrndup(storage->data, chunk_size);
+               php_http_buffer_cut(storage, 0, chunk_size);
+               return chunk_size;
+       }
+       
+       return 0;
+}
+
+PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_output(php_http_buffer_t **s, const char *data, size_t data_len, size_t chunk_len, php_http_buffer_pass_func_t passout, void *opaque TSRMLS_DC)
+{
+       char *chunk = NULL;
+       size_t passed = 0, got = 0;
+
+       while ((got = php_http_buffer_chunk_buffer(s, data, data_len, &chunk, chunk_len))) {
+               if (PHP_HTTP_BUFFER_PASS0 == passout(opaque, chunk, got TSRMLS_CC)) {
+                       PTR_SET(chunk, NULL);
+                       return PHP_HTTP_BUFFER_PASS0;
+               }
+               ++passed;
+               if (!chunk_len) {
+                       /*      we already got the last chunk,
+                               and freed all resources */
+                       break;
+               }
+               data = NULL;
+               data_len = 0;
+               PTR_SET(chunk, NULL);
+       }
+       PTR_FREE(chunk);
+       return passed;
+}
+
+PHP_HTTP_BUFFER_API ssize_t php_http_buffer_passthru(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *passin_arg, php_http_buffer_pass_func_t passon, void *passon_arg TSRMLS_DC)
+{
+       size_t passed_on = 0, passed_in = php_http_buffer_chunked_input(s, chunk_size, passin, passin_arg TSRMLS_CC);
+
+       if (passed_in == PHP_HTTP_BUFFER_PASS0) {
+               return passed_in;
+       }
+       if (passed_in || (*s)->used) {
+               passed_on = passon(passon_arg, (*s)->data, (*s)->used TSRMLS_CC);
+
+               if (passed_on == PHP_HTTP_BUFFER_PASS0) {
+                       return passed_on;
+               }
+
+               if (passed_on) {
+                       php_http_buffer_cut(*s, 0, passed_on);
+               }
+       }
+
+       return passed_on - passed_in;
+}
+
+PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_input(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *opaque TSRMLS_DC)
+{
+       php_http_buffer_t *str;
+       size_t passed;
+
+       if (!*s) {
+               *s = php_http_buffer_init_ex(NULL, chunk_size, chunk_size ? PHP_HTTP_BUFFER_INIT_PREALLOC : 0);
+       }
+       str = *s;
+
+       php_http_buffer_resize(str, chunk_size);
+       passed = passin(opaque, str->data + str->used, chunk_size TSRMLS_CC);
+
+       if (passed != PHP_HTTP_BUFFER_PASS0) {
+               str->used += passed;
+               str->free -= passed;
+       }
+
+       php_http_buffer_fix(str);
+
+       return passed;
+}
+
+#ifdef PHP_HTTP_BUFFER_EXTENDED
+
+PHP_HTTP_BUFFER_API int php_http_buffer_cmp(php_http_buffer_t *left, php_http_buffer_t *right)
+{
+       if (left->used > right->used) {
+               return -1;
+       } else if (right->used > left->used) {
+               return 1;
+       } else {
+               return memcmp(left->data, right->data, left->used);
+       }
+}
+
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_copy(const php_http_buffer_t *from, php_http_buffer_t *to)
+{
+       int free_to = !to;
+
+       to = php_http_buffer_clone(from, to);
+
+       if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(to, from->data, from->used)) {
+               if (free_to) {
+                       php_http_buffer_free(&to);
+               } else {
+                       php_http_buffer_dtor(to);
+               }
+       }
+       return to;
+}
+
+PHP_HTTP_BUFFER_API size_t php_http_buffer_insert(php_http_buffer_t *buf, const char *insert, size_t insert_len, size_t offset)
+{
+       if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, insert_len)) {
+               return PHP_HTTP_BUFFER_NOMEM;
+       }
+       memmove(buf->data + offset + insert_len, buf->data + offset, insert_len);
+       memcpy(buf->data + offset, insert, insert_len);
+       buf->used += insert_len;
+       buf->free -= insert_len;
+       return insert_len;
+}
+
+PHP_HTTP_BUFFER_API size_t php_http_buffer_insertf(php_http_buffer_t *buf, size_t offset, const char *format, ...)
+{
+       va_list argv;
+       char *insert;
+       size_t insert_len, alloc;
+
+       va_start(argv, format);
+       insert_len = vspprintf(&insert, 0, format, argv);
+       va_end(argv);
+
+       alloc = php_http_buffer_insert(buf, insert, insert_len, offset);
+       efree(insert);
+
+       if (PHP_HTTP_BUFFER_NOMEM == alloc) {
+               return PHP_HTTP_BUFFER_NOMEM;
+       }
+       return insert_len;
+}
+
+PHP_HTTP_BUFFER_API size_t php_http_buffer_prepend(php_http_buffer_t *buf, const char *prepend, size_t prepend_len)
+{
+       if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, prepend_len)) {
+               return PHP_HTTP_BUFFER_NOMEM;
+       }
+       memmove(buf->data + prepend_len, buf->data, buf->used);
+       memcpy(buf->data, prepend, prepend_len);
+       buf->used += prepend_len;
+       buf->free -= prepend_len;
+       return prepend_len;
+}
+
+PHP_HTTP_BUFFER_API size_t php_http_buffer_prependf(php_http_buffer_t *buf, const char *format, ...)
+{
+       va_list argv;
+       char *prepend;
+       size_t prepend_len, alloc;
+
+       va_start(argv, format);
+       prepend_len = vspprintf(&prepend, 0, format, argv);
+       va_end(argv);
+
+       alloc = php_http_buffer_prepend(buf, prepend, prepend_len);
+       efree(prepend);
+
+       if (PHP_HTTP_BUFFER_NOMEM == alloc) {
+               return PHP_HTTP_BUFFER_NOMEM;
+       }
+       return prepend_len;
+}
+
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_sub(const php_http_buffer_t *buf, size_t offset, size_t length)
+{
+       if (offset >= buf->used) {
+               return NULL;
+       } else {
+               size_t need = 1 + ((length + offset) > buf->used ? (buf->used - offset) : (length - offset));
+               php_http_buffer_t *sub = php_http_buffer_init_ex(NULL, need, PHP_HTTP_BUFFER_INIT_PREALLOC | (buf->pmem ? PHP_HTTP_BUFFER_INIT_PERSISTENT:0));
+               if (sub) {
+                       if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(sub, buf->data + offset, need)) {
+                               php_http_buffer_free(&sub);
+                       } else {
+                               sub->size = buf->size;
+                       }
+               }
+               return sub;
+       }
+}
+
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_right(const php_http_buffer_t *buf, size_t length)
+{
+       if (length < buf->used) {
+               return php_http_buffer_sub(buf, buf->used - length, length);
+       } else {
+               return php_http_buffer_sub(buf, 0, buf->used);
+       }
+}
+
+
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_va(php_http_buffer_t *buf, unsigned argc, va_list argv)
+{
+       unsigned i = 0;
+       buf = php_http_buffer_init(buf);
+
+       if (buf) {
+               while (argc > i++) {
+                       php_http_buffer_free_t f = va_arg(argv, php_http_buffer_free_t);
+                       php_http_buffer_t *current = va_arg(argv, php_http_buffer_t *);
+                       php_http_buffer_append(buf, current->data, current->used);
+                       FREE_PHP_HTTP_BUFFER(f, current);
+               }
+       }
+
+       return buf;
+}
+
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_ex(php_http_buffer_t *buf, unsigned argc, ...)
+{
+       va_list argv;
+       php_http_buffer_t *ret;
+
+       va_start(argv, argc);
+       ret = php_http_buffer_merge_va(buf, argc, argv);
+       va_end(argv);
+       return ret;
+}
+
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge(unsigned argc, ...)
+{
+       va_list argv;
+       php_http_buffer_t *ret;
+
+       va_start(argv, argc);
+       ret = php_http_buffer_merge_va(NULL, argc, argv);
+       va_end(argv);
+       return ret;
+}
+
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
+
diff --git a/src/php_http_buffer.h b/src/php_http_buffer.h
new file mode 100644 (file)
index 0000000..faf8992
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_BUFFER_H
+#define PHP_HTTP_BUFFER_H
+
+#ifndef PHP_HTTP_BUFFER_DEFAULT_SIZE
+#      define PHP_HTTP_BUFFER_DEFAULT_SIZE 256
+#endif
+
+#define PHP_HTTP_BUFFER_ERROR ((size_t) -1)
+#define PHP_HTTP_BUFFER_NOMEM PHP_HTTP_BUFFER_ERROR
+#define PHP_HTTP_BUFFER_PASS0 PHP_HTTP_BUFFER_ERROR
+
+#ifndef PTR_FREE
+#      define PTR_FREE(PTR) \
+       { \
+               if (PTR) { \
+                       efree(PTR); \
+               } \
+       }
+#endif
+#ifndef PTR_SET
+#      define PTR_SET(PTR, SET) \
+       { \
+               PTR_FREE(PTR); \
+               PTR = SET; \
+       }
+#endif
+#ifndef TSRMLS_D
+#      define TSRMLS_D
+#      define TSRMLS_DC
+#      define TSRMLS_CC
+#      define TSRMLS_C
+#endif
+#ifdef PHP_ATTRIBUTE_FORMAT
+#      define PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(f, a, b) PHP_ATTRIBUTE_FORMAT(f, a, b)
+#else
+#      define PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(f, a, b)
+#endif
+#ifndef pemalloc
+#      define pemalloc(s,p)    malloc(s)
+#      define pefree(x,p)              free(x)
+#      define perealloc(x,s,p) realloc(x,s)
+#      define perealloc_recoverable perealloc
+#      define ecalloc calloc
+static inline void *estrndup(void *p, size_t s)
+{
+       char *r = (char *) malloc(s+1);
+       if (r) memcpy((void *) r, p, s), r[s] = '\0';
+       return (void *) r;
+}
+#endif
+
+#if defined(PHP_WIN32)
+#      if defined(PHP_HTTP_BUFFER_EXPORTS)
+#              define PHP_HTTP_BUFFER_API __declspec(dllexport)
+#      elif defined(COMPILE_DL_PHP_HTTP_BUFFER)
+#              define PHP_HTTP_BUFFER_API __declspec(dllimport)
+#      else
+#              define PHP_HTTP_BUFFER_API
+#      endif
+#else
+#      define PHP_HTTP_BUFFER_API
+#endif
+
+#define PHP_HTTP_BUFFER(p) ((php_http_buffer_t *) (p))
+
+#define FREE_PHP_HTTP_BUFFER_PTR(STR) pefree(STR, STR->pmem)
+#define FREE_PHP_HTTP_BUFFER_VAL(STR) php_http_buffer_dtor(STR)
+#define FREE_PHP_HTTP_BUFFER_ALL(STR) php_http_buffer_free(&(STR))
+#define FREE_PHP_HTTP_BUFFER(free, STR) \
+       switch (free) \
+       { \
+       case PHP_HTTP_BUFFER_FREE_NOT: \
+               break; \
+       case PHP_HTTP_BUFFER_FREE_PTR: \
+               pefree(STR, STR->pmem); break; \
+               break; \
+       case PHP_HTTP_BUFFER_FREE_VAL: \
+               php_http_buffer_dtor(STR); \
+               break; \
+       case PHP_HTTP_BUFFER_FREE_ALL: { \
+               php_http_buffer_t *PTR = (STR); \
+               php_http_buffer_free(&PTR); \
+               break; \
+       } \
+       default:\
+               break; \
+       }
+
+#define RETURN_PHP_HTTP_BUFFER_PTR(STR) RETURN_PHP_HTTP_BUFFER((STR), PHP_HTTP_BUFFER_FREE_PTR, 0)
+#define RETURN_PHP_HTTP_BUFFER_VAL(STR) RETURN_PHP_HTTP_BUFFER((STR), PHP_HTTP_BUFFER_FREE_NOT, 0)
+#define RETURN_PHP_HTTP_BUFFER_DUP(STR) RETURN_PHP_HTTP_BUFFER((STR), PHP_HTTP_BUFFER_FREE_NOT, 1)
+#define RETVAL_PHP_HTTP_BUFFER_PTR(STR) RETVAL_PHP_HTTP_BUFFER((STR), PHP_HTTP_BUFFER_FREE_PTR, 0)
+#define RETVAL_PHP_HTTP_BUFFER_VAL(STR) RETVAL_PHP_HTTP_BUFFER((STR), PHP_HTTP_BUFFER_FREE_NOT, 0)
+#define RETVAL_PHP_HTTP_BUFFER_DUP(STR) RETVAL_PHP_HTTP_BUFFER((STR), PHP_HTTP_BUFFER_FREE_NOT, 1)
+/* RETURN_PHP_HTTP_BUFFER(buf, PHP_HTTP_BUFFER_FREE_PTR, 0) */
+#define RETURN_PHP_HTTP_BUFFER(STR, free, dup) \
+       RETVAL_PHP_HTTP_BUFFER((STR), (free), (dup)); \
+       return;
+
+#define RETVAL_PHP_HTTP_BUFFER(STR, free, dup) \
+       php_http_buffer_fix(STR); \
+       RETVAL_STRINGL((STR)->data, (STR)->used, (dup)); \
+       FREE_PHP_HTTP_BUFFER((free), (STR));
+
+typedef struct php_http_buffer {
+       char  *data;
+       size_t used;
+       size_t free;
+       size_t size;
+       unsigned pmem:1;
+       unsigned reserved:31;
+} php_http_buffer_t;
+
+typedef enum php_http_buffer_free {
+       PHP_HTTP_BUFFER_FREE_NOT = 0,
+       PHP_HTTP_BUFFER_FREE_PTR,       /* pefree() */
+       PHP_HTTP_BUFFER_FREE_VAL,       /* php_http_buffer_dtor() */
+       PHP_HTTP_BUFFER_FREE_ALL        /* php_http_buffer_free() */
+} php_http_buffer_free_t;
+
+#define PHP_HTTP_BUFFER_ALL_FREE(STR) PHP_HTTP_BUFFER_FREE_ALL,(STR)
+#define PHP_HTTP_BUFFER_PTR_FREE(STR) PHP_HTTP_BUFFER_FREE_PTR,(STR)
+#define PHP_HTTP_BUFFER_VAL_FREE(STR) PHP_HTTP_BUFFER_FREE_VAL,(STR)
+#define PHP_HTTP_BUFFER_NOT_FREE(STR) PHP_HTTP_BUFFER_FREE_NOT,(STR)
+
+#define PHP_HTTP_BUFFER_INIT_PREALLOC  0x01
+#define PHP_HTTP_BUFFER_INIT_PERSISTENT        0x02
+
+/* create a new php_http_buffer_t */
+#define php_http_buffer_new() php_http_buffer_init(NULL)
+#define php_http_buffer_init(b) php_http_buffer_init_ex(b, PHP_HTTP_BUFFER_DEFAULT_SIZE, 0)
+#define php_http_buffer_clone(from, to) php_http_buffer_init_ex((to), (from)->size, (from)->pmem ? PHP_HTTP_BUFFER_INIT_PERSISTENT:0)
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_init_ex(php_http_buffer_t *buf, size_t chunk_size, int flags);
+
+/* create a php_http_buffer_t from a zval or c-string */
+#define php_http_buffer_from_zval(z) php_http_buffer_from_string(Z_STRVAL(z), Z_STRLEN(z))
+#define php_http_buffer_from_zval_ex(b, z) php_http_buffer_from_string_ex(b, Z_STRVAL(z), Z_STRLEN(z))
+#define php_http_buffer_from_string(s, l) php_http_buffer_from_string_ex(NULL, (s), (l))
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_from_string_ex(php_http_buffer_t *buf, const char *string, size_t length);
+
+/* usually only called from within the internal functions */
+#define php_http_buffer_resize(b, s) php_http_buffer_resize_ex((b), (s), 0, 0)
+PHP_HTTP_BUFFER_API size_t php_http_buffer_resize_ex(php_http_buffer_t *buf, size_t len, size_t override_size, int allow_error);
+
+PHP_HTTP_BUFFER_API char *php_http_buffer_account(php_http_buffer_t *buf, size_t to_account);
+
+/* shrink memory chunk to actually used size (+1) */
+PHP_HTTP_BUFFER_API size_t php_http_buffer_shrink(php_http_buffer_t *buf);
+
+/* append data to the php_http_buffer_t */
+#define php_http_buffer_appends(b, a) php_http_buffer_append((b), (a), sizeof(a)-1)
+#define php_http_buffer_appendl(b, a) php_http_buffer_append((b), (a), strlen(a))
+PHP_HTTP_BUFFER_API size_t php_http_buffer_append(php_http_buffer_t *buf, const char *append, size_t append_len);
+PHP_HTTP_BUFFER_API size_t php_http_buffer_appendf(php_http_buffer_t *buf, const char *format, ...) PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(printf, 2, 3);
+
+/* get a zero-terminated string */
+PHP_HTTP_BUFFER_API char *php_http_buffer_data(const php_http_buffer_t *buf, char **into, size_t *len);
+
+/* remove a substring */
+PHP_HTTP_BUFFER_API size_t php_http_buffer_cut(php_http_buffer_t *buf, size_t offset, size_t length);
+
+/* sets a trailing NUL byte */
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_fix(php_http_buffer_t *buf);
+
+/* reset php_http_buffer_t object */
+PHP_HTTP_BUFFER_API void php_http_buffer_reset(php_http_buffer_t *buf);
+
+/* free a php_http_buffer_t objects contents */
+PHP_HTTP_BUFFER_API void php_http_buffer_dtor(php_http_buffer_t *buf);
+
+/* free a php_http_buffer_t object completely */
+PHP_HTTP_BUFFER_API void php_http_buffer_free(php_http_buffer_t **buf);
+
+/* stores data in a php_http_buffer_t until it reaches chunk_size */
+PHP_HTTP_BUFFER_API size_t php_http_buffer_chunk_buffer(php_http_buffer_t **s, const char *data, size_t data_len, char **chunk, size_t chunk_size);
+
+typedef size_t (*php_http_buffer_pass_func_t)(void *opaque, char *, size_t TSRMLS_DC);
+
+PHP_HTTP_BUFFER_API ssize_t php_http_buffer_passthru(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *passin_arg, php_http_buffer_pass_func_t passon, void *passon_arg TSRMLS_DC);
+
+/* wrapper around php_http_buffer_chunk_buffer, which passes available chunks to passthru() */
+PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_output(php_http_buffer_t **s, const char *data, size_t data_len, size_t chunk_size, php_http_buffer_pass_func_t passout, void *opaque TSRMLS_DC);
+
+/* write chunks directly into php_http_buffer_t buffer */
+PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_input(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *opaque TSRMLS_DC);
+
+
+#      ifdef PHP_HTTP_BUFFER_EXTENDED
+
+/* memcmp for php_http_buffer_t objects */
+PHP_HTTP_BUFFER_API int php_http_buffer_cmp(php_http_buffer_t *left, php_http_buffer_t *right);
+
+/* get a complete php_http_buffer_t duplicate */
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_copy(const php_http_buffer_t *from, php_http_buffer_t *to);
+
+/* merge several php_http_buffer_t objects
+   use like:
+
+       php_http_buffer_t *final = php_http_buffer_merge(3,
+               PHP_HTTP_BUFFER_NOT_FREE(&keep),
+               PHP_HTTP_BUFFER_ALL_FREE(middle_ptr),
+               PHP_HTTP_BUFFER_VAL_FREE(&local);
+*/
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge(unsigned argc, ...);
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_ex(php_http_buffer_t *buf, unsigned argc, ...);
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_va(php_http_buffer_t *buf, unsigned argc, va_list argv);
+
+/* insert data at a specific position of the php_http_buffer_t */
+#define php_http_buffer_inserts(b, i, o) php_http_buffer_insert((b), (i), sizeof(i)-1, (o))
+#define php_http_buffer_insertl(b, i, o) php_http_buffer_insert((b), (i), strlen(i), (o))
+PHP_HTTP_BUFFER_API size_t php_http_buffer_insert(php_http_buffer_t *buf, const char *insert, size_t insert_len, size_t offset);
+PHP_HTTP_BUFFER_API size_t php_http_buffer_insertf(php_http_buffer_t *buf, size_t offset, const char *format, ...) PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(printf, 3, 4);
+
+/* prepend data */
+#define php_http_buffer_prepends(b, p) php_http_buffer_prepend((b), (p), sizeof(p)-1)
+#define php_http_buffer_prependl(b, p) php_http_buffer_prepend((b), (p), strlen(p))
+PHP_HTTP_BUFFER_API size_t php_http_buffer_prepend(php_http_buffer_t *buf, const char *prepend, size_t prepend_len);
+PHP_HTTP_BUFFER_API size_t php_http_buffer_prependf(php_http_buffer_t *buf, const char *format, ...) PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(printf, 2, 3);
+
+/* get a part of the php_http_buffer_t */
+#define php_http_buffer_mid(b, o, l) php_http_buffer_sub((b), (o), (l))
+#define php_http_buffer_left(b, l) php_http_buffer_sub((b), 0, (l))
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_right(const php_http_buffer_t *buf, size_t length);
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_sub(const php_http_buffer_t *buf, size_t offset, size_t len);
+
+#      endif /* PHP_HTTP_BUFFER_EXTENDED */
+
+#endif /* PHP_HTTP_BUFFER_H */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/src/php_http_client.c b/src/php_http_client.c
new file mode 100644 (file)
index 0000000..160e8bb
--- /dev/null
@@ -0,0 +1,1284 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+#include "php_http_client.h"
+
+#include <ext/spl/spl_observer.h>
+
+/*
+ * array of name => php_http_client_driver_t*
+ */
+static HashTable php_http_client_drivers;
+
+ZEND_RESULT_CODE php_http_client_driver_add(php_http_client_driver_t *driver)
+{
+       return zend_hash_add(&php_http_client_drivers, driver->name_str, driver->name_len + 1, (void *) driver, sizeof(php_http_client_driver_t), NULL);
+}
+
+ZEND_RESULT_CODE php_http_client_driver_get(const char *name_str, size_t name_len, php_http_client_driver_t *driver)
+{
+       php_http_client_driver_t *tmp;
+
+       if ((name_str && SUCCESS == zend_hash_find(&php_http_client_drivers, name_str, name_len + 1, (void *) &tmp))
+       ||      (SUCCESS == zend_hash_get_current_data(&php_http_client_drivers, (void *) &tmp))) {
+               *driver = *tmp;
+               return SUCCESS;
+       }
+       return FAILURE;
+}
+
+static int apply_driver_list(void *p, void *arg TSRMLS_DC)
+{
+       php_http_client_driver_t *d = p;
+       zval *zname;
+
+       MAKE_STD_ZVAL(zname);
+       ZVAL_STRINGL(zname, d->name_str, d->name_len, 1);
+
+       zend_hash_next_index_insert(arg, &zname, sizeof(zval *), NULL);
+       return ZEND_HASH_APPLY_KEEP;
+}
+
+void php_http_client_driver_list(HashTable *ht TSRMLS_DC)
+{
+       zend_hash_apply_with_argument(&php_http_client_drivers, apply_driver_list, ht TSRMLS_CC);
+}
+
+void php_http_client_options_set_subr(zval *this_ptr, char *key, size_t len, zval *opts, int overwrite TSRMLS_DC)
+{
+       if (overwrite || (opts && zend_hash_num_elements(Z_ARRVAL_P(opts)))) {
+               zend_class_entry *this_ce = Z_OBJCE_P(getThis());
+               zval *old_opts, *new_opts, **entry = NULL;
+
+               MAKE_STD_ZVAL(new_opts);
+               array_init(new_opts);
+               old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
+               if (Z_TYPE_P(old_opts) == IS_ARRAY) {
+                       array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL_P(new_opts));
+               }
+
+               if (overwrite) {
+                       if (opts && zend_hash_num_elements(Z_ARRVAL_P(opts))) {
+                               Z_ADDREF_P(opts);
+                               zend_symtable_update(Z_ARRVAL_P(new_opts), key, len, (void *) &opts, sizeof(zval *), NULL);
+                       } else {
+                               zend_symtable_del(Z_ARRVAL_P(new_opts), key, len);
+                       }
+               } else if (opts && zend_hash_num_elements(Z_ARRVAL_P(opts))) {
+                       if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(new_opts), key, len, (void *) &entry)) {
+                               array_join(Z_ARRVAL_P(opts), Z_ARRVAL_PP(entry), 0, 0);
+                       } else {
+                               Z_ADDREF_P(opts);
+                               zend_symtable_update(Z_ARRVAL_P(new_opts), key, len, (void *) &opts, sizeof(zval *), NULL);
+                       }
+               }
+
+               zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC);
+               zval_ptr_dtor(&new_opts);
+       }
+}
+
+void php_http_client_options_set(zval *this_ptr, zval *opts TSRMLS_DC)
+{
+       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+       HashPosition pos;
+       zval *new_opts;
+       zend_class_entry *this_ce = Z_OBJCE_P(getThis());
+       zend_bool is_client = instanceof_function(this_ce, php_http_client_class_entry TSRMLS_CC);
+
+       MAKE_STD_ZVAL(new_opts);
+       array_init(new_opts);
+
+       if (!opts || !zend_hash_num_elements(Z_ARRVAL_P(opts))) {
+               zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC);
+               zval_ptr_dtor(&new_opts);
+       } else {
+               zval *old_opts, *add_opts, **opt;
+
+               MAKE_STD_ZVAL(add_opts);
+               array_init(add_opts);
+               /* some options need extra attention -- thus cannot use array_merge() directly */
+               FOREACH_KEYVAL(pos, opts, key, opt) {
+                       if (key.type == HASH_KEY_IS_STRING) {
+#define KEYMATCH(k, s) ((sizeof(s)==k.len) && !strcasecmp(k.str, s))
+                               if (Z_TYPE_PP(opt) == IS_ARRAY && (KEYMATCH(key, "ssl") || KEYMATCH(key, "cookies"))) {
+                                       php_http_client_options_set_subr(getThis(), key.str, key.len, *opt, 0 TSRMLS_CC);
+                               } else if (is_client && (KEYMATCH(key, "recordHistory") || KEYMATCH(key, "responseMessageClass"))) {
+                                       zend_update_property(this_ce, getThis(), key.str, key.len-1, *opt TSRMLS_CC);
+                               } else if (Z_TYPE_PP(opt) == IS_NULL) {
+                                       old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
+                                       if (Z_TYPE_P(old_opts) == IS_ARRAY) {
+                                               zend_symtable_del(Z_ARRVAL_P(old_opts), key.str, key.len);
+                                       }
+                               } else {
+                                       Z_ADDREF_P(*opt);
+                                       add_assoc_zval_ex(add_opts, key.str, key.len, *opt);
+                               }
+                       }
+               }
+
+               old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
+               if (Z_TYPE_P(old_opts) == IS_ARRAY) {
+                       array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL_P(new_opts));
+               }
+               array_join(Z_ARRVAL_P(add_opts), Z_ARRVAL_P(new_opts), 0, 0);
+               zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC);
+               zval_ptr_dtor(&new_opts);
+               zval_ptr_dtor(&add_opts);
+       }
+}
+
+void php_http_client_options_get_subr(zval *this_ptr, char *key, size_t len, zval *return_value TSRMLS_DC)
+{
+       zend_class_entry *this_ce = Z_OBJCE_P(getThis());
+       zval **options, *opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
+
+       if ((Z_TYPE_P(opts) == IS_ARRAY) && (SUCCESS == zend_symtable_find(Z_ARRVAL_P(opts), key, len, (void *) &options))) {
+               RETVAL_ZVAL(*options, 1, 0);
+       }
+}
+
+static void queue_dtor(void *enqueued)
+{
+       php_http_client_enqueue_t *e = enqueued;
+
+       if (e->dtor) {
+               e->dtor(e);
+       }
+}
+
+php_http_client_t *php_http_client_init(php_http_client_t *h, php_http_client_ops_t *ops, php_resource_factory_t *rf, void *init_arg TSRMLS_DC)
+{
+       php_http_client_t *free_h = NULL;
+
+       if (!h) {
+               free_h = h = emalloc(sizeof(*h));
+       }
+       memset(h, 0, sizeof(*h));
+
+       h->ops = ops;
+       if (rf) {
+               h->rf = rf;
+       } else if (ops->rsrc) {
+               h->rf = php_resource_factory_init(NULL, h->ops->rsrc, h, NULL);
+       }
+       zend_llist_init(&h->requests, sizeof(php_http_client_enqueue_t), queue_dtor, 0);
+       zend_llist_init(&h->responses, sizeof(void *), NULL, 0);
+       TSRMLS_SET_CTX(h->ts);
+
+       if (h->ops->init) {
+               if (!(h = h->ops->init(h, init_arg))) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initialize client");
+                       if (free_h) {
+                               efree(free_h);
+                       }
+               }
+       }
+
+       return h;
+}
+
+php_http_client_t *php_http_client_copy(php_http_client_t *from, php_http_client_t *to)
+{
+       if (from->ops->copy) {
+               return from->ops->copy(from, to);
+       }
+
+       return NULL;
+}
+
+void php_http_client_dtor(php_http_client_t *h)
+{
+       php_http_client_reset(h);
+
+       if (h->ops->dtor) {
+               h->ops->dtor(h);
+       }
+
+       php_resource_factory_free(&h->rf);
+}
+
+void php_http_client_free(php_http_client_t **h) {
+       if (*h) {
+               php_http_client_dtor(*h);
+               efree(*h);
+               *h = NULL;
+       }
+}
+
+ZEND_RESULT_CODE php_http_client_enqueue(php_http_client_t *h, php_http_client_enqueue_t *enqueue)
+{
+       TSRMLS_FETCH_FROM_CTX(h->ts);
+
+       if (h->ops->enqueue) {
+               if (php_http_client_enqueued(h, enqueue->request, NULL)) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enqueue request; request already in queue");
+                       return FAILURE;
+               }
+               return h->ops->enqueue(h, enqueue);
+       }
+
+       return FAILURE;
+}
+
+ZEND_RESULT_CODE php_http_client_dequeue(php_http_client_t *h, php_http_message_t *request)
+{
+       TSRMLS_FETCH_FROM_CTX(h->ts);
+
+       if (h->ops->dequeue) {
+               php_http_client_enqueue_t *enqueue = php_http_client_enqueued(h, request, NULL);
+
+               if (!enqueue) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to dequeue request; request not in queue");
+                       return FAILURE;
+               }
+               return h->ops->dequeue(h, enqueue);
+       }
+       return FAILURE;
+}
+
+php_http_client_enqueue_t *php_http_client_enqueued(php_http_client_t *h, void *compare_arg, php_http_client_enqueue_cmp_func_t compare_func)
+{
+       zend_llist_element *el = NULL;
+
+       if (compare_func) {
+               for (el = h->requests.head; el; el = el->next) {
+                       if (compare_func((php_http_client_enqueue_t *) el->data, compare_arg)) {
+                               break;
+                       }
+               }
+       } else {
+               for (el = h->requests.head; el; el = el->next) {
+                       if (((php_http_client_enqueue_t *) el->data)->request == compare_arg) {
+                               break;
+                       }
+               }
+       }
+       return el ? (php_http_client_enqueue_t *) el->data : NULL;
+}
+
+ZEND_RESULT_CODE php_http_client_wait(php_http_client_t *h, struct timeval *custom_timeout)
+{
+       if (h->ops->wait) {
+               return h->ops->wait(h, custom_timeout);
+       }
+
+       return FAILURE;
+}
+
+int php_http_client_once(php_http_client_t *h)
+{
+       if (h->ops->once) {
+               return h->ops->once(h);
+       }
+
+       return FAILURE;
+}
+
+ZEND_RESULT_CODE php_http_client_exec(php_http_client_t *h)
+{
+       if (h->ops->exec) {
+               return h->ops->exec(h);
+       }
+
+       return FAILURE;
+}
+
+void php_http_client_reset(php_http_client_t *h)
+{
+       if (h->ops->reset) {
+               h->ops->reset(h);
+       }
+
+       zend_llist_clean(&h->requests);
+       zend_llist_clean(&h->responses);
+}
+
+ZEND_RESULT_CODE php_http_client_setopt(php_http_client_t *h, php_http_client_setopt_opt_t opt, void *arg)
+{
+       if (h->ops->setopt) {
+               return h->ops->setopt(h, opt, arg);
+       }
+
+       return FAILURE;
+}
+
+ZEND_RESULT_CODE php_http_client_getopt(php_http_client_t *h, php_http_client_getopt_opt_t opt, void *arg, void *res_ptr)
+{
+       if (h->ops->getopt) {
+               return h->ops->getopt(h, opt, arg, res_ptr);
+       }
+       return FAILURE;
+}
+
+zend_class_entry *php_http_client_class_entry;
+static zend_object_handlers php_http_client_object_handlers;
+
+void php_http_client_object_free(void *object TSRMLS_DC)
+{
+       php_http_client_object_t *o = (php_http_client_object_t *) object;
+
+       php_http_client_free(&o->client);
+       php_http_object_method_dtor(&o->notify);
+       php_http_object_method_free(&o->update);
+       zend_object_std_dtor((zend_object *) o TSRMLS_CC);
+       efree(o);
+}
+
+zend_object_value php_http_client_object_new_ex(zend_class_entry *ce, php_http_client_t *client, php_http_client_object_t **ptr TSRMLS_DC)
+{
+       php_http_client_object_t *o;
+
+       o = ecalloc(1, sizeof(php_http_client_object_t));
+       zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
+       object_properties_init((zend_object *) o, ce);
+
+       o->client = client;
+
+       if (ptr) {
+               *ptr = o;
+       }
+
+       o->zv.handle = zend_objects_store_put(o, NULL, php_http_client_object_free, NULL TSRMLS_CC);
+       o->zv.handlers = &php_http_client_object_handlers;
+
+       return o->zv;
+}
+
+zend_object_value php_http_client_object_new(zend_class_entry *ce TSRMLS_DC)
+{
+       return php_http_client_object_new_ex(ce, NULL, NULL TSRMLS_CC);
+}
+
+static void handle_history(zval *zclient, php_http_message_t *request, php_http_message_t *response TSRMLS_DC)
+{
+       zval *new_hist, *old_hist = zend_read_property(php_http_client_class_entry, zclient, ZEND_STRL("history"), 0 TSRMLS_CC);
+       php_http_message_t *zipped = php_http_message_zip(response, request);
+       zend_object_value ov = php_http_message_object_new_ex(php_http_message_class_entry, zipped, NULL TSRMLS_CC);
+
+       MAKE_STD_ZVAL(new_hist);
+       ZVAL_OBJVAL(new_hist, ov, 0);
+
+       if (Z_TYPE_P(old_hist) == IS_OBJECT) {
+               php_http_message_object_prepend(new_hist, old_hist, 1 TSRMLS_CC);
+       }
+
+       zend_update_property(php_http_client_class_entry, zclient, ZEND_STRL("history"), new_hist TSRMLS_CC);
+       zval_ptr_dtor(&new_hist);
+}
+
+static ZEND_RESULT_CODE handle_response(void *arg, php_http_client_t *client, php_http_client_enqueue_t *e, php_http_message_t **response)
+{
+       zend_bool dequeue = 0;
+       zval zclient;
+       php_http_message_t *msg;
+       php_http_client_progress_state_t *progress;
+       TSRMLS_FETCH_FROM_CTX(client->ts);
+
+       INIT_PZVAL(&zclient);
+       ZVAL_OBJVAL(&zclient, ((php_http_client_object_t*) arg)->zv, 0);
+
+       if ((msg = *response)) {
+               php_http_message_object_t *msg_obj;
+               zval *info, *zresponse, *zrequest;
+               HashTable *info_ht;
+
+               /* ensure the message is of type response (could be uninitialized in case of early error, like DNS) */
+               php_http_message_set_type(msg, PHP_HTTP_RESPONSE);
+
+               if (z_is_true(zend_read_property(php_http_client_class_entry, &zclient, ZEND_STRL("recordHistory"), 0 TSRMLS_CC))) {
+                       handle_history(&zclient, e->request, *response TSRMLS_CC);
+               }
+
+               /* hard detach, redirects etc. are in the history */
+               php_http_message_free(&msg->parent);
+               *response = NULL;
+
+               MAKE_STD_ZVAL(zresponse);
+               ZVAL_OBJVAL(zresponse, php_http_message_object_new_ex(php_http_client_response_class_entry, msg, &msg_obj TSRMLS_CC), 0);
+
+               MAKE_STD_ZVAL(zrequest);
+               ZVAL_OBJVAL(zrequest, ((php_http_message_object_t *) e->opaque)->zv, 1);
+
+               php_http_message_object_prepend(zresponse, zrequest, 1 TSRMLS_CC);
+
+               MAKE_STD_ZVAL(info);
+               object_init(info);
+               info_ht = HASH_OF(info);
+               php_http_client_getopt(client, PHP_HTTP_CLIENT_OPT_TRANSFER_INFO, e->request, &info_ht);
+               zend_update_property(php_http_client_response_class_entry, zresponse, ZEND_STRL("transferInfo"), info TSRMLS_CC);
+               zval_ptr_dtor(&info);
+
+               zend_objects_store_add_ref_by_handle(msg_obj->zv.handle TSRMLS_CC);
+               zend_llist_add_element(&client->responses, &msg_obj);
+
+               if (e->closure.fci.size) {
+                       zval *retval = NULL;
+                       zend_error_handling zeh;
+
+                       zend_fcall_info_argn(&e->closure.fci TSRMLS_CC, 1, &zresponse);
+                       zend_replace_error_handling(EH_NORMAL, NULL, &zeh TSRMLS_CC);
+                       zend_fcall_info_call(&e->closure.fci, &e->closure.fcc, &retval, NULL TSRMLS_CC);
+                       zend_restore_error_handling(&zeh TSRMLS_CC);
+                       zend_fcall_info_argn(&e->closure.fci TSRMLS_CC, 0);
+
+                       if (retval) {
+                               if (Z_TYPE_P(retval) == IS_BOOL) {
+                                       dequeue = Z_BVAL_P(retval);
+                               }
+                               zval_ptr_dtor(&retval);
+                       }
+               }
+
+               zval_ptr_dtor(&zresponse);
+               zval_ptr_dtor(&zrequest);
+       }
+
+       if (SUCCESS == php_http_client_getopt(client, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, e->request, &progress)) {
+               progress->info = "finished";
+               progress->finished = 1;
+               client->callback.progress.func(client->callback.progress.arg, client, e, progress);
+       }
+
+       if (dequeue) {
+               php_http_client_dequeue(client, e->request);
+       }
+
+       return SUCCESS;
+}
+
+static void handle_progress(void *arg, php_http_client_t *client, php_http_client_enqueue_t *e, php_http_client_progress_state_t *progress)
+{
+       zval *zrequest, *zprogress, *zclient, **args[2];
+       php_http_client_object_t *client_obj = arg;
+       zend_error_handling zeh;
+       TSRMLS_FETCH_FROM_CTX(client->ts);
+
+       MAKE_STD_ZVAL(zclient);
+       ZVAL_OBJVAL(zclient, client_obj->zv, 1);
+
+       MAKE_STD_ZVAL(zrequest);
+       ZVAL_OBJVAL(zrequest, ((php_http_message_object_t *) e->opaque)->zv, 1);
+       args[0] = &zrequest;
+
+       MAKE_STD_ZVAL(zprogress);
+       object_init(zprogress);
+       add_property_bool(zprogress, "started", progress->started);
+       add_property_bool(zprogress, "finished", progress->finished);
+       add_property_string(zprogress, "info", STR_PTR(progress->info), 1);
+       add_property_double(zprogress, "dltotal", progress->dl.total);
+       add_property_double(zprogress, "dlnow", progress->dl.now);
+       add_property_double(zprogress, "ultotal", progress->ul.total);
+       add_property_double(zprogress, "ulnow", progress->ul.now);
+       args[1] = &zprogress;
+
+       zend_replace_error_handling(EH_NORMAL, NULL, &zeh TSRMLS_CC);
+       php_http_object_method_call(&client_obj->notify, zclient, NULL, 2, args TSRMLS_CC);
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+
+       zval_ptr_dtor(&zclient);
+       zval_ptr_dtor(&zrequest);
+       zval_ptr_dtor(&zprogress);
+}
+
+static void response_dtor(void *data)
+{
+       php_http_message_object_t *msg_obj = *(php_http_message_object_t **) data;
+       TSRMLS_FETCH_FROM_CTX(msg_obj->message->ts);
+
+       zend_objects_store_del_ref_by_handle_ex(msg_obj->zv.handle, msg_obj->zv.handlers TSRMLS_CC);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_construct, 0, 0, 0)
+       ZEND_ARG_INFO(0, driver)
+       ZEND_ARG_INFO(0, persistent_handle_id)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, __construct)
+{
+               char *driver_str = NULL, *persistent_handle_str = NULL;
+               int driver_len = 0, persistent_handle_len = 0;
+               php_http_client_driver_t driver;
+               php_resource_factory_t *rf = NULL;
+               php_http_client_object_t *obj;
+               zval *os;
+
+               php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ss", &driver_str, &driver_len, &persistent_handle_str, &persistent_handle_len), invalid_arg, return);
+
+               if (SUCCESS != php_http_client_driver_get(driver_str, driver_len, &driver)) {
+                       php_http_throw(unexpected_val, "Failed to locate \"%s\" client request handler", driver_str);
+                       return;
+               }
+
+               MAKE_STD_ZVAL(os);
+               object_init_ex(os, spl_ce_SplObjectStorage);
+               zend_update_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), os TSRMLS_CC);
+               zval_ptr_dtor(&os);
+
+               if (persistent_handle_len) {
+                       char *name_str;
+                       size_t name_len;
+                       php_persistent_handle_factory_t *pf;
+
+                       name_len = spprintf(&name_str, 0, "http\\Client\\%s", driver.name_str);
+                       php_http_pretty_key(name_str + sizeof("http\\Client"), driver.name_len, 1, 1);
+
+                       if ((pf = php_persistent_handle_concede(NULL , name_str, name_len, persistent_handle_str, persistent_handle_len, NULL, NULL TSRMLS_CC))) {
+                               rf = php_persistent_handle_resource_factory_init(NULL, pf);
+                       }
+
+                       efree(name_str);
+               }
+
+               obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               php_http_expect(obj->client = php_http_client_init(NULL, driver.client_ops, rf, NULL TSRMLS_CC), runtime, return);
+
+               php_http_object_method_init(&obj->notify, getThis(), ZEND_STRL("notify") TSRMLS_CC);
+
+               obj->client->callback.response.func = handle_response;
+               obj->client->callback.response.arg = obj;
+               obj->client->callback.progress.func = handle_progress;
+               obj->client->callback.progress.arg = obj;
+
+               obj->client->responses.dtor = response_dtor;
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_reset, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, reset)
+{
+       php_http_client_object_t *obj;
+       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       obj->iterator = 0;
+       php_http_client_reset(obj->client);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+static HashTable *combined_options(zval *client, zval *request TSRMLS_DC)
+{
+       HashTable *options;
+       int num_options = 0;
+       zval *z_roptions = NULL, *z_coptions = zend_read_property(php_http_client_class_entry, client, ZEND_STRL("options"), 0 TSRMLS_CC);
+
+       if (Z_TYPE_P(z_coptions) == IS_ARRAY) {
+               num_options = zend_hash_num_elements(Z_ARRVAL_P(z_coptions));
+       }
+       zend_call_method_with_0_params(&request, NULL, NULL, "getOptions", &z_roptions);
+       if (z_roptions && Z_TYPE_P(z_roptions) == IS_ARRAY) {
+               int num = zend_hash_num_elements(Z_ARRVAL_P(z_roptions));
+               if (num > num_options) {
+                       num_options = num;
+               }
+       }
+       ALLOC_HASHTABLE(options);
+       ZEND_INIT_SYMTABLE_EX(options, num_options, 0);
+       if (Z_TYPE_P(z_coptions) == IS_ARRAY) {
+               array_copy(Z_ARRVAL_P(z_coptions), options);
+       }
+       if (z_roptions) {
+               if (Z_TYPE_P(z_roptions) == IS_ARRAY) {
+                       array_join(Z_ARRVAL_P(z_roptions), options, 0, 0);
+               }
+               zval_ptr_dtor(&z_roptions);
+       }
+       return options;
+}
+
+static void msg_queue_dtor(php_http_client_enqueue_t *e)
+{
+       php_http_message_object_t *msg_obj = e->opaque;
+       TSRMLS_FETCH_FROM_CTX(msg_obj->message->ts);
+
+       zend_objects_store_del_ref_by_handle_ex(msg_obj->zv.handle, msg_obj->zv.handlers TSRMLS_CC);
+       zend_hash_destroy(e->options);
+       FREE_HASHTABLE(e->options);
+
+       if (e->closure.fci.size) {
+               zval_ptr_dtor(&e->closure.fci.function_name);
+               if (e->closure.fci.object_ptr) {
+                       zval_ptr_dtor(&e->closure.fci.object_ptr);
+               }
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_enqueue, 0, 0, 1)
+       ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 0)
+       ZEND_ARG_INFO(0, callable)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, enqueue)
+{
+       zval *request;
+       zend_fcall_info fci = empty_fcall_info;
+       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_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|f", &request, php_http_client_request_class_entry, &fci, &fcc), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       msg_obj = zend_object_store_get_object(request TSRMLS_CC);
+
+       if (php_http_client_enqueued(obj->client, msg_obj->message, NULL)) {
+               php_http_throw(bad_method_call, "Failed to enqueue request; request already in queue", NULL);
+               return;
+       }
+
+       q.request = msg_obj->message;
+       q.options = combined_options(getThis(), request TSRMLS_CC);
+       q.dtor = msg_queue_dtor;
+       q.opaque = msg_obj;
+       q.closure.fci = fci;
+       q.closure.fcc = fcc;
+
+       if (fci.size) {
+               Z_ADDREF_P(fci.function_name);
+               if (fci.object_ptr) {
+                       Z_ADDREF_P(fci.object_ptr);
+               }
+       }
+
+       zend_objects_store_add_ref_by_handle(msg_obj->zv.handle TSRMLS_CC);
+
+       php_http_expect(SUCCESS == php_http_client_enqueue(obj->client, &q), runtime,
+                       msg_queue_dtor(&q);
+                       return;
+       );
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_dequeue, 0, 0, 1)
+       ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, dequeue)
+{
+       zval *request;
+       php_http_client_object_t *obj;
+       php_http_message_object_t *msg_obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, php_http_client_request_class_entry), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       msg_obj = zend_object_store_get_object(request TSRMLS_CC);
+
+       if (!php_http_client_enqueued(obj->client, msg_obj->message, NULL)) {
+               php_http_throw(bad_method_call, "Failed to dequeue request; request not in queue", NULL);
+               return;
+       }
+
+       php_http_expect(SUCCESS == php_http_client_dequeue(obj->client, msg_obj->message), runtime, return);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_requeue, 0, 0, 1)
+       ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 0)
+       ZEND_ARG_INFO(0, callable)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, requeue)
+{
+       zval *request;
+       zend_fcall_info fci = empty_fcall_info;
+       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_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|f", &request, php_http_client_request_class_entry, &fci, &fcc), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       msg_obj = zend_object_store_get_object(request TSRMLS_CC);
+
+       if (php_http_client_enqueued(obj->client, msg_obj->message, NULL)) {
+               php_http_expect(SUCCESS == php_http_client_dequeue(obj->client, msg_obj->message), runtime, return);
+       }
+
+       q.request = msg_obj->message;
+       q.options = combined_options(getThis(), request TSRMLS_CC);
+       q.dtor = msg_queue_dtor;
+       q.opaque = msg_obj;
+       q.closure.fci = fci;
+       q.closure.fcc = fcc;
+
+       if (fci.size) {
+               Z_ADDREF_P(fci.function_name);
+               if (fci.object_ptr) {
+                       Z_ADDREF_P(fci.object_ptr);
+               }
+       }
+
+       zend_objects_store_add_ref_by_handle(msg_obj->zv.handle TSRMLS_CC);
+
+       php_http_expect(SUCCESS == php_http_client_enqueue(obj->client, &q), runtime,
+                       msg_queue_dtor(&q);
+                       return;
+       );
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_count, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, count)
+{
+       long count_mode = -1;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &count_mode)) {
+               php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               RETVAL_LONG(zend_llist_count(&obj->client->requests));
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getResponse, 0, 0, 0)
+       ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, getResponse)
+{
+       zval *zrequest = NULL;
+       php_http_client_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|O", &zrequest, php_http_client_request_class_entry), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       if (zrequest) {
+               /* lookup the response with the request */
+               zend_llist_element *el = NULL;
+               php_http_message_object_t *req_obj = zend_object_store_get_object(zrequest TSRMLS_CC);
+
+               for (el = obj->client->responses.head; el; el = el->next) {
+                       php_http_message_object_t *response_obj = *(php_http_message_object_t **) el->data;
+
+                       if (response_obj->message->parent == req_obj->message) {
+                               RETURN_OBJVAL(response_obj->zv, 1);
+                       }
+               }
+
+               /* not found for the request! */
+               php_http_throw(unexpected_val, "Could not find response for the request", NULL);
+               return;
+       }
+
+       /* pop off the last response */
+       if (obj->client->responses.tail) {
+               php_http_message_object_t *response_obj = *(php_http_message_object_t **) obj->client->responses.tail->data;
+
+               /* pop off and go */
+               if (response_obj) {
+                       RETVAL_OBJVAL(response_obj->zv, 1);
+                       zend_llist_remove_tail(&obj->client->responses);
+               }
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getHistory, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, getHistory)
+{
+       zval *zhistory;
+
+       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
+
+       zhistory = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("history"), 0 TSRMLS_CC);
+       RETVAL_ZVAL(zhistory, 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_send, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, send)
+{
+       php_http_client_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       php_http_expect(SUCCESS == php_http_client_exec(obj->client), runtime, return);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_once, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, once)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               RETURN_BOOL(0 < php_http_client_once(obj->client));
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_wait, 0, 0, 0)
+       ZEND_ARG_INFO(0, timeout)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, wait)
+{
+       double timeout = 0;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|d", &timeout)) {
+               struct timeval timeout_val;
+               php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               timeout_val.tv_sec = (time_t) timeout;
+               timeout_val.tv_usec = PHP_HTTP_USEC(timeout) % PHP_HTTP_MCROSEC;
+
+               RETURN_BOOL(SUCCESS == php_http_client_wait(obj->client, timeout > 0 ? &timeout_val : NULL));
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_configure, 0, 0, 1)
+       ZEND_ARG_ARRAY_INFO(0, settings, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, configure)
+{
+       HashTable *settings = NULL;
+       php_http_client_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|H!", &settings), invalid_arg, return);
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       php_http_expect(SUCCESS == php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_CONFIGURATION, settings), unexpected_val, return);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_enablePipelining, 0, 0, 0)
+       ZEND_ARG_INFO(0, enable)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, enablePipelining)
+{
+       zend_bool enable = 1;
+       php_http_client_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &enable), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       php_http_expect(SUCCESS == php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_ENABLE_PIPELINING, &enable), unexpected_val, return);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_enableEvents, 0, 0, 0)
+       ZEND_ARG_INFO(0, enable)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, enableEvents)
+{
+       zend_bool enable = 1;
+       php_http_client_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &enable), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       php_http_expect(SUCCESS == php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_USE_EVENTS, &enable), unexpected_val, return);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+struct notify_arg {
+       php_http_object_method_t *cb;
+       zval **args[3];
+       int argc;
+};
+
+static int notify(zend_object_iterator *iter, void *puser TSRMLS_DC)
+{
+       zval **observer = NULL;
+       struct notify_arg *arg = puser;
+
+       iter->funcs->get_current_data(iter, &observer TSRMLS_CC);
+       if (observer) {
+               return php_http_object_method_call(arg->cb, *observer, NULL, arg->argc, arg->args TSRMLS_CC);
+       }
+       return FAILURE;
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_notify, 0, 0, 0)
+       ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, notify)
+{
+       zval *request = NULL, *zprogress = NULL, *observers;
+       php_http_client_object_t *client_obj;
+       struct notify_arg arg = {NULL};
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|O!o!", &request, php_http_client_request_class_entry, &zprogress), invalid_arg, return);
+
+       client_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
+
+       if (Z_TYPE_P(observers) != IS_OBJECT) {
+               php_http_throw(unexpected_val, "Observer storage is corrupted", NULL);
+               return;
+       }
+
+       if (client_obj->update) {
+               arg.cb = client_obj->update;
+
+               Z_ADDREF_P(getThis());
+               arg.args[0] = &getThis();
+               arg.argc = 1;
+
+               if (request) {
+                       Z_ADDREF_P(request);
+                       arg.args[1] = &request;
+                       arg.argc += 1;
+               }
+
+               if (zprogress) {
+                       Z_ADDREF_P(zprogress);
+                       arg.args[2] = &zprogress;
+                       arg.argc += 1;
+               }
+
+               spl_iterator_apply(observers, notify, &arg TSRMLS_CC);
+
+               zval_ptr_dtor(&getThis());
+               if (request) {
+                       zval_ptr_dtor(&request);
+               }
+               if (zprogress) {
+                       zval_ptr_dtor(&zprogress);
+               }
+       }
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_attach, 0, 0, 1)
+       ZEND_ARG_OBJ_INFO(0, observer, SplObserver, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, attach)
+{
+       zval *observers, *observer, *retval = NULL;
+       php_http_client_object_t *client_obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &observer, spl_ce_SplObserver), invalid_arg, return);
+
+       client_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
+
+       if (Z_TYPE_P(observers) != IS_OBJECT) {
+               php_http_throw(unexpected_val, "Observer storage is corrupted", NULL);
+               return;
+       }
+
+       if (!client_obj->update) {
+               client_obj->update = php_http_object_method_init(NULL, observer, ZEND_STRL("update") TSRMLS_CC);
+       }
+
+       zend_call_method_with_1_params(&observers, NULL, NULL, "attach", &retval, observer);
+       if (retval) {
+               zval_ptr_dtor(&retval);
+       }
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_detach, 0, 0, 1)
+       ZEND_ARG_OBJ_INFO(0, observer, SplObserver, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, detach)
+{
+       zval *observers, *observer, *retval = NULL;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &observer, spl_ce_SplObserver), invalid_arg, return);
+
+       observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
+
+       if (Z_TYPE_P(observers) != IS_OBJECT) {
+               php_http_throw(unexpected_val, "Observer storage is corrupted", NULL);
+               return;
+       }
+
+       zend_call_method_with_1_params(&observers, NULL, NULL, "detach", &retval, observer);
+       if (retval) {
+               zval_ptr_dtor(&retval);
+       }
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getObservers, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, getObservers)
+{
+       zval *observers;
+
+       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
+
+       observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
+
+       if (Z_TYPE_P(observers) != IS_OBJECT) {
+               php_http_throw(unexpected_val, "Observer storage is corrupted", NULL);
+               return;
+       }
+
+       RETVAL_ZVAL(observers, 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getProgressInfo, 0, 0, 1)
+       ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, getProgressInfo)
+{
+       zval *request;
+       php_http_client_object_t *obj;
+       php_http_message_object_t *req_obj;
+       php_http_client_progress_state_t *progress;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, php_http_client_request_class_entry), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       req_obj = zend_object_store_get_object(request TSRMLS_CC);
+
+       php_http_expect(SUCCESS == php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, req_obj->message, &progress), unexpected_val, return);
+
+       object_init(return_value);
+       add_property_bool(return_value, "started", progress->started);
+       add_property_bool(return_value, "finished", progress->finished);
+       add_property_string(return_value, "info", STR_PTR(progress->info), 1);
+       add_property_double(return_value, "dltotal", progress->dl.total);
+       add_property_double(return_value, "dlnow", progress->dl.now);
+       add_property_double(return_value, "ultotal", progress->ul.total);
+       add_property_double(return_value, "ulnow", progress->ul.now);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getTransferInfo, 0, 0, 1)
+       ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, getTransferInfo)
+{
+       zval *request;
+       HashTable *info;
+       php_http_client_object_t *obj;
+       php_http_message_object_t *req_obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, php_http_client_request_class_entry), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       req_obj = zend_object_store_get_object(request TSRMLS_CC);
+
+       object_init(return_value);
+       info = HASH_OF(return_value);
+       php_http_expect(SUCCESS == php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_TRANSFER_INFO, req_obj->message, &info), unexpected_val, return);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_setOptions, 0, 0, 0)
+       ZEND_ARG_ARRAY_INFO(0, options, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, setOptions)
+{
+       zval *opts = NULL;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return);
+
+       php_http_client_options_set(getThis(), opts TSRMLS_CC);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getOptions, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, getOptions)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               zval *options = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
+               RETVAL_ZVAL(options, 1, 0);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_setSslOptions, 0, 0, 0)
+       ZEND_ARG_ARRAY_INFO(0, ssl_option, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, setSslOptions)
+{
+       zval *opts = NULL;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return);
+
+       php_http_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 1 TSRMLS_CC);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_addSslOptions, 0, 0, 0)
+       ZEND_ARG_ARRAY_INFO(0, ssl_options, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, addSslOptions)
+{
+       zval *opts = NULL;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return);
+
+       php_http_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 0 TSRMLS_CC);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getSslOptions, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, getSslOptions)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_client_options_get_subr(getThis(), ZEND_STRS("ssl"), return_value TSRMLS_CC);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_setCookies, 0, 0, 0)
+       ZEND_ARG_ARRAY_INFO(0, cookies, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, setCookies)
+{
+       zval *opts = NULL;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return);
+
+       php_http_client_options_set_subr(getThis(), ZEND_STRS("cookies"), opts, 1 TSRMLS_CC);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_addCookies, 0, 0, 0)
+       ZEND_ARG_ARRAY_INFO(0, cookies, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, addCookies)
+{
+       zval *opts = NULL;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return);
+
+       php_http_client_options_set_subr(getThis(), ZEND_STRS("cookies"), opts, 0 TSRMLS_CC);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getCookies, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, getCookies)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_client_options_get_subr(getThis(), ZEND_STRS("cookies"), return_value TSRMLS_CC);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getAvailableDrivers, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, getAvailableDrivers) {
+       if (SUCCESS == zend_parse_parameters_none()) {
+               array_init(return_value);
+               php_http_client_driver_list(Z_ARRVAL_P(return_value) TSRMLS_CC);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getAvailableOptions, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, getAvailableOptions)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               array_init(return_value);
+               php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_AVAILABLE_OPTIONS, NULL, &Z_ARRVAL_P(return_value));
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getAvailableConfiguration, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClient, getAvailableConfiguration)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               array_init(return_value);
+               php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_AVAILABLE_CONFIGURATION, NULL, &Z_ARRVAL_P(return_value));
+       }
+}
+
+static zend_function_entry php_http_client_methods[] = {
+       PHP_ME(HttpClient, __construct,          ai_HttpClient_construct,            ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+       PHP_ME(HttpClient, reset,                ai_HttpClient_reset,                ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, enqueue,              ai_HttpClient_enqueue,              ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, dequeue,              ai_HttpClient_dequeue,              ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, requeue,              ai_HttpClient_requeue,              ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, count,                ai_HttpClient_count,                ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, send,                 ai_HttpClient_send,                 ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, once,                 ai_HttpClient_once,                 ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, wait,                 ai_HttpClient_wait,                 ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, getResponse,          ai_HttpClient_getResponse,          ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, getHistory,           ai_HttpClient_getHistory,           ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, configure,            ai_HttpClient_configure,            ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, enablePipelining,     ai_HttpClient_enablePipelining,     ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+       PHP_ME(HttpClient, enableEvents,         ai_HttpClient_enableEvents,         ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+       PHP_ME(HttpClient, notify,               ai_HttpClient_notify,               ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, attach,               ai_HttpClient_attach,               ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, detach,               ai_HttpClient_detach,               ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, getObservers,         ai_HttpClient_getObservers,         ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, getProgressInfo,      ai_HttpClient_getProgressInfo,      ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, getTransferInfo,      ai_HttpClient_getTransferInfo,      ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, setOptions,           ai_HttpClient_setOptions,           ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, getOptions,           ai_HttpClient_getOptions,           ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, setSslOptions,        ai_HttpClient_setSslOptions,        ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, addSslOptions,        ai_HttpClient_addSslOptions,        ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, getSslOptions,        ai_HttpClient_getSslOptions,        ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, setCookies,           ai_HttpClient_setCookies,           ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, addCookies,           ai_HttpClient_addCookies,           ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, getCookies,           ai_HttpClient_getCookies,           ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, getAvailableDrivers,  ai_HttpClient_getAvailableDrivers,  ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       PHP_ME(HttpClient, getAvailableOptions,  ai_HttpClient_getAvailableOptions,  ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClient, getAvailableConfiguration, ai_HttpClient_getAvailableConfiguration, ZEND_ACC_PUBLIC)
+       EMPTY_FUNCTION_ENTRY
+};
+
+PHP_MINIT_FUNCTION(http_client)
+{
+       zend_class_entry ce = {0};
+
+       INIT_NS_CLASS_ENTRY(ce, "http", "Client", php_http_client_methods);
+       php_http_client_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
+       php_http_client_class_entry->create_object = php_http_client_object_new;
+       zend_class_implements(php_http_client_class_entry TSRMLS_CC, 2, spl_ce_SplSubject, spl_ce_Countable);
+       memcpy(&php_http_client_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+       php_http_client_object_handlers.clone_obj = NULL;
+       zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("observers"), ZEND_ACC_PRIVATE TSRMLS_CC);
+       zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("options"), ZEND_ACC_PROTECTED TSRMLS_CC);
+       zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("history"), ZEND_ACC_PROTECTED TSRMLS_CC);
+       zend_declare_property_bool(php_http_client_class_entry, ZEND_STRL("recordHistory"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
+
+       zend_hash_init(&php_http_client_drivers, 2, NULL, NULL, 1);
+
+       return SUCCESS;
+}
+
+PHP_MSHUTDOWN_FUNCTION(http_client)
+{
+       zend_hash_destroy(&php_http_client_drivers);
+       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_client.h b/src/php_http_client.h
new file mode 100644 (file)
index 0000000..f4a5b59
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_CLIENT_H
+#define PHP_HTTP_CLIENT_H
+
+typedef enum php_http_client_setopt_opt {
+       PHP_HTTP_CLIENT_OPT_ENABLE_PIPELINING,
+       PHP_HTTP_CLIENT_OPT_USE_EVENTS,
+       PHP_HTTP_CLIENT_OPT_CONFIGURATION,
+} php_http_client_setopt_opt_t;
+
+typedef enum php_http_client_getopt_opt {
+       PHP_HTTP_CLIENT_OPT_PROGRESS_INFO,              /* php_http_client_enqueue_t*, php_http_client_progress_state_t** */
+       PHP_HTTP_CLIENT_OPT_TRANSFER_INFO,              /* php_http_client_enqueue_t*, HashTable* */
+       PHP_HTTP_CLIENT_OPT_AVAILABLE_OPTIONS,          /* NULL, HashTable* */
+       PHP_HTTP_CLIENT_OPT_AVAILABLE_CONFIGURATION,/* NULL, HashTable */
+} php_http_client_getopt_opt_t;
+
+typedef struct php_http_client_enqueue {
+       php_http_message_t *request; /* unique */
+       HashTable *options;
+       void (*dtor)(struct php_http_client_enqueue *);
+       void *opaque;
+       struct {
+               zend_fcall_info fci;
+               zend_fcall_info_cache fcc;
+       } closure;
+} php_http_client_enqueue_t;
+
+typedef struct php_http_client *(*php_http_client_init_func_t)(struct php_http_client *p, void *init_arg);
+typedef struct php_http_client *(*php_http_client_copy_func_t)(struct php_http_client *from, struct php_http_client *to);
+typedef void (*php_http_client_dtor_func_t)(struct php_http_client *p);
+typedef void (*php_http_client_reset_func_t)(struct php_http_client *p);
+typedef ZEND_RESULT_CODE (*php_http_client_exec_func_t)(struct php_http_client *p);
+typedef int (*php_http_client_once_func_t)(struct php_http_client *p);
+typedef ZEND_RESULT_CODE (*php_http_client_wait_func_t)(struct php_http_client *p, struct timeval *custom_timeout);
+typedef ZEND_RESULT_CODE (*php_http_client_enqueue_func_t)(struct php_http_client *p, php_http_client_enqueue_t *enqueue);
+typedef ZEND_RESULT_CODE (*php_http_client_dequeue_func_t)(struct php_http_client *p, php_http_client_enqueue_t *enqueue);
+typedef ZEND_RESULT_CODE (*php_http_client_setopt_func_t)(struct php_http_client *p, php_http_client_setopt_opt_t opt, void *arg);
+typedef ZEND_RESULT_CODE (*php_http_client_getopt_func_t)(struct php_http_client *h, php_http_client_getopt_opt_t opt, void *arg, void **res);
+
+typedef struct php_http_client_ops {
+       php_resource_factory_ops_t *rsrc;
+       php_http_client_init_func_t init;
+       php_http_client_copy_func_t copy;
+       php_http_client_dtor_func_t dtor;
+       php_http_client_reset_func_t reset;
+       php_http_client_exec_func_t exec;
+       php_http_client_wait_func_t wait;
+       php_http_client_once_func_t once;
+       php_http_client_enqueue_func_t enqueue;
+       php_http_client_dequeue_func_t dequeue;
+       php_http_client_setopt_func_t setopt;
+       php_http_client_getopt_func_t getopt;
+} php_http_client_ops_t;
+
+typedef struct php_http_client_driver {
+       const char *name_str;
+       size_t name_len;
+       php_http_client_ops_t *client_ops;
+} php_http_client_driver_t;
+
+PHP_HTTP_API ZEND_RESULT_CODE php_http_client_driver_add(php_http_client_driver_t *driver);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_client_driver_get(const char *name_str, size_t name_len, php_http_client_driver_t *driver);
+
+typedef struct php_http_client_progress_state {
+       struct {
+               double now;
+               double total;
+       } ul;
+       struct {
+               double now;
+               double total;
+       } dl;
+       const char *info;
+       unsigned started:1;
+       unsigned finished:1;
+} php_http_client_progress_state_t;
+
+typedef ZEND_RESULT_CODE (*php_http_client_response_callback_t)(void *arg, struct php_http_client *client, php_http_client_enqueue_t *e, php_http_message_t **response);
+typedef void (*php_http_client_progress_callback_t)(void *arg, struct php_http_client *client, php_http_client_enqueue_t *e, php_http_client_progress_state_t *state);
+
+typedef struct php_http_client {
+       void *ctx;
+       php_resource_factory_t *rf;
+       php_http_client_ops_t *ops;
+
+       struct {
+               struct {
+                       php_http_client_response_callback_t func;
+                       void *arg;
+               } response;
+               struct {
+                       php_http_client_progress_callback_t func;
+                       void *arg;
+               } progress;
+       } callback;
+
+       zend_llist requests;
+       zend_llist responses;
+
+#ifdef ZTS
+       void ***ts;
+#endif
+} php_http_client_t;
+
+PHP_HTTP_API zend_class_entry *php_http_client_class_entry;
+
+typedef struct php_http_client_object {
+       zend_object zo;
+       zend_object_value zv;
+       php_http_client_t *client;
+       long iterator;
+       php_http_object_method_t *update;
+       php_http_object_method_t notify;
+} php_http_client_object_t;
+
+PHP_HTTP_API php_http_client_t *php_http_client_init(php_http_client_t *h, php_http_client_ops_t *ops, php_resource_factory_t *rf, void *init_arg TSRMLS_DC);
+PHP_HTTP_API php_http_client_t *php_http_client_copy(php_http_client_t *from, php_http_client_t *to);
+PHP_HTTP_API void php_http_client_dtor(php_http_client_t *h);
+PHP_HTTP_API void php_http_client_free(php_http_client_t **h);
+
+PHP_HTTP_API ZEND_RESULT_CODE php_http_client_enqueue(php_http_client_t *h, php_http_client_enqueue_t *enqueue);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_client_dequeue(php_http_client_t *h, php_http_message_t *request);
+
+PHP_HTTP_API ZEND_RESULT_CODE php_http_client_wait(php_http_client_t *h, struct timeval *custom_timeout);
+PHP_HTTP_API int php_http_client_once(php_http_client_t *h);
+
+PHP_HTTP_API ZEND_RESULT_CODE php_http_client_exec(php_http_client_t *h);
+PHP_HTTP_API void php_http_client_reset(php_http_client_t *h);
+
+PHP_HTTP_API ZEND_RESULT_CODE php_http_client_setopt(php_http_client_t *h, php_http_client_setopt_opt_t opt, void *arg);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_client_getopt(php_http_client_t *h, php_http_client_getopt_opt_t opt, void *arg, void *res_ptr);
+
+typedef int (*php_http_client_enqueue_cmp_func_t)(php_http_client_enqueue_t *cmp, void *arg);
+/* compare with request message pointer if compare_func is NULL */
+PHP_HTTP_API php_http_client_enqueue_t *php_http_client_enqueued(php_http_client_t *h, void *compare_arg, php_http_client_enqueue_cmp_func_t compare_func);
+
+PHP_MINIT_FUNCTION(http_client);
+PHP_MSHUTDOWN_FUNCTION(http_client);
+
+#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_client_curl.c b/src/php_http_client_curl.c
new file mode 100644 (file)
index 0000000..fed92fc
--- /dev/null
@@ -0,0 +1,2599 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+#include "php_http_client.h"
+
+#if PHP_HTTP_HAVE_CURL
+
+#if PHP_HTTP_HAVE_EVENT
+#      if !PHP_HTTP_HAVE_EVENT2 && /* just be really sure */ !(LIBEVENT_VERSION_NUMBER >= 0x02000000)
+#              include <event.h>
+#              define event_base_new event_init
+#              define event_assign(e, b, s, a, cb, d) do {\
+                       event_set(e, s, a, cb, d); \
+                       event_base_set(b, e); \
+               } while(0)
+#      else
+#              if PHP_HTTP_HAVE_EVENT2
+#                      include <event2/event.h>
+#                      include <event2/event_struct.h>
+#              else
+#                      error "libevent presence is unknown"
+#              endif
+#      endif
+#      ifndef DBG_EVENTS
+#              define DBG_EVENTS 0
+#      endif
+#endif
+
+#ifdef PHP_HTTP_HAVE_OPENSSL
+#      include <openssl/ssl.h>
+#endif
+#ifdef PHP_HTTP_HAVE_GNUTLS
+#      include <gnutls.h>
+#endif
+
+typedef struct php_http_client_curl {
+       CURLM *handle;
+
+       int unfinished;  /* int because of curl_multi_perform() */
+
+#if PHP_HTTP_HAVE_EVENT
+       struct event_base *evbase;
+       struct event *timeout;
+       unsigned useevents:1;
+#endif
+} php_http_client_curl_t;
+
+typedef struct php_http_client_curl_handler {
+       CURL *handle;
+       php_resource_factory_t *rf;
+       php_http_client_t *client;
+       php_http_client_progress_state_t progress;
+       php_http_client_enqueue_t queue;
+
+       struct {
+               php_http_buffer_t headers;
+               php_http_message_body_t *body;
+       } response;
+
+       struct {
+               HashTable cache;
+
+               struct curl_slist *proxyheaders;
+               struct curl_slist *headers;
+               struct curl_slist *resolve;
+               php_http_buffer_t cookies;
+               php_http_buffer_t ranges;
+
+               long redirects;
+               unsigned range_request:1;
+               unsigned encode_cookies:1;
+
+               struct {
+                       uint count;
+                       double delay;
+               } retry;
+
+       } options;
+
+} php_http_client_curl_handler_t;
+
+typedef struct php_http_curle_storage {
+       char *url;
+       char *cookiestore;
+       CURLcode errorcode;
+       char errorbuffer[0x100];
+} php_http_curle_storage_t;
+
+static inline php_http_curle_storage_t *php_http_curle_get_storage(CURL *ch) {
+       php_http_curle_storage_t *st = NULL;
+
+       curl_easy_getinfo(ch, CURLINFO_PRIVATE, &st);
+
+       if (!st) {
+               st = pecalloc(1, sizeof(*st), 1);
+               curl_easy_setopt(ch, CURLOPT_PRIVATE, st);
+               curl_easy_setopt(ch, CURLOPT_ERRORBUFFER, st->errorbuffer);
+       }
+
+       return st;
+}
+
+static void *php_http_curle_ctor(void *opaque, void *init_arg TSRMLS_DC)
+{
+       void *ch;
+
+       if ((ch = curl_easy_init())) {
+               php_http_curle_get_storage(ch);
+               return ch;
+       }
+       return NULL;
+}
+
+static void *php_http_curle_copy(void *opaque, void *handle TSRMLS_DC)
+{
+       void *ch;
+
+       if ((ch = curl_easy_duphandle(handle))) {
+               curl_easy_reset(ch);
+               php_http_curle_get_storage(ch);
+               return ch;
+       }
+       return NULL;
+}
+
+static void php_http_curle_dtor(void *opaque, void *handle TSRMLS_DC)
+{
+       php_http_curle_storage_t *st = php_http_curle_get_storage(handle);
+
+       curl_easy_cleanup(handle);
+
+       if (st) {
+               if (st->url) {
+                       pefree(st->url, 1);
+               }
+               if (st->cookiestore) {
+                       pefree(st->cookiestore, 1);
+               }
+               pefree(st, 1);
+       }
+}
+
+static php_resource_factory_ops_t php_http_curle_resource_factory_ops = {
+       php_http_curle_ctor,
+       php_http_curle_copy,
+       php_http_curle_dtor
+};
+
+static void *php_http_curlm_ctor(void *opaque, void *init_arg TSRMLS_DC)
+{
+       return curl_multi_init();
+}
+
+static void php_http_curlm_dtor(void *opaque, void *handle TSRMLS_DC)
+{
+       curl_multi_cleanup(handle);
+}
+
+static php_resource_factory_ops_t php_http_curlm_resource_factory_ops = {
+       php_http_curlm_ctor,
+       NULL,
+       php_http_curlm_dtor
+};
+
+/* curl callbacks */
+
+static size_t php_http_curle_read_callback(void *data, size_t len, size_t n, void *ctx)
+{
+       php_http_message_body_t *body = ctx;
+
+       if (body && body->stream_id) {
+               php_stream *s = php_http_message_body_stream(body);
+
+               if (s) {
+                       TSRMLS_FETCH_FROM_CTX(body->ts);
+                       return php_stream_read(s, data, len * n);
+               } else abort();
+       }
+       return 0;
+}
+
+#if PHP_HTTP_CURL_VERSION(7,32,0)
+static int php_http_curle_xferinfo_callback(void *ctx, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
+#else
+static int php_http_curle_progress_callback(void *ctx, double dltotal, double dlnow, double ultotal, double ulnow)
+#endif
+{
+       php_http_client_curl_handler_t *h = ctx;
+       zend_bool update = 0;
+
+       if (h->progress.dl.total != dltotal
+       ||      h->progress.dl.now != dlnow
+       ||      h->progress.ul.total != ultotal
+       ||      h->progress.ul.now != ulnow
+       ) {
+               update = 1;
+
+               h->progress.dl.total = dltotal;
+               h->progress.dl.now = dlnow;
+               h->progress.ul.total = ultotal;
+               h->progress.ul.now = ulnow;
+       }
+
+       if (update && h->client->callback.progress.func) {
+               h->client->callback.progress.func(h->client->callback.progress.arg, h->client, &h->queue, &h->progress);
+       }
+
+       return 0;
+}
+
+static int php_http_curle_seek_callback(void *userdata, curl_off_t offset, int origin)
+{
+       php_http_message_body_t *body = userdata;
+       TSRMLS_FETCH_FROM_CTX(body->ts);
+
+       if (!body) {
+               return 1;
+       }
+       if (0 == php_stream_seek(php_http_message_body_stream(body), offset, origin)) {
+               return 0;
+       }
+       return 2;
+}
+
+static int php_http_curle_raw_callback(CURL *ch, curl_infotype type, char *data, size_t length, void *ctx)
+{
+       php_http_client_curl_handler_t *h = ctx;
+
+       /* catch progress */
+       switch (type) {
+               case CURLINFO_TEXT:
+                       if (data[0] == '-') {
+                       } 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)) {
+                               h->progress.info = "setup";
+                       } else if (php_memnstr(data, ZEND_STRL("About to connect"), data + length)) {
+                               h->progress.info = "resolve";
+                       } else if (php_memnstr(data, ZEND_STRL("Trying"), data + length)) {
+                               h->progress.info = "connect";
+                       } else if (php_memnstr(data, ZEND_STRL("Found bundle for host"), data + length)) {
+                               h->progress.info = "connect";
+                       } else if (php_memnstr(data, ZEND_STRL("Connected"), data + length)) {
+                               h->progress.info = "connected";
+                       } else if (php_memnstr(data, ZEND_STRL("Re-using existing connection!"), data + length)) {
+                               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("SSL"), data + length)) {
+                               h->progress.info = "ssl negotiation";
+                       } 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)) {
+                               h->progress.info = "not disconnected";
+                       } else if (php_memnstr(data, ZEND_STRL("closed"), data + length)) {
+                               h->progress.info = "disconnected";
+                       } else if (php_memnstr(data, ZEND_STRL("Issue another request"), data + length)) {
+                               h->progress.info = "redirect";
+                       } else if (php_memnstr(data, ZEND_STRL("Operation timed out"), data + length)) {
+                               h->progress.info = "timeout";
+                       } else {
+#if 0
+                               h->progress.info = data;
+                               data[length - 1] = '\0';
+#endif
+                       }
+                       if (h->client->callback.progress.func) {
+                               h->client->callback.progress.func(h->client->callback.progress.arg, h->client, &h->queue, &h->progress);
+                       }
+                       break;
+               case CURLINFO_HEADER_OUT:
+               case CURLINFO_DATA_OUT:
+               case CURLINFO_SSL_DATA_OUT:
+                       h->progress.info = "send";
+                       break;
+               case CURLINFO_HEADER_IN:
+               case CURLINFO_DATA_IN:
+               case CURLINFO_SSL_DATA_IN:
+                       h->progress.info = "receive";
+                       break;
+               default:
+                       break;
+       }
+
+#if 0
+       /* debug */
+       _dpf(type, data, length);
+#endif
+
+       return 0;
+}
+
+static int php_http_curle_header_callback(char *data, size_t n, size_t l, void *arg)
+{
+       php_http_client_curl_handler_t *h = arg;
+
+       return php_http_buffer_append(&h->response.headers, data, n * l);
+}
+
+static int php_http_curle_body_callback(char *data, size_t n, size_t l, void *arg)
+{
+       php_http_client_curl_handler_t *h = arg;
+
+       return php_http_message_body_append(h->response.body, data, n*l);
+}
+
+static ZEND_RESULT_CODE php_http_curle_get_info(CURL *ch, HashTable *info)
+{
+       char *c;
+       long l;
+       double d;
+       struct curl_slist *s, *p;
+       zval *subarray, array;
+       INIT_PZVAL_ARRAY(&array, info);
+
+       /* BEGIN::CURLINFO */
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_EFFECTIVE_URL, &c)) {
+               add_assoc_string_ex(&array, "effective_url", sizeof("effective_url"), c ? c : "", 1);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &l)) {
+               add_assoc_long_ex(&array, "response_code", sizeof("response_code"), l);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_TOTAL_TIME, &d)) {
+               add_assoc_double_ex(&array, "total_time", sizeof("total_time"), d);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_NAMELOOKUP_TIME, &d)) {
+               add_assoc_double_ex(&array, "namelookup_time", sizeof("namelookup_time"), d);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONNECT_TIME, &d)) {
+               add_assoc_double_ex(&array, "connect_time", sizeof("connect_time"), d);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PRETRANSFER_TIME, &d)) {
+               add_assoc_double_ex(&array, "pretransfer_time", sizeof("pretransfer_time"), d);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SIZE_UPLOAD, &d)) {
+               add_assoc_double_ex(&array, "size_upload", sizeof("size_upload"), d);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SIZE_DOWNLOAD, &d)) {
+               add_assoc_double_ex(&array, "size_download", sizeof("size_download"), d);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SPEED_DOWNLOAD, &d)) {
+               add_assoc_double_ex(&array, "speed_download", sizeof("speed_download"), d);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SPEED_UPLOAD, &d)) {
+               add_assoc_double_ex(&array, "speed_upload", sizeof("speed_upload"), d);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HEADER_SIZE, &l)) {
+               add_assoc_long_ex(&array, "header_size", sizeof("header_size"), l);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REQUEST_SIZE, &l)) {
+               add_assoc_long_ex(&array, "request_size", sizeof("request_size"), l);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SSL_VERIFYRESULT, &l)) {
+               add_assoc_long_ex(&array, "ssl_verifyresult", sizeof("ssl_verifyresult"), l);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_FILETIME, &l)) {
+               add_assoc_long_ex(&array, "filetime", sizeof("filetime"), l);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d)) {
+               add_assoc_double_ex(&array, "content_length_download", sizeof("content_length_download"), d);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_LENGTH_UPLOAD, &d)) {
+               add_assoc_double_ex(&array, "content_length_upload", sizeof("content_length_upload"), d);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_STARTTRANSFER_TIME, &d)) {
+               add_assoc_double_ex(&array, "starttransfer_time", sizeof("starttransfer_time"), d);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_TYPE, &c)) {
+               add_assoc_string_ex(&array, "content_type", sizeof("content_type"), c ? c : "", 1);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_TIME, &d)) {
+               add_assoc_double_ex(&array, "redirect_time", sizeof("redirect_time"), d);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_COUNT, &l)) {
+               add_assoc_long_ex(&array, "redirect_count", sizeof("redirect_count"), l);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HTTP_CONNECTCODE, &l)) {
+               add_assoc_long_ex(&array, "connect_code", sizeof("connect_code"), l);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HTTPAUTH_AVAIL, &l)) {
+               add_assoc_long_ex(&array, "httpauth_avail", sizeof("httpauth_avail"), l);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PROXYAUTH_AVAIL, &l)) {
+               add_assoc_long_ex(&array, "proxyauth_avail", sizeof("proxyauth_avail"), l);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_OS_ERRNO, &l)) {
+               add_assoc_long_ex(&array, "os_errno", sizeof("os_errno"), l);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_NUM_CONNECTS, &l)) {
+               add_assoc_long_ex(&array, "num_connects", sizeof("num_connects"), l);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SSL_ENGINES, &s)) {
+               MAKE_STD_ZVAL(subarray);
+               array_init(subarray);
+               for (p = s; p; p = p->next) {
+                       if (p->data) {
+                               add_next_index_string(subarray, p->data, 1);
+                       }
+               }
+               add_assoc_zval_ex(&array, "ssl_engines", sizeof("ssl_engines"), subarray);
+               curl_slist_free_all(s);
+       }
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_URL, &c)) {
+               add_assoc_string_ex(&array, "redirect_url", sizeof("redirect_url"), c ? c : "", 1);
+       }
+#if PHP_HTTP_CURL_VERSION(7,19,0)
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PRIMARY_IP, &c)) {
+               add_assoc_string_ex(&array, "primary_ip", sizeof("primary_ip"), c ? c : "", 1);
+       }
+#endif
+#if PHP_HTTP_CURL_VERSION(7,19,0)
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_APPCONNECT_TIME, &d)) {
+               add_assoc_double_ex(&array, "appconnect_time", sizeof("appconnect_time"), d);
+       }
+#endif
+#if PHP_HTTP_CURL_VERSION(7,19,4)
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONDITION_UNMET, &l)) {
+               add_assoc_long_ex(&array, "condition_unmet", sizeof("condition_unmet"), l);
+       }
+#endif
+#if PHP_HTTP_CURL_VERSION(7,21,0)
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PRIMARY_PORT, &l)) {
+               add_assoc_long_ex(&array, "primary_port", sizeof("primary_port"), l);
+       }
+#endif
+#if PHP_HTTP_CURL_VERSION(7,21,0)
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_LOCAL_IP, &c)) {
+               add_assoc_string_ex(&array, "local_ip", sizeof("local_ip"), c ? c : "", 1);
+       }
+#endif
+#if PHP_HTTP_CURL_VERSION(7,21,0)
+       if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_LOCAL_PORT, &l)) {
+               add_assoc_long_ex(&array, "local_port", sizeof("local_port"), l);
+       }
+#endif
+
+       /* END::CURLINFO */
+
+#if PHP_HTTP_CURL_VERSION(7,34,0)
+       {
+               zval *ti_array;
+               struct curl_tlssessioninfo *ti;
+
+               if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_TLS_SESSION, &ti)) {
+                       const char *backend;
+
+                       MAKE_STD_ZVAL(subarray);
+                       ZVAL_NULL(subarray);
+                       MAKE_STD_ZVAL(ti_array);
+                       array_init(ti_array);
+
+                       switch (ti->backend) {
+                       case CURLSSLBACKEND_NONE:
+                               backend = "none";
+                               break;
+                       case CURLSSLBACKEND_OPENSSL:
+                               backend = "openssl";
+#ifdef PHP_HTTP_HAVE_OPENSSL
+                               {
+                                       SSL_CTX *ctx = ti->internals;
+
+                                       array_init(subarray);
+                                       add_assoc_long_ex(subarray, ZEND_STRS("number"), SSL_CTX_sess_number(ctx));
+                                       add_assoc_long_ex(subarray, ZEND_STRS("connect"), SSL_CTX_sess_connect(ctx));
+                                       add_assoc_long_ex(subarray, ZEND_STRS("connect_good"), SSL_CTX_sess_connect_good(ctx));
+                                       add_assoc_long_ex(subarray, ZEND_STRS("connect_renegotiate"), SSL_CTX_sess_connect_renegotiate(ctx));
+                                       add_assoc_long_ex(subarray, ZEND_STRS("hits"), SSL_CTX_sess_hits(ctx));
+                                       add_assoc_long_ex(subarray, ZEND_STRS("cache_full"), SSL_CTX_sess_cache_full(ctx));
+                               }
+#endif
+                               break;
+                       case CURLSSLBACKEND_GNUTLS:
+                               backend = "gnutls";
+#ifdef PHP_HTTP_HAVE_GNUTLS
+                               {
+                                       gnutls_session_t sess = ti->internals;
+                                       char *desc;
+
+                                       array_init(subarray);
+                                       if ((desc = gnutls_session_get_desc(sess))) {
+                                               add_assoc_string_ex(subarray, ZEND_STRS("desc"), desc, 1);
+                                               gnutls_free(desc);
+                                       }
+                                       add_assoc_bool_ex(subarray, ZEND_STRS("resumed"), gnutls_session_is_resumed(sess));
+                               }
+#endif
+                               break;
+                       case CURLSSLBACKEND_NSS:
+                               backend = "nss";
+                               break;
+#if !PHP_HTTP_CURL_VERSION(7,39,0)
+                       case CURLSSLBACKEND_QSOSSL:
+                               backend = "qsossl";
+                               break;
+#else
+                       case CURLSSLBACKEND_GSKIT:
+                               backend = "gskit";
+                               break;
+#endif
+                       case CURLSSLBACKEND_POLARSSL:
+                               backend = "polarssl";
+                               break;
+                       case CURLSSLBACKEND_CYASSL:
+                               backend = "cyassl";
+                               break;
+                       case CURLSSLBACKEND_SCHANNEL:
+                               backend = "schannel";
+                               break;
+                       case CURLSSLBACKEND_DARWINSSL:
+                               backend = "darwinssl";
+                               break;
+                       default:
+                               backend = "unknown";
+                       }
+                       add_assoc_string_ex(ti_array, ZEND_STRS("backend"), estrdup(backend), 0);
+                       add_assoc_zval_ex(ti_array, ZEND_STRS("internals"), subarray);
+                       add_assoc_zval_ex(&array, "tls_session", sizeof("tls_session"), ti_array);
+               }
+       }
+#endif
+
+#if (PHP_HTTP_CURL_VERSION(7,19,1) && defined(PHP_HTTP_HAVE_OPENSSL)) || (PHP_HTTP_CURL_VERSION(7,34,0) && defined(PHP_HTTP_HAVE_NSS)) || (PHP_HTTP_CURL_VERSION(7,42,0) && defined(PHP_HTTP_HAVE_GNUTLS)) || (PHP_HTTP_CURL_VERSION(7,39,0) && defined(PHP_HTTP_HAVE_GSKIT))
+       {
+               int i;
+               zval *ci_array;
+               struct curl_certinfo *ci;
+               char *colon, *keyname;
+
+               if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CERTINFO, &ci)) {
+                       MAKE_STD_ZVAL(ci_array);
+                       array_init(ci_array);
+
+                       for (i = 0; i < ci->num_of_certs; ++i) {
+                               s = ci->certinfo[i];
+
+                               MAKE_STD_ZVAL(subarray);
+                               array_init(subarray);
+                               for (p = s; p; p = p->next) {
+                                       if (p->data) {
+                                               if ((colon = strchr(p->data, ':'))) {
+                                                       keyname = estrndup(p->data, colon - p->data);
+                                                       add_assoc_string_ex(subarray, keyname, colon - p->data + 1, colon + 1, 1);
+                                                       efree(keyname);
+                                               } else {
+                                                       add_next_index_string(subarray, p->data, 1);
+                                               }
+                                       }
+                               }
+                               add_next_index_zval(ci_array, subarray);
+                       }
+                       add_assoc_zval_ex(&array, "certinfo", sizeof("certinfo"), ci_array);
+               }
+       }
+#endif
+       {
+               php_http_curle_storage_t *st = php_http_curle_get_storage(ch);
+
+               add_assoc_long_ex(&array, "curlcode", sizeof("curlcode"), st->errorcode);
+               add_assoc_string_ex(&array, "error", sizeof("error"), st->errorbuffer, 1);
+       }
+
+       return SUCCESS;
+}
+
+static int compare_queue(php_http_client_enqueue_t *e, void *handle)
+{
+       return handle == ((php_http_client_curl_handler_t *) e->opaque)->handle;
+}
+
+static php_http_message_t *php_http_curlm_responseparser(php_http_client_curl_handler_t *h TSRMLS_DC)
+{
+       php_http_message_t *response;
+       php_http_header_parser_t parser;
+       zval *zh;
+
+       response = php_http_message_init(NULL, 0, h->response.body TSRMLS_CC);
+       php_http_header_parser_init(&parser TSRMLS_CC);
+       while (h->response.headers.used) {
+               php_http_header_parser_state_t st = php_http_header_parser_parse(&parser,
+                               &h->response.headers, PHP_HTTP_HEADER_PARSER_CLEANUP, &response->hdrs,
+                               (php_http_info_callback_t) php_http_message_info_callback, (void *) &response);
+               if (PHP_HTTP_HEADER_PARSER_STATE_FAILURE == st) {
+                       break;
+               }
+       }
+       php_http_header_parser_dtor(&parser);
+
+       /* move body to right message */
+       if (response->body != h->response.body) {
+               php_http_message_t *ptr = response;
+
+               while (ptr->parent) {
+                       ptr = ptr->parent;
+               }
+               php_http_message_body_free(&response->body);
+               response->body = ptr->body;
+               ptr->body = NULL;
+       }
+       php_http_message_body_addref(h->response.body);
+
+       /* let's update the response headers */
+       if ((zh = php_http_message_header(response, ZEND_STRL("Content-Length"), 1))) {
+               zend_hash_update(&response->hdrs, "X-Original-Content-Length", sizeof("X-Original-Content-Length"), &zh, sizeof(zval *), NULL);
+       }
+       if ((zh = php_http_message_header(response, ZEND_STRL("Transfer-Encoding"), 0))) {
+               zend_hash_update(&response->hdrs, "X-Original-Transfer-Encoding", sizeof("X-Original-Transfer-Encoding"), (void *) &zh, sizeof(zval *), NULL);
+               zend_hash_del(&response->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding"));
+       }
+       if ((zh = php_http_message_header(response, ZEND_STRL("Content-Range"), 0))) {
+               zend_hash_update(&response->hdrs, "X-Original-Content-Range", sizeof("X-Original-Content-Range"), &zh, sizeof(zval *), NULL);
+               zend_hash_del(&response->hdrs, "Content-Range", sizeof("Content-Range"));
+       }
+       if ((zh = php_http_message_header(response, ZEND_STRL("Content-Encoding"), 0))) {
+               zend_hash_update(&response->hdrs, "X-Original-Content-Encoding", sizeof("X-Original-Content-Encoding"), &zh, sizeof(zval *), NULL);
+               zend_hash_del(&response->hdrs, "Content-Encoding", sizeof("Content-Encoding"));
+       }
+       php_http_message_update_headers(response);
+
+       return response;
+}
+
+static void php_http_curlm_responsehandler(php_http_client_t *context)
+{
+       int err_count = 0, remaining = 0;
+       php_http_curle_storage_t *st, *err = NULL;
+       php_http_client_enqueue_t *enqueue;
+       php_http_client_curl_t *curl = context->ctx;
+       TSRMLS_FETCH_FROM_CTX(context->ts);
+
+       do {
+               CURLMsg *msg = curl_multi_info_read(curl->handle, &remaining);
+
+               if (msg && CURLMSG_DONE == msg->msg) {
+                       if (CURLE_OK != msg->data.result) {
+                               st = php_http_curle_get_storage(msg->easy_handle);
+                               st->errorcode = msg->data.result;
+
+                               /* defer the warnings/exceptions, so the callback is still called for this request */
+                               if (!err) {
+                                       err = ecalloc(remaining + 1, sizeof(*err));
+                               }
+                               memcpy(&err[err_count], st, sizeof(*st));
+                               if (st->url) {
+                                       err[err_count].url = estrdup(st->url);
+                               }
+                               err_count++;
+                       }
+
+                       if ((enqueue = php_http_client_enqueued(context, msg->easy_handle, compare_queue))) {
+                               php_http_client_curl_handler_t *handler = enqueue->opaque;
+                               php_http_message_t *response = php_http_curlm_responseparser(handler TSRMLS_CC);
+
+                               if (response) {
+                                       context->callback.response.func(context->callback.response.arg, context, &handler->queue, &response);
+                                       php_http_message_free(&response);
+                               }
+                       }
+               }
+       } while (remaining);
+
+       if (err_count) {
+               int i = 0;
+
+               do {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s; %s (%s)", curl_easy_strerror(err[i].errorcode), err[i].errorbuffer, STR_PTR(err[i].url));
+                       if (err[i].url) {
+                               efree(err[i].url);
+                       }
+               } while (++i < err_count);
+
+               efree(err);
+       }
+}
+
+#if PHP_HTTP_HAVE_EVENT
+
+typedef struct php_http_curlm_event {
+       struct event evnt;
+       php_http_client_t *context;
+} php_http_curlm_event_t;
+
+static inline int etoca(short action) {
+       switch (action & (EV_READ|EV_WRITE)) {
+               case EV_READ:
+                       return CURL_CSELECT_IN;
+                       break;
+               case EV_WRITE:
+                       return CURL_CSELECT_OUT;
+                       break;
+               case EV_READ|EV_WRITE:
+                       return CURL_CSELECT_IN|CURL_CSELECT_OUT;
+                       break;
+               default:
+                       return 0;
+       }
+}
+
+static void php_http_curlm_timeout_callback(int socket, short action, void *event_data)
+{
+       php_http_client_t *context = event_data;
+       php_http_client_curl_t *curl = context->ctx;
+
+#if DBG_EVENTS
+       fprintf(stderr, "T");
+#endif
+       if (curl->useevents) {
+               CURLMcode rc;
+               TSRMLS_FETCH_FROM_CTX(context->ts);
+
+               /* ignore and use -1,0 on timeout */
+               (void) socket;
+               (void) action;
+
+               while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle, CURL_SOCKET_TIMEOUT, 0, &curl->unfinished)));
+
+               if (CURLM_OK != rc) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s",  curl_multi_strerror(rc));
+               }
+
+               php_http_curlm_responsehandler(context);
+       }
+}
+
+static void php_http_curlm_event_callback(int socket, short action, void *event_data)
+{
+       php_http_client_t *context = event_data;
+       php_http_client_curl_t *curl = context->ctx;
+
+#if DBG_EVENTS
+       fprintf(stderr, "E");
+#endif
+       if (curl->useevents) {
+               CURLMcode rc = CURLM_OK;
+               TSRMLS_FETCH_FROM_CTX(context->ts);
+
+               while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle, socket, etoca(action), &curl->unfinished)));
+
+               if (CURLM_OK != rc) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", curl_multi_strerror(rc));
+               }
+
+               php_http_curlm_responsehandler(context);
+
+               /* remove timeout if there are no transfers left */
+               if (!curl->unfinished && event_initialized(curl->timeout) && event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
+                       event_del(curl->timeout);
+               }
+       }
+}
+
+static int php_http_curlm_socket_callback(CURL *easy, curl_socket_t sock, int action, void *socket_data, void *assign_data)
+{
+       php_http_client_t *context = socket_data;
+       php_http_client_curl_t *curl = context->ctx;
+
+#if DBG_EVENTS
+       fprintf(stderr, "S");
+#endif
+       if (curl->useevents) {
+               int events = EV_PERSIST;
+               php_http_curlm_event_t *ev = assign_data;
+               TSRMLS_FETCH_FROM_CTX(context->ts);
+
+               if (!ev) {
+                       ev = ecalloc(1, sizeof(php_http_curlm_event_t));
+                       ev->context = context;
+                       curl_multi_assign(curl->handle, sock, ev);
+               } else {
+                       event_del(&ev->evnt);
+               }
+
+               switch (action) {
+                       case CURL_POLL_IN:
+                               events |= EV_READ;
+                               break;
+                       case CURL_POLL_OUT:
+                               events |= EV_WRITE;
+                               break;
+                       case CURL_POLL_INOUT:
+                               events |= EV_READ|EV_WRITE;
+                               break;
+
+                       case CURL_POLL_REMOVE:
+                               efree(ev);
+                               /* no break */
+                       case CURL_POLL_NONE:
+                               return 0;
+
+                       default:
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown socket action %d", action);
+                               return -1;
+               }
+
+               event_assign(&ev->evnt, curl->evbase, sock, events, php_http_curlm_event_callback, context);
+               event_add(&ev->evnt, NULL);
+       }
+
+       return 0;
+}
+
+static void php_http_curlm_timer_callback(CURLM *multi, long timeout_ms, void *timer_data)
+{
+       php_http_client_t *context = timer_data;
+       php_http_client_curl_t *curl = context->ctx;
+
+#if DBG_EVENTS
+       fprintf(stderr, "\ntimer <- timeout_ms: %ld\n", timeout_ms);
+#endif
+       if (curl->useevents) {
+
+               if (timeout_ms < 0) {
+                       php_http_curlm_timeout_callback(CURL_SOCKET_TIMEOUT, /*EV_READ|EV_WRITE*/0, context);
+               } else if (timeout_ms > 0 || !event_initialized(curl->timeout) || !event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
+                       struct timeval timeout;
+
+                       if (!event_initialized(curl->timeout)) {
+                               event_assign(curl->timeout, curl->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_curlm_timeout_callback, context);
+                       }
+
+                       timeout.tv_sec = timeout_ms / 1000;
+                       timeout.tv_usec = (timeout_ms % 1000) * 1000;
+
+                       event_add(curl->timeout, &timeout);
+               }
+       }
+}
+
+#endif /* HAVE_EVENT */
+
+/* curl options */
+
+static php_http_options_t php_http_curle_options, php_http_curlm_options;
+
+#define PHP_HTTP_CURLE_OPTION_CHECK_STRLEN             0x0001
+#define PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR            0x0002
+#define PHP_HTTP_CURLE_OPTION_TRANSFORM_MS             0x0004
+
+static ZEND_RESULT_CODE php_http_curle_option_set_ssl_verifyhost(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+       CURL *ch = curl->handle;
+
+       if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_SSL_VERIFYHOST, Z_BVAL_P(val) ? 2 : 0)) {
+               return FAILURE;
+       }
+       return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_cookiestore(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+       CURL *ch = curl->handle;
+       php_http_curle_storage_t *storage = php_http_curle_get_storage(curl->handle);
+
+       if (storage->cookiestore) {
+               pefree(storage->cookiestore, 1);
+       }
+       if (val && Z_STRLEN_P(val)) {
+               storage->cookiestore = pestrndup(Z_STRVAL_P(val), Z_STRLEN_P(val), 1);
+       } else {
+               storage->cookiestore = NULL;
+       }
+       if (    CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIEFILE, storage->cookiestore)
+               ||      CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIEJAR, storage->cookiestore)
+       ) {
+               return FAILURE;
+       }
+       return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_cookies(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+       CURL *ch = curl->handle;
+       TSRMLS_FETCH_FROM_CTX(curl->client->ts);
+
+       if (val && Z_TYPE_P(val) != IS_NULL) {
+               if (curl->options.encode_cookies) {
+                       if (SUCCESS == php_http_url_encode_hash_ex(HASH_OF(val), &curl->options.cookies, ZEND_STRL(";"), ZEND_STRL("="), NULL, 0 TSRMLS_CC)) {
+                               php_http_buffer_fix(&curl->options.cookies);
+                               if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIE, curl->options.cookies.data)) {
+                                       return FAILURE;
+                               }
+                       } else {
+                               return FAILURE;
+                       }
+               } else {
+                       HashPosition pos;
+                       php_http_array_hashkey_t cookie_key = php_http_array_hashkey_init(0);
+                       zval **cookie_val;
+
+                       FOREACH_KEYVAL(pos, val, cookie_key, cookie_val) {
+                               zval *zv = php_http_ztyp(IS_STRING, *cookie_val);
+
+                               php_http_array_hashkey_stringify(&cookie_key);
+                               php_http_buffer_appendf(&curl->options.cookies, "%s=%s; ", cookie_key.str, Z_STRVAL_P(zv));
+                               php_http_array_hashkey_stringfree(&cookie_key);
+
+                               zval_ptr_dtor(&zv);
+                       }
+
+                       php_http_buffer_fix(&curl->options.cookies);
+                       if (curl->options.cookies.used) {
+                               if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIE, curl->options.cookies.data)) {
+                                       return FAILURE;
+                               }
+                       }
+               }
+       } else {
+               php_http_buffer_reset(&curl->options.cookies);
+               if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIE, NULL)) {
+                       return FAILURE;
+               }
+       }
+       return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_encodecookies(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+
+       curl->options.encode_cookies = Z_BVAL_P(val);
+       return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_lastmodified(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+       CURL *ch = curl->handle;
+       TSRMLS_FETCH_FROM_CTX(curl->client->ts);
+
+       if (Z_LVAL_P(val)) {
+               if (Z_LVAL_P(val) > 0) {
+                       if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, Z_LVAL_P(val))) {
+                               return FAILURE;
+                       }
+               } else {
+                       if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, (long) sapi_get_request_time(TSRMLS_C) + Z_LVAL_P(val))) {
+                               return FAILURE;
+                       }
+               }
+               if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMECONDITION, (long) (curl->options.range_request ? CURL_TIMECOND_IFUNMODSINCE : CURL_TIMECOND_IFMODSINCE))) {
+                       return FAILURE;
+               }
+       } else {
+               if (    CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, 0)
+                       ||      CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMECONDITION, 0)
+               ) {
+                       return FAILURE;
+               }
+       }
+       return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_compress(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+       CURL *ch = curl->handle;
+
+#if !PHP_HTTP_CURL_VERSION(7,21,6)
+#      define CURLOPT_ACCEPT_ENCODING CURLOPT_ENCODING
+#endif
+       if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_ACCEPT_ENCODING, Z_BVAL_P(val) ? "" : NULL)) {
+               return FAILURE;
+       }
+       return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_etag(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+       php_http_buffer_t header;
+
+       if (Z_STRLEN_P(val)) {
+               zend_bool is_quoted = !((Z_STRVAL_P(val)[0] != '"') || (Z_STRVAL_P(val)[Z_STRLEN_P(val)-1] != '"'));
+               php_http_buffer_init(&header);
+               php_http_buffer_appendf(&header, is_quoted?"%s: %s":"%s: \"%s\"", curl->options.range_request?"If-Match":"If-None-Match", Z_STRVAL_P(val));
+               php_http_buffer_fix(&header);
+               curl->options.headers = curl_slist_append(curl->options.headers, header.data);
+               php_http_buffer_dtor(&header);
+       }
+       return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_range(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+       CURL *ch = curl->handle;
+       TSRMLS_FETCH_FROM_CTX(curl->client->ts);
+
+       php_http_buffer_reset(&curl->options.ranges);
+
+       if (val && Z_TYPE_P(val) != IS_NULL) {
+               HashPosition pos;
+               zval **rr, **rb, **re;
+
+               FOREACH_VAL(pos, val, rr) {
+                       if (Z_TYPE_PP(rr) == IS_ARRAY) {
+                               if (2 == php_http_array_list(Z_ARRVAL_PP(rr) TSRMLS_CC, 2, &rb, &re)) {
+                                       if (    ((Z_TYPE_PP(rb) == IS_LONG) || ((Z_TYPE_PP(rb) == IS_STRING) && is_numeric_string(Z_STRVAL_PP(rb), Z_STRLEN_PP(rb), NULL, NULL, 1))) &&
+                                                       ((Z_TYPE_PP(re) == IS_LONG) || ((Z_TYPE_PP(re) == IS_STRING) && is_numeric_string(Z_STRVAL_PP(re), Z_STRLEN_PP(re), NULL, NULL, 1)))) {
+                                               zval *rbl = php_http_ztyp(IS_LONG, *rb);
+                                               zval *rel = php_http_ztyp(IS_LONG, *re);
+
+                                               if ((Z_LVAL_P(rbl) >= 0) && (Z_LVAL_P(rel) >= 0)) {
+                                                       php_http_buffer_appendf(&curl->options.ranges, "%ld-%ld,", Z_LVAL_P(rbl), Z_LVAL_P(rel));
+                                               }
+                                               zval_ptr_dtor(&rbl);
+                                               zval_ptr_dtor(&rel);
+                                       }
+
+                               }
+                       }
+               }
+
+               if (curl->options.ranges.used) {
+                       curl->options.range_request = 1;
+                       /* ditch last comma */
+                       curl->options.ranges.data[curl->options.ranges.used - 1] = '\0';
+               }
+       }
+
+       if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RANGE, curl->options.ranges.data)) {
+               return FAILURE;
+       }
+       return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_resume(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+       CURL *ch = curl->handle;
+
+       if (Z_LVAL_P(val) > 0) {
+               curl->options.range_request = 1;
+       }
+       if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RESUME_FROM, Z_LVAL_P(val))) {
+               return FAILURE;
+       }
+       return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_retrydelay(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+
+       curl->options.retry.delay = Z_DVAL_P(val);
+       return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_retrycount(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+
+       curl->options.retry.count = Z_LVAL_P(val);
+       return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_redirect(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+       CURL *ch = curl->handle;
+
+       if (    CURLE_OK != curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, Z_LVAL_P(val) ? 1L : 0L)
+               ||      CURLE_OK != curl_easy_setopt(ch, CURLOPT_MAXREDIRS, curl->options.redirects = Z_LVAL_P(val))
+       ) {
+               return FAILURE;
+       }
+       return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_portrange(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+       CURL *ch = curl->handle;
+       long localport = 0, localportrange = 0;
+       TSRMLS_FETCH_FROM_CTX(curl->client->ts);
+
+       if (val && Z_TYPE_P(val) != IS_NULL) {
+               zval **z_port_start, *zps_copy = NULL, **z_port_end, *zpe_copy = NULL;
+
+               switch (php_http_array_list(Z_ARRVAL_P(val) TSRMLS_CC, 2, &z_port_start, &z_port_end)) {
+               case 2:
+                       zps_copy = php_http_ztyp(IS_LONG, *z_port_start);
+                       zpe_copy = php_http_ztyp(IS_LONG, *z_port_end);
+                       localportrange = labs(Z_LVAL_P(zps_copy)-Z_LVAL_P(zpe_copy))+1L;
+                       /* no break */
+               case 1:
+                       if (!zps_copy) {
+                               zps_copy = php_http_ztyp(IS_LONG, *z_port_start);
+                       }
+                       localport = (zpe_copy && Z_LVAL_P(zpe_copy) > 0) ? MIN(Z_LVAL_P(zps_copy), Z_LVAL_P(zpe_copy)) : Z_LVAL_P(zps_copy);
+                       zval_ptr_dtor(&zps_copy);
+                       if (zpe_copy) {
+                               zval_ptr_dtor(&zpe_copy);
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+       if (    CURLE_OK != curl_easy_setopt(ch, CURLOPT_LOCALPORT, localport)
+               ||      CURLE_OK != curl_easy_setopt(ch, CURLOPT_LOCALPORTRANGE, localportrange)
+       ) {
+               return FAILURE;
+       }
+       return SUCCESS;
+}
+
+#if PHP_HTTP_CURL_VERSION(7,37,0)
+static ZEND_RESULT_CODE php_http_curle_option_set_proxyheader(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+       TSRMLS_FETCH_FROM_CTX(curl->client->ts);
+
+       if (val && Z_TYPE_P(val) != IS_NULL) {
+               php_http_array_hashkey_t header_key = php_http_array_hashkey_init(0);
+               zval **header_val, *header_cpy;
+               HashPosition pos;
+               php_http_buffer_t header;
+
+               php_http_buffer_init(&header);
+               FOREACH_KEYVAL(pos, val, header_key, header_val) {
+                       if (header_key.type == HASH_KEY_IS_STRING) {
+                               header_cpy = php_http_ztyp(IS_STRING, *header_val);
+                               php_http_buffer_appendf(&header, "%s: %s", header_key.str, Z_STRVAL_P(header_cpy));
+                               php_http_buffer_fix(&header);
+                               curl->options.proxyheaders = curl_slist_append(curl->options.proxyheaders, header.data);
+                               php_http_buffer_reset(&header);
+
+                               zval_ptr_dtor(&header_cpy);
+                       }
+               }
+               php_http_buffer_dtor(&header);
+       }
+       if (CURLE_OK != curl_easy_setopt(curl->handle, CURLOPT_PROXYHEADER, curl->options.proxyheaders)) {
+               return FAILURE;
+       }
+       if (CURLE_OK != curl_easy_setopt(curl->handle, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE)) {
+               curl_easy_setopt(curl->handle, CURLOPT_PROXYHEADER, NULL);
+               return FAILURE;
+       }
+       return SUCCESS;
+}
+#endif
+
+#if PHP_HTTP_CURL_VERSION(7,21,3)
+static ZEND_RESULT_CODE php_http_curle_option_set_resolve(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+       CURL *ch = curl->handle;
+       TSRMLS_FETCH_FROM_CTX(curl->client->ts);
+
+       if (val && Z_TYPE_P(val) != IS_NULL) {
+               php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+               HashPosition pos;
+               zval **data;
+
+               FOREACH_KEYVAL(pos, val, key, data) {
+                       zval *cpy = php_http_ztyp(IS_STRING, *data);
+                       curl->options.resolve = curl_slist_append(curl->options.resolve, Z_STRVAL_P(cpy));
+                       zval_ptr_dtor(&cpy);
+               }
+
+               if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RESOLVE, curl->options.resolve)) {
+                       return FAILURE;
+               }
+       } else {
+               if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RESOLVE, NULL)) {
+                       return FAILURE;
+               }
+       }
+       return SUCCESS;
+}
+#endif
+
+#if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP)
+static ZEND_RESULT_CODE php_http_curle_option_set_ssl_tlsauthtype(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+       CURL *ch = curl->handle;
+
+       if (val && Z_LVAL_P(val)) {
+               switch (Z_LVAL_P(val)) {
+               case CURL_TLSAUTH_SRP:
+                       if (CURLE_OK == curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, PHP_HTTP_CURL_TLSAUTH_SRP)) {
+                               return SUCCESS;
+                       }
+                       /* no break */
+               default:
+                       return FAILURE;
+               }
+       }
+       if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, PHP_HTTP_CURL_TLSAUTH_DEF)) {
+               return FAILURE;
+       }
+       return SUCCESS;
+}
+#endif
+
+static void php_http_curle_options_init(php_http_options_t *registry TSRMLS_DC)
+{
+       php_http_option_t *opt;
+
+       /* url options */
+#if PHP_HTTP_CURL_VERSION(7,42,0)
+       php_http_option_register(registry, ZEND_STRL("path_as_is"), CURLOPT_PATH_AS_IS, IS_BOOL);
+#endif
+
+       /* proxy */
+       if ((opt = php_http_option_register(registry, ZEND_STRL("proxyhost"), CURLOPT_PROXY, IS_STRING))) {
+               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+       }
+       php_http_option_register(registry, ZEND_STRL("proxytype"), CURLOPT_PROXYTYPE, IS_LONG);
+       php_http_option_register(registry, ZEND_STRL("proxyport"), CURLOPT_PROXYPORT, IS_LONG);
+       if ((opt = php_http_option_register(registry, ZEND_STRL("proxyauth"), CURLOPT_PROXYUSERPWD, IS_STRING))) {
+               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+       }
+       if ((opt = php_http_option_register(registry, ZEND_STRL("proxyauthtype"), CURLOPT_PROXYAUTH, IS_LONG))) {
+               Z_LVAL(opt->defval) = CURLAUTH_ANYSAFE;
+       }
+       php_http_option_register(registry, ZEND_STRL("proxytunnel"), CURLOPT_HTTPPROXYTUNNEL, IS_BOOL);
+#if PHP_HTTP_CURL_VERSION(7,19,4)
+       php_http_option_register(registry, ZEND_STRL("noproxy"), CURLOPT_NOPROXY, IS_STRING);
+#endif
+
+#if PHP_HTTP_CURL_VERSION(7,37,0)
+       if ((opt = php_http_option_register(registry, ZEND_STRL("proxyheader"), CURLOPT_PROXYHEADER, IS_ARRAY))) {
+               opt->setter = php_http_curle_option_set_proxyheader;
+       }
+#endif
+#if PHP_HTTP_CURL_VERSION(7,43,0)
+       if ((opt = php_http_option_register(registry, ZEND_STRL("proxy_service_name"), CURLOPT_PROXY_SERVICE_NAME, IS_STRING))) {
+               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+       }
+#endif
+
+#if PHP_HTTP_CURL_VERSION(7,40,0)
+       if ((opt = php_http_option_register(registry, ZEND_STRL("unix_socket_path"), CURLOPT_UNIX_SOCKET_PATH, IS_STRING))) {
+               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
+       }
+#endif
+
+       /* dns */
+       if ((opt = php_http_option_register(registry, ZEND_STRL("dns_cache_timeout"), CURLOPT_DNS_CACHE_TIMEOUT, IS_LONG))) {
+               Z_LVAL(opt->defval) = 60;
+       }
+       php_http_option_register(registry, ZEND_STRL("ipresolve"), CURLOPT_IPRESOLVE, IS_LONG);
+#if PHP_HTTP_CURL_VERSION(7,21,3)
+       if ((opt = php_http_option_register(registry, ZEND_STRL("resolve"), CURLOPT_RESOLVE, IS_ARRAY))) {
+               opt->setter = php_http_curle_option_set_resolve;
+       }
+#endif
+#if PHP_HTTP_HAVE_ARES
+# if PHP_HTTP_CURL_VERSION(7,24,0)
+       if ((opt = php_http_option_register(registry, ZEND_STRL("dns_servers"), CURLOPT_DNS_SERVERS, IS_STRING))) {
+               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+       }
+# endif
+# if PHP_HTTP_CURL_VERSION(7,33,0)
+       if ((opt = php_http_option_register(registry, ZEND_STRL("dns_interface"), CURLOPT_DNS_INTERFACE, IS_STRING))) {
+               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+       }
+       if ((opt = php_http_option_register(registry, ZEND_STRL("dns_local_ip4"), CURLOPT_DNS_LOCAL_IP4, IS_STRING))) {
+               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+       }
+       if ((opt = php_http_option_register(registry, ZEND_STRL("dns_local_ip6"), CURLOPT_DNS_LOCAL_IP6, IS_STRING))) {
+               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+       }
+# endif
+#endif
+
+       /* limits */
+       php_http_option_register(registry, ZEND_STRL("low_speed_limit"), CURLOPT_LOW_SPEED_LIMIT, IS_LONG);
+       php_http_option_register(registry, ZEND_STRL("low_speed_time"), CURLOPT_LOW_SPEED_TIME, IS_LONG);
+
+       /* LSF weirdance
+       php_http_option_register(registry, ZEND_STRL("max_send_speed"), CURLOPT_MAX_SEND_SPEED_LARGE, IS_LONG);
+       php_http_option_register(registry, ZEND_STRL("max_recv_speed"), CURLOPT_MAX_RECV_SPEED_LARGE, IS_LONG);
+       */
+
+       /* connection handling */
+       /* crashes
+       if ((opt = php_http_option_register(registry, ZEND_STRL("maxconnects"), CURLOPT_MAXCONNECTS, IS_LONG))) {
+               Z_LVAL(opt->defval) = 5;
+       }
+       */
+       php_http_option_register(registry, ZEND_STRL("fresh_connect"), CURLOPT_FRESH_CONNECT, IS_BOOL);
+       php_http_option_register(registry, ZEND_STRL("forbid_reuse"), CURLOPT_FORBID_REUSE, IS_BOOL);
+
+       /* outgoing interface */
+       php_http_option_register(registry, ZEND_STRL("interface"), CURLOPT_INTERFACE, IS_STRING);
+       if ((opt = php_http_option_register(registry, ZEND_STRL("portrange"), CURLOPT_LOCALPORT, IS_ARRAY))) {
+               opt->setter = php_http_curle_option_set_portrange;
+       }
+
+       /* another endpoint port */
+       php_http_option_register(registry, ZEND_STRL("port"), CURLOPT_PORT, IS_LONG);
+
+       /* RFC4007 zone_id */
+#if PHP_HTTP_CURL_VERSION(7,19,0)
+       php_http_option_register(registry, ZEND_STRL("address_scope"), CURLOPT_ADDRESS_SCOPE, IS_LONG);
+#endif
+
+       /* auth */
+       if ((opt = php_http_option_register(registry, ZEND_STRL("httpauth"), CURLOPT_USERPWD, IS_STRING))) {
+               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+       }
+       if ((opt = php_http_option_register(registry, ZEND_STRL("httpauthtype"), CURLOPT_HTTPAUTH, IS_LONG))) {
+               Z_LVAL(opt->defval) = CURLAUTH_ANYSAFE;
+       }
+#if PHP_HTTP_CURL_VERSION(7,43,0)
+       if ((opt = php_http_option_register(registry, ZEND_STRL("service_name"), CURLOPT_SERVICE_NAME, IS_STRING))) {
+               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+       }
+#endif
+
+       /* redirects */
+       if ((opt = php_http_option_register(registry, ZEND_STRL("redirect"), CURLOPT_FOLLOWLOCATION, IS_LONG))) {
+               opt->setter = php_http_curle_option_set_redirect;
+       }
+       php_http_option_register(registry, ZEND_STRL("unrestricted_auth"), CURLOPT_UNRESTRICTED_AUTH, IS_BOOL);
+#if PHP_HTTP_CURL_VERSION(7,19,1)
+       php_http_option_register(registry, ZEND_STRL("postredir"), CURLOPT_POSTREDIR, IS_LONG);
+#endif
+
+       /* retries */
+       if ((opt = php_http_option_register(registry, ZEND_STRL("retrycount"), 0, IS_LONG))) {
+               opt->setter = php_http_curle_option_set_retrycount;
+       }
+       if ((opt = php_http_option_register(registry, ZEND_STRL("retrydelay"), 0, IS_DOUBLE))) {
+               opt->setter = php_http_curle_option_set_retrydelay;
+       }
+
+       /* referer */
+       if ((opt = php_http_option_register(registry, ZEND_STRL("referer"), CURLOPT_REFERER, IS_STRING))) {
+               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+       }
+       if ((opt = php_http_option_register(registry, ZEND_STRL("autoreferer"), CURLOPT_AUTOREFERER, IS_BOOL))) {
+               ZVAL_BOOL(&opt->defval, 1);
+       }
+
+       /* useragent */
+       if ((opt = php_http_option_register(registry, ZEND_STRL("useragent"), CURLOPT_USERAGENT, IS_STRING))) {
+               /* don't check strlen, to allow sending no useragent at all */
+               ZVAL_STRING(&opt->defval,
+                               "PECL_HTTP/" PHP_PECL_HTTP_VERSION " "
+                               "PHP/" PHP_VERSION " "
+                               "libcurl/" LIBCURL_VERSION
+                       , 0);
+       }
+
+       /* resume */
+       if ((opt = php_http_option_register(registry, ZEND_STRL("resume"), CURLOPT_RESUME_FROM, IS_LONG))) {
+               opt->setter = php_http_curle_option_set_resume;
+       }
+       /* ranges */
+       if ((opt = php_http_option_register(registry, ZEND_STRL("range"), CURLOPT_RANGE, IS_ARRAY))) {
+               opt->setter = php_http_curle_option_set_range;
+       }
+
+       /* etag */
+       if ((opt = php_http_option_register(registry, ZEND_STRL("etag"), 0, IS_STRING))) {
+               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+               opt->setter = php_http_curle_option_set_etag;
+       }
+
+       /* compression */
+       if ((opt = php_http_option_register(registry, ZEND_STRL("compress"), 0, IS_BOOL))) {
+               opt->setter = php_http_curle_option_set_compress;
+       }
+
+       /* lastmodified */
+       if ((opt = php_http_option_register(registry, ZEND_STRL("lastmodified"), 0, IS_LONG))) {
+               opt->setter = php_http_curle_option_set_lastmodified;
+       }
+
+       /* cookies */
+       if ((opt = php_http_option_register(registry, ZEND_STRL("encodecookies"), 0, IS_BOOL))) {
+               opt->setter = php_http_curle_option_set_encodecookies;
+               ZVAL_BOOL(&opt->defval, 1);
+       }
+       if ((opt = php_http_option_register(registry, ZEND_STRL("cookies"), 0, IS_ARRAY))) {
+               opt->setter = php_http_curle_option_set_cookies;
+       }
+
+       /* cookiesession, don't load session cookies from cookiestore */
+       php_http_option_register(registry, ZEND_STRL("cookiesession"), CURLOPT_COOKIESESSION, IS_BOOL);
+       /* cookiestore, read initial cookies from that file and store cookies back into that file */
+       if ((opt = php_http_option_register(registry, ZEND_STRL("cookiestore"), 0, IS_STRING))) {
+               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+               opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
+               opt->setter = php_http_curle_option_set_cookiestore;
+       }
+
+       /* maxfilesize */
+       php_http_option_register(registry, ZEND_STRL("maxfilesize"), CURLOPT_MAXFILESIZE, IS_LONG);
+
+       /* http protocol version */
+       php_http_option_register(registry, ZEND_STRL("protocol"), CURLOPT_HTTP_VERSION, IS_LONG);
+
+       /* timeouts */
+       if ((opt = php_http_option_register(registry, ZEND_STRL("timeout"), CURLOPT_TIMEOUT_MS, IS_DOUBLE))) {
+               opt->flags |= PHP_HTTP_CURLE_OPTION_TRANSFORM_MS;
+       }
+       if ((opt = php_http_option_register(registry, ZEND_STRL("connecttimeout"), CURLOPT_CONNECTTIMEOUT_MS, IS_DOUBLE))) {
+               opt->flags |= PHP_HTTP_CURLE_OPTION_TRANSFORM_MS;
+               Z_DVAL(opt->defval) = 3;
+       }
+#if PHP_HTTP_CURL_VERSION(7,36,0)
+       if ((opt = php_http_option_register(registry, ZEND_STRL("expect_100_timeout"), CURLOPT_EXPECT_100_TIMEOUT_MS, IS_DOUBLE))) {
+               opt->flags |= PHP_HTTP_CURLE_OPTION_TRANSFORM_MS;
+               Z_DVAL(opt->defval) = 1;
+       }
+#endif
+
+       /* tcp */
+       php_http_option_register(registry, ZEND_STRL("tcp_nodelay"), CURLOPT_TCP_NODELAY, IS_BOOL);
+#if PHP_HTTP_CURL_VERSION(7,25,0)
+       php_http_option_register(registry, ZEND_STRL("tcp_keepalive"), CURLOPT_TCP_KEEPALIVE, IS_BOOL);
+       if ((opt = php_http_option_register(registry, ZEND_STRL("tcp_keepidle"), CURLOPT_TCP_KEEPIDLE, IS_LONG))) {
+               Z_LVAL(opt->defval) = 60;
+       }
+       if ((opt = php_http_option_register(registry, ZEND_STRL("tcp_keepintvl"), CURLOPT_TCP_KEEPINTVL, IS_LONG))) {
+               Z_LVAL(opt->defval) = 60;
+       }
+#endif
+
+       /* ssl */
+       if ((opt = php_http_option_register(registry, ZEND_STRL("ssl"), 0, IS_ARRAY))) {
+               registry = &opt->suboptions;
+
+               if ((opt = php_http_option_register(registry, ZEND_STRL("cert"), CURLOPT_SSLCERT, IS_STRING))) {
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
+               }
+               if ((opt = php_http_option_register(registry, ZEND_STRL("certtype"), CURLOPT_SSLCERTTYPE, IS_STRING))) {
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+                       ZVAL_STRING(&opt->defval, "PEM", 0);
+               }
+               if ((opt = php_http_option_register(registry, ZEND_STRL("key"), CURLOPT_SSLKEY, IS_STRING))) {
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
+               }
+               if ((opt = php_http_option_register(registry, ZEND_STRL("keytype"), CURLOPT_SSLKEYTYPE, IS_STRING))) {
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+                       ZVAL_STRING(&opt->defval, "PEM", 0);
+               }
+               if ((opt = php_http_option_register(registry, ZEND_STRL("keypasswd"), CURLOPT_SSLKEYPASSWD, IS_STRING))) {
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+               }
+               php_http_option_register(registry, ZEND_STRL("engine"), CURLOPT_SSLENGINE, IS_STRING);
+               php_http_option_register(registry, ZEND_STRL("version"), CURLOPT_SSLVERSION, IS_LONG);
+               if ((opt = php_http_option_register(registry, ZEND_STRL("verifypeer"), CURLOPT_SSL_VERIFYPEER, IS_BOOL))) {
+                       ZVAL_BOOL(&opt->defval, 1);
+               }
+               if ((opt = php_http_option_register(registry, ZEND_STRL("verifyhost"), CURLOPT_SSL_VERIFYHOST, IS_BOOL))) {
+                       ZVAL_BOOL(&opt->defval, 1);
+                       opt->setter = php_http_curle_option_set_ssl_verifyhost;
+               }
+#if PHP_HTTP_CURL_VERSION(7,41,0)
+               php_http_option_register(registry, ZEND_STRL("verifystatus"), CURLOPT_SSL_VERIFYSTATUS, IS_BOOL);
+#endif
+               php_http_option_register(registry, ZEND_STRL("cipher_list"), CURLOPT_SSL_CIPHER_LIST, IS_STRING);
+               if ((opt = php_http_option_register(registry, ZEND_STRL("cainfo"), CURLOPT_CAINFO, IS_STRING))) {
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
+#ifdef PHP_HTTP_CURL_CAINFO
+                       ZVAL_STRING(&opt->defval, PHP_HTTP_CURL_CAINFO, 0);
+#endif
+               }
+               if ((opt = php_http_option_register(registry, ZEND_STRL("capath"), CURLOPT_CAPATH, IS_STRING))) {
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
+               }
+               if ((opt = php_http_option_register(registry, ZEND_STRL("random_file"), CURLOPT_RANDOM_FILE, IS_STRING))) {
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
+               }
+               if ((opt = php_http_option_register(registry, ZEND_STRL("egdsocket"), CURLOPT_EGDSOCKET, IS_STRING))) {
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
+               }
+#if PHP_HTTP_CURL_VERSION(7,19,0)
+               if ((opt = php_http_option_register(registry, ZEND_STRL("issuercert"), CURLOPT_ISSUERCERT, IS_STRING))) {
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
+               }
+#      ifdef PHP_HTTP_HAVE_OPENSSL
+               if ((opt = php_http_option_register(registry, ZEND_STRL("crlfile"), CURLOPT_CRLFILE, IS_STRING))) {
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
+               }
+#      endif
+#endif
+#if (PHP_HTTP_CURL_VERSION(7,19,1) && defined(PHP_HTTP_HAVE_OPENSSL)) || (PHP_HTTP_CURL_VERSION(7,34,0) && defined(PHP_HTTP_HAVE_NSS)) || (PHP_HTTP_CURL_VERSION(7,42,0) && defined(PHP_HTTP_HAVE_GNUTLS)) || (PHP_HTTP_CURL_VERSION(7,39,0) && defined(PHP_HTTP_HAVE_GSKIT))
+               php_http_option_register(registry, ZEND_STRL("certinfo"), CURLOPT_CERTINFO, IS_BOOL);
+#endif
+#if PHP_HTTP_CURL_VERSION(7,36,0)
+               if ((opt = php_http_option_register(registry, ZEND_STRL("enable_npn"), CURLOPT_SSL_ENABLE_NPN, IS_BOOL))) {
+                       ZVAL_BOOL(&opt->defval, 1);
+               }
+               if ((opt = php_http_option_register(registry, ZEND_STRL("enable_alpn"), CURLOPT_SSL_ENABLE_ALPN, IS_BOOL))) {
+                       ZVAL_BOOL(&opt->defval, 1);
+               }
+#endif
+#if PHP_HTTP_CURL_VERSION(7,39,0)
+               /* FIXME: see http://curl.haxx.se/libcurl/c/CURLOPT_PINNEDPUBLICKEY.html#AVAILABILITY */
+               if ((opt = php_http_option_register(registry, ZEND_STRL("pinned_publickey"), CURLOPT_PINNEDPUBLICKEY, IS_STRING))) {
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
+               }
+#endif
+#if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP)
+               if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthtype"), CURLOPT_TLSAUTH_TYPE, IS_LONG))) {
+                       opt->setter = php_http_curle_option_set_ssl_tlsauthtype;
+               }
+               if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthuser"), CURLOPT_TLSAUTH_USERNAME, IS_STRING))) {
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+               }
+               if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthpass"), CURLOPT_TLSAUTH_PASSWORD, IS_STRING))) {
+                       opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+               }
+#endif
+#if PHP_HTTP_CURL_VERSION(7,42,0) && (defined(PHP_HTTP_HAVE_NSS) || defined(PHP_HTTP_HAVE_DARWINSSL))
+               php_http_option_register(registry, ZEND_STRL("falsestart"), CURLOPT_SSL_FALSESTART, IS_BOOL);
+#endif
+       }
+}
+
+static zval *php_http_curle_get_option(php_http_option_t *opt, HashTable *options, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+       zval *option;
+
+       if ((option = php_http_option_get(opt, options, NULL))) {
+               option = php_http_ztyp(opt->type, option);
+               zend_hash_quick_update(&curl->options.cache, opt->name.s, opt->name.l, opt->name.h, &option, sizeof(zval *), NULL);
+       }
+       return option;
+}
+
+static ZEND_RESULT_CODE php_http_curle_set_option(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_curl_handler_t *curl = userdata;
+       CURL *ch = curl->handle;
+       zval tmp;
+       CURLcode rc = CURLE_OK;
+       ZEND_RESULT_CODE rv = SUCCESS;
+       TSRMLS_FETCH_FROM_CTX(curl->client->ts);
+
+       if (!val) {
+               val = &opt->defval;
+       }
+
+       switch (opt->type) {
+       case IS_BOOL:
+               if (opt->setter) {
+                       rv = opt->setter(opt, val, curl);
+               } else if (CURLE_OK != curl_easy_setopt(ch, opt->option, (long) Z_BVAL_P(val))) {
+                       rv = FAILURE;
+               }
+               break;
+
+       case IS_LONG:
+               if (opt->setter) {
+                       rv = opt->setter(opt, val, curl);
+               } else if (CURLE_OK != curl_easy_setopt(ch, opt->option, Z_LVAL_P(val))) {
+                       rv = FAILURE;
+               }
+               break;
+
+       case IS_STRING:
+               if (opt->setter) {
+                       rv = opt->setter(opt, val, curl);
+               } else if ((opt->flags & PHP_HTTP_CURLE_OPTION_CHECK_STRLEN) && !Z_STRLEN_P(val)) {
+                       if (CURLE_OK != (rc = curl_easy_setopt(ch, opt->option, NULL))) {
+                               rv = FAILURE;
+                       }
+               } else if ((opt->flags & PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR) && Z_STRVAL_P(val) && SUCCESS != php_check_open_basedir(Z_STRVAL_P(val) TSRMLS_CC)) {
+                       if (CURLE_OK != (rc = curl_easy_setopt(ch, opt->option, NULL))) {
+                               rv = FAILURE;
+                       }
+               } else if (CURLE_OK != (rc = curl_easy_setopt(ch, opt->option, Z_STRVAL_P(val)))) {
+                       rv = FAILURE;
+               }
+               break;
+
+       case IS_DOUBLE:
+               if (opt->flags & PHP_HTTP_CURLE_OPTION_TRANSFORM_MS) {
+                       tmp = *val;
+                       Z_DVAL(tmp) *= 1000;
+                       val = &tmp;
+               }
+               if (opt->setter) {
+                       rv = opt->setter(opt, val, curl);
+               } else if (CURLE_OK != curl_easy_setopt(ch, opt->option, (long) Z_DVAL_P(val))) {
+                       rv = FAILURE;
+               }
+               break;
+
+       case IS_ARRAY:
+               if (opt->setter) {
+                       rv = opt->setter(opt, val, curl);
+               } else if (Z_TYPE_P(val) != IS_NULL) {
+                       rv = php_http_options_apply(&opt->suboptions, Z_ARRVAL_P(val), curl);
+               }
+               break;
+
+       default:
+               if (opt->setter) {
+                       rv = opt->setter(opt, val, curl);
+               } else {
+                       rv = FAILURE;
+               }
+               break;
+       }
+       if (rv != SUCCESS) {
+               php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s (%s)", opt->name.s, curl_easy_strerror(rc));
+       }
+       return rv;
+}
+
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+static ZEND_RESULT_CODE php_http_curlm_option_set_pipelining_bl(php_http_option_t *opt, zval *value, void *userdata)
+{
+       php_http_client_t *client = userdata;
+       php_http_client_curl_t *curl = client->ctx;
+       CURLM *ch = curl->handle;
+       HashTable tmp_ht;
+       char **bl = NULL;
+       TSRMLS_FETCH_FROM_CTX(client->ts);
+
+       /* array of char *, ending with a NULL */
+       if (value && Z_TYPE_P(value) != IS_NULL) {
+               zval **entry;
+               HashPosition pos;
+               HashTable *ht = HASH_OF(value);
+               int c = zend_hash_num_elements(ht);
+               char **ptr = ecalloc(c + 1, sizeof(char *));
+
+               bl = ptr;
+
+               zend_hash_init(&tmp_ht, c, NULL, ZVAL_PTR_DTOR, 0);
+               array_join(ht, &tmp_ht, 0, ARRAY_JOIN_STRINGIFY);
+
+               FOREACH_HASH_VAL(pos, &tmp_ht, entry) {
+                       *ptr++ = Z_STRVAL_PP(entry);
+               }
+       }
+
+       if (CURLM_OK != curl_multi_setopt(ch, opt->option, bl)) {
+               if (bl) {
+                       efree(bl);
+                       zend_hash_destroy(&tmp_ht);
+               }
+               return FAILURE;
+       }
+
+       if (bl) {
+               efree(bl);
+               zend_hash_destroy(&tmp_ht);
+       }
+       return SUCCESS;
+}
+#endif
+
+#if PHP_HTTP_HAVE_EVENT
+static inline ZEND_RESULT_CODE php_http_curlm_use_eventloop(php_http_client_t *h, zend_bool enable)
+{
+       php_http_client_curl_t *curl = h->ctx;
+
+       if ((curl->useevents = enable)) {
+               if (!curl->evbase) {
+                       curl->evbase = event_base_new();
+               }
+               if (!curl->timeout) {
+                       curl->timeout = ecalloc(1, sizeof(struct event));
+               }
+               curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, h);
+               curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, php_http_curlm_socket_callback);
+               curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, h);
+               curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, php_http_curlm_timer_callback);
+       } else {
+               curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, NULL);
+               curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, NULL);
+               curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, NULL);
+               curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, NULL);
+       }
+
+       return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curlm_option_set_use_eventloop(php_http_option_t *opt, zval *value, void *userdata)
+{
+       php_http_client_t *client = userdata;
+
+       return php_http_curlm_use_eventloop(client, value && Z_BVAL_P(value));
+}
+#endif
+
+static void php_http_curlm_options_init(php_http_options_t *registry TSRMLS_DC)
+{
+       php_http_option_t *opt;
+
+       /* set size of connection cache */
+       if ((opt = php_http_option_register(registry, ZEND_STRL("maxconnects"), CURLMOPT_MAXCONNECTS, IS_LONG))) {
+               /* -1 == default, 0 == unlimited */
+               ZVAL_LONG(&opt->defval, -1);
+       }
+       /* set max number of connections to a single host */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+       php_http_option_register(registry, ZEND_STRL("max_host_connections"), CURLMOPT_MAX_HOST_CONNECTIONS, IS_LONG);
+#endif
+       /* maximum number of requests in a pipeline */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+       if ((opt = php_http_option_register(registry, ZEND_STRL("max_pipeline_length"), CURLMOPT_MAX_PIPELINE_LENGTH, IS_LONG))) {
+               ZVAL_LONG(&opt->defval, 5);
+       }
+#endif
+       /* max simultaneously open connections */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+       php_http_option_register(registry, ZEND_STRL("max_total_connections"), CURLMOPT_MAX_TOTAL_CONNECTIONS, IS_LONG);
+#endif
+       /* enable/disable HTTP pipelining */
+       php_http_option_register(registry, ZEND_STRL("pipelining"), CURLMOPT_PIPELINING, IS_BOOL);
+       /* chunk length threshold for pipelining */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+       php_http_option_register(registry, ZEND_STRL("chunk_length_penalty_size"), CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE, IS_LONG);
+#endif
+       /* size threshold for pipelining penalty */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+       php_http_option_register(registry, ZEND_STRL("content_length_penalty_size"), CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE, IS_LONG);
+#endif
+       /* pipelining server blacklist */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+       if ((opt = php_http_option_register(registry, ZEND_STRL("pipelining_server_bl"), CURLMOPT_PIPELINING_SERVER_BL, IS_ARRAY))) {
+               opt->setter = php_http_curlm_option_set_pipelining_bl;
+       }
+#endif
+       /* pipelining host blacklist */
+#if PHP_HTTP_CURL_VERSION(7,30,0)
+       if ((opt = php_http_option_register(registry, ZEND_STRL("pipelining_site_bl"), CURLMOPT_PIPELINING_SITE_BL, IS_ARRAY))) {
+               opt->setter = php_http_curlm_option_set_pipelining_bl;
+       }
+#endif
+       /* events */
+#if PHP_HTTP_HAVE_EVENT
+       if ((opt = php_http_option_register(registry, ZEND_STRL("use_eventloop"), 0, IS_BOOL))) {
+               opt->setter = php_http_curlm_option_set_use_eventloop;
+       }
+#endif
+}
+
+static ZEND_RESULT_CODE php_http_curlm_set_option(php_http_option_t *opt, zval *val, void *userdata)
+{
+       php_http_client_t *client = userdata;
+       php_http_client_curl_t *curl = client->ctx;
+       CURLM *ch = curl->handle;
+       zval *orig = val;
+       CURLMcode rc = CURLM_UNKNOWN_OPTION;
+       ZEND_RESULT_CODE rv = SUCCESS;
+       TSRMLS_FETCH_FROM_CTX(client->ts);
+
+       if (!val) {
+               val = &opt->defval;
+       } else if (opt->type && Z_TYPE_P(val) != opt->type && !(Z_TYPE_P(val) == IS_NULL && opt->type == IS_ARRAY)) {
+               val = php_http_ztyp(opt->type, val);
+       }
+
+       if (opt->setter) {
+               rv = opt->setter(opt, val, client);
+       } else {
+               switch (opt->type) {
+               case IS_BOOL:
+                       if (CURLM_OK != (rc = curl_multi_setopt(ch, opt->option, (long) Z_BVAL_P(val)))) {
+                               rv = FAILURE;
+                       }
+                       break;
+               case IS_LONG:
+                       if (CURLM_OK != (rc = curl_multi_setopt(ch, opt->option, Z_LVAL_P(val)))) {
+                               rv = FAILURE;
+                       }
+                       break;
+               default:
+                       rv = FAILURE;
+                       break;
+               }
+       }
+
+       if (val && val != orig && val != &opt->defval) {
+               zval_ptr_dtor(&val);
+       }
+
+       if (rv != SUCCESS) {
+               php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s (%s)", opt->name.s, curl_easy_strerror(rc));
+       }
+       return rv;
+}
+
+/* client ops */
+
+static ZEND_RESULT_CODE php_http_client_curl_handler_reset(php_http_client_curl_handler_t *curl)
+{
+       CURL *ch = curl->handle;
+       php_http_curle_storage_t *st;
+
+       if ((st = php_http_curle_get_storage(ch))) {
+               if (st->url) {
+                       pefree(st->url, 1);
+                       st->url = NULL;
+               }
+               if (st->cookiestore) {
+                       pefree(st->cookiestore, 1);
+                       st->cookiestore = NULL;
+               }
+               st->errorbuffer[0] = '\0';
+       }
+
+       curl_easy_setopt(ch, CURLOPT_URL, NULL);
+       curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, NULL);
+       curl_easy_setopt(ch, CURLOPT_HTTPGET, 1L);
+       curl_easy_setopt(ch, CURLOPT_NOBODY, 0L);
+       /* libcurl < 7.19.6 does not clear auth info with USERPWD set to NULL */
+#if PHP_HTTP_CURL_VERSION(7,19,1)
+       curl_easy_setopt(ch, CURLOPT_PROXYUSERNAME, NULL);
+       curl_easy_setopt(ch, CURLOPT_PROXYPASSWORD, NULL);
+       curl_easy_setopt(ch, CURLOPT_USERNAME, NULL);
+       curl_easy_setopt(ch, CURLOPT_PASSWORD, NULL);
+#endif
+
+#if PHP_HTTP_CURL_VERSION(7,21,3)
+       if (curl->options.resolve) {
+               curl_slist_free_all(curl->options.resolve);
+               curl->options.resolve = NULL;
+       }
+#endif
+       curl->options.retry.count = 0;
+       curl->options.retry.delay = 0;
+       curl->options.redirects = 0;
+       curl->options.encode_cookies = 1;
+
+       if (curl->options.headers) {
+               curl_slist_free_all(curl->options.headers);
+               curl->options.headers = NULL;
+       }
+       if (curl->options.proxyheaders) {
+               curl_slist_free_all(curl->options.proxyheaders);
+               curl->options.proxyheaders = NULL;
+       }
+
+       php_http_buffer_reset(&curl->options.cookies);
+       php_http_buffer_reset(&curl->options.ranges);
+
+       return SUCCESS;
+}
+
+static php_http_client_curl_handler_t *php_http_client_curl_handler_init(php_http_client_t *h, php_resource_factory_t *rf)
+{
+       void *handle;
+       php_http_client_curl_handler_t *handler;
+       TSRMLS_FETCH_FROM_CTX(h->ts);
+
+       if (!(handle = php_resource_factory_handle_ctor(rf, NULL TSRMLS_CC))) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to initialize curl handle");
+               return NULL;
+       }
+
+       handler = ecalloc(1, sizeof(*handler));
+       handler->rf = rf;
+       handler->client = h;
+       handler->handle = handle;
+       handler->response.body = php_http_message_body_init(NULL, NULL TSRMLS_CC);
+       php_http_buffer_init(&handler->response.headers);
+       php_http_buffer_init(&handler->options.cookies);
+       php_http_buffer_init(&handler->options.ranges);
+       zend_hash_init(&handler->options.cache, 0, NULL, ZVAL_PTR_DTOR, 0);
+
+#if defined(ZTS)
+       curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1L);
+#endif
+       curl_easy_setopt(handle, CURLOPT_HEADER, 0L);
+       curl_easy_setopt(handle, CURLOPT_FILETIME, 1L);
+       curl_easy_setopt(handle, CURLOPT_AUTOREFERER, 1L);
+       curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L);
+       curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0L);
+       curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, php_http_curle_header_callback);
+       curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, php_http_curle_body_callback);
+       curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, php_http_curle_raw_callback);
+       curl_easy_setopt(handle, CURLOPT_READFUNCTION, php_http_curle_read_callback);
+       curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, php_http_curle_seek_callback);
+#if PHP_HTTP_CURL_VERSION(7,32,0)
+       curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, php_http_curle_xferinfo_callback);
+       curl_easy_setopt(handle, CURLOPT_XFERINFODATA, handler);
+#else
+       curl_easy_setopt(handle, CURLOPT_PROGRESSFUNCTION, php_http_curle_progress_callback);
+       curl_easy_setopt(handle, CURLOPT_PROGRESSDATA, handler);
+#endif
+       curl_easy_setopt(handle, CURLOPT_DEBUGDATA, handler);
+       curl_easy_setopt(handle, CURLOPT_WRITEDATA, handler);
+       curl_easy_setopt(handle, CURLOPT_HEADERDATA, handler);
+
+       php_http_client_curl_handler_reset(handler);
+
+       return handler;
+}
+
+
+static ZEND_RESULT_CODE php_http_client_curl_handler_prepare(php_http_client_curl_handler_t *curl, php_http_client_enqueue_t *enqueue)
+{
+       size_t body_size;
+       php_http_message_t *msg = enqueue->request;
+       php_http_curle_storage_t *storage = php_http_curle_get_storage(curl->handle);
+       TSRMLS_FETCH_FROM_CTX(curl->client->ts);
+
+       /* request url */
+       if (!PHP_HTTP_INFO(msg).request.url) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot request empty URL");
+               return FAILURE;
+       }
+       storage->errorbuffer[0] = '\0';
+       if (storage->url) {
+               pefree(storage->url, 1);
+       }
+       php_http_url_to_string(PHP_HTTP_INFO(msg).request.url, &storage->url, NULL, 1);
+       curl_easy_setopt(curl->handle, CURLOPT_URL, storage->url);
+
+       /* apply options */
+       php_http_options_apply(&php_http_curle_options, enqueue->options, curl);
+
+       /* request headers */
+       php_http_message_update_headers(msg);
+       if (zend_hash_num_elements(&msg->hdrs)) {
+               php_http_array_hashkey_t header_key = php_http_array_hashkey_init(0);
+               zval **header_val, *header_cpy;
+               HashPosition pos;
+               php_http_buffer_t header;
+#if !PHP_HTTP_CURL_VERSION(7,23,0)
+               zval **ct = NULL;
+
+               zend_hash_find(&msg->hdrs, ZEND_STRS("Content-Length"), (void *) &ct);
+#endif
+
+               php_http_buffer_init(&header);
+               FOREACH_HASH_KEYVAL(pos, &msg->hdrs, header_key, header_val) {
+                       if (header_key.type == HASH_KEY_IS_STRING) {
+#if !PHP_HTTP_CURL_VERSION(7,23,0)
+                               /* avoid duplicate content-length header */
+                               if (ct && *ct == *header_val) {
+                                       continue;
+                               }
+#endif
+                               header_cpy = php_http_ztyp(IS_STRING, *header_val);
+                               php_http_buffer_appendf(&header, "%s: %s", header_key.str, Z_STRVAL_P(header_cpy));
+                               php_http_buffer_fix(&header);
+                               curl->options.headers = curl_slist_append(curl->options.headers, header.data);
+                               php_http_buffer_reset(&header);
+
+                               zval_ptr_dtor(&header_cpy);
+                       }
+               }
+               php_http_buffer_dtor(&header);
+       }
+       curl_easy_setopt(curl->handle, CURLOPT_HTTPHEADER, curl->options.headers);
+
+       /* attach request body */
+       if ((body_size = php_http_message_body_size(msg->body))) {
+               /* RFC2616, section 4.3 (para. 4) states that Â»a message-body MUST NOT be included in a request if the
+                * specification of the request method (section 5.1.1) does not allow sending an entity-body in request.«
+                * Following the clause in section 5.1.1 (para. 2) that request methods Â»MUST be implemented with the
+                * same semantics as those specified in section 9« reveal that not any single defined HTTP/1.1 method
+                * does not allow a request body.
+                */
+               php_stream_rewind(php_http_message_body_stream(msg->body));
+               curl_easy_setopt(curl->handle, CURLOPT_SEEKDATA, msg->body);
+               curl_easy_setopt(curl->handle, CURLOPT_READDATA, msg->body);
+               curl_easy_setopt(curl->handle, CURLOPT_INFILESIZE, body_size);
+               curl_easy_setopt(curl->handle, CURLOPT_POSTFIELDSIZE, body_size);
+               curl_easy_setopt(curl->handle, CURLOPT_POST, 1L);
+       } else {
+               curl_easy_setopt(curl->handle, CURLOPT_SEEKDATA, NULL);
+               curl_easy_setopt(curl->handle, CURLOPT_READDATA, NULL);
+               curl_easy_setopt(curl->handle, CURLOPT_INFILESIZE, 0L);
+               curl_easy_setopt(curl->handle, CURLOPT_POSTFIELDSIZE, 0L);
+       }
+
+       /*
+        * Always use CUSTOMREQUEST, else curl won't send any request body for GET etc.
+        * See e.g. bug #69313.
+        *
+        * Here's what curl does:
+        * - CURLOPT_HTTPGET: ignore request body
+        * - CURLOPT_UPLOAD: set "Expect: 100-continue" header
+        * - CURLOPT_POST: set "Content-Type: application/x-www-form-urlencoded" header
+        * Now select the least bad.
+        *
+        * See also https://tools.ietf.org/html/rfc7231#section-5.1.1
+        */
+       if (PHP_HTTP_INFO(msg).request.method) {
+               switch(php_http_select_str(PHP_HTTP_INFO(msg).request.method, 2, "HEAD", "PUT")) {
+               case 0:
+                       curl_easy_setopt(curl->handle, CURLOPT_NOBODY, 1L);
+                       break;
+               case 1:
+                       curl_easy_setopt(curl->handle, CURLOPT_UPLOAD, 1L);
+                       break;
+               default:
+                       curl_easy_setopt(curl->handle, CURLOPT_CUSTOMREQUEST, PHP_HTTP_INFO(msg).request.method);
+               }
+       } else {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot use empty request method");
+               return FAILURE;
+       }
+
+       return SUCCESS;
+}
+
+static void php_http_client_curl_handler_clear(php_http_client_curl_handler_t *handler)
+{
+       curl_easy_setopt(handler->handle, CURLOPT_NOPROGRESS, 1L);
+#if PHP_HTTP_CURL_VERSION(7,32,0)
+       curl_easy_setopt(handler->handle, CURLOPT_XFERINFOFUNCTION, NULL);
+#else
+       curl_easy_setopt(handler->handle, CURLOPT_PROGRESSFUNCTION, NULL);
+#endif
+       curl_easy_setopt(handler->handle, CURLOPT_VERBOSE, 0L);
+       curl_easy_setopt(handler->handle, CURLOPT_DEBUGFUNCTION, NULL);
+}
+
+static void php_http_client_curl_handler_dtor(php_http_client_curl_handler_t *handler)
+{
+       TSRMLS_FETCH_FROM_CTX(handler->client->ts);
+
+       php_http_client_curl_handler_clear(handler);
+
+       php_resource_factory_handle_dtor(handler->rf, handler->handle TSRMLS_CC);
+       php_resource_factory_free(&handler->rf);
+
+       php_http_message_body_free(&handler->response.body);
+       php_http_buffer_dtor(&handler->response.headers);
+       php_http_buffer_dtor(&handler->options.ranges);
+       php_http_buffer_dtor(&handler->options.cookies);
+       zend_hash_destroy(&handler->options.cache);
+
+       if (handler->options.headers) {
+               curl_slist_free_all(handler->options.headers);
+               handler->options.headers = NULL;
+       }
+
+       efree(handler);
+}
+
+static php_http_client_t *php_http_client_curl_init(php_http_client_t *h, void *handle)
+{
+       php_http_client_curl_t *curl;
+       TSRMLS_FETCH_FROM_CTX(h->ts);
+
+       if (!handle && !(handle = php_resource_factory_handle_ctor(h->rf, NULL TSRMLS_CC))) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to initialize curl handle");
+               return NULL;
+       }
+
+       curl = ecalloc(1, sizeof(*curl));
+       curl->handle = handle;
+       curl->unfinished = 0;
+       h->ctx = curl;
+
+       return h;
+}
+
+static void php_http_client_curl_dtor(php_http_client_t *h)
+{
+       php_http_client_curl_t *curl = h->ctx;
+       TSRMLS_FETCH_FROM_CTX(h->ts);
+
+#if PHP_HTTP_HAVE_EVENT
+       if (curl->timeout) {
+               if (event_initialized(curl->timeout) && event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
+                       event_del(curl->timeout);
+               }
+               efree(curl->timeout);
+               curl->timeout = NULL;
+       }
+       if (curl->evbase) {
+               event_base_free(curl->evbase);
+               curl->evbase = NULL;
+       }
+#endif
+       curl->unfinished = 0;
+
+       php_resource_factory_handle_dtor(h->rf, curl->handle TSRMLS_CC);
+
+       efree(curl);
+       h->ctx = NULL;
+}
+
+static void queue_dtor(php_http_client_enqueue_t *e)
+{
+       php_http_client_curl_handler_t *handler = e->opaque;
+
+       if (handler->queue.dtor) {
+               e->opaque = handler->queue.opaque;
+               handler->queue.dtor(e);
+       }
+       php_http_client_curl_handler_dtor(handler);
+}
+
+static php_resource_factory_t *create_rf(php_http_client_t *h, php_http_client_enqueue_t *enqueue TSRMLS_DC)
+{
+       php_persistent_handle_factory_t *pf = NULL;
+       php_resource_factory_t *rf = NULL;
+       php_http_url_t *url = enqueue->request->http.info.request.url;
+
+       if (!url || (!url->host && !url->path)) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot request empty URL");
+               return NULL;
+       }
+
+       /* only if the client itself is setup for persistence */
+       if (php_resource_factory_is_persistent(h->rf)) {
+               char *id_str = NULL;
+               size_t id_len;
+               int port = url->port ? url->port : 80;
+               zval **zport;
+
+               if (SUCCESS == zend_hash_find(enqueue->options, ZEND_STRS("port"), (void *) &zport)) {
+                       zval *zcpy = php_http_ztyp(IS_LONG, *zport);
+
+                       if (Z_LVAL_P(zcpy)) {
+                               port = Z_LVAL_P(zcpy);
+                       }
+                       zval_ptr_dtor(&zcpy);
+               }
+
+               id_len = spprintf(&id_str, 0, "%s:%d", STR_PTR(url->host), port);
+               pf = php_persistent_handle_concede(NULL, ZEND_STRL("http\\Client\\Curl\\Request"), id_str, id_len, NULL, NULL TSRMLS_CC);
+               efree(id_str);
+       }
+
+       if (pf) {
+               rf = php_persistent_handle_resource_factory_init(NULL, pf);
+       } else {
+               rf = php_resource_factory_init(NULL, &php_http_curle_resource_factory_ops, NULL, NULL);
+       }
+
+       return rf;
+}
+
+static ZEND_RESULT_CODE php_http_client_curl_enqueue(php_http_client_t *h, php_http_client_enqueue_t *enqueue)
+{
+       CURLMcode rs;
+       php_http_client_curl_t *curl = h->ctx;
+       php_http_client_curl_handler_t *handler;
+       php_http_client_progress_state_t *progress;
+       php_resource_factory_t *rf;
+       TSRMLS_FETCH_FROM_CTX(h->ts);
+
+       rf = create_rf(h, enqueue TSRMLS_CC);
+       if (!rf) {
+               return FAILURE;
+       }
+
+       handler = php_http_client_curl_handler_init(h, rf);
+       if (!handler) {
+               return FAILURE;
+       }
+
+       if (SUCCESS != php_http_client_curl_handler_prepare(handler, enqueue)) {
+               php_http_client_curl_handler_dtor(handler);
+               return FAILURE;
+       }
+
+       handler->queue = *enqueue;
+       enqueue->opaque = handler;
+       enqueue->dtor = queue_dtor;
+
+       if (CURLM_OK == (rs = curl_multi_add_handle(curl->handle, handler->handle))) {
+               zend_llist_add_element(&h->requests, enqueue);
+               ++curl->unfinished;
+
+               if (h->callback.progress.func && SUCCESS == php_http_client_getopt(h, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, enqueue->request, &progress)) {
+                       progress->info = "start";
+                       h->callback.progress.func(h->callback.progress.arg, h, &handler->queue, progress);
+                       progress->started = 1;
+               }
+
+               return SUCCESS;
+       } else {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not enqueue request: %s", curl_multi_strerror(rs));
+               return FAILURE;
+       }
+}
+
+static ZEND_RESULT_CODE php_http_client_curl_dequeue(php_http_client_t *h, php_http_client_enqueue_t *enqueue)
+{
+       CURLMcode rs;
+       php_http_client_curl_t *curl = h->ctx;
+       php_http_client_curl_handler_t *handler = enqueue->opaque;
+       TSRMLS_FETCH_FROM_CTX(h->ts);
+
+       php_http_client_curl_handler_clear(handler);
+       if (CURLM_OK == (rs = curl_multi_remove_handle(curl->handle, handler->handle))) {
+               zend_llist_del_element(&h->requests, handler->handle, (int (*)(void *, void *)) compare_queue);
+               return SUCCESS;
+       } else {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not dequeue request: %s", curl_multi_strerror(rs));
+       }
+
+       return FAILURE;
+}
+
+static void php_http_client_curl_reset(php_http_client_t *h)
+{
+       zend_llist_element *next_el, *this_el;
+
+       for (this_el = h->requests.head; this_el; this_el = next_el) {
+               next_el = this_el->next;
+               php_http_client_curl_dequeue(h, (void *) this_el->data);
+       }
+}
+
+static inline void php_http_client_curl_get_timeout(php_http_client_curl_t *curl, long max_tout, struct timeval *timeout)
+{
+       if ((CURLM_OK == curl_multi_timeout(curl->handle, &max_tout)) && (max_tout > 0)) {
+               timeout->tv_sec = max_tout / 1000;
+               timeout->tv_usec = (max_tout % 1000) * 1000;
+       } else {
+               timeout->tv_sec = 0;
+               timeout->tv_usec = 1000;
+       }
+}
+
+#ifdef PHP_WIN32
+#      define SELECT_ERROR SOCKET_ERROR
+#else
+#      define SELECT_ERROR -1
+#endif
+
+static ZEND_RESULT_CODE php_http_client_curl_wait(php_http_client_t *h, struct timeval *custom_timeout)
+{
+       int MAX;
+       fd_set R, W, E;
+       struct timeval timeout;
+       php_http_client_curl_t *curl = h->ctx;
+
+#if PHP_HTTP_HAVE_EVENT
+       if (curl->useevents) {
+               if (!event_initialized(curl->timeout)) {
+                       event_assign(curl->timeout, curl->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_curlm_timeout_callback, h);
+               } else if (custom_timeout && timerisset(custom_timeout)) {
+                       event_add(curl->timeout, custom_timeout);
+               } else if (!event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
+                       php_http_client_curl_get_timeout(curl, 1000, &timeout);
+                       event_add(curl->timeout, &timeout);
+               }
+
+               event_base_loop(curl->evbase, EVLOOP_ONCE);
+
+               return SUCCESS;
+       }
+#endif
+
+       FD_ZERO(&R);
+       FD_ZERO(&W);
+       FD_ZERO(&E);
+
+       if (CURLM_OK == curl_multi_fdset(curl->handle, &R, &W, &E, &MAX)) {
+               if (custom_timeout && timerisset(custom_timeout)) {
+                       timeout = *custom_timeout;
+               } else {
+                       php_http_client_curl_get_timeout(curl, 1000, &timeout);
+               }
+
+               if (MAX == -1) {
+                       php_http_sleep((double) timeout.tv_sec + (double) (timeout.tv_usec / PHP_HTTP_MCROSEC));
+                       return SUCCESS;
+               } else if (SELECT_ERROR != select(MAX + 1, &R, &W, &E, &timeout)) {
+                       return SUCCESS;
+               }
+       }
+       return FAILURE;
+}
+
+static int php_http_client_curl_once(php_http_client_t *h)
+{
+       php_http_client_curl_t *curl = h->ctx;
+
+#if PHP_HTTP_HAVE_EVENT
+       if (curl->useevents) {
+               event_base_loop(curl->evbase, EVLOOP_NONBLOCK);
+       } else
+#endif
+       while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curl->handle, &curl->unfinished));
+
+       php_http_curlm_responsehandler(h);
+
+       return curl->unfinished;
+
+}
+
+static ZEND_RESULT_CODE php_http_client_curl_exec(php_http_client_t *h)
+{
+#if PHP_HTTP_HAVE_EVENT
+       php_http_client_curl_t *curl = h->ctx;
+#endif
+       TSRMLS_FETCH_FROM_CTX(h->ts);
+
+#if PHP_HTTP_HAVE_EVENT
+       if (curl->useevents) {
+               php_http_curlm_timeout_callback(CURL_SOCKET_TIMEOUT, /*EV_READ|EV_WRITE*/0, h);
+               do {
+                       int ev_rc = event_base_dispatch(curl->evbase);
+
+#if DBG_EVENTS
+                       fprintf(stderr, "%c", "X.0"[ev_rc+1]);
+#endif
+
+                       if (ev_rc < 0) {
+                               php_error_docref(NULL TSRMLS_CC, E_ERROR, "Error in event_base_dispatch()");
+                               return FAILURE;
+                       }
+               } while (curl->unfinished && !EG(exception));
+       } else
+#endif
+       {
+               while (php_http_client_curl_once(h) && !EG(exception)) {
+                       if (SUCCESS != php_http_client_curl_wait(h, NULL)) {
+#ifdef PHP_WIN32
+                               /* see http://msdn.microsoft.com/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp */
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "WinSock error: %d", WSAGetLastError());
+#else
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno));
+#endif
+                               return FAILURE;
+                       }
+               }
+       }
+
+       return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_client_curl_setopt(php_http_client_t *h, php_http_client_setopt_opt_t opt, void *arg)
+{
+       php_http_client_curl_t *curl = h->ctx;
+
+       switch (opt) {
+               case PHP_HTTP_CLIENT_OPT_CONFIGURATION:
+                       return php_http_options_apply(&php_http_curlm_options, (HashTable *) arg,  h);
+                       break;
+
+               case PHP_HTTP_CLIENT_OPT_ENABLE_PIPELINING:
+                       if (CURLM_OK != curl_multi_setopt(curl->handle, CURLMOPT_PIPELINING, (long) *((zend_bool *) arg))) {
+                               return FAILURE;
+                       }
+                       break;
+
+               case PHP_HTTP_CLIENT_OPT_USE_EVENTS:
+#if PHP_HTTP_HAVE_EVENT
+                       return php_http_curlm_use_eventloop(h, *(zend_bool *) arg);
+                       break;
+#endif
+
+               default:
+                       return FAILURE;
+       }
+       return SUCCESS;
+}
+
+static int apply_available_options(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
+{
+       php_http_option_t *opt = pDest;
+       HashTable *ht;
+       zval *entry;
+       int c;
+
+       ht = va_arg(args, HashTable*);
+
+       MAKE_STD_ZVAL(entry);
+
+       if ((c = zend_hash_num_elements(&opt->suboptions.options))) {
+               array_init_size(entry, c);
+               zend_hash_apply_with_arguments(&opt->suboptions.options TSRMLS_CC, apply_available_options, 1, Z_ARRVAL_P(entry));
+       } else {
+               /* catch deliberate NULL options */
+               if (Z_TYPE(opt->defval) == IS_STRING && !Z_STRVAL(opt->defval)) {
+                       ZVAL_NULL(entry);
+               } else {
+                       ZVAL_COPY_VALUE(entry, &opt->defval);
+                       zval_copy_ctor(entry);
+               }
+       }
+
+       if (hash_key->nKeyLength) {
+               zend_hash_quick_update(ht, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &entry, sizeof(zval *), NULL);
+       } else {
+               zend_hash_index_update(ht, hash_key->h, (void *) &entry, sizeof(zval *), NULL);
+       }
+
+       return ZEND_HASH_APPLY_KEEP;
+}
+
+static ZEND_RESULT_CODE php_http_client_curl_getopt(php_http_client_t *h, php_http_client_getopt_opt_t opt, void *arg, void **res)
+{
+       php_http_client_enqueue_t *enqueue;
+       TSRMLS_FETCH_FROM_CTX(h->ts);
+
+       switch (opt) {
+       case PHP_HTTP_CLIENT_OPT_PROGRESS_INFO:
+               if ((enqueue = php_http_client_enqueued(h, arg, NULL))) {
+                       php_http_client_curl_handler_t *handler = enqueue->opaque;
+
+                       *((php_http_client_progress_state_t **) res) = &handler->progress;
+                       return SUCCESS;
+               }
+               break;
+
+       case PHP_HTTP_CLIENT_OPT_TRANSFER_INFO:
+               if ((enqueue = php_http_client_enqueued(h, arg, NULL))) {
+                       php_http_client_curl_handler_t *handler = enqueue->opaque;
+
+                       php_http_curle_get_info(handler->handle, *(HashTable **) res);
+                       return SUCCESS;
+               }
+               break;
+
+       case PHP_HTTP_CLIENT_OPT_AVAILABLE_OPTIONS:
+               zend_hash_apply_with_arguments(&php_http_curle_options.options TSRMLS_CC, apply_available_options, 1, *(HashTable **) res);
+               break;
+
+       case PHP_HTTP_CLIENT_OPT_AVAILABLE_CONFIGURATION:
+               zend_hash_apply_with_arguments(&php_http_curlm_options.options TSRMLS_CC, apply_available_options, 1, *(HashTable **) res);
+               break;
+
+       default:
+               break;
+       }
+
+       return FAILURE;
+}
+
+static php_http_client_ops_t php_http_client_curl_ops = {
+       &php_http_curlm_resource_factory_ops,
+       php_http_client_curl_init,
+       NULL /* copy */,
+       php_http_client_curl_dtor,
+       php_http_client_curl_reset,
+       php_http_client_curl_exec,
+       php_http_client_curl_wait,
+       php_http_client_curl_once,
+       php_http_client_curl_enqueue,
+       php_http_client_curl_dequeue,
+       php_http_client_curl_setopt,
+       php_http_client_curl_getopt
+};
+
+php_http_client_ops_t *php_http_client_curl_get_ops(void)
+{
+       return &php_http_client_curl_ops;
+}
+
+PHP_MINIT_FUNCTION(http_client_curl)
+{
+       php_http_options_t *options;
+       php_http_client_driver_t driver = {
+               ZEND_STRL("curl"),
+               &php_http_client_curl_ops
+       };
+
+       if (SUCCESS != php_http_client_driver_add(&driver)) {
+               return FAILURE;
+       }
+
+       if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl"), &php_http_curlm_resource_factory_ops, NULL, NULL TSRMLS_CC)) {
+               return FAILURE;
+       }
+       if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl\\Request"), &php_http_curle_resource_factory_ops, NULL, NULL TSRMLS_CC)) {
+               return FAILURE;
+       }
+
+       if ((options = php_http_options_init(&php_http_curle_options, 1))) {
+               options->getter = php_http_curle_get_option;
+               options->setter = php_http_curle_set_option;
+
+               php_http_curle_options_init(options TSRMLS_CC);
+       }
+       if ((options = php_http_options_init(&php_http_curlm_options, 1))) {
+               options->getter = php_http_option_get;
+               options->setter = php_http_curlm_set_option;
+
+               php_http_curlm_options_init(options TSRMLS_CC);
+       }
+
+       /*
+       * HTTP Protocol Version Constants
+       */
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_1_0", CURL_HTTP_VERSION_1_0, CONST_CS|CONST_PERSISTENT);
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_1_1", CURL_HTTP_VERSION_1_1, CONST_CS|CONST_PERSISTENT);
+#if PHP_HTTP_CURL_VERSION(7,33,0)
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_2_0", CURL_HTTP_VERSION_2_0, CONST_CS|CONST_PERSISTENT);
+#endif
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_ANY", CURL_HTTP_VERSION_NONE, CONST_CS|CONST_PERSISTENT);
+
+       /*
+       * SSL Version Constants
+       */
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1", CURL_SSLVERSION_TLSv1, CONST_CS|CONST_PERSISTENT);
+#if PHP_HTTP_CURL_VERSION(7,34,0)
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1_0", CURL_SSLVERSION_TLSv1_0, CONST_CS|CONST_PERSISTENT);
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1_1", CURL_SSLVERSION_TLSv1_1, CONST_CS|CONST_PERSISTENT);
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1_2", CURL_SSLVERSION_TLSv1_2, CONST_CS|CONST_PERSISTENT);
+#endif
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_SSLv2", CURL_SSLVERSION_SSLv2, CONST_CS|CONST_PERSISTENT);
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_SSLv3", CURL_SSLVERSION_SSLv3, CONST_CS|CONST_PERSISTENT);
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_ANY", CURL_SSLVERSION_DEFAULT, CONST_CS|CONST_PERSISTENT);
+#if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP)
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "TLSAUTH_SRP", CURL_TLSAUTH_SRP, CONST_CS|CONST_PERSISTENT);
+#endif
+
+       /*
+       * DNS IPvX resolving
+       */
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "IPRESOLVE_V4", CURL_IPRESOLVE_V4, CONST_CS|CONST_PERSISTENT);
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "IPRESOLVE_V6", CURL_IPRESOLVE_V6, CONST_CS|CONST_PERSISTENT);
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "IPRESOLVE_ANY", CURL_IPRESOLVE_WHATEVER, CONST_CS|CONST_PERSISTENT);
+
+       /*
+       * Auth Constants
+       */
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_BASIC", CURLAUTH_BASIC, CONST_CS|CONST_PERSISTENT);
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_DIGEST", CURLAUTH_DIGEST, CONST_CS|CONST_PERSISTENT);
+#if PHP_HTTP_CURL_VERSION(7,19,3)
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_DIGEST_IE", CURLAUTH_DIGEST_IE, CONST_CS|CONST_PERSISTENT);
+#endif
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_NTLM", CURLAUTH_NTLM, CONST_CS|CONST_PERSISTENT);
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_GSSNEG", CURLAUTH_GSSNEGOTIATE, CONST_CS|CONST_PERSISTENT);
+#if PHP_HTTP_CURL_VERSION(7,38,0)
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_SPNEGO", CURLAUTH_NEGOTIATE, CONST_CS|CONST_PERSISTENT);
+#endif
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_ANY", CURLAUTH_ANY, CONST_CS|CONST_PERSISTENT);
+
+       /*
+       * Proxy Type Constants
+       */
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS4", CURLPROXY_SOCKS4, CONST_CS|CONST_PERSISTENT);
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS4A", CURLPROXY_SOCKS5, CONST_CS|CONST_PERSISTENT);
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS5_HOSTNAME", CURLPROXY_SOCKS5, CONST_CS|CONST_PERSISTENT);
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS5", CURLPROXY_SOCKS5, CONST_CS|CONST_PERSISTENT);
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_HTTP", CURLPROXY_HTTP, CONST_CS|CONST_PERSISTENT);
+#if PHP_HTTP_CURL_VERSION(7,19,4)
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_HTTP_1_0", CURLPROXY_HTTP_1_0, CONST_CS|CONST_PERSISTENT);
+#endif
+
+       /*
+       * Post Redirection Constants
+       */
+#if PHP_HTTP_CURL_VERSION(7,19,1)
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_301", CURL_REDIR_POST_301, CONST_CS|CONST_PERSISTENT);
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_302", CURL_REDIR_POST_302, CONST_CS|CONST_PERSISTENT);
+#if PHP_HTTP_CURL_VERSION(7,26,0)
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_303", CURL_REDIR_POST_303, CONST_CS|CONST_PERSISTENT);
+#endif
+       REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_ALL", CURL_REDIR_POST_ALL, CONST_CS|CONST_PERSISTENT);
+#endif
+
+       return SUCCESS;
+}
+
+PHP_MSHUTDOWN_FUNCTION(http_client_curl)
+{
+       php_persistent_handle_cleanup(ZEND_STRL("http\\Client\\Curl"), NULL, 0 TSRMLS_CC);
+       php_persistent_handle_cleanup(ZEND_STRL("http\\Client\\Curl\\Request"), NULL, 0 TSRMLS_CC);
+
+       php_http_options_dtor(&php_http_curle_options);
+       php_http_options_dtor(&php_http_curlm_options);
+
+       return SUCCESS;
+}
+
+#endif /* PHP_HTTP_HAVE_CURL */
+
+/*
+ * 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_client_curl.h b/src/php_http_client_curl.h
new file mode 100644 (file)
index 0000000..c82a09c
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_CLIENT_CURL_H
+#define PHP_HTTP_CLIENT_CURL_H
+
+#if PHP_HTTP_HAVE_CURL
+
+PHP_MINIT_FUNCTION(http_client_curl);
+PHP_MSHUTDOWN_FUNCTION(http_client_curl);
+#endif /* PHP_HTTP_HAVE_CURL */
+
+#endif /* PHP_HTTP_CLIENT_CURL_H */
+
+/*
+ * 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_client_request.c b/src/php_http_client_request.c
new file mode 100644 (file)
index 0000000..0e40cc5
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+void php_http_client_options_set_subr(zval *this_ptr, char *key, size_t len, zval *opts, int overwrite TSRMLS_DC);
+void php_http_client_options_set(zval *this_ptr, zval *opts TSRMLS_DC);
+void php_http_client_options_get_subr(zval *this_ptr, char *key, size_t len, zval *return_value TSRMLS_DC);
+
+#define PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj) \
+       do { \
+               if (!obj->message) { \
+                       obj->message = php_http_message_init(NULL, PHP_HTTP_REQUEST, NULL TSRMLS_CC); \
+               } \
+       } while(0)
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest___construct, 0, 0, 0)
+       ZEND_ARG_INFO(0, method)
+       ZEND_ARG_INFO(0, url)
+       ZEND_ARG_ARRAY_INFO(0, headers, 1)
+       ZEND_ARG_OBJ_INFO(0, body, http\\Message\\Body, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClientRequest, __construct)
+{
+       char *meth_str = NULL;
+       int meth_len = 0;
+       zval *zheaders = NULL, *zbody = NULL, *zurl = NULL;
+       php_http_message_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!z!a!O!", &meth_str, &meth_len, &zurl, &zheaders, &zbody, php_http_message_body_class_entry), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       if (obj->message) {
+               php_http_message_set_type(obj->message, PHP_HTTP_REQUEST);
+       } else {
+               obj->message = php_http_message_init(NULL, PHP_HTTP_REQUEST, NULL TSRMLS_CC);
+       }
+
+       if (zbody) {
+               php_http_expect(SUCCESS == php_http_message_object_set_body(obj, zbody TSRMLS_CC), unexpected_val, return);
+       }
+       if (meth_str && meth_len) {
+               PHP_HTTP_INFO(obj->message).request.method = estrndup(meth_str, meth_len);
+       }
+       if (zurl) {
+               PHP_HTTP_INFO(obj->message).request.url = php_http_url_from_zval(zurl, ~0 TSRMLS_CC);
+       }
+       if (zheaders) {
+               array_copy(Z_ARRVAL_P(zheaders), &obj->message->hdrs);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_setContentType, 0, 0, 1)
+       ZEND_ARG_INFO(0, content_type)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClientRequest, setContentType)
+{
+       char *ct_str;
+       int ct_len;
+       php_http_message_object_t *obj;
+       zval *zct;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ct_str, &ct_len), invalid_arg, return);
+
+       if (ct_len && !strchr(ct_str, '/')) {
+               php_http_throw(unexpected_val, "Content type \"%s\" does not seem to contain a primary and a secondary part", ct_str);
+               return;
+       }
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj);
+
+       MAKE_STD_ZVAL(zct);
+       ZVAL_STRINGL(zct, ct_str, ct_len, 1);
+       zend_hash_update(&obj->message->hdrs, "Content-Type", sizeof("Content-Type"), (void *) &zct, sizeof(void *), NULL);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_getContentType, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClientRequest, getContentType)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+               zval *zct;
+
+               PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj);
+
+               php_http_message_update_headers(obj->message);
+               zct = php_http_message_header(obj->message, ZEND_STRL("Content-Type"), 1);
+               if (zct) {
+                       RETURN_ZVAL(zct, 0, 1);
+               }
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_setQuery, 0, 0, 0)
+       ZEND_ARG_INFO(0, query_data)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClientRequest, setQuery)
+{
+       zval *qdata = NULL;
+       php_http_message_object_t *obj;
+       php_http_url_t *old_url = NULL, new_url = {NULL};
+       char empty[] = "";
+       unsigned flags = PHP_HTTP_URL_REPLACE;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z!", &qdata), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj);
+
+       if (qdata) {
+               zval arr, str;
+
+               INIT_PZVAL(&arr);
+               array_init(&arr);
+               INIT_PZVAL(&str);
+               ZVAL_NULL(&str);
+
+               php_http_expect(SUCCESS == php_http_querystring_update(&arr, qdata, &str TSRMLS_CC), bad_querystring,
+                               zval_dtor(&arr);
+                               return;
+               );
+               new_url.query = Z_STRVAL(str);
+               zval_dtor(&arr);
+       } else {
+               flags = PHP_HTTP_URL_STRIP_QUERY;
+       }
+
+       if (obj->message->http.info.request.url) {
+               old_url = obj->message->http.info.request.url;
+       }
+
+       obj->message->http.info.request.url = php_http_url_mod(old_url, &new_url, flags TSRMLS_CC);
+
+       if (old_url) {
+               php_http_url_free(&old_url);
+       }
+       if (new_url.query != &empty[0]) {
+               PTR_FREE(new_url.query);
+       }
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_getQuery, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClientRequest, getQuery)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj);
+
+               if (obj->message->http.info.request.url && obj->message->http.info.request.url->query) {
+                       RETVAL_STRING(obj->message->http.info.request.url->query, 1);
+               }
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_addQuery, 0, 0, 1)
+       ZEND_ARG_INFO(0, query_data)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClientRequest, addQuery)
+{
+       zval *qdata, arr, str;
+       php_http_message_object_t *obj;
+       php_http_url_t *old_url = NULL, new_url = {NULL};
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &qdata), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj);
+
+       INIT_PZVAL(&arr);
+       array_init(&arr);
+       INIT_PZVAL(&str);
+       ZVAL_NULL(&str);
+
+       php_http_expect(SUCCESS == php_http_querystring_update(&arr, qdata, &str TSRMLS_CC), bad_querystring,
+                       zval_dtor(&arr);
+                       return;
+       );
+       new_url.query = Z_STRVAL(str);
+       zval_dtor(&arr);
+
+       if (obj->message->http.info.request.url) {
+               old_url = obj->message->http.info.request.url;
+       }
+
+       obj->message->http.info.request.url = php_http_url_mod(old_url, &new_url, PHP_HTTP_URL_JOIN_QUERY TSRMLS_CC);
+
+       if (old_url) {
+               php_http_url_free(&old_url);
+       }
+       PTR_FREE(new_url.query);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_setOptions, 0, 0, 0)
+       ZEND_ARG_ARRAY_INFO(0, options, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClientRequest, setOptions)
+{
+       zval *opts = NULL;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return);
+
+       php_http_client_options_set(getThis(), opts TSRMLS_CC);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_getOptions, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClientRequest, getOptions)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               zval *zoptions = zend_read_property(php_http_client_request_class_entry, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
+               RETURN_ZVAL(zoptions, 1, 0);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_setSslOptions, 0, 0, 0)
+       ZEND_ARG_ARRAY_INFO(0, ssl_options, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClientRequest, setSslOptions)
+{
+       zval *opts = NULL;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return);
+
+       php_http_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 1 TSRMLS_CC);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_addSslOptions, 0, 0, 0)
+       ZEND_ARG_ARRAY_INFO(0, ssl_options, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClientRequest, addSslOptions)
+{
+       zval *opts = NULL;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return);
+
+       php_http_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 0 TSRMLS_CC);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_getSslOptions, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClientRequest, getSslOptions)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_client_options_get_subr(getThis(), ZEND_STRS("ssl"), return_value TSRMLS_CC);
+       }
+}
+
+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, 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)
+       PHP_ME(HttpClientRequest, getQuery,       ai_HttpClientRequest_getQuery,       ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClientRequest, addQuery,       ai_HttpClientRequest_addQuery,       ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClientRequest, setOptions,     ai_HttpClientRequest_setOptions,     ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClientRequest, getOptions,     ai_HttpClientRequest_getOptions,     ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClientRequest, setSslOptions,  ai_HttpClientRequest_setSslOptions,  ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClientRequest, getSslOptions,  ai_HttpClientRequest_getSslOptions,  ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClientRequest, addSslOptions,  ai_HttpClientRequest_addSslOptions,  ZEND_ACC_PUBLIC)
+       EMPTY_FUNCTION_ENTRY
+};
+
+zend_class_entry *php_http_client_request_class_entry;
+
+PHP_MINIT_FUNCTION(http_client_request)
+{
+       zend_class_entry ce = {0};
+
+       INIT_NS_CLASS_ENTRY(ce, "http\\Client", "Request", php_http_client_request_methods);
+       php_http_client_request_class_entry = zend_register_internal_class_ex(&ce, php_http_message_class_entry, NULL TSRMLS_CC);
+
+       zend_declare_property_null(php_http_client_request_class_entry, ZEND_STRL("options"), ZEND_ACC_PROTECTED TSRMLS_CC);
+
+       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_client_request.h b/src/php_http_client_request.h
new file mode 100644 (file)
index 0000000..474114b
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_CLIENT_REQUEST_H
+#define PHP_HTTP_CLIENT_REQUEST_H
+
+PHP_HTTP_API zend_class_entry *php_http_client_request_class_entry;
+PHP_MINIT_FUNCTION(http_client_request);
+
+#endif /* PHP_HTTP_CLIENT_REQUEST_H */
+
+/*
+ * 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_client_response.c b/src/php_http_client_response.c
new file mode 100644 (file)
index 0000000..8d512ec
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientResponse_getCookies, 0, 0, 0)
+       ZEND_ARG_INFO(0, flags)
+       ZEND_ARG_INFO(0, allowed_extras)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClientResponse, getCookies)
+{
+       long flags = 0;
+       zval *allowed_extras_array = NULL;
+       int i = 0;
+       char **allowed_extras = NULL;
+       zval *header = NULL, **entry = NULL;
+       HashPosition pos;
+       php_http_message_object_t *msg;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|la!", &flags, &allowed_extras_array)) {
+               return;
+       }
+
+       msg = zend_object_store_get_object(getThis() TSRMLS_CC);
+       array_init(return_value);
+
+       if (allowed_extras_array) {
+               allowed_extras = ecalloc(zend_hash_num_elements(Z_ARRVAL_P(allowed_extras_array)) + 1, sizeof(char *));
+               FOREACH_VAL(pos, allowed_extras_array, entry) {
+                       zval *data = php_http_ztyp(IS_STRING, *entry);
+                       allowed_extras[i++] = estrndup(Z_STRVAL_P(data), Z_STRLEN_P(data));
+                       zval_ptr_dtor(&data);
+               }
+       }
+
+       if ((header = php_http_message_header(msg->message, ZEND_STRL("Set-Cookie"), 0))) {
+               php_http_cookie_list_t *list;
+
+               if (Z_TYPE_P(header) == IS_ARRAY) {
+                       zval **single_header;
+
+                       FOREACH_VAL(pos, header, single_header) {
+                               zval *data = php_http_ztyp(IS_STRING, *single_header);
+
+                               if ((list = php_http_cookie_list_parse(NULL, Z_STRVAL_P(data), Z_STRLEN_P(data), flags, allowed_extras TSRMLS_CC))) {
+                                       zval *cookie;
+
+                                       MAKE_STD_ZVAL(cookie);
+                                       ZVAL_OBJVAL(cookie, php_http_cookie_object_new_ex(php_http_cookie_class_entry, list, NULL TSRMLS_CC), 0);
+                                       add_next_index_zval(return_value, cookie);
+                               }
+                               zval_ptr_dtor(&data);
+                       }
+               } else {
+                       zval *data = php_http_ztyp(IS_STRING, header);
+                       if ((list = php_http_cookie_list_parse(NULL, Z_STRVAL_P(data), Z_STRLEN_P(data), flags, allowed_extras TSRMLS_CC))) {
+                               zval *cookie;
+
+                               MAKE_STD_ZVAL(cookie);
+                               ZVAL_OBJVAL(cookie, php_http_cookie_object_new_ex(php_http_cookie_class_entry, list, NULL TSRMLS_CC), 0);
+                               add_next_index_zval(return_value, cookie);
+                       }
+                       zval_ptr_dtor(&data);
+               }
+               zval_ptr_dtor(&header);
+       }
+
+       if (allowed_extras) {
+               for (i = 0; allowed_extras[i]; ++i) {
+                       efree(allowed_extras[i]);
+               }
+               efree(allowed_extras);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientResponse_getTransferInfo, 0, 0, 0)
+       ZEND_ARG_INFO(0, element)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpClientResponse, getTransferInfo)
+{
+       char *info_name = NULL;
+       int info_len = 0;
+       zval *info;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &info_name, &info_len), invalid_arg, return);
+
+       info = zend_read_property(php_http_client_response_class_entry, getThis(), ZEND_STRL("transferInfo"), 0 TSRMLS_CC);
+
+       /* request completed? */
+       if (Z_TYPE_P(info) != IS_OBJECT) {
+               php_http_throw(bad_method_call, "Incomplete state", NULL);
+               return;
+       }
+
+       if (info_len && info_name) {
+               info = zend_read_property(NULL, info, php_http_pretty_key(info_name, info_len, 0, 0), info_len, 0 TSRMLS_CC);
+
+               if (!info) {
+                       php_http_throw(unexpected_val, "Could not find transfer info with name '%s'", info_name);
+                       return;
+               }
+       }
+
+       RETURN_ZVAL(info, 1, 0);
+}
+
+static zend_function_entry php_http_client_response_methods[] = {
+       PHP_ME(HttpClientResponse, getCookies,      ai_HttpClientResponse_getCookies,      ZEND_ACC_PUBLIC)
+       PHP_ME(HttpClientResponse, getTransferInfo, ai_HttpClientResponse_getTransferInfo, ZEND_ACC_PUBLIC)
+       EMPTY_FUNCTION_ENTRY
+};
+
+zend_class_entry *php_http_client_response_class_entry;
+
+PHP_MINIT_FUNCTION(http_client_response)
+{
+       zend_class_entry ce = {0};
+
+       INIT_NS_CLASS_ENTRY(ce, "http\\Client", "Response", php_http_client_response_methods);
+       php_http_client_response_class_entry = zend_register_internal_class_ex(&ce, php_http_message_class_entry, NULL TSRMLS_CC);
+       zend_declare_property_null(php_http_client_response_class_entry, ZEND_STRL("transferInfo"), ZEND_ACC_PROTECTED TSRMLS_CC);
+
+       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_client_response.h b/src/php_http_client_response.h
new file mode 100644 (file)
index 0000000..15d004e
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_CLIENT_RESPONSE_H
+#define PHP_HTTP_CLIENT_RESPONSE_H
+
+PHP_HTTP_API zend_class_entry *php_http_client_response_class_entry;
+PHP_MINIT_FUNCTION(http_client_response);
+
+#endif /* PHP_HTTP_CLIENT_RESPONSE_H */
+
+
+/*
+ * 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_cookie.c b/src/php_http_cookie.c
new file mode 100644 (file)
index 0000000..354dfa6
--- /dev/null
@@ -0,0 +1,1038 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+php_http_cookie_list_t *php_http_cookie_list_init(php_http_cookie_list_t *list TSRMLS_DC)
+{
+       if (!list) {
+               list = emalloc(sizeof(*list));
+       }
+       
+       zend_hash_init(&list->cookies, 0, NULL, ZVAL_PTR_DTOR, 0);
+       zend_hash_init(&list->extras, 0, NULL, ZVAL_PTR_DTOR, 0);
+       
+       list->path = NULL;
+       list->domain = NULL;
+       list->expires = -1;
+       list->max_age = -1;
+       list->flags = 0;
+       
+       TSRMLS_SET_CTX(list->ts);
+
+       return list;
+}
+
+php_http_cookie_list_t *php_http_cookie_list_copy(php_http_cookie_list_t *from, php_http_cookie_list_t *to)
+{
+       TSRMLS_FETCH_FROM_CTX(from->ts);
+
+       to = php_http_cookie_list_init(to TSRMLS_CC);
+
+       array_copy(&from->cookies, &to->cookies);
+       array_copy(&from->extras, &to->extras);
+
+       PTR_SET(to->path, from->path ? estrdup(from->path) : NULL);
+       PTR_SET(to->domain, from->domain ? estrdup(from->domain) : NULL);
+       to->expires = from->expires;
+       to->max_age = from->max_age;
+       to->flags = from->flags;
+
+       return to;
+}
+
+void php_http_cookie_list_dtor(php_http_cookie_list_t *list)
+{
+       if (list) {
+               zend_hash_destroy(&list->cookies);
+               zend_hash_destroy(&list->extras);
+       
+               PTR_SET(list->path, NULL);
+               PTR_SET(list->domain, NULL);
+       }
+}
+
+
+
+void php_http_cookie_list_free(php_http_cookie_list_t **list)
+{
+       if (*list) {
+               php_http_cookie_list_dtor(*list);
+               efree(*list);
+               *list = NULL;
+       }
+}
+
+const char *php_http_cookie_list_get_cookie(php_http_cookie_list_t *list, const char *name, size_t name_len, zval **zcookie)
+{
+       zval **cookie;
+       if ((SUCCESS != zend_symtable_find(&list->cookies, name, name_len + 1, (void *) &cookie)) || (Z_TYPE_PP(cookie) != IS_STRING)) {
+               return NULL;
+       }
+       if (zcookie) {
+               *zcookie = *cookie;
+       }
+       return Z_STRVAL_PP(cookie);
+}
+
+const char *php_http_cookie_list_get_extra(php_http_cookie_list_t *list, const char *name, size_t name_len, zval **zextra)
+{
+       zval **extra;
+
+       if ((SUCCESS != zend_symtable_find(&list->extras, name, name_len + 1, (void *) &extra)) || (Z_TYPE_PP(extra) != IS_STRING)) {
+               return NULL;
+       }
+       if (zextra) {
+               *zextra = *extra;
+       }
+       return Z_STRVAL_PP(extra);
+}
+
+void php_http_cookie_list_add_cookie(php_http_cookie_list_t *list, const char *name, size_t name_len, const char *value, size_t value_len)
+{
+       zval *cookie_value;
+
+       MAKE_STD_ZVAL(cookie_value);
+       ZVAL_STRINGL(cookie_value, estrndup(value, value_len), value_len, 0);
+       zend_symtable_update(&list->cookies, name, name_len + 1, (void *) &cookie_value, sizeof(zval *), NULL);
+}
+
+void php_http_cookie_list_add_extra(php_http_cookie_list_t *list, const char *name, size_t name_len, const char *value, size_t value_len)
+{
+       zval *cookie_value;
+
+       MAKE_STD_ZVAL(cookie_value);
+       ZVAL_STRINGL(cookie_value, estrndup(value, value_len), value_len, 0);
+       zend_symtable_update(&list->extras, name, name_len + 1, (void *) &cookie_value, sizeof(zval *), NULL);
+}
+
+#define _KEY_IS(s) (key->len == sizeof(s) && !strncasecmp(key->str, (s), key->len))
+static void add_entry(php_http_cookie_list_t *list, char **allowed_extras, long flags, php_http_array_hashkey_t *key, zval *val)
+{
+       zval *arg = php_http_zsep(1, IS_STRING, val);
+
+       if (!(flags & PHP_HTTP_COOKIE_PARSE_RAW)) {
+               Z_STRLEN_P(arg) = php_raw_url_decode(Z_STRVAL_P(arg), Z_STRLEN_P(arg));
+       }
+
+       if _KEY_IS("path") {
+               PTR_SET(list->path, estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(arg)));
+       } else if _KEY_IS("domain") {
+               PTR_SET(list->domain, estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(arg)));
+       } else if _KEY_IS("expires") {
+               char *date = estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(arg));
+               list->expires = php_parse_date(date, NULL);
+               efree(date);
+       } else if _KEY_IS("max-age") {
+               list->max_age = strtol(Z_STRVAL_P(arg), NULL, 10);
+       } else if _KEY_IS("secure") {
+               list->flags |= PHP_HTTP_COOKIE_SECURE;
+       } else if _KEY_IS("httpOnly") {
+               list->flags |= PHP_HTTP_COOKIE_HTTPONLY;
+       } else {
+               /* check for extra */
+               if (allowed_extras) {
+                       char **ae = allowed_extras;
+
+                       php_http_array_hashkey_stringify(key);
+                       for (; *ae; ++ae) {
+                               if (!strncasecmp(key->str, *ae, key->len)) {
+                                       if (key->type == HASH_KEY_IS_LONG) {
+                                               zend_hash_index_update(&list->extras, key->num, (void *) &arg, sizeof(zval *), NULL);
+                                       } else {
+                                               zend_hash_update(&list->extras, key->str, key->len, (void *) &arg, sizeof(zval *), NULL);
+                                       }
+                                       php_http_array_hashkey_stringfree(key);
+                                       return;
+                               }
+                       }
+                       php_http_array_hashkey_stringfree(key);
+               }
+
+               /* cookie */
+               if (key->type == HASH_KEY_IS_LONG) {
+                       zend_hash_index_update(&list->cookies, key->num, (void *) &arg, sizeof(zval *), NULL);
+               } else {
+                       zend_hash_update(&list->cookies, key->str, key->len, (void *) &arg, sizeof(zval *), NULL);
+               }
+               return;
+       }
+       zval_ptr_dtor(&arg);
+}
+
+php_http_cookie_list_t *php_http_cookie_list_parse(php_http_cookie_list_t *list, const char *str, size_t len, long flags, char **allowed_extras TSRMLS_DC)
+{
+       php_http_params_opts_t opts;
+       HashTable params;
+       HashPosition pos1, pos2;
+       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+       zval **param, **val, **args, **arg;
+
+       php_http_params_opts_default_get(&opts);
+       opts.input.str = estrndup(str, len);
+       opts.input.len = len;
+       opts.param = NULL;
+       zend_hash_init(&params, 10, NULL, ZVAL_PTR_DTOR, 0);
+       php_http_params_parse(&params, &opts TSRMLS_CC);
+       efree(opts.input.str);
+
+       list = php_http_cookie_list_init(list TSRMLS_CC);
+       FOREACH_HASH_KEYVAL(pos1, &params, key, param) {
+               if (Z_TYPE_PP(param) == IS_ARRAY) {
+                       if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(param), ZEND_STRS("value"), (void *) &val)) {
+                               add_entry(list, NULL, flags, &key, *val);
+                       }
+                       if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(param), ZEND_STRS("arguments"), (void *) &args) && Z_TYPE_PP(args) == IS_ARRAY) {
+                               FOREACH_KEYVAL(pos2, *args, key, arg) {
+                                       add_entry(list, allowed_extras, flags, &key, *arg);
+                               }
+                       }
+               }
+       }
+       zend_hash_destroy(&params);
+
+       return list;
+}
+
+void php_http_cookie_list_to_struct(php_http_cookie_list_t *list, zval *strct)
+{
+       zval array, *cookies, *extras;
+       TSRMLS_FETCH_FROM_CTX(list->ts);
+       
+       INIT_PZVAL_ARRAY(&array, HASH_OF(strct));
+       
+       MAKE_STD_ZVAL(cookies);
+       array_init(cookies);
+       zend_hash_copy(Z_ARRVAL_P(cookies), &list->cookies, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
+       add_assoc_zval(&array, "cookies", cookies);
+       
+       MAKE_STD_ZVAL(extras);
+       array_init(extras);
+       zend_hash_copy(Z_ARRVAL_P(extras), &list->extras, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
+       add_assoc_zval(&array, "extras", extras);
+       
+       add_assoc_long(&array, "flags", list->flags);
+       add_assoc_long(&array, "expires", (long) list->expires);
+       add_assoc_long(&array, "max-age", (long) list->max_age);
+       add_assoc_string(&array, "path", STR_PTR(list->path), 1);
+       add_assoc_string(&array, "domain", STR_PTR(list->domain), 1);
+}
+
+php_http_cookie_list_t *php_http_cookie_list_from_struct(php_http_cookie_list_t *list, zval *strct TSRMLS_DC)
+{
+       zval **tmp, *cpy;
+       HashTable *ht = HASH_OF(strct);
+       
+       list = php_http_cookie_list_init(list TSRMLS_CC);
+       
+       if (SUCCESS == zend_hash_find(ht, "cookies", sizeof("cookies"), (void *) &tmp) && Z_TYPE_PP(tmp) == IS_ARRAY) {
+               zend_hash_copy(&list->cookies, Z_ARRVAL_PP(tmp), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
+       }
+       if (SUCCESS == zend_hash_find(ht, "extras", sizeof("extras"), (void *) &tmp) && Z_TYPE_PP(tmp) == IS_ARRAY) {
+               zend_hash_copy(&list->extras, Z_ARRVAL_PP(tmp), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
+       }
+       if (SUCCESS == zend_hash_find(ht, "flags", sizeof("flags"), (void *) &tmp)) {
+               cpy = php_http_ztyp(IS_LONG, *tmp);
+               list->flags = Z_LVAL_P(cpy);
+               zval_ptr_dtor(&cpy);
+       }
+       if (SUCCESS == zend_hash_find(ht, "expires", sizeof("expires"), (void *) &tmp)) {
+               if (Z_TYPE_PP(tmp) == IS_LONG) {
+                       list->expires = Z_LVAL_PP(tmp);
+               } else {
+                       long lval;
+
+                       cpy = php_http_ztyp(IS_STRING, *tmp);
+                       if (IS_LONG == is_numeric_string(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy), &lval, NULL, 0)) {
+                               list->expires = lval;
+                       } else {
+                               list->expires = php_parse_date(Z_STRVAL_P(cpy), NULL);
+                       }
+
+                       zval_ptr_dtor(&cpy);
+               }
+       }
+       if (SUCCESS == zend_hash_find(ht, "max-age", sizeof("max-age"), (void *) &tmp)) {
+               if (Z_TYPE_PP(tmp) == IS_LONG) {
+                       list->max_age = Z_LVAL_PP(tmp);
+               } else {
+                       long lval;
+
+                       cpy = php_http_ztyp(IS_STRING, *tmp);
+                       if (IS_LONG == is_numeric_string(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy), &lval, NULL, 0)) {
+                               list->max_age = lval;
+                       }
+
+                       zval_ptr_dtor(&cpy);
+               }
+       }
+       if (SUCCESS == zend_hash_find(ht, "path", sizeof("path"), (void *) &tmp) && Z_TYPE_PP(tmp) == IS_STRING) {
+               list->path = estrndup(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
+       }
+       if (SUCCESS == zend_hash_find(ht, "domain", sizeof("domain"), (void *) &tmp) && Z_TYPE_PP(tmp) == IS_STRING) {
+               list->domain = estrndup(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
+       }
+       
+       return list;
+}
+
+static inline void append_encoded(php_http_buffer_t *buf, const char *key, size_t key_len, const char *val, size_t val_len)
+{
+       char *enc_str[2];
+       int enc_len[2];
+       
+       enc_str[0] = php_raw_url_encode(key, key_len, &enc_len[0]);
+       enc_str[1] = php_raw_url_encode(val, val_len, &enc_len[1]);
+       
+       php_http_buffer_append(buf, enc_str[0], enc_len[0]);
+       php_http_buffer_appends(buf, "=");
+       php_http_buffer_append(buf, enc_str[1], enc_len[1]);
+       php_http_buffer_appends(buf, "; ");
+       
+       efree(enc_str[0]);
+       efree(enc_str[1]);
+}
+
+void php_http_cookie_list_to_string(php_http_cookie_list_t *list, char **str, size_t *len)
+{
+       php_http_buffer_t buf;
+       zval **val;
+       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+       HashPosition pos;
+       TSRMLS_FETCH_FROM_CTX(list->ts);
+       
+       php_http_buffer_init(&buf);
+       
+       FOREACH_HASH_KEYVAL(pos, &list->cookies, key, val) {
+               zval *tmp = php_http_ztyp(IS_STRING, *val);
+
+               php_http_array_hashkey_stringify(&key);
+               append_encoded(&buf, key.str, key.len-1, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
+               php_http_array_hashkey_stringfree(&key);
+
+               zval_ptr_dtor(&tmp);
+       }
+       
+       if (list->domain && *list->domain) {
+               php_http_buffer_appendf(&buf, "domain=%s; ", list->domain);
+       }
+       if (list->path && *list->path) {
+               php_http_buffer_appendf(&buf, "path=%s; ", list->path);
+       }
+       if (list->expires >= 0) {
+               char *date = php_format_date(ZEND_STRL(PHP_HTTP_DATE_FORMAT), list->expires, 0 TSRMLS_CC);
+               php_http_buffer_appendf(&buf, "expires=%s; ", date);
+               efree(date);
+       }
+       if (list->max_age >= 0) {
+               php_http_buffer_appendf(&buf, "max-age=%ld; ", list->max_age);
+       }
+       
+       FOREACH_HASH_KEYVAL(pos, &list->extras, key, val) {
+               zval *tmp = php_http_ztyp(IS_STRING, *val);
+
+               php_http_array_hashkey_stringify(&key);
+               append_encoded(&buf, key.str, key.len-1, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
+               php_http_array_hashkey_stringfree(&key);
+
+               zval_ptr_dtor(&tmp);
+       }
+       
+       if (list->flags & PHP_HTTP_COOKIE_SECURE) {
+               php_http_buffer_appends(&buf, "secure; ");
+       }
+       if (list->flags & PHP_HTTP_COOKIE_HTTPONLY) {
+               php_http_buffer_appends(&buf, "httpOnly; ");
+       }
+       
+       php_http_buffer_fix(&buf);
+       *str = buf.data;
+       *len = buf.used;
+}
+
+
+
+static zend_object_handlers php_http_cookie_object_handlers;
+
+zend_object_value php_http_cookie_object_new(zend_class_entry *ce TSRMLS_DC)
+{
+       return php_http_cookie_object_new_ex(ce, NULL, NULL TSRMLS_CC);
+}
+
+zend_object_value php_http_cookie_object_new_ex(zend_class_entry *ce, php_http_cookie_list_t *list, php_http_cookie_object_t **ptr TSRMLS_DC)
+{
+       php_http_cookie_object_t *o;
+
+       o = ecalloc(sizeof(*o), 1);
+       zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
+       object_properties_init((zend_object *) o, ce);
+
+       if (list) {
+               o->list = list;
+       }
+
+       if (ptr) {
+               *ptr = o;
+       }
+
+       o->zv.handle = zend_objects_store_put(o, NULL, php_http_cookie_object_free, NULL TSRMLS_CC);
+       o->zv.handlers = &php_http_cookie_object_handlers;
+
+       return o->zv;
+}
+
+#define PHP_HTTP_COOKIE_OBJECT_INIT(obj) \
+       do { \
+               if (!obj->list) { \
+                       obj->list = php_http_cookie_list_init(NULL TSRMLS_CC); \
+               } \
+       } while(0)
+
+zend_object_value php_http_cookie_object_clone(zval *this_ptr TSRMLS_DC)
+{
+       php_http_cookie_object_t *new_obj, *old_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       zend_object_value ov;
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(old_obj);
+
+       ov = php_http_cookie_object_new_ex(old_obj->zo.ce, php_http_cookie_list_copy(old_obj->list, NULL), &new_obj TSRMLS_CC);
+       zend_objects_clone_members((zend_object *) new_obj, ov, (zend_object *) old_obj, Z_OBJ_HANDLE_P(getThis()) TSRMLS_CC);
+
+       return ov;
+}
+
+void php_http_cookie_object_free(void *object TSRMLS_DC)
+{
+       php_http_cookie_object_t *obj = object;
+
+       php_http_cookie_list_free(&obj->list);
+       zend_object_std_dtor((zend_object *) obj TSRMLS_CC);
+       efree(obj);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie___construct, 0, 0, 0)
+       ZEND_ARG_INFO(0, cookie_string)
+       ZEND_ARG_INFO(0, parser_flags)
+       ZEND_ARG_INFO(0, allowed_extras)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpCookie, __construct)
+{
+       php_http_cookie_object_t *obj;
+       zval *zcookie = NULL;
+       long flags = 0;
+       char **ae = NULL;
+       HashTable *allowed_extras = NULL;
+       zend_error_handling zeh;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!lH", &zcookie, &flags, &allowed_extras), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh TSRMLS_CC);
+       if (zcookie) {
+
+               if (allowed_extras && zend_hash_num_elements(allowed_extras)) {
+                       char **ae_ptr = safe_emalloc(zend_hash_num_elements(allowed_extras) + 1, sizeof(char *), 0);
+                       HashPosition pos;
+                       zval **val;
+
+                       ae = ae_ptr;
+                       FOREACH_HASH_VAL(pos, allowed_extras, val) {
+                               zval *cpy = php_http_ztyp(IS_STRING, *val);
+
+                               *ae_ptr++ = estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy));
+                               zval_ptr_dtor(&cpy);
+                       }
+                       *ae_ptr = NULL;
+               }
+
+               switch (Z_TYPE_P(zcookie)) {
+                       case IS_OBJECT:
+                               if (instanceof_function(Z_OBJCE_P(zcookie), php_http_cookie_class_entry TSRMLS_CC)) {
+                                       php_http_cookie_object_t *zco = zend_object_store_get_object(zcookie TSRMLS_CC);
+
+                                       if (zco->list) {
+                                               obj->list = php_http_cookie_list_copy(zco->list, NULL);
+                                       }
+                                       break;
+                               }
+                               /* no break */
+                       case IS_ARRAY:
+                               obj->list = php_http_cookie_list_from_struct(obj->list, zcookie TSRMLS_CC);
+                               break;
+                       default: {
+                               zval *cpy = php_http_ztyp(IS_STRING, zcookie);
+
+                               obj->list = php_http_cookie_list_parse(obj->list, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy), flags, ae TSRMLS_CC);
+                               zval_ptr_dtor(&cpy);
+                               break;
+                       }
+               }
+
+               if (ae) {
+                       char **ae_ptr;
+
+                       for (ae_ptr = ae; *ae_ptr; ++ae_ptr) {
+                               efree(*ae_ptr);
+                       }
+                       efree(ae);
+               }
+       }
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getCookies, 0, 0, 0)
+ZEND_END_ARG_INFO();;
+static PHP_METHOD(HttpCookie, getCookies)
+{
+       php_http_cookie_object_t *obj;
+
+       if (SUCCESS != zend_parse_parameters_none()) {
+               return;
+       }
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       array_init(return_value);
+       array_copy(&obj->list->cookies, Z_ARRVAL_P(return_value));
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setCookies, 0, 0, 0)
+       ZEND_ARG_INFO(0, cookies)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpCookie, setCookies)
+{
+       HashTable *cookies = NULL;
+       php_http_cookie_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|H", &cookies), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       zend_hash_clean(&obj->list->cookies);
+       if (cookies) {
+               array_copy_strings(cookies, &obj->list->cookies);
+       }
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_addCookies, 0, 0, 1)
+       ZEND_ARG_INFO(0, cookies)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpCookie, addCookies)
+{
+       HashTable *cookies = NULL;
+       php_http_cookie_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &cookies), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       array_join(cookies, &obj->list->cookies, 1, ARRAY_JOIN_STRONLY|ARRAY_JOIN_STRINGIFY);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getExtras, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpCookie, getExtras)
+{
+       php_http_cookie_object_t *obj;
+
+       if (SUCCESS != zend_parse_parameters_none()) {
+               return;
+       }
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       array_init(return_value);
+       array_copy(&obj->list->extras, Z_ARRVAL_P(return_value));
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setExtras, 0, 0, 0)
+       ZEND_ARG_INFO(0, extras)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpCookie, setExtras)
+{
+       HashTable *extras = NULL;
+       php_http_cookie_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|H", &extras), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       zend_hash_clean(&obj->list->extras);
+       if (extras) {
+               array_copy_strings(extras, &obj->list->extras);
+       }
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_addExtras, 0, 0, 1)
+       ZEND_ARG_INFO(0, extras)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpCookie, addExtras)
+{
+       HashTable *extras = NULL;
+       php_http_cookie_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &extras), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       array_join(extras, &obj->list->extras, 1, ARRAY_JOIN_STRONLY|ARRAY_JOIN_STRINGIFY);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getCookie, 0, 0, 1)
+       ZEND_ARG_INFO(0, name)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpCookie, getCookie)
+{
+       char *name_str;
+       int name_len;
+       zval *zvalue;
+       php_http_cookie_object_t *obj;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) {
+               return;
+       }
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       if (php_http_cookie_list_get_cookie(obj->list, name_str, name_len, &zvalue)) {
+               RETURN_ZVAL(zvalue, 1, 0);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setCookie, 0, 0, 1)
+       ZEND_ARG_INFO(0, cookie_name)
+       ZEND_ARG_INFO(0, cookie_value)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpCookie, setCookie)
+{
+       char *name_str, *value_str = NULL;
+       int name_len, value_len = 0;
+       php_http_cookie_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s!", &name_str, &name_len, &value_str, &value_len), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       if (!value_str) {
+               php_http_cookie_list_del_cookie(obj->list, name_str, name_len);
+       } else {
+               php_http_cookie_list_add_cookie(obj->list, name_str, name_len, value_str, value_len);
+       }
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_addCookie, 0, 0, 2)
+       ZEND_ARG_INFO(0, cookie_name)
+       ZEND_ARG_INFO(0, cookie_value)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpCookie, addCookie)
+{
+       char *name_str, *value_str;
+       int name_len, value_len;
+       php_http_cookie_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &name_str, &name_len, &value_str, &value_len), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       php_http_cookie_list_add_cookie(obj->list, name_str, name_len, value_str, value_len);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getExtra, 0, 0, 1)
+       ZEND_ARG_INFO(0, name)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpCookie, getExtra)
+{
+       char *name_str;
+       int name_len;
+       zval *zvalue;
+       php_http_cookie_object_t *obj;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) {
+               return;
+       }
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       if (php_http_cookie_list_get_extra(obj->list, name_str, name_len, &zvalue)) {
+               RETURN_ZVAL(zvalue, 1, 0);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setExtra, 0, 0, 1)
+       ZEND_ARG_INFO(0, extra_name)
+       ZEND_ARG_INFO(0, extra_value)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpCookie, setExtra)
+{
+       char *name_str, *value_str = NULL;
+       int name_len, value_len = 0;
+       php_http_cookie_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s!", &name_str, &name_len, &value_str, &value_len), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       if (!value_str) {
+               php_http_cookie_list_del_extra(obj->list, name_str, name_len);
+       } else {
+               php_http_cookie_list_add_extra(obj->list, name_str, name_len, value_str, value_len);
+       }
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_addExtra, 0, 0, 2)
+       ZEND_ARG_INFO(0, extra_name)
+       ZEND_ARG_INFO(0, extra_value)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpCookie, addExtra)
+{
+       char *name_str, *value_str;
+       int name_len, value_len;
+       php_http_cookie_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &name_str, &name_len, &value_str, &value_len), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       php_http_cookie_list_add_extra(obj->list, name_str, name_len, value_str, value_len);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getDomain, 0, 0, 0)
+ZEND_END_ARG_INFO();;
+static PHP_METHOD(HttpCookie, getDomain)
+{
+       php_http_cookie_object_t *obj;
+
+       if (SUCCESS != zend_parse_parameters_none()) {
+               return;
+       }
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       if (obj->list->domain) {
+               RETURN_STRING(obj->list->domain, 1);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setDomain, 0, 0, 0)
+       ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpCookie, setDomain)
+{
+       char *domain_str = NULL;
+       int domain_len = 0;
+       php_http_cookie_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &domain_str, &domain_len), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       PTR_SET(obj->list->domain, domain_str ? estrndup(domain_str, domain_len) : NULL);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getPath, 0, 0, 0)
+ZEND_END_ARG_INFO();;
+static PHP_METHOD(HttpCookie, getPath)
+{
+       php_http_cookie_object_t *obj;
+
+       if (SUCCESS != zend_parse_parameters_none()) {
+               return;
+       }
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       if (obj->list->path) {
+               RETURN_STRING(obj->list->path, 1);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setPath, 0, 0, 0)
+       ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpCookie, setPath)
+{
+       char *path_str = NULL;
+       int path_len = 0;
+       php_http_cookie_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &path_str, &path_len), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       PTR_SET(obj->list->path, path_str ? estrndup(path_str, path_len) : NULL);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getExpires, 0, 0, 0)
+ZEND_END_ARG_INFO();;
+static PHP_METHOD(HttpCookie, getExpires)
+{
+       php_http_cookie_object_t *obj;
+
+       if (SUCCESS != zend_parse_parameters_none()) {
+               return;
+       }
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       RETURN_LONG(obj->list->expires);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setExpires, 0, 0, 0)
+       ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpCookie, setExpires)
+{
+       long ts = -1;
+       php_http_cookie_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &ts), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       obj->list->expires = ts;
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getMaxAge, 0, 0, 0)
+ZEND_END_ARG_INFO();;
+static PHP_METHOD(HttpCookie, getMaxAge)
+{
+       php_http_cookie_object_t *obj;
+
+       if (SUCCESS != zend_parse_parameters_none()) {
+               return;
+       }
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       RETURN_LONG(obj->list->max_age);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setMaxAge, 0, 0, 0)
+       ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpCookie, setMaxAge)
+{
+       long ts = -1;
+       php_http_cookie_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &ts), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       obj->list->max_age = ts;
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getFlags, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpCookie, getFlags)
+{
+       php_http_cookie_object_t *obj;
+
+       if (SUCCESS != zend_parse_parameters_none()) {
+               return;
+       }
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       RETURN_LONG(obj->list->flags);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setFlags, 0, 0, 0)
+       ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpCookie, setFlags)
+{
+       long flags = 0;
+       php_http_cookie_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flags), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       obj->list->flags = flags;
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_toString, 0, 0, 0)
+ZEND_END_ARG_INFO();;
+static PHP_METHOD(HttpCookie, toString)
+{
+       php_http_cookie_object_t *obj;
+       char *str;
+       size_t len;
+
+       if (SUCCESS != zend_parse_parameters_none()) {
+               RETURN_EMPTY_STRING();
+       }
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       php_http_cookie_list_to_string(obj->list, &str, &len);
+
+       RETURN_STRINGL(str, len, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_toArray, 0, 0, 0)
+ZEND_END_ARG_INFO();;
+static PHP_METHOD(HttpCookie, toArray)
+{
+       php_http_cookie_object_t *obj;
+
+       if (SUCCESS != zend_parse_parameters_none()) {
+               return;
+       }
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_COOKIE_OBJECT_INIT(obj);
+
+       array_init(return_value);
+       php_http_cookie_list_to_struct(obj->list, return_value);
+}
+
+static zend_function_entry php_http_cookie_methods[] = {
+       PHP_ME(HttpCookie, __construct,   ai_HttpCookie___construct,  ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, getCookies,    ai_HttpCookie_getCookies,   ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, setCookies,    ai_HttpCookie_setCookies,   ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, addCookies,    ai_HttpCookie_addCookies,   ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, getCookie,     ai_HttpCookie_getCookie,    ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, setCookie,     ai_HttpCookie_setCookie,    ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, addCookie,     ai_HttpCookie_addCookie,    ZEND_ACC_PUBLIC)
+
+       PHP_ME(HttpCookie, getExtras,     ai_HttpCookie_getExtras,    ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, setExtras,     ai_HttpCookie_setExtras,    ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, addExtras,     ai_HttpCookie_addExtras,    ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, getExtra,      ai_HttpCookie_getExtra,     ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, setExtra,      ai_HttpCookie_setExtra,     ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, addExtra,      ai_HttpCookie_addExtra,     ZEND_ACC_PUBLIC)
+
+       PHP_ME(HttpCookie, getDomain,     ai_HttpCookie_getDomain,    ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, setDomain,     ai_HttpCookie_setDomain,    ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, getPath,       ai_HttpCookie_getPath,      ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, setPath,       ai_HttpCookie_setPath,      ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, getExpires,    ai_HttpCookie_getExpires,   ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, setExpires,    ai_HttpCookie_setExpires,   ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, getMaxAge,     ai_HttpCookie_getMaxAge,    ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, setMaxAge,     ai_HttpCookie_setMaxAge,    ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, getFlags,      ai_HttpCookie_getFlags,     ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, setFlags,      ai_HttpCookie_setFlags,     ZEND_ACC_PUBLIC)
+
+       PHP_ME(HttpCookie, toArray,       ai_HttpCookie_toArray,      ZEND_ACC_PUBLIC)
+       PHP_ME(HttpCookie, toString,      ai_HttpCookie_toString,     ZEND_ACC_PUBLIC)
+       ZEND_MALIAS(HttpCookie, __toString, toString, ai_HttpCookie_toString, ZEND_ACC_PUBLIC)
+
+       EMPTY_FUNCTION_ENTRY
+};
+
+zend_class_entry *php_http_cookie_class_entry;
+
+PHP_MINIT_FUNCTION(http_cookie)
+{
+       zend_class_entry ce = {0};
+
+       INIT_NS_CLASS_ENTRY(ce, "http", "Cookie", php_http_cookie_methods);
+       php_http_cookie_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
+       php_http_cookie_class_entry->create_object = php_http_cookie_object_new;
+       memcpy(&php_http_cookie_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+       php_http_cookie_object_handlers.clone_obj = php_http_cookie_object_clone;
+
+       zend_declare_class_constant_long(php_http_cookie_class_entry, ZEND_STRL("PARSE_RAW"), PHP_HTTP_COOKIE_PARSE_RAW TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_cookie_class_entry, ZEND_STRL("SECURE"), PHP_HTTP_COOKIE_SECURE TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_cookie_class_entry, ZEND_STRL("HTTPONLY"), PHP_HTTP_COOKIE_HTTPONLY TSRMLS_CC);
+
+       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_cookie.h b/src/php_http_cookie.h
new file mode 100644 (file)
index 0000000..7cf00fe
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_COOKIE_H
+#define PHP_HTTP_COOKIE_H
+
+#define PHP_HTTP_COOKIE_SECURE         0x10L
+#define PHP_HTTP_COOKIE_HTTPONLY       0x20L
+
+#define PHP_HTTP_COOKIE_PARSE_RAW      0x01L
+
+/*
+       generally a netscape cookie compliant struct, recognizing httpOnly attribute, too;
+       cookie params like those from rfc2109 and rfc2965 are just put into extras, if
+       one specifies them in allowed extras, else they're treated like cookies themself
+*/
+typedef struct php_http_cookie_list {
+       HashTable cookies;
+       HashTable extras;
+       long flags;
+       char *path;
+       char *domain;
+       time_t expires;
+       time_t max_age;
+
+#ifdef ZTS
+       void ***ts;
+#endif
+} php_http_cookie_list_t;
+
+PHP_HTTP_API php_http_cookie_list_t *php_http_cookie_list_init(php_http_cookie_list_t *list TSRMLS_DC);
+PHP_HTTP_API php_http_cookie_list_t *php_http_cookie_list_parse(php_http_cookie_list_t *list, const char *str, size_t len, long flags, char **allowed_extras TSRMLS_DC);
+PHP_HTTP_API php_http_cookie_list_t *php_http_cookie_list_copy(php_http_cookie_list_t *from, php_http_cookie_list_t *to);
+PHP_HTTP_API void php_http_cookie_list_dtor(php_http_cookie_list_t *list);
+PHP_HTTP_API void php_http_cookie_list_free(php_http_cookie_list_t **list);
+
+#define php_http_cookie_list_has_cookie(list, name, name_len) zend_symtable_exists(&(list)->cookies, (name), (name_len)+1)
+#define php_http_cookie_list_del_cookie(list, name, name_len) zend_symtable_del(&(list)->cookies, (name), (name_len)+1)
+PHP_HTTP_API void php_http_cookie_list_add_cookie(php_http_cookie_list_t *list, const char *name, size_t name_len, const char *value, size_t value_len);
+PHP_HTTP_API const char *php_http_cookie_list_get_cookie(php_http_cookie_list_t *list, const char *name, size_t name_len, zval **cookie);
+
+#define php_http_cookie_list_has_extra(list, name, name_len) zend_symtable_exists(&(list)->extras, (name), (name_len)+1)
+#define php_http_cookie_list_del_extra(list, name, name_len) zend_symtable_del(&(list)->extras, (name), (name_len)+1)
+PHP_HTTP_API void php_http_cookie_list_add_extra(php_http_cookie_list_t *list, const char *name, size_t name_len, const char *value, size_t value_len);
+PHP_HTTP_API const char *php_http_cookie_list_get_extra(php_http_cookie_list_t *list, const char *name, size_t name_len, zval **extra);
+
+PHP_HTTP_API void php_http_cookie_list_to_string(php_http_cookie_list_t *list, char **str, size_t *len);
+PHP_HTTP_API php_http_cookie_list_t *php_http_cookie_list_from_struct(php_http_cookie_list_t *list, zval *strct TSRMLS_DC);
+PHP_HTTP_API void php_http_cookie_list_to_struct(php_http_cookie_list_t *list, zval *strct);
+
+PHP_HTTP_API zend_class_entry *php_http_cookie_class_entry;
+
+typedef struct php_http_cookie_object {
+       zend_object zo;
+       zend_object_value zv;
+       php_http_cookie_list_t *list;
+} php_http_cookie_object_t;
+
+zend_object_value php_http_cookie_object_new(zend_class_entry *ce TSRMLS_DC);
+zend_object_value php_http_cookie_object_new_ex(zend_class_entry *ce, php_http_cookie_list_t *list, php_http_cookie_object_t **obj TSRMLS_DC);
+zend_object_value php_http_cookie_object_clone(zval *this_ptr TSRMLS_DC);
+void php_http_cookie_object_free(void *object TSRMLS_DC);
+
+PHP_MINIT_FUNCTION(http_cookie);
+
+#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_curl.c b/src/php_http_curl.c
new file mode 100644 (file)
index 0000000..a995094
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_CURL
+
+#if defined(ZTS) && defined(PHP_HTTP_HAVE_SSL)
+#      ifdef PHP_WIN32
+#              define PHP_HTTP_NEED_OPENSSL_TSL
+#              include <openssl/crypto.h>
+#      else /* !PHP_WIN32 */
+#              if defined(PHP_HTTP_HAVE_OPENSSL)
+#                      define PHP_HTTP_NEED_OPENSSL_TSL
+#                      include <openssl/crypto.h>
+#              elif defined(PHP_HTTP_HAVE_GNUTLS)
+#                      define PHP_HTTP_NEED_GNUTLS_TSL
+#                      include <gcrypt.h>
+#              endif /* PHP_HTTP_HAVE_OPENSSL || PHP_HTTP_HAVE_GNUTLS */
+#      endif /* PHP_WIN32 */
+#endif /* ZTS && PHP_HTTP_HAVE_SSL */
+
+
+#ifdef PHP_HTTP_NEED_OPENSSL_TSL
+static MUTEX_T *php_http_openssl_tsl = NULL;
+
+static void php_http_openssl_thread_lock(int mode, int n, const char * file, int line)
+{
+       if (mode & CRYPTO_LOCK) {
+               tsrm_mutex_lock(php_http_openssl_tsl[n]);
+       } else {
+               tsrm_mutex_unlock(php_http_openssl_tsl[n]);
+       }
+}
+
+static ulong php_http_openssl_thread_id(void)
+{
+       return (ulong) tsrm_thread_id();
+}
+#endif
+#ifdef PHP_HTTP_NEED_GNUTLS_TSL
+static int php_http_gnutls_mutex_create(void **m)
+{
+       if (*((MUTEX_T *) m) = tsrm_mutex_alloc()) {
+               return SUCCESS;
+       } else {
+               return FAILURE;
+       }
+}
+
+static int php_http_gnutls_mutex_destroy(void **m)
+{
+       tsrm_mutex_free(*((MUTEX_T *) m));
+       return SUCCESS;
+}
+
+static int php_http_gnutls_mutex_lock(void **m)
+{
+       return tsrm_mutex_lock(*((MUTEX_T *) m));
+}
+
+static int php_http_gnutls_mutex_unlock(void **m)
+{
+       return tsrm_mutex_unlock(*((MUTEX_T *) m));
+}
+
+static struct gcry_thread_cbs php_http_gnutls_tsl = {
+       GCRY_THREAD_OPTION_USER,
+       NULL,
+       php_http_gnutls_mutex_create,
+       php_http_gnutls_mutex_destroy,
+       php_http_gnutls_mutex_lock,
+       php_http_gnutls_mutex_unlock
+};
+#endif
+
+
+PHP_MINIT_FUNCTION(http_curl)
+{
+#ifdef PHP_HTTP_NEED_OPENSSL_TSL
+       /* mod_ssl, libpq or ext/curl might already have set thread lock callbacks */
+       if (!CRYPTO_get_id_callback()) {
+               int i, c = CRYPTO_num_locks();
+
+               php_http_openssl_tsl = malloc(c * sizeof(MUTEX_T));
+
+               for (i = 0; i < c; ++i) {
+                       php_http_openssl_tsl[i] = tsrm_mutex_alloc();
+               }
+
+               CRYPTO_set_id_callback(php_http_openssl_thread_id);
+               CRYPTO_set_locking_callback(php_http_openssl_thread_lock);
+       }
+#endif
+#ifdef PHP_HTTP_NEED_GNUTLS_TSL
+       gcry_control(GCRYCTL_SET_THREAD_CBS, &php_http_gnutls_tsl);
+#endif
+
+       if (CURLE_OK != curl_global_init(CURL_GLOBAL_ALL)) {
+               return FAILURE;
+       }
+
+       return SUCCESS;
+}
+
+PHP_MSHUTDOWN_FUNCTION(http_curl)
+{
+       curl_global_cleanup();
+#ifdef PHP_HTTP_NEED_OPENSSL_TSL
+       if (php_http_openssl_tsl) {
+               int i, c = CRYPTO_num_locks();
+
+               CRYPTO_set_id_callback(NULL);
+               CRYPTO_set_locking_callback(NULL);
+
+               for (i = 0; i < c; ++i) {
+                       tsrm_mutex_free(php_http_openssl_tsl[i]);
+               }
+
+               free(php_http_openssl_tsl);
+               php_http_openssl_tsl = NULL;
+       }
+#endif
+       return SUCCESS;
+}
+
+#endif /* PHP_HTTP_HAVE_CURL */
+
+/*
+ * 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_curl.h b/src/php_http_curl.h
new file mode 100644 (file)
index 0000000..ab8b63c
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_CURL_H
+#define PHP_HTTP_CURL_H
+
+#if PHP_HTTP_HAVE_CURL
+
+#include <curl/curl.h>
+#define PHP_HTTP_CURL_VERSION(x, y, z) (LIBCURL_VERSION_NUM >= (((x)<<16) + ((y)<<8) + (z)))
+
+PHP_MINIT_FUNCTION(http_curl);
+PHP_MSHUTDOWN_FUNCTION(http_curl);
+
+#endif /* PHP_HTTP_HAVE_CURL */
+
+#endif /* PHP_HTTP_CURL_H */
+
+/*
+ * 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.c b/src/php_http_encoding.c
new file mode 100644 (file)
index 0000000..286f2b5
--- /dev/null
@@ -0,0 +1,1227 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+#include <zlib.h>
+
+static inline int eol_match(char **line, int *eol_len)
+{
+       char *ptr = *line;
+       
+       while (' ' == *ptr) ++ptr;
+
+       if (ptr == php_http_locate_eol(*line, eol_len)) {
+               *line = ptr;
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
+const char *php_http_encoding_dechunk(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC)
+{
+       int eol_len = 0;
+       char *n_ptr = NULL;
+       const char *e_ptr = encoded;
+       
+       *decoded_len = 0;
+       *decoded = ecalloc(1, encoded_len + 1);
+
+       while ((encoded + encoded_len - e_ptr) > 0) {
+               ulong chunk_len = 0, rest;
+
+               chunk_len = strtoul(e_ptr, &n_ptr, 16);
+
+               /* we could not read in chunk size */
+               if (n_ptr == e_ptr) {
+                       /*
+                        * if this is the first turn and there doesn't seem to be a chunk
+                        * size at the begining of the body, do not fail on apparently
+                        * not encoded data and return a copy
+                        */
+                       if (e_ptr == encoded) {
+                               php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Data does not seem to be chunked encoded");
+                               memcpy(*decoded, encoded, encoded_len);
+                               *decoded_len = encoded_len;
+                               return encoded + encoded_len;
+                       } else {
+                               efree(*decoded);
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected chunk size at pos %tu of %zu but got trash", n_ptr - encoded, encoded_len);
+                               return NULL;
+                       }
+               }
+               
+               /* reached the end */
+               if (!chunk_len) {
+                       /* move over '0' chunked encoding terminator and any new lines */
+                       do {
+                               switch (*e_ptr) {
+                                       case '0':
+                                       case '\r':
+                                       case '\n':
+                                               ++e_ptr;
+                                               continue;
+                               }
+                       } while (0);
+                       break;
+               }
+
+               /* there should be CRLF after the chunk size, but we'll ignore SP+ too */
+               if (*n_ptr && !eol_match(&n_ptr, &eol_len)) {
+                       if (eol_len == 2) {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected CRLF at pos %tu of %zu but got 0x%02X 0x%02X", n_ptr - encoded, encoded_len, *n_ptr, *(n_ptr + 1));
+                       } else {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected LF at pos %tu of %zu but got 0x%02X", n_ptr - encoded, encoded_len, *n_ptr);
+                       }
+               }
+               n_ptr += eol_len;
+               
+               /* chunk size pretends more data than we actually got, so it's probably a truncated message */
+               if (chunk_len > (rest = encoded + encoded_len - n_ptr)) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Truncated message: chunk size %lu exceeds remaining data size %lu at pos %tu of %zu", chunk_len, rest, n_ptr - encoded, encoded_len);
+                       chunk_len = rest;
+               }
+
+               /* copy the chunk */
+               memcpy(*decoded + *decoded_len, n_ptr, chunk_len);
+               *decoded_len += chunk_len;
+               
+               if (chunk_len == rest) {
+                       e_ptr = n_ptr + chunk_len;
+                       break;
+               } else {
+                       /* advance to next chunk */
+                       e_ptr = n_ptr + chunk_len + eol_len;
+               }
+       }
+
+       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 TSRMLS_DC)
+{
+       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 TSRMLS_CC, 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 TSRMLS_DC)
+{
+       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 TSRMLS_CC, 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 TSRMLS_DC)
+{
+       int freeme;
+
+       if ((freeme = !s)) {
+               s = pemalloc(sizeof(*s), (flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT));
+       }
+       memset(s, 0, sizeof(*s));
+
+       s->flags = flags;
+       TSRMLS_SET_CTX(s->ts);
+
+       if ((s->ops = ops)) {
+               php_http_encoding_stream_t *ss = s->ops->init(s);
+
+               if (ss) {
+                       return ss;
+               }
+       } else {
+               return s;
+       }
+
+       if (freeme) {
+               pefree(s, (flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT));
+       }
+       return NULL;
+}
+
+php_http_encoding_stream_t *php_http_encoding_stream_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to)
+{
+       TSRMLS_FETCH_FROM_CTX(from->ts);
+
+       if (from->ops->copy) {
+               int freeme;
+               php_http_encoding_stream_t *ns;
+
+               if ((freeme = !to)) {
+                       to = pemalloc(sizeof(*to), (from->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT));
+               }
+               memset(to, 0, sizeof(*to));
+
+               to->flags = from->flags;
+               to->ops = from->ops;
+               TSRMLS_SET_CTX(to->ts);
+
+               if ((ns = to->ops->copy(from, to))) {
+                       return ns;
+               } else {
+                       return to;
+               }
+
+               if (freeme) {
+                       pefree(to, (to->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT));
+               }
+       }
+
+       return NULL;
+}
+
+ZEND_RESULT_CODE php_http_encoding_stream_reset(php_http_encoding_stream_t **s)
+{
+       php_http_encoding_stream_t *ss;
+       if ((*s)->ops->dtor) {
+               (*s)->ops->dtor(*s);
+       }
+       if ((ss = (*s)->ops->init(*s))) {
+               *s = ss;
+               return SUCCESS;
+       }
+       return FAILURE;
+}
+
+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;
+       }
+       return s->ops->update(s, in_str, in_len, out_str, out_len);
+}
+
+ZEND_RESULT_CODE php_http_encoding_stream_flush(php_http_encoding_stream_t *s, char **out_str, size_t *out_len)
+{
+       if (!s->ops->flush) {
+               *out_str = NULL;
+               *out_len = 0;
+               return SUCCESS;
+       }
+       return s->ops->flush(s, out_str, out_len);
+}
+
+zend_bool php_http_encoding_stream_done(php_http_encoding_stream_t *s)
+{
+       if (!s->ops->done) {
+               return 0;
+       }
+       return s->ops->done(s);
+}
+
+ZEND_RESULT_CODE php_http_encoding_stream_finish(php_http_encoding_stream_t *s, char **out_str, size_t *out_len)
+{
+       if (!s->ops->finish) {
+               *out_str = NULL;
+               *out_len = 0;
+               return SUCCESS;
+       }
+       return s->ops->finish(s, out_str, out_len);
+}
+
+void php_http_encoding_stream_dtor(php_http_encoding_stream_t *s)
+{
+       if (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) {
+                       (*s)->ops->dtor(*s);
+               }
+               pefree(*s, ((*s)->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT));
+               *s = NULL;
+       }
+}
+
+struct dechunk_ctx {
+       php_http_buffer_t buffer;
+       ulong hexlen;
+       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);
+       TSRMLS_FETCH_FROM_CTX(s->ts);
+       
+       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 TSRMLS_CC, 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);
+       TSRMLS_FETCH_FROM_CTX(s->ts);
+       
+       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 TSRMLS_CC, 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));
+
+       if (!php_http_buffer_init_ex(&ctx->buffer, PHP_HTTP_BUFFER_DEFAULT_SIZE, (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT) ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0)) {
+               return NULL;
+       }
+
+       ctx->hexlen = 0;
+       ctx->zeroed = 0;
+       s->ctx = ctx;
+
+       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);
+       TSRMLS_FETCH_FROM_CTX(from->ts);
+
+       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 TSRMLS_CC, 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);
+       TSRMLS_FETCH_FROM_CTX(from->ts);
+
+       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 TSRMLS_CC, 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;
+       struct dechunk_ctx *from_ctx = from->ctx, *to_ctx = pemalloc(sizeof(*to_ctx), p);
+       TSRMLS_FETCH_FROM_CTX(from->ts);
+
+       if (php_http_buffer_init_ex(&to_ctx->buffer, PHP_HTTP_BUFFER_DEFAULT_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0)) {
+               to_ctx->hexlen = from_ctx->hexlen;
+               to_ctx->zeroed = from_ctx->zeroed;
+               php_http_buffer_append(&to_ctx->buffer, from_ctx->buffer.data, from_ctx->buffer.used);
+               to->ctx = to_ctx;
+               return to;
+       }
+       pefree(to_ctx, p);
+       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to copy inflate encoding stream: out of memory");
+       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;
+       TSRMLS_FETCH_FROM_CTX(s->ts);
+       
+       /* 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 TSRMLS_CC, 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;
+       TSRMLS_FETCH_FROM_CTX(s->ts);
+       
+       /* 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 TSRMLS_CC, 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;
+       struct dechunk_ctx *ctx = s->ctx;
+       TSRMLS_FETCH_FROM_CTX(s->ts);
+
+       if (ctx->zeroed) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Dechunk encoding stream has already reached the end of chunked input");
+               return FAILURE;
+       }
+       if ((PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(&ctx->buffer, data, data_len)) || !php_http_buffer_fix(&ctx->buffer)) {
+               /* OOM */
+               return FAILURE;
+       }
+
+       *decoded = NULL;
+       *decoded_len = 0;
+
+       php_http_buffer_init(&tmp);
+
+       /* we have data in our buffer */
+       while (ctx->buffer.used) {
+
+               /* we already know the size of the chunk and are waiting for data */
+               if (ctx->hexlen) {
+
+                       /* not enough data buffered */
+                       if (ctx->buffer.used < ctx->hexlen) {
+
+                               /* flush anyway? */
+                               if (s->flags & PHP_HTTP_ENCODING_STREAM_FLUSH_FULL) {
+                                       /* flush all data (should only be chunk data) */
+                                       php_http_buffer_append(&tmp, ctx->buffer.data, ctx->buffer.used);
+                                       /* waiting for less data now */
+                                       ctx->hexlen -= ctx->buffer.used;
+                                       /* no more buffered data */
+                                       php_http_buffer_reset(&ctx->buffer);
+                                       /* break */
+                               }
+
+                               /* we have too less data and don't need to flush */
+                               else {
+                                       break;
+                               }
+                       }
+
+                       /* we seem to have all data of the chunk */
+                       else {
+                               php_http_buffer_append(&tmp, ctx->buffer.data, ctx->hexlen);
+                               /* remove outgoing data from the buffer */
+                               php_http_buffer_cut(&ctx->buffer, 0, ctx->hexlen);
+                               /* reset hexlen */
+                               ctx->hexlen = 0;
+                               /* continue */
+                       }
+               }
+
+               /* we don't know the length of the chunk yet */
+               else {
+                       size_t off = 0;
+
+                       /* ignore preceeding CRLFs (too loose?) */
+                       while (off < ctx->buffer.used && (
+                                       ctx->buffer.data[off] == '\n' ||
+                                       ctx->buffer.data[off] == '\r')) {
+                               ++off;
+                       }
+                       if (off) {
+                               php_http_buffer_cut(&ctx->buffer, 0, off);
+                       }
+
+                       /* still data there? */
+                       if (ctx->buffer.used) {
+                               int eollen;
+                               const char *eolstr;
+
+                               /* we need eol, so we can be sure we have all hex digits */
+                               php_http_buffer_fix(&ctx->buffer);
+                               if ((eolstr = php_http_locate_bin_eol(ctx->buffer.data, ctx->buffer.used, &eollen))) {
+                                       char *stop = NULL;
+
+                                       /* read in chunk size */
+                                       ctx->hexlen = strtoul(ctx->buffer.data, &stop, 16);
+
+                                       /*      if strtoul() stops at the beginning of the buffered data
+                                               there's something oddly wrong, i.e. bad input */
+                                       if (stop == ctx->buffer.data) {
+                                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse chunk len from '%.*s'", (int) MIN(16, ctx->buffer.used), ctx->buffer.data);
+                                               php_http_buffer_dtor(&tmp);
+                                               return FAILURE;
+                                       }
+
+                                       /* cut out <chunk size hex><chunk extension><eol> */
+                                       php_http_buffer_cut(&ctx->buffer, 0, eolstr + eollen - ctx->buffer.data);
+                                       /* buffer->hexlen is 0 now or contains the size of the next chunk */
+                                       if (!ctx->hexlen) {
+                                               size_t off = 0;
+
+                                               /* ignore following CRLFs (too loose?) */
+                                               while (off < ctx->buffer.used && (
+                                                               ctx->buffer.data[off] == '\n' ||
+                                                               ctx->buffer.data[off] == '\r')) {
+                                                       ++off;
+                                               }
+                                               if (off) {
+                                                       php_http_buffer_cut(&ctx->buffer, 0, off);
+                                               }
+
+                                               ctx->zeroed = 1;
+                                               break;
+                                       }
+                                       /* continue */
+                               } else {
+                                       /* we have not enough data buffered to read in chunk size */
+                                       break;
+                               }
+                       }
+                       /* break */
+               }
+       }
+
+       php_http_buffer_fix(&tmp);
+       *decoded = tmp.data;
+       *decoded_len = tmp.used;
+
+       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;
+       TSRMLS_FETCH_FROM_CTX(s->ts);
+       
+       *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 TSRMLS_CC, 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;
+
+       if (ctx->hexlen) {
+               /* flush all data (should only be chunk data) */
+               php_http_buffer_fix(&ctx->buffer);
+               php_http_buffer_data(&ctx->buffer, decoded, decoded_len);
+               /* waiting for less data now */
+               ctx->hexlen -= ctx->buffer.used;
+               /* no more buffered data */
+               php_http_buffer_reset(&ctx->buffer);
+       } else {
+               *decoded = NULL;
+               *decoded_len = 0;
+       }
+
+       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;
+       TSRMLS_FETCH_FROM_CTX(s->ts);
+       
+       *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 TSRMLS_CC, 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;
+       TSRMLS_FETCH_FROM_CTX(s->ts);
+       
+       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 TSRMLS_CC, 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) {
+               struct dechunk_ctx *ctx = s->ctx;
+
+               php_http_buffer_dtor(&ctx->buffer);
+               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 php_http_encoding_stream_ops_t php_http_encoding_dechunk_ops = {
+       dechunk_init,
+       dechunk_copy,
+       dechunk_update,
+       dechunk_flush,
+       dechunk_done,
+       NULL,
+       dechunk_dtor
+};
+
+php_http_encoding_stream_ops_t *php_http_encoding_stream_get_dechunk_ops(void)
+{
+       return &php_http_encoding_dechunk_ops;
+}
+
+static zend_object_handlers php_http_encoding_stream_object_handlers;
+
+zend_object_value php_http_encoding_stream_object_new(zend_class_entry *ce TSRMLS_DC)
+{
+       return php_http_encoding_stream_object_new_ex(ce, NULL, NULL TSRMLS_CC);
+}
+
+zend_object_value php_http_encoding_stream_object_new_ex(zend_class_entry *ce, php_http_encoding_stream_t *s, php_http_encoding_stream_object_t **ptr TSRMLS_DC)
+{
+       php_http_encoding_stream_object_t *o;
+
+       o = ecalloc(1, sizeof(*o));
+       zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
+       object_properties_init((zend_object *) o, ce);
+
+       if (ptr) {
+               *ptr = o;
+       }
+
+       if (s) {
+               o->stream = s;
+       }
+
+       o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_encoding_stream_object_free, NULL TSRMLS_CC);
+       o->zv.handlers = &php_http_encoding_stream_object_handlers;
+
+       return o->zv;
+}
+
+zend_object_value php_http_encoding_stream_object_clone(zval *this_ptr TSRMLS_DC)
+{
+       zend_object_value new_ov;
+       php_http_encoding_stream_object_t *new_obj = NULL, *old_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       new_ov = php_http_encoding_stream_object_new_ex(old_obj->zo.ce, php_http_encoding_stream_copy(old_obj->stream, NULL), &new_obj TSRMLS_CC);
+       zend_objects_clone_members(&new_obj->zo, new_ov, &old_obj->zo, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC);
+
+       return new_ov;
+}
+
+void php_http_encoding_stream_object_free(void *object TSRMLS_DC)
+{
+       php_http_encoding_stream_object_t *o = (php_http_encoding_stream_object_t *) object;
+
+       if (o->stream) {
+               php_http_encoding_stream_free(&o->stream);
+       }
+       zend_object_std_dtor((zend_object *) o TSRMLS_CC);
+       efree(o);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEncodingStream___construct, 0, 0, 0)
+       ZEND_ARG_INFO(0, flags)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEncodingStream, __construct)
+{
+       long flags = 0;
+       php_http_encoding_stream_object_t *obj;
+       php_http_encoding_stream_ops_t *ops;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flags), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       if (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 TSRMLS_CC)) {
+               ops = &php_http_encoding_deflate_ops;
+       } else if (instanceof_function(obj->zo.ce, php_http_inflate_stream_class_entry TSRMLS_CC)) {
+               ops = &php_http_encoding_inflate_ops;
+       } else if (instanceof_function(obj->zo.ce, php_http_dechunk_stream_class_entry TSRMLS_CC)) {
+               ops = &php_http_encoding_dechunk_ops;
+       } else {
+               php_http_throw(runtime, "Unknown http\\Encoding\\Stream class '%s'", obj->zo.ce->name);
+               return;
+       }
+
+       php_http_expect(obj->stream = php_http_encoding_stream_init(obj->stream, ops, flags TSRMLS_CC), runtime, return);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEncodingStream_update, 0, 0, 1)
+       ZEND_ARG_INFO(0, data)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEncodingStream, update)
+{
+       int data_len;
+       char *data_str;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data_str, &data_len)) {
+               php_http_encoding_stream_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               if (obj->stream) {
+                       size_t encoded_len;
+                       char *encoded_str;
+
+                       if (SUCCESS == php_http_encoding_stream_update(obj->stream, data_str, data_len, &encoded_str, &encoded_len)) {
+                               RETURN_STRINGL(encoded_str, encoded_len, 0);
+                       }
+               }
+       }
+}
+
+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()) {
+               php_http_encoding_stream_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               if (obj->stream) {
+                       char *encoded_str;
+                       size_t encoded_len;
+
+                       if (SUCCESS == php_http_encoding_stream_flush(obj->stream, &encoded_str, &encoded_len)) {
+                               if (encoded_str) {
+                                       RETURN_STRINGL(encoded_str, encoded_len, 0);
+                               } else {
+                                       RETURN_EMPTY_STRING();
+                               }
+                       }
+               }
+       }
+}
+
+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()) {
+               php_http_encoding_stream_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               if (obj->stream) {
+                       RETURN_BOOL(php_http_encoding_stream_done(obj->stream));
+               }
+       }
+}
+
+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()) {
+               php_http_encoding_stream_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               if (obj->stream) {
+                       char *encoded_str;
+                       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 (encoded_str) {
+                                               RETURN_STRINGL(encoded_str, encoded_len, 0);
+                                       } else {
+                                               RETURN_EMPTY_STRING();
+                                       }
+                               } else {
+                                       PTR_FREE(encoded_str);
+                               }
+                       }
+               }
+       }
+}
+
+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, 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)
+       PHP_ME(HttpEncodingStream, finish,       ai_HttpEncodingStream_finish,       ZEND_ACC_PUBLIC)
+       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;
+       int len;
+       long flags = 0;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &len, &flags)) {
+               char *enc_str;
+               size_t enc_len;
+
+               if (SUCCESS == php_http_encoding_deflate(flags, str, len, &enc_str, &enc_len TSRMLS_CC)) {
+                       RETURN_STRINGL(enc_str, enc_len, 0);
+               }
+       }
+       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;
+       int len;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) {
+               char *enc_str;
+               size_t enc_len;
+
+               if (SUCCESS == php_http_encoding_inflate(str, len, &enc_str, &enc_len TSRMLS_CC)) {
+                       RETURN_STRINGL(enc_str, enc_len, 0);
+               }
+       }
+       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)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpDechunkStream, decode)
+{
+       char *str;
+       int len;
+       zval *zlen = NULL;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z!", &str, &len, &zlen)) {
+               const char *end_ptr;
+               char *enc_str;
+               size_t enc_len;
+
+               if ((end_ptr = php_http_encoding_dechunk(str, len, &enc_str, &enc_len TSRMLS_CC))) {
+                       if (zlen) {
+                               zval_dtor(zlen);
+                               ZVAL_LONG(zlen, str + len - end_ptr);
+                       }
+                       RETURN_STRINGL(enc_str, enc_len, 0);
+               }
+       }
+       RETURN_FALSE;
+}
+
+static zend_function_entry php_http_dechunk_stream_methods[] = {
+       PHP_ME(HttpDechunkStream, decode, ai_HttpDechunkStream_decode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       EMPTY_FUNCTION_ENTRY
+};
+
+zend_class_entry *php_http_encoding_stream_class_entry;
+zend_class_entry *php_http_deflate_stream_class_entry;
+zend_class_entry *php_http_inflate_stream_class_entry;
+zend_class_entry *php_http_dechunk_stream_class_entry;
+
+PHP_MINIT_FUNCTION(http_encoding)
+{
+       zend_class_entry ce = {0};
+
+       INIT_NS_CLASS_ENTRY(ce, "http\\Encoding", "Stream", php_http_encoding_stream_methods);
+       php_http_encoding_stream_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
+       php_http_encoding_stream_class_entry->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;
+       php_http_encoding_stream_class_entry->create_object = php_http_encoding_stream_object_new;
+       memcpy(&php_http_encoding_stream_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+       php_http_encoding_stream_object_handlers.clone_obj = php_http_encoding_stream_object_clone;
+
+       zend_declare_class_constant_long(php_http_encoding_stream_class_entry, ZEND_STRL("FLUSH_NONE"), PHP_HTTP_ENCODING_STREAM_FLUSH_NONE TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_encoding_stream_class_entry, ZEND_STRL("FLUSH_SYNC"), PHP_HTTP_ENCODING_STREAM_FLUSH_SYNC TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_encoding_stream_class_entry, ZEND_STRL("FLUSH_FULL"), PHP_HTTP_ENCODING_STREAM_FLUSH_FULL TSRMLS_CC);
+
+       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, NULL TSRMLS_CC);
+
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_GZIP"), PHP_HTTP_DEFLATE_TYPE_GZIP TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_ZLIB"), PHP_HTTP_DEFLATE_TYPE_ZLIB TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_RAW"), PHP_HTTP_DEFLATE_TYPE_RAW TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_DEF"), PHP_HTTP_DEFLATE_LEVEL_DEF TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_MIN"), PHP_HTTP_DEFLATE_LEVEL_MIN TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_MAX"), PHP_HTTP_DEFLATE_LEVEL_MAX TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_DEF"), PHP_HTTP_DEFLATE_STRATEGY_DEF TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_FILT"), PHP_HTTP_DEFLATE_STRATEGY_FILT TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_HUFF"), PHP_HTTP_DEFLATE_STRATEGY_HUFF TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_RLE"), PHP_HTTP_DEFLATE_STRATEGY_RLE TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_FIXED"), PHP_HTTP_DEFLATE_STRATEGY_FIXED TSRMLS_CC);
+
+       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, NULL TSRMLS_CC);
+
+       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, NULL TSRMLS_CC);
+
+       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.h b/src/php_http_encoding.h
new file mode 100644 (file)
index 0000000..cdf7a1a
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_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_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
+
+typedef struct php_http_encoding_stream php_http_encoding_stream_t;
+
+typedef php_http_encoding_stream_t *(*php_http_encoding_stream_init_func_t)(php_http_encoding_stream_t *s);
+typedef php_http_encoding_stream_t *(*php_http_encoding_stream_copy_func_t)(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to);
+typedef ZEND_RESULT_CODE (*php_http_encoding_stream_update_func_t)(php_http_encoding_stream_t *s, const char *in_str, size_t in_len, char **out_str, size_t *out_len);
+typedef ZEND_RESULT_CODE (*php_http_encoding_stream_flush_func_t)(php_http_encoding_stream_t *s, char **out_str, size_t *out_len);
+typedef zend_bool (*php_http_encoding_stream_done_func_t)(php_http_encoding_stream_t *s);
+typedef ZEND_RESULT_CODE (*php_http_encoding_stream_finish_func_t)(php_http_encoding_stream_t *s, char **out_str, size_t *out_len);
+typedef void (*php_http_encoding_stream_dtor_func_t)(php_http_encoding_stream_t *s);
+
+typedef struct php_http_encoding_stream_ops {
+       php_http_encoding_stream_init_func_t init;
+       php_http_encoding_stream_copy_func_t copy;
+       php_http_encoding_stream_update_func_t update;
+       php_http_encoding_stream_flush_func_t flush;
+       php_http_encoding_stream_done_func_t done;
+       php_http_encoding_stream_finish_func_t finish;
+       php_http_encoding_stream_dtor_func_t dtor;
+} php_http_encoding_stream_ops_t;
+
+struct php_http_encoding_stream {
+       unsigned flags;
+       void *ctx;
+       php_http_encoding_stream_ops_t *ops;
+#ifdef ZTS
+       void ***ts;
+#endif
+};
+
+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 TSRMLS_DC);
+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);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_stream_reset(php_http_encoding_stream_t **s);
+PHP_HTTP_API 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);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_stream_flush(php_http_encoding_stream_t *s, char **out_str, size_t *len);
+PHP_HTTP_API zend_bool php_http_encoding_stream_done(php_http_encoding_stream_t *s);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_stream_finish(php_http_encoding_stream_t *s, char **out_str, size_t *len);
+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 const char *php_http_encoding_dechunk(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC);
+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 TSRMLS_DC);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len TSRMLS_DC);
+
+typedef struct php_http_encoding_stream_object {
+       zend_object zo;
+       zend_object_value zv;
+       php_http_encoding_stream_t *stream;
+} php_http_encoding_stream_object_t;
+
+PHP_HTTP_API zend_class_entry *php_http_encoding_stream_class_entry;
+
+zend_object_value php_http_encoding_stream_object_new(zend_class_entry *ce TSRMLS_DC);
+zend_object_value php_http_encoding_stream_object_new_ex(zend_class_entry *ce, php_http_encoding_stream_t *s, php_http_encoding_stream_object_t **ptr TSRMLS_DC);
+zend_object_value php_http_encoding_stream_object_clone(zval *object TSRMLS_DC);
+void php_http_encoding_stream_object_free(void *object TSRMLS_DC);
+
+PHP_HTTP_API zend_class_entry *php_http_deflate_stream_class_entry;
+PHP_HTTP_API zend_class_entry *php_http_inflate_stream_class_entry;
+PHP_HTTP_API zend_class_entry *php_http_dechunk_stream_class_entry;
+
+#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_env.c b/src/php_http_env.c
new file mode 100644 (file)
index 0000000..c60d4c0
--- /dev/null
@@ -0,0 +1,819 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+#include "php_variables.h"
+
+PHP_RSHUTDOWN_FUNCTION(http_env)
+{
+       if (PHP_HTTP_G->env.request.headers) {
+               zend_hash_destroy(PHP_HTTP_G->env.request.headers);
+               FREE_HASHTABLE(PHP_HTTP_G->env.request.headers);
+               PHP_HTTP_G->env.request.headers = NULL;
+       }
+       if (PHP_HTTP_G->env.request.body) {
+               php_http_message_body_free(&PHP_HTTP_G->env.request.body);
+       }
+
+       if (PHP_HTTP_G->env.server_var) {
+               zval_ptr_dtor(&PHP_HTTP_G->env.server_var);
+               PHP_HTTP_G->env.server_var = NULL;
+       }
+
+       return SUCCESS;
+}
+
+void php_http_env_get_request_headers(HashTable *headers TSRMLS_DC)
+{
+       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+       zval **hsv, **header;
+       HashPosition pos;
+
+       if (!PHP_HTTP_G->env.request.headers) {
+               ALLOC_HASHTABLE(PHP_HTTP_G->env.request.headers);
+               zend_hash_init(PHP_HTTP_G->env.request.headers, 0, NULL, ZVAL_PTR_DTOR, 0);
+
+               zend_is_auto_global("_SERVER", lenof("_SERVER") TSRMLS_CC);
+
+               if (SUCCESS == zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void *) &hsv) && Z_TYPE_PP(hsv) == IS_ARRAY) {
+                       FOREACH_KEY(pos, *hsv, key) {
+                               if (key.type == HASH_KEY_IS_STRING && key.len > 6 && *key.str == 'H' && !strncmp(key.str, "HTTP_", 5)) {
+                                       key.len -= 5;
+                                       key.str = php_http_pretty_key(estrndup(key.str + 5, key.len - 1), key.len - 1, 1, 1);
+
+                                       zend_hash_get_current_data_ex(Z_ARRVAL_PP(hsv), (void *) &header, &pos);
+                                       Z_ADDREF_P(*header);
+                                       zend_symtable_update(PHP_HTTP_G->env.request.headers, key.str, key.len, (void *) header, sizeof(zval *), NULL);
+
+                                       efree(key.str);
+                               } else if (key.type == HASH_KEY_IS_STRING && key.len > 9 && *key.str == 'C' && !strncmp(key.str, "CONTENT_", 8)) {
+                                       key.str = php_http_pretty_key(estrndup(key.str, key.len - 1), key.len - 1, 1, 1);
+
+                                       zend_hash_get_current_data_ex(Z_ARRVAL_PP(hsv), (void *) &header, &pos);
+                                       Z_ADDREF_P(*header);
+                                       zend_symtable_update(PHP_HTTP_G->env.request.headers, key.str, key.len, (void *) header, sizeof(zval *), NULL);
+
+                                       efree(key.str);
+                               }
+                       }
+               }
+       }
+
+       if (headers) {
+               zend_hash_copy(headers, PHP_HTTP_G->env.request.headers, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
+       }
+}
+
+char *php_http_env_get_request_header(const char *name_str, size_t name_len, size_t *len, php_http_message_t *request TSRMLS_DC)
+{
+       HashTable *request_headers;
+       zval **zvalue = NULL;
+       char *val = NULL, *key = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1);
+
+       if (request) {
+               request_headers = &request->hdrs;
+       } else {
+               php_http_env_get_request_headers(NULL TSRMLS_CC);
+               request_headers = PHP_HTTP_G->env.request.headers;
+       }
+
+       if (SUCCESS == zend_symtable_find(request_headers, key, name_len + 1, (void *) &zvalue)) {
+               zval *zcopy = php_http_ztyp(IS_STRING, *zvalue);
+
+               val = estrndup(Z_STRVAL_P(zcopy), Z_STRLEN_P(zcopy));
+               if (len) {
+                       *len = Z_STRLEN_P(zcopy);
+               }
+               zval_ptr_dtor(&zcopy);
+       }
+
+       efree(key);
+
+       return val;
+}
+
+int php_http_env_got_request_header(const char *name_str, size_t name_len, php_http_message_t *request TSRMLS_DC)
+{
+       HashTable *request_headers;
+       char *key = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1);
+       int got;
+
+       if (request) {
+               request_headers = &request->hdrs;
+       } else {
+               php_http_env_get_request_headers(NULL TSRMLS_CC);
+               request_headers = PHP_HTTP_G->env.request.headers;
+       }
+       got = zend_symtable_exists(request_headers, key, name_len + 1);
+       efree(key);
+
+       return got;
+}
+
+zval *php_http_env_get_superglobal(const char *key, size_t key_len TSRMLS_DC)
+{
+       zval **hsv;
+
+       zend_is_auto_global(key, key_len TSRMLS_CC);
+
+       if ((SUCCESS != zend_hash_find(&EG(symbol_table), key, key_len + 1, (void *) &hsv)) || (Z_TYPE_PP(hsv) != IS_ARRAY)) {
+               return NULL;
+       }
+
+       return *hsv;
+}
+
+zval *php_http_env_get_server_var(const char *key, size_t key_len, zend_bool check TSRMLS_DC)
+{
+       zval *hsv, **var;
+       char *env;
+
+       /* if available, this is a lot faster than accessing $_SERVER */
+       if (sapi_module.getenv) {
+               if ((!(env = sapi_module.getenv((char *) key, key_len TSRMLS_CC))) || (check && !*env)) {
+                       return NULL;
+               }
+               if (PHP_HTTP_G->env.server_var) {
+                       zval_ptr_dtor(&PHP_HTTP_G->env.server_var);
+               }
+               MAKE_STD_ZVAL(PHP_HTTP_G->env.server_var);
+               ZVAL_STRING(PHP_HTTP_G->env.server_var, env, 1);
+               return PHP_HTTP_G->env.server_var;
+       }
+
+       if (!(hsv = php_http_env_get_superglobal(ZEND_STRL("_SERVER") TSRMLS_CC))) {
+               return NULL;
+       }
+       if ((SUCCESS != zend_symtable_find(Z_ARRVAL_P(hsv), key, key_len + 1, (void *) &var))) {
+               return NULL;
+       }
+       if (check && !((Z_TYPE_PP(var) == IS_STRING) && Z_STRVAL_PP(var) && Z_STRLEN_PP(var))) {
+               return NULL;
+       }
+       return *var;
+}
+
+php_http_message_body_t *php_http_env_get_request_body(TSRMLS_D)
+{
+       if (!PHP_HTTP_G->env.request.body) {
+               php_stream *s = php_stream_temp_new();
+#if PHP_VERSION_ID >= 50600
+               php_stream *input = php_stream_open_wrapper("php://input", "r", 0, NULL);
+
+               /* php://input does not support stat */
+               php_stream_copy_to_stream_ex(input, s, -1, NULL);
+               php_stream_close(input);
+#else
+               if (SG(request_info).post_data || SG(request_info).raw_post_data) {
+                       /* php://input does not support seek() in PHP <= 5.5 */
+                       if (SG(request_info).raw_post_data) {
+                               php_stream_write(s, SG(request_info).raw_post_data, SG(request_info).raw_post_data_length);
+                       } else {
+                               php_stream_write(s, SG(request_info).post_data, SG(request_info).post_data_length);
+                       }
+               } else if (sapi_module.read_post && !SG(read_post_bytes)) {
+                       char *buf = emalloc(4096);
+                       int len;
+
+                       while (0 < (len = sapi_module.read_post(buf, 4096 TSRMLS_CC))) {
+                               SG(read_post_bytes) += len;
+                               php_stream_write(s, buf, len);
+
+                               if (len < 4096) {
+                                       break;
+                               }
+                       }
+                       efree(buf);
+               }
+#endif
+               php_stream_rewind(s);
+               PHP_HTTP_G->env.request.body = php_http_message_body_init(NULL, s TSRMLS_CC);
+       }
+
+       return PHP_HTTP_G->env.request.body;
+}
+
+const char *php_http_env_get_request_method(php_http_message_t *request TSRMLS_DC)
+{
+       const char *m;
+
+       if (PHP_HTTP_MESSAGE_TYPE(REQUEST, request)) {
+               m = request->http.info.request.method;
+       } else {
+               m = SG(request_info).request_method;
+       }
+
+       return m ? m : "GET";
+}
+
+php_http_range_status_t php_http_env_get_request_ranges(HashTable *ranges, size_t length, php_http_message_t *request TSRMLS_DC)
+{
+       zval *zentry;
+       char *range, *rp, c;
+       long begin = -1, end = -1, *ptr;
+
+       if (!(range = php_http_env_get_request_header(ZEND_STRL("Range"), NULL, request TSRMLS_CC))) {
+               return PHP_HTTP_RANGE_NO;
+       }
+       if (strncmp(range, "bytes=", lenof("bytes="))) {
+               PTR_FREE(range);
+               return PHP_HTTP_RANGE_NO;
+       }
+
+       rp  = range + lenof("bytes=");
+       ptr = &begin;
+
+       do {
+               switch (c = *(rp++)) {
+                       case '0':
+                               /* allow 000... - shall we? */
+                               if (*ptr != -10) {
+                                       *ptr *= 10;
+                               }
+                               break;
+
+                       case '1': case '2': case '3':
+                       case '4': case '5': case '6':
+                       case '7': case '8': case '9':
+                               /*
+                                * If the value of the pointer is already set (non-negative)
+                                * then multiply its value by ten and add the current value,
+                                * else initialise the pointers value with the current value
+                                * --
+                                * This let us recognize empty fields when validating the
+                                * ranges, i.e. a "-10" for begin and "12345" for the end
+                                * was the following range request: "Range: bytes=0-12345";
+                                * While a "-1" for begin and "12345" for the end would
+                                * have been: "Range: bytes=-12345".
+                                */
+                               if (*ptr > 0) {
+                                       *ptr *= 10;
+                                       *ptr += c - '0';
+                               } else {
+                                       *ptr = c - '0';
+                               }
+                               break;
+
+                       case '-':
+                               ptr = &end;
+                               break;
+
+                       case ' ':
+                               break;
+
+                       case 0:
+                       case ',':
+
+                               if (length) {
+                                       /* validate ranges */
+                                       switch (begin) {
+                                               /* "0-12345" */
+                                               case -10:
+                                                       switch (end) {
+                                                               /* "0-" */
+                                                               case -1:
+                                                                       PTR_FREE(range);
+                                                                       return PHP_HTTP_RANGE_NO;
+
+                                                               /* "0-0" */
+                                                               case -10:
+                                                                       end = 0;
+                                                                       break;
+
+                                                               default:
+                                                                       if (length <= (size_t) end) {
+                                                                               end = length - 1;
+                                                                       }
+                                                                       break;
+                                                       }
+                                                       begin = 0;
+                                                       break;
+
+                                               /* "-12345" */
+                                               case -1:
+                                                       /* "-", "-0" */
+                                                       if (end == -1 || end == -10) {
+                                                               PTR_FREE(range);
+                                                               return PHP_HTTP_RANGE_ERR;
+                                                       }
+                                                       begin = length - end;
+                                                       end = length - 1;
+                                                       break;
+
+                                               /* "12345-(NNN)" */
+                                               default:
+                                                       if (length <= (size_t) begin) {
+                                                               PTR_FREE(range);
+                                                               return PHP_HTTP_RANGE_ERR;
+                                                       }
+                                                       switch (end) {
+                                                               /* "12345-0" */
+                                                               case -10:
+                                                                       PTR_FREE(range);
+                                                                       return PHP_HTTP_RANGE_ERR;
+
+                                                               /* "12345-" */
+                                                               case -1:
+                                                                       end = length - 1;
+                                                                       break;
+
+                                                               /* "12345-67890" */
+                                                               default:
+                                                                       if (length <= (size_t) end) {
+                                                                               end = length - 1;
+                                                                       } else if (end <  begin) {
+                                                                               PTR_FREE(range);
+                                                                               return PHP_HTTP_RANGE_ERR;
+                                                                       }
+                                                                       break;
+                                                       }
+                                                       break;
+                                       }
+                               }
+
+                               MAKE_STD_ZVAL(zentry);
+                               array_init(zentry);
+                               add_index_long(zentry, 0, begin);
+                               add_index_long(zentry, 1, end);
+                               zend_hash_next_index_insert(ranges, &zentry, sizeof(zval *), NULL);
+
+                               begin = -1;
+                               end = -1;
+                               ptr = &begin;
+
+                               break;
+
+                       default:
+                               PTR_FREE(range);
+                               return PHP_HTTP_RANGE_NO;
+               }
+       } while (c != 0);
+
+       PTR_FREE(range);
+       return PHP_HTTP_RANGE_OK;
+}
+
+static void grab_headers(void *data, void *arg TSRMLS_DC)
+{
+       php_http_buffer_appendl(PHP_HTTP_BUFFER(arg), ((sapi_header_struct *)data)->header);
+       php_http_buffer_appends(PHP_HTTP_BUFFER(arg), PHP_HTTP_CRLF);
+}
+
+ZEND_RESULT_CODE php_http_env_get_response_headers(HashTable *headers_ht TSRMLS_DC)
+{
+       ZEND_RESULT_CODE status;
+       php_http_buffer_t headers;
+
+       php_http_buffer_init(&headers);
+       zend_llist_apply_with_argument(&SG(sapi_headers).headers, grab_headers, &headers TSRMLS_CC);
+       php_http_buffer_fix(&headers);
+
+       status = php_http_header_parse(headers.data, headers.used, headers_ht, NULL, NULL TSRMLS_CC);
+       php_http_buffer_dtor(&headers);
+
+       return status;
+}
+
+char *php_http_env_get_response_header(const char *name_str, size_t name_len TSRMLS_DC)
+{
+       char *val = NULL;
+       HashTable headers;
+
+       zend_hash_init(&headers, 0, NULL, ZVAL_PTR_DTOR, 0);
+       if (SUCCESS == php_http_env_get_response_headers(&headers TSRMLS_CC)) {
+               zval **zvalue;
+               char *key = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1);
+
+               if (SUCCESS == zend_symtable_find(&headers, key, name_len + 1, (void *) &zvalue)) {
+                       zval *zcopy = php_http_ztyp(IS_STRING, *zvalue);
+
+                       val = estrndup(Z_STRVAL_P(zcopy), Z_STRLEN_P(zcopy));
+                       zval_ptr_dtor(&zcopy);
+               }
+
+               efree(key);
+       }
+       zend_hash_destroy(&headers);
+
+       return val;
+}
+
+long php_http_env_get_response_code(TSRMLS_D)
+{
+       long code = SG(sapi_headers).http_response_code;
+       return code ? code : 200;
+}
+
+ZEND_RESULT_CODE php_http_env_set_response_code(long http_code TSRMLS_DC)
+{
+       return sapi_header_op(SAPI_HEADER_SET_STATUS, (void *) http_code TSRMLS_CC);
+}
+
+ZEND_RESULT_CODE php_http_env_set_response_status_line(long code, php_http_version_t *v TSRMLS_DC)
+{
+       sapi_header_line h = {NULL, 0, 0};
+       ZEND_RESULT_CODE ret;
+
+       h.line_len = spprintf(&h.line, 0, "HTTP/%u.%u %ld %s", v->major, v->minor, code, php_http_env_get_response_status_for_code(code));
+       ret = sapi_header_op(SAPI_HEADER_REPLACE, (void *) &h TSRMLS_CC);
+       efree(h.line);
+
+       return ret;
+}
+
+ZEND_RESULT_CODE php_http_env_set_response_protocol_version(php_http_version_t *v TSRMLS_DC)
+{
+       return php_http_env_set_response_status_line(php_http_env_get_response_code(TSRMLS_C), v TSRMLS_CC);
+}
+
+ZEND_RESULT_CODE php_http_env_set_response_header(long http_code, const char *header_str, size_t header_len, zend_bool replace TSRMLS_DC)
+{
+       sapi_header_line h = {estrndup(header_str, header_len), header_len, http_code};
+       ZEND_RESULT_CODE ret = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, (void *) &h TSRMLS_CC);
+       efree(h.line);
+       return ret;
+}
+
+ZEND_RESULT_CODE php_http_env_set_response_header_va(long http_code, zend_bool replace, const char *fmt, va_list argv TSRMLS_DC)
+{
+       ZEND_RESULT_CODE ret = FAILURE;
+       sapi_header_line h = {NULL, 0, http_code};
+
+       h.line_len = vspprintf(&h.line, 0, fmt, argv);
+
+       if (h.line) {
+               if (h.line_len) {
+                       ret = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, (void *) &h TSRMLS_CC);
+               }
+               efree(h.line);
+       }
+       return ret;
+}
+
+ZEND_RESULT_CODE php_http_env_set_response_header_format(long http_code, zend_bool replace TSRMLS_DC, const char *fmt, ...)
+{
+       ZEND_RESULT_CODE ret;
+       va_list args;
+
+       va_start(args, fmt);
+       ret = php_http_env_set_response_header_va(http_code, replace, fmt, args TSRMLS_CC);
+       va_end(args);
+
+       return ret;
+}
+
+ZEND_RESULT_CODE php_http_env_set_response_header_value(long http_code, const char *name_str, size_t name_len, zval *value, zend_bool replace TSRMLS_DC)
+{
+       if (!value) {
+               sapi_header_line h = {(char *) name_str, name_len, http_code};
+
+               return sapi_header_op(SAPI_HEADER_DELETE, (void *) &h TSRMLS_CC);
+       }
+
+       if(Z_TYPE_P(value) == IS_ARRAY || Z_TYPE_P(value) == IS_OBJECT) {
+               HashPosition pos;
+               int first = replace;
+               zval **data_ptr;
+
+               FOREACH_HASH_VAL(pos, HASH_OF(value), data_ptr) {
+                       if (SUCCESS != php_http_env_set_response_header_value(http_code, name_str, name_len, *data_ptr, first TSRMLS_CC)) {
+                               return FAILURE;
+                       }
+                       first = 0;
+               }
+
+               return SUCCESS;
+       } else {
+               zval *data = php_http_ztyp(IS_STRING, value);
+
+               if (!Z_STRLEN_P(data)) {
+                       zval_ptr_dtor(&data);
+                       return php_http_env_set_response_header_value(http_code, name_str, name_len, NULL, replace TSRMLS_CC);
+               } else {
+                       sapi_header_line h;
+                       ZEND_RESULT_CODE ret;
+
+                       if (name_len > INT_MAX) {
+                               name_len = INT_MAX;
+                       }
+                       h.response_code = http_code;
+                       h.line_len = spprintf(&h.line, 0, "%.*s: %.*s", (int) name_len, name_str, Z_STRLEN_P(data), Z_STRVAL_P(data));
+
+                       ret = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, (void *) &h TSRMLS_CC);
+
+                       zval_ptr_dtor(&data);
+                       PTR_FREE(h.line);
+
+                       return ret;
+               }
+       }
+}
+
+const char *php_http_env_get_response_status_for_code(unsigned code)
+{
+       switch (code) {
+#define PHP_HTTP_RESPONSE_CODE(c, s) case c: return s;
+#include "php_http_response_codes.h"
+#undef PHP_HTTP_RESPONSE_CODE
+       default:
+               return NULL;
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getRequestHeader, 0, 0, 0)
+       ZEND_ARG_INFO(0, header_name)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnv, getRequestHeader)
+{
+       char *header_name_str = NULL;
+       int header_name_len = 0;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) {
+               return;
+       }
+       if (header_name_str && header_name_len) {
+               size_t header_length;
+               char *header_value = php_http_env_get_request_header(header_name_str, header_name_len, &header_length, NULL TSRMLS_CC);
+
+               if (header_value) {
+                       RETURN_STRINGL(header_value, header_length, 0);
+               }
+       } else {
+               array_init(return_value);
+               php_http_env_get_request_headers(Z_ARRVAL_P(return_value) TSRMLS_CC);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getRequestBody, 0, 0, 0)
+       ZEND_ARG_INFO(0, body_class_name)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnv, getRequestBody)
+{
+       zend_object_value ov;
+       php_http_message_body_t *body;
+       zend_class_entry *class_entry = php_http_message_body_class_entry;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|C", &class_entry), invalid_arg, return);
+
+       body = php_http_env_get_request_body(TSRMLS_C);
+       if (SUCCESS == php_http_new(&ov, class_entry, (php_http_new_t) php_http_message_body_object_new_ex, php_http_message_body_class_entry, body, NULL TSRMLS_CC)) {
+               php_http_message_body_addref(body);
+               RETVAL_OBJVAL(ov, 0);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getResponseStatusForCode, 0, 0, 1)
+       ZEND_ARG_INFO(0, code)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnv, getResponseStatusForCode)
+{
+       long code;
+       const char *status;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) {
+               return;
+       }
+
+       if ((status = php_http_env_get_response_status_for_code(code))) {
+               RETURN_STRING(status, 1);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getResponseStatusForAllCodes, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnv, getResponseStatusForAllCodes)
+{
+       if (SUCCESS != zend_parse_parameters_none()) {
+               return;
+       }
+
+       array_init(return_value);
+#define PHP_HTTP_RESPONSE_CODE(code, status) add_index_string(return_value, code, status, 1);
+#include "php_http_response_codes.h"
+#undef PHP_HTTP_RESPONSE_CODE
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getResponseHeader, 0, 0, 0)
+       ZEND_ARG_INFO(0, header_name)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnv, getResponseHeader)
+{
+       char *header_name_str = NULL;
+       int header_name_len = 0;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) {
+               return;
+       }
+       if (header_name_str && header_name_len) {
+               char *header_value = php_http_env_get_response_header(header_name_str, header_name_len TSRMLS_CC);
+
+               if (header_value) {
+                       RETURN_STRING(header_value, 0);
+               }
+       } else {
+               array_init(return_value);
+               php_http_env_get_response_headers(Z_ARRVAL_P(return_value) TSRMLS_CC);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getResponseCode, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnv, getResponseCode)
+{
+       if (SUCCESS != zend_parse_parameters_none()) {
+               return;
+       }
+       RETURN_LONG(php_http_env_get_response_code(TSRMLS_C));
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_setResponseHeader, 0, 0, 1)
+       ZEND_ARG_INFO(0, header_name)
+       ZEND_ARG_INFO(0, header_value)
+       ZEND_ARG_INFO(0, response_code)
+       ZEND_ARG_INFO(0, replace_header)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnv, setResponseHeader)
+{
+       char *header_name_str;
+       int header_name_len;
+       zval *header_value = NULL;
+       long code = 0;
+       zend_bool replace_header = 1;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z!lb", &header_name_str, &header_name_len, &header_value, &code, &replace_header)) {
+               return;
+       }
+       RETURN_BOOL(SUCCESS == php_http_env_set_response_header_value(code, header_name_str, header_name_len, header_value, replace_header TSRMLS_CC));
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_setResponseCode, 0, 0, 1)
+       ZEND_ARG_INFO(0, code)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnv, setResponseCode)
+{
+       long code;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) {
+               return;
+       }
+       RETURN_BOOL(SUCCESS == php_http_env_set_response_code(code TSRMLS_CC));
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_negotiateLanguage, 0, 0, 1)
+       ZEND_ARG_INFO(0, supported)
+       ZEND_ARG_INFO(1, result_array)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnv, negotiateLanguage)
+{
+       HashTable *supported;
+       zval *rs_array = NULL;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H|z", &supported, &rs_array)) {
+               return;
+       }
+       if (rs_array) {
+               zval_dtor(rs_array);
+               array_init(rs_array);
+       }
+
+       PHP_HTTP_DO_NEGOTIATE(language, supported, rs_array);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_negotiateCharset, 0, 0, 1)
+       ZEND_ARG_INFO(0, supported)
+       ZEND_ARG_INFO(1, result_array)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnv, negotiateCharset)
+{
+       HashTable *supported;
+       zval *rs_array = NULL;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H|z", &supported, &rs_array)) {
+               return;
+       }
+       if (rs_array) {
+               zval_dtor(rs_array);
+               array_init(rs_array);
+       }
+       PHP_HTTP_DO_NEGOTIATE(charset, supported, rs_array);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_negotiateEncoding, 0, 0, 1)
+       ZEND_ARG_INFO(0, supported)
+       ZEND_ARG_INFO(1, result_array)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnv, negotiateEncoding)
+{
+       HashTable *supported;
+       zval *rs_array = NULL;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H|z", &supported, &rs_array)) {
+               return;
+       }
+       if (rs_array) {
+               zval_dtor(rs_array);
+               array_init(rs_array);
+       }
+       PHP_HTTP_DO_NEGOTIATE(encoding, supported, rs_array);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_negotiateContentType, 0, 0, 1)
+       ZEND_ARG_INFO(0, supported)
+       ZEND_ARG_INFO(1, result_array)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnv, negotiateContentType)
+{
+       HashTable *supported;
+       zval *rs_array = NULL;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H|z", &supported, &rs_array)) {
+               return;
+       }
+       if (rs_array) {
+               zval_dtor(rs_array);
+               array_init(rs_array);
+       }
+       PHP_HTTP_DO_NEGOTIATE(content_type, supported, rs_array);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_negotiate, 0, 0, 2)
+       ZEND_ARG_INFO(0, params)
+       ZEND_ARG_INFO(0, supported)
+       ZEND_ARG_INFO(0, primary_type_separator)
+       ZEND_ARG_INFO(1, result_array)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnv, negotiate)
+{
+       HashTable *supported, *rs;
+       zval *rs_array = NULL;
+       char *value_str, *sep_str = NULL;
+       int value_len, sep_len = 0;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sH|s!z", &value_str, &value_len, &supported, &sep_str, &sep_len, &rs_array)) {
+               return;
+       }
+
+
+       if (rs_array) {
+               zval_dtor(rs_array);
+               array_init(rs_array);
+       }
+
+       if ((rs = php_http_negotiate(value_str, value_len, supported, sep_str, sep_len TSRMLS_CC))) {
+               PHP_HTTP_DO_NEGOTIATE_HANDLE_RESULT(rs, supported, rs_array);
+       } else {
+               PHP_HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported, rs_array);
+       }
+}
+
+static zend_function_entry php_http_env_methods[] = {
+       PHP_ME(HttpEnv, getRequestHeader,              ai_HttpEnv_getRequestHeader,              ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       PHP_ME(HttpEnv, getRequestBody,                ai_HttpEnv_getRequestBody,                ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+
+       PHP_ME(HttpEnv, getResponseStatusForCode,      ai_HttpEnv_getResponseStatusForCode,      ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       PHP_ME(HttpEnv, getResponseStatusForAllCodes,  ai_HttpEnv_getResponseStatusForAllCodes,  ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+
+       PHP_ME(HttpEnv, getResponseHeader,             ai_HttpEnv_getResponseHeader,             ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       PHP_ME(HttpEnv, getResponseCode,               ai_HttpEnv_getResponseCode,               ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       PHP_ME(HttpEnv, setResponseHeader,             ai_HttpEnv_setResponseHeader,             ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       PHP_ME(HttpEnv, setResponseCode,               ai_HttpEnv_setResponseCode,               ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+
+       PHP_ME(HttpEnv, negotiateLanguage,             ai_HttpEnv_negotiateLanguage,             ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       PHP_ME(HttpEnv, negotiateContentType,          ai_HttpEnv_negotiateContentType,          ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       PHP_ME(HttpEnv, negotiateEncoding,             ai_HttpEnv_negotiateEncoding,             ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       PHP_ME(HttpEnv, negotiateCharset,              ai_HttpEnv_negotiateCharset,              ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       PHP_ME(HttpEnv, negotiate,                     ai_HttpEnv_negotiate,                     ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+
+       EMPTY_FUNCTION_ENTRY
+};
+
+zend_class_entry *php_http_env_class_entry;
+
+PHP_MINIT_FUNCTION(http_env)
+{
+       zend_class_entry ce = {0};
+
+       INIT_NS_CLASS_ENTRY(ce, "http", "Env", php_http_env_methods);
+       php_http_env_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
+
+       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_env.h b/src/php_http_env.h
new file mode 100644 (file)
index 0000000..3fc80ab
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_ENV_H
+#define PHP_HTTP_ENV_H
+
+#include "php_http_message_body.h"
+#include "php_http_version.h"
+
+struct php_http_env_globals {
+       zval *server_var;
+       char *etag_mode;
+
+       struct {
+               HashTable *headers;
+               php_http_message_body_t *body;
+       } request;
+};
+
+typedef enum php_http_content_encoding {
+       PHP_HTTP_CONTENT_ENCODING_NONE,
+       PHP_HTTP_CONTENT_ENCODING_GZIP
+} php_http_content_encoding_t;
+
+typedef enum php_http_range_status {
+       PHP_HTTP_RANGE_NO,
+       PHP_HTTP_RANGE_OK,
+       PHP_HTTP_RANGE_ERR
+} php_http_range_status_t;
+
+PHP_HTTP_API php_http_range_status_t php_http_env_get_request_ranges(HashTable *ranges, size_t entity_length, php_http_message_t *request TSRMLS_DC);
+PHP_HTTP_API void php_http_env_get_request_headers(HashTable *headers TSRMLS_DC);
+PHP_HTTP_API char *php_http_env_get_request_header(const char *name_str, size_t name_len, size_t *len, php_http_message_t *request TSRMLS_DC);
+PHP_HTTP_API int php_http_env_got_request_header(const char *name_str, size_t name_len, php_http_message_t *request TSRMLS_DC);
+PHP_HTTP_API php_http_message_body_t *php_http_env_get_request_body(TSRMLS_D);
+PHP_HTTP_API const char *php_http_env_get_request_method(php_http_message_t *request TSRMLS_DC);
+
+typedef enum php_http_content_disposition {
+       PHP_HTTP_CONTENT_DISPOSITION_NONE,
+       PHP_HTTP_CONTENT_DISPOSITION_INLINE,
+       PHP_HTTP_CONTENT_DISPOSITION_ATTACHMENT
+} php_http_content_disposition_t;
+
+typedef enum php_http_cache_status {
+       PHP_HTTP_CACHE_NO,
+       PHP_HTTP_CACHE_HIT,
+       PHP_HTTP_CACHE_MISS
+} php_http_cache_status_t;
+
+PHP_HTTP_API long php_http_env_get_response_code(TSRMLS_D);
+PHP_HTTP_API const char *php_http_env_get_response_status_for_code(unsigned code);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_env_get_response_headers(HashTable *headers_ht TSRMLS_DC);
+PHP_HTTP_API char *php_http_env_get_response_header(const char *name_str, size_t name_len TSRMLS_DC);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_code(long http_code TSRMLS_DC);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_protocol_version(php_http_version_t *v TSRMLS_DC);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_header(long http_code, const char *header_str, size_t header_len, zend_bool replace TSRMLS_DC);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_header_value(long http_code, const char *name_str, size_t name_len, zval *value, zend_bool replace TSRMLS_DC);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_header_format(long http_code, zend_bool replace TSRMLS_DC, const char *fmt, ...);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_header_va(long http_code, zend_bool replace, const char *fmt, va_list argv TSRMLS_DC);
+
+PHP_HTTP_API zval *php_http_env_get_server_var(const char *key_str, size_t key_len, zend_bool check TSRMLS_DC);
+#define php_http_env_got_server_var(v) (NULL != php_http_env_get_server_var((v), strlen(v), 1 TSRMLS_CC))
+PHP_HTTP_API zval *php_http_env_get_superglobal(const char *key, size_t key_len TSRMLS_DC);
+
+PHP_HTTP_API zend_class_entry *php_http_env_class_entry;
+PHP_MINIT_FUNCTION(http_env);
+PHP_RSHUTDOWN_FUNCTION(http_env);
+
+#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_env_request.c b/src/php_http_env_request.c
new file mode 100644 (file)
index 0000000..a884d2f
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+static int grab_file(void *zpp TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
+{
+       zval *zfiles, **name, **zname, **error, **zerror, **type, **ztype, **size, **zsize, **tmp_name = zpp;
+       zend_hash_key *file_key;
+
+       zfiles = (zval *) va_arg(argv, zval *);
+       file_key = (zend_hash_key *) va_arg(argv, zend_hash_key *);
+       name = (zval **) va_arg(argv, zval **);
+       size = (zval **) va_arg(argv, zval **);
+       type = (zval **) va_arg(argv, zval **);
+       error = (zval **) va_arg(argv, zval **);
+
+       if (SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(name), key->h, (void *) &zname)
+       &&      SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(size), key->h, (void *) &zsize)
+       &&      SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(type), key->h, (void *) &ztype)
+       &&      SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(error), key->h, (void *) &zerror)
+       ) {
+               zval *entry, **array;
+
+               MAKE_STD_ZVAL(entry);
+               array_init(entry);
+
+               Z_ADDREF_PP(tmp_name);
+               add_assoc_zval_ex(entry, ZEND_STRS("file"), *tmp_name);
+               Z_ADDREF_PP(zname);
+               add_assoc_zval_ex(entry, ZEND_STRS("name"), *zname);
+               Z_ADDREF_PP(zsize);
+               add_assoc_zval_ex(entry, ZEND_STRS("size"), *zsize);
+               Z_ADDREF_PP(ztype);
+               add_assoc_zval_ex(entry, ZEND_STRS("type"), *ztype);
+               Z_ADDREF_PP(zerror);
+               add_assoc_zval_ex(entry, ZEND_STRS("error"), *zerror);
+
+               if (SUCCESS == zend_hash_quick_find(Z_ARRVAL_P(zfiles), file_key->arKey, file_key->nKeyLength, file_key->h, (void *) &array)) {
+                       add_next_index_zval(*array, entry);
+               } else {
+                       zval *tmp;
+
+                       MAKE_STD_ZVAL(tmp);
+                       array_init(tmp);
+                       add_next_index_zval(tmp, entry);
+                       zend_hash_quick_update(Z_ARRVAL_P(zfiles), file_key->arKey, file_key->nKeyLength, file_key->h, (void *) &tmp, sizeof(zval *), NULL);
+               }
+       }
+
+       return ZEND_HASH_APPLY_KEEP;
+}
+
+static int grab_files(void *zpp TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
+{
+       zval *zfiles, **name, **tmp_name, **error, **type, **size, **val = zpp;
+
+       zfiles = (zval *) va_arg(argv, zval *);
+
+       if (Z_TYPE_PP(val) == IS_ARRAY
+       &&      SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("tmp_name"), (void *) &tmp_name)
+       &&      SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("name"), (void *) &name)
+       &&      SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("size"), (void *) &size)
+       &&      SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("type"), (void *) &type)
+       &&      SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("error"), (void *) &error)
+       ) {
+               int count;
+
+               if (Z_TYPE_PP(tmp_name) == IS_ARRAY && (count = zend_hash_num_elements(Z_ARRVAL_PP(tmp_name))) > 1) {
+                       if (count == zend_hash_num_elements(Z_ARRVAL_PP(name))
+                       &&      count == zend_hash_num_elements(Z_ARRVAL_PP(size))
+                       &&      count == zend_hash_num_elements(Z_ARRVAL_PP(type))
+                       &&      count == zend_hash_num_elements(Z_ARRVAL_PP(error))
+                       ) {
+                               zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp_name) TSRMLS_CC, grab_file, 6, zfiles, key, name, size, type, error);
+                       } else {
+                               /* wat?! */
+                               return ZEND_HASH_APPLY_STOP;
+                       }
+               } else  {
+                       zval *cpy, **tmp;
+
+                       MAKE_STD_ZVAL(cpy);
+                       MAKE_COPY_ZVAL(val, cpy);
+                       if (SUCCESS == zend_hash_find(Z_ARRVAL_P(cpy), ZEND_STRS("tmp_name"), (void *) &tmp)) {
+                               Z_ADDREF_PP(tmp);
+                               add_assoc_zval_ex(cpy, ZEND_STRS("file"), *tmp);
+                               zend_hash_del_key_or_index(Z_ARRVAL_P(cpy), ZEND_STRS("tmp_name"), 0, HASH_DEL_KEY);
+                       }
+                       if (key->nKeyLength > 0) {
+                               zend_hash_quick_update(Z_ARRVAL_P(zfiles), key->arKey, key->nKeyLength, key->h, (void *) &cpy, sizeof(zval *), NULL);
+                       } else {
+                               zend_hash_index_update(Z_ARRVAL_P(zfiles), key->h, (void *) &cpy, sizeof(zval *), NULL);
+                       }
+               }
+       }
+
+       return ZEND_HASH_APPLY_KEEP;
+}
+
+#define PHP_HTTP_ENV_REQUEST_OBJECT_INIT(obj) \
+       do { \
+               if (!obj->message) { \
+                       obj->message = php_http_message_init_env(NULL, PHP_HTTP_REQUEST TSRMLS_CC); \
+               } \
+       } while(0)
+
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvRequest___construct, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvRequest, __construct)
+{
+       php_http_message_object_t *obj;
+       zval *zsg, *zqs;
+
+       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       obj->body = NULL;
+
+       php_http_expect(obj->message = php_http_message_init_env(obj->message, PHP_HTTP_REQUEST TSRMLS_CC), unexpected_val, return);
+
+       zsg = php_http_env_get_superglobal(ZEND_STRL("_GET") TSRMLS_CC);
+       MAKE_STD_ZVAL(zqs);
+       object_init_ex(zqs, php_http_querystring_class_entry);
+       php_http_expect(SUCCESS == php_http_querystring_ctor(zqs, zsg TSRMLS_CC), unexpected_val,
+                       zval_ptr_dtor(&zqs);
+                       return;
+       );
+       zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("query"), zqs TSRMLS_CC);
+       zval_ptr_dtor(&zqs);
+
+       zsg = php_http_env_get_superglobal(ZEND_STRL("_POST") TSRMLS_CC);
+       MAKE_STD_ZVAL(zqs);
+       object_init_ex(zqs, php_http_querystring_class_entry);
+       php_http_expect(SUCCESS == php_http_querystring_ctor(zqs, zsg TSRMLS_CC), unexpected_val,
+                       zval_ptr_dtor(&zqs);
+                       return;
+       );
+       zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("form"), zqs TSRMLS_CC);
+       zval_ptr_dtor(&zqs);
+
+       zsg = php_http_env_get_superglobal(ZEND_STRL("_COOKIE") TSRMLS_CC);
+       MAKE_STD_ZVAL(zqs);
+       object_init_ex(zqs, php_http_querystring_class_entry);
+       php_http_expect(SUCCESS == php_http_querystring_ctor(zqs, zsg TSRMLS_CC), unexpected_val,
+                       zval_ptr_dtor(&zqs);
+                       return;
+       );
+       zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("cookie"), zqs TSRMLS_CC);
+       zval_ptr_dtor(&zqs);
+
+       MAKE_STD_ZVAL(zqs);
+       array_init(zqs);
+       if ((zsg = php_http_env_get_superglobal(ZEND_STRL("_FILES") TSRMLS_CC))) {
+               zend_hash_apply_with_arguments(Z_ARRVAL_P(zsg) TSRMLS_CC, grab_files, 1, zqs);
+       }
+       zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("files"), zqs TSRMLS_CC);
+       zval_ptr_dtor(&zqs);
+}
+
+#define call_querystring_get(prop) \
+       do {\
+               zend_fcall_info fci; \
+               zend_fcall_info_cache fcc; \
+               zval *rv = NULL, mn, ***args = ecalloc(sizeof(zval **), ZEND_NUM_ARGS()); \
+               zval *qs = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL(prop), 0 TSRMLS_CC); \
+                \
+               INIT_PZVAL(&mn); \
+               array_init(&mn); \
+               Z_ADDREF_P(qs); \
+               add_next_index_zval(&mn, qs); \
+               add_next_index_stringl(&mn, ZEND_STRL("get"), 1); \
+               zend_fcall_info_init(&mn, 0, &fci, &fcc, NULL, NULL TSRMLS_CC); \
+               zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args); \
+               zend_fcall_info_argp(&fci TSRMLS_CC, ZEND_NUM_ARGS(), args); \
+               zend_fcall_info_call(&fci, &fcc, &rv, NULL TSRMLS_CC); \
+               zend_fcall_info_args_clear(&fci, 1); \
+               efree(args); \
+               zval_dtor(&mn); \
+               if (rv) { \
+                       RETVAL_ZVAL(rv, 0, 1); \
+               } \
+       } while(0);
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvRequest_getForm, 0, 0, 0)
+       ZEND_ARG_INFO(0, name)
+       ZEND_ARG_INFO(0, type)
+       ZEND_ARG_INFO(0, defval)
+       ZEND_ARG_INFO(0, delete)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvRequest, getForm)
+{
+       if (ZEND_NUM_ARGS()) {
+               call_querystring_get("form");
+       } else {
+               zval *zform = zend_read_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("form"), 0 TSRMLS_CC);
+               RETURN_ZVAL(zform, 1, 0);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvRequest_getQuery, 0, 0, 0)
+       ZEND_ARG_INFO(0, name)
+       ZEND_ARG_INFO(0, type)
+       ZEND_ARG_INFO(0, defval)
+       ZEND_ARG_INFO(0, delete)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvRequest, getQuery)
+{
+       if (ZEND_NUM_ARGS()) {
+               call_querystring_get("query");
+       } else {
+               zval *zquery = zend_read_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("query"), 0 TSRMLS_CC);
+               RETURN_ZVAL(zquery, 1, 0);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvRequest_getCookie, 0, 0, 0)
+       ZEND_ARG_INFO(0, name)
+       ZEND_ARG_INFO(0, type)
+       ZEND_ARG_INFO(0, defval)
+       ZEND_ARG_INFO(0, delete)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvRequest, getCookie)
+{
+       if (ZEND_NUM_ARGS()) {
+               call_querystring_get("cookie");
+       } else {
+               zval *zcookie = zend_read_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("cookie"), 0 TSRMLS_CC);
+               RETURN_ZVAL(zcookie, 1, 0);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvRequest_getFiles, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvRequest, getFiles)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               zval *zfiles = zend_read_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("files"), 0 TSRMLS_CC);
+               RETURN_ZVAL(zfiles, 1, 0);
+       }
+}
+
+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, 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)
+       PHP_ME(HttpEnvRequest, getFiles,     ai_HttpEnvRequest_getFiles,     ZEND_ACC_PUBLIC)
+       EMPTY_FUNCTION_ENTRY
+};
+
+zend_class_entry *php_http_env_request_class_entry;
+
+PHP_MINIT_FUNCTION(http_env_request)
+{
+       zend_class_entry ce = {0};
+
+       INIT_NS_CLASS_ENTRY(ce, "http\\Env", "Request", php_http_env_request_methods);
+       php_http_env_request_class_entry = zend_register_internal_class_ex(&ce, php_http_message_class_entry, NULL TSRMLS_CC);
+
+       zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("query"), ZEND_ACC_PROTECTED TSRMLS_CC);
+       zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("form"), ZEND_ACC_PROTECTED TSRMLS_CC);
+       zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("cookie"), ZEND_ACC_PROTECTED TSRMLS_CC);
+       zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("files"), ZEND_ACC_PROTECTED TSRMLS_CC);
+
+       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_env_request.h b/src/php_http_env_request.h
new file mode 100644 (file)
index 0000000..a97836b
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_ENV_REQUEST_H
+#define PHP_HTTP_ENV_REQUEST_H
+
+PHP_HTTP_API zend_class_entry *php_http_env_request_class_entry;
+PHP_MINIT_FUNCTION(http_env_request);
+
+#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_env_response.c b/src/php_http_env_response.c
new file mode 100644 (file)
index 0000000..20a31eb
--- /dev/null
@@ -0,0 +1,1513 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+static void set_option(zval *options, const char *name_str, size_t name_len, int type, void *value_ptr, size_t value_len TSRMLS_DC)
+{
+       if (Z_TYPE_P(options) == IS_OBJECT) {
+               if (value_ptr) {
+                       switch (type) {
+                               case IS_DOUBLE:
+                                       zend_update_property_double(Z_OBJCE_P(options), options, name_str, name_len, *(double *)value_ptr TSRMLS_CC);
+                                       break;
+                               case IS_LONG:
+                                       zend_update_property_long(Z_OBJCE_P(options), options, name_str, name_len, *(long *)value_ptr TSRMLS_CC);
+                                       break;
+                               case IS_STRING:
+                                       zend_update_property_stringl(Z_OBJCE_P(options), options, name_str, name_len, value_ptr, value_len TSRMLS_CC);
+                                       break;
+                               case IS_ARRAY:
+                               case IS_OBJECT:
+                                       zend_update_property(Z_OBJCE_P(options), options, name_str, name_len, value_ptr TSRMLS_CC);
+                                       break;
+                       }
+               } else {
+                       zend_update_property_null(Z_OBJCE_P(options), options, name_str, name_len TSRMLS_CC);
+               }
+       } else {
+               convert_to_array(options);
+               if (value_ptr) {
+                       switch (type) {
+                               case IS_DOUBLE:
+                                       add_assoc_double_ex(options, name_str, name_len + 1, *(double *)value_ptr);
+                                       break;
+                               case IS_LONG:
+                                       add_assoc_long_ex(options, name_str, name_len + 1, *(long *)value_ptr);
+                                       break;
+                               case IS_STRING: {
+                                       char *value = estrndup(value_ptr, value_len);
+                                       add_assoc_stringl_ex(options, name_str, name_len + 1, value, value_len, 0);
+                                       break;
+                               case IS_ARRAY:
+                               case IS_OBJECT:
+                                       Z_ADDREF_P(value_ptr);
+                                       add_assoc_zval_ex(options, name_str, name_len + 1, value_ptr);
+                                       break;
+                               }
+                       }
+               } else {
+                       add_assoc_null_ex(options, name_str, name_len + 1);
+               }
+       }
+}
+static zval *get_option(zval *options, const char *name_str, size_t name_len TSRMLS_DC)
+{
+       zval *val, **valptr;
+
+       if (Z_TYPE_P(options) == IS_OBJECT) {
+               val = zend_read_property(Z_OBJCE_P(options), options, name_str, name_len, 0 TSRMLS_CC);
+       } else {
+               if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(options), name_str, name_len + 1, (void *) &valptr)) {
+                       val = *valptr;
+               } else {
+                       val = NULL;
+               }
+       }
+       if (val) {
+               Z_ADDREF_P(val);
+       }
+       return val;
+}
+static php_http_message_body_t *get_body(zval *options TSRMLS_DC)
+{
+       zval *zbody;
+       php_http_message_body_t *body = NULL;
+
+       if ((zbody = get_option(options, ZEND_STRL("body") TSRMLS_CC))) {
+               if ((Z_TYPE_P(zbody) == IS_OBJECT) && instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_class_entry TSRMLS_CC)) {
+                       php_http_message_body_object_t *body_obj = zend_object_store_get_object(zbody TSRMLS_CC);
+
+                       body = body_obj->body;
+               }
+               zval_ptr_dtor(&zbody);
+       }
+
+       return body;
+}
+static php_http_message_t *get_request(zval *options TSRMLS_DC)
+{
+       zval *zrequest;
+       php_http_message_t *request = NULL;
+
+       if ((zrequest = get_option(options, ZEND_STRL("request") TSRMLS_CC))) {
+               if (Z_TYPE_P(zrequest) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zrequest), php_http_message_class_entry TSRMLS_CC)) {
+                       php_http_message_object_t *request_obj = zend_object_store_get_object(zrequest TSRMLS_CC);
+
+                       request = request_obj->message;
+               }
+               zval_ptr_dtor(&zrequest);
+       }
+
+       return request;
+}
+static void set_cookie(zval *options, zval *zcookie_new TSRMLS_DC)
+{
+       HashPosition pos;
+       zval *zcookies_set;
+       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+       php_http_cookie_object_t *obj = zend_object_store_get_object(zcookie_new TSRMLS_CC);
+
+       zcookies_set = get_option(options, ZEND_STRL("cookies") TSRMLS_CC);
+       if (!zcookies_set || Z_TYPE_P(zcookies_set) != IS_ARRAY) {
+               if (zcookies_set) {
+                       zval_ptr_dtor(&zcookies_set);
+               }
+               MAKE_STD_ZVAL(zcookies_set);
+               array_init_size(zcookies_set, zend_hash_num_elements(&obj->list->cookies));
+       } else {
+               SEPARATE_ZVAL(&zcookies_set);
+       }
+
+       FOREACH_HASH_KEY(pos, &obj->list->cookies, key) {
+               Z_ADDREF_P(zcookie_new);
+               if (key.type == HASH_KEY_IS_STRING) {
+                       add_assoc_zval_ex(zcookies_set, key.str, key.len, zcookie_new);
+               } else {
+                       add_index_zval(zcookies_set, key.num, zcookie_new);
+               }
+       }
+
+       set_option(options, ZEND_STRL("cookies"), IS_ARRAY, zcookies_set, 0 TSRMLS_CC);
+       zval_ptr_dtor(&zcookies_set);
+}
+
+php_http_cache_status_t php_http_env_is_response_cached_by_etag(zval *options, const char *header_str, size_t header_len, php_http_message_t *request TSRMLS_DC)
+{
+       php_http_cache_status_t ret = PHP_HTTP_CACHE_NO;
+       int free_etag = 0;
+       char *header = NULL, *etag;
+       php_http_message_body_t *body;
+       zval *zetag;
+
+
+       if (!(body = get_body(options TSRMLS_CC))) {
+               return ret;
+       }
+
+       if ((zetag = get_option(options, ZEND_STRL("etag") TSRMLS_CC))) {
+               zval *zetag_copy = php_http_ztyp(IS_STRING, zetag);
+               zval_ptr_dtor(&zetag);
+               zetag = zetag_copy;
+       }
+
+       if (zetag && Z_STRLEN_P(zetag)) {
+               etag = Z_STRVAL_P(zetag);
+       } else if ((etag = php_http_message_body_etag(body))) {
+               set_option(options, ZEND_STRL("etag"), IS_STRING, etag, strlen(etag) TSRMLS_CC);
+               free_etag = 1;
+       }
+
+       if (zetag) {
+               zval_ptr_dtor(&zetag);
+       }
+
+       if (etag && (header = php_http_env_get_request_header(header_str, header_len, NULL, request TSRMLS_CC))) {
+               ret = php_http_match(header, etag, PHP_HTTP_MATCH_WORD)  ? PHP_HTTP_CACHE_HIT : PHP_HTTP_CACHE_MISS;
+       }
+
+       if (free_etag) {
+               efree(etag);
+       }
+
+       PTR_FREE(header);
+       return ret;
+}
+
+php_http_cache_status_t php_http_env_is_response_cached_by_last_modified(zval *options, const char *header_str, size_t header_len, php_http_message_t *request TSRMLS_DC)
+{
+       php_http_cache_status_t ret = PHP_HTTP_CACHE_NO;
+       char *header;
+       time_t ums, lm = 0;
+       php_http_message_body_t *body;
+       zval *zlm;
+
+       if (!(body = get_body(options TSRMLS_CC))) {
+               return ret;
+       }
+
+       if ((zlm = get_option(options, ZEND_STRL("lastModified") TSRMLS_CC))) {
+               zval *zlm_copy = php_http_ztyp(IS_LONG, zlm);
+               zval_ptr_dtor(&zlm);
+               zlm = zlm_copy;
+       }
+
+       if (zlm && Z_LVAL_P(zlm) > 0) {
+               lm = Z_LVAL_P(zlm);
+       } else {
+               lm = php_http_message_body_mtime(body);
+               set_option(options, ZEND_STRL("lastModified"), IS_LONG, &lm, 0 TSRMLS_CC);
+       }
+
+       if (zlm) {
+               zval_ptr_dtor(&zlm);
+       }
+
+       if ((header = php_http_env_get_request_header(header_str, header_len, NULL, request TSRMLS_CC))) {
+               ums = php_parse_date(header, NULL);
+
+               if (ums > 0 && ums >= lm) {
+                       ret = PHP_HTTP_CACHE_HIT;
+               } else {
+                       ret = PHP_HTTP_CACHE_MISS;
+               }
+       }
+
+       PTR_FREE(header);
+       return ret;
+}
+
+static zend_bool php_http_env_response_is_cacheable(php_http_env_response_t *r, php_http_message_t *request)
+{
+       long status = r->ops->get_status(r);
+       TSRMLS_FETCH_FROM_CTX(r->ts);
+
+       if (status && status / 100 != 2) {
+               return 0;
+       }
+
+       if (php_http_env_got_request_header(ZEND_STRL("Authorization"), request TSRMLS_CC)) {
+               return 0;
+       }
+
+       if (-1 == php_http_select_str(php_http_env_get_request_method(request TSRMLS_CC), 2, "HEAD", "GET")) {
+               return 0;
+       }
+
+       return 1;
+}
+
+static size_t output(void *context, char *buf, size_t len TSRMLS_DC)
+{
+       php_http_env_response_t *r = context;
+
+       if (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) {
+               r->ops->flush(r);
+               php_http_sleep(r->throttle.delay);
+       }
+       return len;
+}
+
+#define php_http_env_response_send_done(r) php_http_env_response_send_data((r), NULL, 0)
+static ZEND_RESULT_CODE php_http_env_response_send_data(php_http_env_response_t *r, const char *buf, size_t len)
+{
+       size_t chunks_sent, chunk = r->throttle.chunk ? r->throttle.chunk : PHP_HTTP_SENDBUF_SIZE;
+       TSRMLS_FETCH_FROM_CTX(r->ts);
+
+       if (r->content.encoder) {
+               char *enc_str = NULL;
+               size_t enc_len = 0;
+
+               if (buf) {
+                       if (SUCCESS != php_http_encoding_stream_update(r->content.encoder, buf, len, &enc_str, &enc_len)) {
+                               return FAILURE;
+                       }
+               } else {
+                       if (SUCCESS != php_http_encoding_stream_finish(r->content.encoder, &enc_str, &enc_len)) {
+                               return FAILURE;
+                       }
+               }
+
+               if (!enc_str) {
+                       return SUCCESS;
+               }
+               chunks_sent = php_http_buffer_chunked_output(&r->buffer, enc_str, enc_len, buf ? chunk : 0, output, r TSRMLS_CC);
+               PTR_FREE(enc_str);
+       } else {
+               chunks_sent = php_http_buffer_chunked_output(&r->buffer, buf, len, buf ? chunk : 0, output, r TSRMLS_CC);
+       }
+
+       return chunks_sent != (size_t) -1 ? SUCCESS : FAILURE;
+}
+
+php_http_env_response_t *php_http_env_response_init(php_http_env_response_t *r, zval *options, php_http_env_response_ops_t *ops, void *init_arg TSRMLS_DC)
+{
+       zend_bool free_r;
+
+       if ((free_r = !r)) {
+               r = emalloc(sizeof(*r));
+       }
+       memset(r, 0, sizeof(*r));
+
+       if (ops) {
+               r->ops = ops;
+       } else {
+               r->ops = php_http_env_response_get_sapi_ops();
+       }
+
+       r->buffer = php_http_buffer_init(NULL);
+
+       Z_ADDREF_P(options);
+       r->options = options;
+
+       TSRMLS_SET_CTX(r->ts);
+
+       if (r->ops->init && (SUCCESS != r->ops->init(r, init_arg))) {
+               if (free_r) {
+                       php_http_env_response_free(&r);
+               } else {
+                       php_http_env_response_dtor(r);
+                       r = NULL;
+               }
+       }
+
+       return r;
+}
+
+void php_http_env_response_dtor(php_http_env_response_t *r)
+{
+       if (r->ops->dtor) {
+               r->ops->dtor(r);
+       }
+       php_http_buffer_free(&r->buffer);
+       zval_ptr_dtor(&r->options);
+       PTR_FREE(r->content.type);
+       PTR_FREE(r->content.encoding);
+       if (r->content.encoder) {
+               php_http_encoding_stream_free(&r->content.encoder);
+       }
+}
+
+void php_http_env_response_free(php_http_env_response_t **r)
+{
+       if (*r) {
+               php_http_env_response_dtor(*r);
+               efree(*r);
+               *r = NULL;
+       }
+}
+
+static ZEND_RESULT_CODE php_http_env_response_send_head(php_http_env_response_t *r, php_http_message_t *request)
+{
+       ZEND_RESULT_CODE ret = SUCCESS;
+       zval *zoption, *options = r->options;
+       TSRMLS_FETCH_FROM_CTX(r->ts);
+
+       if (r->done) {
+               return ret;
+       }
+
+       if ((zoption = get_option(options, ZEND_STRL("headers") TSRMLS_CC))) {
+               if (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 TSRMLS_CC);
+               }
+               zval_ptr_dtor(&zoption);
+       }
+
+       if (ret != SUCCESS) {
+               return ret;
+       }
+
+       if ((zoption = get_option(options, ZEND_STRL("responseCode") TSRMLS_CC))) {
+               zval *zoption_copy = php_http_ztyp(IS_LONG, zoption);
+
+               zval_ptr_dtor(&zoption);
+               if (Z_LVAL_P(zoption_copy) > 0) {
+                       ret = r->ops->set_status(r, Z_LVAL_P(zoption_copy));
+               }
+               zval_ptr_dtor(&zoption_copy);
+       }
+
+       if (ret != SUCCESS) {
+               return ret;
+       }
+
+       if ((zoption = get_option(options, ZEND_STRL("httpVersion") TSRMLS_CC))) {
+               php_http_version_t v;
+               zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
+
+               zval_ptr_dtor(&zoption);
+               if (Z_STRLEN_P(zoption_copy) && php_http_version_parse(&v, Z_STRVAL_P(zoption_copy) TSRMLS_CC)) {
+                       ret = r->ops->set_protocol_version(r, &v);
+                       php_http_version_dtor(&v);
+               }
+               zval_ptr_dtor(&zoption_copy);
+       }
+
+       if (ret != SUCCESS) {
+               return ret;
+       }
+
+       if ((zoption = get_option(options, ZEND_STRL("cookies") TSRMLS_CC))) {
+               if (Z_TYPE_P(zoption) == IS_ARRAY) {
+                       HashPosition pos;
+                       zval **zcookie;
+
+                       FOREACH_VAL(pos, zoption, zcookie) {
+                               if (Z_TYPE_PP(zcookie) == IS_OBJECT && instanceof_function(Z_OBJCE_PP(zcookie), php_http_cookie_class_entry TSRMLS_CC)) {
+                                       php_http_cookie_object_t *obj = zend_object_store_get_object(*zcookie TSRMLS_CC);
+                                       char *str;
+                                       size_t len;
+
+                                       php_http_cookie_list_to_string(obj->list, &str, &len);
+                                       if (SUCCESS != (ret = r->ops->add_header(r, "Set-Cookie: %s", str))) {
+                                               efree(str);
+                                               break;
+                                       }
+                                       efree(str);
+                               }
+                       }
+               }
+               zval_ptr_dtor(&zoption);
+       }
+
+       if (ret != SUCCESS) {
+               return ret;
+       }
+
+       if ((zoption = get_option(options, ZEND_STRL("contentType") TSRMLS_CC))) {
+               zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
+
+               zval_ptr_dtor(&zoption);
+               if (Z_STRLEN_P(zoption_copy) && strchr(Z_STRVAL_P(zoption_copy), '/')) {
+                       if (SUCCESS == (ret = r->ops->set_header(r, "Content-Type: %.*s", Z_STRLEN_P(zoption_copy), Z_STRVAL_P(zoption_copy)))) {
+                               r->content.type = estrndup(Z_STRVAL_P(zoption_copy), Z_STRLEN_P(zoption_copy));
+                       }
+               }
+               zval_ptr_dtor(&zoption_copy);
+       }
+
+       if (ret != SUCCESS) {
+               return ret;
+       }
+
+       if (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 TSRMLS_CC, 1, &range)
+                               &&      2 == php_http_array_list(Z_ARRVAL_PP(range) TSRMLS_CC, 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_PP(begin), Z_LVAL_PP(end), r->content.length);
+                               }
+                       } else {
+                               /* this should never happen */
+                               zend_hash_destroy(&r->range.values);
+                               ret = FAILURE;
+                       }
+               } else {
+                       php_http_boundary(r->range.boundary, sizeof(r->range.boundary) TSRMLS_CC);
+                       if (SUCCESS == (ret = r->ops->set_status(r, 206))) {
+                               ret = r->ops->set_header(r, "Content-Type: multipart/byteranges; boundary=%s", r->range.boundary);
+                       }
+               }
+       } else {
+               if ((zoption = get_option(options, ZEND_STRL("cacheControl") TSRMLS_CC))) {
+                       zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
+
+                       zval_ptr_dtor(&zoption);
+                       if (Z_STRLEN_P(zoption_copy)) {
+                               ret = r->ops->set_header(r, "Cache-Control: %.*s", Z_STRLEN_P(zoption_copy), Z_STRVAL_P(zoption_copy));
+                       }
+                       zval_ptr_dtor(&zoption_copy);
+               }
+
+               if (ret != SUCCESS) {
+                       return ret;
+               }
+
+               if ((zoption = get_option(options, ZEND_STRL("contentDisposition") TSRMLS_CC))) {
+                       zval *zoption_copy = php_http_ztyp(IS_ARRAY, zoption);
+                       php_http_buffer_t buf;
+
+                       php_http_buffer_init(&buf);
+                       if (php_http_params_to_string(&buf, Z_ARRVAL_P(zoption_copy), ZEND_STRL(","), ZEND_STRL(";"), ZEND_STRL("="), PHP_HTTP_PARAMS_DEFAULT TSRMLS_CC)) {
+                               if (buf.used) {
+                                       ret = r->ops->set_header(r, "Content-Disposition: %.*s", buf.used, buf.data);
+                               }
+                       }
+
+                       php_http_buffer_dtor(&buf);
+                       zval_ptr_dtor(&zoption_copy);
+                       zval_ptr_dtor(&zoption);
+               }
+
+               if (ret != SUCCESS) {
+                       return ret;
+               }
+
+               if ((zoption = get_option(options, ZEND_STRL("contentEncoding") TSRMLS_CC))) {
+                       zval *zoption_copy = php_http_ztyp(IS_LONG, zoption);
+                       zval zsupported;
+                       HashTable *result = NULL;
+
+                       zval_ptr_dtor(&zoption);
+                       switch (Z_LVAL_P(zoption_copy)) {
+                               case PHP_HTTP_CONTENT_ENCODING_GZIP:
+                                       INIT_PZVAL(&zsupported);
+                                       array_init(&zsupported);
+                                       add_next_index_stringl(&zsupported, ZEND_STRL("none"), 1);
+                                       add_next_index_stringl(&zsupported, ZEND_STRL("gzip"), 1);
+                                       add_next_index_stringl(&zsupported, ZEND_STRL("deflate"), 1);
+
+                                       if ((result = php_http_negotiate_encoding(Z_ARRVAL(zsupported), request TSRMLS_CC))) {
+                                               char *key_str = NULL;
+                                               uint key_len = 0;
+
+                                               zend_hash_internal_pointer_reset(result);
+                                               if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(result, &key_str, &key_len, NULL, 0, NULL)) {
+                                                       if (!strcmp(key_str, "gzip")) {
+                                                               if (!(r->content.encoder = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), PHP_HTTP_DEFLATE_TYPE_GZIP TSRMLS_CC))) {
+                                                                       ret = FAILURE;
+                                                               } else if (SUCCESS == (ret = r->ops->set_header(r, "Content-Encoding: gzip"))) {
+                                                                       r->content.encoding = estrndup(key_str, key_len - 1);
+                                                               }
+                                                       } else if (!strcmp(key_str, "deflate")) {
+                                                               if (!(r->content.encoder = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), PHP_HTTP_DEFLATE_TYPE_ZLIB TSRMLS_CC))) {
+                                                                       ret = FAILURE;
+                                                               } else if (SUCCESS == (ret = r->ops->set_header(r, "Content-Encoding: deflate"))) {
+                                                                       r->content.encoding = estrndup(key_str, key_len - 1);
+                                                               }
+                                                       } else {
+                                                               ret = r->ops->del_header(r, ZEND_STRL("Content-Encoding"));
+                                                       }
+
+                                                       if (SUCCESS == ret) {
+                                                               ret = r->ops->add_header(r, "Vary: Accept-Encoding");
+                                                       }
+                                               }
+
+                                               zend_hash_destroy(result);
+                                               FREE_HASHTABLE(result);
+                                       }
+
+                                       zval_dtor(&zsupported);
+                                       break;
+
+                               case PHP_HTTP_CONTENT_ENCODING_NONE:
+                               default:
+                                       ret = r->ops->del_header(r, ZEND_STRL("Content-Encoding"));
+                                       break;
+                       }
+                       zval_ptr_dtor(&zoption_copy);
+               }
+
+               if (SUCCESS != ret) {
+                       return ret;
+               }
+
+               if (php_http_env_response_is_cacheable(r, request)) {
+                       switch (php_http_env_is_response_cached_by_etag(options, ZEND_STRL("If-None-Match"), request TSRMLS_CC)) {
+                               case PHP_HTTP_CACHE_MISS:
+                                       break;
+
+                               case PHP_HTTP_CACHE_NO:
+                                       if (PHP_HTTP_CACHE_HIT != php_http_env_is_response_cached_by_last_modified(options, ZEND_STRL("If-Modified-Since"), request TSRMLS_CC)) {
+                                               break;
+                                       }
+                                       /*  no break */
+
+                               case PHP_HTTP_CACHE_HIT:
+                                       ret = r->ops->set_status(r, 304);
+                                       r->done = 1;
+                                       break;
+                       }
+
+                       if ((zoption = get_option(options, ZEND_STRL("etag") TSRMLS_CC))) {
+                               zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
+
+                               zval_ptr_dtor(&zoption);
+                               if (*Z_STRVAL_P(zoption_copy) != '"' && strncmp(Z_STRVAL_P(zoption_copy), "W/\"", 3)) {
+                                       ret = r->ops->set_header(r, "ETag: \"%s\"", Z_STRVAL_P(zoption_copy));
+                               } else {
+                                       ret = r->ops->set_header(r, "ETag: %s", Z_STRVAL_P(zoption_copy));
+                               }
+                               zval_ptr_dtor(&zoption_copy);
+                       }
+                       if ((zoption = get_option(options, ZEND_STRL("lastModified") TSRMLS_CC))) {
+                               zval *zoption_copy = php_http_ztyp(IS_LONG, zoption);
+
+                               zval_ptr_dtor(&zoption);
+                               if (Z_LVAL_P(zoption_copy)) {
+                                       char *date = php_format_date(ZEND_STRL(PHP_HTTP_DATE_FORMAT), Z_LVAL_P(zoption_copy), 0 TSRMLS_CC);
+                                       if (date) {
+                                               ret = r->ops->set_header(r, "Last-Modified: %s", date);
+                                               efree(date);
+                                       }
+                               }
+                               zval_ptr_dtor(&zoption_copy);
+                       }
+               }
+       }
+
+       return ret;
+}
+
+static ZEND_RESULT_CODE php_http_env_response_send_body(php_http_env_response_t *r)
+{
+       ZEND_RESULT_CODE ret = SUCCESS;
+       zval *zoption;
+       php_http_message_body_t *body;
+       TSRMLS_FETCH_FROM_CTX(r->ts);
+
+       if (r->done) {
+               return ret;
+       }
+
+       if ((body = get_body(r->options TSRMLS_CC))) {
+               if ((zoption = get_option(r->options, ZEND_STRL("throttleDelay") TSRMLS_CC))) {
+                       if (Z_TYPE_P(zoption) == IS_DOUBLE) {
+                               r->throttle.delay =  Z_DVAL_P(zoption);
+                       }
+                       zval_ptr_dtor(&zoption);
+               }
+               if ((zoption = get_option(r->options, ZEND_STRL("throttleChunk") TSRMLS_CC))) {
+                       if (Z_TYPE_P(zoption) == IS_LONG) {
+                               r->throttle.chunk = Z_LVAL_P(zoption);
+                       }
+                       zval_ptr_dtor(&zoption);
+               }
+
+               if (r->range.status == PHP_HTTP_RANGE_OK) {
+                       if (zend_hash_num_elements(&r->range.values) == 1) {
+                               /* single range */
+                               zval **range, **begin, **end;
+
+                               if (    1 == php_http_array_list(&r->range.values TSRMLS_CC, 1, &range)
+                                       &&      2 == php_http_array_list(Z_ARRVAL_PP(range) TSRMLS_CC, 2, &begin, &end)
+                               ) {
+                                       /* send chunk */
+                                       ret = php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_env_response_send_data, r, Z_LVAL_PP(begin), Z_LVAL_PP(end) - Z_LVAL_PP(begin) + 1);
+                                       if (ret == SUCCESS) {
+                                               ret = php_http_env_response_send_done(r);
+                                       }
+                                       zend_hash_destroy(&r->range.values);
+                               } else {
+                                       /* this should never happen */
+                                       zend_hash_destroy(&r->range.values);
+                                       r->ops->set_status(r, 500);
+                                       ret = FAILURE;
+                               }
+
+                       } else {
+                               /* send multipart/byte-ranges message */
+                               HashPosition pos;
+                               zval **chunk;
+
+                               FOREACH_HASH_VAL(pos, &r->range.values, chunk) {
+                                       zval **begin, **end;
+
+                                       if (2 == php_http_array_list(Z_ARRVAL_PP(chunk) TSRMLS_CC, 2, &begin, &end)) {
+                                               php_http_buffer_appendf(r->buffer,
+                                                               PHP_HTTP_CRLF
+                                                               "--%s" PHP_HTTP_CRLF
+                                                               "Content-Type: %s" PHP_HTTP_CRLF
+                                                               "Content-Range: bytes %ld-%ld/%zu" PHP_HTTP_CRLF PHP_HTTP_CRLF,
+                                                               /* - */
+                                                               r->range.boundary,
+                                                               r->content.type ? r->content.type : "application/octet-stream",
+                                                               Z_LVAL_PP(begin),
+                                                               Z_LVAL_PP(end),
+                                                               r->content.length
+                                               );
+                                               ret = php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_env_response_send_data, r, Z_LVAL_PP(begin), Z_LVAL_PP(end) - Z_LVAL_PP(begin) + 1);
+                                       }
+                               }
+
+                               if (ret == SUCCESS) {
+                                       php_http_buffer_appendf(r->buffer, PHP_HTTP_CRLF "--%s--", r->range.boundary);
+                                       ret = php_http_env_response_send_done(r);
+                               }
+                               zend_hash_destroy(&r->range.values);
+                       }
+
+               } else {
+                       ret = php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_env_response_send_data, r, 0, 0);
+                       if (ret == SUCCESS) {
+                               ret = php_http_env_response_send_done(r);
+                       }
+               }
+       }
+       return ret;
+}
+
+ZEND_RESULT_CODE php_http_env_response_send(php_http_env_response_t *r)
+{
+       php_http_message_t *request;
+       php_http_message_body_t *body;
+       TSRMLS_FETCH_FROM_CTX(r->ts);
+
+       request = get_request(r->options TSRMLS_CC);
+
+       /* check for ranges */
+       if ((body = get_body(r->options TSRMLS_CC))) {
+               r->content.length = php_http_message_body_size(body);
+
+               if (SUCCESS != r->ops->set_header(r, "Accept-Ranges: bytes")) {
+                       return FAILURE;
+               } else {
+                       zend_hash_init(&r->range.values, 0, NULL, ZVAL_PTR_DTOR, 0);
+                       r->range.status = php_http_env_get_request_ranges(&r->range.values, r->content.length, request TSRMLS_CC);
+
+                       switch (r->range.status) {
+                               case PHP_HTTP_RANGE_NO:
+                                       zend_hash_destroy(&r->range.values);
+                                       break;
+
+                               case PHP_HTTP_RANGE_ERR:
+                                       if (php_http_env_got_request_header(ZEND_STRL("If-Range"), request TSRMLS_CC)) {
+                                               r->range.status = PHP_HTTP_RANGE_NO;
+                                               zend_hash_destroy(&r->range.values);
+                                       } else {
+                                               r->done = 1;
+                                               zend_hash_destroy(&r->range.values);
+                                               if (SUCCESS != r->ops->set_status(r, 416)) {
+                                                       return FAILURE;
+                                               }
+                                               if (SUCCESS != r->ops->set_header(r, "Content-Range: bytes */%zu", r->content.length)) {
+                                                       return FAILURE;
+                                               }
+                                       }
+                                       break;
+
+                               case PHP_HTTP_RANGE_OK:
+                                       if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(r->options, ZEND_STRL("If-Range"), request TSRMLS_CC)
+                                       ||      PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("If-Range"), request TSRMLS_CC)
+                                       ) {
+                                               r->range.status = PHP_HTTP_RANGE_NO;
+                                               zend_hash_destroy(&r->range.values);
+                                               break;
+                                       }
+                                       if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(r->options, ZEND_STRL("If-Match"), request TSRMLS_CC)
+                                       ||      PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("If-Unmodified-Since"), request TSRMLS_CC)
+                                       ||      PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("Unless-Modified-Since"), request TSRMLS_CC)
+                                       ) {
+                                               r->done = 1;
+                                               zend_hash_destroy(&r->range.values);
+                                               if (SUCCESS != r->ops->set_status(r, 412)) {
+                                                       return FAILURE;
+                                               }
+                                               break;
+                                       }
+
+                                       break;
+                       }
+               }
+       }
+
+       if (SUCCESS != php_http_env_response_send_head(r, request)) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to send response headers");
+               return FAILURE;
+       }
+
+       if (SUCCESS != php_http_env_response_send_body(r)) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to send response body");
+               return FAILURE;
+       }
+
+       if (SUCCESS != r->ops->finish(r)) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to finish response");
+               return FAILURE;
+       }
+
+       return SUCCESS;
+}
+
+static long php_http_env_response_sapi_get_status(php_http_env_response_t *r)
+{
+       TSRMLS_FETCH_FROM_CTX(r->ts);
+
+       return php_http_env_get_response_code(TSRMLS_C);
+}
+static ZEND_RESULT_CODE php_http_env_response_sapi_set_status(php_http_env_response_t *r, long http_code)
+{
+       TSRMLS_FETCH_FROM_CTX(r->ts);
+
+       return php_http_env_set_response_code(http_code TSRMLS_CC);
+}
+static ZEND_RESULT_CODE php_http_env_response_sapi_set_protocol_version(php_http_env_response_t *r, php_http_version_t *v)
+{
+       TSRMLS_FETCH_FROM_CTX(r->ts);
+
+       return php_http_env_set_response_protocol_version(v TSRMLS_CC);
+}
+static ZEND_RESULT_CODE php_http_env_response_sapi_set_header(php_http_env_response_t *r, const char *fmt, ...)
+{
+       ZEND_RESULT_CODE ret;
+       va_list args;
+       TSRMLS_FETCH_FROM_CTX(r->ts);
+
+       va_start(args, fmt);
+       ret = php_http_env_set_response_header_va(0, 1, fmt, args TSRMLS_CC);
+       va_end(args);
+
+       return ret;
+}
+static ZEND_RESULT_CODE php_http_env_response_sapi_add_header(php_http_env_response_t *r, const char *fmt, ...)
+{
+       ZEND_RESULT_CODE ret;
+       va_list args;
+       TSRMLS_FETCH_FROM_CTX(r->ts);
+
+       va_start(args, fmt);
+       ret = php_http_env_set_response_header_va(0, 0, fmt, args TSRMLS_CC);
+       va_end(args);
+
+       return ret;
+}
+static ZEND_RESULT_CODE php_http_env_response_sapi_del_header(php_http_env_response_t *r, const char *header_str, size_t header_len)
+{
+       TSRMLS_FETCH_FROM_CTX(r->ts);
+
+       return php_http_env_set_response_header_value(0, header_str, header_len, NULL, 1 TSRMLS_CC);
+}
+static ZEND_RESULT_CODE php_http_env_response_sapi_write(php_http_env_response_t *r, const char *data_str, size_t data_len)
+{
+       TSRMLS_FETCH_FROM_CTX(r->ts);
+
+       if (0 < PHPWRITE(data_str, data_len)) {
+               return SUCCESS;
+       }
+       return FAILURE;
+}
+static ZEND_RESULT_CODE php_http_env_response_sapi_flush(php_http_env_response_t *r)
+{
+       TSRMLS_FETCH_FROM_CTX(r->ts);
+
+#if PHP_VERSION_ID >= 50400
+       if (php_output_get_level(TSRMLS_C)) {
+               php_output_flush_all(TSRMLS_C);
+       }
+       if (!(php_output_get_status(TSRMLS_C) & PHP_OUTPUT_IMPLICITFLUSH)) {
+               sapi_flush(TSRMLS_C);
+       }
+#else
+       php_end_ob_buffer(1, 1 TSRMLS_CC);
+       sapi_flush(TSRMLS_C);
+#endif
+
+       return SUCCESS;
+}
+static ZEND_RESULT_CODE php_http_env_response_sapi_finish(php_http_env_response_t *r)
+{
+       return SUCCESS;
+}
+
+static php_http_env_response_ops_t php_http_env_response_sapi_ops = {
+       NULL,
+       NULL,
+       php_http_env_response_sapi_get_status,
+       php_http_env_response_sapi_set_status,
+       php_http_env_response_sapi_set_protocol_version,
+       php_http_env_response_sapi_set_header,
+       php_http_env_response_sapi_add_header,
+       php_http_env_response_sapi_del_header,
+       php_http_env_response_sapi_write,
+       php_http_env_response_sapi_flush,
+       php_http_env_response_sapi_finish
+};
+
+php_http_env_response_ops_t *php_http_env_response_get_sapi_ops(void)
+{
+       return &php_http_env_response_sapi_ops;
+}
+
+typedef struct php_http_env_response_stream_ctx {
+       HashTable header;
+       php_http_version_t version;
+       long status_code;
+
+       php_stream *stream;
+       php_stream_filter *chunked_filter;
+       php_http_message_t *request;
+
+       unsigned started:1;
+       unsigned finished:1;
+       unsigned chunked:1;
+} php_http_env_response_stream_ctx_t;
+
+static ZEND_RESULT_CODE php_http_env_response_stream_init(php_http_env_response_t *r, void *init_arg)
+{
+       php_http_env_response_stream_ctx_t *ctx;
+       size_t buffer_size = 0x1000;
+       TSRMLS_FETCH_FROM_CTX(r->ts);
+
+       ctx = ecalloc(1, sizeof(*ctx));
+
+       ctx->stream = init_arg;
+       if (!ctx->stream || SUCCESS != zend_list_addref(ctx->stream->rsrc_id)) {
+               efree(ctx);
+               return FAILURE;
+       }
+       php_stream_set_option(ctx->stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_FULL, &buffer_size);
+       zend_hash_init(&ctx->header, 0, NULL, ZVAL_PTR_DTOR, 0);
+       php_http_version_init(&ctx->version, 1, 1 TSRMLS_CC);
+       ctx->status_code = 200;
+       ctx->chunked = 1;
+       ctx->request = get_request(r->options TSRMLS_CC);
+
+       /* 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) {
+               ctx->version.minor = 0;
+       }
+
+       r->ctx = ctx;
+
+       return SUCCESS;
+}
+static void php_http_env_response_stream_dtor(php_http_env_response_t *r)
+{
+       php_http_env_response_stream_ctx_t *ctx = r->ctx;
+       TSRMLS_FETCH_FROM_CTX(r->ts);
+
+       if (ctx->chunked_filter) {
+               ctx->chunked_filter = php_stream_filter_remove(ctx->chunked_filter, 1 TSRMLS_CC);
+       }
+       zend_hash_destroy(&ctx->header);
+       zend_list_delete(ctx->stream->rsrc_id);
+       efree(ctx);
+       r->ctx = NULL;
+}
+static void php_http_env_response_stream_header(php_http_env_response_stream_ctx_t *ctx, HashTable *header, php_http_buffer_t *buf TSRMLS_DC)
+{
+       HashPosition pos;
+       zval **val;
+
+       FOREACH_HASH_VAL(pos, header, val) {
+               if (Z_TYPE_PP(val) == IS_ARRAY) {
+                       php_http_env_response_stream_header(ctx, Z_ARRVAL_PP(val), buf TSRMLS_CC);
+               } else {
+                       zval *tmp = php_http_ztyp(IS_STRING, *val);
+
+                       if (ctx->chunked) {
+                               /* disable chunked transfer encoding if we've got an explicit content-length */
+                               if (!strncasecmp(Z_STRVAL_P(tmp), "Content-Length:", lenof("Content-Length:"))) {
+                                       ctx->chunked = 0;
+                               }
+                       }
+                       php_http_buffer_append(buf, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
+                       php_http_buffer_appends(buf, PHP_HTTP_CRLF);
+                       zval_ptr_dtor(&tmp);
+               }
+       }
+}
+static ZEND_RESULT_CODE php_http_env_response_stream_start(php_http_env_response_stream_ctx_t *ctx TSRMLS_DC)
+{
+       php_http_buffer_t header_buf;
+
+       if (ctx->started || ctx->finished) {
+               return FAILURE;
+       }
+
+       php_http_buffer_init(&header_buf);
+       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) {
+               ctx->chunked = 0;
+       } else if (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")) {
+               ctx->chunked = 0;
+       }
+
+       php_http_env_response_stream_header(ctx, &ctx->header, &header_buf TSRMLS_CC);
+
+       /* enable chunked transfer encoding */
+       if (ctx->chunked) {
+               php_http_buffer_appends(&header_buf, "Transfer-Encoding: chunked" PHP_HTTP_CRLF);
+       }
+       php_http_buffer_appends(&header_buf, PHP_HTTP_CRLF);
+
+       if (header_buf.used == php_stream_write(ctx->stream, header_buf.data, header_buf.used)) {
+               ctx->started = 1;
+       }
+       php_http_buffer_dtor(&header_buf);
+       php_stream_flush(ctx->stream);
+
+       if (ctx->chunked) {
+               ctx->chunked_filter = php_stream_filter_create("http.chunked_encode", NULL, 0 TSRMLS_CC);
+               php_stream_filter_append(&ctx->stream->writefilters, ctx->chunked_filter);
+       }
+
+       return ctx->started ? SUCCESS : FAILURE;
+}
+static long php_http_env_response_stream_get_status(php_http_env_response_t *r)
+{
+       php_http_env_response_stream_ctx_t *ctx = r->ctx;
+
+       return ctx->status_code;
+}
+static ZEND_RESULT_CODE php_http_env_response_stream_set_status(php_http_env_response_t *r, long http_code)
+{
+       php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
+
+       if (stream_ctx->started || stream_ctx->finished) {
+               return FAILURE;
+       }
+
+       stream_ctx->status_code = http_code;
+
+       return SUCCESS;
+}
+static ZEND_RESULT_CODE php_http_env_response_stream_set_protocol_version(php_http_env_response_t *r, php_http_version_t *v)
+{
+       php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
+
+       if (stream_ctx->started || stream_ctx->finished) {
+               return FAILURE;
+       }
+
+       memcpy(&stream_ctx->version, v, sizeof(stream_ctx->version));
+
+       return SUCCESS;
+}
+static ZEND_RESULT_CODE php_http_env_response_stream_set_header_ex(php_http_env_response_t *r, zend_bool replace, const char *fmt, va_list argv)
+{
+       php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
+       char *header_end, *header_str = NULL;
+       size_t header_len = 0;
+       zval *zheader, **zheader_ptr;
+
+       if (stream_ctx->started || stream_ctx->finished) {
+               return FAILURE;
+       }
+
+       header_len = vspprintf(&header_str, 0, fmt, argv);
+
+       if (!(header_end = strchr(header_str, ':'))) {
+               efree(header_str);
+               return FAILURE;
+       }
+
+       *header_end = '\0';
+
+       if (!replace && (SUCCESS == zend_hash_find(&stream_ctx->header, header_str, header_end - header_str + 1, (void *) &zheader_ptr))) {
+               convert_to_array(*zheader_ptr);
+               *header_end = ':';
+               return add_next_index_stringl(*zheader_ptr, header_str, header_len, 0);
+       } else {
+               MAKE_STD_ZVAL(zheader);
+               ZVAL_STRINGL(zheader, header_str, header_len, 0);
+
+               if (SUCCESS != zend_hash_update(&stream_ctx->header, header_str, header_end - header_str + 1, (void *) &zheader, sizeof(zval *), NULL)) {
+                       zval_ptr_dtor(&zheader);
+                       return FAILURE;
+               }
+
+               *header_end = ':';
+               return SUCCESS;
+       }
+}
+static ZEND_RESULT_CODE php_http_env_response_stream_set_header(php_http_env_response_t *r, const char *fmt, ...)
+{
+       ZEND_RESULT_CODE ret;
+       va_list argv;
+
+       va_start(argv, fmt);
+       ret = php_http_env_response_stream_set_header_ex(r, 1, fmt, argv);
+       va_end(argv);
+
+       return ret;
+}
+static ZEND_RESULT_CODE php_http_env_response_stream_add_header(php_http_env_response_t *r, const char *fmt, ...)
+{
+       ZEND_RESULT_CODE ret;
+       va_list argv;
+
+       va_start(argv, fmt);
+       ret = php_http_env_response_stream_set_header_ex(r, 0, fmt, argv);
+       va_end(argv);
+
+       return ret;
+}
+static ZEND_RESULT_CODE php_http_env_response_stream_del_header(php_http_env_response_t *r, const char *header_str, size_t header_len)
+{
+       php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
+
+       if (stream_ctx->started || stream_ctx->finished) {
+               return FAILURE;
+       }
+
+       zend_hash_del(&stream_ctx->header, header_str, header_len + 1);
+       return SUCCESS;
+}
+static ZEND_RESULT_CODE php_http_env_response_stream_write(php_http_env_response_t *r, const char *data_str, size_t data_len)
+{
+       php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
+       TSRMLS_FETCH_FROM_CTX(r->ts);
+
+       if (stream_ctx->finished) {
+               return FAILURE;
+       }
+       if (!stream_ctx->started) {
+               if (SUCCESS != php_http_env_response_stream_start(stream_ctx TSRMLS_CC)) {
+                       return FAILURE;
+               }
+       }
+
+       if (data_len != php_stream_write(stream_ctx->stream, data_str, data_len)) {
+               return FAILURE;
+       }
+
+       return SUCCESS;
+}
+static ZEND_RESULT_CODE php_http_env_response_stream_flush(php_http_env_response_t *r)
+{
+       php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
+       TSRMLS_FETCH_FROM_CTX(r->ts);
+
+       if (stream_ctx->finished) {
+               return FAILURE;
+       }
+       if (!stream_ctx->started) {
+               if (SUCCESS != php_http_env_response_stream_start(stream_ctx TSRMLS_CC)) {
+                       return FAILURE;
+               }
+       }
+
+       return php_stream_flush(stream_ctx->stream);
+}
+static ZEND_RESULT_CODE php_http_env_response_stream_finish(php_http_env_response_t *r)
+{
+       php_http_env_response_stream_ctx_t *ctx = r->ctx;
+       TSRMLS_FETCH_FROM_CTX(r->ts);
+
+       if (ctx->finished) {
+               return FAILURE;
+       }
+       if (!ctx->started) {
+               if (SUCCESS != php_http_env_response_stream_start(ctx TSRMLS_CC)) {
+                       return FAILURE;
+               }
+       }
+
+       php_stream_flush(ctx->stream);
+       if (ctx->chunked && ctx->chunked_filter) {
+               php_stream_filter_flush(ctx->chunked_filter, 1);
+               ctx->chunked_filter = php_stream_filter_remove(ctx->chunked_filter, 1 TSRMLS_CC);
+       }
+
+       ctx->finished = 1;
+
+       return SUCCESS;
+}
+
+static php_http_env_response_ops_t php_http_env_response_stream_ops = {
+       php_http_env_response_stream_init,
+       php_http_env_response_stream_dtor,
+       php_http_env_response_stream_get_status,
+       php_http_env_response_stream_set_status,
+       php_http_env_response_stream_set_protocol_version,
+       php_http_env_response_stream_set_header,
+       php_http_env_response_stream_add_header,
+       php_http_env_response_stream_del_header,
+       php_http_env_response_stream_write,
+       php_http_env_response_stream_flush,
+       php_http_env_response_stream_finish
+};
+
+php_http_env_response_ops_t *php_http_env_response_get_stream_ops(void)
+{
+       return &php_http_env_response_stream_ops;
+}
+
+#define PHP_HTTP_ENV_RESPONSE_OBJECT_INIT(obj) \
+       do { \
+               if (!obj->message) { \
+                       obj->message = php_http_message_init_env(NULL, PHP_HTTP_RESPONSE TSRMLS_CC); \
+               } \
+       } while (0)
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse___construct, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvResponse, __construct)
+{
+       php_http_message_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       php_http_expect(obj->message = php_http_message_init_env(obj->message, PHP_HTTP_RESPONSE TSRMLS_CC), unexpected_val, return);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse___invoke, 0, 0, 1)
+       ZEND_ARG_INFO(0, ob_string)
+       ZEND_ARG_INFO(0, ob_flags)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvResponse, __invoke)
+{
+       char *ob_str;
+       int ob_len;
+       long ob_flags = 0;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &ob_str, &ob_len, &ob_flags)) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_ENV_RESPONSE_OBJECT_INIT(obj);
+
+               if (!obj->body) {
+                       php_http_message_object_init_body_object(obj);
+               }
+               php_http_message_body_append(obj->message->body, ob_str, ob_len);
+#if PHP_VERSION_ID >= 50400
+               if (ob_flags & PHP_OUTPUT_HANDLER_CLEAN) {
+                       php_stream_truncate_set_size(php_http_message_body_stream(obj->message->body), 0);
+               }
+               RETURN_TRUE;
+#else
+               RETURN_EMPTY_STRING();
+#endif
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setEnvRequest, 0, 0, 1)
+       ZEND_ARG_OBJ_INFO(0, env_request, http\\Message, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvResponse, setEnvRequest)
+{
+       zval *env_req = NULL;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|O", &env_req, php_http_message_class_entry), invalid_arg, return);
+
+       set_option(getThis(), ZEND_STRL("request"), IS_OBJECT, env_req, 0 TSRMLS_CC);
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setContentType, 0, 0, 1)
+       ZEND_ARG_INFO(0, content_type)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvResponse, setContentType)
+{
+       char *ct_str = NULL;
+       int ct_len = 0;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &ct_str, &ct_len), invalid_arg, return);
+
+       set_option(getThis(), ZEND_STRL("contentType"), IS_STRING, ct_str, ct_len TSRMLS_CC);
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setContentDisposition, 0, 0, 1)
+       ZEND_ARG_ARRAY_INFO(0, disposition_params, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvResponse, setContentDisposition)
+{
+       zval *zdisposition;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &zdisposition), invalid_arg, return);
+
+       zend_update_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("contentDisposition"), zdisposition TSRMLS_CC);
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setContentEncoding, 0, 0, 1)
+       ZEND_ARG_INFO(0, content_encoding)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvResponse, setContentEncoding)
+{
+       long ce;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &ce), invalid_arg, return);
+
+       set_option(getThis(), ZEND_STRL("contentEncoding"), IS_LONG, &ce, 0 TSRMLS_CC);
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setCacheControl, 0, 0, 1)
+       ZEND_ARG_INFO(0, cache_control)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvResponse, setCacheControl)
+{
+       char *cc_str = NULL;
+       int cc_len = 0;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &cc_str, &cc_len), invalid_arg, return);
+
+       set_option(getThis(), ZEND_STRL("cacheControl"), IS_STRING, cc_str, cc_len TSRMLS_CC);
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setLastModified, 0, 0, 1)
+       ZEND_ARG_INFO(0, last_modified)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvResponse, setLastModified)
+{
+       long last_modified;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &last_modified), invalid_arg, return);
+
+       set_option(getThis(), ZEND_STRL("lastModified"), IS_LONG, &last_modified, 0 TSRMLS_CC);
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_isCachedByLastModified, 0, 0, 0)
+       ZEND_ARG_INFO(0, header_name)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvResponse, isCachedByLastModified)
+{
+       char *header_name_str = NULL;
+       int header_name_len = 0;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) {
+               if (!header_name_str || !header_name_len) {
+                       header_name_str = "If-Modified-Since";
+                       header_name_len = lenof("If-Modified-Since");
+               }
+
+               RETURN_LONG(php_http_env_is_response_cached_by_last_modified(getThis(), header_name_str, header_name_len, get_request(getThis() TSRMLS_CC) TSRMLS_CC));
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setEtag, 0, 0, 1)
+       ZEND_ARG_INFO(0, etag)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvResponse, setEtag)
+{
+       char *etag_str = NULL;
+       int etag_len = 0;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &etag_str, &etag_len), invalid_arg, return);
+
+       set_option(getThis(), ZEND_STRL("etag"), IS_STRING, etag_str, etag_len TSRMLS_CC);
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_isCachedByEtag, 0, 0, 0)
+       ZEND_ARG_INFO(0, header_name)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvResponse, isCachedByEtag)
+{
+       char *header_name_str = NULL;
+       int header_name_len = 0;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) {
+               if (!header_name_str || !header_name_len) {
+                       header_name_str = "If-None-Match";
+                       header_name_len = lenof("If-None-Match");
+               }
+               RETURN_LONG(php_http_env_is_response_cached_by_etag(getThis(), header_name_str, header_name_len, get_request(getThis() TSRMLS_CC) TSRMLS_CC));
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setThrottleRate, 0, 0, 1)
+       ZEND_ARG_INFO(0, chunk_size)
+       ZEND_ARG_INFO(0, delay)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvResponse, setThrottleRate)
+{
+       long chunk_size;
+       double delay = 1;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|d", &chunk_size, &delay), invalid_arg, return);
+
+       set_option(getThis(), ZEND_STRL("throttleDelay"), IS_DOUBLE, &delay, 0 TSRMLS_CC);
+       set_option(getThis(), ZEND_STRL("throttleChunk"), IS_LONG, &chunk_size, 0 TSRMLS_CC);
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setCookie, 0, 0, 1)
+       ZEND_ARG_INFO(0, cookie)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvResponse, setCookie)
+{
+       zval *zcookie_new;
+       zend_error_handling zeh;
+       php_http_cookie_list_t *list = NULL;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zcookie_new), invalid_arg, return);
+
+       zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC);
+       switch (Z_TYPE_P(zcookie_new)) {
+       case IS_OBJECT:
+               if (instanceof_function(Z_OBJCE_P(zcookie_new), php_http_cookie_class_entry TSRMLS_CC)) {
+                       Z_ADDREF_P(zcookie_new);
+                       break;
+               }
+               /* no break */
+       case IS_ARRAY:
+               list = php_http_cookie_list_from_struct(NULL, zcookie_new TSRMLS_CC);
+               MAKE_STD_ZVAL(zcookie_new);
+               ZVAL_OBJVAL(zcookie_new, php_http_cookie_object_new_ex(php_http_cookie_class_entry, list, NULL TSRMLS_CC), 0);
+               break;
+
+       default:
+               zcookie_new = php_http_ztyp(IS_STRING, zcookie_new);
+               list = php_http_cookie_list_parse(NULL, Z_STRVAL_P(zcookie_new), Z_STRLEN_P(zcookie_new), 0, NULL TSRMLS_CC);
+               zval_ptr_dtor(&zcookie_new);
+               MAKE_STD_ZVAL(zcookie_new);
+               ZVAL_OBJVAL(zcookie_new, php_http_cookie_object_new_ex(php_http_cookie_class_entry, list, NULL TSRMLS_CC), 0);
+       }
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+
+       set_cookie(getThis(), zcookie_new TSRMLS_CC);
+       zval_ptr_dtor(&zcookie_new);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_send, 0, 0, 0)
+       ZEND_ARG_INFO(0, stream)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpEnvResponse, send)
+{
+       zval *zstream = NULL;
+       php_stream *s = NULL;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &zstream)) {
+               /* first flush the output layer to avoid conflicting headers and output;
+                * also, ob_start($thisEnvResponse) might have been called */
+#if PHP_VERSION_ID >= 50400
+               php_output_end_all(TSRMLS_C);
+#else
+               php_end_ob_buffers(1 TSRMLS_CC);
+#endif
+
+               if (zstream) {
+                       php_http_env_response_t *r;
+
+                       php_stream_from_zval(s, &zstream);
+                       r = php_http_env_response_init(NULL, getThis(), php_http_env_response_get_stream_ops(), s TSRMLS_CC);
+                       if (!r) {
+                               RETURN_FALSE;
+                       }
+
+                       RETVAL_BOOL(SUCCESS == php_http_env_response_send(r));
+                       php_http_env_response_free(&r);
+               } else {
+                       php_http_env_response_t r;
+
+                       if (!php_http_env_response_init(&r, getThis(), NULL, NULL TSRMLS_CC)) {
+                               RETURN_FALSE;
+                       }
+
+                       RETVAL_BOOL(SUCCESS == php_http_env_response_send(&r));
+                       php_http_env_response_dtor(&r);
+               }
+       }
+}
+
+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, __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)
+       PHP_ME(HttpEnvResponse, setContentType,          ai_HttpEnvResponse_setContentType,          ZEND_ACC_PUBLIC)
+       PHP_ME(HttpEnvResponse, setContentDisposition,   ai_HttpEnvResponse_setContentDisposition,   ZEND_ACC_PUBLIC)
+       PHP_ME(HttpEnvResponse, setContentEncoding,      ai_HttpEnvResponse_setContentEncoding,      ZEND_ACC_PUBLIC)
+       PHP_ME(HttpEnvResponse, setCacheControl,         ai_HttpEnvResponse_setCacheControl,         ZEND_ACC_PUBLIC)
+       PHP_ME(HttpEnvResponse, setLastModified,         ai_HttpEnvResponse_setLastModified,         ZEND_ACC_PUBLIC)
+       PHP_ME(HttpEnvResponse, isCachedByLastModified,  ai_HttpEnvResponse_isCachedByLastModified,  ZEND_ACC_PUBLIC)
+       PHP_ME(HttpEnvResponse, setEtag,                 ai_HttpEnvResponse_setEtag,                 ZEND_ACC_PUBLIC)
+       PHP_ME(HttpEnvResponse, isCachedByEtag,          ai_HttpEnvResponse_isCachedByEtag,          ZEND_ACC_PUBLIC)
+       PHP_ME(HttpEnvResponse, setThrottleRate,         ai_HttpEnvResponse_setThrottleRate,         ZEND_ACC_PUBLIC)
+       PHP_ME(HttpEnvResponse, send,                    ai_HttpEnvResponse_send,                    ZEND_ACC_PUBLIC)
+       EMPTY_FUNCTION_ENTRY
+};
+
+zend_class_entry *php_http_env_response_class_entry;
+
+PHP_MINIT_FUNCTION(http_env_response)
+{
+       zend_class_entry ce = {0};
+
+       INIT_NS_CLASS_ENTRY(ce, "http\\Env", "Response", php_http_env_response_methods);
+       php_http_env_response_class_entry = zend_register_internal_class_ex(&ce, php_http_message_class_entry, NULL TSRMLS_CC);
+
+       zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CONTENT_ENCODING_NONE"), PHP_HTTP_CONTENT_ENCODING_NONE TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CONTENT_ENCODING_GZIP"), PHP_HTTP_CONTENT_ENCODING_GZIP TSRMLS_CC);
+
+       zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_NO"), PHP_HTTP_CACHE_NO TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_HIT"), PHP_HTTP_CACHE_HIT TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_MISS"), PHP_HTTP_CACHE_MISS TSRMLS_CC);
+
+       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("request"), ZEND_ACC_PROTECTED TSRMLS_CC);
+       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("cookies"), ZEND_ACC_PROTECTED TSRMLS_CC);
+       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentType"), ZEND_ACC_PROTECTED TSRMLS_CC);
+       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentDisposition"), ZEND_ACC_PROTECTED TSRMLS_CC);
+       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentEncoding"), ZEND_ACC_PROTECTED TSRMLS_CC);
+       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("cacheControl"), ZEND_ACC_PROTECTED TSRMLS_CC);
+       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("etag"), ZEND_ACC_PROTECTED TSRMLS_CC);
+       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("lastModified"), ZEND_ACC_PROTECTED TSRMLS_CC);
+       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("throttleDelay"), ZEND_ACC_PROTECTED TSRMLS_CC);
+       zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("throttleChunk"), ZEND_ACC_PROTECTED TSRMLS_CC);
+
+       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_env_response.h b/src/php_http_env_response.h
new file mode 100644 (file)
index 0000000..e6a112f
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_ENV_RESPONSE_H
+#define PHP_HTTP_ENV_RESPONSE_H
+
+typedef struct php_http_env_response php_http_env_response_t;
+
+typedef struct php_http_env_response_ops {
+       ZEND_RESULT_CODE (*init)(php_http_env_response_t *r, void *arg);
+       void (*dtor)(php_http_env_response_t *r);
+       long (*get_status)(php_http_env_response_t *r);
+       ZEND_RESULT_CODE (*set_status)(php_http_env_response_t *r, long http_code);
+       ZEND_RESULT_CODE (*set_protocol_version)(php_http_env_response_t *r, php_http_version_t *v);
+       ZEND_RESULT_CODE (*set_header)(php_http_env_response_t *r, const char *fmt, ...);
+       ZEND_RESULT_CODE (*add_header)(php_http_env_response_t *r, const char *fmt, ...);
+       ZEND_RESULT_CODE (*del_header)(php_http_env_response_t *r, const char *header_str, size_t header_len);
+       ZEND_RESULT_CODE (*write)(php_http_env_response_t *r, const char *data_str, size_t data_len);
+       ZEND_RESULT_CODE (*flush)(php_http_env_response_t *r);
+       ZEND_RESULT_CODE (*finish)(php_http_env_response_t *r);
+} php_http_env_response_ops_t;
+
+PHP_HTTP_API php_http_env_response_ops_t *php_http_env_response_get_sapi_ops(void);
+PHP_HTTP_API php_http_env_response_ops_t *php_http_env_response_get_stream_ops(void);
+
+struct php_http_env_response {
+       void *ctx;
+       php_http_env_response_ops_t *ops;
+
+       php_http_cookie_list_t *cookies;
+       php_http_buffer_t *buffer;
+       zval *options;
+
+       struct {
+               size_t chunk;
+               double delay;
+       } throttle;
+
+       struct {
+               php_http_range_status_t status;
+               HashTable values;
+               char boundary[32];
+       } range;
+
+       struct {
+               size_t length;
+               char *type;
+               char *encoding;
+
+               php_http_encoding_stream_t *encoder;
+       } content;
+
+       zend_bool done;
+
+#ifdef ZTS
+       void ***ts;
+#endif
+};
+
+PHP_HTTP_API php_http_env_response_t *php_http_env_response_init(php_http_env_response_t *r, zval *options, php_http_env_response_ops_t *ops, void *ops_ctx TSRMLS_DC);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_env_response_send(php_http_env_response_t *r);
+PHP_HTTP_API void php_http_env_response_dtor(php_http_env_response_t *r);
+PHP_HTTP_API void php_http_env_response_free(php_http_env_response_t **r);
+
+PHP_HTTP_API php_http_cache_status_t php_http_env_is_response_cached_by_etag(zval *options, const char *header_str, size_t header_len, php_http_message_t *request TSRMLS_DC);
+PHP_HTTP_API php_http_cache_status_t php_http_env_is_response_cached_by_last_modified(zval *options, const char *header_str, size_t header_len, php_http_message_t *request TSRMLS_DC);
+
+PHP_HTTP_API zend_class_entry *php_http_env_response_class_entry;
+PHP_MINIT_FUNCTION(http_env_response);
+
+#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_etag.c b/src/php_http_etag.c
new file mode 100644 (file)
index 0000000..3604ad8
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+#ifdef PHP_HTTP_HAVE_HASH
+#      include "php_hash.h"
+#endif
+
+#include <ext/standard/crc32.h>
+#include <ext/standard/sha1.h>
+#include <ext/standard/md5.h>
+
+php_http_etag_t *php_http_etag_init(const char *mode TSRMLS_DC)
+{
+       void *ctx;
+       php_http_etag_t *e;
+
+       if (mode && (!strcasecmp(mode, "crc32b"))) {
+               ctx = emalloc(sizeof(uint));
+               *((uint *) ctx) = ~0;
+       } else if (mode && !strcasecmp(mode, "sha1")) {
+               PHP_SHA1Init(ctx = emalloc(sizeof(PHP_SHA1_CTX)));
+       } else if (mode && !strcasecmp(mode, "md5")) {
+               PHP_MD5Init(ctx = emalloc(sizeof(PHP_MD5_CTX)));
+       } else {
+#ifdef PHP_HTTP_HAVE_HASH
+               const php_hash_ops *eho = NULL;
+
+               if (mode && (eho = php_hash_fetch_ops(mode, strlen(mode)))) {
+                       ctx = emalloc(eho->context_size);
+                       eho->hash_init(ctx);
+               } else
+#endif
+               return NULL;
+       }
+
+       e = emalloc(sizeof(*e));
+       e->ctx = ctx;
+       e->mode = estrdup(mode);
+       TSRMLS_SET_CTX(e->ts);
+
+       return e;
+}
+
+char *php_http_etag_finish(php_http_etag_t *e)
+{
+       unsigned char digest[128] = {0};
+       char *etag = NULL;
+
+       if (!strcasecmp(e->mode, "crc32b")) {
+               unsigned char buf[4];
+
+               *((uint *) e->ctx) = ~*((uint *) e->ctx);
+#ifdef WORDS_BIGENDIAN
+               etag = php_http_etag_digest((unsigned char *) e->ctx, 4);
+#else
+               buf[0] = ((unsigned char *) e->ctx)[3];
+               buf[1] = ((unsigned char *) e->ctx)[2];
+               buf[2] = ((unsigned char *) e->ctx)[1];
+               buf[3] = ((unsigned char *) e->ctx)[0];
+               etag = php_http_etag_digest(buf, 4);
+#endif
+       } else if ((!strcasecmp(e->mode, "sha1"))) {
+               PHP_SHA1Final(digest, e->ctx);
+               etag = php_http_etag_digest(digest, 20);
+       } else if ((!strcasecmp(e->mode, "md5"))) {
+               PHP_MD5Final(digest, e->ctx);
+               etag = php_http_etag_digest(digest, 16);
+       } else {
+#ifdef PHP_HTTP_HAVE_HASH
+               const php_hash_ops *eho = NULL;
+
+               if (e->mode && (eho = php_hash_fetch_ops(e->mode, strlen(e->mode)))) {
+                       eho->hash_final(digest, e->ctx);
+                       etag = php_http_etag_digest(digest, eho->digest_size);
+               }
+#endif
+       }
+
+       efree(e->ctx);
+       efree(e->mode);
+       efree(e);
+
+       return etag;
+}
+
+size_t php_http_etag_update(php_http_etag_t *e, const char *data_ptr, size_t data_len)
+{
+       if (!strcasecmp(e->mode, "crc32b")) {
+               uint i, c = *((uint *) e->ctx);
+               for (i = 0; i < data_len; ++i) {
+                       CRC32(c, data_ptr[i]);
+               }
+               *((uint *) e->ctx) = c;
+       } else if ((!strcasecmp(e->mode, "sha1"))) {
+               PHP_SHA1Update(e->ctx, (const unsigned char *) data_ptr, data_len);
+       } else if ((!strcasecmp(e->mode, "md5"))) {
+               PHP_MD5Update(e->ctx, (const unsigned char *) data_ptr, data_len);
+       } else {
+#ifdef PHP_HTTP_HAVE_HASH
+               const php_hash_ops *eho = NULL;
+
+               if (e->mode && (eho = php_hash_fetch_ops(e->mode, strlen(e->mode)))) {
+                       eho->hash_update(e->ctx, (const unsigned char *) data_ptr, data_len);
+               }
+#endif
+       }
+
+       return data_len;
+}
+
+
+/*
+ * 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_etag.h b/src/php_http_etag.h
new file mode 100644 (file)
index 0000000..bf6cf49
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_ETAG_H
+#define PHP_HTTP_ETAG_H
+
+typedef struct php_http_etag {
+       void *ctx;
+       char *mode;
+
+#ifdef ZTS
+       void ***ts;
+#endif
+} php_http_etag_t;
+
+PHP_HTTP_API php_http_etag_t *php_http_etag_init(const char *mode TSRMLS_DC);
+PHP_HTTP_API size_t php_http_etag_update(php_http_etag_t *e, const char *data_ptr, size_t data_len);
+PHP_HTTP_API char *php_http_etag_finish(php_http_etag_t *e);
+
+static inline char *php_http_etag_digest(const unsigned char *digest, int len)
+{
+       static const char hexdigits[17] = "0123456789abcdef";
+       int i;
+       char *hex = emalloc(len * 2 + 1);
+       char *ptr = hex;
+
+       for (i = 0; i < len; ++i) {
+               *ptr++ = hexdigits[digest[i] >> 4];
+               *ptr++ = hexdigits[digest[i] & 0xF];
+       }
+       *ptr = '\0';
+
+       return hex;
+}
+
+#endif /* PHP_HTTP_ETAG_H */
+
+/*
+ * 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_exception.c b/src/php_http_exception.c
new file mode 100644 (file)
index 0000000..25a33e6
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+#include <ext/spl/spl_exceptions.h>
+
+#ifndef PHP_HTTP_DBG_EXCEPTIONS
+#      define PHP_HTTP_DBG_EXCEPTIONS 0
+#endif
+
+#if PHP_HTTP_DBG_EXCEPTIONS
+static void php_http_exception_hook(zval *ex TSRMLS_DC)
+{
+       if (ex) {
+               zval *m = zend_read_property(Z_OBJCE_P(ex), ex, "message", lenof("message"), 0 TSRMLS_CC);
+               fprintf(stderr, "*** Threw exception '%s'\n", Z_STRVAL_P(m));
+       } else {
+               fprintf(stderr, "*** Threw NULL exception\n");
+       }
+}
+#endif
+
+zend_class_entry *php_http_exception_interface_class_entry;
+zend_class_entry *php_http_exception_runtime_class_entry;
+zend_class_entry *php_http_exception_unexpected_val_class_entry;
+zend_class_entry *php_http_exception_bad_method_call_class_entry;
+zend_class_entry *php_http_exception_invalid_arg_class_entry;
+zend_class_entry *php_http_exception_bad_header_class_entry;
+zend_class_entry *php_http_exception_bad_url_class_entry;
+zend_class_entry *php_http_exception_bad_message_class_entry;
+zend_class_entry *php_http_exception_bad_conversion_class_entry;
+zend_class_entry *php_http_exception_bad_querystring_class_entry;
+
+PHP_MINIT_FUNCTION(http_exception)
+{
+       zend_class_entry *cep, ce = {0};
+
+       INIT_NS_CLASS_ENTRY(ce, "http", "Exception", NULL);
+       php_http_exception_interface_class_entry = zend_register_internal_interface(&ce TSRMLS_CC);
+       
+       /*
+        * Would be great to only have a few exceptions and rather more identifying
+        * error codes, but zend_replace_error_handling() does not accept any codes.
+        */
+
+       memset(&ce, 0, sizeof(ce));
+       INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "RuntimeException", NULL);
+       cep = zend_register_internal_class_ex(&ce, spl_ce_RuntimeException, NULL TSRMLS_CC);
+       zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry);
+       php_http_exception_runtime_class_entry = cep;
+
+       memset(&ce, 0, sizeof(ce));
+       INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "UnexpectedValueException", NULL);
+       cep = zend_register_internal_class_ex(&ce, spl_ce_UnexpectedValueException, NULL TSRMLS_CC);
+       zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry);
+       php_http_exception_unexpected_val_class_entry = cep;
+
+       memset(&ce, 0, sizeof(ce));
+       INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "BadMethodCallException", NULL);
+       cep = zend_register_internal_class_ex(&ce, spl_ce_BadMethodCallException, NULL TSRMLS_CC);
+       zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry);
+       php_http_exception_bad_method_call_class_entry = cep;
+
+       memset(&ce, 0, sizeof(ce));
+       INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "InvalidArgumentException", NULL);
+       cep = zend_register_internal_class_ex(&ce, spl_ce_InvalidArgumentException, NULL TSRMLS_CC);
+       zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry);
+       php_http_exception_invalid_arg_class_entry = cep;
+
+       memset(&ce, 0, sizeof(ce));
+       INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "BadHeaderException", NULL);
+       cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException, NULL TSRMLS_CC);
+       zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry);
+       php_http_exception_bad_header_class_entry = cep;
+
+       memset(&ce, 0, sizeof(ce));
+       INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "BadUrlException", NULL);
+       cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException, NULL TSRMLS_CC);
+       zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry);
+       php_http_exception_bad_url_class_entry = cep;
+
+       memset(&ce, 0, sizeof(ce));
+       INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "BadMessageException", NULL);
+       cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException, NULL TSRMLS_CC);
+       zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry);
+       php_http_exception_bad_message_class_entry = cep;
+
+       memset(&ce, 0, sizeof(ce));
+       INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "BadConversionException", NULL);
+       cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException, NULL TSRMLS_CC);
+       zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry);
+       php_http_exception_bad_conversion_class_entry = cep;
+
+       memset(&ce, 0, sizeof(ce));
+       INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "BadQueryStringException", NULL);
+       cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException, NULL TSRMLS_CC);
+       zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry);
+       php_http_exception_bad_querystring_class_entry = cep;
+
+#if PHP_HTTP_DBG_EXCEPTIONS
+       zend_throw_exception_hook = php_http_exception_hook;
+#endif
+       
+       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_exception.h b/src/php_http_exception.h
new file mode 100644 (file)
index 0000000..969a351
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_EXCEPTION_H
+#define PHP_HTTP_EXCEPTION_H
+
+/* short hand for zend_throw_exception_ex */
+#define php_http_throw(e, fmt, ...) \
+       zend_throw_exception_ex(php_http_exception_ ##e## _class_entry, 0 TSRMLS_CC, fmt, __VA_ARGS__)
+
+/* wrap a call with replaced zend_error_handling */
+#define php_http_expect(test, e, fail) \
+       do { \
+               zend_error_handling __zeh; \
+               zend_replace_error_handling(EH_THROW, php_http_exception_ ##e## _class_entry, &__zeh TSRMLS_CC); \
+               if (!(test)) { \
+                       zend_restore_error_handling(&__zeh TSRMLS_CC); \
+                       fail; \
+               } \
+               zend_restore_error_handling(&__zeh TSRMLS_CC); \
+       } while(0)
+
+PHP_HTTP_API zend_class_entry *php_http_exception_interface_class_entry;
+PHP_HTTP_API zend_class_entry *php_http_exception_runtime_class_entry;
+PHP_HTTP_API zend_class_entry *php_http_exception_unexpected_val_class_entry;
+PHP_HTTP_API zend_class_entry *php_http_exception_bad_method_call_class_entry;
+PHP_HTTP_API zend_class_entry *php_http_exception_invalid_arg_class_entry;
+PHP_HTTP_API zend_class_entry *php_http_exception_bad_header_class_entry;
+PHP_HTTP_API zend_class_entry *php_http_exception_bad_url_class_entry;
+PHP_HTTP_API zend_class_entry *php_http_exception_bad_message_class_entry;
+PHP_HTTP_API zend_class_entry *php_http_exception_bad_conversion_class_entry;
+PHP_HTTP_API zend_class_entry *php_http_exception_bad_querystring_class_entry;
+
+PHP_MINIT_FUNCTION(http_exception);
+
+#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_filter.c b/src/php_http_filter.c
new file mode 100644 (file)
index 0000000..b6d967b
--- /dev/null
@@ -0,0 +1,453 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+#ifndef DBG_FILTER
+#      define DBG_FILTER 0
+#endif
+
+PHP_MINIT_FUNCTION(http_filter)
+{
+       php_stream_filter_register_factory("http.*", &php_http_filter_factory TSRMLS_CC);
+       return SUCCESS;
+}
+
+#define PHP_HTTP_FILTER_PARAMS \
+       php_stream *stream, \
+       php_stream_filter *this, \
+       php_stream_bucket_brigade *buckets_in, \
+       php_stream_bucket_brigade *buckets_out, \
+       size_t *bytes_consumed, int flags \
+       TSRMLS_DC
+#define PHP_HTTP_FILTER_OP(filter) \
+       http_filter_op_ ##filter
+#define PHP_HTTP_FILTER_OPS(filter) \
+       php_stream_filter_ops PHP_HTTP_FILTER_OP(filter)
+#define PHP_HTTP_FILTER_DTOR(filter) \
+       http_filter_ ##filter## _dtor
+#define PHP_HTTP_FILTER_DESTRUCTOR(filter) \
+       void PHP_HTTP_FILTER_DTOR(filter)(php_stream_filter *this TSRMLS_DC)
+#define PHP_HTTP_FILTER_FUNC(filter) \
+       http_filter_ ##filter
+#define PHP_HTTP_FILTER_FUNCTION(filter) \
+       php_stream_filter_status_t PHP_HTTP_FILTER_FUNC(filter)(PHP_HTTP_FILTER_PARAMS)
+#define PHP_HTTP_FILTER_BUFFER(filter) \
+       http_filter_ ##filter## _buffer
+
+#define PHP_HTTP_FILTER_IS_CLOSING(stream, flags) \
+       (       (flags & PSFS_FLAG_FLUSH_CLOSE) \
+       ||      php_stream_eof(stream) \
+       || ((stream->ops == &php_stream_temp_ops || stream->ops == &php_stream_memory_ops) && stream->eof) \
+       )
+
+#define NEW_BUCKET(data, length) \
+       { \
+               char *__data; \
+               php_stream_bucket *__buck; \
+               \
+               __data = pemalloc(length, this->is_persistent); \
+               if (!__data) { \
+                       return PSFS_ERR_FATAL; \
+               } \
+               memcpy(__data, data, length); \
+               \
+               __buck = php_stream_bucket_new(stream, __data, length, 1, this->is_persistent TSRMLS_CC); \
+               if (!__buck) { \
+                       pefree(__data, this->is_persistent); \
+                       return PSFS_ERR_FATAL; \
+               } \
+               \
+               php_stream_bucket_append(buckets_out, __buck TSRMLS_CC); \
+       }
+
+typedef struct _http_chunked_decode_filter_buffer_t {
+       php_http_buffer_t       buffer;
+       ulong   hexlen;
+} PHP_HTTP_FILTER_BUFFER(chunked_decode);
+
+typedef php_http_encoding_stream_t PHP_HTTP_FILTER_BUFFER(zlib);
+
+static PHP_HTTP_FILTER_FUNCTION(chunked_decode)
+{
+       int out_avail = 0;
+       php_stream_bucket *ptr, *nxt;
+       PHP_HTTP_FILTER_BUFFER(chunked_decode) *buffer = (PHP_HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract);
+       
+       if (bytes_consumed) {
+               *bytes_consumed = 0;
+       }
+       
+       /* fetch available bucket data */
+       for (ptr = buckets_in->head; ptr; ptr = nxt) {
+               if (bytes_consumed) {
+                       *bytes_consumed += ptr->buflen;
+               }
+
+               if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(PHP_HTTP_BUFFER(buffer), ptr->buf, ptr->buflen)) {
+                       return PSFS_ERR_FATAL;
+               }
+
+               nxt = ptr->next;
+               php_stream_bucket_unlink(ptr TSRMLS_CC);
+               php_stream_bucket_delref(ptr TSRMLS_CC);
+       }
+       
+       if (!php_http_buffer_fix(PHP_HTTP_BUFFER(buffer))) {
+               return PSFS_ERR_FATAL;
+       }
+
+       /* we have data in our buffer */
+       while (PHP_HTTP_BUFFER(buffer)->used) {
+       
+               /* we already know the size of the chunk and are waiting for data */
+               if (buffer->hexlen) {
+               
+                       /* not enough data buffered */
+                       if (PHP_HTTP_BUFFER(buffer)->used < buffer->hexlen) {
+                       
+                               /* flush anyway? */
+                               if (flags & PSFS_FLAG_FLUSH_INC) {
+                               
+                                       /* flush all data (should only be chunk data) */
+                                       out_avail = 1;
+                                       NEW_BUCKET(PHP_HTTP_BUFFER(buffer)->data, PHP_HTTP_BUFFER(buffer)->used);
+                                       
+                                       /* waiting for less data now */
+                                       buffer->hexlen -= PHP_HTTP_BUFFER(buffer)->used;
+                                       /* no more buffered data */
+                                       php_http_buffer_reset(PHP_HTTP_BUFFER(buffer));
+                                       /* break */
+                               } 
+                               
+                               /* we have too less data and don't need to flush */
+                               else {
+                                       break;
+                               }
+                       } 
+                       
+                       /* we seem to have all data of the chunk */
+                       else {
+                               out_avail = 1;
+                               NEW_BUCKET(PHP_HTTP_BUFFER(buffer)->data, buffer->hexlen);
+                               
+                               /* remove outgoing data from the buffer */
+                               php_http_buffer_cut(PHP_HTTP_BUFFER(buffer), 0, buffer->hexlen);
+                               /* reset hexlen */
+                               buffer->hexlen = 0;
+                               /* continue */
+                       }
+               } 
+               
+               /* we don't know the length of the chunk yet */
+               else {
+                       size_t off = 0;
+                       
+                       /* ignore preceeding CRLFs (too loose?) */
+                       while (off < PHP_HTTP_BUFFER(buffer)->used && (
+                                       PHP_HTTP_BUFFER(buffer)->data[off] == '\n' || 
+                                       PHP_HTTP_BUFFER(buffer)->data[off] == '\r')) {
+                               ++off;
+                       }
+                       if (off) {
+                               php_http_buffer_cut(PHP_HTTP_BUFFER(buffer), 0, off);
+                       }
+                       
+                       /* still data there? */
+                       if (PHP_HTTP_BUFFER(buffer)->used) {
+                               int eollen;
+                               const char *eolstr;
+                               
+                               /* we need eol, so we can be sure we have all hex digits */
+                               php_http_buffer_fix(PHP_HTTP_BUFFER(buffer));
+                               if ((eolstr = php_http_locate_bin_eol(PHP_HTTP_BUFFER(buffer)->data, PHP_HTTP_BUFFER(buffer)->used, &eollen))) {
+                                       char *stop = NULL;
+                                       
+                                       /* read in chunk size */
+                                       buffer->hexlen = strtoul(PHP_HTTP_BUFFER(buffer)->data, &stop, 16);
+                                       
+                                       /*      if strtoul() stops at the beginning of the buffered data
+                                               there's something oddly wrong, i.e. bad input */
+                                       if (stop == PHP_HTTP_BUFFER(buffer)->data) {
+                                               return PSFS_ERR_FATAL;
+                                       }
+                                       
+                                       /* cut out <chunk size hex><chunk extension><eol> */
+                                       php_http_buffer_cut(PHP_HTTP_BUFFER(buffer), 0, eolstr + eollen - PHP_HTTP_BUFFER(buffer)->data);
+                                       /* buffer->hexlen is 0 now or contains the size of the next chunk */
+                                       if (!buffer->hexlen) {
+                                               php_stream_notify_info(stream->context, PHP_STREAM_NOTIFY_COMPLETED, NULL, 0);
+                                               break;
+                                       }
+                                       /* continue */
+                               } else {
+                                       /* we have not enough data buffered to read in chunk size */
+                                       break;
+                               }
+                       }
+                       /* break */
+               }
+       }
+       
+       /* flush before close, but only if we are already waiting for more data */
+       if (PHP_HTTP_FILTER_IS_CLOSING(stream, flags) && buffer->hexlen && PHP_HTTP_BUFFER(buffer)->used) {
+               out_avail = 1;
+               NEW_BUCKET(PHP_HTTP_BUFFER(buffer)->data, PHP_HTTP_BUFFER(buffer)->used);
+               php_http_buffer_reset(PHP_HTTP_BUFFER(buffer));
+               buffer->hexlen = 0;
+       }
+       
+       return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
+}
+
+static PHP_HTTP_FILTER_DESTRUCTOR(chunked_decode)
+{
+       PHP_HTTP_FILTER_BUFFER(chunked_decode) *b = (PHP_HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract);
+       
+       php_http_buffer_dtor(PHP_HTTP_BUFFER(b));
+       pefree(b, this->is_persistent);
+}
+
+static PHP_HTTP_FILTER_FUNCTION(chunked_encode)
+{
+       php_http_buffer_t buf;
+       php_stream_bucket *ptr, *nxt;
+       
+       if (bytes_consumed) {
+               *bytes_consumed = 0;
+       }
+       
+       /* new data available? */
+       php_http_buffer_init(&buf);
+
+       /* fetch available bucket data */
+       for (ptr = buckets_in->head; ptr; ptr = nxt) {
+               if (bytes_consumed) {
+                       *bytes_consumed += ptr->buflen;
+               }
+#if DBG_FILTER
+               fprintf(stderr, "update: chunked (-> %zu) (w: %zu, r: %zu)\n", ptr->buflen, stream->writepos, stream->readpos);
+#endif
+               
+               nxt = ptr->next;
+               php_stream_bucket_unlink(ptr TSRMLS_CC);
+               php_http_buffer_appendf(&buf, "%lx" PHP_HTTP_CRLF, (long unsigned int) ptr->buflen);
+               php_http_buffer_append(&buf, ptr->buf, ptr->buflen);
+               php_http_buffer_appends(&buf, PHP_HTTP_CRLF);
+
+               /* pass through */
+               NEW_BUCKET(buf.data, buf.used);
+               /* reset */
+               php_http_buffer_reset(&buf);
+               php_stream_bucket_delref(ptr TSRMLS_CC);
+       }
+
+       /* free buffer */
+       php_http_buffer_dtor(&buf);
+       
+       /* terminate with "0" */
+       if (PHP_HTTP_FILTER_IS_CLOSING(stream, flags)) {
+#if DBG_FILTER
+               fprintf(stderr, "finish: chunked\n");
+#endif
+               
+               NEW_BUCKET("0" PHP_HTTP_CRLF PHP_HTTP_CRLF, lenof("0" PHP_HTTP_CRLF PHP_HTTP_CRLF));
+       }
+       
+       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)
+{
+       php_stream_bucket *ptr, *nxt;
+       PHP_HTTP_FILTER_BUFFER(zlib) *buffer = (PHP_HTTP_FILTER_BUFFER(zlib) *) this->abstract;
+       
+       if (bytes_consumed) {
+               *bytes_consumed = 0;
+       }
+       
+       /* fetch available bucket data */
+       for (ptr = buckets_in->head; ptr; ptr = nxt) {
+               char *encoded = NULL;
+               size_t encoded_len = 0;
+
+               if (bytes_consumed) {
+                       *bytes_consumed += ptr->buflen;
+               }
+
+#if DBG_FILTER
+               fprintf(stderr, "bucket: b=%p p=%p p=%p\n", ptr->brigade, ptr->prev,  ptr->next);
+#endif
+               
+               nxt = ptr->next;
+               php_stream_bucket_unlink(ptr TSRMLS_CC);
+               php_http_encoding_stream_update(buffer, ptr->buf, ptr->buflen, &encoded, &encoded_len);
+               
+#if DBG_FILTER
+               fprintf(stderr, "update: deflate (-> %zu) (w: %zu, r: %zu)\n", encoded_len, stream->writepos, stream->readpos);
+#endif
+               
+               if (encoded) {
+                       if (encoded_len) {
+                               NEW_BUCKET(encoded, encoded_len);
+                       }
+                       efree(encoded);
+               }
+               php_stream_bucket_delref(ptr TSRMLS_CC);
+       }
+
+       /* flush & close */
+       if (flags & PSFS_FLAG_FLUSH_INC) {
+               char *encoded = NULL;
+               size_t encoded_len = 0;
+               
+               php_http_encoding_stream_flush(buffer, &encoded, &encoded_len);
+               
+#if DBG_FILTER
+               fprintf(stderr, "flush: deflate (-> %zu)\n", encoded_len);
+#endif
+               
+               if (encoded) {
+                       if (encoded_len) {
+                               NEW_BUCKET(encoded, encoded_len);
+                       }
+                       efree(encoded);
+               }
+       }
+       
+       if (PHP_HTTP_FILTER_IS_CLOSING(stream, flags)) {
+               char *encoded = NULL;
+               size_t encoded_len = 0;
+               
+               php_http_encoding_stream_finish(buffer, &encoded, &encoded_len);
+               
+#if DBG_FILTER
+               fprintf(stderr, "finish: deflate (-> %zu)\n", encoded_len);
+#endif
+               
+               if (encoded) {
+                       if (encoded_len) {
+                               NEW_BUCKET(encoded, encoded_len);
+                       }
+                       efree(encoded);
+               }
+       }
+       
+       return PSFS_PASS_ON;
+}
+static PHP_HTTP_FILTER_DESTRUCTOR(zlib)
+{
+       PHP_HTTP_FILTER_BUFFER(zlib) *buffer = (PHP_HTTP_FILTER_BUFFER(zlib) *) this->abstract;
+       php_http_encoding_stream_free(&buffer);
+}
+
+static PHP_HTTP_FILTER_OPS(deflate) = {
+       PHP_HTTP_FILTER_FUNC(zlib),
+       PHP_HTTP_FILTER_DTOR(zlib),
+       "http.deflate"
+};
+
+static PHP_HTTP_FILTER_OPS(inflate) = {
+       PHP_HTTP_FILTER_FUNC(zlib),
+       PHP_HTTP_FILTER_DTOR(zlib),
+       "http.inflate"
+};
+
+static php_stream_filter *http_filter_create(const char *name, zval *params, int p TSRMLS_DC)
+{
+       zval **tmp = &params;
+       php_stream_filter *f = NULL;
+       int flags = p ? PHP_HTTP_ENCODING_STREAM_PERSISTENT : 0;
+       
+       if (params) {
+               switch (Z_TYPE_P(params)) {
+                       case IS_ARRAY:
+                       case IS_OBJECT:
+                               if (SUCCESS != zend_hash_find(HASH_OF(params), "flags", sizeof("flags"), (void *) &tmp)) {
+                                       break;
+                               }
+                               /* no break */
+                       default:
+                       {
+                               zval *num = php_http_ztyp(IS_LONG, *tmp);
+                               
+                               flags |= (Z_LVAL_P(num) & 0x0fffffff);
+                               zval_ptr_dtor(&num);
+
+                       }
+                       break;
+               }
+       }
+
+       if (!strcasecmp(name, "http.chunked_decode")) {
+               PHP_HTTP_FILTER_BUFFER(chunked_decode) *b = NULL;
+               
+               if ((b = pecalloc(1, sizeof(PHP_HTTP_FILTER_BUFFER(chunked_decode)), p))) {
+                       php_http_buffer_init_ex(PHP_HTTP_BUFFER(b), 4096, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0);
+                       if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(chunked_decode), b, p))) {
+                               pefree(b, p);
+                       }
+               }
+       } else
+       
+       if (!strcasecmp(name, "http.chunked_encode")) {
+               f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(chunked_encode), NULL, p);
+       } else
+       
+       if (!strcasecmp(name, "http.inflate")) {
+               PHP_HTTP_FILTER_BUFFER(zlib) *b = NULL;
+               
+               if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), flags TSRMLS_CC))) {
+                       if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(inflate), b, p))) {
+                               php_http_encoding_stream_free(&b);
+                       }
+               }
+       } else
+       
+       if (!strcasecmp(name, "http.deflate")) {
+               PHP_HTTP_FILTER_BUFFER(zlib) *b = NULL;
+               
+               if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), flags TSRMLS_CC))) {
+                       if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(deflate), b, p))) {
+                               php_http_encoding_stream_free(&b);
+                       }
+               }
+       }
+       
+       return f;
+}
+
+php_stream_filter_factory php_http_filter_factory = {
+       http_filter_create
+};
+
+
+/*
+ * 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_filter.h b/src/php_http_filter.h
new file mode 100644 (file)
index 0000000..21fe4db
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_FILTER_H
+#define PHP_HTTP_FILTER_H
+
+PHP_HTTP_API php_stream_filter_factory php_http_filter_factory;
+PHP_MINIT_FUNCTION(http_filter);
+
+#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_header.c b/src/php_http_header.c
new file mode 100644 (file)
index 0000000..41601df
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+ZEND_RESULT_CODE php_http_header_parse(const char *header, size_t length, HashTable *headers, php_http_info_callback_t callback_func, void **callback_data TSRMLS_DC)
+{
+       php_http_header_parser_t ctx;
+       php_http_buffer_t buf;
+       php_http_header_parser_state_t rs;
+       
+       if (!php_http_buffer_from_string_ex(&buf, header, length)) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not allocate buffer");
+               return FAILURE;
+       }
+       
+       if (!php_http_header_parser_init(&ctx TSRMLS_CC)) {
+               php_http_buffer_dtor(&buf);
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initialize header parser");
+               return FAILURE;
+       }
+       
+       rs = php_http_header_parser_parse(&ctx, &buf, PHP_HTTP_HEADER_PARSER_CLEANUP, headers, callback_func, callback_data);
+       php_http_header_parser_dtor(&ctx);
+       php_http_buffer_dtor(&buf);
+
+       return rs == PHP_HTTP_HEADER_PARSER_STATE_FAILURE ? FAILURE : SUCCESS;
+}
+
+void php_http_header_to_callback(HashTable *headers, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg TSRMLS_DC)
+{
+       HashPosition pos1, pos2;
+       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+       zval **header, **single_header;
+
+       FOREACH_HASH_KEYVAL(pos1, headers, key, header) {
+               if (key.type == HASH_KEY_IS_STRING) {
+                       if (key.len == sizeof("Set-Cookie") && !strcasecmp(key.str, "Set-Cookie") && Z_TYPE_PP(header) == IS_ARRAY) {
+                               FOREACH_VAL(pos2, *header, single_header) {
+                                       if (Z_TYPE_PP(single_header) == IS_ARRAY) {
+                                               php_http_cookie_list_t *cookie = php_http_cookie_list_from_struct(NULL, *single_header TSRMLS_CC);
+
+                                               if (cookie) {
+                                                       char *buf;
+                                                       size_t len;
+
+                                                       php_http_cookie_list_to_string(cookie, &buf, &len);
+                                                       cb(cb_arg, crlf ? "Set-Cookie: %s" PHP_HTTP_CRLF : "Set-Cookie: %s", buf);
+                                                       php_http_cookie_list_free(&cookie);
+                                                       efree(buf);
+                                               }
+                                       } else {
+                                               zval *strval = php_http_header_value_to_string(*single_header TSRMLS_CC);
+
+                                               cb(cb_arg, crlf ? "Set-Cookie: %s" PHP_HTTP_CRLF : "Set-Cookie: %s", Z_STRVAL_P(strval));
+                                               zval_ptr_dtor(&strval);
+                                       }
+                               }
+                       } else {
+                               zval *strval = php_http_header_value_to_string(*header TSRMLS_CC);
+
+                               cb(cb_arg, crlf ? "%s: %s" PHP_HTTP_CRLF : "%s: %s", key.str, Z_STRVAL_P(strval));
+                               zval_ptr_dtor(&strval);
+                       }
+               }
+       }
+}
+
+void php_http_header_to_string(php_http_buffer_t *str, HashTable *headers TSRMLS_DC)
+{
+       php_http_header_to_callback(headers, 1, (php_http_pass_format_callback_t) php_http_buffer_appendf, str TSRMLS_CC);
+}
+
+zval *php_http_header_value_to_string(zval *header TSRMLS_DC)
+{
+       zval *ret;
+
+       if (Z_TYPE_P(header) == IS_BOOL) {
+               MAKE_STD_ZVAL(ret);
+               ZVAL_STRING(ret, Z_BVAL_P(header) ? "true" : "false", 1);
+       } else if (Z_TYPE_P(header) == IS_ARRAY) {
+               zval **val;
+               HashPosition pos;
+               php_http_buffer_t str;
+
+               php_http_buffer_init(&str);
+               MAKE_STD_ZVAL(ret);
+               FOREACH_VAL(pos,header, val) {
+                       zval *strval = php_http_header_value_to_string(*val TSRMLS_CC);
+
+                       php_http_buffer_appendf(&str, str.used ? ", %s":"%s", Z_STRVAL_P(strval));
+                       zval_ptr_dtor(&strval);
+               }
+               php_http_buffer_fix(&str);
+               ZVAL_STRINGL(ret, str.data, str.used, 0);
+       } else  {
+               ret = php_http_zsep(1, IS_STRING, header);
+       }
+
+       return ret;
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader___construct, 0, 0, 0)
+       ZEND_ARG_INFO(0, name)
+       ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpHeader, __construct)
+{
+       char *name_str = NULL, *value_str = NULL;
+       int name_len = 0, value_len = 0;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!s!", &name_str, &name_len, &value_str, &value_len), invalid_arg, return);
+
+       if (name_str && name_len) {
+               char *pretty_str = estrndup(name_str, name_len);
+               zend_update_property_stringl(php_http_header_class_entry, getThis(), ZEND_STRL("name"), php_http_pretty_key(pretty_str, name_len, 1, 1), name_len TSRMLS_CC);
+               efree(pretty_str);
+       }
+       if (value_str && value_len) {
+               zend_update_property_stringl(php_http_header_class_entry, getThis(), ZEND_STRL("value"), value_str, value_len TSRMLS_CC);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_serialize, 0, 0, 0)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpHeader, serialize)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_buffer_t buf;
+               zval *zname, *zvalue;
+
+               php_http_buffer_init(&buf);
+               zname = php_http_ztyp(IS_STRING, zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("name"), 0 TSRMLS_CC));
+               php_http_buffer_append(&buf, Z_STRVAL_P(zname), Z_STRLEN_P(zname));
+               zval_ptr_dtor(&zname);
+               zvalue = php_http_ztyp(IS_STRING, zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), 0 TSRMLS_CC));
+               if (Z_STRLEN_P(zvalue)) {
+                       php_http_buffer_appends(&buf, ": ");
+                       php_http_buffer_append(&buf, Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue));
+               } else {
+                       php_http_buffer_appends(&buf, ":");
+               }
+               zval_ptr_dtor(&zvalue);
+
+               RETURN_PHP_HTTP_BUFFER_VAL(&buf);
+       }
+       RETURN_EMPTY_STRING();
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_unserialize, 0, 0, 1)
+       ZEND_ARG_INFO(0, serialized)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpHeader, unserialize)
+{
+       char *serialized_str;
+       int serialized_len;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &serialized_str, &serialized_len)) {
+               HashTable ht;
+
+               zend_hash_init(&ht, 1, NULL, ZVAL_PTR_DTOR, 0);
+               if (SUCCESS == php_http_header_parse(serialized_str, serialized_len, &ht, NULL, NULL TSRMLS_CC)) {
+                       if (zend_hash_num_elements(&ht)) {
+                               zval **val, *cpy;
+                               char *str;
+                               uint len;
+                               ulong idx;
+
+                               zend_hash_internal_pointer_reset(&ht);
+                               switch (zend_hash_get_current_key_ex(&ht, &str, &len, &idx, 0, NULL)) {
+                                       case HASH_KEY_IS_STRING:
+                                               zend_update_property_stringl(php_http_header_class_entry, getThis(), ZEND_STRL("name"), str, len - 1 TSRMLS_CC);
+                                               break;
+                                       case HASH_KEY_IS_LONG:
+                                               zend_update_property_long(php_http_header_class_entry, getThis(), ZEND_STRL("name"), idx TSRMLS_CC);
+                                               break;
+                                       default:
+                                               break;
+                               }
+                               zend_hash_get_current_data(&ht, (void *) &val);
+                               cpy = php_http_zsep(1, IS_STRING, *val);
+                               zend_update_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), cpy TSRMLS_CC);
+                               zval_ptr_dtor(&cpy);
+                       }
+               }
+               zend_hash_destroy(&ht);
+       }
+
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_match, 0, 0, 1)
+       ZEND_ARG_INFO(0, value)
+       ZEND_ARG_INFO(0, flags)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpHeader, match)
+{
+       char *val_str;
+       int val_len;
+       long flags = PHP_HTTP_MATCH_LOOSE;
+       zval *zvalue;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sl", &val_str, &val_len, &flags)) {
+               return;
+       }
+
+       zvalue = php_http_ztyp(IS_STRING, zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), 0 TSRMLS_CC));
+       RETVAL_BOOL(php_http_match(Z_STRVAL_P(zvalue), val_str, flags));
+       zval_ptr_dtor(&zvalue);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_negotiate, 0, 0, 1)
+       ZEND_ARG_INFO(0, supported)
+       ZEND_ARG_INFO(1, result)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpHeader, negotiate)
+{
+       HashTable *supported, *rs;
+       zval *zname, *zvalue, *rs_array = NULL;
+       char *sep_str = NULL;
+       size_t sep_len = 0;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H|z", &supported, &rs_array)) {
+               return;
+       }
+       if (rs_array) {
+               zval_dtor(rs_array);
+               array_init(rs_array);
+       }
+
+       zname = php_http_ztyp(IS_STRING, zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("name"), 0 TSRMLS_CC));
+       if (!strcasecmp(Z_STRVAL_P(zname), "Accept")) {
+               sep_str = "/";
+               sep_len = 1;
+       } else if (!strcasecmp(Z_STRVAL_P(zname), "Accept-Language")) {
+               sep_str = "-";
+               sep_len = 1;
+       }
+       zval_ptr_dtor(&zname);
+
+       zvalue = php_http_ztyp(IS_STRING, zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), 0 TSRMLS_CC));
+       if ((rs = php_http_negotiate(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue), supported, sep_str, sep_len TSRMLS_CC))) {
+               PHP_HTTP_DO_NEGOTIATE_HANDLE_RESULT(rs, supported, rs_array);
+       } else {
+               PHP_HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported, rs_array);
+       }
+       zval_ptr_dtor(&zvalue);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_getParams, 0, 0, 0)
+       ZEND_ARG_INFO(0, param_sep)
+       ZEND_ARG_INFO(0, arg_sep)
+       ZEND_ARG_INFO(0, val_sep)
+       ZEND_ARG_INFO(0, flags)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpHeader, getParams)
+{
+       zval zctor, *zparams_obj, **zargs = NULL;
+       
+       INIT_PZVAL(&zctor);
+       ZVAL_STRINGL(&zctor, "__construct", lenof("__construct"), 0);
+       
+       MAKE_STD_ZVAL(zparams_obj);
+       object_init_ex(zparams_obj, php_http_params_class_entry);
+       
+       zargs = (zval **) ecalloc(ZEND_NUM_ARGS()+1, sizeof(zval *));
+       zargs[0] = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("value"), 0 TSRMLS_CC);
+       if (ZEND_NUM_ARGS()) {
+               zend_get_parameters_array(ZEND_NUM_ARGS(), ZEND_NUM_ARGS(), &zargs[1]);
+       }
+       
+       if (SUCCESS == call_user_function(NULL, &zparams_obj, &zctor, return_value, ZEND_NUM_ARGS()+1, zargs TSRMLS_CC)) {
+               RETVAL_ZVAL(zparams_obj, 0, 1);
+       }
+       
+       if (zargs) {
+               efree(zargs);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_parse, 0, 0, 1)
+       ZEND_ARG_INFO(0, string)
+       ZEND_ARG_INFO(0, header_class)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpHeader, parse)
+{
+       char *header_str;
+       int header_len;
+       zend_class_entry *ce = NULL;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|C", &header_str, &header_len, &ce)) {
+               array_init(return_value);
+
+               if (SUCCESS != php_http_header_parse(header_str, header_len, Z_ARRVAL_P(return_value), NULL, NULL TSRMLS_CC)) {
+                       zval_dtor(return_value);
+                       RETURN_FALSE;
+               } else {
+                       if (ce && instanceof_function(ce, php_http_header_class_entry TSRMLS_CC)) {
+                               HashPosition pos;
+                               php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+                               zval **val;
+
+                               FOREACH_KEYVAL(pos, return_value, key, val) {
+                                       zval *zho, *zkey, *zvalue;
+
+                                       Z_ADDREF_PP(val);
+                                       zvalue = *val;
+
+                                       MAKE_STD_ZVAL(zkey);
+                                       if (key.type == HASH_KEY_IS_LONG) {
+                                               ZVAL_LONG(zkey, key.num);
+                                       } else {
+                                               ZVAL_STRINGL(zkey, key.str, key.len - 1, 1);
+                                       }
+
+                                       MAKE_STD_ZVAL(zho);
+                                       object_init_ex(zho, ce);
+                                       zend_call_method_with_2_params(&zho, ce, NULL, "__construct", NULL, zkey, zvalue);
+
+                                       if (key.type == HASH_KEY_IS_LONG) {
+                                               zend_hash_index_update(Z_ARRVAL_P(return_value), key.num, (void *) &zho, sizeof(zval *), NULL);
+                                       } else {
+                                               zend_hash_update(Z_ARRVAL_P(return_value), key.str, key.len, (void *) &zho, sizeof(zval *), NULL);
+                                       }
+
+                                       zval_ptr_dtor(&zvalue);
+                                       zval_ptr_dtor(&zkey);
+                               }
+                       }
+               }
+       }
+}
+
+static zend_function_entry php_http_header_methods[] = {
+       PHP_ME(HttpHeader, __construct,   ai_HttpHeader___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+       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)
+       PHP_ME(HttpHeader, unserialize,   ai_HttpHeader_unserialize, ZEND_ACC_PUBLIC)
+       PHP_ME(HttpHeader, match,         ai_HttpHeader_match, ZEND_ACC_PUBLIC)
+       PHP_ME(HttpHeader, negotiate,     ai_HttpHeader_negotiate, ZEND_ACC_PUBLIC)
+       PHP_ME(HttpHeader, getParams,     ai_HttpHeader_getParams, ZEND_ACC_PUBLIC)
+       PHP_ME(HttpHeader, parse,         ai_HttpHeader_parse, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       EMPTY_FUNCTION_ENTRY
+};
+
+zend_class_entry *php_http_header_class_entry;
+
+PHP_MINIT_FUNCTION(http_header)
+{
+       zend_class_entry ce = {0};
+
+       INIT_NS_CLASS_ENTRY(ce, "http", "Header", php_http_header_methods);
+       php_http_header_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
+       zend_class_implements(php_http_header_class_entry TSRMLS_CC, 1, zend_ce_serializable);
+       zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_LOOSE"), PHP_HTTP_MATCH_LOOSE TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_CASE"), PHP_HTTP_MATCH_CASE TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_WORD"), PHP_HTTP_MATCH_WORD TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_FULL"), PHP_HTTP_MATCH_FULL TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_STRICT"), PHP_HTTP_MATCH_STRICT TSRMLS_CC);
+       zend_declare_property_null(php_http_header_class_entry, ZEND_STRL("name"), ZEND_ACC_PUBLIC TSRMLS_CC);
+       zend_declare_property_null(php_http_header_class_entry, ZEND_STRL("value"), ZEND_ACC_PUBLIC TSRMLS_CC);
+
+       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_header.h b/src/php_http_header.h
new file mode 100644 (file)
index 0000000..a2baecb
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_HEADERS_H
+#define PHP_HTTP_HEADERS_H
+
+#include "php_http_info.h"
+
+PHP_HTTP_API ZEND_RESULT_CODE php_http_header_parse(const char *header, size_t length, HashTable *headers, php_http_info_callback_t callback_func, void **callback_data TSRMLS_DC);
+
+PHP_HTTP_API void php_http_header_to_callback(HashTable *headers, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg TSRMLS_DC);
+PHP_HTTP_API void php_http_header_to_string(php_http_buffer_t *str, HashTable *headers TSRMLS_DC);
+
+PHP_HTTP_API zval *php_http_header_value_to_string(zval *header TSRMLS_DC);
+
+PHP_HTTP_API zend_class_entry *php_http_header_class_entry;
+PHP_MINIT_FUNCTION(http_header);
+
+#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_header_parser.c b/src/php_http_header_parser.c
new file mode 100644 (file)
index 0000000..46551e2
--- /dev/null
@@ -0,0 +1,489 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+#ifndef DBG_PARSER
+#      define DBG_PARSER 0
+#endif
+
+typedef struct php_http_header_parser_state_spec {
+       php_http_header_parser_state_t state;
+       unsigned need_data:1;
+} php_http_header_parser_state_spec_t;
+
+static const php_http_header_parser_state_spec_t php_http_header_parser_states[] = {
+               {PHP_HTTP_HEADER_PARSER_STATE_START,            1},
+               {PHP_HTTP_HEADER_PARSER_STATE_KEY,                      1},
+               {PHP_HTTP_HEADER_PARSER_STATE_VALUE,            1},
+               {PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX,         0},
+               {PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE,      0},
+               {PHP_HTTP_HEADER_PARSER_STATE_DONE,                     0}
+};
+
+php_http_header_parser_t *php_http_header_parser_init(php_http_header_parser_t *parser TSRMLS_DC)
+{
+       if (!parser) {
+               parser = emalloc(sizeof(*parser));
+       }
+       memset(parser, 0, sizeof(*parser));
+
+       TSRMLS_SET_CTX(parser->ts);
+
+       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;
+}
+
+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;
+}
+
+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;
+}
+
+void php_http_header_parser_dtor(php_http_header_parser_t *parser)
+{
+       zend_ptr_stack_destroy(&parser->stack);
+       php_http_info_dtor(&parser->info);
+       PTR_FREE(parser->_key.str);
+       PTR_FREE(parser->_val.str);
+}
+
+void php_http_header_parser_free(php_http_header_parser_t **parser)
+{
+       if (*parser) {
+               php_http_header_parser_dtor(*parser);
+               efree(*parser);
+               *parser = NULL;
+       }
+}
+
+/* NOTE: 'str' has to be null terminated */
+static void php_http_header_parser_error(size_t valid_len, char *str, size_t len, const char *eol_str TSRMLS_DC)
+{
+       int escaped_len;
+       char *escaped_str;
+
+       escaped_str = php_addcslashes(str, len, &escaped_len, 0, ZEND_STRL("\x0..\x1F\x7F..\xFF") TSRMLS_CC);
+
+       if (valid_len != len && (!eol_str || (str+valid_len) != eol_str)) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse headers: unexpected character '\\%03o' at pos %zu of '%.*s'", str[valid_len], valid_len, escaped_len, escaped_str);
+       } else if (eol_str) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse headers: unexpected end of line at pos %zu of '%.*s'", eol_str - str, escaped_len, escaped_str);
+       } else {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse headers: unexpected end of input at pos %zu of '%.*s'", len, escaped_len, escaped_str);
+       }
+
+       efree(escaped_str);
+}
+
+php_http_header_parser_state_t php_http_header_parser_parse(php_http_header_parser_t *parser, php_http_buffer_t *buffer, unsigned flags, HashTable *headers, php_http_info_callback_t callback_func, void *callback_arg)
+{
+       TSRMLS_FETCH_FROM_CTX(parser->ts);
+
+       while (buffer->used || !php_http_header_parser_states[php_http_header_parser_state_is(parser)].need_data) {
+#if 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);
+               _dpf(0, buffer->data, buffer->used);
+#endif
+               switch (php_http_header_parser_state_pop(parser)) {
+                       case PHP_HTTP_HEADER_PARSER_STATE_FAILURE:
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse headers");
+                               return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
+
+                       case PHP_HTTP_HEADER_PARSER_STATE_START: {
+                               char *ptr = buffer->data;
+
+                               while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) {
+                                       ++ptr;
+                               }
+
+                               php_http_buffer_cut(buffer, 0, ptr - buffer->data);
+                               php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY);
+                               break;
+                       }
+
+                       case PHP_HTTP_HEADER_PARSER_STATE_KEY: {
+                               const char *colon, *eol_str = NULL;
+                               int eol_len = 0;
+
+                               /* fix buffer here, so eol_str pointer doesn't become obsolete afterwards */
+                               php_http_buffer_fix(buffer);
+
+                               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);
+                               } else if (php_http_info_parse(&parser->info, buffer->data TSRMLS_CC)) {
+                                       /* new message starting with request/response line */
+                                       if (callback_func) {
+                                               callback_func(callback_arg, &headers, &parser->info TSRMLS_CC);
+                                       }
+                                       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);
+                               } else if ((colon = memchr(buffer->data, ':', buffer->used)) && (!eol_str || eol_str > colon)) {
+                                       /* header: string */
+                                       size_t valid_len;
+
+                                       parser->_key.len = colon - buffer->data;
+                                       parser->_key.str = estrndup(buffer->data, parser->_key.len);
+
+                                       valid_len = strspn(parser->_key.str, PHP_HTTP_HEADER_NAME_CHARS);
+                                       if (valid_len != parser->_key.len) {
+                                               php_http_header_parser_error(valid_len, parser->_key.str, parser->_key.len, eol_str TSRMLS_CC);
+                                               PTR_SET(parser->_key.str, NULL);
+                                               return php_http_header_parser_state_push(parser, 1, 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);
+                               } 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 TSRMLS_CC);
+                                       return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
+                               } else {
+                                       /* keep feeding */
+                                       return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY);
+                               }
+                               break;
+                       }
+
+                       case PHP_HTTP_HEADER_PARSER_STATE_VALUE: {
+                               const char *eol_str;
+                               int eol_len;
+
+#define SET_ADD_VAL(slen, eol_len) \
+       do { \
+               const char *ptr = buffer->data; \
+               size_t len = slen; \
+                \
+               while (len > 0 && PHP_HTTP_IS_CTYPE(space, *ptr)) { \
+                       ++ptr; \
+                       --len; \
+               } \
+               while (len > 0 && PHP_HTTP_IS_CTYPE(space, ptr[len - 1])) { \
+                       --len; \
+               } \
+                \
+               if (len > 0) { \
+                       if (parser->_val.str) { \
+                               parser->_val.str = erealloc(parser->_val.str, parser->_val.len + len + 2); \
+                               parser->_val.str[parser->_val.len++] = ' '; \
+                               memcpy(&parser->_val.str[parser->_val.len], ptr, len); \
+                               parser->_val.len += len; \
+                               parser->_val.str[parser->_val.len] = '\0'; \
+                       } else { \
+                               parser->_val.len = len; \
+                               parser->_val.str = estrndup(ptr, len); \
+                       } \
+               } \
+               php_http_buffer_cut(buffer, 0, slen + eol_len); \
+       } while (0)
+
+                               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);
+                               } 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);
+                               } else {
+                                       return php_http_header_parser_state_push(parser, 1, 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);
+                               } 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);
+                               } else {
+                                       /* keep feeding */
+                                       return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX);
+                               }
+                               break;
+
+                       case PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE:
+                               if (parser->_key.str && parser->_val.str) {
+                                       zval array, **exist;
+                                       size_t valid_len = strlen(parser->_val.str);
+
+                                       /* check for truncation */
+                                       if (valid_len != parser->_val.len) {
+                                               php_http_header_parser_error(valid_len, parser->_val.str, parser->_val.len, NULL TSRMLS_CC);
+
+                                               PTR_SET(parser->_key.str, NULL);
+                                               PTR_SET(parser->_val.str, NULL);
+
+                                               return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
+                                       }
+
+                                       if (!headers && callback_func) {
+                                               callback_func(callback_arg, &headers, NULL TSRMLS_CC);
+                                       }
+
+                                       INIT_PZVAL_ARRAY(&array, headers);
+                                       php_http_pretty_key(parser->_key.str, parser->_key.len, 1, 1);
+                                       if (SUCCESS == zend_symtable_find(headers, parser->_key.str, parser->_key.len + 1, (void *) &exist)) {
+                                               convert_to_array(*exist);
+                                               add_next_index_stringl(*exist, parser->_val.str, parser->_val.len, 0);
+                                       } else {
+                                               add_assoc_stringl_ex(&array, parser->_key.str, parser->_key.len + 1, parser->_val.str, parser->_val.len, 0);
+                                       }
+                                       parser->_val.str = NULL;
+                               }
+
+                               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);
+                               break;
+
+                       case PHP_HTTP_HEADER_PARSER_STATE_DONE:
+                               return PHP_HTTP_HEADER_PARSER_STATE_DONE;
+               }
+       }
+
+       return php_http_header_parser_state_is(parser);
+}
+
+php_http_header_parser_state_t php_http_header_parser_parse_stream(php_http_header_parser_t *parser, php_http_buffer_t *buf, php_stream *s, unsigned flags, HashTable *headers, php_http_info_callback_t callback_func, void *callback_arg)
+{
+       php_http_header_parser_state_t state = PHP_HTTP_HEADER_PARSER_STATE_START;
+       TSRMLS_FETCH_FROM_CTX(parser->ts);
+
+       if (!buf->data) {
+               php_http_buffer_resize_ex(buf, 0x1000, 1, 0);
+       }
+       while (1) {
+               size_t justread = 0;
+#if DBG_PARSER
+               const char *states[] = {"START", "KEY", "VALUE", "VALUE_EX", "HEADER_DONE", "DONE"};
+               fprintf(stderr, "#SHP: %s (f:%u)\n", states[state], flags);
+#endif
+               /* resize if needed */
+               if (buf->free < 0x1000) {
+                       php_http_buffer_resize_ex(buf, 0x1000, 1, 0);
+               }
+               switch (state) {
+               case PHP_HTTP_HEADER_PARSER_STATE_FAILURE:
+               case PHP_HTTP_HEADER_PARSER_STATE_DONE:
+                       return state;
+
+               default:
+                       /* read line */
+                       php_stream_get_line(s, buf->data + buf->used, buf->free, &justread);
+                       /* if we fail reading a whole line, try a single char */
+                       if (!justread) {
+                               int c = php_stream_getc(s);
+
+                               if (c != EOF) {
+                                       char s[1] = {c};
+                                       justread = php_http_buffer_append(buf, s, 1);
+                               }
+                       }
+                       php_http_buffer_account(buf, justread);
+               }
+
+               if (justread) {
+                       state = php_http_header_parser_parse(parser, buf, flags, headers, callback_func, callback_arg);
+               } else if (php_stream_eof(s)) {
+                       return php_http_header_parser_parse(parser, buf, flags | PHP_HTTP_HEADER_PARSER_CLEANUP, headers, callback_func, callback_arg);
+               } else  {
+                       return state;
+               }
+       }
+
+       return PHP_HTTP_HEADER_PARSER_STATE_DONE;
+}
+
+zend_class_entry *php_http_header_parser_class_entry;
+static zend_object_handlers php_http_header_parser_object_handlers;
+
+zend_object_value php_http_header_parser_object_new(zend_class_entry *ce TSRMLS_DC)
+{
+       return php_http_header_parser_object_new_ex(ce, NULL, NULL TSRMLS_CC);
+}
+
+zend_object_value php_http_header_parser_object_new_ex(zend_class_entry *ce, php_http_header_parser_t *parser, php_http_header_parser_object_t **ptr TSRMLS_DC)
+{
+       php_http_header_parser_object_t *o;
+
+       o = ecalloc(1, sizeof(php_http_header_parser_object_t));
+       zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
+       object_properties_init((zend_object *) o, ce);
+
+       if (ptr) {
+               *ptr = o;
+       }
+
+       if (parser) {
+               o->parser = parser;
+       } else {
+               o->parser = php_http_header_parser_init(NULL TSRMLS_CC);
+       }
+       o->buffer = php_http_buffer_new();
+
+       o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_header_parser_object_free, NULL TSRMLS_CC);
+       o->zv.handlers = &php_http_header_parser_object_handlers;
+
+       return o->zv;
+}
+
+void php_http_header_parser_object_free(void *object TSRMLS_DC)
+{
+       php_http_header_parser_object_t *o = (php_http_header_parser_object_t *) object;
+
+       if (o->parser) {
+               php_http_header_parser_free(&o->parser);
+       }
+       if (o->buffer) {
+               php_http_buffer_free(&o->buffer);
+       }
+       zend_object_std_dtor((zend_object *) o TSRMLS_CC);
+       efree(o);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeaderParser_getState, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpHeaderParser, getState)
+{
+       php_http_header_parser_object_t *parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       zend_parse_parameters_none();
+       /* always return the real state */
+       RETVAL_LONG(php_http_header_parser_state_is(parser_obj->parser));
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeaderParser_parse, 0, 0, 3)
+       ZEND_ARG_INFO(0, data)
+       ZEND_ARG_INFO(0, flags)
+       ZEND_ARG_ARRAY_INFO(1, headers, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpHeaderParser, parse)
+{
+       php_http_header_parser_object_t *parser_obj;
+       zval *zmsg;
+       char *data_str;
+       int data_len;
+       long flags;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slz", &data_str, &data_len, &flags, &zmsg), invalid_arg, return);
+
+       if (Z_TYPE_P(zmsg) != IS_ARRAY) {
+               zval_dtor(zmsg);
+               array_init(zmsg);
+       }
+       parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       php_http_buffer_append(parser_obj->buffer, data_str, data_len);
+       RETVAL_LONG(php_http_header_parser_parse(parser_obj->parser, parser_obj->buffer, flags, Z_ARRVAL_P(zmsg), NULL, NULL));
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeaderParser_stream, 0, 0, 3)
+       ZEND_ARG_INFO(0, stream)
+       ZEND_ARG_INFO(0, flags)
+       ZEND_ARG_ARRAY_INFO(1, headers, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpHeaderParser, stream)
+{
+       php_http_header_parser_object_t *parser_obj;
+       zend_error_handling zeh;
+       zval *zmsg, *zstream;
+       php_stream *s;
+       long flags;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz", &zstream, &flags, &zmsg), invalid_arg, return);
+
+       zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC);
+       php_stream_from_zval(s, &zstream);
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+
+       if (Z_TYPE_P(zmsg) != IS_ARRAY) {
+               zval_dtor(zmsg);
+               array_init(zmsg);
+       }
+       parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       RETVAL_LONG(php_http_header_parser_parse_stream(parser_obj->parser, parser_obj->buffer, s, flags, Z_ARRVAL_P(zmsg), NULL, NULL));
+}
+
+static zend_function_entry php_http_header_parser_methods[] = {
+               PHP_ME(HttpHeaderParser, getState, ai_HttpHeaderParser_getState, ZEND_ACC_PUBLIC)
+               PHP_ME(HttpHeaderParser, parse, ai_HttpHeaderParser_parse, ZEND_ACC_PUBLIC)
+               PHP_ME(HttpHeaderParser, stream, ai_HttpHeaderParser_stream, ZEND_ACC_PUBLIC)
+               {NULL, NULL, NULL}
+};
+
+PHP_MINIT_FUNCTION(http_header_parser)
+{
+       zend_class_entry ce;
+
+       INIT_NS_CLASS_ENTRY(ce, "http\\Header", "Parser", php_http_header_parser_methods);
+       php_http_header_parser_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
+       memcpy(&php_http_header_parser_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+       php_http_header_parser_class_entry->create_object = php_http_header_parser_object_new;
+       php_http_header_parser_object_handlers.clone_obj = NULL;
+
+       zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("CLEANUP"), PHP_HTTP_HEADER_PARSER_CLEANUP TSRMLS_CC);
+
+       zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_FAILURE"), PHP_HTTP_HEADER_PARSER_STATE_FAILURE TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_START"), PHP_HTTP_HEADER_PARSER_STATE_START TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_KEY"), PHP_HTTP_HEADER_PARSER_STATE_KEY TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_VALUE"), PHP_HTTP_HEADER_PARSER_STATE_VALUE TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_VALUE_EX"), PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_HEADER_DONE"), PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_DONE"), PHP_HTTP_HEADER_PARSER_STATE_DONE TSRMLS_CC);
+
+       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_header_parser.h b/src/php_http_header_parser.h
new file mode 100644 (file)
index 0000000..ed9ecaf
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_HEADER_PARSER_H
+#define PHP_HTTP_HEADER_PARSER_H
+
+#include "php_http_info.h"
+
+typedef enum php_http_header_parser_state {
+       PHP_HTTP_HEADER_PARSER_STATE_FAILURE = FAILURE,
+       PHP_HTTP_HEADER_PARSER_STATE_START = 0,
+       PHP_HTTP_HEADER_PARSER_STATE_KEY,
+       PHP_HTTP_HEADER_PARSER_STATE_VALUE,
+       PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX,
+       PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE,
+       PHP_HTTP_HEADER_PARSER_STATE_DONE
+} php_http_header_parser_state_t;
+
+#define PHP_HTTP_HEADER_PARSER_CLEANUP 0x1
+
+typedef struct php_http_header_parser {
+       zend_ptr_stack stack;
+       php_http_info_t info;
+       struct {
+               char *str;
+               size_t len;
+       } _key;
+       struct {
+               char *str;
+               size_t len;
+       } _val;
+#ifdef ZTS
+       void ***ts;
+#endif
+} php_http_header_parser_t;
+
+PHP_HTTP_API php_http_header_parser_t *php_http_header_parser_init(php_http_header_parser_t *parser TSRMLS_DC);
+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);
+PHP_HTTP_API php_http_header_parser_state_t php_http_headerparser_parse_stream(php_http_header_parser_t *parser, php_http_buffer_t *buffer, php_stream *s, unsigned flags, HashTable *headers, php_http_info_callback_t callback_func, void *callback_arg);
+
+typedef struct php_http_header_parser_object {
+       zend_object zo;
+       zend_object_value zv;
+       php_http_buffer_t *buffer;
+       php_http_header_parser_t *parser;
+} php_http_header_parser_object_t;
+
+PHP_HTTP_API zend_class_entry *php_http_header_parser_class_entry;
+
+PHP_MINIT_FUNCTION(http_header_parser);
+
+zend_object_value php_http_header_parser_object_new(zend_class_entry *ce TSRMLS_DC);
+zend_object_value php_http_header_parser_object_new_ex(zend_class_entry *ce, php_http_header_parser_t *parser, php_http_header_parser_object_t **ptr TSRMLS_DC);
+void php_http_header_parser_object_free(void *object TSRMLS_DC);
+
+#endif /* PHP_HTTP_HEADER_PARSER_H */
+
+/*
+ * 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_info.c b/src/php_http_info.c
new file mode 100644 (file)
index 0000000..4fb067f
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+php_http_info_t *php_http_info_init(php_http_info_t *i TSRMLS_DC)
+{
+       if (!i) {
+               i = emalloc(sizeof(*i));
+       }
+
+       memset(i, 0, sizeof(*i));
+
+       return i;
+}
+
+void php_http_info_dtor(php_http_info_t *i)
+{
+       switch (i->type) {
+               case PHP_HTTP_REQUEST:
+                       PTR_SET(PHP_HTTP_INFO(i).request.method, NULL);
+                       PTR_SET(PHP_HTTP_INFO(i).request.url, NULL);
+                       break;
+               
+               case PHP_HTTP_RESPONSE:
+                       PTR_SET(PHP_HTTP_INFO(i).response.status, NULL);
+                       break;
+               
+               default:
+                       break;
+       }
+}
+
+void php_http_info_free(php_http_info_t **i)
+{
+       if (*i) {
+               php_http_info_dtor(*i);
+               efree(*i);
+               *i = NULL;
+       }
+}
+
+php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_header TSRMLS_DC)
+{
+       const char *end, *http;
+       zend_bool free_info = !info;
+       
+       /* sane parameter */
+       if ((!pre_header) || (!*pre_header)) {
+               return NULL;
+       }
+       
+       /* where's the end of the line */
+       if (!(end = php_http_locate_eol(pre_header, NULL))) {
+               end = pre_header + strlen(pre_header);
+       }
+       
+       /* there must be HTTP/1.x in the line */
+       if (!(http = php_http_locate_str(pre_header, end - pre_header, "HTTP/", lenof("HTTP/")))) {
+               return NULL;
+       }
+       
+       info = php_http_info_init(info TSRMLS_CC);
+
+       /* and nothing than SPACE or NUL after HTTP/X.x */
+       if (!php_http_version_parse(&info->http.version, http TSRMLS_CC)
+       ||      (http[lenof("HTTP/X.x")] && (!PHP_HTTP_IS_CTYPE(space, http[lenof("HTTP/X.x")])))) {
+               if (free_info) {
+                       php_http_info_free(&info);
+               }
+               return NULL;
+       }
+
+#if 0
+       {
+               char *line = estrndup(pre_header, end - pre_header);
+               fprintf(stderr, "http_parse_info('%s')\n", line);
+               efree(line);
+       }
+#endif
+
+       /* is response */
+       if (pre_header == http) {
+               const char *status = NULL, *code = http + sizeof("HTTP/X.x");
+               
+               info->type = PHP_HTTP_RESPONSE;
+               while (' ' == *code) ++code;
+               if (code && 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');
+                       PHP_HTTP_INFO(info).response.code +=     *code++ - '0';
+                       if (PHP_HTTP_INFO(info).response.code < 100 || PHP_HTTP_INFO(info).response.code > 599) {
+                               if (free_info) {
+                                       php_http_info_free(&info);
+                               }
+                               return NULL;
+                       }
+                       status = code;
+               } else {
+                       PHP_HTTP_INFO(info).response.code = 0;
+               }
+               if (status && end > status) {
+                       while (' ' == *status) ++status;
+                       PHP_HTTP_INFO(info).response.status = estrndup(status, end - status);
+               } else {
+                       PHP_HTTP_INFO(info).response.status = NULL;
+               }
+               
+               return info;
+       }
+       
+       /* is request */
+       else if (*(http - 1) == ' ' && (!http[lenof("HTTP/X.x")] || http[lenof("HTTP/X.x")] == '\r' || http[lenof("HTTP/X.x")] == '\n')) {
+               const char *url = strchr(pre_header, ' ');
+               
+               info->type = PHP_HTTP_REQUEST;
+               if (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 (' ' == *(http-1)) --http;
+
+                       if (http > url) {
+                               /* CONNECT presents an authority only */
+                               if (strcasecmp(PHP_HTTP_INFO(info).request.method, "CONNECT")) {
+                                       PHP_HTTP_INFO(info).request.url = php_http_url_parse(url, http - url, ~0 TSRMLS_CC);
+                               } else {
+                                       PHP_HTTP_INFO(info).request.url = php_http_url_parse_authority(url, http - url, ~0 TSRMLS_CC);
+                               }
+                               if (!PHP_HTTP_INFO(info).request.url) {
+                                       PTR_SET(PHP_HTTP_INFO(info).request.method, NULL);
+                                       return NULL;
+                               }
+                       } else {
+                               PTR_SET(PHP_HTTP_INFO(info).request.method, NULL);
+                               return NULL;
+                       }
+               } else {
+                       PHP_HTTP_INFO(info).request.method = NULL;
+                       PHP_HTTP_INFO(info).request.url = NULL;
+               }
+               
+               return info;
+       }
+
+       /* some darn header containing HTTP/X.x */
+       else {
+               if (free_info) {
+                       php_http_info_free(&info);
+               }
+               return NULL;
+       }
+}
+
+/*
+ * 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_info.h b/src/php_http_info.h
new file mode 100644 (file)
index 0000000..4f02908
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_INFO_H
+#define PHP_HTTP_INFO_H
+
+#include "php_http_version.h"
+#include "php_http_url.h"
+
+#define PHP_HTTP_INFO_REQUEST_FMT_ARGS(_http_ptr, tmp, eol) "%s %s HTTP/%u.%u" eol, \
+                               (_http_ptr)->info.request.method?(_http_ptr)->info.request.method:"UNKNOWN", \
+                               (_http_ptr)->info.request.method&&!strcasecmp((_http_ptr)->info.request.method,"CONNECT")?( \
+                               (_http_ptr)->info.request.url?php_http_url_authority_to_string((_http_ptr)->info.request.url, &(tmp), NULL):"0"):( \
+                               (_http_ptr)->info.request.url?php_http_url_to_string((_http_ptr)->info.request.url, &(tmp), NULL, 0):"/"), \
+                               (_http_ptr)->version.major||(_http_ptr)->version.major?(_http_ptr)->version.major:1, \
+                               (_http_ptr)->version.major||(_http_ptr)->version.minor?(_http_ptr)->version.minor:1
+
+#define PHP_HTTP_INFO_RESPONSE_FMT_ARGS(_http_ptr, tmp, eol) "HTTP/%u.%u %d%s%s" eol, \
+                               (_http_ptr)->version.major||(_http_ptr)->version.major?(_http_ptr)->version.major:1, \
+                               (_http_ptr)->version.major||(_http_ptr)->version.minor?(_http_ptr)->version.minor:1, \
+                               (_http_ptr)->info.response.code?(_http_ptr)->info.response.code:200, \
+                               (_http_ptr)->info.response.status&&*(_http_ptr)->info.response.status ? " ":"", \
+                               STR_PTR((_http_ptr)->info.response.status)
+
+typedef struct php_http_info_data {
+       union {
+               /* GET /foo/bar */
+               struct { char *method; php_http_url_t *url; } request;
+               /* 200 Ok */
+               struct { unsigned code; char *status; } response;
+       } info;
+       php_http_version_t version;
+} php_http_info_data_t;
+
+typedef enum php_http_info_type {
+       PHP_HTTP_NONE = 0,
+       PHP_HTTP_REQUEST,
+       PHP_HTTP_RESPONSE
+} php_http_info_type_t;
+
+#define PHP_HTTP_INFO(ptr) (ptr)->http.info
+#define PHP_HTTP_INFO_IMPL(_http, _type) \
+       php_http_info_data_t _http; \
+       php_http_info_type_t _type;
+
+typedef struct php_http_info {
+       PHP_HTTP_INFO_IMPL(http, type)
+} php_http_info_t;
+
+typedef zend_bool (*php_http_info_callback_t)(void **callback_data, HashTable **headers, php_http_info_t *info TSRMLS_DC);
+
+PHP_HTTP_API php_http_info_t *php_http_info_init(php_http_info_t *info TSRMLS_DC);
+PHP_HTTP_API php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_header TSRMLS_DC);
+PHP_HTTP_API void php_http_info_dtor(php_http_info_t *info);
+PHP_HTTP_API void php_http_info_free(php_http_info_t **info);
+
+#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_message.c b/src/php_http_message.c
new file mode 100644 (file)
index 0000000..c6b03ff
--- /dev/null
@@ -0,0 +1,2085 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+static void message_headers(php_http_message_t *msg, php_http_buffer_t *str);
+
+zend_bool php_http_message_info_callback(php_http_message_t **message, HashTable **headers, php_http_info_t *info TSRMLS_DC)
+{
+       php_http_message_t *old = *message;
+
+       /* advance message */
+       if (!old || old->type || zend_hash_num_elements(&old->hdrs)) {
+               (*message) = php_http_message_init(NULL, 0, NULL TSRMLS_CC);
+               (*message)->parent = old;
+               if (headers) {
+                       (*headers) = &((*message)->hdrs);
+               }
+       }
+
+       if (info) {
+               php_http_message_set_info(*message, info);
+       }
+
+       return old != *message;
+}
+
+php_http_message_t *php_http_message_init(php_http_message_t *message, php_http_message_type_t type, php_http_message_body_t *body TSRMLS_DC)
+{
+       if (!message) {
+               message = emalloc(sizeof(*message));
+       }
+       memset(message, 0, sizeof(*message));
+       TSRMLS_SET_CTX(message->ts);
+
+       php_http_message_set_type(message, type);
+       message->http.version.major = 1;
+       message->http.version.minor = 1;
+       zend_hash_init(&message->hdrs, 0, NULL, ZVAL_PTR_DTOR, 0);
+       message->body = body ? body : php_http_message_body_init(NULL, NULL TSRMLS_CC);
+
+       return message;
+}
+
+php_http_message_t *php_http_message_init_env(php_http_message_t *message, php_http_message_type_t type TSRMLS_DC)
+{
+       int free_msg = !message;
+       zval *sval, tval;
+       php_http_message_body_t *mbody;
+       
+       switch (type) {
+               case PHP_HTTP_REQUEST:
+                       mbody = php_http_env_get_request_body(TSRMLS_C);
+                       php_http_message_body_addref(mbody);
+                       message = php_http_message_init(message, type, mbody TSRMLS_CC);
+                       if ((sval = php_http_env_get_server_var(ZEND_STRL("SERVER_PROTOCOL"), 1 TSRMLS_CC)) && !strncmp(Z_STRVAL_P(sval), "HTTP/", lenof("HTTP/"))) {
+                               php_http_version_parse(&message->http.version, Z_STRVAL_P(sval) TSRMLS_CC);
+                       }
+                       if ((sval = php_http_env_get_server_var(ZEND_STRL("REQUEST_METHOD"), 1 TSRMLS_CC))) {
+                               message->http.info.request.method = estrdup(Z_STRVAL_P(sval));
+                       }
+                       if ((sval = php_http_env_get_server_var(ZEND_STRL("REQUEST_URI"), 1 TSRMLS_CC))) {
+                               message->http.info.request.url = php_http_url_parse(Z_STRVAL_P(sval), Z_STRLEN_P(sval), ~0 TSRMLS_CC);
+                       }
+                       
+                       php_http_env_get_request_headers(&message->hdrs TSRMLS_CC);
+                       break;
+                       
+               case PHP_HTTP_RESPONSE:
+                       message = php_http_message_init(NULL, type, NULL TSRMLS_CC);
+                       if (!SG(sapi_headers).http_status_line || !php_http_info_parse((php_http_info_t *) &message->http, SG(sapi_headers).http_status_line TSRMLS_CC)) {
+                               if (!(message->http.info.response.code = SG(sapi_headers).http_response_code)) {
+                                       message->http.info.response.code = 200;
+                               }
+                               message->http.info.response.status = estrdup(php_http_env_get_response_status_for_code(message->http.info.response.code));
+                       }
+                       
+                       php_http_env_get_response_headers(&message->hdrs TSRMLS_CC);
+#if PHP_VERSION_ID >= 50400
+                       if (php_output_get_level(TSRMLS_C)) {
+                               if (php_output_get_status(TSRMLS_C) & PHP_OUTPUT_SENT) {
+                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not fetch response body, output has already been sent at %s:%d", php_output_get_start_filename(TSRMLS_C), php_output_get_start_lineno(TSRMLS_C));
+                                       goto error;
+                               } else if (SUCCESS != php_output_get_contents(&tval TSRMLS_CC)) {
+                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not fetch response body");
+                                       goto error;
+                               } else {
+                                       php_http_message_body_append(message->body, Z_STRVAL(tval), Z_STRLEN(tval));
+                                       zval_dtor(&tval);
+                               }
+                       }
+#else
+                       if (OG(ob_nesting_level)) {
+                               if (php_get_output_start_filename(TSRMLS_C)) {
+                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not fetch response body, output has already been sent at %s:%d", php_get_output_start_filename(TSRMLS_C), php_get_output_start_lineno(TSRMLS_C));
+                                       goto error;
+                               } else if (SUCCESS != php_ob_get_buffer(&tval TSRMLS_CC)) {
+                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not fetch response body");
+                                       goto error;
+                               } else {
+                                       php_http_message_body_append(message->body, Z_STRVAL(tval), Z_STRLEN(tval));
+                                       zval_dtor(&tval);
+                               }
+                       }
+#endif
+                       break;
+                       
+               default:
+               error:
+                       if (free_msg) {
+                               if (message) {
+                                       php_http_message_free(&message);
+                               }
+                       } else {
+                               message = NULL;
+                       }
+                       break;
+       }
+       
+       return message;
+}
+
+php_http_message_t *php_http_message_parse(php_http_message_t *msg, const char *str, size_t len, zend_bool greedy TSRMLS_DC)
+{
+       php_http_message_parser_t p;
+       php_http_buffer_t buf;
+       unsigned flags = PHP_HTTP_MESSAGE_PARSER_CLEANUP;
+       int free_msg;
+
+       php_http_buffer_from_string_ex(&buf, str, len);
+       php_http_message_parser_init(&p TSRMLS_CC);
+
+       if ((free_msg = !msg)) {
+               msg = php_http_message_init(NULL, 0, NULL TSRMLS_CC);
+       }
+
+       if (greedy) {
+               flags |= PHP_HTTP_MESSAGE_PARSER_GREEDY;
+       }
+       if (PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE == php_http_message_parser_parse(&p, &buf, flags, &msg)) {
+               if (free_msg) {
+                       php_http_message_free(&msg);
+               }
+               msg = NULL;
+       }
+
+       php_http_message_parser_dtor(&p);
+       php_http_buffer_dtor(&buf);
+
+       return msg;
+}
+
+zval *php_http_message_header(php_http_message_t *msg, const char *key_str, size_t key_len, int join)
+{
+       zval *ret = NULL, **header;
+       char *key;
+       ALLOCA_FLAG(free_key);
+
+       key = do_alloca(key_len + 1, free_key);
+       memcpy(key, key_str, key_len);
+       key[key_len] = '\0';
+       php_http_pretty_key(key, key_len, 1, 1);
+
+       if (SUCCESS == zend_symtable_find(&msg->hdrs, key, key_len + 1, (void *) &header)) {
+               if (join && Z_TYPE_PP(header) == IS_ARRAY) {
+                       TSRMLS_FETCH_FROM_CTX(msg->ts);
+
+                       ret = php_http_header_value_to_string(*header TSRMLS_CC);
+               } else {
+                       Z_ADDREF_PP(header);
+                       ret = *header;
+               }
+       }
+
+       free_alloca(key, free_key);
+
+       return ret;
+}
+
+zend_bool php_http_message_is_multipart(php_http_message_t *msg, char **boundary)
+{
+       zval *ct = php_http_message_header(msg, ZEND_STRL("Content-Type"), 1);
+       zend_bool is_multipart = 0;
+       TSRMLS_FETCH_FROM_CTX(msg->ts);
+
+       if (ct) {
+               php_http_params_opts_t popts;
+               HashTable params;
+
+               ZEND_INIT_SYMTABLE(&params);
+               php_http_params_opts_default_get(&popts);
+               popts.input.str = Z_STRVAL_P(ct);
+               popts.input.len = Z_STRLEN_P(ct);
+
+               if (php_http_params_parse(&params, &popts TSRMLS_CC)) {
+                       zval **cur, **arg;
+                       char *ct_str;
+
+                       zend_hash_internal_pointer_reset(&params);
+
+                       if (SUCCESS == zend_hash_get_current_data(&params, (void *) &cur)
+                       &&      Z_TYPE_PP(cur) == IS_ARRAY
+                       &&      HASH_KEY_IS_STRING == zend_hash_get_current_key(&params, &ct_str, NULL, 0)
+                       ) {
+                               if (php_http_match(ct_str, "multipart", PHP_HTTP_MATCH_WORD)) {
+                                       is_multipart = 1;
+
+                                       /* get boundary */
+                                       if (boundary
+                                       &&      SUCCESS == zend_hash_find(Z_ARRVAL_PP(cur), ZEND_STRS("arguments"), (void *) &arg)
+                                       &&      Z_TYPE_PP(arg) == IS_ARRAY
+                                       ) {
+                                               zval **val;
+                                               HashPosition pos;
+                                               php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+
+                                               FOREACH_KEYVAL(pos, *arg, key, val) {
+                                                       if (key.type == HASH_KEY_IS_STRING && !strcasecmp(key.str, "boundary")) {
+                                                               zval *bnd = php_http_ztyp(IS_STRING, *val);
+
+                                                               if (Z_STRLEN_P(bnd)) {
+                                                                       *boundary = estrndup(Z_STRVAL_P(bnd), Z_STRLEN_P(bnd));
+                                                               }
+                                                               zval_ptr_dtor(&bnd);
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+               zend_hash_destroy(&params);
+               zval_ptr_dtor(&ct);
+       }
+
+       return is_multipart;
+}
+
+/* */
+void php_http_message_set_type(php_http_message_t *message, php_http_message_type_t type)
+{
+       /* just act if different */
+       if (type != message->type) {
+
+               /* free request info */
+               switch (message->type) {
+                       case PHP_HTTP_REQUEST:
+                               PTR_FREE(message->http.info.request.method);
+                               PTR_FREE(message->http.info.request.url);
+                               break;
+                       
+                       case PHP_HTTP_RESPONSE:
+                               PTR_FREE(message->http.info.response.status);
+                               break;
+                       
+                       default:
+                               break;
+               }
+
+               message->type = type;
+               memset(&message->http, 0, sizeof(message->http));
+       }
+}
+
+void php_http_message_set_info(php_http_message_t *message, php_http_info_t *info)
+{
+       php_http_message_set_type(message, info->type);
+       message->http.version = info->http.version;
+       switch (message->type) {
+               case PHP_HTTP_REQUEST:
+                       PTR_SET(PHP_HTTP_INFO(message).request.url, PHP_HTTP_INFO(info).request.url ? php_http_url_copy(PHP_HTTP_INFO(info).request.url, 0) : NULL);
+                       PTR_SET(PHP_HTTP_INFO(message).request.method, PHP_HTTP_INFO(info).request.method ? estrdup(PHP_HTTP_INFO(info).request.method) : NULL);
+                       break;
+               
+               case PHP_HTTP_RESPONSE:
+                       PHP_HTTP_INFO(message).response.code = PHP_HTTP_INFO(info).response.code;
+                       PTR_SET(PHP_HTTP_INFO(message).response.status, PHP_HTTP_INFO(info).response.status ? estrdup(PHP_HTTP_INFO(info).response.status) : NULL);
+                       break;
+               
+               default:
+                       break;
+       }
+}
+
+void php_http_message_update_headers(php_http_message_t *msg)
+{
+       zval *h;
+       size_t size;
+
+       if (php_http_message_body_stream(msg->body)->readfilters.head) {
+               /* if a read stream filter is attached to the body the caller must also care for the headers */
+       } else if ((h = php_http_message_header(msg, ZEND_STRL("Content-Range"), 0))) {
+               /* don't mess around with a Content-Range message */
+               zval_ptr_dtor(&h);
+       } else if ((size = php_http_message_body_size(msg->body))) {
+               MAKE_STD_ZVAL(h);
+               ZVAL_LONG(h, size);
+               zend_hash_update(&msg->hdrs, "Content-Length", sizeof("Content-Length"), &h, sizeof(zval *), NULL);
+
+               if (msg->body->boundary) {
+                       char *str;
+                       size_t len;
+
+                       if (!(h = php_http_message_header(msg, ZEND_STRL("Content-Type"), 1))) {
+                               len = spprintf(&str, 0, "multipart/form-data; boundary=\"%s\"", msg->body->boundary);
+                               MAKE_STD_ZVAL(h);
+                               ZVAL_STRINGL(h, str, len, 0);
+                               zend_hash_update(&msg->hdrs, "Content-Type", sizeof("Content-Type"), &h, sizeof(zval *), NULL);
+                       } else if (!php_http_match(Z_STRVAL_P(h), "boundary=", PHP_HTTP_MATCH_WORD)) {
+                               zval_dtor(h);
+                               Z_STRLEN_P(h) = spprintf(&Z_STRVAL_P(h), 0, "%s; boundary=\"%s\"", Z_STRVAL_P(h), msg->body->boundary);
+                               zend_hash_update(&msg->hdrs, "Content-Type", sizeof("Content-Type"), &h, sizeof(zval *), NULL);
+                       } else {
+                               zval_ptr_dtor(&h);
+                       }
+               }
+       } else if ((h = php_http_message_header(msg, ZEND_STRL("Content-Length"), 1))) {
+               zval *h_cpy = php_http_ztyp(IS_LONG, h);
+
+               zval_ptr_dtor(&h);
+               if (Z_LVAL_P(h_cpy)) {
+                       /* body->size == 0, so get rid of old Content-Length */
+                       zend_hash_del(&msg->hdrs, "Content-Length", sizeof("Content-Length"));
+               }
+               zval_ptr_dtor(&h_cpy);
+       }
+}
+
+static void message_headers(php_http_message_t *msg, php_http_buffer_t *str)
+{
+       char *tmp = NULL;
+       TSRMLS_FETCH_FROM_CTX(msg->ts);
+
+       switch (msg->type) {
+               case PHP_HTTP_REQUEST:
+                       php_http_buffer_appendf(str, PHP_HTTP_INFO_REQUEST_FMT_ARGS(&msg->http, tmp, PHP_HTTP_CRLF));
+                       PTR_FREE(tmp);
+                       break;
+
+               case PHP_HTTP_RESPONSE:
+                       php_http_buffer_appendf(str, PHP_HTTP_INFO_RESPONSE_FMT_ARGS(&msg->http, tmp, PHP_HTTP_CRLF));
+                       PTR_FREE(tmp);
+                       break;
+
+               default:
+                       break;
+       }
+
+       php_http_message_update_headers(msg);
+       php_http_header_to_string(str, &msg->hdrs TSRMLS_CC);
+}
+
+void php_http_message_to_callback(php_http_message_t *msg, php_http_pass_callback_t cb, void *cb_arg)
+{
+       php_http_buffer_t str;
+
+       php_http_buffer_init_ex(&str, 0x1000, 0);
+       message_headers(msg, &str);
+       cb(cb_arg, str.data, str.used);
+       php_http_buffer_dtor(&str);
+
+       if (php_http_message_body_size(msg->body)) {
+               cb(cb_arg, ZEND_STRL(PHP_HTTP_CRLF));
+               php_http_message_body_to_callback(msg->body, cb, cb_arg, 0, 0);
+       }
+}
+
+void php_http_message_to_string(php_http_message_t *msg, char **string, size_t *length)
+{
+       php_http_buffer_t str;
+       char *data;
+
+       php_http_buffer_init_ex(&str, 0x1000, 0);
+       message_headers(msg, &str);
+       if (php_http_message_body_size(msg->body)) {
+               php_http_buffer_appends(&str, PHP_HTTP_CRLF);
+               php_http_message_body_to_callback(msg->body, (php_http_pass_callback_t) php_http_buffer_append, &str, 0, 0);
+       }
+
+       data = php_http_buffer_data(&str, string, length);
+       if (!string) {
+               efree(data);
+       }
+
+       php_http_buffer_dtor(&str);
+}
+
+void php_http_message_serialize(php_http_message_t *message, char **string, size_t *length)
+{
+       char *buf;
+       php_http_buffer_t str;
+       php_http_message_t *msg;
+
+       php_http_buffer_init(&str);
+
+       msg = message = php_http_message_reverse(message);
+       do {
+               php_http_message_to_callback(message, (php_http_pass_callback_t) php_http_buffer_append, &str);
+               php_http_buffer_appends(&str, PHP_HTTP_CRLF);
+       } while ((message = message->parent));
+       php_http_message_reverse(msg);
+
+       buf = php_http_buffer_data(&str, string, length);
+       if (!string) {
+               efree(buf);
+       }
+
+       php_http_buffer_dtor(&str);
+}
+
+php_http_message_t *php_http_message_reverse(php_http_message_t *msg)
+{
+       int i, c = 0;
+       
+       php_http_message_count(c, msg);
+       
+       if (c > 1) {
+               php_http_message_t *tmp = msg, **arr;
+
+               arr = ecalloc(c, sizeof(**arr));
+               for (i = 0; i < c; ++i) {
+                       arr[i] = tmp;
+                       tmp = tmp->parent;
+               }
+               arr[0]->parent = NULL;
+               for (i = 0; i < c-1; ++i) {
+                       arr[i+1]->parent = arr[i];
+               }
+               
+               msg = arr[c-1];
+               efree(arr);
+       }
+       
+       return msg;
+}
+
+php_http_message_t *php_http_message_zip(php_http_message_t *one, php_http_message_t *two)
+{
+       php_http_message_t *dst = php_http_message_copy(one, NULL), *src = php_http_message_copy(two, NULL), *tmp_dst, *tmp_src, *ret = dst;
+
+       while(dst && src) {
+               tmp_dst = dst->parent;
+               tmp_src = src->parent;
+               dst->parent = src;
+               if (tmp_dst) {
+                       src->parent = tmp_dst;
+               }
+               src = tmp_src;
+               dst = tmp_dst;
+       }
+
+       return ret;
+}
+
+php_http_message_t *php_http_message_copy_ex(php_http_message_t *from, php_http_message_t *to, zend_bool parents)
+{
+       php_http_message_t *temp, *copy = NULL;
+       php_http_info_t info;
+       TSRMLS_FETCH_FROM_CTX(from->ts);
+       
+       if (from) {
+               info.type = from->type;
+               info.http = from->http;
+               
+               copy = temp = php_http_message_init(to, 0, php_http_message_body_copy(from->body, NULL) TSRMLS_CC);
+               php_http_message_set_info(temp, &info);
+               zend_hash_copy(&temp->hdrs, &from->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
+       
+               if (parents) while (from->parent) {
+                       info.type = from->parent->type;
+                       info.http = from->parent->http;
+               
+                       temp->parent = php_http_message_init(NULL, 0, php_http_message_body_copy(from->parent->body, NULL) TSRMLS_CC);
+                       php_http_message_set_info(temp->parent, &info);
+                       zend_hash_copy(&temp->parent->hdrs, &from->parent->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
+               
+                       temp = temp->parent;
+                       from = from->parent;
+               }
+       }
+       
+       return copy;
+}
+
+php_http_message_t *php_http_message_copy(php_http_message_t *from, php_http_message_t *to)
+{
+       return php_http_message_copy_ex(from, to, 1);
+}
+
+void php_http_message_dtor(php_http_message_t *message)
+{
+       if (message) {
+               zend_hash_destroy(&message->hdrs);
+               php_http_message_body_free(&message->body);
+               
+               switch (message->type) {
+                       case PHP_HTTP_REQUEST:
+                               PTR_SET(message->http.info.request.method, NULL);
+                               PTR_SET(message->http.info.request.url, NULL);
+                               break;
+                       
+                       case PHP_HTTP_RESPONSE:
+                               PTR_SET(message->http.info.response.status, NULL);
+                               break;
+                       
+                       default:
+                               break;
+               }
+       }
+}
+
+void php_http_message_free(php_http_message_t **message)
+{
+       if (*message) {
+               if ((*message)->parent) {
+                       php_http_message_free(&(*message)->parent);
+               }
+               php_http_message_dtor(*message);
+               efree(*message);
+               *message = NULL;
+       }
+}
+
+static zval *php_http_message_object_read_prop(zval *object, zval *member, int type PHP_HTTP_ZEND_LITERAL_DC TSRMLS_DC);
+static void php_http_message_object_write_prop(zval *object, zval *member, zval *value PHP_HTTP_ZEND_LITERAL_DC TSRMLS_DC);
+static HashTable *php_http_message_object_get_props(zval *object TSRMLS_DC);
+
+static zend_object_handlers php_http_message_object_handlers;
+static HashTable php_http_message_object_prophandlers;
+
+typedef void (*php_http_message_object_prophandler_func_t)(php_http_message_object_t *o, zval *v TSRMLS_DC);
+
+typedef struct php_http_message_object_prophandler {
+       php_http_message_object_prophandler_func_t read;
+       php_http_message_object_prophandler_func_t write;
+} php_http_message_object_prophandler_t;
+
+static ZEND_RESULT_CODE php_http_message_object_add_prophandler(const char *prop_str, size_t prop_len, php_http_message_object_prophandler_func_t read, php_http_message_object_prophandler_func_t write) {
+       php_http_message_object_prophandler_t h = { read, write };
+       return zend_hash_add(&php_http_message_object_prophandlers, prop_str, prop_len + 1, (void *) &h, sizeof(h), NULL);
+}
+static ZEND_RESULT_CODE php_http_message_object_get_prophandler(const char *prop_str, size_t prop_len, php_http_message_object_prophandler_t **handler) {
+       return zend_hash_find(&php_http_message_object_prophandlers, prop_str, prop_len + 1, (void *) handler);
+}
+static void php_http_message_object_prophandler_get_type(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
+       RETVAL_LONG(obj->message->type);
+}
+static void php_http_message_object_prophandler_set_type(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
+       zval *cpy = php_http_ztyp(IS_LONG, value);
+       php_http_message_set_type(obj->message, Z_LVAL_P(cpy));
+       zval_ptr_dtor(&cpy);
+}
+static void php_http_message_object_prophandler_get_request_method(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
+       if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message) && obj->message->http.info.request.method) {
+               RETVAL_STRING(obj->message->http.info.request.method, 1);
+       } else {
+               RETVAL_NULL();
+       }
+}
+static void php_http_message_object_prophandler_set_request_method(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
+       if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message)) {
+               zval *cpy = php_http_ztyp(IS_STRING, value);
+               PTR_SET(obj->message->http.info.request.method, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
+               zval_ptr_dtor(&cpy);
+       }
+}
+static void php_http_message_object_prophandler_get_request_url(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
+       char *url_str;
+       size_t url_len;
+
+       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_STRINGL(url_str, url_len, 0);
+       } else {
+               RETVAL_NULL();
+       }
+}
+static void php_http_message_object_prophandler_set_request_url(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
+       if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message)) {
+               PTR_SET(obj->message->http.info.request.url, php_http_url_from_zval(value, ~0 TSRMLS_CC));
+       }
+}
+static void php_http_message_object_prophandler_get_response_status(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
+       if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message) && obj->message->http.info.response.status) {
+               RETVAL_STRING(obj->message->http.info.response.status, 1);
+       } else {
+               RETVAL_NULL();
+       }
+}
+static void php_http_message_object_prophandler_set_response_status(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
+       if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message)) {
+               zval *cpy = php_http_ztyp(IS_STRING, value);
+               PTR_SET(obj->message->http.info.response.status, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
+               zval_ptr_dtor(&cpy);
+       }
+}
+static void php_http_message_object_prophandler_get_response_code(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
+       if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message)) {
+               RETVAL_LONG(obj->message->http.info.response.code);
+       } else {
+               RETVAL_NULL();
+       }
+}
+static void php_http_message_object_prophandler_set_response_code(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
+       if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message)) {
+               zval *cpy = php_http_ztyp(IS_LONG, value);
+               obj->message->http.info.response.code = Z_LVAL_P(cpy);
+               PTR_SET(obj->message->http.info.response.status, estrdup(php_http_env_get_response_status_for_code(obj->message->http.info.response.code)));
+               zval_ptr_dtor(&cpy);
+       }
+}
+static void php_http_message_object_prophandler_get_http_version(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
+       char *version_str;
+       size_t version_len;
+
+       php_http_version_to_string(&obj->message->http.version, &version_str, &version_len, NULL, NULL TSRMLS_CC);
+       RETVAL_STRINGL(version_str, version_len, 0);
+}
+static void php_http_message_object_prophandler_set_http_version(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
+       zval *cpy = php_http_ztyp(IS_STRING, value);
+       php_http_version_parse(&obj->message->http.version, Z_STRVAL_P(cpy) TSRMLS_CC);
+       zval_ptr_dtor(&cpy);
+}
+static void php_http_message_object_prophandler_get_headers(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
+       array_init(return_value);
+       zend_hash_copy(Z_ARRVAL_P(return_value), &obj->message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
+}
+static void php_http_message_object_prophandler_set_headers(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
+       zval *cpy = php_http_ztyp(IS_ARRAY, value);
+
+       zend_hash_clean(&obj->message->hdrs);
+       zend_hash_copy(&obj->message->hdrs, Z_ARRVAL_P(cpy), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
+       zval_ptr_dtor(&cpy);
+}
+static void php_http_message_object_prophandler_get_body(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
+       if (obj->body) {
+               RETVAL_OBJVAL(obj->body->zv, 1);
+       } else {
+               RETVAL_NULL();
+       }
+}
+static void php_http_message_object_prophandler_set_body(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
+       php_http_message_object_set_body(obj, value TSRMLS_CC);
+}
+static void php_http_message_object_prophandler_get_parent_message(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
+       if (obj->message->parent) {
+               RETVAL_OBJVAL(obj->parent->zv, 1);
+       } else {
+               RETVAL_NULL();
+       }
+}
+static void php_http_message_object_prophandler_set_parent_message(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
+       if (Z_TYPE_P(value) == IS_OBJECT && instanceof_function(Z_OBJCE_P(value), php_http_message_class_entry TSRMLS_CC)) {
+               php_http_message_object_t *parent_obj = zend_object_store_get_object(value TSRMLS_CC);
+
+               if (obj->message->parent) {
+                       zend_objects_store_del_ref_by_handle(obj->parent->zv.handle TSRMLS_CC);
+               }
+               Z_OBJ_ADDREF_P(value);
+               obj->parent = parent_obj;
+               obj->message->parent = parent_obj->message;
+       }
+}
+
+#define PHP_HTTP_MESSAGE_OBJECT_INIT(obj) \
+       do { \
+               if (!obj->message) { \
+                       obj->message = php_http_message_init(NULL, 0, NULL TSRMLS_CC); \
+               } \
+       } while(0)
+
+
+void php_http_message_object_reverse(zval *this_ptr, zval *return_value TSRMLS_DC)
+{
+       int i = 0;
+       php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+       /* count */
+       php_http_message_count(i, obj->message);
+
+       if (i > 1) {
+               php_http_message_object_t **objects;
+               int last;
+
+               objects = ecalloc(i, sizeof(**objects));
+
+               /* we are the first message */
+               objects[0] = obj;
+
+               /* fetch parents */
+               for (i = 1; obj->parent; ++i) {
+                        objects[i] = obj = obj->parent;
+               }
+
+               /* reorder parents */
+               for (last = --i; i; --i) {
+                       objects[i]->message->parent = objects[i-1]->message;
+                       objects[i]->parent = objects[i-1];
+               }
+
+               objects[0]->message->parent = NULL;
+               objects[0]->parent = NULL;
+
+               /* add ref, because we previously have not been a parent message */
+               Z_OBJ_ADDREF_P(getThis());
+               RETVAL_OBJVAL(objects[last]->zv, 0);
+
+               efree(objects);
+       } else {
+               RETURN_ZVAL(getThis(), 1, 0);
+       }
+}
+
+void php_http_message_object_prepend(zval *this_ptr, zval *prepend, zend_bool top TSRMLS_DC)
+{
+       zval m;
+       php_http_message_t *save_parent_msg = NULL;
+       php_http_message_object_t *save_parent_obj = NULL, *obj = zend_object_store_get_object(this_ptr TSRMLS_CC);
+       php_http_message_object_t *prepend_obj = zend_object_store_get_object(prepend TSRMLS_CC);
+
+       INIT_PZVAL(&m);
+       m.type = IS_OBJECT;
+
+       if (!top) {
+               save_parent_obj = obj->parent;
+               save_parent_msg = obj->message->parent;
+       } else {
+               /* iterate to the most parent object */
+               while (obj->parent) {
+                       obj = obj->parent;
+               }
+       }
+
+       /* prepend */
+       obj->parent = prepend_obj;
+       obj->message->parent = prepend_obj->message;
+
+       /* add ref */
+       zend_objects_store_add_ref(prepend TSRMLS_CC);
+
+       if (!top) {
+               prepend_obj->parent = save_parent_obj;
+               prepend_obj->message->parent = save_parent_msg;
+       }
+}
+
+ZEND_RESULT_CODE php_http_message_object_set_body(php_http_message_object_t *msg_obj, zval *zbody TSRMLS_DC)
+{
+       zval *tmp = NULL;
+       php_stream *s;
+       zend_object_value ov;
+       php_http_message_body_t *body;
+       php_http_message_body_object_t *body_obj;
+
+       switch (Z_TYPE_P(zbody)) {
+               case IS_RESOURCE:
+                       php_stream_from_zval_no_verify(s, &zbody);
+                       if (!s) {
+                               php_http_throw(unexpected_val, "The stream is not a valid resource", NULL);
+                               return FAILURE;
+                       }
+
+                       is_resource:
+
+                       body = php_http_message_body_init(NULL, s TSRMLS_CC);
+                       if (SUCCESS != php_http_new(&ov, php_http_message_body_class_entry, (php_http_new_t) php_http_message_body_object_new_ex, NULL, body, NULL TSRMLS_CC)) {
+                               php_http_message_body_free(&body);
+                               return FAILURE;
+                       }
+                       MAKE_STD_ZVAL(tmp);
+                       ZVAL_OBJVAL(tmp, ov, 0);
+                       zbody = tmp;
+                       break;
+
+               case IS_OBJECT:
+                       if (instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_class_entry TSRMLS_CC)) {
+                               Z_OBJ_ADDREF_P(zbody);
+                               break;
+                       }
+                       /* no break */
+
+               default:
+                       tmp = php_http_ztyp(IS_STRING, zbody);
+                       s = php_stream_temp_new();
+                       php_stream_write(s, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
+                       zval_ptr_dtor(&tmp);
+                       tmp = NULL;
+                       goto is_resource;
+
+       }
+
+       body_obj = zend_object_store_get_object(zbody TSRMLS_CC);
+       if (!body_obj->body) {
+               body_obj->body = php_http_message_body_init(NULL, NULL TSRMLS_CC);
+       }
+       if (msg_obj->body) {
+               zend_objects_store_del_ref_by_handle(msg_obj->body->zv.handle TSRMLS_CC);
+       }
+       if (msg_obj->message) {
+               php_http_message_body_free(&msg_obj->message->body);
+               msg_obj->message->body = php_http_message_body_init(&body_obj->body, NULL TSRMLS_CC);
+       } else {
+               msg_obj->message = php_http_message_init(NULL, 0, php_http_message_body_init(&body_obj->body, NULL TSRMLS_CC) TSRMLS_CC);
+       }
+       msg_obj->body = body_obj;
+
+       if (tmp) {
+               FREE_ZVAL(tmp);
+       }
+       return SUCCESS;
+}
+
+ZEND_RESULT_CODE php_http_message_object_init_body_object(php_http_message_object_t *obj)
+{
+       TSRMLS_FETCH_FROM_CTX(obj->message->ts);
+
+       php_http_message_body_addref(obj->message->body);
+       return php_http_new(NULL, php_http_message_body_class_entry, (php_http_new_t) php_http_message_body_object_new_ex, NULL, obj->message->body, (void *) &obj->body TSRMLS_CC);
+}
+
+zend_object_value php_http_message_object_new(zend_class_entry *ce TSRMLS_DC)
+{
+       return php_http_message_object_new_ex(ce, NULL, NULL TSRMLS_CC);
+}
+
+zend_object_value php_http_message_object_new_ex(zend_class_entry *ce, php_http_message_t *msg, php_http_message_object_t **ptr TSRMLS_DC)
+{
+       php_http_message_object_t *o;
+
+       o = ecalloc(1, sizeof(php_http_message_object_t));
+       zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
+       object_properties_init((zend_object *) o, ce);
+
+       if (ptr) {
+               *ptr = o;
+       }
+
+       if (msg) {
+               o->message = msg;
+               if (msg->parent) {
+                       php_http_message_object_new_ex(ce, msg->parent, &o->parent TSRMLS_CC);
+               }
+               php_http_message_body_object_new_ex(php_http_message_body_class_entry, php_http_message_body_init(&msg->body, NULL TSRMLS_CC), &o->body TSRMLS_CC);
+       }
+
+       o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_message_object_free, NULL TSRMLS_CC);
+       o->zv.handlers = &php_http_message_object_handlers;
+
+       return o->zv;
+}
+
+zend_object_value php_http_message_object_clone(zval *this_ptr TSRMLS_DC)
+{
+       zend_object_value new_ov;
+       php_http_message_object_t *new_obj = NULL;
+       php_http_message_object_t *old_obj = zend_object_store_get_object(this_ptr TSRMLS_CC);
+
+       new_ov = php_http_message_object_new_ex(old_obj->zo.ce, php_http_message_copy(old_obj->message, NULL), &new_obj TSRMLS_CC);
+       zend_objects_clone_members(&new_obj->zo, new_ov, &old_obj->zo, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC);
+
+       return new_ov;
+}
+
+void php_http_message_object_free(void *object TSRMLS_DC)
+{
+       php_http_message_object_t *o = (php_http_message_object_t *) object;
+
+       if (o->iterator) {
+               zval_ptr_dtor(&o->iterator);
+               o->iterator = NULL;
+       }
+       if (o->message) {
+               /* do NOT free recursivly */
+               php_http_message_dtor(o->message);
+               efree(o->message);
+               o->message = NULL;
+       }
+       if (o->parent) {
+               zend_objects_store_del_ref_by_handle(o->parent->zv.handle TSRMLS_CC);
+               o->parent = NULL;
+       }
+       if (o->body) {
+               zend_objects_store_del_ref_by_handle(o->body->zv.handle TSRMLS_CC);
+               o->body = NULL;
+       }
+       zend_object_std_dtor((zend_object *) o TSRMLS_CC);
+       efree(o);
+}
+
+static zval *php_http_message_object_read_prop(zval *object, zval *member, int type PHP_HTTP_ZEND_LITERAL_DC TSRMLS_DC)
+{
+       php_http_message_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
+       php_http_message_object_prophandler_t *handler;
+       zval *return_value, *copy = php_http_ztyp(IS_STRING, member);
+
+       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+       if (SUCCESS == php_http_message_object_get_prophandler(Z_STRVAL_P(copy), Z_STRLEN_P(copy), &handler)) {
+               ALLOC_ZVAL(return_value);
+               Z_SET_REFCOUNT_P(return_value, 0);
+               Z_UNSET_ISREF_P(return_value);
+
+               if (type == BP_VAR_R) {
+                       handler->read(obj, return_value TSRMLS_CC);
+               } else {
+                       php_property_proxy_t *proxy = php_property_proxy_init(object, Z_STRVAL_P(copy), Z_STRLEN_P(copy) TSRMLS_CC);
+                       RETVAL_OBJVAL(php_property_proxy_object_new_ex(php_property_proxy_get_class_entry(), proxy, NULL TSRMLS_CC), 0);
+               }
+       } else {
+               return_value = zend_get_std_object_handlers()->read_property(object, member, type PHP_HTTP_ZEND_LITERAL_CC TSRMLS_CC);
+       }
+
+       zval_ptr_dtor(&copy);
+
+       return return_value;
+}
+
+static void php_http_message_object_write_prop(zval *object, zval *member, zval *value PHP_HTTP_ZEND_LITERAL_DC TSRMLS_DC)
+{
+       php_http_message_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
+       php_http_message_object_prophandler_t *handler;
+       zval *copy = php_http_ztyp(IS_STRING, member);
+
+       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+       if (SUCCESS == php_http_message_object_get_prophandler(Z_STRVAL_P(copy), Z_STRLEN_P(copy), &handler)) {
+               handler->write(obj, value TSRMLS_CC);
+       } else {
+               zend_get_std_object_handlers()->write_property(object, member, value PHP_HTTP_ZEND_LITERAL_CC TSRMLS_CC);
+       }
+
+       zval_ptr_dtor(&copy);
+}
+
+static HashTable *php_http_message_object_get_props(zval *object TSRMLS_DC)
+{
+       zval *headers;
+       php_http_message_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
+       HashTable *props = zend_get_std_object_handlers()->get_properties(object TSRMLS_CC);
+       zval array, *parent, *body;
+       char *ver_str, *url_str = NULL;
+       size_t ver_len, url_len = 0;
+
+       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+       INIT_PZVAL_ARRAY(&array, props);
+       
+#define ASSOC_PROP(ptype, n, val) \
+       do { \
+               zend_property_info *pi; \
+               if (SUCCESS == zend_hash_find(&obj->zo.ce->properties_info, n, sizeof(n), (void *) &pi)) { \
+                       add_assoc_ ##ptype## _ex(&array, pi->name, pi->name_length + 1, val); \
+               } \
+       } while(0) \
+
+#define ASSOC_STRING(name, val) ASSOC_STRINGL(name, val, strlen(val))
+#define ASSOC_STRINGL(name, val, len) ASSOC_STRINGL_EX(name, val, len, 1)
+#define ASSOC_STRINGL_EX(n, val, len, cpy) \
+       do { \
+               zend_property_info *pi; \
+               if (SUCCESS == zend_hash_find(&obj->zo.ce->properties_info, n, sizeof(n), (void *) &pi)) { \
+                       add_assoc_stringl_ex(&array, pi->name, pi->name_length + 1, val, len, cpy); \
+               } \
+       } while(0)
+
+       ASSOC_PROP(long, "type", obj->message->type);
+       ver_len = spprintf(&ver_str, 0, "%u.%u", obj->message->http.version.major, obj->message->http.version.minor);
+       ASSOC_STRINGL_EX("httpVersion", ver_str, ver_len, 0);
+
+       switch (obj->message->type) {
+               case PHP_HTTP_REQUEST:
+                       ASSOC_PROP(long, "responseCode", 0);
+                       ASSOC_STRINGL("responseStatus", "", 0);
+                       ASSOC_STRING("requestMethod", STR_PTR(obj->message->http.info.request.method));
+                       if (obj->message->http.info.request.url) {
+                               php_http_url_to_string(obj->message->http.info.request.url, &url_str, &url_len, 0);
+                               ASSOC_STRINGL_EX("requestUrl", url_str, url_len, 0);
+                       } else {
+                               ASSOC_STRINGL("requestUrl", "", 0);
+                       }
+
+                       break;
+
+               case PHP_HTTP_RESPONSE:
+                       ASSOC_PROP(long, "responseCode", obj->message->http.info.response.code);
+                       ASSOC_STRING("responseStatus", STR_PTR(obj->message->http.info.response.status));
+                       ASSOC_STRINGL("requestMethod", "", 0);
+                       ASSOC_STRINGL("requestUrl", "", 0);
+                       break;
+
+               case PHP_HTTP_NONE:
+               default:
+                       ASSOC_PROP(long, "responseCode", 0);
+                       ASSOC_STRINGL("responseStatus", "", 0);
+                       ASSOC_STRINGL("requestMethod", "", 0);
+                       ASSOC_STRINGL("requestUrl", "", 0);
+                       break;
+       }
+
+       MAKE_STD_ZVAL(headers);
+       array_init(headers);
+       zend_hash_copy(Z_ARRVAL_P(headers), &obj->message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
+       ASSOC_PROP(zval, "headers", headers);
+
+       MAKE_STD_ZVAL(body);
+       if (obj->body) {
+               ZVAL_OBJVAL(body, obj->body->zv, 1);
+       } else {
+               ZVAL_NULL(body);
+       }
+       ASSOC_PROP(zval, "body", body);
+
+       MAKE_STD_ZVAL(parent);
+       if (obj->message->parent) {
+               ZVAL_OBJVAL(parent, obj->parent->zv, 1);
+       } else {
+               ZVAL_NULL(parent);
+       }
+       ASSOC_PROP(zval, "parentMessage", parent);
+
+       return props;
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage___construct, 0, 0, 0)
+       ZEND_ARG_INFO(0, message)
+       ZEND_ARG_INFO(0, greedy)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, __construct)
+{
+       zend_bool greedy = 1;
+       zval *zmessage = NULL;
+       php_http_message_t *msg = NULL;
+       php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       zend_error_handling zeh;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!b", &zmessage, &greedy), invalid_arg, return);
+
+       zend_replace_error_handling(EH_THROW, php_http_exception_bad_message_class_entry, &zeh TSRMLS_CC);
+       if (zmessage && Z_TYPE_P(zmessage) == IS_RESOURCE) {
+               php_stream *s;
+               php_http_message_parser_t p;
+               zend_error_handling zeh;
+
+               zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC);
+               php_stream_from_zval(s, &zmessage);
+               zend_restore_error_handling(&zeh TSRMLS_CC);
+
+               if (s && php_http_message_parser_init(&p TSRMLS_CC)) {
+                       unsigned flags = (greedy ? PHP_HTTP_MESSAGE_PARSER_GREEDY : 0);
+                       php_http_buffer_t buf;
+
+                       php_http_buffer_init_ex(&buf, 0x1000, PHP_HTTP_BUFFER_INIT_PREALLOC);
+                       if (PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE == php_http_message_parser_parse_stream(&p, &buf, s, flags, &msg)) {
+                               if (!EG(exception)) {
+                                       php_http_throw(bad_message, "Could not parse message from stream", NULL);
+                               }
+                       }
+                       php_http_buffer_dtor(&buf);
+                       php_http_message_parser_dtor(&p);
+               }
+
+               if (!msg && !EG(exception)) {
+                       php_http_throw(bad_message, "Empty message received from stream", NULL);
+               }
+       } else if (zmessage) {
+               zmessage = php_http_ztyp(IS_STRING, zmessage);
+               msg = php_http_message_parse(NULL, Z_STRVAL_P(zmessage), Z_STRLEN_P(zmessage), greedy TSRMLS_CC);
+
+               if (!msg && !EG(exception)) {
+                       php_http_throw(bad_message, "Could not parse message: %.*s", MIN(25, Z_STRLEN_P(zmessage)), Z_STRVAL_P(zmessage));
+               }
+               zval_ptr_dtor(&zmessage);
+       }
+
+       if (msg) {
+               php_http_message_dtor(obj->message);
+               obj->message = msg;
+               if (obj->message->parent) {
+                       php_http_message_object_new_ex(Z_OBJCE_P(getThis()), obj->message->parent, &obj->parent TSRMLS_CC);
+               }
+       }
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getBody, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, getBody)
+{
+       php_http_message_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+       if (!obj->body) {
+               php_http_message_object_init_body_object(obj);
+
+       }
+       if (obj->body) {
+               RETVAL_OBJVAL(obj->body->zv, 1);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setBody, 0, 0, 1)
+       ZEND_ARG_OBJ_INFO(0, body, http\\Message\\Body, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, setBody)
+{
+       zval *zbody;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zbody, php_http_message_body_class_entry)) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+               php_http_message_object_prophandler_set_body(obj, zbody TSRMLS_CC);
+       }
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_addBody, 0, 0, 1)
+       ZEND_ARG_OBJ_INFO(0, body, http\\Message\\Body, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, addBody)
+{
+       zval *new_body;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &new_body, php_http_message_body_class_entry)) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+               php_http_message_body_object_t *new_obj = zend_object_store_get_object(new_body TSRMLS_CC);
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+               php_http_message_body_to_callback(new_obj->body, (php_http_pass_callback_t) php_http_message_body_append, obj->message->body, 0, 0);
+       }
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getHeader, 0, 0, 1)
+       ZEND_ARG_INFO(0, header)
+       ZEND_ARG_INFO(0, into_class)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, getHeader)
+{
+       char *header_str;
+       int header_len;
+       zend_class_entry *header_ce = NULL;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|C!", &header_str, &header_len, &header_ce)) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+               zval *header;
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               if ((header = php_http_message_header(obj->message, header_str, header_len, 0))) {
+                       if (!header_ce) {
+                               RETURN_ZVAL(header, 1, 1);
+                       } else if (instanceof_function(header_ce, php_http_header_class_entry TSRMLS_CC)) {
+                               php_http_object_method_t cb;
+                               zval *header_name, **argv[2];
+
+                               MAKE_STD_ZVAL(header_name);
+                               ZVAL_STRINGL(header_name, header_str, header_len, 1);
+
+                               argv[0] = &header_name;
+                               argv[1] = &header;
+
+                               object_init_ex(return_value, header_ce);
+                               php_http_object_method_init(&cb, return_value, ZEND_STRL("__construct") TSRMLS_CC);
+                               php_http_object_method_call(&cb, return_value, NULL, 2, argv TSRMLS_CC);
+                               php_http_object_method_dtor(&cb);
+
+                               zval_ptr_dtor(&header_name);
+                               zval_ptr_dtor(&header);
+
+                               return;
+                       } else {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Class '%s' is not as descendant of http\\Header", header_ce->name);
+                       }
+               }
+       }
+       RETURN_FALSE;
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getHeaders, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, getHeaders)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               array_init(return_value);
+               array_copy(&obj->message->hdrs, Z_ARRVAL_P(return_value));
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setHeader, 0, 0, 1)
+       ZEND_ARG_INFO(0, header)
+       ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, setHeader)
+{
+       zval *zvalue = NULL;
+       char *name_str;
+       int name_len;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z!", &name_str, &name_len, &zvalue)) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+               char *name = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1);
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               if (!zvalue) {
+                       zend_symtable_del(&obj->message->hdrs, name, name_len + 1);
+               } else {
+                       Z_ADDREF_P(zvalue);
+                       zend_symtable_update(&obj->message->hdrs, name, name_len + 1, &zvalue, sizeof(void *), NULL);
+               }
+               efree(name);
+       }
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setHeaders, 0, 0, 1)
+       ZEND_ARG_ARRAY_INFO(0, headers, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, setHeaders)
+{
+       zval *new_headers = NULL;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/!", &new_headers)) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               zend_hash_clean(&obj->message->hdrs);
+               if (new_headers) {
+                       array_join(Z_ARRVAL_P(new_headers), &obj->message->hdrs, 0, ARRAY_JOIN_PRETTIFY|ARRAY_JOIN_STRONLY);
+               }
+       }
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_addHeader, 0, 0, 2)
+       ZEND_ARG_INFO(0, header)
+       ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, addHeader)
+{
+       zval *zvalue;
+       char *name_str;
+       int name_len;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &name_str, &name_len, &zvalue)) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+               char *name = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1);
+               zval *header;
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               Z_ADDREF_P(zvalue);
+               if ((header = php_http_message_header(obj->message, name, name_len, 0))) {
+                       convert_to_array(header);
+                       zend_hash_next_index_insert(Z_ARRVAL_P(header), &zvalue, sizeof(void *), NULL);
+                       zval_ptr_dtor(&header);
+               } else {
+                       zend_symtable_update(&obj->message->hdrs, name, name_len + 1, &zvalue, sizeof(void *), NULL);
+               }
+               efree(name);
+       }
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_addHeaders, 0, 0, 1)
+       ZEND_ARG_ARRAY_INFO(0, headers, 0)
+       ZEND_ARG_INFO(0, append)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, addHeaders)
+{
+       zval *new_headers;
+       zend_bool append = 0;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &new_headers, &append)) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               array_join(Z_ARRVAL_P(new_headers), &obj->message->hdrs, append, ARRAY_JOIN_STRONLY|ARRAY_JOIN_PRETTIFY);
+       }
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getType, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, getType)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               RETURN_LONG(obj->message->type);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setType, 0, 0, 1)
+       ZEND_ARG_INFO(0, type)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, setType)
+{
+       long type;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type)) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               php_http_message_set_type(obj->message, type);
+       }
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getInfo, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, getInfo)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               char *tmp = NULL;
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               switch (obj->message->type) {
+                       case PHP_HTTP_REQUEST:
+                               Z_STRLEN_P(return_value) = spprintf(&Z_STRVAL_P(return_value), 0, PHP_HTTP_INFO_REQUEST_FMT_ARGS(&obj->message->http, tmp, ""));
+                               PTR_FREE(tmp);
+                               break;
+                       case PHP_HTTP_RESPONSE:
+                               Z_STRLEN_P(return_value) = spprintf(&Z_STRVAL_P(return_value), 0, PHP_HTTP_INFO_RESPONSE_FMT_ARGS(&obj->message->http, tmp, ""));
+                               PTR_FREE(tmp);
+                               break;
+                       default:
+                               RETURN_NULL();
+                               break;
+               }
+               Z_TYPE_P(return_value) = IS_STRING;
+               return;
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setInfo, 0, 0, 1)
+       ZEND_ARG_INFO(0, http_info)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, setInfo)
+{
+       char *str;
+       int len;
+       php_http_message_object_t *obj;
+       php_http_info_t inf;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+       if (!php_http_info_parse(&inf, str TSRMLS_CC)) {
+               php_http_throw(bad_header, "Could not parse message info '%s'", str);
+               return;
+       }
+
+       php_http_message_set_info(obj->message, &inf);
+       php_http_info_dtor(&inf);
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getHttpVersion, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, getHttpVersion)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               char *str;
+               size_t len;
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               php_http_version_to_string(&obj->message->http.version, &str, &len, NULL, NULL TSRMLS_CC);
+               RETURN_STRINGL(str, len, 0);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setHttpVersion, 0, 0, 1)
+       ZEND_ARG_INFO(0, http_version)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, setHttpVersion)
+{
+       char *v_str;
+       int v_len;
+       php_http_version_t version;
+       php_http_message_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &v_str, &v_len), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+       php_http_expect(php_http_version_parse(&version, v_str TSRMLS_CC), unexpected_val, return);
+
+       obj->message->http.version = version;
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getResponseCode, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, getResponseCode)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               if (obj->message->type != PHP_HTTP_RESPONSE) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "http\\Message is not if type response");
+                       RETURN_FALSE;
+               }
+
+               RETURN_LONG(obj->message->http.info.response.code);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setResponseCode, 0, 0, 1)
+       ZEND_ARG_INFO(0, response_code)
+       ZEND_ARG_INFO(0, strict)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, setResponseCode)
+{
+       long code;
+       zend_bool strict = 1;
+       php_http_message_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|b", &code, &strict), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+       if (obj->message->type != PHP_HTTP_RESPONSE) {
+               php_http_throw(bad_method_call, "http\\Message is not of type response", NULL);
+               return;
+       }
+
+       if (strict && (code < 100 || code > 599)) {
+               php_http_throw(invalid_arg, "Invalid response code (100-599): %ld", code);
+               return;
+       }
+
+       obj->message->http.info.response.code = code;
+       PTR_SET(obj->message->http.info.response.status, estrdup(php_http_env_get_response_status_for_code(code)));
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getResponseStatus, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, getResponseStatus)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               if (obj->message->type != PHP_HTTP_RESPONSE) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "http\\Message is not of type response");
+               }
+
+               if (obj->message->http.info.response.status) {
+                       RETURN_STRING(obj->message->http.info.response.status, 1);
+               } else {
+                       RETURN_EMPTY_STRING();
+               }
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setResponseStatus, 0, 0, 1)
+       ZEND_ARG_INFO(0, response_status)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, setResponseStatus)
+{
+       char *status;
+       int status_len;
+       php_http_message_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &status, &status_len), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+       if (obj->message->type != PHP_HTTP_RESPONSE) {
+               php_http_throw(bad_method_call, "http\\Message is not of type response", NULL);
+       }
+
+       PTR_SET(obj->message->http.info.response.status, estrndup(status, status_len));
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getRequestMethod, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, getRequestMethod)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               if (obj->message->type != PHP_HTTP_REQUEST) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "http\\Message is not of type request");
+                       RETURN_FALSE;
+               }
+
+               if (obj->message->http.info.request.method) {
+                       RETURN_STRING(obj->message->http.info.request.method, 1);
+               } else {
+                       RETURN_EMPTY_STRING();
+               }
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setRequestMethod, 0, 0, 1)
+       ZEND_ARG_INFO(0, request_method)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, setRequestMethod)
+{
+       char *method;
+       int method_len;
+       php_http_message_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+       if (obj->message->type != PHP_HTTP_REQUEST) {
+               php_http_throw(bad_method_call, "http\\Message is not of type request", NULL);
+               return;
+       }
+
+       if (method_len < 1) {
+               php_http_throw(invalid_arg, "Cannot set http\\Message's request method to an empty string", NULL);
+               return;
+       }
+
+       PTR_SET(obj->message->http.info.request.method, estrndup(method, method_len));
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getRequestUrl, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, getRequestUrl)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               if (obj->message->type != PHP_HTTP_REQUEST) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "http\\Message is not of type request");
+                       RETURN_FALSE;
+               }
+
+               if (obj->message->http.info.request.url) {
+                       char *url_str;
+                       size_t url_len;
+
+                       php_http_url_to_string(obj->message->http.info.request.url, &url_str, &url_len, 0);
+                       RETURN_STRINGL(url_str, url_len, 0);
+               } else {
+                       RETURN_EMPTY_STRING();
+               }
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setRequestUrl, 0, 0, 1)
+       ZEND_ARG_INFO(0, url)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, setRequestUrl)
+{
+       zval *zurl;
+       php_http_url_t *url;
+       php_http_message_object_t *obj;
+       zend_error_handling zeh;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zurl), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+       if (obj->message->type != PHP_HTTP_REQUEST) {
+               php_http_throw(bad_method_call, "http\\Message is not of type request", NULL);
+               return;
+       }
+
+       zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC);
+       url = php_http_url_from_zval(zurl, ~0 TSRMLS_CC);
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+
+       if (url && php_http_url_is_empty(url)) {
+               php_http_url_free(&url);
+               php_http_throw(invalid_arg, "Cannot set http\\Message's request url to an empty string", NULL);
+       } else if (url) {
+               PTR_SET(obj->message->http.info.request.url, url);
+       }
+
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getParentMessage, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, getParentMessage)
+{
+       php_http_message_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+       if (!obj->message->parent) {
+               php_http_throw(unexpected_val, "http\\Message has not parent message", NULL);
+               return;
+       }
+
+       RETVAL_OBJVAL(obj->parent->zv, 1);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage___toString, 0, 0, 0)
+ZEND_END_ARG_INFO();
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_toString, 0, 0, 0)
+       ZEND_ARG_INFO(0, include_parent)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, toString)
+{
+       zend_bool include_parent = 0;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &include_parent)) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+               char *string;
+               size_t length;
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               if (include_parent) {
+                       php_http_message_serialize(obj->message, &string, &length);
+               } else {
+                       php_http_message_to_string(obj->message, &string, &length);
+               }
+               if (string) {
+                       RETURN_STRINGL(string, length, 0);
+               }
+       }
+       RETURN_EMPTY_STRING();
+}
+
+#ifdef ZTS
+static size_t write_to_stream(void *s, const char *str, size_t len)
+{
+       TSRMLS_FETCH();
+       return php_stream_write(s, str, len);
+}
+#else
+#      define write_to_stream (php_http_pass_callback_t)_php_stream_write
+#endif
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_toStream, 0, 0, 1)
+       ZEND_ARG_INFO(0, stream)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, toStream)
+{
+       zval *zstream;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zstream)) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+               php_stream *s;
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               php_stream_from_zval(s, &zstream);
+               php_http_message_to_callback(obj->message, write_to_stream, s);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_toCallback, 0, 0, 1)
+       ZEND_ARG_INFO(0, callback)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, toCallback)
+{
+       php_http_pass_fcall_arg_t fcd;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f", &fcd.fci, &fcd.fcc)) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               fcd.fcz = getThis();
+               Z_ADDREF_P(fcd.fcz);
+               TSRMLS_SET_CTX(fcd.ts);
+
+               php_http_message_to_callback(obj->message, php_http_pass_fcall_callback, &fcd);
+               zend_fcall_info_args_clear(&fcd.fci, 1);
+
+               zval_ptr_dtor(&fcd.fcz);
+               RETURN_ZVAL(getThis(), 1, 0);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_serialize, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, serialize)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+               char *string;
+               size_t length;
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               php_http_message_serialize(obj->message, &string, &length);
+               RETURN_STRINGL(string, length, 0);
+       }
+       RETURN_EMPTY_STRING();
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_unserialize, 0, 0, 1)
+       ZEND_ARG_INFO(0, serialized)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, unserialize)
+{
+       int length;
+       char *serialized;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &serialized, &length)) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+               php_http_message_t *msg;
+
+               if (obj->message) {
+                       php_http_message_dtor(obj->message);
+                       efree(obj->message);
+               }
+               if ((msg = php_http_message_parse(NULL, serialized, (size_t) length, 1 TSRMLS_CC))) {
+                       obj->message = msg;
+               } else {
+                       obj->message = php_http_message_init(NULL, 0, NULL TSRMLS_CC);
+                       php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not unserialize http\\Message");
+               }
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_detach, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, detach)
+{
+       php_http_message_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+       RETVAL_OBJVAL(php_http_message_object_new_ex(obj->zo.ce, php_http_message_copy_ex(obj->message, NULL, 0), NULL TSRMLS_CC), 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_prepend, 0, 0, 1)
+       ZEND_ARG_OBJ_INFO(0, message, http\\Message, 0)
+       ZEND_ARG_INFO(0, top)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, prepend)
+{
+       zval *prepend;
+       zend_bool top = 1;
+       php_http_message_t *msg[2];
+       php_http_message_object_t *obj, *prepend_obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|b", &prepend, php_http_message_class_entry, &top), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+       prepend_obj = zend_object_store_get_object(prepend TSRMLS_CC);
+       PHP_HTTP_MESSAGE_OBJECT_INIT(prepend_obj);
+
+       /* safety check */
+       for (msg[0] = obj->message; msg[0]; msg[0] = msg[0]->parent) {
+               for (msg[1] = prepend_obj->message; msg[1]; msg[1] = msg[1]->parent) {
+                       if (msg[0] == msg[1]) {
+                               php_http_throw(unexpected_val, "Cannot prepend a message located within the same message chain", NULL);
+                               return;
+                       }
+               }
+       }
+
+       php_http_message_object_prepend(getThis(), prepend, top TSRMLS_CC);
+       RETURN_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_reverse, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, reverse)
+{
+       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
+
+       php_http_message_object_reverse(getThis(), return_value TSRMLS_CC);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_isMultipart, 0, 0, 0)
+       ZEND_ARG_INFO(1, boundary)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, isMultipart)
+{
+       zval *zboundary = NULL;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z", &zboundary)) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+               char *boundary = NULL;
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               RETVAL_BOOL(php_http_message_is_multipart(obj->message, zboundary ? &boundary : NULL));
+
+               if (zboundary && boundary) {
+                       zval_dtor(zboundary);
+                       ZVAL_STRING(zboundary, boundary, 0);
+               }
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_splitMultipartBody, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, splitMultipartBody)
+{
+       php_http_message_object_t *obj;
+       php_http_message_t *msg;
+       char *boundary = NULL;
+
+       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+       if (!php_http_message_is_multipart(obj->message, &boundary)) {
+               php_http_throw(bad_method_call, "http\\Message is not a multipart message", NULL);
+               return;
+       }
+
+       php_http_expect(msg = php_http_message_body_split(obj->message->body, boundary), bad_message, return);
+
+       PTR_FREE(boundary);
+
+       RETURN_OBJVAL(php_http_message_object_new_ex(php_http_message_class_entry, msg, NULL TSRMLS_CC), 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_count, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, count)
+{
+       long count_mode = -1;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &count_mode)) {
+               long i = 0;
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
+
+               php_http_message_count(i, obj->message);
+               RETURN_LONG(i);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_rewind, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, rewind)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               zval *zobj = getThis();
+               php_http_message_object_t *obj = zend_object_store_get_object(zobj TSRMLS_CC);
+
+               if (obj->iterator) {
+                       zval_ptr_dtor(&obj->iterator);
+               }
+               Z_ADDREF_P(zobj);
+               obj->iterator = zobj;
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_valid, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, valid)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               RETURN_BOOL(obj->iterator != NULL);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_next, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, next)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               if (obj->iterator) {
+                       php_http_message_object_t *itr = zend_object_store_get_object(obj->iterator TSRMLS_CC);
+
+                       if (itr && itr->parent) {
+                               zval *old = obj->iterator;
+                               MAKE_STD_ZVAL(obj->iterator);
+                               ZVAL_OBJVAL(obj->iterator, itr->parent->zv, 1);
+                               zval_ptr_dtor(&old);
+                       } else {
+                               zval_ptr_dtor(&obj->iterator);
+                               obj->iterator = NULL;
+                       }
+               }
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_key, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, key)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               RETURN_LONG(obj->iterator ? obj->iterator->value.obj.handle:0);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_current, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessage, current)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               if (obj->iterator) {
+                       RETURN_ZVAL(obj->iterator, 1, 0);
+               }
+       }
+}
+
+static zend_function_entry php_http_message_methods[] = {
+       PHP_ME(HttpMessage, __construct,        ai_HttpMessage___construct,        ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+       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)
+       PHP_ME(HttpMessage, getHeader,          ai_HttpMessage_getHeader,          ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, setHeader,          ai_HttpMessage_setHeader,          ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, addHeader,          ai_HttpMessage_addHeader,          ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, getHeaders,         ai_HttpMessage_getHeaders,         ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, setHeaders,         ai_HttpMessage_setHeaders,         ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, addHeaders,         ai_HttpMessage_addHeaders,         ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, getType,            ai_HttpMessage_getType,            ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, setType,            ai_HttpMessage_setType,            ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, getInfo,            ai_HttpMessage_getInfo,            ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, setInfo,            ai_HttpMessage_setInfo,            ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, getResponseCode,    ai_HttpMessage_getResponseCode,    ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, setResponseCode,    ai_HttpMessage_setResponseCode,    ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, getResponseStatus,  ai_HttpMessage_getResponseStatus,  ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, setResponseStatus,  ai_HttpMessage_setResponseStatus,  ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, getRequestMethod,   ai_HttpMessage_getRequestMethod,   ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, setRequestMethod,   ai_HttpMessage_setRequestMethod,   ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, getRequestUrl,      ai_HttpMessage_getRequestUrl,      ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, setRequestUrl,      ai_HttpMessage_setRequestUrl,      ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, getHttpVersion,     ai_HttpMessage_getHttpVersion,     ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, setHttpVersion,     ai_HttpMessage_setHttpVersion,     ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, getParentMessage,   ai_HttpMessage_getParentMessage,   ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, toString,           ai_HttpMessage_toString,           ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, toCallback,         ai_HttpMessage_toCallback,         ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, toStream,           ai_HttpMessage_toStream,           ZEND_ACC_PUBLIC)
+
+       /* implements Countable */
+       PHP_ME(HttpMessage, count,              ai_HttpMessage_count,              ZEND_ACC_PUBLIC)
+
+       /* implements Serializable */
+       PHP_ME(HttpMessage, serialize,          ai_HttpMessage_serialize,          ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, unserialize,        ai_HttpMessage_unserialize,        ZEND_ACC_PUBLIC)
+
+       /* implements Iterator */
+       PHP_ME(HttpMessage, rewind,             ai_HttpMessage_rewind,             ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, valid,              ai_HttpMessage_valid,              ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, current,            ai_HttpMessage_current,            ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, key,                ai_HttpMessage_key,                ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, next,               ai_HttpMessage_next,               ZEND_ACC_PUBLIC)
+
+       ZEND_MALIAS(HttpMessage, __toString, toString, ai_HttpMessage___toString,  ZEND_ACC_PUBLIC)
+
+       PHP_ME(HttpMessage, detach,             ai_HttpMessage_detach,             ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, prepend,            ai_HttpMessage_prepend,            ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, reverse,            ai_HttpMessage_reverse,            ZEND_ACC_PUBLIC)
+
+       PHP_ME(HttpMessage, isMultipart,        ai_HttpMessage_isMultipart,        ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessage, splitMultipartBody, ai_HttpMessage_splitMultipartBody, ZEND_ACC_PUBLIC)
+
+       EMPTY_FUNCTION_ENTRY
+};
+
+zend_class_entry *php_http_message_class_entry;
+
+PHP_MINIT_FUNCTION(http_message)
+{
+       zend_class_entry ce = {0};
+
+       INIT_NS_CLASS_ENTRY(ce, "http", "Message", php_http_message_methods);
+       php_http_message_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
+       php_http_message_class_entry->create_object = php_http_message_object_new;
+       memcpy(&php_http_message_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+       php_http_message_object_handlers.clone_obj = php_http_message_object_clone;
+       php_http_message_object_handlers.read_property = php_http_message_object_read_prop;
+       php_http_message_object_handlers.write_property = php_http_message_object_write_prop;
+       php_http_message_object_handlers.get_properties = php_http_message_object_get_props;
+       php_http_message_object_handlers.get_property_ptr_ptr = NULL;
+
+       zend_class_implements(php_http_message_class_entry TSRMLS_CC, 3, spl_ce_Countable, zend_ce_serializable, zend_ce_iterator);
+
+       zend_hash_init(&php_http_message_object_prophandlers, 9, NULL, NULL, 1);
+       zend_declare_property_long(php_http_message_class_entry, ZEND_STRL("type"), PHP_HTTP_NONE, ZEND_ACC_PROTECTED TSRMLS_CC);
+       php_http_message_object_add_prophandler(ZEND_STRL("type"), php_http_message_object_prophandler_get_type, php_http_message_object_prophandler_set_type);
+       zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("body"), ZEND_ACC_PROTECTED TSRMLS_CC);
+       php_http_message_object_add_prophandler(ZEND_STRL("body"), php_http_message_object_prophandler_get_body, php_http_message_object_prophandler_set_body);
+       zend_declare_property_string(php_http_message_class_entry, ZEND_STRL("requestMethod"), "", ZEND_ACC_PROTECTED TSRMLS_CC);
+       php_http_message_object_add_prophandler(ZEND_STRL("requestMethod"), php_http_message_object_prophandler_get_request_method, php_http_message_object_prophandler_set_request_method);
+       zend_declare_property_string(php_http_message_class_entry, ZEND_STRL("requestUrl"), "", ZEND_ACC_PROTECTED TSRMLS_CC);
+       php_http_message_object_add_prophandler(ZEND_STRL("requestUrl"), php_http_message_object_prophandler_get_request_url, php_http_message_object_prophandler_set_request_url);
+       zend_declare_property_string(php_http_message_class_entry, ZEND_STRL("responseStatus"), "", ZEND_ACC_PROTECTED TSRMLS_CC);
+       php_http_message_object_add_prophandler(ZEND_STRL("responseStatus"), php_http_message_object_prophandler_get_response_status, php_http_message_object_prophandler_set_response_status);
+       zend_declare_property_long(php_http_message_class_entry, ZEND_STRL("responseCode"), 0, ZEND_ACC_PROTECTED TSRMLS_CC);
+       php_http_message_object_add_prophandler(ZEND_STRL("responseCode"), php_http_message_object_prophandler_get_response_code, php_http_message_object_prophandler_set_response_code);
+       zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("httpVersion"), ZEND_ACC_PROTECTED TSRMLS_CC);
+       php_http_message_object_add_prophandler(ZEND_STRL("httpVersion"), php_http_message_object_prophandler_get_http_version, php_http_message_object_prophandler_set_http_version);
+       zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("headers"), ZEND_ACC_PROTECTED TSRMLS_CC);
+       php_http_message_object_add_prophandler(ZEND_STRL("headers"), php_http_message_object_prophandler_get_headers, php_http_message_object_prophandler_set_headers);
+       zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("parentMessage"), ZEND_ACC_PROTECTED TSRMLS_CC);
+       php_http_message_object_add_prophandler(ZEND_STRL("parentMessage"), php_http_message_object_prophandler_get_parent_message, php_http_message_object_prophandler_set_parent_message);
+
+       zend_declare_class_constant_long(php_http_message_class_entry, ZEND_STRL("TYPE_NONE"), PHP_HTTP_NONE TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_message_class_entry, ZEND_STRL("TYPE_REQUEST"), PHP_HTTP_REQUEST TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_message_class_entry, ZEND_STRL("TYPE_RESPONSE"), PHP_HTTP_RESPONSE TSRMLS_CC);
+
+       return SUCCESS;
+}
+
+PHP_MSHUTDOWN_FUNCTION(http_message)
+{
+       zend_hash_destroy(&php_http_message_object_prophandlers);
+
+       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_message.h b/src/php_http_message.h
new file mode 100644 (file)
index 0000000..780ea68
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_MESSAGE_H
+#define PHP_HTTP_MESSAGE_H
+
+#include "php_http_message_body.h"
+
+/* required minimum length of an HTTP message "HTTP/1.1" */
+#define PHP_HTTP_MESSAGE_MIN_SIZE 8
+#define PHP_HTTP_MESSAGE_TYPE(TYPE, msg) ((msg) && ((msg)->type == PHP_HTTP_ ##TYPE))
+
+typedef php_http_info_type_t php_http_message_type_t;
+typedef struct php_http_message php_http_message_t;
+
+struct php_http_message {
+       PHP_HTTP_INFO_IMPL(http, type)
+       HashTable hdrs;
+       php_http_message_body_t *body;
+       php_http_message_t *parent;
+       void *opaque;
+#ifdef ZTS
+       void ***ts;
+#endif
+};
+
+PHP_HTTP_API zend_bool php_http_message_info_callback(php_http_message_t **message, HashTable **headers, php_http_info_t *info TSRMLS_DC);
+
+PHP_HTTP_API php_http_message_t *php_http_message_init(php_http_message_t *m, php_http_message_type_t t, php_http_message_body_t *body TSRMLS_DC);
+PHP_HTTP_API php_http_message_t *php_http_message_init_env(php_http_message_t *m, php_http_message_type_t t TSRMLS_DC);
+PHP_HTTP_API php_http_message_t *php_http_message_copy(php_http_message_t *from, php_http_message_t *to);
+PHP_HTTP_API php_http_message_t *php_http_message_copy_ex(php_http_message_t *from, php_http_message_t *to, zend_bool parents);
+PHP_HTTP_API void php_http_message_dtor(php_http_message_t *message);
+PHP_HTTP_API void php_http_message_free(php_http_message_t **message);
+
+PHP_HTTP_API void php_http_message_set_type(php_http_message_t *m, php_http_message_type_t t);
+PHP_HTTP_API void php_http_message_set_info(php_http_message_t *message, php_http_info_t *info);
+
+PHP_HTTP_API void php_http_message_update_headers(php_http_message_t *msg);
+
+PHP_HTTP_API zval *php_http_message_header(php_http_message_t *msg, const char *key_str, size_t key_len, int join);
+PHP_HTTP_API zend_bool php_http_message_is_multipart(php_http_message_t *msg, char **boundary);
+
+PHP_HTTP_API void php_http_message_to_string(php_http_message_t *msg, char **string, size_t *length);
+PHP_HTTP_API void php_http_message_to_struct(php_http_message_t *msg, zval *strct);
+PHP_HTTP_API void php_http_message_to_callback(php_http_message_t *msg, php_http_pass_callback_t cb, void *cb_arg);
+
+PHP_HTTP_API void php_http_message_serialize(php_http_message_t *message, char **string, size_t *length);
+PHP_HTTP_API php_http_message_t *php_http_message_reverse(php_http_message_t *msg);
+PHP_HTTP_API php_http_message_t *php_http_message_zip(php_http_message_t *one, php_http_message_t *two);
+
+#define php_http_message_count(c, m) \
+{ \
+       php_http_message_t *__tmp_msg = (m); \
+       for (c = 0; __tmp_msg; __tmp_msg = __tmp_msg->parent, ++(c)); \
+}
+
+PHP_HTTP_API php_http_message_t *php_http_message_parse(php_http_message_t *msg, const char *str, size_t len, zend_bool greedy TSRMLS_DC);
+
+typedef struct php_http_message_object {
+       zend_object zo;
+       zend_object_value zv;
+       php_http_message_t *message;
+       struct php_http_message_object *parent;
+       php_http_message_body_object_t *body;
+       zval *iterator;
+} php_http_message_object_t;
+
+PHP_HTTP_API zend_class_entry *php_http_message_class_entry;
+
+PHP_MINIT_FUNCTION(http_message);
+PHP_MSHUTDOWN_FUNCTION(http_message);
+
+void php_http_message_object_prepend(zval *this_ptr, zval *prepend, zend_bool top /* = 1 */ TSRMLS_DC);
+void php_http_message_object_reverse(zval *this_ptr, zval *return_value TSRMLS_DC);
+ZEND_RESULT_CODE php_http_message_object_set_body(php_http_message_object_t *obj, zval *zbody TSRMLS_DC);
+ZEND_RESULT_CODE php_http_message_object_init_body_object(php_http_message_object_t *obj);
+
+zend_object_value php_http_message_object_new(zend_class_entry *ce TSRMLS_DC);
+zend_object_value php_http_message_object_new_ex(zend_class_entry *ce, php_http_message_t *msg, php_http_message_object_t **ptr TSRMLS_DC);
+zend_object_value php_http_message_object_clone(zval *object TSRMLS_DC);
+void php_http_message_object_free(void *object TSRMLS_DC);
+
+#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_message_body.c b/src/php_http_message_body.c
new file mode 100644 (file)
index 0000000..c80c238
--- /dev/null
@@ -0,0 +1,934 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+#include <ext/standard/php_lcg.h>
+
+#define BOUNDARY_OPEN(body) \
+       do {\
+               size_t size = php_http_message_body_size(body); \
+               if (size) { \
+                       php_stream_truncate_set_size(php_http_message_body_stream(body), size - lenof("--" PHP_HTTP_CRLF)); \
+                       php_http_message_body_append(body, ZEND_STRL(PHP_HTTP_CRLF)); \
+               } else { \
+                       php_http_message_body_appendf(body, "--%s" PHP_HTTP_CRLF, php_http_message_body_boundary(body)); \
+               } \
+       } while(0)
+
+#define BOUNDARY_CLOSE(body) \
+               php_http_message_body_appendf(body, PHP_HTTP_CRLF "--%s--" PHP_HTTP_CRLF, php_http_message_body_boundary(body))
+
+static ZEND_RESULT_CODE add_recursive_fields(php_http_message_body_t *body, const char *name, zval *value);
+static ZEND_RESULT_CODE add_recursive_files(php_http_message_body_t *body, const char *name, zval *value);
+
+php_http_message_body_t *php_http_message_body_init(php_http_message_body_t **body_ptr, php_stream *stream TSRMLS_DC)
+{
+       php_http_message_body_t *body;
+
+       if (body_ptr && *body_ptr) {
+               body = *body_ptr;
+               ++body->refcount;
+               return body;
+       }
+       
+       body = ecalloc(1, sizeof(php_http_message_body_t));
+       body->refcount = 1;
+
+       if (stream) {
+               php_stream_auto_cleanup(stream);
+               body->stream_id = php_stream_get_resource_id(stream);
+               zend_list_addref(body->stream_id);
+       } else {
+               stream = php_stream_temp_create(TEMP_STREAM_DEFAULT, 0xffff);
+               php_stream_auto_cleanup(stream);
+               body->stream_id = php_stream_get_resource_id(stream);
+       }
+       TSRMLS_SET_CTX(body->ts);
+
+       if (body_ptr) {
+               *body_ptr = body;
+       }
+
+       return body;
+}
+
+unsigned php_http_message_body_addref(php_http_message_body_t *body)
+{
+       return ++body->refcount;
+}
+
+php_http_message_body_t *php_http_message_body_copy(php_http_message_body_t *from, php_http_message_body_t *to)
+{
+       if (from) {
+               TSRMLS_FETCH_FROM_CTX(from->ts);
+               
+               if (to) {
+                       php_stream_truncate_set_size(php_http_message_body_stream(to), 0);
+               } else {
+                       to = php_http_message_body_init(NULL, NULL TSRMLS_CC);
+               }
+               php_http_message_body_to_stream(from, php_http_message_body_stream(to), 0, 0);
+
+               if (to->boundary) {
+                       efree(to->boundary);
+               }
+               if (from->boundary) {
+                       to->boundary = estrdup(from->boundary);
+               }
+       } else {
+               to = NULL;
+       }
+       return to;
+}
+
+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) {
+                       TSRMLS_FETCH_FROM_CTX(body->ts);
+                       /* NOFIXME: shows leakinfo in DEBUG mode */
+                       zend_list_delete(body->stream_id);
+                       PTR_FREE(body->boundary);
+                       efree(body);
+               }
+               *body_ptr = NULL;
+       }
+}
+
+const php_stream_statbuf *php_http_message_body_stat(php_http_message_body_t *body)
+{
+       TSRMLS_FETCH_FROM_CTX(body->ts);
+       php_stream_stat(php_http_message_body_stream(body), &body->ssb);
+       return &body->ssb;
+}
+
+const char *php_http_message_body_boundary(php_http_message_body_t *body)
+{
+       if (!body->boundary) {
+               union { double dbl; int num[2]; } data;
+               TSRMLS_FETCH_FROM_CTX(body->ts);
+
+               data.dbl = php_combined_lcg(TSRMLS_C);
+               spprintf(&body->boundary, 0, "%x.%x", data.num[0], data.num[1]);
+       }
+       return body->boundary;
+}
+
+char *php_http_message_body_etag(php_http_message_body_t *body)
+{
+       php_http_etag_t *etag;
+       php_stream *s = php_http_message_body_stream(body);
+       TSRMLS_FETCH_FROM_CTX(body->ts);
+
+       /* real file or temp buffer ? */
+       if (s->ops != &php_stream_temp_ops && s->ops != &php_stream_memory_ops) {
+               php_stream_stat(php_http_message_body_stream(body), &body->ssb);
+
+               if (body->ssb.sb.st_mtime) {
+                       char *etag;
+
+                       spprintf(&etag, 0, "%lx-%lx-%lx", body->ssb.sb.st_ino, body->ssb.sb.st_mtime, body->ssb.sb.st_size);
+                       return etag;
+               }
+       }
+
+       /* content based */
+       if ((etag = php_http_etag_init(PHP_HTTP_G->env.etag_mode TSRMLS_CC))) {
+               php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_etag_update, etag, 0, 0);
+               return php_http_etag_finish(etag);
+       }
+
+       return NULL;
+}
+
+void php_http_message_body_to_string(php_http_message_body_t *body, char **buf, size_t *len, off_t offset, size_t forlen)
+{
+       php_stream *s = php_http_message_body_stream(body);
+       TSRMLS_FETCH_FROM_CTX(body->ts);
+
+       php_stream_seek(s, offset, SEEK_SET);
+       if (!forlen) {
+               forlen = -1;
+       }
+       *len = php_stream_copy_to_mem(s, buf, forlen, 0);
+}
+
+ZEND_RESULT_CODE php_http_message_body_to_stream(php_http_message_body_t *body, php_stream *dst, off_t offset, size_t forlen)
+{
+       php_stream *s = php_http_message_body_stream(body);
+       TSRMLS_FETCH_FROM_CTX(body->ts);
+
+       php_stream_seek(s, offset, SEEK_SET);
+
+       if (!forlen) {
+               forlen = -1;
+       }
+       return php_stream_copy_to_stream_ex(s, dst, forlen, NULL);
+}
+
+ZEND_RESULT_CODE php_http_message_body_to_callback(php_http_message_body_t *body, php_http_pass_callback_t cb, void *cb_arg, off_t offset, size_t forlen)
+{
+       php_stream *s = php_http_message_body_stream(body);
+       char *buf = emalloc(0x1000);
+       TSRMLS_FETCH_FROM_CTX(body->ts);
+
+       php_stream_seek(s, offset, SEEK_SET);
+
+       if (!forlen) {
+               forlen = -1;
+       }
+       while (!php_stream_eof(s)) {
+               size_t read = php_stream_read(s, buf, MIN(forlen, 0x1000));
+
+               if (read) {
+                       if (-1 == cb(cb_arg, buf, read)) {
+                               return FAILURE;
+                       }
+               }
+
+               if (read < MIN(forlen, sizeof(buf))) {
+                       break;
+               }
+
+               if (forlen && !(forlen -= read)) {
+                       break;
+               }
+       }
+       efree(buf);
+
+       return SUCCESS;
+}
+
+size_t php_http_message_body_append(php_http_message_body_t *body, const char *buf, size_t len)
+{
+       php_stream *s;
+       size_t written;
+       TSRMLS_FETCH_FROM_CTX(body->ts);
+
+       if (!(s = php_http_message_body_stream(body))) {
+               return -1;
+       }
+
+       if (s->ops->seek) {
+               php_stream_seek(s, 0, SEEK_END);
+       }
+
+       written = php_stream_write(s, buf, len);
+
+       if (written != len) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to append %zu bytes to body; wrote %zu", len, written);
+       }
+
+       return len;
+}
+
+size_t php_http_message_body_appendf(php_http_message_body_t *body, const char *fmt, ...)
+{
+       va_list argv;
+       char *print_str;
+       size_t print_len;
+
+       va_start(argv, fmt);
+       print_len = vspprintf(&print_str, 0, fmt, argv);
+       va_end(argv);
+
+       print_len = php_http_message_body_append(body, print_str, print_len);
+       efree(print_str);
+
+       return print_len;
+}
+
+ZEND_RESULT_CODE php_http_message_body_add_form(php_http_message_body_t *body, HashTable *fields, HashTable *files)
+{
+       zval tmp;
+
+       if (fields) {
+               INIT_PZVAL_ARRAY(&tmp, fields);
+               if (SUCCESS != add_recursive_fields(body, NULL, &tmp)) {
+                       return FAILURE;
+               }
+       }
+       if (files) {
+               INIT_PZVAL_ARRAY(&tmp, files);
+               if (SUCCESS != add_recursive_files(body, NULL, &tmp)) {
+                       return FAILURE;
+               }
+       }
+
+       return SUCCESS;
+}
+
+void php_http_message_body_add_part(php_http_message_body_t *body, php_http_message_t *part)
+{
+       TSRMLS_FETCH_FROM_CTX(body->ts);
+
+       BOUNDARY_OPEN(body);
+       php_http_message_to_callback(part, (php_http_pass_callback_t) php_http_message_body_append, body);
+       BOUNDARY_CLOSE(body);
+}
+
+
+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)
+{
+       char *safe_name;
+       TSRMLS_FETCH_FROM_CTX(body->ts);
+
+       safe_name = php_addslashes(estrdup(name), strlen(name), NULL, 1 TSRMLS_CC);
+
+       BOUNDARY_OPEN(body);
+       php_http_message_body_appendf(
+               body,
+               "Content-Disposition: form-data; name=\"%s\"" PHP_HTTP_CRLF
+               "" PHP_HTTP_CRLF,
+               safe_name
+       );
+       php_http_message_body_append(body, value_str, value_len);
+       BOUNDARY_CLOSE(body);
+
+       efree(safe_name);
+       return SUCCESS;
+}
+
+ZEND_RESULT_CODE php_http_message_body_add_form_file(php_http_message_body_t *body, const char *name, const char *ctype, const char *path, php_stream *in)
+{
+       char *safe_name, *path_dup = estrdup(path), *bname;
+       size_t bname_len;
+       TSRMLS_FETCH_FROM_CTX(body->ts);
+
+       safe_name = php_addslashes(estrdup(name), strlen(name), NULL, 1 TSRMLS_CC);
+       
+       php_basename(path_dup, strlen(path_dup), NULL, 0, &bname, &bname_len TSRMLS_CC); 
+
+       BOUNDARY_OPEN(body);
+       php_http_message_body_appendf(
+               body,
+               "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"" PHP_HTTP_CRLF
+               "Content-Transfer-Encoding: binary" PHP_HTTP_CRLF
+               "Content-Type: %s" PHP_HTTP_CRLF
+               PHP_HTTP_CRLF,
+               safe_name, bname, ctype
+       );
+       php_stream_copy_to_stream_ex(in, php_http_message_body_stream(body), PHP_STREAM_COPY_ALL, NULL);
+       BOUNDARY_CLOSE(body);
+
+       efree(safe_name);
+       efree(path_dup);
+       efree(bname);
+
+       return SUCCESS;
+}
+
+static inline char *format_key(uint type, char *str, ulong num, const char *prefix) {
+       char *new_key = NULL;
+
+       if (prefix && *prefix) {
+               if (type == HASH_KEY_IS_STRING) {
+                       spprintf(&new_key, 0, "%s[%s]", prefix, str);
+               } else {
+                       spprintf(&new_key, 0, "%s[%lu]", prefix, num);
+               }
+       } else if (type == HASH_KEY_IS_STRING) {
+               new_key = estrdup(str);
+       } else {
+               new_key = estrdup("");
+       }
+
+       return new_key;
+}
+
+static ZEND_RESULT_CODE add_recursive_fields(php_http_message_body_t *body, const char *name, zval *value)
+{
+       if (Z_TYPE_P(value) == IS_ARRAY || Z_TYPE_P(value) == IS_OBJECT) {
+               zval **val;
+               HashTable *ht;
+               HashPosition pos;
+               php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+               TSRMLS_FETCH_FROM_CTX(body->ts);
+
+               ht = HASH_OF(value);
+               if (!ht->nApplyCount) {
+                       ++ht->nApplyCount;
+                       FOREACH_KEYVAL(pos, value, key, val) {
+                               char *str = format_key(key.type, key.str, key.num, name);
+                               if (SUCCESS != add_recursive_fields(body, str, *val)) {
+                                       efree(str);
+                                       ht->nApplyCount--;
+                                       return FAILURE;
+                               }
+                               efree(str);
+                       }
+                       --ht->nApplyCount;
+               }
+       } else {
+               zval *cpy = php_http_ztyp(IS_STRING, value);
+               php_http_message_body_add_form_field(body, name, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy));
+               zval_ptr_dtor(&cpy);
+       }
+
+       return SUCCESS;
+}
+
+static ZEND_RESULT_CODE add_recursive_files(php_http_message_body_t *body, const char *name, zval *value)
+{
+       zval **zdata = NULL, **zfile, **zname, **ztype;
+       HashTable *ht;
+       TSRMLS_FETCH_FROM_CTX(body->ts);
+
+       if (Z_TYPE_P(value) != IS_ARRAY && Z_TYPE_P(value) != IS_OBJECT) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected array or object (name, type, file) for message body file to add");
+               return FAILURE;
+       }
+
+       ht = HASH_OF(value);
+
+       if ((SUCCESS != zend_hash_find(ht, ZEND_STRS("name"), (void *) &zname))
+       ||      (SUCCESS != zend_hash_find(ht, ZEND_STRS("type"), (void *) &ztype))
+       ||      (SUCCESS != zend_hash_find(ht, ZEND_STRS("file"), (void *) &zfile))
+       ) {
+               zval **val;
+               HashPosition pos;
+               php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+
+               if (!ht->nApplyCount) {
+                       ++ht->nApplyCount;
+                       FOREACH_HASH_KEYVAL(pos, ht, key, val) {
+                               if (Z_TYPE_PP(val) == IS_ARRAY || Z_TYPE_PP(val) == IS_OBJECT) {
+                                       char *str = format_key(key.type, key.str, key.num, name);
+
+                                       if (SUCCESS != add_recursive_files(body, str, *val)) {
+                                               efree(str);
+                                               --ht->nApplyCount;
+                                               return FAILURE;
+                                       }
+                                       efree(str);
+                               }
+                       }
+                       --ht->nApplyCount;
+               }
+               return SUCCESS;
+       } else {
+               php_stream *stream;
+               zval *zfc = php_http_ztyp(IS_STRING, *zfile);
+
+               if (SUCCESS == zend_hash_find(ht, ZEND_STRS("data"), (void *) &zdata)) {
+                       if (Z_TYPE_PP(zdata) == IS_RESOURCE) {
+                               php_stream_from_zval_no_verify(stream, zdata);
+                       } else {
+                               zval *tmp = php_http_ztyp(IS_STRING, *zdata);
+
+                               stream = php_stream_memory_open(TEMP_STREAM_READONLY, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
+                               zval_ptr_dtor(&tmp);
+                       }
+               } else {
+                       stream = php_stream_open_wrapper(Z_STRVAL_P(zfc), "r", REPORT_ERRORS|USE_PATH, NULL);
+               }
+
+               if (!stream) {
+                       zval_ptr_dtor(&zfc);
+                       return FAILURE;
+               } else {
+                       zval *znc = php_http_ztyp(IS_STRING, *zname), *ztc = php_http_ztyp(IS_STRING, *ztype);
+                       char *key = format_key(HASH_KEY_IS_STRING, Z_STRVAL_P(znc), 0, name);
+                       ZEND_RESULT_CODE ret =  php_http_message_body_add_form_file(body, key, Z_STRVAL_P(ztc), Z_STRVAL_P(zfc), stream);
+
+                       efree(key);
+                       zval_ptr_dtor(&znc);
+                       zval_ptr_dtor(&ztc);
+                       zval_ptr_dtor(&zfc);
+                       if (!zdata || Z_TYPE_PP(zdata) != IS_RESOURCE) {
+                               php_stream_close(stream);
+                       }
+                       return ret;
+               }
+
+       }
+}
+
+struct splitbody_arg {
+       php_http_buffer_t buf;
+       php_http_message_parser_t *parser;
+       char *boundary_str;
+       size_t boundary_len;
+       size_t consumed;
+};
+
+static size_t splitbody(void *opaque, char *buf, size_t len TSRMLS_DC)
+{
+       struct splitbody_arg *arg = opaque;
+       const char *boundary = NULL;
+       size_t consumed = 0;
+       int first_boundary;
+
+       do {
+               first_boundary = !(consumed || arg->consumed);
+
+               if ((boundary = php_http_locate_str(buf, len, arg->boundary_str + first_boundary, arg->boundary_len - first_boundary))) {
+                       size_t real_boundary_len = arg->boundary_len - 1, cut;
+                       const char *real_boundary = boundary + !first_boundary;
+                       int eol_len = 0;
+
+                       if (buf + len <= real_boundary + real_boundary_len) {
+                               /* if we just have enough data for the boundary, it's just a byte too less */
+                               arg->consumed += consumed;
+                               return consumed;
+                       }
+
+                       if (!first_boundary) {
+                               /* this is not the first boundary, read rest of this message */
+                               php_http_buffer_append(&arg->buf, buf, real_boundary - buf);
+                               php_http_message_parser_parse(arg->parser, &arg->buf, 0, &arg->parser->message);
+                       }
+
+                       /* move after the boundary */
+                       cut = real_boundary - buf + real_boundary_len;
+                       buf += cut;
+                       len -= cut;
+                       consumed += cut;
+
+                       if (buf == php_http_locate_bin_eol(buf, len, &eol_len)) {
+                               /* skip CRLF */
+                               buf += eol_len;
+                               len -= eol_len;
+                               consumed += eol_len;
+
+                               if (!first_boundary) {
+                                       /* advance messages */
+                                       php_http_message_t *msg;
+
+                                       msg = php_http_message_init(NULL, 0, NULL TSRMLS_CC);
+                                       msg->parent = arg->parser->message;
+                                       arg->parser->message = msg;
+                               }
+                       } else {
+                               /* is this the last boundary? */
+                               if (*buf == '-') {
+                                       /* ignore the rest */
+                                       consumed += len;
+                                       len = 0;
+                               } else {
+                                       /* let this be garbage */
+                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Malformed multipart boundary at pos %zu", consumed);
+                                       return -1;
+                               }
+                       }
+               }
+       } while (boundary && len);
+
+       /* let there be room for the next boundary */
+       if (len > arg->boundary_len) {
+               consumed += len - arg->boundary_len;
+               php_http_buffer_append(&arg->buf, buf, len - arg->boundary_len);
+               php_http_message_parser_parse(arg->parser, &arg->buf, 0, &arg->parser->message);
+       }
+
+       arg->consumed += consumed;
+       return consumed;
+}
+
+php_http_message_t *php_http_message_body_split(php_http_message_body_t *body, const char *boundary)
+{
+       php_stream *s = php_http_message_body_stream(body);
+       php_http_buffer_t *tmp = NULL;
+       php_http_message_t *msg = NULL;
+       struct splitbody_arg arg;
+       TSRMLS_FETCH_FROM_CTX(body->ts);
+
+       php_http_buffer_init(&arg.buf);
+       arg.parser = php_http_message_parser_init(NULL TSRMLS_CC);
+       arg.boundary_len = spprintf(&arg.boundary_str, 0, "\n--%s", boundary);
+       arg.consumed = 0;
+
+       php_stream_rewind(s);
+       while (!php_stream_eof(s)) {
+               php_http_buffer_passthru(&tmp, 0x1000, (php_http_buffer_pass_func_t) _php_stream_read, s, splitbody, &arg TSRMLS_CC);
+       }
+
+       msg = arg.parser->message;
+       arg.parser->message = NULL;
+
+       php_http_buffer_free(&tmp);
+       php_http_message_parser_free(&arg.parser);
+       php_http_buffer_dtor(&arg.buf);
+       PTR_FREE(arg.boundary_str);
+
+       return msg;
+}
+
+static zend_object_handlers php_http_message_body_object_handlers;
+
+zend_object_value php_http_message_body_object_new(zend_class_entry *ce TSRMLS_DC)
+{
+       return php_http_message_body_object_new_ex(ce, NULL, NULL TSRMLS_CC);
+}
+
+zend_object_value php_http_message_body_object_new_ex(zend_class_entry *ce, php_http_message_body_t *body, php_http_message_body_object_t **ptr TSRMLS_DC)
+{
+       php_http_message_body_object_t *o;
+
+       o = ecalloc(1, sizeof(php_http_message_body_object_t));
+       zend_object_std_init((zend_object *) o, php_http_message_body_class_entry TSRMLS_CC);
+       object_properties_init((zend_object *) o, ce);
+
+       if (ptr) {
+               *ptr = o;
+       }
+
+       if (body) {
+               o->body = body;
+       }
+
+       o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_message_body_object_free, NULL TSRMLS_CC);
+       o->zv.handlers = &php_http_message_body_object_handlers;
+
+       return o->zv;
+}
+
+zend_object_value php_http_message_body_object_clone(zval *object TSRMLS_DC)
+{
+       zend_object_value new_ov;
+       php_http_message_body_object_t *new_obj = NULL;
+       php_http_message_body_object_t *old_obj = zend_object_store_get_object(object TSRMLS_CC);
+       php_http_message_body_t *body = php_http_message_body_copy(old_obj->body, NULL);
+
+       new_ov = php_http_message_body_object_new_ex(old_obj->zo.ce, body, &new_obj TSRMLS_CC);
+       zend_objects_clone_members(&new_obj->zo, new_ov, &old_obj->zo, Z_OBJ_HANDLE_P(object) TSRMLS_CC);
+
+       return new_ov;
+}
+
+void php_http_message_body_object_free(void *object TSRMLS_DC)
+{
+       php_http_message_body_object_t *obj = object;
+
+       php_http_message_body_free(&obj->body);
+       zend_object_std_dtor((zend_object *) obj TSRMLS_CC);
+       efree(obj);
+}
+
+#define PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj) \
+       do { \
+               if (!obj->body) { \
+                       obj->body = php_http_message_body_init(NULL, NULL TSRMLS_CC); \
+               } \
+       } while(0)
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody___construct, 0, 0, 0)
+       ZEND_ARG_INFO(0, stream)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpMessageBody, __construct)
+{
+       php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       zval *zstream = NULL;
+       php_stream *stream;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r!", &zstream), invalid_arg, return);
+
+       if (zstream) {
+               php_http_expect(php_stream_from_zval_no_verify(stream, &zstream), unexpected_val, return);
+
+               if (obj->body) {
+                       php_http_message_body_free(&obj->body);
+               }
+               obj->body = php_http_message_body_init(NULL, stream TSRMLS_CC);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody___toString, 0, 0, 0)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpMessageBody, __toString)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+               char *str;
+               size_t len;
+
+               PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
+
+               php_http_message_body_to_string(obj->body, &str, &len, 0, 0);
+               if (str) {
+                       RETURN_STRINGL(str, len, 0);
+               }
+       }
+       RETURN_EMPTY_STRING();
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_unserialize, 0, 0, 1)
+       ZEND_ARG_INFO(0, serialized)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpMessageBody, unserialize)
+{
+       char *us_str;
+       int us_len;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &us_str, &us_len)) {
+               php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+               php_stream *s = php_stream_memory_open(0, us_str, us_len);
+
+               obj->body = php_http_message_body_init(NULL, s TSRMLS_CC);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_toStream, 0, 0, 1)
+       ZEND_ARG_INFO(0, stream)
+       ZEND_ARG_INFO(0, offset)
+       ZEND_ARG_INFO(0, maxlen)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpMessageBody, toStream)
+{
+       zval *zstream;
+       long offset = 0, forlen = 0;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|ll", &zstream, &offset, &forlen)) {
+               php_stream *stream;
+               php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
+
+               php_stream_from_zval(stream, &zstream);
+               php_http_message_body_to_stream(obj->body, stream, offset, forlen);
+               RETURN_ZVAL(getThis(), 1, 0);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_toCallback, 0, 0, 1)
+       ZEND_ARG_INFO(0, callback)
+       ZEND_ARG_INFO(0, offset)
+       ZEND_ARG_INFO(0, maxlen)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpMessageBody, toCallback)
+{
+       php_http_pass_fcall_arg_t fcd;
+       long offset = 0, forlen = 0;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f|ll", &fcd.fci, &fcd.fcc, &offset, &forlen)) {
+               php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
+
+               fcd.fcz = getThis();
+               Z_ADDREF_P(fcd.fcz);
+               TSRMLS_SET_CTX(fcd.ts);
+
+               php_http_message_body_to_callback(obj->body, php_http_pass_fcall_callback, &fcd, offset, forlen);
+               zend_fcall_info_args_clear(&fcd.fci, 1);
+
+               zval_ptr_dtor(&fcd.fcz);
+               RETURN_ZVAL(getThis(), 1, 0);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_getResource, 0, 0, 0)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpMessageBody, getResource)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
+
+               zend_list_addref(obj->body->stream_id);
+               RETVAL_RESOURCE(obj->body->stream_id);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_getBoundary, 0, 0, 0)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpMessageBody, getBoundary)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_message_body_object_t * obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
+
+               if (obj->body->boundary) {
+                       RETURN_STRING(obj->body->boundary, 1);
+               }
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_append, 0, 0, 1)
+       ZEND_ARG_INFO(0, string)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpMessageBody, append)
+{
+       char *str;
+       int len;
+       php_http_message_body_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
+
+       php_http_expect(len == php_http_message_body_append(obj->body, str, len), runtime, return);
+
+       RETURN_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_addForm, 0, 0, 0)
+       ZEND_ARG_ARRAY_INFO(0, fields, 1)
+       ZEND_ARG_ARRAY_INFO(0, files, 1)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpMessageBody, addForm)
+{
+       HashTable *fields = NULL, *files = NULL;
+       php_http_message_body_object_t *obj;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|h!h!", &fields, &files), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
+
+       php_http_expect(SUCCESS == php_http_message_body_add_form(obj->body, fields, files), runtime, return);
+
+       RETURN_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_addPart, 0, 0, 1)
+       ZEND_ARG_OBJ_INFO(0, message, http\\Message, 0)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpMessageBody, addPart)
+{
+       zval *zobj;
+       php_http_message_body_object_t *obj;
+       php_http_message_object_t *mobj;
+       zend_error_handling zeh;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zobj, php_http_message_class_entry), invalid_arg, return);
+
+       obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       mobj = zend_object_store_get_object(zobj TSRMLS_CC);
+
+       PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
+
+       zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh TSRMLS_CC);
+       php_http_message_body_add_part(obj->body, mobj->message);
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+
+       if (!EG(exception)) {
+               RETURN_ZVAL(getThis(), 1, 0);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_etag, 0, 0, 0)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpMessageBody, etag)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+               char *etag;
+
+               PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
+
+               if ((etag = php_http_message_body_etag(obj->body))) {
+                       RETURN_STRING(etag, 0);
+               } else {
+                       RETURN_FALSE;
+               }
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_stat, 0, 0, 0)
+       ZEND_ARG_INFO(0, field)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpMessageBody, stat)
+{
+       char *field_str = NULL;
+       int field_len = 0;
+
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &field_str, &field_len)) {
+               php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+               const php_stream_statbuf *sb;
+
+               PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
+
+               if ((sb = php_http_message_body_stat(obj->body))) {
+                       if (field_str && field_len) {
+                                       switch (*field_str) {
+                                               case 's':
+                                               case 'S':
+                                                       RETURN_LONG(sb->sb.st_size);
+                                                       break;
+                                               case 'a':
+                                               case 'A':
+                                                       RETURN_LONG(sb->sb.st_atime);
+                                                       break;
+                                               case 'm':
+                                               case 'M':
+                                                       RETURN_LONG(sb->sb.st_mtime);
+                                                       break;
+                                               case 'c':
+                                               case 'C':
+                                                       RETURN_LONG(sb->sb.st_ctime);
+                                                       break;
+                                               default:
+                                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown stat field: '%s' (should be one of [s]ize, [a]time, [m]time or [c]time)", field_str);
+                                                       break;
+                                       }
+                       } else {
+                               object_init(return_value);
+                               add_property_long_ex(return_value, ZEND_STRS("size"), sb->sb.st_size TSRMLS_CC);
+                               add_property_long_ex(return_value, ZEND_STRS("atime"), sb->sb.st_atime TSRMLS_CC);
+                               add_property_long_ex(return_value, ZEND_STRS("mtime"), sb->sb.st_mtime TSRMLS_CC);
+                               add_property_long_ex(return_value, ZEND_STRS("ctime"), sb->sb.st_ctime TSRMLS_CC);
+                       }
+               }
+       }
+}
+
+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, __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)
+       PHP_ME(HttpMessageBody, unserialize,  ai_HttpMessageBody_unserialize,  ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessageBody, toStream,     ai_HttpMessageBody_toStream,     ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessageBody, toCallback,   ai_HttpMessageBody_toCallback,   ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessageBody, getResource,  ai_HttpMessageBody_getResource,  ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessageBody, getBoundary,  ai_HttpMessageBody_getBoundary,  ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessageBody, append,       ai_HttpMessageBody_append,       ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessageBody, addForm,      ai_HttpMessageBody_addForm,      ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessageBody, addPart,      ai_HttpMessageBody_addPart,      ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessageBody, etag,         ai_HttpMessageBody_etag,         ZEND_ACC_PUBLIC)
+       PHP_ME(HttpMessageBody, stat,         ai_HttpMessageBody_stat,         ZEND_ACC_PUBLIC)
+       EMPTY_FUNCTION_ENTRY
+};
+
+zend_class_entry *php_http_message_body_class_entry;
+
+PHP_MINIT_FUNCTION(http_message_body)
+{
+       zend_class_entry ce = {0};
+
+       INIT_NS_CLASS_ENTRY(ce, "http\\Message", "Body", php_http_message_body_methods);
+       php_http_message_body_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
+       php_http_message_body_class_entry->create_object = php_http_message_body_object_new;
+       memcpy(&php_http_message_body_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+       php_http_message_body_object_handlers.clone_obj = php_http_message_body_object_clone;
+       zend_class_implements(php_http_message_body_class_entry TSRMLS_CC, 1, zend_ce_serializable);
+
+       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_message_body.h b/src/php_http_message_body.h
new file mode 100644 (file)
index 0000000..dc2c7a2
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_MESSAGE_BODY_H
+#define PHP_HTTP_MESSAGE_BODY_H
+
+typedef struct php_http_message_body {
+       int stream_id;
+       php_stream_statbuf ssb;
+       char *boundary;
+       unsigned refcount;
+#ifdef ZTS
+       void ***ts;
+#endif
+} php_http_message_body_t;
+
+struct php_http_message;
+
+PHP_HTTP_API php_http_message_body_t *php_http_message_body_init(php_http_message_body_t **body, php_stream *stream TSRMLS_DC);
+PHP_HTTP_API unsigned php_http_message_body_addref(php_http_message_body_t *body);
+PHP_HTTP_API php_http_message_body_t *php_http_message_body_copy(php_http_message_body_t *from, php_http_message_body_t *to);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_message_body_add_form(php_http_message_body_t *body, HashTable *fields, HashTable *files);
+PHP_HTTP_API 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);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_message_body_add_form_file(php_http_message_body_t *body, const char *name, const char *ctype, const char *file, php_stream *stream);
+PHP_HTTP_API void php_http_message_body_add_part(php_http_message_body_t *body, struct php_http_message *part);
+PHP_HTTP_API size_t php_http_message_body_append(php_http_message_body_t *body, const char *buf, size_t len);
+PHP_HTTP_API size_t php_http_message_body_appendf(php_http_message_body_t *body, const char *fmt, ...);
+PHP_HTTP_API void php_http_message_body_to_string(php_http_message_body_t *body, char **buf, size_t *len, off_t offset, size_t forlen);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_message_body_to_stream(php_http_message_body_t *body, php_stream *s, off_t offset, size_t forlen);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_message_body_to_callback(php_http_message_body_t *body, php_http_pass_callback_t cb, void *cb_arg, off_t offset, size_t forlen);
+PHP_HTTP_API void php_http_message_body_free(php_http_message_body_t **body);
+PHP_HTTP_API const php_stream_statbuf *php_http_message_body_stat(php_http_message_body_t *body);
+#define php_http_message_body_size(b) (php_http_message_body_stat((b))->sb.st_size)
+#define php_http_message_body_mtime(b) (php_http_message_body_stat((b))->sb.st_mtime)
+PHP_HTTP_API char *php_http_message_body_etag(php_http_message_body_t *body);
+PHP_HTTP_API const char *php_http_message_body_boundary(php_http_message_body_t *body);
+PHP_HTTP_API struct php_http_message *php_http_message_body_split(php_http_message_body_t *body, const char *boundary);
+
+static inline php_stream *php_http_message_body_stream(php_http_message_body_t *body)
+{
+       TSRMLS_FETCH_FROM_CTX(body->ts);
+       return zend_fetch_resource(NULL TSRMLS_CC, body->stream_id, "stream", NULL, 2, php_file_le_stream(), php_file_le_pstream());
+}
+
+typedef struct php_http_message_body_object {
+       zend_object zo;
+       zend_object_value zv;
+       php_http_message_body_t *body;
+} php_http_message_body_object_t;
+
+PHP_HTTP_API zend_class_entry *php_http_message_body_class_entry;
+PHP_MINIT_FUNCTION(http_message_body);
+
+zend_object_value php_http_message_body_object_new(zend_class_entry *ce TSRMLS_DC);
+zend_object_value php_http_message_body_object_new_ex(zend_class_entry *ce, php_http_message_body_t *body, php_http_message_body_object_t **ptr TSRMLS_DC);
+zend_object_value php_http_message_body_object_clone(zval *object TSRMLS_DC);
+void php_http_message_body_object_free(void *object TSRMLS_DC);
+
+#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_message_parser.c b/src/php_http_message_parser.c
new file mode 100644 (file)
index 0000000..fae16f1
--- /dev/null
@@ -0,0 +1,705 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+#ifndef DBG_PARSER
+#      define DBG_PARSER 0
+#endif
+
+typedef struct php_http_message_parser_state_spec {
+       php_http_message_parser_state_t state;
+       unsigned need_data:1;
+} php_http_message_parser_state_spec_t;
+
+static const php_http_message_parser_state_spec_t php_http_message_parser_states[] = {
+               {PHP_HTTP_MESSAGE_PARSER_STATE_START,                   1},
+               {PHP_HTTP_MESSAGE_PARSER_STATE_HEADER,                  0},
+               {PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE,             0},
+               {PHP_HTTP_MESSAGE_PARSER_STATE_BODY,                    0},
+               {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB,               1},
+               {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH,             1},
+               {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED,    1},
+               {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE,               0},
+               {PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL,               0},
+               {PHP_HTTP_MESSAGE_PARSER_STATE_DONE,                    0}
+};
+
+#if DBG_PARSER
+const char *php_http_message_parser_state_name(php_http_message_parser_state_t state) {
+       const char *states[] = {"START", "HEADER", "HEADER_DONE", "BODY", "BODY_DUMB", "BODY_LENGTH", "BODY_CHUNK", "BODY_DONE", "UPDATE_CL", "DONE"};
+       
+       if (state < 0 || state > (sizeof(states)/sizeof(char*))-1) {
+               return "FAILURE";
+       }
+       return states[state];
+}
+#endif
+
+php_http_message_parser_t *php_http_message_parser_init(php_http_message_parser_t *parser TSRMLS_DC)
+{
+       if (!parser) {
+               parser = emalloc(sizeof(*parser));
+       }
+       memset(parser, 0, sizeof(*parser));
+
+       TSRMLS_SET_CTX(parser->ts);
+
+       php_http_header_parser_init(&parser->header TSRMLS_CC);
+
+       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;
+}
+
+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;
+}
+
+void php_http_message_parser_dtor(php_http_message_parser_t *parser)
+{
+       php_http_header_parser_dtor(&parser->header);
+       zend_ptr_stack_destroy(&parser->stack);
+       php_http_message_free(&parser->message);
+       if (parser->dechunk) {
+               php_http_encoding_stream_free(&parser->dechunk);
+       }
+       if (parser->inflate) {
+               php_http_encoding_stream_free(&parser->inflate);
+       }
+}
+
+void php_http_message_parser_free(php_http_message_parser_t **parser)
+{
+       if (*parser) {
+               php_http_message_parser_dtor(*parser);
+               efree(*parser);
+               *parser = NULL;
+       }
+}
+
+php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_message_parser_t *parser, php_http_buffer_t *buf, php_stream *s, unsigned flags, php_http_message_t **message)
+{
+       php_http_message_parser_state_t state = PHP_HTTP_MESSAGE_PARSER_STATE_START;
+       TSRMLS_FETCH_FROM_CTX(parser->ts);
+
+       if (!buf->data) {
+               php_http_buffer_resize_ex(buf, 0x1000, 1, 0);
+       }
+       while (1) {
+               size_t justread = 0;
+#if DBG_PARSER
+               fprintf(stderr, "#SP: %s (f:%u)\n", php_http_message_parser_state_name(state), flags);
+#endif
+               /* resize if needed */
+               if (buf->free < 0x1000) {
+                       php_http_buffer_resize_ex(buf, 0x1000, 1, 0);
+               }
+               switch (state) {
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_START:
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER:
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE:
+                               /* read line */
+                               php_stream_get_line(s, buf->data + buf->used, buf->free, &justread);
+                               /* if we fail reading a whole line, try a single char */
+                               if (!justread) {
+                                       int c = php_stream_getc(s);
+
+                                       if (c != EOF) {
+                                               char s[1] = {c};
+                                               justread = php_http_buffer_append(buf, s, 1);
+                                       }
+                               }
+                               php_http_buffer_account(buf, justread);
+                               break;
+
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB:
+                               /* read all */
+                               justread = php_stream_read(s, buf->data + buf->used, buf->free);
+                               php_http_buffer_account(buf, justread);
+                               break;
+
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH:
+                               /* read body_length */
+                               justread = php_stream_read(s, buf->data + buf->used, MIN(buf->free, parser->body_length));
+                               php_http_buffer_account(buf, justread);
+                               break;
+
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED:
+                               /* duh, this is very naive */
+                               if (parser->body_length) {
+                                       justread = php_stream_read(s, buf->data + buf->used, MIN(parser->body_length, buf->free));
+
+                                       php_http_buffer_account(buf, justread);
+
+                                       parser->body_length -= justread;
+                               } else {
+                                       php_http_buffer_resize(buf, 24);
+                                       php_stream_get_line(s, buf->data, buf->free, &justread);
+                                       php_http_buffer_account(buf, justread);
+
+                                       parser->body_length = strtoul(buf->data + buf->used - justread, NULL, 16);
+                               }
+                               break;
+
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY:
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL:
+                               /* should not occur */
+                               abort();
+                               break;
+
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_DONE:
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE:
+                               return php_http_message_parser_state_is(parser);
+               }
+
+               if (justread) {
+                       state = php_http_message_parser_parse(parser, buf, flags, message);
+               } else if (php_stream_eof(s)) {
+                       return php_http_message_parser_parse(parser, buf, flags | PHP_HTTP_MESSAGE_PARSER_CLEANUP, message);
+               } else {
+                       return state;
+               }
+       }
+
+       return PHP_HTTP_MESSAGE_PARSER_STATE_DONE;
+}
+
+
+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)
+{
+       char *str = NULL;
+       size_t len = 0;
+       size_t cut = 0;
+       TSRMLS_FETCH_FROM_CTX(parser->ts);
+
+       while (buffer->used || !php_http_message_parser_states[php_http_message_parser_state_is(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)),
+                       flags, 
+                       message && *message ? (*message)->type : -1, 
+                       buffer->used
+               );
+               _dpf(0, buffer->data, buffer->used);
+#endif
+
+               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);
+
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_START:
+                       {
+                               char *ptr = buffer->data;
+
+                               while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) {
+                                       ++ptr;
+                               }
+
+                               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);
+                               }
+                               break;
+                       }
+
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER:
+                       {
+                               unsigned header_parser_flags = (flags & PHP_HTTP_MESSAGE_PARSER_CLEANUP) ? PHP_HTTP_HEADER_PARSER_CLEANUP : 0;
+
+                               switch (php_http_header_parser_parse(&parser->header, buffer, header_parser_flags, *message ? &(*message)->hdrs : NULL, (php_http_info_callback_t) php_http_message_info_callback, message)) {
+                                       case PHP_HTTP_HEADER_PARSER_STATE_FAILURE:
+                                               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);
+                                               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);
+                                               } else {
+                                                       php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE);
+                                               }
+                               }
+                               break;
+                       }
+
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE:
+                       {
+                               zval *h, *h_loc = NULL, *h_con = NULL, **h_cl = NULL, **h_cr = NULL, **h_te = NULL;
+
+                               /* Content-Range has higher precedence than Content-Length,
+                                * and content-length denotes the original length of the entity,
+                                * so let's *NOT* remove CR/CL, because that would fundamentally
+                                * change the meaning of the whole message
+                                */
+                               if ((h = php_http_message_header(*message, ZEND_STRL("Transfer-Encoding"), 1))) {
+                                       zend_hash_update(&(*message)->hdrs, "X-Original-Transfer-Encoding", sizeof("X-Original-Transfer-Encoding"), (void *) &h, sizeof(zval *), (void *) &h_te);
+                                       zend_hash_del(&(*message)->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding"));
+
+                                       /* reset */
+                                       MAKE_STD_ZVAL(h);
+                                       ZVAL_LONG(h, 0);
+                                       zend_hash_update(&(*message)->hdrs, "Content-Length", sizeof("Content-Length"), (void *) &h, sizeof(zval *), NULL);
+                               } else if ((h = php_http_message_header(*message, ZEND_STRL("Content-Length"), 1))) {
+                                       zend_hash_update(&(*message)->hdrs, "X-Original-Content-Length", sizeof("X-Original-Content-Length"), (void *) &h, sizeof(zval *), (void *) &h_cl);
+                               }
+
+                               if ((h = php_http_message_header(*message, ZEND_STRL("Content-Range"), 1))) {
+                                       zend_hash_find(&(*message)->hdrs, ZEND_STRS("Content-Range"), (void *) &h_cr);
+                                       if (h != *h_cr) {
+                                               zend_hash_update(&(*message)->hdrs, "Content-Range", sizeof("Content-Range"), &h, sizeof(zval *), (void *) &h_cr);
+                                       } else {
+                                               zval_ptr_dtor(&h);
+                                       }
+                               }
+
+                               /* so, if curl sees a 3xx code, a Location header and a Connection:close header
+                                * it decides not to read the response body.
+                                */
+                               if ((flags & PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS)
+                               &&      (*message)->type == PHP_HTTP_RESPONSE
+                               &&      (*message)->http.info.response.code/100 == 3
+                               &&      (h_loc = php_http_message_header(*message, ZEND_STRL("Location"), 1))
+                               &&      (h_con = php_http_message_header(*message, ZEND_STRL("Connection"), 1))
+                               ) {
+                                       if (php_http_match(Z_STRVAL_P(h_con), "close", PHP_HTTP_MATCH_WORD)) {
+                                               php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
+                                               zval_ptr_dtor(&h_loc);
+                                               zval_ptr_dtor(&h_con);
+                                               break;
+                                       }
+                               }
+                               if (h_loc) {
+                                       zval_ptr_dtor(&h_loc);
+                               }
+                               if (h_con) {
+                                       zval_ptr_dtor(&h_con);
+                               }
+
+                               if ((h = php_http_message_header(*message, ZEND_STRL("Content-Encoding"), 1))) {
+                                       if (php_http_match(Z_STRVAL_P(h), "gzip", PHP_HTTP_MATCH_WORD)
+                                       ||      php_http_match(Z_STRVAL_P(h), "x-gzip", PHP_HTTP_MATCH_WORD)
+                                       ||      php_http_match(Z_STRVAL_P(h), "deflate", PHP_HTTP_MATCH_WORD)
+                                       ) {
+                                               if (parser->inflate) {
+                                                       php_http_encoding_stream_reset(&parser->inflate);
+                                               } else {
+                                                       parser->inflate = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), 0 TSRMLS_CC);
+                                               }
+                                               zend_hash_update(&(*message)->hdrs, "X-Original-Content-Encoding", sizeof("X-Original-Content-Encoding"), &h, sizeof(zval *), NULL);
+                                               zend_hash_del(&(*message)->hdrs, "Content-Encoding", sizeof("Content-Encoding"));
+                                       } else {
+                                               zval_ptr_dtor(&h);
+                                       }
+                               }
+
+                               if ((flags & PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES)) {
+                                       php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB);
+                               } else {
+                                       if (h_te) {
+                                               if (strstr(Z_STRVAL_PP(h_te), "chunked")) {
+                                                       parser->dechunk = php_http_encoding_stream_init(parser->dechunk, php_http_encoding_stream_get_dechunk_ops(), 0 TSRMLS_CC);
+                                                       php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED);
+                                                       break;
+                                               }
+                                       }
+
+                                       if (h_cr) {
+                                               ulong total = 0, start = 0, end = 0;
+
+                                               if (!strncasecmp(Z_STRVAL_PP(h_cr), "bytes", lenof("bytes"))
+                                               && (    Z_STRVAL_PP(h_cr)[lenof("bytes")] == ':'
+                                                       ||      Z_STRVAL_PP(h_cr)[lenof("bytes")] == ' '
+                                                       ||      Z_STRVAL_PP(h_cr)[lenof("bytes")] == '='
+                                                       )
+                                               ) {
+                                                       char *total_at = NULL, *end_at = NULL;
+                                                       char *start_at = Z_STRVAL_PP(h_cr) + sizeof("bytes");
+
+                                                       start = strtoul(start_at, &end_at, 10);
+                                                       if (end_at) {
+                                                               end = strtoul(end_at + 1, &total_at, 10);
+                                                               if (total_at && strncmp(total_at + 1, "*", 1)) {
+                                                                       total = strtoul(total_at + 1, NULL, 10);
+                                                               }
+
+                                                               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);
+                                                                       break;
+                                                               }
+                                                       }
+                                               }
+                                       }
+
+                                       if (h_cl) {
+                                               char *stop;
+
+                                               if (Z_TYPE_PP(h_cl) == IS_STRING) {
+                                                       parser->body_length = strtoul(Z_STRVAL_PP(h_cl), &stop, 10);
+
+                                                       if (stop != Z_STRVAL_PP(h_cl)) {
+                                                               php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
+                                                               break;
+                                                       }
+                                               } else if (Z_TYPE_PP(h_cl) == IS_LONG) {
+                                                       parser->body_length = Z_LVAL_PP(h_cl);
+                                                       php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
+                                                       break;
+                                               }
+                                       }
+
+                                       if ((*message)->type == PHP_HTTP_REQUEST) {
+                                               php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
+                                       } else {
+                                               php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB);
+                                       }
+                               }
+                               break;
+                       }
+
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY:
+                       {
+                               if (len) {
+                                       /* FIXME: what if we re-use the parser? */
+                                       if (parser->inflate) {
+                                               char *dec_str = NULL;
+                                               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);
+                                               }
+
+                                               if (str != buffer->data) {
+                                                       PTR_FREE(str);
+                                               }
+                                               str = dec_str;
+                                               len = dec_len;
+                                       }
+
+                                       php_stream_write(php_http_message_body_stream((*message)->body), str, len);
+
+                               }
+
+                               if (cut) {
+                                       php_http_buffer_cut(buffer, 0, cut);
+                               }
+
+                               if (str != buffer->data) {
+                                       PTR_FREE(str);
+                               }
+
+                               str = NULL;
+                               len = 0;
+                               cut = 0;
+                               break;
+                       }
+
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB:
+                       {
+                               str = buffer->data;
+                               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);
+                               break;
+                       }
+
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH:
+                       {
+                               len = MIN(parser->body_length, buffer->used);
+                               str = buffer->data;
+                               cut = len;
+
+                               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);
+                               break;
+                       }
+
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED:
+                       {
+                               /*
+                                * - pass available data through the dechunk stream
+                                * - pass decoded data along
+                                * - if stream zeroed:
+                                *      Y:      - cut processed string out of buffer, but leave length of unprocessed dechunk stream data untouched
+                                *              - body done
+                                *      N:      - parse ahaed
+                                */
+                               char *dec_str = NULL;
+                               size_t dec_len;
+
+                               if (SUCCESS != php_http_encoding_stream_update(parser->dechunk, buffer->data, buffer->used, &dec_str, &dec_len)) {
+                                       return PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE;
+                               }
+
+                               str = dec_str;
+                               len = dec_len;
+
+                               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);
+                               } 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);
+                               }
+                               break;
+                       }
+
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:
+                       {
+                               php_http_message_parser_state_push(parser, 1, 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);
+                                       }
+                                       php_http_encoding_stream_dtor(parser->dechunk);
+
+                                       if (dec_str && dec_len) {
+                                               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);
+                                       }
+                               }
+
+                               break;
+                       }
+
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL:
+                       {
+                               zval *zcl;
+                               MAKE_STD_ZVAL(zcl);
+                               ZVAL_LONG(zcl, php_http_message_body_size((*message)->body));
+                               zend_hash_update(&(*message)->hdrs, "Content-Length", sizeof("Content-Length"), &zcl, sizeof(zval *), NULL);
+                               break;
+                       }
+
+                       case PHP_HTTP_MESSAGE_PARSER_STATE_DONE:
+                       {
+                               char *ptr = buffer->data;
+
+                               while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) {
+                                       ++ptr;
+                               }
+
+                               php_http_buffer_cut(buffer, 0, ptr - buffer->data);
+                               
+                               if (!(flags & PHP_HTTP_MESSAGE_PARSER_GREEDY)) {
+                                       return PHP_HTTP_MESSAGE_PARSER_STATE_DONE;
+                               }
+                               break;
+                       }
+               }
+       }
+
+       return php_http_message_parser_state_is(parser);
+}
+
+zend_class_entry *php_http_message_parser_class_entry;
+static zend_object_handlers php_http_message_parser_object_handlers;
+
+zend_object_value php_http_message_parser_object_new(zend_class_entry *ce TSRMLS_DC)
+{
+       return php_http_message_parser_object_new_ex(ce, NULL, NULL TSRMLS_CC);
+}
+
+zend_object_value php_http_message_parser_object_new_ex(zend_class_entry *ce, php_http_message_parser_t *parser, php_http_message_parser_object_t **ptr TSRMLS_DC)
+{
+       php_http_message_parser_object_t *o;
+
+       o = ecalloc(1, sizeof(php_http_message_parser_object_t));
+       zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
+       object_properties_init((zend_object *) o, ce);
+
+       if (ptr) {
+               *ptr = o;
+       }
+
+       if (parser) {
+               o->parser = parser;
+       } else {
+               o->parser = php_http_message_parser_init(NULL TSRMLS_CC);
+       }
+       o->buffer = php_http_buffer_new();
+
+       o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_message_parser_object_free, NULL TSRMLS_CC);
+       o->zv.handlers = &php_http_message_parser_object_handlers;
+
+       return o->zv;
+}
+
+void php_http_message_parser_object_free(void *object TSRMLS_DC)
+{
+       php_http_message_parser_object_t *o = (php_http_message_parser_object_t *) object;
+
+       if (o->parser) {
+               php_http_message_parser_free(&o->parser);
+       }
+       if (o->buffer) {
+               php_http_buffer_free(&o->buffer);
+       }
+       zend_object_std_dtor((zend_object *) o TSRMLS_CC);
+       efree(o);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_getState, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessageParser, getState)
+{
+       php_http_message_parser_object_t *parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       zend_parse_parameters_none();
+       /* always return the real state */
+       RETVAL_LONG(php_http_message_parser_state_is(parser_obj->parser));
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_parse, 0, 0, 3)
+       ZEND_ARG_INFO(0, data)
+       ZEND_ARG_INFO(0, flags)
+       ZEND_ARG_INFO(1, message)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessageParser, parse)
+{
+       php_http_message_parser_object_t *parser_obj;
+       zval *zmsg;
+       char *data_str;
+       int data_len;
+       long flags;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slz", &data_str, &data_len, &flags, &zmsg), invalid_arg, return);
+
+       parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       php_http_buffer_append(parser_obj->buffer, data_str, data_len);
+       RETVAL_LONG(php_http_message_parser_parse(parser_obj->parser, parser_obj->buffer, flags, &parser_obj->parser->message));
+
+       zval_dtor(zmsg);
+       if (parser_obj->parser->message) {
+               ZVAL_OBJVAL(zmsg, php_http_message_object_new_ex(php_http_message_class_entry, php_http_message_copy(parser_obj->parser->message, NULL), NULL TSRMLS_CC), 0);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_stream, 0, 0, 3)
+       ZEND_ARG_INFO(0, stream)
+       ZEND_ARG_INFO(0, flags)
+       ZEND_ARG_INFO(1, message)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessageParser, stream)
+{
+       php_http_message_parser_object_t *parser_obj;
+       zend_error_handling zeh;
+       zval *zmsg, *zstream;
+       php_stream *s;
+       long flags;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz", &zstream, &flags, &zmsg), invalid_arg, return);
+
+       zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC);
+       php_stream_from_zval(s, &zstream);
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+
+       parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       RETVAL_LONG(php_http_message_parser_parse_stream(parser_obj->parser, parser_obj->buffer, s, flags, &parser_obj->parser->message));
+
+       zval_dtor(zmsg);
+       if (parser_obj->parser->message) {
+               ZVAL_OBJVAL(zmsg, php_http_message_object_new_ex(php_http_message_class_entry, php_http_message_copy(parser_obj->parser->message, NULL), NULL TSRMLS_CC), 0);
+       }
+}
+
+static zend_function_entry php_http_message_parser_methods[] = {
+               PHP_ME(HttpMessageParser, getState, ai_HttpMessageParser_getState, ZEND_ACC_PUBLIC)
+               PHP_ME(HttpMessageParser, parse, ai_HttpMessageParser_parse, ZEND_ACC_PUBLIC)
+               PHP_ME(HttpMessageParser, stream, ai_HttpMessageParser_stream, ZEND_ACC_PUBLIC)
+               {NULL, NULL, NULL}
+};
+
+PHP_MINIT_FUNCTION(http_message_parser)
+{
+       zend_class_entry ce;
+
+       INIT_NS_CLASS_ENTRY(ce, "http\\Message", "Parser", php_http_message_parser_methods);
+       php_http_message_parser_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
+       memcpy(&php_http_message_parser_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+       php_http_message_parser_class_entry->create_object = php_http_message_parser_object_new;
+       php_http_message_parser_object_handlers.clone_obj = NULL;
+
+       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("CLEANUP"), PHP_HTTP_MESSAGE_PARSER_CLEANUP TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("DUMB_BODIES"), PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("EMPTY_REDIRECTS"), PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("GREEDY"), PHP_HTTP_MESSAGE_PARSER_GREEDY TSRMLS_CC);
+
+       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_FAILURE"), PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_START"), PHP_HTTP_MESSAGE_PARSER_STATE_START TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DUMB"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_LENGTH"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_CHUNKED"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_UPDATE_CL"), PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_DONE TSRMLS_CC);
+
+       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_message_parser.h b/src/php_http_message_parser.h
new file mode 100644 (file)
index 0000000..0bac9da
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_MESSAGE_PARSER_H
+#define PHP_HTTP_MESSAGE_PARSER_H
+
+#include "php_http_header_parser.h"
+#include "php_http_encoding.h"
+#include "php_http_message.h"
+
+typedef enum php_http_message_parser_state {
+       PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE = FAILURE,
+       PHP_HTTP_MESSAGE_PARSER_STATE_START = 0,
+       PHP_HTTP_MESSAGE_PARSER_STATE_HEADER,
+       PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE,
+       PHP_HTTP_MESSAGE_PARSER_STATE_BODY,
+       PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB,
+       PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH,
+       PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED,
+       PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE,
+       PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL,
+       PHP_HTTP_MESSAGE_PARSER_STATE_DONE
+} php_http_message_parser_state_t;
+
+#define PHP_HTTP_MESSAGE_PARSER_CLEANUP                        0x1
+#define PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES            0x2
+#define PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS        0x4
+#define PHP_HTTP_MESSAGE_PARSER_GREEDY                 0x8
+
+typedef struct php_http_message_parser {
+       php_http_header_parser_t header;
+       zend_ptr_stack stack;
+       size_t body_length;
+       php_http_message_t *message;
+       php_http_encoding_stream_t *dechunk;
+       php_http_encoding_stream_t *inflate;
+#ifdef ZTS
+       void ***ts;
+#endif
+} php_http_message_parser_t;
+
+PHP_HTTP_API php_http_message_parser_t *php_http_message_parser_init(php_http_message_parser_t *parser TSRMLS_DC);
+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);
+PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_message_parser_t *parser, php_http_buffer_t *buffer, php_stream *s, unsigned flags, php_http_message_t **message);
+
+typedef struct php_http_message_parser_object {
+       zend_object zo;
+       zend_object_value zv;
+       php_http_buffer_t *buffer;
+       php_http_message_parser_t *parser;
+} php_http_message_parser_object_t;
+
+PHP_HTTP_API zend_class_entry *php_http_message_parser_class_entry;
+
+PHP_MINIT_FUNCTION(http_message_parser);
+
+zend_object_value php_http_message_parser_object_new(zend_class_entry *ce TSRMLS_DC);
+zend_object_value php_http_message_parser_object_new_ex(zend_class_entry *ce, php_http_message_parser_t *parser, php_http_message_parser_object_t **ptr TSRMLS_DC);
+void php_http_message_parser_object_free(void *object TSRMLS_DC);
+
+#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_misc.c b/src/php_http_misc.c
new file mode 100644 (file)
index 0000000..8e2227d
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+#include <ext/standard/php_lcg.h>
+#include <zend_exceptions.h>
+
+/* SLEEP */
+
+void php_http_sleep(double s)
+{
+#if defined(PHP_WIN32)
+       Sleep((DWORD) PHP_HTTP_MSEC(s));
+#elif defined(HAVE_USLEEP)
+       usleep(PHP_HTTP_USEC(s));
+#elif defined(HAVE_NANOSLEEP)
+       struct timespec req, rem;
+
+       req.tv_sec = (time_t) s;
+       req.tv_nsec = PHP_HTTP_NSEC(s) % PHP_HTTP_NANOSEC;
+
+       while (nanosleep(&req, &rem) && (errno == EINTR) && (PHP_HTTP_NSEC(rem.tv_sec) + rem.tv_nsec) > PHP_HTTP_NSEC(PHP_HTTP_DIFFSEC))) {
+               req.tv_sec = rem.tv_sec;
+               req.tv_nsec = rem.tv_nsec;
+       }
+#else
+       struct timeval timeout;
+
+       timeout.tv_sec = (time_t) s;
+       timeout.tv_usec = PHP_HTTP_USEC(s) % PHP_HTTP_MCROSEC;
+
+       select(0, NULL, NULL, NULL, &timeout);
+#endif
+}
+
+
+/* STRING UTILITIES */
+
+int php_http_match(const char *haystack_str, const char *needle_str, int flags)
+{
+       int result = 0;
+
+       if (!haystack_str || !needle_str) {
+               return result;
+       }
+
+       if (flags & PHP_HTTP_MATCH_FULL) {
+               if (flags & PHP_HTTP_MATCH_CASE) {
+                       result = !strcmp(haystack_str, needle_str);
+               } else {
+                       result = !strcasecmp(haystack_str, needle_str);
+               }
+       } else {
+               const char *found;
+               char *haystack = estrdup(haystack_str), *needle = estrdup(needle_str);
+
+               if (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));
+               }
+
+               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))))
+                               )
+                       ) {
+                               result = 1;
+                       }
+               }
+
+               PTR_FREE(haystack);
+               PTR_FREE(needle);
+       }
+
+       return result;
+}
+
+char *php_http_pretty_key(register char *key, size_t key_len, zend_bool uctitle, zend_bool xhyphen)
+{
+       size_t i = 1;
+       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]));
+               }
+               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;
+                       } else {
+                               if (xhyphen && (key[i] == '_')) {
+                                       key[i] = '-';
+                               }
+                               wasalpha = 0;
+                       }
+                       ++i;
+               );
+       }
+       return key;
+}
+
+
+size_t php_http_boundary(char *buf, size_t buf_len TSRMLS_DC)
+{
+       return snprintf(buf, buf_len, "%15.15F", sapi_get_request_time(TSRMLS_C) * php_combined_lcg(TSRMLS_C));
+}
+
+int php_http_select_str(const char *cmp, int argc, ...)
+{
+       va_list argv;
+       int match = -1;
+
+       if (cmp && argc > 0) {
+               int i;
+
+               va_start(argv, argc);
+               for (i = 0; i < argc; ++i) {
+                       const char *test = va_arg(argv, const char *);
+
+                       if (!strcasecmp(cmp, test)) {
+                               match = i;
+                               break;
+                       }
+               }
+               va_end(argv);
+       }
+
+       return match;
+}
+
+
+/* ARRAYS */
+
+unsigned php_http_array_list(HashTable *ht TSRMLS_DC, unsigned argc, ...)
+{
+       HashPosition pos;
+       unsigned argl = 0;
+       va_list argv;
+
+       va_start(argv, argc);
+       for (   zend_hash_internal_pointer_reset_ex(ht, &pos);
+                       SUCCESS == zend_hash_has_more_elements_ex(ht, &pos) && (argl < argc);
+                       zend_hash_move_forward_ex(ht, &pos))
+       {
+               zval **data, ***argp = (zval ***) va_arg(argv, zval ***);
+
+               if (SUCCESS == zend_hash_get_current_data_ex(ht, (void *) &data, &pos)) {
+                       *argp = data;
+                       ++argl;
+               }
+       }
+       va_end(argv);
+
+       return argl;
+}
+
+void php_http_array_copy_strings(void *zpp)
+{
+       zval **zvpp = ((zval **) zpp);
+
+       *zvpp = php_http_zsep(1, IS_STRING, *zvpp);
+}
+
+int php_http_array_apply_append_func(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
+{
+       int flags;
+       char *key = NULL;
+       HashTable *dst;
+       zval **data = NULL, *value = *((zval **) pDest);
+
+       dst = va_arg(args, HashTable *);
+       flags = va_arg(args, int);
+
+       if ((!(flags & ARRAY_JOIN_STRONLY)) || hash_key->nKeyLength) {
+               if ((flags & ARRAY_JOIN_PRETTIFY) && hash_key->nKeyLength) {
+                       key = php_http_pretty_key(estrndup(hash_key->arKey, hash_key->nKeyLength - 1), hash_key->nKeyLength - 1, 1, 1);
+                       zend_hash_find(dst, key, hash_key->nKeyLength, (void *) &data);
+               } else if (hash_key->nKeyLength) {
+                       zend_hash_quick_find(dst, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &data);
+               } else {
+                       zend_hash_index_find(dst, hash_key->h, (void *) &data);
+               }
+
+               if (flags & ARRAY_JOIN_STRINGIFY) {
+                       value = php_http_zsep(1, IS_STRING, value);
+               } else {
+                       Z_ADDREF_P(value);
+               }
+
+               if (data) {
+                       if (Z_TYPE_PP(data) != IS_ARRAY) {
+                               convert_to_array(*data);
+                       }
+                       add_next_index_zval(*data, value);
+               } else if (key) {
+                       zend_symtable_update(dst, key, hash_key->nKeyLength, &value, sizeof(zval *), NULL);
+               } else if (hash_key->nKeyLength) {
+                       zend_hash_quick_add(dst, hash_key->arKey, hash_key->nKeyLength, hash_key->h, &value, sizeof(zval *), NULL);
+               } else {
+                       zend_hash_index_update(dst, hash_key->h, (void *) &value, sizeof(zval *), NULL);
+               }
+
+               if (key) {
+                       efree(key);
+               }
+       }
+
+       return ZEND_HASH_APPLY_KEEP;
+}
+
+int php_http_array_apply_merge_func(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
+{
+       int flags;
+       char *key = NULL;
+       HashTable *dst;
+       zval *value = *((zval **) pDest);
+
+       dst = va_arg(args, HashTable *);
+       flags = va_arg(args, int);
+
+       if ((!(flags & ARRAY_JOIN_STRONLY)) || hash_key->nKeyLength) {
+               if (flags & ARRAY_JOIN_STRINGIFY) {
+                       value = php_http_zsep(1, IS_STRING, value);
+               } else {
+                       Z_ADDREF_P(value);
+               }
+
+               if ((flags & ARRAY_JOIN_PRETTIFY) && hash_key->nKeyLength) {
+                       key = php_http_pretty_key(estrndup(hash_key->arKey, hash_key->nKeyLength - 1), hash_key->nKeyLength - 1, 1, 1);
+                       zend_hash_update(dst, key, hash_key->nKeyLength, (void *) &value, sizeof(zval *), NULL);
+                       efree(key);
+               } else if (hash_key->nKeyLength) {
+                       zend_hash_quick_update(dst, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &value, sizeof(zval *), NULL);
+               } else {
+                       zend_hash_index_update(dst, hash_key->h, (void *) &value, sizeof(zval *), NULL);
+               }
+       }
+
+       return ZEND_HASH_APPLY_KEEP;
+}
+
+/* PASS CALLBACK */
+
+size_t php_http_pass_fcall_callback(void *cb_arg, const char *str, size_t len)
+{
+       php_http_pass_fcall_arg_t *fcd = cb_arg;
+       zval *zdata;
+       TSRMLS_FETCH_FROM_CTX(fcd->ts);
+
+       MAKE_STD_ZVAL(zdata);
+       ZVAL_STRINGL(zdata, str, len, 1);
+       if (SUCCESS == zend_fcall_info_argn(&fcd->fci TSRMLS_CC, 2, &fcd->fcz, &zdata)) {
+               zend_fcall_info_call(&fcd->fci, &fcd->fcc, NULL, NULL TSRMLS_CC);
+               zend_fcall_info_args_clear(&fcd->fci, 0);
+       }
+       zval_ptr_dtor(&zdata);
+       return len;
+}
+
+
+/* ZEND */
+
+/*
+ * 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_misc.h b/src/php_http_misc.h
new file mode 100644 (file)
index 0000000..a4f579d
--- /dev/null
@@ -0,0 +1,366 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_MISC_H
+#define PHP_HTTP_MISC_H
+
+/* DEFAULTS */
+
+/* DATE FORMAT RFC1123 */
+#define PHP_HTTP_DATE_FORMAT "D, d M Y H:i:s \\G\\M\\T"
+
+/* CR LF */
+#define PHP_HTTP_CRLF "\r\n"
+
+/* def URL arg separator */
+#define PHP_HTTP_URL_ARGSEP "&"
+
+/* send buffer size */
+#define PHP_HTTP_SENDBUF_SIZE 40960
+
+/* allowed characters of header field names */
+#define PHP_HTTP_HEADER_NAME_CHARS "!#$%&'*+-.^_`|~1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+/* SLEEP */
+
+#define PHP_HTTP_DIFFSEC (0.001)
+#define PHP_HTTP_MLLISEC (1000)
+#define PHP_HTTP_MCROSEC (1000 * 1000)
+#define PHP_HTTP_NANOSEC (1000 * 1000 * 1000)
+#define PHP_HTTP_MSEC(s) ((long)(s * PHP_HTTP_MLLISEC))
+#define PHP_HTTP_USEC(s) ((long)(s * PHP_HTTP_MCROSEC))
+#define PHP_HTTP_NSEC(s) ((long)(s * PHP_HTTP_NANOSEC))
+
+PHP_HTTP_API void php_http_sleep(double s);
+
+/* STRING UTILITIES */
+
+#ifndef PTR_SET
+#      define PTR_SET(STR, SET) \
+       { \
+               PTR_FREE(STR); \
+               STR = SET; \
+       }
+#endif
+
+#define STR_PTR(s) (s?s:"")
+
+#define lenof(S) (sizeof(S) - 1)
+
+#define PHP_HTTP_MATCH_LOOSE   0
+#define PHP_HTTP_MATCH_CASE            0x01
+#define PHP_HTTP_MATCH_WORD            0x10
+#define PHP_HTTP_MATCH_FULL            0x20
+#define PHP_HTTP_MATCH_STRICT  (PHP_HTTP_MATCH_CASE|PHP_HTTP_MATCH_FULL)
+
+int php_http_match(const char *haystack, const char *needle, int flags);
+char *php_http_pretty_key(register char *key, size_t key_len, zend_bool uctitle, zend_bool xhyphen);
+size_t php_http_boundary(char *buf, size_t len TSRMLS_DC);
+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;
+}
+
+static inline const char *php_http_locate_eol(const char *line, int *eol_len)
+{
+       const char *eol = strpbrk(line, "\r\n");
+
+       if (eol_len) {
+               *eol_len = eol ? ((eol[0] == '\r' && eol[1] == '\n') ? 2 : 1) : 0;
+       }
+       return eol;
+}
+
+static inline const char *php_http_locate_bin_eol(const char *bin, size_t len, int *eol_len)
+{
+       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;
+                       }
+                       ++eol;
+               );
+       }
+       return NULL;
+}
+
+/* ZEND */
+
+#if PHP_VERSION_ID < 50400
+#      define object_properties_init(o, ce) zend_hash_copy(((zend_object *) o)->properties, &(ce->default_properties), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*))
+#      define PHP_HTTP_ZEND_LITERAL_DC 
+#      define PHP_HTTP_ZEND_LITERAL_CC
+#      define PHP_HTTP_ZEND_LITERAL_CCN
+#      define ZVAL_COPY_VALUE(zv, arr) do { \
+               (zv)->value = (arr)->value; \
+               Z_TYPE_P(zv) = Z_TYPE_P(arr); \
+       } while (0)
+#else
+#      define PHP_HTTP_ZEND_LITERAL_DC , const zend_literal *literal_key
+#      define PHP_HTTP_ZEND_LITERAL_CC , (literal_key)
+#      define PHP_HTTP_ZEND_LITERAL_CCN , NULL
+#endif
+
+#if PHP_VERSION_ID < 50500
+#undef SUCCESS
+#undef FAILURE
+typedef enum {
+       SUCCESS = 0,
+       FAILURE = -1
+} ZEND_RESULT_CODE;
+#endif
+
+#if PHP_VERSION_ID < 50700
+#      define z_is_true zend_is_true
+#else
+#      define z_is_true(z) zend_is_true(z TSRMLS_CC)
+#endif
+
+#define INIT_PZVAL_ARRAY(zv, ht) \
+       { \
+               INIT_PZVAL((zv)); \
+               Z_TYPE_P(zv) = IS_ARRAY; \
+               Z_ARRVAL_P(zv) = (ht); \
+       }
+
+static inline zval *php_http_zconv(int type, zval *z)
+{
+       switch (type) {
+               case IS_NULL:   convert_to_null_ex(&z);         break;
+               case IS_BOOL:   convert_to_boolean_ex(&z);      break;
+               case IS_LONG:   convert_to_long_ex(&z);         break;
+               case IS_DOUBLE: convert_to_double_ex(&z);       break;
+               case IS_STRING: convert_to_string_ex(&z);       break;
+               case IS_ARRAY:  convert_to_array_ex(&z);        break;
+               case IS_OBJECT: convert_to_object_ex(&z);       break;
+       }
+       return z;
+}
+
+static inline zval *php_http_ztyp(int type, zval *z)
+{
+       SEPARATE_ARG_IF_REF(z);
+       return (Z_TYPE_P(z) == type) ? z : php_http_zconv(type, z);
+}
+
+static inline zval *php_http_zsep(zend_bool add_ref, int type, zval *z)
+{
+       if (add_ref) {
+               Z_ADDREF_P(z);
+       }
+       if (Z_TYPE_P(z) != type) {
+               return php_http_zconv(type, z);
+       } else {
+               SEPARATE_ZVAL_IF_NOT_REF(&z);
+               return z;
+       }
+}
+
+static inline ZEND_RESULT_CODE php_http_ini_entry(const char *name_str, size_t name_len, const char **value_str, size_t *value_len, zend_bool orig TSRMLS_DC)
+{
+       zend_ini_entry *ini_entry;
+
+       if (SUCCESS == zend_hash_find(EG(ini_directives), name_str, name_len + 1, (void *) &ini_entry)) {
+               if (orig && ini_entry->modified) {
+                       *value_str = ini_entry->orig_value;
+                       *value_len = (size_t) ini_entry->orig_value_length;
+               } else {
+                       *value_str = ini_entry->value;
+                       *value_len = (size_t) ini_entry->value_length;
+               }
+               return SUCCESS;
+       }
+       return FAILURE;
+}
+
+/* return object(values) */
+#define RETVAL_OBJECT(o, addref) \
+       RETVAL_OBJVAL((o)->value.obj, addref)
+#define RETURN_OBJECT(o, addref) \
+       RETVAL_OBJECT(o, addref); \
+       return
+#define RETVAL_OBJVAL(ov, addref) \
+       ZVAL_OBJVAL(return_value, ov, addref)
+#define RETURN_OBJVAL(ov, addref) \
+       RETVAL_OBJVAL(ov, addref); \
+       return
+#define ZVAL_OBJVAL(zv, ov, addref) \
+       (zv)->type = IS_OBJECT; \
+       (zv)->value.obj = (ov);\
+       if (addref && Z_OBJ_HT_P(zv)->add_ref) { \
+               Z_OBJ_HT_P(zv)->add_ref((zv) TSRMLS_CC); \
+       }
+
+#define Z_OBJ_DELREF(z) \
+       if (Z_OBJ_HT(z)->del_ref) { \
+               Z_OBJ_HT(z)->del_ref(&(z) TSRMLS_CC); \
+       }
+#define Z_OBJ_ADDREF(z) \
+       if (Z_OBJ_HT(z)->add_ref) { \
+               Z_OBJ_HT(z)->add_ref(&(z) TSRMLS_CC); \
+       }
+#define Z_OBJ_DELREF_P(z) \
+       if (Z_OBJ_HT_P(z)->del_ref) { \
+               Z_OBJ_HT_P(z)->del_ref((z) TSRMLS_CC); \
+       }
+#define Z_OBJ_ADDREF_P(z) \
+       if (Z_OBJ_HT_P(z)->add_ref) { \
+               Z_OBJ_HT_P(z)->add_ref((z) TSRMLS_CC); \
+       }
+#define Z_OBJ_DELREF_PP(z) \
+       if (Z_OBJ_HT_PP(z)->del_ref) { \
+               Z_OBJ_HT_PP(z)->del_ref(*(z) TSRMLS_CC); \
+       }
+#define Z_OBJ_ADDREF_PP(z) \
+       if (Z_OBJ_HT_PP(z)->add_ref) { \
+               Z_OBJ_HT_PP(z)->add_ref(*(z) TSRMLS_CC); \
+       }
+
+#define EMPTY_FUNCTION_ENTRY {NULL, NULL, NULL, 0, 0}
+
+#define PHP_MINIT_CALL(func) PHP_MINIT(func)(INIT_FUNC_ARGS_PASSTHRU)
+#define PHP_RINIT_CALL(func) PHP_RINIT(func)(INIT_FUNC_ARGS_PASSTHRU)
+#define PHP_MSHUTDOWN_CALL(func) PHP_MSHUTDOWN(func)(SHUTDOWN_FUNC_ARGS_PASSTHRU)
+#define PHP_RSHUTDOWN_CALL(func) PHP_RSHUTDOWN(func)(SHUTDOWN_FUNC_ARGS_PASSTHRU)
+
+/* ARRAYS */
+
+#ifndef HASH_KEY_NON_EXISTENT
+#      define HASH_KEY_NON_EXISTENT HASH_KEY_NON_EXISTANT
+#endif
+
+PHP_HTTP_API unsigned php_http_array_list(HashTable *ht TSRMLS_DC, unsigned argc, ...);
+
+typedef struct php_http_array_hashkey {
+       char *str;
+       uint len;
+       ulong num;
+       uint dup:1;
+       uint type:31;
+} php_http_array_hashkey_t;
+#define php_http_array_hashkey_init(dup) {NULL, 0, 0, (dup), 0}
+
+static inline void php_http_array_hashkey_stringify(php_http_array_hashkey_t *key)
+{
+       if (key->type != HASH_KEY_IS_STRING) {
+               key->len = spprintf(&key->str, 0, "%lu", key->num) + 1;
+       }
+}
+
+static inline void php_http_array_hashkey_stringfree(php_http_array_hashkey_t *key)
+{
+       if (key->type != HASH_KEY_IS_STRING || key->dup) {
+               PTR_FREE(key->str);
+       }
+}
+
+#define FOREACH_VAL(pos, array, val) FOREACH_HASH_VAL(pos, HASH_OF(array), val)
+#define FOREACH_HASH_VAL(pos, hash, val) \
+       for (   zend_hash_internal_pointer_reset_ex(hash, &pos); \
+                       zend_hash_get_current_data_ex(hash, (void *) &val, &pos) == SUCCESS; \
+                       zend_hash_move_forward_ex(hash, &pos))
+
+#define FOREACH_KEY(pos, array, key) FOREACH_HASH_KEY(pos, HASH_OF(array), key)
+#define FOREACH_HASH_KEY(pos, hash, _key) \
+       for (   zend_hash_internal_pointer_reset_ex(hash, &pos); \
+                       ((_key).type = zend_hash_get_current_key_ex(hash, &(_key).str, &(_key).len, &(_key).num, (zend_bool) (_key).dup, &pos)) != HASH_KEY_NON_EXISTANT; \
+                       zend_hash_move_forward_ex(hash, &pos)) \
+
+#define FOREACH_KEYVAL(pos, array, key, val) FOREACH_HASH_KEYVAL(pos, HASH_OF(array), key, val)
+#define FOREACH_HASH_KEYVAL(pos, hash, _key, val) \
+       for (   zend_hash_internal_pointer_reset_ex(hash, &pos); \
+                       ((_key).type = zend_hash_get_current_key_ex(hash, &(_key).str, &(_key).len, &(_key).num, (zend_bool) (_key).dup, &pos)) != HASH_KEY_NON_EXISTANT && \
+                       zend_hash_get_current_data_ex(hash, (void *) &val, &pos) == SUCCESS; \
+                       zend_hash_move_forward_ex(hash, &pos))
+
+#define array_copy(src, dst) zend_hash_copy(dst, src, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *))
+#define array_copy_strings(src, dst) zend_hash_copy(dst, src, php_http_array_copy_strings, NULL, sizeof(zval *))
+#define ARRAY_JOIN_STRONLY   0x01
+#define ARRAY_JOIN_PRETTIFY  0x02
+#define ARRAY_JOIN_STRINGIFY 0x04
+#define array_join(src, dst, append, flags) zend_hash_apply_with_arguments(src TSRMLS_CC, (append)?php_http_array_apply_append_func:php_http_array_apply_merge_func, 2, dst, (int)flags)
+
+void php_http_array_copy_strings(void *zpp);
+int php_http_array_apply_append_func(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key);
+int php_http_array_apply_merge_func(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key);
+
+/* PASS CALLBACK */
+
+typedef size_t (*php_http_pass_callback_t)(void *cb_arg, const char *str, size_t len);
+typedef size_t (*php_http_pass_php_http_buffer_callback_t)(void *cb_arg, php_http_buffer_t *str);
+typedef size_t (*php_http_pass_format_callback_t)(void *cb_arg, const char *fmt, ...);
+
+typedef struct php_http_pass_fcall_arg {
+       zval *fcz;
+       zend_fcall_info fci;
+       zend_fcall_info_cache fcc;
+#ifdef ZTS
+       void ***ts;
+#endif
+} php_http_pass_fcall_arg_t;
+
+PHP_HTTP_API size_t php_http_pass_fcall_callback(void *cb_arg, const char *str, size_t len);
+
+#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_negotiate.c b/src/php_http_negotiate.c
new file mode 100644 (file)
index 0000000..a74875b
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+static int php_http_negotiate_sort(const void *a, const void *b TSRMLS_DC)
+{
+       zval result, *first, *second;
+
+       first = *((zval **) (*((Bucket **) a))->pData);
+       second= *((zval **) (*((Bucket **) b))->pData);
+
+       if (numeric_compare_function(&result, first, second TSRMLS_CC) != SUCCESS) {
+               return 0;
+       }
+       return (Z_LVAL(result) > 0 ? -1 : (Z_LVAL(result) < 0 ? 1 : 0));
+}
+
+#define M_PRI 5
+#define M_SEC 2
+#define M_ANY 1
+#define M_NOT 0
+#define M_ALL -1
+static inline unsigned php_http_negotiate_match(const char *param_str, size_t param_len, const char *supported_str, size_t supported_len, const char *sep_str, size_t sep_len)
+{
+       int match = M_NOT;
+
+       if (param_len == supported_len && !strncasecmp(param_str, supported_str, param_len)) {
+               /* that was easy */
+               match = M_ALL;
+       } else if (sep_str && sep_len) {
+               const char *param_sec = php_http_locate_str(param_str, param_len, sep_str, sep_len);
+               size_t param_pri_len = param_sec ? param_sec - param_str : param_len;
+               const char *supported_sec = php_http_locate_str(supported_str, supported_len, sep_str, sep_len);
+               size_t supported_pri_len = supported_sec ? supported_sec - supported_str : supported_len;
+               size_t cmp_len = MIN(param_pri_len, supported_pri_len);
+
+               if (((*param_str == '*') || (*supported_str == '*'))
+               ||      ((param_pri_len == supported_pri_len) && !strncasecmp(param_str, supported_str, param_pri_len))
+               ||      ((!param_sec || !supported_sec) && cmp_len && !strncasecmp(param_str, supported_str, cmp_len))
+               ) {
+                       match += M_PRI;
+               }
+
+               if (param_sec && supported_sec && !strcasecmp(param_sec, supported_sec)) {
+                       match += M_SEC;
+               }
+
+               if ((param_sec && *(param_sec + sep_len) == '*')
+               ||      (supported_sec && *(supported_sec + sep_len) == '*')
+               ||      ((*param_str == '*') || (*supported_str == '*'))
+               ) {
+                       match += M_ANY;
+               }
+       }
+#if 0
+       fprintf(stderr, "match: %s == %s => %u\n", supported_str, param_str, match);
+#endif
+       return match;
+}
+
+static int php_http_negotiate_reduce(void *p TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
+{
+       unsigned best_match = 0;
+       HashPosition pos;
+       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+       zval **q = NULL, **val, *supported = php_http_ztyp(IS_STRING, *(zval **)p);
+       HashTable *params = va_arg(args, HashTable *);
+       HashTable *result = va_arg(args, HashTable *);
+       const char *sep_str = va_arg(args, const char *);
+       size_t sep_len = va_arg(args, size_t);
+
+       FOREACH_HASH_KEYVAL(pos, params, key, val) {
+               if (key.type == HASH_KEY_IS_STRING) {
+                       unsigned match = php_http_negotiate_match(key.str, key.len-1, Z_STRVAL_P(supported), Z_STRLEN_P(supported), sep_str, sep_len);
+
+                       if (match > best_match) {
+                               best_match = match;
+                               q = val;
+                       }
+               }
+       }
+
+       if (q && Z_DVAL_PP(q) > 0) {
+               Z_ADDREF_PP(q);
+               zend_hash_update(result, Z_STRVAL_P(supported), Z_STRLEN_P(supported) + 1, (void *) q, sizeof(zval *), NULL);
+       }
+
+       zval_ptr_dtor(&supported);
+       return ZEND_HASH_APPLY_KEEP;
+}
+
+HashTable *php_http_negotiate(const char *value_str, size_t value_len, HashTable *supported, const char *primary_sep_str, size_t primary_sep_len TSRMLS_DC)
+{
+       HashTable *result = NULL;
+
+       if (value_str && value_len) {
+               unsigned i = 0;
+               zval arr, **val, **arg, **zq;
+               HashPosition pos;
+               HashTable params;
+               php_http_array_hashkey_t key = php_http_array_hashkey_init(1);
+               php_http_params_opts_t opts;
+
+               zend_hash_init(&params, 10, NULL, ZVAL_PTR_DTOR, 0);
+               php_http_params_opts_default_get(&opts);
+               opts.input.str = estrndup(value_str, value_len);
+               opts.input.len = value_len;
+               php_http_params_parse(&params, &opts TSRMLS_CC);
+               efree(opts.input.str);
+
+               INIT_PZVAL(&arr);
+               array_init(&arr);
+
+               FOREACH_HASH_KEYVAL(pos, &params, key, val) {
+                       double q;
+
+                       if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("arguments"), (void *) &arg)
+                       &&      IS_ARRAY == Z_TYPE_PP(arg)
+                       &&      SUCCESS == zend_hash_find(Z_ARRVAL_PP(arg), ZEND_STRS("q"), (void *) &zq)) {
+                               zval *tmp = php_http_ztyp(IS_DOUBLE, *zq);
+
+                               q = Z_DVAL_P(tmp);
+                               zval_ptr_dtor(&tmp);
+                       } else {
+                               q = 1.0 - ++i / 100.0;
+                       }
+
+                       if (key.type == HASH_KEY_IS_STRING) {
+                               add_assoc_double_ex(&arr, key.str, key.len, q);
+                       } else {
+                               add_index_double(&arr, key.num, q);
+                       }
+
+                       PTR_FREE(key.str);
+               }
+
+#if 0
+               zend_print_zval_r(&arr, 1 TSRMLS_CC);
+#endif
+
+               ALLOC_HASHTABLE(result);
+               zend_hash_init(result, zend_hash_num_elements(supported), NULL, ZVAL_PTR_DTOR, 0);
+               zend_hash_apply_with_arguments(supported TSRMLS_CC, php_http_negotiate_reduce, 4, Z_ARRVAL(arr), result, primary_sep_str, primary_sep_len);
+               zend_hash_destroy(&params);
+               zval_dtor(&arr);
+               zend_hash_sort(result, zend_qsort, php_http_negotiate_sort, 0 TSRMLS_CC);
+       }
+       
+       return result;
+}
+
+
+
+/*
+ * 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_negotiate.h b/src/php_http_negotiate.h
new file mode 100644 (file)
index 0000000..f7405b5
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_NEGOTIATE_H
+#define PHP_HTTP_NEGOTIATE_H
+
+PHP_HTTP_API HashTable *php_http_negotiate(const char *value_str, size_t value_len, HashTable *supported, const char *primary_sep_str, size_t primary_sep_len TSRMLS_DC);
+
+static inline HashTable *php_http_negotiate_language(HashTable *supported, php_http_message_t *request TSRMLS_DC)
+{
+       HashTable *result = NULL;
+       size_t length;
+       char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Language"), &length, request TSRMLS_CC);
+
+       if (value) {
+               result = php_http_negotiate(value, length, supported, "-", 1 TSRMLS_CC);
+       }
+       PTR_FREE(value);
+
+       return result;
+}
+
+static inline HashTable *php_http_negotiate_encoding(HashTable *supported, php_http_message_t *request TSRMLS_DC)
+{
+       HashTable *result = NULL;
+       size_t length;
+       char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Encoding"), &length, request TSRMLS_CC);
+
+       if (value) {
+               result = php_http_negotiate(value, length, supported, NULL, 0 TSRMLS_CC);
+       }
+       PTR_FREE(value);
+
+       return result;
+}
+
+static inline HashTable *php_http_negotiate_charset(HashTable *supported, php_http_message_t *request TSRMLS_DC)
+{
+       HashTable *result = NULL;
+       size_t length;
+       char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Charset"), &length, request TSRMLS_CC);
+
+       if (value) {
+               result = php_http_negotiate(value, length, supported, NULL, 0 TSRMLS_CC);
+       }
+       PTR_FREE(value);
+
+       return result;
+}
+
+static inline HashTable *php_http_negotiate_content_type(HashTable *supported, php_http_message_t *request TSRMLS_DC)
+{
+       HashTable *result = NULL;
+       size_t length;
+       char *value = php_http_env_get_request_header(ZEND_STRL("Accept"), &length, request TSRMLS_CC);
+
+       if (value) {
+               result = php_http_negotiate(value, length, supported, "/", 1 TSRMLS_CC);
+       }
+       PTR_FREE(value);
+
+       return result;
+}
+
+#define PHP_HTTP_DO_NEGOTIATE_DEFAULT(supported) \
+       { \
+               zval **value; \
+                \
+               zend_hash_internal_pointer_reset((supported)); \
+               if (SUCCESS == zend_hash_get_current_data((supported), (void *) &value)) { \
+                       RETVAL_ZVAL(*value, 1, 0); \
+               } else { \
+                       RETVAL_NULL(); \
+               } \
+       }
+
+#define PHP_HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported, rs_array) \
+       PHP_HTTP_DO_NEGOTIATE_DEFAULT(supported); \
+       if (rs_array) { \
+               HashPosition pos; \
+               zval **value_ptr; \
+                \
+               FOREACH_HASH_VAL(pos, supported, value_ptr) { \
+                       zval *value = php_http_ztyp(IS_STRING, *value_ptr); \
+                       add_assoc_double(rs_array, Z_STRVAL_P(value), 1.0); \
+                       zval_ptr_dtor(&value); \
+               } \
+       }
+
+#define PHP_HTTP_DO_NEGOTIATE_HANDLE_RESULT(result, supported, rs_array) \
+       { \
+               char *key; \
+               uint key_len; \
+               ulong idx; \
+                \
+               if (zend_hash_num_elements(result) && HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(result, &key, &key_len, &idx, 1, NULL)) { \
+                       RETVAL_STRINGL(key, key_len-1, 0); \
+               } else { \
+                       PHP_HTTP_DO_NEGOTIATE_DEFAULT(supported); \
+               } \
+               \
+               if (rs_array) { \
+                       zend_hash_copy(Z_ARRVAL_P(rs_array), result, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); \
+               } \
+               \
+               zend_hash_destroy(result); \
+               FREE_HASHTABLE(result); \
+       }
+
+#define PHP_HTTP_DO_NEGOTIATE(type, supported, rs_array) \
+       { \
+               HashTable *result; \
+               if ((result = php_http_negotiate_ ##type(supported, NULL TSRMLS_CC))) { \
+                       PHP_HTTP_DO_NEGOTIATE_HANDLE_RESULT(result, supported, rs_array); \
+               } else { \
+                       PHP_HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported, rs_array); \
+               } \
+       }
+
+
+#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_object.c b/src/php_http_object.c
new file mode 100644 (file)
index 0000000..d57388d
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+zend_object_value php_http_object_new(zend_class_entry *ce TSRMLS_DC)
+{
+       return php_http_object_new_ex(ce, NULL, NULL TSRMLS_CC);
+}
+
+zend_object_value php_http_object_new_ex(zend_class_entry *ce, void *nothing, php_http_object_t **ptr TSRMLS_DC)
+{
+       php_http_object_t *o;
+
+       o = ecalloc(1, sizeof(php_http_object_t));
+       zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
+       object_properties_init((zend_object *) o, ce);
+
+       if (ptr) {
+               *ptr = o;
+       }
+
+       o->zv.handle = zend_objects_store_put(o, NULL, (zend_objects_free_object_storage_t) zend_objects_free_object_storage, NULL TSRMLS_CC);
+       o->zv.handlers = zend_get_std_object_handlers();
+
+       return o->zv;
+}
+
+ZEND_RESULT_CODE php_http_new(zend_object_value *ovp, zend_class_entry *ce, php_http_new_t create, zend_class_entry *parent_ce, void *intern_ptr, void **obj_ptr TSRMLS_DC)
+{
+       zend_object_value ov;
+
+       if (!ce) {
+               ce = parent_ce;
+       } else if (parent_ce && !instanceof_function(ce, parent_ce TSRMLS_CC)) {
+               php_http_throw(unexpected_val, "Class %s does not extend %s", ce->name, parent_ce->name);
+               return FAILURE;
+       }
+
+       ov = create(ce, intern_ptr, obj_ptr TSRMLS_CC);
+       if (ovp) {
+               *ovp = ov;
+       }
+       return SUCCESS;
+}
+
+static inline zend_function *get_object_method(zval *zobject, zval *zmeth TSRMLS_DC)
+{
+#if PHP_VERSION_ID >= 50400
+       return Z_OBJ_HT_P(zobject)->get_method(&zobject, Z_STRVAL_P(zmeth), Z_STRLEN_P(zmeth), NULL TSRMLS_CC);
+#else
+       return Z_OBJ_HT_P(zobject)->get_method(&zobject, Z_STRVAL_P(zmeth), Z_STRLEN_P(zmeth) TSRMLS_CC);
+#endif
+}
+
+php_http_object_method_t *php_http_object_method_init(php_http_object_method_t *cb, zval *zobject, const char *method_str, size_t method_len TSRMLS_DC)
+{
+       zval *zfn;
+
+       if (!cb) {
+               cb = ecalloc(1, sizeof(*cb));
+       } else {
+               memset(cb, 0, sizeof(*cb));
+       }
+
+       MAKE_STD_ZVAL(zfn);
+       ZVAL_STRINGL(zfn, method_str, method_len, 1);
+
+       cb->fci.size = sizeof(cb->fci);
+       cb->fci.function_name = zfn;
+       cb->fcc.initialized = 1;
+       cb->fcc.calling_scope = cb->fcc.called_scope = Z_OBJCE_P(zobject);
+       cb->fcc.function_handler = get_object_method(zobject, cb->fci.function_name TSRMLS_CC);
+
+       return cb;
+}
+
+void php_http_object_method_dtor(php_http_object_method_t *cb)
+{
+       if (cb->fci.function_name) {
+               zval_ptr_dtor(&cb->fci.function_name);
+               cb->fci.function_name = NULL;
+       }
+}
+
+void php_http_object_method_free(php_http_object_method_t **cb)
+{
+       if (*cb) {
+               php_http_object_method_dtor(*cb);
+               efree(*cb);
+               *cb = NULL;
+       }
+}
+
+ZEND_RESULT_CODE php_http_object_method_call(php_http_object_method_t *cb, zval *zobject, zval **retval_ptr, int argc, zval ***args TSRMLS_DC)
+{
+       ZEND_RESULT_CODE rv;
+       zval *retval = NULL;
+
+       Z_ADDREF_P(zobject);
+       cb->fci.object_ptr = zobject;
+       cb->fcc.object_ptr = zobject;
+
+       cb->fci.retval_ptr_ptr = retval_ptr ? retval_ptr : &retval;
+
+       cb->fci.param_count = argc;
+       cb->fci.params = args;
+
+       if (cb->fcc.called_scope != Z_OBJCE_P(zobject)) {
+               cb->fcc.called_scope = Z_OBJCE_P(zobject);
+               cb->fcc.function_handler = get_object_method(zobject, cb->fci.function_name TSRMLS_CC);
+       }
+
+       rv = zend_call_function(&cb->fci, &cb->fcc TSRMLS_CC);
+
+       zval_ptr_dtor(&zobject);
+       if (!retval_ptr && retval) {
+               zval_ptr_dtor(&retval);
+       }
+
+       return rv;
+}
+
+/*
+ * 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_object.h b/src/php_http_object.h
new file mode 100644 (file)
index 0000000..3f75932
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_OBJECT_H
+#define PHP_HTTP_OBJECT_H
+
+typedef struct php_http_object {
+       zend_object zo;
+       zend_object_value zv;
+} php_http_object_t;
+
+zend_object_value php_http_object_new(zend_class_entry *ce TSRMLS_DC);
+zend_object_value php_http_object_new_ex(zend_class_entry *ce, void *nothing, php_http_object_t **ptr TSRMLS_DC);
+
+typedef zend_object_value (*php_http_new_t)(zend_class_entry *ce, void *, void ** TSRMLS_DC);
+
+ZEND_RESULT_CODE php_http_new(zend_object_value *ov, zend_class_entry *ce, php_http_new_t create, zend_class_entry *parent_ce, void *intern_ptr, void **obj_ptr TSRMLS_DC);
+
+typedef struct php_http_method {
+       zend_fcall_info fci;
+       zend_fcall_info_cache fcc;
+} php_http_object_method_t;
+
+php_http_object_method_t *php_http_object_method_init(php_http_object_method_t *cb, zval *zobject, const char *method_str, size_t method_len TSRMLS_DC);
+ZEND_RESULT_CODE php_http_object_method_call(php_http_object_method_t *cb, zval *zobject, zval **retval, int argc, zval ***args TSRMLS_DC);
+void php_http_object_method_dtor(php_http_object_method_t *cb);
+void php_http_object_method_free(php_http_object_method_t **cb);
+
+#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_options.c b/src/php_http_options.c
new file mode 100644 (file)
index 0000000..59b9c5f
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+php_http_options_t *php_http_options_init(php_http_options_t *registry, zend_bool persistent)
+{
+       if (!registry) {
+               registry = pecalloc(1, sizeof(*registry), persistent);
+       } else {
+               memset(registry, 0, sizeof(*registry));
+       }
+
+       registry->persistent = persistent;
+       zend_hash_init(&registry->options, 0, NULL, (dtor_func_t) zend_hash_destroy, persistent);
+
+       return registry;
+}
+
+ZEND_RESULT_CODE php_http_options_apply(php_http_options_t *registry, HashTable *options, void *userdata)
+{
+       HashPosition pos;
+       zval *val;
+       php_http_option_t *opt;
+
+       FOREACH_HASH_VAL(pos, &registry->options, opt) {
+               if (!(val = registry->getter(opt, options, userdata))) {
+                       val = &opt->defval;
+               }
+               if (registry->setter) {
+                       if (SUCCESS != registry->setter(opt, val, userdata)) {
+                               return FAILURE;
+                       }
+               } else if (!opt->setter || SUCCESS != opt->setter(opt, val, userdata)) {
+                       return FAILURE;
+               }
+       }
+       return SUCCESS;
+}
+
+void php_http_options_dtor(php_http_options_t *registry)
+{
+       zend_hash_destroy(&registry->options);
+}
+
+void php_http_options_free(php_http_options_t **registry)
+{
+       if (*registry) {
+               php_http_options_dtor(*registry);
+               pefree(*registry, (*registry)->persistent);
+               *registry = NULL;
+       }
+}
+
+php_http_option_t *php_http_option_register(php_http_options_t *registry, const char *name_str, size_t name_len, ulong option, zend_uchar type)
+{
+       php_http_option_t opt, *dst = NULL;
+
+       memset(&opt, 0, sizeof(opt));
+
+       php_http_options_init(&opt.suboptions, registry->persistent);
+       opt.suboptions.getter = registry->getter;
+       opt.suboptions.setter = registry->setter;
+
+       opt.name.h = zend_hash_func(opt.name.s = name_str, opt.name.l = name_len + 1);
+       opt.type = type;
+       opt.option = option;
+
+       INIT_ZVAL(opt.defval);
+       switch ((opt.type = type)) {
+       case IS_BOOL:
+               ZVAL_BOOL(&opt.defval, 0);
+               break;
+
+       case IS_LONG:
+               ZVAL_LONG(&opt.defval, 0);
+               break;
+
+       case IS_STRING:
+               ZVAL_STRINGL(&opt.defval, NULL, 0, 0);
+               break;
+
+       case IS_DOUBLE:
+               ZVAL_DOUBLE(&opt.defval, 0);
+               break;
+
+       default:
+               ZVAL_NULL(&opt.defval);
+               break;
+       }
+
+       zend_hash_quick_update(&registry->options, opt.name.s, opt.name.l, opt.name.h, (void *) &opt, sizeof(opt), (void *) &dst);
+       return dst;
+}
+
+zval *php_http_option_get(php_http_option_t *opt, HashTable *options, void *userdata)
+{
+       if (options) {
+               zval **zoption;
+
+               if (SUCCESS == zend_hash_quick_find(options, opt->name.s, opt->name.l, opt->name.h, (void *) &zoption)) {
+                       return *zoption;
+               }
+       }
+
+       return NULL;
+}
+
+
+/*
+ * 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_options.h b/src/php_http_options.h
new file mode 100644 (file)
index 0000000..2475383
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_OPTIONS_H
+#define PHP_HTTP_OPTIONS_H
+
+typedef struct php_http_option php_http_option_t;
+typedef struct php_http_options php_http_options_t;
+
+typedef ZEND_RESULT_CODE (*php_http_option_set_callback_t)(php_http_option_t *opt, zval *val, void *userdata);
+typedef zval *(*php_http_option_get_callback_t)(php_http_option_t *opt, HashTable *options, void *userdata);
+
+struct php_http_options {
+       HashTable options;
+
+       php_http_option_get_callback_t getter;
+       php_http_option_set_callback_t setter;
+
+       unsigned persistent:1;
+};
+
+struct php_http_option {
+       php_http_options_t suboptions;
+
+       struct {
+               const char *s;
+               size_t l;
+               ulong h;
+       } name;
+
+       ulong option;
+       zend_uchar type;
+       unsigned flags;
+       zval defval;
+
+       php_http_option_set_callback_t setter;
+};
+
+PHP_HTTP_API php_http_options_t *php_http_options_init(php_http_options_t *registry, zend_bool persistent);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_options_apply(php_http_options_t *registry, HashTable *options, void *userdata);
+PHP_HTTP_API void php_http_options_dtor(php_http_options_t *registry);
+PHP_HTTP_API void php_http_options_free(php_http_options_t **registry);
+
+PHP_HTTP_API php_http_option_t *php_http_option_register(php_http_options_t *registry, const char *name_str, size_t name_len, ulong option, zend_uchar type);
+PHP_HTTP_API zval *php_http_option_get(php_http_option_t *opt, HashTable *options, void *userdata);
+
+#endif /* PHP_HTTP_OPTIONS_H */
+
+/*
+ * 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_params.c b/src/php_http_params.c
new file mode 100644 (file)
index 0000000..5adeb91
--- /dev/null
@@ -0,0 +1,1283 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+static php_http_params_token_t def_param_sep = {",", 1}, *def_param_sep_ptr[] = {&def_param_sep, NULL};
+static php_http_params_token_t def_arg_sep = {";", 1}, *def_arg_sep_ptr[] = {&def_arg_sep, NULL};
+static php_http_params_token_t def_val_sep = {"=", 1}, *def_val_sep_ptr[] = {&def_val_sep, NULL};
+static php_http_params_opts_t def_opts = {
+       {NULL, 0},
+       def_param_sep_ptr,
+       def_arg_sep_ptr,
+       def_val_sep_ptr,
+       NULL,
+       PHP_HTTP_PARAMS_DEFAULT
+};
+
+php_http_params_opts_t *php_http_params_opts_default_get(php_http_params_opts_t *opts)
+{
+       if (!opts) {
+               opts = emalloc(sizeof(*opts));
+       }
+
+       memcpy(opts, &def_opts, sizeof(def_opts));
+
+       return opts;
+}
+
+typedef struct php_http_params_state {
+       php_http_params_token_t input;
+       php_http_params_token_t param;
+       php_http_params_token_t arg;
+       php_http_params_token_t val;
+       struct {
+               zval **param;
+               zval **args;
+               zval **val;
+       } current;
+       unsigned quotes:1;
+       unsigned escape:1;
+       unsigned rfc5987:1;
+} php_http_params_state_t;
+
+static inline void sanitize_escaped(zval *zv TSRMLS_DC)
+{
+       if (Z_STRVAL_P(zv)[0] == '"' && Z_STRVAL_P(zv)[Z_STRLEN_P(zv) - 1] == '"') {
+               size_t deq_len = Z_STRLEN_P(zv) - 2;
+               char *deq = estrndup(Z_STRVAL_P(zv) + 1, deq_len);
+
+               zval_dtor(zv);
+               ZVAL_STRINGL(zv, deq, deq_len, 0);
+       }
+
+       php_stripcslashes(Z_STRVAL_P(zv), &Z_STRLEN_P(zv));
+}
+
+static inline void quote_string(zval *zv, zend_bool force TSRMLS_DC)
+{
+       int len = Z_STRLEN_P(zv);
+
+       Z_STRVAL_P(zv) = php_addcslashes(Z_STRVAL_P(zv), Z_STRLEN_P(zv), &Z_STRLEN_P(zv), 1,
+                       ZEND_STRL("\0..\37\173\\\"") TSRMLS_CC);
+
+       if (force || len != Z_STRLEN_P(zv) || strpbrk(Z_STRVAL_P(zv), "()<>@,;:\"[]?={} ")) {
+               zval tmp = *zv;
+               int len = Z_STRLEN_P(zv) + 2;
+               char *str = emalloc(len + 1);
+
+               str[0] = '"';
+               memcpy(&str[1], Z_STRVAL_P(zv), Z_STRLEN_P(zv));
+               str[len-1] = '"';
+               str[len] = '\0';
+
+               zval_dtor(&tmp);
+               ZVAL_STRINGL(zv, str, len, 0);
+       }
+}
+
+static inline void prepare_escaped(zval *zv TSRMLS_DC)
+{
+       if (Z_TYPE_P(zv) == IS_STRING) {
+               quote_string(zv, 0 TSRMLS_CC);
+       } else {
+               zval_dtor(zv);
+               ZVAL_EMPTY_STRING(zv);
+       }
+}
+
+static inline void sanitize_urlencoded(zval *zv TSRMLS_DC)
+{
+       Z_STRLEN_P(zv) = php_raw_url_decode(Z_STRVAL_P(zv), Z_STRLEN_P(zv));
+}
+
+static inline void prepare_urlencoded(zval *zv TSRMLS_DC)
+{
+       int len;
+       char *str =     php_raw_url_encode(Z_STRVAL_P(zv), Z_STRLEN_P(zv), &len);
+
+       zval_dtor(zv);
+       ZVAL_STRINGL(zv, str, len, 0);
+}
+
+static void sanitize_dimension(zval *zv TSRMLS_DC)
+{
+       zval *arr = NULL, *tmp = NULL, **cur = NULL;
+       char *var = NULL, *ptr = Z_STRVAL_P(zv), *end = Z_STRVAL_P(zv) + Z_STRLEN_P(zv);
+       long level = 0;
+
+       MAKE_STD_ZVAL(arr);
+       array_init(arr);
+       cur = &arr;
+
+       while (ptr < end) {
+               if (!var) {
+                       var = ptr;
+               }
+
+               switch (*ptr) {
+                       case '[':
+                               if (++level > PG(max_input_nesting_level)) {
+                                       zval_ptr_dtor(&arr);
+                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Max input nesting level of %ld exceeded", (long) PG(max_input_nesting_level));
+                                       return;
+                               }
+                               if (ptr - var == 0) {
+                                       ++var;
+                                       break;
+                               }
+                               /* no break */
+
+                       case ']':
+
+                               MAKE_STD_ZVAL(tmp);
+                               ZVAL_NULL(tmp);
+                               convert_to_array(*cur);
+
+                               if (ptr - var) {
+                                       char chr = *ptr;
+                                       *ptr = '\0';
+                                       zend_symtable_update(Z_ARRVAL_PP(cur), var, ptr - var + 1, (void *) &tmp, sizeof(zval *), (void *) &cur);
+                                       *ptr = chr;
+                               } else {
+                                       zend_hash_next_index_insert(Z_ARRVAL_PP(cur), (void *) &tmp, sizeof(zval *), (void *) &cur);
+                               }
+
+                               var = NULL;
+                               break;
+               }
+
+               ++ptr;
+       }
+
+       if (zend_hash_num_elements(Z_ARRVAL_P(arr))) {
+               zval_dtor(zv);
+#if PHP_VERSION_ID >= 50400
+               ZVAL_COPY_VALUE(zv, arr);
+#else
+               zv->value = arr->value;
+               Z_TYPE_P(zv) = Z_TYPE_P(arr);
+#endif
+               FREE_ZVAL(arr);
+       } else {
+               zval_ptr_dtor(&arr);
+       }
+}
+
+static inline void shift_key(php_http_buffer_t *buf, char *key_str, size_t key_len, const char *ass, size_t asl, unsigned flags TSRMLS_DC);
+static inline void shift_val(php_http_buffer_t *buf, zval *zvalue, const char *vss, size_t vsl, unsigned flags TSRMLS_DC);
+
+static void prepare_dimension(php_http_buffer_t *buf, php_http_buffer_t *keybuf, zval *zvalue, const char *pss, size_t psl, const char *vss, size_t vsl, unsigned flags TSRMLS_DC)
+{
+       HashTable *ht = HASH_OF(zvalue);
+       HashPosition pos;
+       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+       zval **val;
+       php_http_buffer_t prefix;
+
+       if (!ht->nApplyCount++) {
+               php_http_buffer_init(&prefix);
+               php_http_buffer_append(&prefix, keybuf->data, keybuf->used);
+
+               FOREACH_HASH_KEYVAL(pos, ht, key, val) {
+                       if (key.type == HASH_KEY_IS_STRING && !*key.str) {
+                               /* only public properties */
+                               continue;
+                       }
+
+                       php_http_buffer_appends(&prefix, "[");
+                       if (key.type == HASH_KEY_IS_STRING) {
+                               php_http_buffer_append(&prefix, key.str, key.len - 1);
+                       } else {
+                               php_http_buffer_appendf(&prefix, "%lu", key.num);
+                       }
+                       php_http_buffer_appends(&prefix, "]");
+
+                       if (Z_TYPE_PP(val) == IS_ARRAY || Z_TYPE_PP(val) == IS_OBJECT) {
+                               prepare_dimension(buf, &prefix, *val, pss, psl, vss, vsl, flags TSRMLS_CC);
+                       } else {
+                               zval *cpy = php_http_ztyp(IS_STRING, *val);
+
+                               shift_key(buf, prefix.data, prefix.used, pss, psl, flags TSRMLS_CC);
+                               shift_val(buf, cpy, vss, vsl, flags TSRMLS_CC);
+                               zval_ptr_dtor(&cpy);
+                       }
+
+                       php_http_buffer_cut(&prefix, keybuf->used, prefix.used - keybuf->used);
+               }
+               php_http_buffer_dtor(&prefix);
+       }
+       --ht->nApplyCount;
+}
+
+static inline void sanitize_key(unsigned flags, char *str, size_t len, zval *zv, zend_bool *rfc5987 TSRMLS_DC)
+{
+       char *eos;
+
+       zval_dtor(zv);
+       php_trim(str, len, NULL, 0, zv, 3 TSRMLS_CC);
+
+       if (flags & PHP_HTTP_PARAMS_ESCAPED) {
+               sanitize_escaped(zv TSRMLS_CC);
+       }
+       
+       if (!Z_STRLEN_P(zv)) {
+               return;
+       }
+
+       eos = &Z_STRVAL_P(zv)[Z_STRLEN_P(zv)-1];
+       if (*eos == '*') {
+               *eos = '\0';
+               *rfc5987 = 1;
+               Z_STRLEN_P(zv) -= 1;
+       }
+
+       if (flags & PHP_HTTP_PARAMS_URLENCODED) {
+               sanitize_urlencoded(zv TSRMLS_CC);
+       }
+
+       if (flags & PHP_HTTP_PARAMS_DIMENSION) {
+               sanitize_dimension(zv TSRMLS_CC);
+       }
+}
+
+static inline void sanitize_rfc5987(zval *zv, char **language, zend_bool *latin1 TSRMLS_DC)
+{
+       char *ptr;
+
+       /* examples:
+        * iso-8850-1'de'bl%f6der%20schei%df%21
+        * utf-8'de-DE'bl%c3%b6der%20schei%c3%9f%21
+        */
+
+       switch (Z_STRVAL_P(zv)[0]) {
+       case 'I':
+       case 'i':
+               if (!strncasecmp(Z_STRVAL_P(zv), "iso-8859-1", lenof("iso-8859-1"))) {
+                       *latin1 = 1;
+                       ptr = Z_STRVAL_P(zv) + lenof("iso-8859-1");
+                       break;
+               }
+               /* no break */
+       case 'U':
+       case 'u':
+               if (!strncasecmp(Z_STRVAL_P(zv), "utf-8", lenof("utf-8"))) {
+                       *latin1 = 0;
+                       ptr = Z_STRVAL_P(zv) + lenof("utf-8");
+                       break;
+               }
+               /* no break */
+       default:
+               return;
+       }
+
+       /* extract language */
+       if (*ptr == '\'') {
+               for (*language = ++ptr; *ptr && *ptr != '\''; ++ptr);
+               if (!*ptr) {
+                       *language = NULL;
+                       return;
+               }
+               *language = estrndup(*language, ptr - *language);
+
+               /* remainder */
+               ptr = estrdup(++ptr);
+               zval_dtor(zv);
+               ZVAL_STRING(zv, ptr, 0);
+       }
+}
+
+static inline void sanitize_rfc5988(char *str, size_t len, zval *zv TSRMLS_DC)
+{
+       zval_dtor(zv);
+       php_trim(str, len, " ><", 3, zv, 3 TSRMLS_CC);
+}
+
+static inline void prepare_rfc5988(zval *zv TSRMLS_DC)
+{
+       if (Z_TYPE_P(zv) != IS_STRING) {
+               zval_dtor(zv);
+               ZVAL_EMPTY_STRING(zv);
+       }
+}
+
+static void utf8encode(zval *zv)
+{
+       size_t pos, len = 0;
+       unsigned char *ptr = (unsigned char *) Z_STRVAL_P(zv);
+
+       while (*ptr) {
+               if (*ptr++ >= 0x80) {
+                       ++len;
+               }
+               ++len;
+       }
+
+       ptr = safe_emalloc(1, len, 1);
+       for (len = 0, pos = 0; len <= Z_STRLEN_P(zv); ++len, ++pos) {
+               ptr[pos] = Z_STRVAL_P(zv)[len];
+               if ((ptr[pos]) >= 0x80) {
+                       ptr[pos + 1] = 0x80 | (ptr[pos] & 0x3f);
+                       ptr[pos] = 0xc0 | ((ptr[pos] >> 6) & 0x1f);
+                       ++pos;
+               }
+       }
+       zval_dtor(zv);
+       ZVAL_STRINGL(zv, (char *) ptr, pos-1, 0);
+}
+
+static inline void sanitize_value(unsigned flags, char *str, size_t len, zval *zv, zend_bool rfc5987 TSRMLS_DC)
+{
+       char *language = NULL;
+       zend_bool latin1 = 0;
+
+       zval_dtor(zv);
+       php_trim(str, len, NULL, 0, zv, 3 TSRMLS_CC);
+
+       if (rfc5987) {
+               sanitize_rfc5987(zv, &language, &latin1 TSRMLS_CC);
+       }
+
+       if (flags & PHP_HTTP_PARAMS_ESCAPED) {
+               sanitize_escaped(zv TSRMLS_CC);
+       }
+
+       if ((flags & PHP_HTTP_PARAMS_URLENCODED) || (rfc5987 && language)) {
+               sanitize_urlencoded(zv TSRMLS_CC);
+       }
+
+       if (rfc5987 && language) {
+               zval *tmp;
+
+               if (latin1) {
+                       utf8encode(zv);
+               }
+
+               MAKE_STD_ZVAL(tmp);
+               ZVAL_COPY_VALUE(tmp, zv);
+               array_init(zv);
+               add_assoc_zval(zv, language, tmp);
+               PTR_FREE(language);
+       }
+}
+
+static inline void prepare_key(unsigned flags, char *old_key, size_t old_len, char **new_key, size_t *new_len TSRMLS_DC)
+{
+       zval zv;
+
+       INIT_PZVAL(&zv);
+       ZVAL_STRINGL(&zv, old_key, old_len, 1);
+
+       if (flags & PHP_HTTP_PARAMS_URLENCODED) {
+               prepare_urlencoded(&zv TSRMLS_CC);
+       }
+
+       if (flags & PHP_HTTP_PARAMS_ESCAPED) {
+               if (flags & PHP_HTTP_PARAMS_RFC5988) {
+                       prepare_rfc5988(&zv TSRMLS_CC);
+               } else {
+                       prepare_escaped(&zv TSRMLS_CC);
+               }
+       }
+
+       *new_key = Z_STRVAL(zv);
+       *new_len = Z_STRLEN(zv);
+}
+
+static inline void prepare_value(unsigned flags, zval *zv TSRMLS_DC)
+{
+       if (flags & PHP_HTTP_PARAMS_URLENCODED) {
+               prepare_urlencoded(zv TSRMLS_CC);
+       }
+
+       if (flags & PHP_HTTP_PARAMS_ESCAPED) {
+               prepare_escaped(zv TSRMLS_CC);
+       }
+}
+
+static void merge_param(HashTable *params, zval *zdata, zval ***current_param, zval ***current_args TSRMLS_DC)
+{
+       zval **ptr, **zdata_ptr;
+       php_http_array_hashkey_t hkey = php_http_array_hashkey_init(0);
+
+#if 0
+       {
+               zval tmp;
+               INIT_PZVAL_ARRAY(&tmp, params);
+               fprintf(stderr, "params = ");
+               zend_print_zval_r(&tmp, 1 TSRMLS_CC);
+               fprintf(stderr, "\n");
+       }
+#endif
+
+       hkey.type = zend_hash_get_current_key_ex(Z_ARRVAL_P(zdata), &hkey.str, &hkey.len, &hkey.num, hkey.dup, NULL);
+
+       if ((hkey.type == HASH_KEY_IS_STRING && !zend_hash_exists(params, hkey.str, hkey.len))
+       ||      (hkey.type == HASH_KEY_IS_LONG && !zend_hash_index_exists(params, hkey.num))
+       ) {
+               zval *tmp, *arg, **args;
+
+               /* create the entry if it doesn't exist */
+               zend_hash_get_current_data(Z_ARRVAL_P(zdata), (void *) &ptr);
+               Z_ADDREF_PP(ptr);
+               MAKE_STD_ZVAL(tmp);
+               array_init(tmp);
+               add_assoc_zval_ex(tmp, ZEND_STRS("value"), *ptr);
+
+               MAKE_STD_ZVAL(arg);
+               array_init(arg);
+               zend_hash_update(Z_ARRVAL_P(tmp), "arguments", sizeof("arguments"), (void *) &arg, sizeof(zval *), (void *) &args);
+               *current_args = args;
+
+               if (hkey.type == HASH_KEY_IS_STRING) {
+                       zend_hash_update(params, hkey.str, hkey.len, (void *) &tmp, sizeof(zval *), (void *) &ptr);
+               } else {
+                       zend_hash_index_update(params, hkey.num, (void *) &tmp, sizeof(zval *), (void *) &ptr);
+               }
+       } else {
+               /* merge */
+               if (hkey.type == HASH_KEY_IS_STRING) {
+                       zend_hash_find(params, hkey.str, hkey.len, (void *) &ptr);
+               } else {
+                       zend_hash_index_find(params, hkey.num, (void *) &ptr);
+               }
+
+               zdata_ptr = &zdata;
+
+               if (Z_TYPE_PP(ptr) == IS_ARRAY
+               &&      SUCCESS == zend_hash_find(Z_ARRVAL_PP(ptr), "value", sizeof("value"), (void *) &ptr)
+               &&      SUCCESS == zend_hash_get_current_data(Z_ARRVAL_PP(zdata_ptr), (void *) &zdata_ptr)
+               ) {
+                       /*
+                        * params = [arr => [value => [0 => 1]]]
+                        *                            ^- ptr
+                        * zdata  = [arr => [0 => NULL]]
+                        *                  ^- zdata_ptr
+                        */
+                       zval **test_ptr;
+
+                       while (Z_TYPE_PP(zdata_ptr) == IS_ARRAY
+                       &&      SUCCESS == zend_hash_get_current_data(Z_ARRVAL_PP(zdata_ptr), (void *) &test_ptr)
+                       ) {
+                               if (Z_TYPE_PP(test_ptr) == IS_ARRAY) {
+
+                                       /* now find key in ptr */
+                                       if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(Z_ARRVAL_PP(zdata_ptr), &hkey.str, &hkey.len, &hkey.num, hkey.dup, NULL)) {
+                                               if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(ptr), hkey.str, hkey.len, (void *) &ptr)) {
+                                                       zdata_ptr = test_ptr;
+                                               } else {
+                                                       Z_ADDREF_PP(test_ptr);
+                                                       zend_hash_update(Z_ARRVAL_PP(ptr), hkey.str, hkey.len, (void *) test_ptr, sizeof(zval *), (void *) &ptr);
+                                                       break;
+                                               }
+                                       } else {
+                                               if (SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(ptr), hkey.num, (void *) &ptr)) {
+                                                       zdata_ptr = test_ptr;
+                                               } else if (hkey.num) {
+                                                       Z_ADDREF_PP(test_ptr);
+                                                       zend_hash_index_update(Z_ARRVAL_PP(ptr), hkey.num, (void *) test_ptr, sizeof(zval *), (void *) &ptr);
+                                                       break;
+                                               } else {
+                                                       Z_ADDREF_PP(test_ptr);
+                                                       zend_hash_next_index_insert(Z_ARRVAL_PP(ptr), (void *) test_ptr, sizeof(zval *), (void *) &ptr);
+                                                       break;
+                                               }
+                                       }
+                               } else {
+                                       /* this is the leaf */
+                                       Z_ADDREF_PP(test_ptr);
+                                       if (Z_TYPE_PP(ptr) != IS_ARRAY) {
+                                               zval_dtor(*ptr);
+                                               array_init(*ptr);
+                                       }
+                                       if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(Z_ARRVAL_PP(zdata_ptr), &hkey.str, &hkey.len, &hkey.num, hkey.dup, NULL)) {
+                                               zend_hash_update(Z_ARRVAL_PP(ptr), hkey.str, hkey.len, (void *) test_ptr, sizeof(zval *), (void *) &ptr);
+                                       } else if (hkey.num) {
+                                               zend_hash_index_update(Z_ARRVAL_PP(ptr), hkey.num, (void *) test_ptr, sizeof(zval *), (void *) &ptr);
+                                       } else {
+                                               zend_hash_next_index_insert(Z_ARRVAL_PP(ptr), (void *) test_ptr, sizeof(zval *), (void *) &ptr);
+                                       }
+                                       break;
+                               }
+                       }
+
+               }
+       }
+
+       /* bubble up */
+       while (Z_TYPE_PP(ptr) == IS_ARRAY && SUCCESS == zend_hash_get_current_data(Z_ARRVAL_PP(ptr), (void *) &ptr));
+       *current_param = ptr;
+}
+
+static void push_param(HashTable *params, php_http_params_state_t *state, const php_http_params_opts_t *opts TSRMLS_DC)
+{
+       if (state->val.str) {
+               if (0 < (state->val.len = state->input.str - state->val.str)) {
+                       sanitize_value(opts->flags, state->val.str, state->val.len, *(state->current.val), state->rfc5987 TSRMLS_CC);
+               }
+               state->rfc5987 = 0;
+       } else if (state->arg.str) {
+               if (0 < (state->arg.len = state->input.str - state->arg.str)) {
+                       zval *val, key;
+                       zend_bool rfc5987 = 0;
+
+                       INIT_PZVAL(&key);
+                       ZVAL_NULL(&key);
+                       sanitize_key(opts->flags, state->arg.str, state->arg.len, &key, &rfc5987 TSRMLS_CC);
+                       state->rfc5987 = rfc5987;
+                       if (Z_TYPE(key) == IS_STRING && Z_STRLEN(key)) {
+                               MAKE_STD_ZVAL(val);
+                               ZVAL_TRUE(val);
+
+                               if (rfc5987) {
+                                       zval **rfc;
+
+                                       if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(state->current.args), ZEND_STRS("*rfc5987*"), (void *) &rfc)) {
+                                               zend_symtable_update(Z_ARRVAL_PP(rfc), Z_STRVAL(key), Z_STRLEN(key) + 1, (void *) &val, sizeof(zval *), (void *) &state->current.val);
+                                       } else {
+                                               zval *tmp;
+
+                                               MAKE_STD_ZVAL(tmp);
+                                               array_init_size(tmp, 1);
+                                               zend_symtable_update(Z_ARRVAL_P(tmp), Z_STRVAL(key), Z_STRLEN(key) + 1, (void *) &val, sizeof(zval *), (void *) &state->current.val);
+                                               zend_symtable_update(Z_ARRVAL_PP(state->current.args), ZEND_STRS("*rfc5987*"), (void *) &tmp, sizeof(zval *), NULL);
+                                       }
+                               } else {
+                                       zend_symtable_update(Z_ARRVAL_PP(state->current.args), Z_STRVAL(key), Z_STRLEN(key) + 1, (void *) &val, sizeof(zval *), (void *) &state->current.val);
+                               }
+                       }
+                       zval_dtor(&key);
+               }
+       } else if (state->param.str) {
+               if (0 < (state->param.len = state->input.str - state->param.str)) {
+                       zval *prm, *arg, *val, *key;
+                       zend_bool rfc5987 = 0;
+
+                       MAKE_STD_ZVAL(key);
+                       ZVAL_NULL(key);
+                       if (opts->flags & PHP_HTTP_PARAMS_RFC5988) {
+                               sanitize_rfc5988(state->param.str, state->param.len, key TSRMLS_CC);
+                       } else {
+                               sanitize_key(opts->flags, state->param.str, state->param.len, key, &rfc5987 TSRMLS_CC);
+                               state->rfc5987 = rfc5987;
+                       }
+                       if (Z_TYPE_P(key) != IS_STRING) {
+                               merge_param(params, key, &state->current.val, &state->current.args TSRMLS_CC);
+                       } else if (Z_STRLEN_P(key)) {
+                               MAKE_STD_ZVAL(prm);
+                               array_init_size(prm, 2);
+
+                               MAKE_STD_ZVAL(val);
+                               if (opts->defval) {
+                                       ZVAL_COPY_VALUE(val, opts->defval);
+                                       zval_copy_ctor(val);
+                               } else {
+                                       ZVAL_TRUE(val);
+                               }
+                               if (rfc5987 && (opts->flags & PHP_HTTP_PARAMS_RFC5987)) {
+                                       zend_hash_update(Z_ARRVAL_P(prm), "*rfc5987*", sizeof("*rfc5987*"), (void *) &val, sizeof(zval *), (void *) &state->current.val);
+                               } else {
+                                       zend_hash_update(Z_ARRVAL_P(prm), "value", sizeof("value"), (void *) &val, sizeof(zval *), (void *) &state->current.val);
+                               }
+
+                               MAKE_STD_ZVAL(arg);
+                               array_init_size(arg, 3);
+                               zend_hash_update(Z_ARRVAL_P(prm), "arguments", sizeof("arguments"), (void *) &arg, sizeof(zval *), (void *) &state->current.args);
+
+                               zend_symtable_update(params, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, (void *) &prm, sizeof(zval *), (void *) &state->current.param);
+                       }
+                       zval_ptr_dtor(&key);
+               }
+       }
+}
+
+static inline zend_bool check_str(const char *chk_str, size_t chk_len, const char *sep_str, size_t sep_len) {
+       return 0 < sep_len && chk_len >= sep_len && *chk_str == *sep_str && !memcmp(chk_str + 1, sep_str + 1, sep_len - 1);
+}
+
+static size_t check_sep(php_http_params_state_t *state, php_http_params_token_t **separators)
+{
+       php_http_params_token_t **sep = separators;
+
+       if (state->quotes || state->escape) {
+               return 0;
+       }
+       
+       if (sep) while (*sep) {
+               if (check_str(state->input.str, state->input.len, (*sep)->str, (*sep)->len)) {
+                       return (*sep)->len;
+               }
+               ++sep;
+       }
+       return 0;
+}
+
+static void skip_sep(size_t skip, php_http_params_state_t *state, php_http_params_token_t **param, php_http_params_token_t **arg, php_http_params_token_t **val TSRMLS_DC)
+{
+       size_t sep_len;
+
+       state->input.str += skip;
+       state->input.len -= skip;
+
+       while ( (param && (sep_len = check_sep(state, param)))
+       ||              (arg && (sep_len = check_sep(state, arg)))
+       ||              (val && (sep_len = check_sep(state, val)))
+       ) {
+               state->input.str += sep_len;
+               state->input.len -= sep_len;
+       }
+}
+
+HashTable *php_http_params_parse(HashTable *params, const php_http_params_opts_t *opts TSRMLS_DC)
+{
+       php_http_params_state_t state = {{NULL,0}, {NULL,0}, {NULL,0}, {NULL,0}, {NULL,NULL,NULL}, 0, 0};
+
+       state.input.str = opts->input.str;
+       state.input.len = opts->input.len;
+
+       if (!params) {
+               ALLOC_HASHTABLE(params);
+               ZEND_INIT_SYMTABLE(params);
+       }
+
+       while (state.input.len) {
+               if ((opts->flags & PHP_HTTP_PARAMS_RFC5988) && !state.arg.str) {
+                       if (*state.input.str == '<') {
+                               state.quotes = 1;
+                       } else if (*state.input.str == '>') {
+                               state.quotes = 0;
+                       }
+               } else if (*state.input.str == '"' && !state.escape) {
+                       state.quotes = !state.quotes;
+               } else {
+                       state.escape = (*state.input.str == '\\');
+               }
+               
+               if (!state.param.str) {
+                       /* initialize */
+                       skip_sep(0, &state, opts->param, opts->arg, opts->val TSRMLS_CC);
+                       state.param.str = state.input.str;
+               } else {
+                       size_t sep_len;
+                       /* are we at a param separator? */
+                       if (0 < (sep_len = check_sep(&state, opts->param))) {
+                               push_param(params, &state, opts TSRMLS_CC);
+
+                               skip_sep(sep_len, &state, opts->param, opts->arg, opts->val TSRMLS_CC);
+
+                               /* start off with a new param */
+                               state.param.str = state.input.str;
+                               state.param.len = 0;
+                               state.arg.str = NULL;
+                               state.arg.len = 0;
+                               state.val.str = NULL;
+                               state.val.len = 0;
+
+                               continue;
+
+                       } else
+                       /* are we at an arg separator? */
+                       if (0 < (sep_len = check_sep(&state, opts->arg))) {
+                               push_param(params, &state, opts TSRMLS_CC);
+
+                               skip_sep(sep_len, &state, NULL, opts->arg, opts->val TSRMLS_CC);
+
+                               /* continue with a new arg */
+                               state.arg.str = state.input.str;
+                               state.arg.len = 0;
+                               state.val.str = NULL;
+                               state.val.len = 0;
+
+                               continue;
+
+                       } else
+                       /* are we at a val separator? */
+                       if (0 < (sep_len = check_sep(&state, opts->val))) {
+                               /* only handle separator if we're not already reading in a val */
+                               if (!state.val.str) {
+                                       push_param(params, &state, opts TSRMLS_CC);
+
+                                       skip_sep(sep_len, &state, NULL, NULL, opts->val TSRMLS_CC);
+
+                                       state.val.str = state.input.str;
+                                       state.val.len = 0;
+
+                                       continue;
+                               }
+                       }
+               }
+               
+               if (state.input.len) {
+                       ++state.input.str;
+                       --state.input.len;
+               }
+       }
+       /* finalize */
+       push_param(params, &state, opts TSRMLS_CC);
+
+       return params;
+}
+
+static inline void shift_key(php_http_buffer_t *buf, char *key_str, size_t key_len, const char *ass, size_t asl, unsigned flags TSRMLS_DC)
+{
+       char *str;
+       size_t len;
+
+       if (buf->used) {
+               php_http_buffer_append(buf, ass, asl);
+       }
+
+       prepare_key(flags, key_str, key_len, &str, &len TSRMLS_CC);
+       php_http_buffer_append(buf, str, len);
+       efree(str);
+}
+
+static inline void shift_rfc5987(php_http_buffer_t *buf, zval *zvalue, const char *vss, size_t vsl, unsigned flags TSRMLS_DC)
+{
+       HashTable *ht = HASH_OF(zvalue);
+       zval **zdata, *tmp;
+       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+
+       if (SUCCESS == zend_hash_get_current_data(ht, (void *) &zdata)
+       &&      HASH_KEY_NON_EXISTENT != (key.type = zend_hash_get_current_key_ex(ht, &key.str, &key.len, &key.num, key.dup, NULL))
+       ) {
+               php_http_array_hashkey_stringify(&key);
+               php_http_buffer_appendf(buf, "*%.*sutf-8'%.*s'",
+                               (int) (vsl > INT_MAX ? INT_MAX : vsl), vss,
+                               (int) (key.len > INT_MAX ? INT_MAX : key.len), key.str);
+               php_http_array_hashkey_stringfree(&key);
+
+               tmp = php_http_zsep(1, IS_STRING, *zdata);
+               prepare_value(flags | PHP_HTTP_PARAMS_URLENCODED, tmp TSRMLS_CC);
+               php_http_buffer_append(buf, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
+               zval_ptr_dtor(&tmp);
+       }
+}
+
+static inline void shift_rfc5988(php_http_buffer_t *buf, char *key_str, size_t key_len, const char *ass, size_t asl, unsigned flags TSRMLS_DC)
+{
+       char *str;
+       size_t len;
+
+       if (buf->used) {
+               php_http_buffer_append(buf, ass, asl);
+       }
+
+       prepare_key(flags, key_str, key_len, &str, &len TSRMLS_CC);
+       php_http_buffer_appends(buf, "<");
+       php_http_buffer_append(buf, str, len);
+       php_http_buffer_appends(buf, ">");
+       efree(str);
+}
+
+static inline void shift_rfc5988_val(php_http_buffer_t *buf, zval *zv, const char *vss, size_t vsl, unsigned flags TSRMLS_DC)
+{
+       zval *tmp = php_http_zsep(1, IS_STRING, zv);
+
+       quote_string(tmp, 1 TSRMLS_CC);
+       php_http_buffer_append(buf, vss, vsl);
+       php_http_buffer_append(buf, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
+
+       zval_ptr_dtor(&tmp);
+}
+
+static inline void shift_val(php_http_buffer_t *buf, zval *zvalue, const char *vss, size_t vsl, unsigned flags TSRMLS_DC)
+{
+       if (Z_TYPE_P(zvalue) != IS_BOOL) {
+               zval *tmp = php_http_zsep(1, IS_STRING, zvalue);
+
+               prepare_value(flags, tmp TSRMLS_CC);
+               php_http_buffer_append(buf, vss, vsl);
+               php_http_buffer_append(buf, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
+
+               zval_ptr_dtor(&tmp);
+       } else if (!Z_BVAL_P(zvalue)) {
+               php_http_buffer_append(buf, vss, vsl);
+               php_http_buffer_appends(buf, "0");
+       }
+}
+
+static void shift_arg(php_http_buffer_t *buf, char *key_str, size_t key_len, zval *zvalue, const char *ass, size_t asl, const char *vss, size_t vsl, unsigned flags TSRMLS_DC)
+{
+       if (Z_TYPE_P(zvalue) == IS_ARRAY || Z_TYPE_P(zvalue) == IS_OBJECT) {
+               HashPosition pos;
+               php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+               zval **val;
+               zend_bool rfc5987 = !strcmp(key_str, "*rfc5987*");
+
+               if (!rfc5987) {
+                       shift_key(buf, key_str, key_len, ass, asl, flags TSRMLS_CC);
+               }
+               FOREACH_KEYVAL(pos, zvalue, key, val) {
+                       /* did you mean recursion? */
+                       php_http_array_hashkey_stringify(&key);
+                       if (rfc5987 && (Z_TYPE_PP(val) == IS_ARRAY || Z_TYPE_PP(val) == IS_OBJECT)) {
+                               shift_key(buf, key.str, key.len-1, ass, asl, flags TSRMLS_CC);
+                               shift_rfc5987(buf, *val, vss, vsl, flags TSRMLS_CC);
+                       } else {
+                               shift_arg(buf, key.str, key.len-1, *val, ass, asl, vss, vsl, flags TSRMLS_CC);
+                       }
+                       php_http_array_hashkey_stringfree(&key);
+               }
+       } else {
+               shift_key(buf, key_str, key_len, ass, asl, flags TSRMLS_CC);
+
+               if (flags & PHP_HTTP_PARAMS_RFC5988) {
+                       switch (key_len) {
+                       case lenof("rel"):
+                       case lenof("title"):
+                       case lenof("anchor"):
+                               /* some args must be quoted */
+                               if (0 <= php_http_select_str(key_str, 3, "rel", "title", "anchor")) {
+                                       shift_rfc5988_val(buf, zvalue, vss, vsl, flags TSRMLS_CC);
+                                       return;
+                               }
+                               break;
+                       }
+               }
+
+               shift_val(buf, zvalue, vss, vsl, flags TSRMLS_CC);
+       }
+}
+
+static void shift_param(php_http_buffer_t *buf, char *key_str, size_t key_len, zval *zvalue, const char *pss, size_t psl, const char *ass, size_t asl, const char *vss, size_t vsl, unsigned flags, zend_bool rfc5987 TSRMLS_DC)
+{
+       if (Z_TYPE_P(zvalue) == IS_ARRAY || Z_TYPE_P(zvalue) == IS_OBJECT) {
+               /* treat as arguments, unless we care for dimensions or rfc5987 */
+               if (flags & PHP_HTTP_PARAMS_DIMENSION) {
+                       php_http_buffer_t *keybuf = php_http_buffer_from_string(key_str, key_len);
+                       prepare_dimension(buf, keybuf, zvalue, pss, psl, vss, vsl, flags TSRMLS_CC);
+                       php_http_buffer_free(&keybuf);
+               } else if (rfc5987) {
+                       shift_key(buf, key_str, key_len, pss, psl, flags TSRMLS_CC);
+                       shift_rfc5987(buf, zvalue, vss, vsl, flags TSRMLS_CC);
+               } else {
+                       shift_arg(buf, key_str, key_len, zvalue, ass, asl, vss, vsl, flags TSRMLS_CC);
+               }
+       } else {
+               if (flags & PHP_HTTP_PARAMS_RFC5988) {
+                       shift_rfc5988(buf, key_str, key_len, pss, psl, flags TSRMLS_CC);
+               } else {
+                       shift_key(buf, key_str, key_len, pss, psl, flags TSRMLS_CC);
+               }
+               shift_val(buf, zvalue, vss, vsl, flags TSRMLS_CC);
+       }
+}
+
+php_http_buffer_t *php_http_params_to_string(php_http_buffer_t *buf, HashTable *params, const char *pss, size_t psl, const char *ass, size_t asl, const char *vss, size_t vsl, unsigned flags TSRMLS_DC)
+{
+       zval **zparam;
+       HashPosition pos, pos1;
+       php_http_array_hashkey_t key = php_http_array_hashkey_init(0), key1 = php_http_array_hashkey_init(0);
+       zend_bool rfc5987 = 0;
+
+       if (!buf) {
+               buf = php_http_buffer_init(NULL);
+       }
+
+       FOREACH_HASH_KEYVAL(pos, params, key, zparam) {
+               zval **zvalue, **zargs;
+
+               if (Z_TYPE_PP(zparam) != IS_ARRAY) {
+                       zvalue = zparam;
+               } else {
+                       if (SUCCESS != zend_hash_find(Z_ARRVAL_PP(zparam), ZEND_STRS("value"), (void *) &zvalue)) {
+                               if (SUCCESS != zend_hash_find(Z_ARRVAL_PP(zparam), ZEND_STRS("*rfc5987*"), (void *) &zvalue)) {
+                                       zvalue = zparam;
+                               } else {
+                                       rfc5987 = 1;
+                               }
+                       }
+               }
+
+               php_http_array_hashkey_stringify(&key);
+               shift_param(buf, key.str, key.len - 1, *zvalue, pss, psl, ass, asl, vss, vsl, flags, rfc5987 TSRMLS_CC);
+               php_http_array_hashkey_stringfree(&key);
+
+               if (Z_TYPE_PP(zparam) == IS_ARRAY && SUCCESS != zend_hash_find(Z_ARRVAL_PP(zparam), ZEND_STRS("arguments"), (void *) &zvalue)) {
+                       if (zvalue == zparam) {
+                               continue;
+                       }
+                       zvalue = zparam;
+               }
+
+               if (Z_TYPE_PP(zvalue) == IS_ARRAY) {
+                       FOREACH_KEYVAL(pos1, *zvalue, key1, zargs) {
+                               if (zvalue == zparam && key1.type == HASH_KEY_IS_STRING && !strcmp(key1.str, "value")) {
+                                       continue;
+                               }
+
+                               php_http_array_hashkey_stringify(&key1);
+                               shift_arg(buf, key1.str, key1.len - 1, *zargs, ass, asl, vss, vsl, flags TSRMLS_CC);
+                               php_http_array_hashkey_stringfree(&key1);
+                       }
+               }
+       }
+
+       php_http_buffer_shrink(buf);
+       php_http_buffer_fix(buf);
+
+       return buf;
+}
+
+php_http_params_token_t **php_http_params_separator_init(zval *zv TSRMLS_DC)
+{
+       zval **sep;
+       HashPosition pos;
+       php_http_params_token_t **ret, **tmp;
+
+       if (!zv) {
+               return NULL;
+       }
+
+       zv = php_http_ztyp(IS_ARRAY, zv);
+       ret = ecalloc(zend_hash_num_elements(Z_ARRVAL_P(zv)) + 1, sizeof(*ret));
+
+       tmp = ret;
+       FOREACH_VAL(pos, zv, sep) {
+               zval *zt = php_http_ztyp(IS_STRING, *sep);
+
+               if (Z_STRLEN_P(zt)) {
+                       *tmp = emalloc(sizeof(**tmp));
+                       (*tmp)->str = estrndup(Z_STRVAL_P(zt), (*tmp)->len = Z_STRLEN_P(zt));
+                       ++tmp;
+               }
+               zval_ptr_dtor(&zt);
+       }
+       zval_ptr_dtor(&zv);
+
+       *tmp = NULL;
+       return ret;
+}
+
+void php_http_params_separator_free(php_http_params_token_t **separator)
+{
+       php_http_params_token_t **sep = separator;
+       if (sep) {
+               while (*sep) {
+                       PTR_FREE((*sep)->str);
+                       efree(*sep);
+                       ++sep;
+               }
+               efree(separator);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams___construct, 0, 0, 0)
+       ZEND_ARG_INFO(0, params)
+       ZEND_ARG_INFO(0, param_sep)
+       ZEND_ARG_INFO(0, arg_sep)
+       ZEND_ARG_INFO(0, val_sep)
+       ZEND_ARG_INFO(0, flags)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpParams, __construct)
+{
+       zval *zcopy, *zparams = NULL, *param_sep = NULL, *arg_sep = NULL, *val_sep = NULL;
+       long flags = PHP_HTTP_PARAMS_DEFAULT;
+       zend_error_handling zeh;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!/z/z/z/l", &zparams, &param_sep, &arg_sep, &val_sep, &flags), invalid_arg, return);
+
+       zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh TSRMLS_CC);
+       {
+               switch (ZEND_NUM_ARGS()) {
+                       case 5:
+                               zend_update_property_long(php_http_params_class_entry, getThis(), ZEND_STRL("flags"), flags TSRMLS_CC);
+                               /* no break */
+                       case 4:
+                               zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("val_sep"), val_sep TSRMLS_CC);
+                               /* no break */
+                       case 3:
+                               zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("arg_sep"), arg_sep TSRMLS_CC);
+                               /* no break */
+                       case 2:
+                               zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("param_sep"), param_sep TSRMLS_CC);
+                               /* no break */
+               }
+
+               if (zparams) {
+                       switch (Z_TYPE_P(zparams)) {
+                               case IS_OBJECT:
+                               case IS_ARRAY:
+                                       zcopy = php_http_zsep(1, IS_ARRAY, zparams);
+                                       zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), zcopy TSRMLS_CC);
+                                       zval_ptr_dtor(&zcopy);
+                                       break;
+                               default:
+                                       zcopy = php_http_ztyp(IS_STRING, zparams);
+                                       if (Z_STRLEN_P(zcopy)) {
+                                               php_http_params_opts_t opts = {
+                                                       {Z_STRVAL_P(zcopy), Z_STRLEN_P(zcopy)},
+                                                       php_http_params_separator_init(zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("param_sep"), 0 TSRMLS_CC) TSRMLS_CC),
+                                                       php_http_params_separator_init(zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("arg_sep"), 0 TSRMLS_CC) TSRMLS_CC),
+                                                       php_http_params_separator_init(zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("val_sep"), 0 TSRMLS_CC) TSRMLS_CC),
+                                                       NULL, flags
+                                               };
+
+                                               MAKE_STD_ZVAL(zparams);
+                                               array_init(zparams);
+                                               php_http_params_parse(Z_ARRVAL_P(zparams), &opts TSRMLS_CC);
+                                               zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), zparams TSRMLS_CC);
+                                               zval_ptr_dtor(&zparams);
+
+                                               php_http_params_separator_free(opts.param);
+                                               php_http_params_separator_free(opts.arg);
+                                               php_http_params_separator_free(opts.val);
+                                       }
+                                       zval_ptr_dtor(&zcopy);
+                                       break;
+                       }
+               } else {
+                       MAKE_STD_ZVAL(zparams);
+                       array_init(zparams);
+                       zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), zparams TSRMLS_CC);
+                       zval_ptr_dtor(&zparams);
+               }
+       }
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_toArray, 0, 0, 0)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpParams, toArray)
+{
+       zval *zparams;
+
+       if (SUCCESS != zend_parse_parameters_none()) {
+               return;
+       }
+       zparams = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC);
+       RETURN_ZVAL(zparams, 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_toString, 0, 0, 0)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpParams, toString)
+{
+       zval **tmp, *zparams, *zpsep, *zasep, *zvsep, *zflags;
+       php_http_buffer_t buf;
+
+       zparams = php_http_zsep(1, IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC));
+       zflags = php_http_ztyp(IS_LONG, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("flags"), 0 TSRMLS_CC));
+
+       zpsep = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("param_sep"), 0 TSRMLS_CC);
+       if (Z_TYPE_P(zpsep) == IS_ARRAY && SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(zpsep), (void *) &tmp)) {
+               zpsep = php_http_ztyp(IS_STRING, *tmp);
+       } else {
+               zpsep = php_http_ztyp(IS_STRING, zpsep);
+       }
+       zasep = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("arg_sep"), 0 TSRMLS_CC);
+       if (Z_TYPE_P(zasep) == IS_ARRAY && SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(zasep), (void *) &tmp)) {
+               zasep = php_http_ztyp(IS_STRING, *tmp);
+       } else {
+               zasep = php_http_ztyp(IS_STRING, zasep);
+       }
+       zvsep = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("val_sep"), 0 TSRMLS_CC);
+       if (Z_TYPE_P(zvsep) == IS_ARRAY && SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(zvsep), (void *) &tmp)) {
+               zvsep = php_http_ztyp(IS_STRING, *tmp);
+       } else {
+               zvsep = php_http_ztyp(IS_STRING, zvsep);
+       }
+
+       php_http_buffer_init(&buf);
+       php_http_params_to_string(&buf, Z_ARRVAL_P(zparams), Z_STRVAL_P(zpsep), Z_STRLEN_P(zpsep), Z_STRVAL_P(zasep), Z_STRLEN_P(zasep), Z_STRVAL_P(zvsep), Z_STRLEN_P(zvsep), Z_LVAL_P(zflags) TSRMLS_CC);
+
+       zval_ptr_dtor(&zparams);
+       zval_ptr_dtor(&zpsep);
+       zval_ptr_dtor(&zasep);
+       zval_ptr_dtor(&zvsep);
+       zval_ptr_dtor(&zflags);
+
+       RETVAL_PHP_HTTP_BUFFER_VAL(&buf);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_offsetExists, 0, 0, 1)
+       ZEND_ARG_INFO(0, name)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpParams, offsetExists)
+{
+       char *name_str;
+       int name_len;
+       zval **zparam, *zparams;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) {
+               return;
+       }
+
+       zparams = php_http_ztyp(IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC));
+
+       if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(zparams), name_str, name_len + 1, (void *) &zparam)) {
+               RETVAL_BOOL(Z_TYPE_PP(zparam) != IS_NULL);
+       } else {
+               RETVAL_FALSE;
+       }
+       zval_ptr_dtor(&zparams);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_offsetGet, 0, 0, 1)
+       ZEND_ARG_INFO(0, name)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpParams, offsetGet)
+{
+       char *name_str;
+       int name_len;
+       zval **zparam, *zparams;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) {
+               return;
+       }
+
+       zparams = php_http_ztyp(IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC));
+
+       if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(zparams), name_str, name_len + 1, (void *) &zparam)) {
+               RETVAL_ZVAL(*zparam, 1, 0);
+       }
+
+       zval_ptr_dtor(&zparams);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_offsetUnset, 0, 0, 1)
+       ZEND_ARG_INFO(0, name)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpParams, offsetUnset)
+{
+       char *name_str;
+       int name_len;
+       zval *zparams;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) {
+               return;
+       }
+
+       zparams = php_http_zsep(1, IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC));
+
+       zend_symtable_del(Z_ARRVAL_P(zparams), name_str, name_len + 1);
+       zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), zparams TSRMLS_CC);
+
+       zval_ptr_dtor(&zparams);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_offsetSet, 0, 0, 2)
+       ZEND_ARG_INFO(0, name)
+       ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpParams, offsetSet)
+{
+       zval *nvalue;
+       char *name_str;
+       int name_len;
+       zval **zparam, *zparams;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &name_str, &name_len, &nvalue)) {
+               return;
+       }
+
+       zparams = php_http_zsep(1, IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC));
+
+       if (name_len) {
+               if (Z_TYPE_P(nvalue) == IS_ARRAY) {
+                       zval *new_zparam;
+
+                       if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(zparams), name_str, name_len + 1, (void *) &zparam)) {
+                               new_zparam = php_http_zsep(1, IS_ARRAY, *zparam);
+                               array_join(Z_ARRVAL_P(nvalue), Z_ARRVAL_P(new_zparam), 0, 0);
+                       } else {
+                               new_zparam = nvalue;
+                               Z_ADDREF_P(new_zparam);
+                       }
+                       add_assoc_zval_ex(zparams, name_str, name_len + 1, new_zparam);
+               } else {
+                       zval *tmp;
+
+                       if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(zparams), name_str, name_len + 1, (void *) &zparam)) {
+                               tmp = php_http_zsep(1, IS_ARRAY, *zparam);
+                       } else {
+                               MAKE_STD_ZVAL(tmp);
+                               array_init(tmp);
+                       }
+
+                       Z_ADDREF_P(nvalue);
+                       add_assoc_zval_ex(tmp, ZEND_STRS("value"), nvalue);
+                       add_assoc_zval_ex(zparams, name_str, name_len + 1, tmp);
+               }
+       } else {
+               zval *tmp = php_http_ztyp(IS_STRING, nvalue), *arr;
+
+               MAKE_STD_ZVAL(arr);
+               array_init(arr);
+               add_assoc_bool_ex(arr, ZEND_STRS("value"), 1);
+               add_assoc_zval_ex(zparams, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp) + 1, arr);
+               zval_ptr_dtor(&tmp);
+       }
+
+       zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), zparams TSRMLS_CC);
+       zval_ptr_dtor(&zparams);
+}
+
+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, toArray,       ai_HttpParams_toArray,       ZEND_ACC_PUBLIC)
+       PHP_ME(HttpParams, toString,      ai_HttpParams_toString,      ZEND_ACC_PUBLIC)
+       ZEND_MALIAS(HttpParams, __toString, toString, ai_HttpParams_toString, ZEND_ACC_PUBLIC)
+
+       PHP_ME(HttpParams, offsetExists,  ai_HttpParams_offsetExists,  ZEND_ACC_PUBLIC)
+       PHP_ME(HttpParams, offsetUnset,   ai_HttpParams_offsetUnset,   ZEND_ACC_PUBLIC)
+       PHP_ME(HttpParams, offsetSet,     ai_HttpParams_offsetSet,     ZEND_ACC_PUBLIC)
+       PHP_ME(HttpParams, offsetGet,     ai_HttpParams_offsetGet,     ZEND_ACC_PUBLIC)
+
+       EMPTY_FUNCTION_ENTRY
+};
+
+zend_class_entry *php_http_params_class_entry;
+
+PHP_MINIT_FUNCTION(http_params)
+{
+       zend_class_entry ce = {0};
+
+       INIT_NS_CLASS_ENTRY(ce, "http", "Params", php_http_params_methods);
+       php_http_params_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
+       php_http_params_class_entry->create_object = php_http_params_object_new;
+       zend_class_implements(php_http_params_class_entry TSRMLS_CC, 1, zend_ce_arrayaccess);
+
+       zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("DEF_PARAM_SEP"), ZEND_STRL(",") TSRMLS_CC);
+       zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("DEF_ARG_SEP"), ZEND_STRL(";") TSRMLS_CC);
+       zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("DEF_VAL_SEP"), ZEND_STRL("=") TSRMLS_CC);
+       zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("COOKIE_PARAM_SEP"), ZEND_STRL("") TSRMLS_CC);
+
+       zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_RAW"), PHP_HTTP_PARAMS_RAW TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_ESCAPED"), PHP_HTTP_PARAMS_ESCAPED TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_URLENCODED"), PHP_HTTP_PARAMS_URLENCODED TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_DIMENSION"), PHP_HTTP_PARAMS_DIMENSION TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_RFC5987"), PHP_HTTP_PARAMS_RFC5987 TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_RFC5988"), PHP_HTTP_PARAMS_RFC5988 TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_DEFAULT"), PHP_HTTP_PARAMS_DEFAULT TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_QUERY"), PHP_HTTP_PARAMS_QUERY TSRMLS_CC);
+
+       zend_declare_property_null(php_http_params_class_entry, ZEND_STRL("params"), ZEND_ACC_PUBLIC TSRMLS_CC);
+       zend_declare_property_stringl(php_http_params_class_entry, ZEND_STRL("param_sep"), ZEND_STRL(","), ZEND_ACC_PUBLIC TSRMLS_CC);
+       zend_declare_property_stringl(php_http_params_class_entry, ZEND_STRL("arg_sep"), ZEND_STRL(";"), ZEND_ACC_PUBLIC TSRMLS_CC);
+       zend_declare_property_stringl(php_http_params_class_entry, ZEND_STRL("val_sep"), ZEND_STRL("="), ZEND_ACC_PUBLIC TSRMLS_CC);
+       zend_declare_property_long(php_http_params_class_entry, ZEND_STRL("flags"), PHP_HTTP_PARAMS_DEFAULT, ZEND_ACC_PUBLIC TSRMLS_CC);
+
+       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_params.h b/src/php_http_params.h
new file mode 100644 (file)
index 0000000..e1ebe27
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_PARAMS_H
+#define PHP_HTTP_PARAMS_H
+
+typedef struct php_http_params_token {
+       char *str;
+       size_t len;
+} php_http_params_token_t;
+
+#define PHP_HTTP_PARAMS_RAW                    0x00
+#define PHP_HTTP_PARAMS_ESCAPED                0x01
+#define PHP_HTTP_PARAMS_URLENCODED     0x04
+#define PHP_HTTP_PARAMS_DIMENSION      0x08
+#define PHP_HTTP_PARAMS_RFC5987                0x10
+#define PHP_HTTP_PARAMS_RFC5988                0x20
+#define PHP_HTTP_PARAMS_QUERY          (PHP_HTTP_PARAMS_URLENCODED|PHP_HTTP_PARAMS_DIMENSION)
+#define PHP_HTTP_PARAMS_DEFAULT                (PHP_HTTP_PARAMS_ESCAPED|PHP_HTTP_PARAMS_RFC5987)
+
+typedef struct php_http_params_opts {
+       php_http_params_token_t input;
+       php_http_params_token_t **param;
+       php_http_params_token_t **arg;
+       php_http_params_token_t **val;
+       zval *defval;
+       unsigned flags;
+} php_http_params_opts_t;
+
+PHP_HTTP_API php_http_params_opts_t *php_http_params_opts_default_get(php_http_params_opts_t *opts);
+PHP_HTTP_API HashTable *php_http_params_parse(HashTable *params, const php_http_params_opts_t *opts TSRMLS_DC);
+PHP_HTTP_API php_http_buffer_t *php_http_params_to_string(php_http_buffer_t *buf, HashTable *params, const char *pss, size_t psl, const char *ass, size_t asl, const char *vss, size_t vsl, unsigned flags TSRMLS_DC);
+
+PHP_HTTP_API php_http_params_token_t **php_http_params_separator_init(zval *zv TSRMLS_DC);
+PHP_HTTP_API void php_http_params_separator_free(php_http_params_token_t **separator);
+
+typedef php_http_object_t php_http_params_object_t;
+
+PHP_HTTP_API zend_class_entry *php_http_params_class_entry;
+
+PHP_MINIT_FUNCTION(http_params);
+
+#define php_http_params_object_new php_http_object_new
+#define php_http_params_object_new_ex php_http_object_new_ex
+
+#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_querystring.c b/src/php_http_querystring.c
new file mode 100644 (file)
index 0000000..d72337f
--- /dev/null
@@ -0,0 +1,740 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+#include <php_variables.h>
+#include <ext/spl/spl_array.h>
+
+#ifdef PHP_HTTP_HAVE_ICONV
+#      ifndef HAVE_ICONV
+#              define HAVE_ICONV 1
+#      endif
+#      undef PHP_ATOM_INC
+#      include <ext/iconv/php_iconv.h>
+#endif
+
+#define QS_MERGE 1
+
+static inline void php_http_querystring_set(zval *instance, zval *params, int flags TSRMLS_DC)
+{
+       zval *qa;
+
+       if (flags & QS_MERGE) {
+               qa = php_http_zsep(1, IS_ARRAY, zend_read_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), 0 TSRMLS_CC));
+       } else {
+               MAKE_STD_ZVAL(qa);
+               array_init(qa);
+       }
+
+       php_http_querystring_update(qa, params, NULL TSRMLS_CC);
+       zend_update_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), qa TSRMLS_CC);
+       zval_ptr_dtor(&qa);
+}
+
+static inline void php_http_querystring_str(zval *instance, zval *return_value TSRMLS_DC)
+{
+       zval *qa = zend_read_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), 0 TSRMLS_CC);
+
+       if (Z_TYPE_P(qa) == IS_ARRAY) {
+               php_http_querystring_update(qa, NULL, return_value TSRMLS_CC);
+       } else {
+               RETURN_EMPTY_STRING();
+       }
+}
+
+static inline void php_http_querystring_get(zval *this_ptr, int type, char *name, uint name_len, zval *defval, zend_bool del, zval *return_value TSRMLS_DC)
+{
+       zval **arrval, *qarray = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC);
+
+       if ((Z_TYPE_P(qarray) == IS_ARRAY) && (SUCCESS == zend_symtable_find(Z_ARRVAL_P(qarray), name, name_len + 1, (void *) &arrval))) {
+               if (type) {
+                       zval *value = php_http_ztyp(type, *arrval);
+                       RETVAL_ZVAL(value, 1, 1);
+               } else {
+                       RETVAL_ZVAL(*arrval, 1, 0);
+               }
+
+               if (del) {
+                       zval *delarr;
+
+                       MAKE_STD_ZVAL(delarr);
+                       array_init(delarr);
+                       add_assoc_null_ex(delarr, name, name_len + 1);
+                       php_http_querystring_set(this_ptr, delarr, QS_MERGE TSRMLS_CC);
+                       zval_ptr_dtor(&delarr);
+               }
+       } else if(defval) {
+               RETURN_ZVAL(defval, 1, 0);
+       }
+}
+
+#ifdef PHP_HTTP_HAVE_ICONV
+ZEND_RESULT_CODE php_http_querystring_xlate(zval *dst, zval *src, const char *ie, const char *oe TSRMLS_DC)
+{
+       HashPosition pos;
+       zval **entry = NULL;
+       char *xlate_str = NULL, *xkey;
+       size_t xlate_len = 0, xlen;
+       php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+       
+       FOREACH_KEYVAL(pos, src, key, entry) {
+               if (key.type == HASH_KEY_IS_STRING) {
+                       if (PHP_ICONV_ERR_SUCCESS != php_iconv_string(key.str, key.len-1, &xkey, &xlen, oe, ie)) {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to convert '%.*s' from '%s' to '%s'", key.len-1, key.str, ie, oe);
+                               return FAILURE;
+                       }
+               }
+               
+               if (Z_TYPE_PP(entry) == IS_STRING) {
+                       if (PHP_ICONV_ERR_SUCCESS != php_iconv_string(Z_STRVAL_PP(entry), Z_STRLEN_PP(entry), &xlate_str, &xlate_len, oe, ie)) {
+                               if (key.type == HASH_KEY_IS_STRING) {
+                                       efree(xkey);
+                               }
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to convert '%.*s' from '%s' to '%s'", Z_STRLEN_PP(entry), Z_STRVAL_PP(entry), ie, oe);
+                               return FAILURE;
+                       }
+                       if (key.type == HASH_KEY_IS_STRING) {
+                               add_assoc_stringl_ex(dst, xkey, xlen+1, xlate_str, xlate_len, 0);
+                       } else {
+                               add_index_stringl(dst, key.num, xlate_str, xlate_len, 0);
+                       }
+               } else if (Z_TYPE_PP(entry) == IS_ARRAY) {
+                       zval *subarray;
+                       
+                       MAKE_STD_ZVAL(subarray);
+                       array_init(subarray);
+                       if (key.type == HASH_KEY_IS_STRING) {
+                               add_assoc_zval_ex(dst, xkey, xlen+1, subarray);
+                       } else {
+                               add_index_zval(dst, key.num, subarray);
+                       }
+                       if (SUCCESS != php_http_querystring_xlate(subarray, *entry, ie, oe TSRMLS_CC)) {
+                               if (key.type == HASH_KEY_IS_STRING) {
+                                       efree(xkey);
+                               }
+                               return FAILURE;
+                       }
+               }
+               
+               if (key.type == HASH_KEY_IS_STRING) {
+                       efree(xkey);
+               }
+       }
+       return SUCCESS;
+}
+#endif /* HAVE_ICONV */
+
+ZEND_RESULT_CODE php_http_querystring_ctor(zval *instance, zval *params TSRMLS_DC)
+{
+       php_http_querystring_set(instance, params, 0 TSRMLS_CC);
+       return SUCCESS;
+}
+
+static int apply_querystring(void *pData TSRMLS_DC)
+{
+       zval **val = pData;
+
+       if (Z_TYPE_PP(val) == IS_ARRAY) {
+               zval **zvalue;
+
+               if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("value"), (void *) &zvalue)) {
+                       zval *tmp = *val;
+
+                       Z_ADDREF_PP(zvalue);
+                       *val = *zvalue;
+                       zval_dtor(tmp);
+                       Z_TYPE_P(tmp) = IS_NULL;
+                       zval_ptr_dtor(&tmp);
+               }
+       }
+
+       return ZEND_HASH_APPLY_KEEP;
+}
+
+ZEND_RESULT_CODE php_http_querystring_parse(HashTable *ht, const char *str, size_t len TSRMLS_DC)
+{
+       ZEND_RESULT_CODE rv = FAILURE;
+       php_http_params_opts_t opts;
+       php_http_params_token_t psep = { ZEND_STRL("&") }, *psepp[] = { &psep, NULL };
+       php_http_params_token_t vsep = { ZEND_STRL("=") }, *vsepp[] = { &vsep, NULL };
+       const char *asi_str = NULL;
+       size_t asi_len = 0;
+
+       opts.input.str = estrndup(str, len);
+       opts.input.len = len;
+       opts.param = psepp;
+       opts.arg = NULL;
+       opts.val = vsepp;
+       opts.flags = PHP_HTTP_PARAMS_QUERY;
+
+       if (SUCCESS == php_http_ini_entry(ZEND_STRL("arg_separator.input"), &asi_str, &asi_len, 0 TSRMLS_CC) && asi_len) {
+               zval *arr;
+
+               MAKE_STD_ZVAL(arr);
+               array_init_size(arr, asi_len);
+
+               do {
+                       add_next_index_stringl(arr, asi_str++, 1, 1);
+               } while (*asi_str);
+
+               opts.param = php_http_params_separator_init(arr TSRMLS_CC);
+
+               zval_ptr_dtor(&arr);
+       }
+
+       MAKE_STD_ZVAL(opts.defval);
+       ZVAL_NULL(opts.defval);
+
+       if (php_http_params_parse(ht, &opts TSRMLS_CC)) {
+               zend_hash_apply(ht, apply_querystring TSRMLS_CC);
+               rv = SUCCESS;
+       }
+
+       if (asi_len) {
+               php_http_params_separator_free(opts.param);
+       }
+
+       zval_ptr_dtor(&opts.defval);
+       efree(opts.input.str);
+       return rv;
+}
+
+ZEND_RESULT_CODE php_http_querystring_update(zval *qarray, zval *params, zval *outstring TSRMLS_DC)
+{
+       /* enforce proper type */
+       if (Z_TYPE_P(qarray) != IS_ARRAY) {
+               convert_to_array(qarray);
+       }
+
+       /* modify qarray */
+       if (params) {
+               HashPosition pos;
+               HashTable *ptr;
+               php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+               zval **params_entry,  **qarray_entry;
+               zval zv, *zv_ptr = NULL;
+
+               INIT_PZVAL(&zv);
+               ZVAL_NULL(&zv);
+
+               /* squeeze the hash out of the zval */
+               if (Z_TYPE_P(params) == IS_OBJECT && instanceof_function(Z_OBJCE_P(params), php_http_querystring_class_entry TSRMLS_CC)) {
+                       zv_ptr = php_http_ztyp(IS_ARRAY, zend_read_property(php_http_querystring_class_entry, params, ZEND_STRL("queryArray"), 0 TSRMLS_CC));
+                       ptr = Z_ARRVAL_P(zv_ptr);
+               } else if (Z_TYPE_P(params) == IS_OBJECT || Z_TYPE_P(params) == IS_ARRAY) {
+                       ptr = HASH_OF(params);
+               } else {
+                       zv_ptr = php_http_ztyp(IS_STRING, params);
+                       array_init(&zv);
+                       php_http_querystring_parse(Z_ARRVAL(zv), Z_STRVAL_P(zv_ptr), Z_STRLEN_P(zv_ptr) TSRMLS_CC);
+                       zval_ptr_dtor(&zv_ptr);
+                       zv_ptr = NULL;
+                       ptr = Z_ARRVAL(zv);
+               }
+
+               FOREACH_HASH_KEYVAL(pos, ptr, key, params_entry) {
+                       /* only public properties */
+                       if (key.type != HASH_KEY_IS_STRING || *key.str) {
+                               if (Z_TYPE_PP(params_entry) == IS_NULL) {
+                                       /*
+                                        * delete
+                                        */
+                                       if (key.type == HASH_KEY_IS_STRING) {
+                                               zend_hash_del(Z_ARRVAL_P(qarray), key.str, key.len);
+                                       } else {
+                                               zend_hash_index_del(Z_ARRVAL_P(qarray), key.num);
+                                       }
+                               } else if (     ((key.type == HASH_KEY_IS_STRING) && (SUCCESS == zend_hash_find(Z_ARRVAL_P(qarray), key.str, key.len, (void *) &qarray_entry)))
+                                               ||      ((key.type == HASH_KEY_IS_LONG) && (SUCCESS == zend_hash_index_find(Z_ARRVAL_P(qarray), key.num, (void *) &qarray_entry)))) {
+                                       /*
+                                        * update
+                                        */
+                                       zval equal, *entry = NULL;
+
+                                       /* recursive */
+                                       if (Z_TYPE_PP(params_entry) == IS_ARRAY || Z_TYPE_PP(params_entry) == IS_OBJECT) {
+                                               entry = php_http_zsep(1, IS_ARRAY, *qarray_entry);
+                                               php_http_querystring_update(entry, *params_entry, NULL TSRMLS_CC);
+                                       } else if ((FAILURE == is_equal_function(&equal, *qarray_entry, *params_entry TSRMLS_CC)) || !Z_BVAL(equal)) {
+                                               Z_ADDREF_PP(params_entry);
+                                               entry = *params_entry;
+                                       }
+
+                                       if (entry) {
+                                               if (key.type == HASH_KEY_IS_STRING) {
+                                                       zend_hash_update(Z_ARRVAL_P(qarray), key.str, key.len, (void *) &entry, sizeof(zval *), NULL);
+                                               } else {
+                                                       zend_hash_index_update(Z_ARRVAL_P(qarray), key.num, (void *) &entry, sizeof(zval *), NULL);
+                                               }
+                                       }
+                               } else {
+                                       zval *entry;
+                                       /*
+                                        * add
+                                        */
+                                       if (Z_TYPE_PP(params_entry) == IS_OBJECT) {
+                                               MAKE_STD_ZVAL(entry);
+                                               array_init(entry);
+                                               php_http_querystring_update(entry, *params_entry, NULL TSRMLS_CC);
+                                       } else {
+                                               Z_ADDREF_PP(params_entry);
+                                               entry = *params_entry;
+                                       }
+                                       if (key.type == HASH_KEY_IS_STRING) {
+                                               add_assoc_zval_ex(qarray, key.str, key.len, entry);
+                                       } else {
+                                               add_index_zval(qarray, key.num, entry);
+                                       }
+                               }
+                       }
+               }
+               /* clean up */
+               if (zv_ptr) {
+                       zval_ptr_dtor(&zv_ptr);
+               }
+               zval_dtor(&zv);
+       }
+
+       /* serialize to string */
+       if (outstring) {
+               char *s;
+               size_t l;
+
+               if (SUCCESS == php_http_url_encode_hash(Z_ARRVAL_P(qarray), NULL, 0, &s, &l TSRMLS_CC)) {
+                       zval_dtor(outstring);
+                       ZVAL_STRINGL(outstring, s, l, 0);
+               } else {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to encode query string");
+                       return FAILURE;
+               }
+       }
+
+       return SUCCESS;
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString___construct, 0, 0, 0)
+       ZEND_ARG_INFO(0, params)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpQueryString, __construct)
+{
+       zval *params = NULL;
+       zend_error_handling zeh;
+       
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z", &params), invalid_arg, return);
+
+       zend_replace_error_handling(EH_THROW, php_http_exception_bad_querystring_class_entry, &zeh TSRMLS_CC);
+       php_http_querystring_set(getThis(), params, 0 TSRMLS_CC);
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_getGlobalInstance, 0, 0, 0)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpQueryString, getGlobalInstance)
+{
+       zval *instance;
+
+       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
+
+       instance = *zend_std_get_static_property(php_http_querystring_class_entry, ZEND_STRL("instance"), 0 PHP_HTTP_ZEND_LITERAL_CCN TSRMLS_CC);
+
+       if (Z_TYPE_P(instance) != IS_OBJECT) {
+               zval **_GET = NULL;
+
+               zend_is_auto_global("_GET", lenof("_GET") TSRMLS_CC);
+
+               if ((SUCCESS == zend_hash_find(&EG(symbol_table), "_GET", sizeof("_GET"), (void *) &_GET))
+               &&      (Z_TYPE_PP(_GET) == IS_ARRAY)
+               ) {
+                       MAKE_STD_ZVAL(instance);
+                       ZVAL_OBJVAL(instance, php_http_querystring_object_new(php_http_querystring_class_entry TSRMLS_CC), 0);
+
+                       SEPARATE_ZVAL_TO_MAKE_IS_REF(_GET);
+                       convert_to_array(*_GET);
+                       zend_update_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), *_GET TSRMLS_CC);
+
+                       zend_update_static_property(php_http_querystring_class_entry, ZEND_STRL("instance"), instance TSRMLS_CC);
+                       zval_ptr_dtor(&instance);
+               } else {
+                       php_http_throw(unexpected_val, "Could not acquire reference to superglobal GET array", NULL);
+               }
+       }
+
+       RETVAL_ZVAL(instance, 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_getIterator, 0, 0, 0)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpQueryString, getIterator)
+{
+       zval *retval = NULL, *qa;
+
+       php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
+
+       qa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC);
+
+       object_init_ex(return_value, spl_ce_RecursiveArrayIterator);
+       zend_call_method_with_1_params(&return_value, spl_ce_RecursiveArrayIterator, NULL, "__construct", &retval, qa);
+       if (retval) {
+               zval_ptr_dtor(&retval);
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_toString, 0, 0, 0)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpQueryString, toString)
+{
+       if (SUCCESS != zend_parse_parameters_none()) {
+               return;
+       }
+       php_http_querystring_str(getThis(), return_value TSRMLS_CC);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_toArray, 0, 0, 0)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpQueryString, toArray)
+{
+       zval *zqa;
+
+       if (SUCCESS != zend_parse_parameters_none()) {
+               return;
+       }
+
+       zqa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC);
+       RETURN_ZVAL(zqa, 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_get, 0, 0, 0)
+       ZEND_ARG_INFO(0, name)
+       ZEND_ARG_INFO(0, type)
+       ZEND_ARG_INFO(0, defval)
+       ZEND_ARG_INFO(0, delete)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpQueryString, get)
+{
+       char *name_str = NULL;
+       int name_len = 0;
+       long type = 0;
+       zend_bool del = 0;
+       zval *ztype = NULL, *defval = NULL;
+       
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|szzb", &name_str, &name_len, &ztype, &defval, &del)) {
+               if (name_str && name_len) {
+                       if (ztype) {
+                               if (Z_TYPE_P(ztype) == IS_LONG) {
+                                       type = Z_LVAL_P(ztype);
+                               } else if(Z_TYPE_P(ztype) == IS_STRING) {
+                                       switch (Z_STRVAL_P(ztype)[0]) {
+                                               case 'B': 
+                                               case 'b':       type = PHP_HTTP_QUERYSTRING_TYPE_BOOL;          break;
+                                               case 'L':
+                                               case 'l':
+                                               case 'I':
+                                               case 'i':       type = PHP_HTTP_QUERYSTRING_TYPE_INT;           break;
+                                               case 'd':
+                                               case 'D':
+                                               case 'F':
+                                               case 'f':       type = PHP_HTTP_QUERYSTRING_TYPE_FLOAT;         break;  
+                                               case 'S':
+                                               case 's':       type = PHP_HTTP_QUERYSTRING_TYPE_STRING;        break;
+                                               case 'A':
+                                               case 'a':       type = PHP_HTTP_QUERYSTRING_TYPE_ARRAY;         break;
+                                               case 'O':
+                                               case 'o':       type = PHP_HTTP_QUERYSTRING_TYPE_OBJECT;        break;
+                                       }
+                               }
+                       }
+                       php_http_querystring_get(getThis(), type, name_str, name_len, defval, del, return_value TSRMLS_CC);
+               } else {
+                       php_http_querystring_str(getThis(), return_value TSRMLS_CC);
+               }
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_set, 0, 0, 1)
+       ZEND_ARG_INFO(0, params)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpQueryString, set)
+{
+       zval *params;
+       
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &params)) {
+               return;
+       }
+       
+       php_http_querystring_set(getThis(), params, QS_MERGE TSRMLS_CC);
+       RETVAL_ZVAL(getThis(), 1, 0);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_mod, 0, 0, 0)
+       ZEND_ARG_INFO(0, params)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpQueryString, mod)
+{
+       zval *params;
+       zend_error_handling zeh;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &params), invalid_arg, return);
+       
+       zend_replace_error_handling(EH_THROW, php_http_exception_bad_querystring_class_entry, &zeh TSRMLS_CC);
+       ZVAL_OBJVAL(return_value, Z_OBJ_HT_P(getThis())->clone_obj(getThis() TSRMLS_CC), 0);
+       php_http_querystring_set(return_value, params, QS_MERGE TSRMLS_CC);
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString___getter, 0, 0, 1)
+       ZEND_ARG_INFO(0, name)
+       ZEND_ARG_INFO(0, defval)
+       ZEND_ARG_INFO(0, delete)
+ZEND_END_ARG_INFO();
+#define PHP_HTTP_QUERYSTRING_GETTER(method, TYPE) \
+PHP_METHOD(HttpQueryString, method) \
+{ \
+       char *name; \
+       int name_len; \
+       zval *defval = NULL; \
+       zend_bool del = 0; \
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|zb", &name, &name_len, &defval, &del)) { \
+               php_http_querystring_get(getThis(), TYPE, name, name_len, defval, del, return_value TSRMLS_CC); \
+       } \
+}
+PHP_HTTP_QUERYSTRING_GETTER(getBool, IS_BOOL);
+PHP_HTTP_QUERYSTRING_GETTER(getInt, IS_LONG);
+PHP_HTTP_QUERYSTRING_GETTER(getFloat, IS_DOUBLE);
+PHP_HTTP_QUERYSTRING_GETTER(getString, IS_STRING);
+PHP_HTTP_QUERYSTRING_GETTER(getArray, IS_ARRAY);
+PHP_HTTP_QUERYSTRING_GETTER(getObject, IS_OBJECT);
+
+#ifdef PHP_HTTP_HAVE_ICONV
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_xlate, 0, 0, 2)
+       ZEND_ARG_INFO(0, from_encoding)
+       ZEND_ARG_INFO(0, to_encoding)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpQueryString, xlate)
+{
+       char *ie, *oe;
+       int ie_len, oe_len;
+       zval *na, *qa;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &ie, &ie_len, &oe, &oe_len), invalid_arg, return);
+
+       MAKE_STD_ZVAL(na);
+       array_init(na);
+       qa = php_http_ztyp(IS_ARRAY, zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC));
+
+       php_http_expect(SUCCESS == php_http_querystring_xlate(na, qa, ie, oe TSRMLS_CC), bad_conversion,
+                       zval_ptr_dtor(&na);
+                       zval_ptr_dtor(&qa);
+                       return;
+       );
+
+       php_http_querystring_set(getThis(), na, 0 TSRMLS_CC);
+       RETVAL_ZVAL(getThis(), 1, 0);
+       
+       zval_ptr_dtor(&na);
+       zval_ptr_dtor(&qa);
+}
+#endif /* HAVE_ICONV */
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_serialize, 0, 0, 0)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpQueryString, serialize)
+{
+       if (SUCCESS != zend_parse_parameters_none()) {
+               return;
+       }
+       php_http_querystring_str(getThis(), return_value TSRMLS_CC);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_unserialize, 0, 0, 1)
+       ZEND_ARG_INFO(0, serialized)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpQueryString, unserialize)
+{
+       zval *serialized;
+       
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &serialized)) {
+               return;
+       }
+
+       if (Z_TYPE_P(serialized) == IS_STRING) {
+               php_http_querystring_set(getThis(), serialized, 0 TSRMLS_CC);
+       } else {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected a string as parameter");
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_offsetGet, 0, 0, 1)
+       ZEND_ARG_INFO(0, offset)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpQueryString, offsetGet)
+{
+       char *offset_str;
+       int offset_len;
+       zval **value, *qa;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset_str, &offset_len)) {
+               return;
+       }
+       
+       qa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC);
+
+       if (Z_TYPE_P(qa) == IS_ARRAY) {
+               if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(qa), offset_str, offset_len + 1, (void *) &value)) {
+                       RETVAL_ZVAL(*value, 1, 0);
+               }
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_offsetSet, 0, 0, 2)
+       ZEND_ARG_INFO(0, offset)
+       ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpQueryString, offsetSet)
+{
+       char *offset_str;
+       int offset_len;
+       zval *value, *param;
+       
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &offset_str, &offset_len, &value)) {
+               return;
+       }
+
+       param = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC);
+
+       if (Z_TYPE_P(param) == IS_ARRAY && zend_symtable_exists(Z_ARRVAL_P(param), offset_str, offset_len + 1)) {
+               Z_ADDREF_P(value);
+               zend_symtable_update(Z_ARRVAL_P(param), offset_str, offset_len + 1, (void *) &value, sizeof(zval *), NULL);
+               Z_ADDREF_P(param);
+       } else {
+               MAKE_STD_ZVAL(param);
+               array_init(param);
+               Z_ADDREF_P(value);
+               add_assoc_zval_ex(param, offset_str, offset_len + 1, value);
+       }
+       php_http_querystring_set(getThis(), param, QS_MERGE TSRMLS_CC);
+       zval_ptr_dtor(&param);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_offsetExists, 0, 0, 1)
+       ZEND_ARG_INFO(0, offset)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpQueryString, offsetExists)
+{
+       char *offset_str;
+       int offset_len;
+       zval **value, *qa;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset_str, &offset_len)) {
+               return;
+       }
+       
+       qa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC);
+
+       if (Z_TYPE_P(qa) == IS_ARRAY) {
+               if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(qa), offset_str, offset_len + 1, (void *) &value)) {
+                       RETURN_BOOL(Z_TYPE_PP(value) != IS_NULL);
+               }
+       }
+       RETURN_FALSE;
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_offsetUnset, 0, 0, 1)
+       ZEND_ARG_INFO(0, offset)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpQueryString, offsetUnset)
+{
+       char *offset_str;
+       int offset_len;
+       zval *param;
+       
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset_str, &offset_len)) {
+               return;
+       }
+
+       MAKE_STD_ZVAL(param);
+       array_init(param);
+       add_assoc_null_ex(param, offset_str, offset_len + 1);
+       php_http_querystring_set(getThis(), param, QS_MERGE TSRMLS_CC);
+       zval_ptr_dtor(&param);
+}
+
+zend_class_entry *php_http_querystring_class_entry;
+
+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, toArray, ai_HttpQueryString_toArray, ZEND_ACC_PUBLIC)
+       PHP_ME(HttpQueryString, toString, ai_HttpQueryString_toString, ZEND_ACC_PUBLIC)
+       ZEND_MALIAS(HttpQueryString, __toString, toString, ai_HttpQueryString_toString, ZEND_ACC_PUBLIC)
+
+       PHP_ME(HttpQueryString, get, ai_HttpQueryString_get, ZEND_ACC_PUBLIC)
+       PHP_ME(HttpQueryString, set, ai_HttpQueryString_set, ZEND_ACC_PUBLIC)
+       PHP_ME(HttpQueryString, mod, ai_HttpQueryString_mod, ZEND_ACC_PUBLIC)
+
+       PHP_ME(HttpQueryString, getBool, ai_HttpQueryString___getter, ZEND_ACC_PUBLIC)
+       PHP_ME(HttpQueryString, getInt, ai_HttpQueryString___getter, ZEND_ACC_PUBLIC)
+       PHP_ME(HttpQueryString, getFloat, ai_HttpQueryString___getter, ZEND_ACC_PUBLIC)
+       PHP_ME(HttpQueryString, getString, ai_HttpQueryString___getter, ZEND_ACC_PUBLIC)
+       PHP_ME(HttpQueryString, getArray, ai_HttpQueryString___getter, ZEND_ACC_PUBLIC)
+       PHP_ME(HttpQueryString, getObject, ai_HttpQueryString___getter, ZEND_ACC_PUBLIC)
+
+       PHP_ME(HttpQueryString, getIterator, ai_HttpQueryString_getIterator, ZEND_ACC_PUBLIC)
+
+       PHP_ME(HttpQueryString, getGlobalInstance, ai_HttpQueryString_getGlobalInstance, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+#ifdef PHP_HTTP_HAVE_ICONV
+       PHP_ME(HttpQueryString, xlate, ai_HttpQueryString_xlate, ZEND_ACC_PUBLIC)
+#endif
+
+       /* Implements Serializable */
+       PHP_ME(HttpQueryString, serialize, ai_HttpQueryString_serialize, ZEND_ACC_PUBLIC)
+       PHP_ME(HttpQueryString, unserialize, ai_HttpQueryString_unserialize, ZEND_ACC_PUBLIC)
+
+       /* Implements ArrayAccess */
+       PHP_ME(HttpQueryString, offsetGet, ai_HttpQueryString_offsetGet, ZEND_ACC_PUBLIC)
+       PHP_ME(HttpQueryString, offsetSet, ai_HttpQueryString_offsetSet, ZEND_ACC_PUBLIC)
+       PHP_ME(HttpQueryString, offsetExists, ai_HttpQueryString_offsetExists, ZEND_ACC_PUBLIC)
+       PHP_ME(HttpQueryString, offsetUnset, ai_HttpQueryString_offsetUnset, ZEND_ACC_PUBLIC)
+
+       EMPTY_FUNCTION_ENTRY
+};
+
+PHP_MINIT_FUNCTION(http_querystring)
+{
+       zend_class_entry ce = {0};
+
+       INIT_NS_CLASS_ENTRY(ce, "http", "QueryString", php_http_querystring_methods);
+       php_http_querystring_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
+       php_http_querystring_class_entry->create_object = php_http_querystring_object_new;
+       zend_class_implements(php_http_querystring_class_entry TSRMLS_CC, 3, zend_ce_serializable, zend_ce_arrayaccess, zend_ce_aggregate);
+
+       zend_declare_property_null(php_http_querystring_class_entry, ZEND_STRL("instance"), (ZEND_ACC_STATIC|ZEND_ACC_PRIVATE) TSRMLS_CC);
+       zend_declare_property_null(php_http_querystring_class_entry, ZEND_STRL("queryArray"), ZEND_ACC_PRIVATE TSRMLS_CC);
+
+       zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_BOOL"), PHP_HTTP_QUERYSTRING_TYPE_BOOL TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_INT"), PHP_HTTP_QUERYSTRING_TYPE_INT TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_FLOAT"), PHP_HTTP_QUERYSTRING_TYPE_FLOAT TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_STRING"), PHP_HTTP_QUERYSTRING_TYPE_STRING TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_ARRAY"), PHP_HTTP_QUERYSTRING_TYPE_ARRAY TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_OBJECT"), PHP_HTTP_QUERYSTRING_TYPE_OBJECT TSRMLS_CC);
+
+       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_querystring.h b/src/php_http_querystring.h
new file mode 100644 (file)
index 0000000..3391e91
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_QUERYSTRING_H
+#define PHP_HTTP_QUERYSTRING_H
+
+#ifdef PHP_HTTP_HAVE_ICONV
+PHP_HTTP_API ZEND_RESULT_CODE php_http_querystring_xlate(zval *dst, zval *src, const char *ie, const char *oe TSRMLS_DC);
+#endif /* PHP_HTTP_HAVE_ICONV */
+PHP_HTTP_API ZEND_RESULT_CODE php_http_querystring_update(zval *qarray, zval *params, zval *qstring TSRMLS_DC);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_querystring_ctor(zval *instance, zval *params TSRMLS_DC);
+
+typedef php_http_object_t php_http_querystring_object_t;
+
+#define PHP_HTTP_QUERYSTRING_TYPE_BOOL         IS_BOOL
+#define PHP_HTTP_QUERYSTRING_TYPE_INT          IS_LONG
+#define PHP_HTTP_QUERYSTRING_TYPE_FLOAT                IS_DOUBLE
+#define PHP_HTTP_QUERYSTRING_TYPE_STRING       IS_STRING
+#define PHP_HTTP_QUERYSTRING_TYPE_ARRAY                IS_ARRAY
+#define PHP_HTTP_QUERYSTRING_TYPE_OBJECT       IS_OBJECT
+
+PHP_HTTP_API zend_class_entry *php_http_querystring_class_entry;
+
+PHP_MINIT_FUNCTION(http_querystring);
+
+#define php_http_querystring_object_new php_http_object_new
+#define php_http_querystring_object_new_ex php_http_object_new_ex
+
+#endif /* PHP_HTTP_QUERYSTRING_H */
+
+/*
+ * 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_response_codes.h b/src/php_http_response_codes.h
new file mode 100644 (file)
index 0000000..82157f9
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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-2015, Michael Wallner <mike@php.net>            |
+    +--------------------------------------------------------------------+
+*/
+
+#ifndef PHP_HTTP_RESPONSE_CODE
+#      define PHP_HTTP_RESPONSE_CODE(code, status)
+#endif
+
+PHP_HTTP_RESPONSE_CODE(100, "Continue")
+PHP_HTTP_RESPONSE_CODE(101, "Switching Protocols")
+PHP_HTTP_RESPONSE_CODE(102, "Processing")
+PHP_HTTP_RESPONSE_CODE(200, "OK")
+PHP_HTTP_RESPONSE_CODE(201, "Created")
+PHP_HTTP_RESPONSE_CODE(202, "Accepted")
+PHP_HTTP_RESPONSE_CODE(203, "Non-Authoritative Information")
+PHP_HTTP_RESPONSE_CODE(204, "No Content")
+PHP_HTTP_RESPONSE_CODE(205, "Reset Content")
+PHP_HTTP_RESPONSE_CODE(206, "Partial Content")
+PHP_HTTP_RESPONSE_CODE(207, "Multi-Status")
+PHP_HTTP_RESPONSE_CODE(208, "Already Reported")
+PHP_HTTP_RESPONSE_CODE(226, "IM Used")
+PHP_HTTP_RESPONSE_CODE(300, "Multiple Choices")
+PHP_HTTP_RESPONSE_CODE(301, "Moved Permanently")
+PHP_HTTP_RESPONSE_CODE(302, "Found")
+PHP_HTTP_RESPONSE_CODE(303, "See Other")
+PHP_HTTP_RESPONSE_CODE(304, "Not Modified")
+PHP_HTTP_RESPONSE_CODE(305, "Use Proxy")
+PHP_HTTP_RESPONSE_CODE(307, "Temporary Redirect")
+PHP_HTTP_RESPONSE_CODE(308, "Permanent Redirect")
+PHP_HTTP_RESPONSE_CODE(400, "Bad Request")
+PHP_HTTP_RESPONSE_CODE(401, "Unauthorized")
+PHP_HTTP_RESPONSE_CODE(402, "Payment Required")
+PHP_HTTP_RESPONSE_CODE(403, "Forbidden")
+PHP_HTTP_RESPONSE_CODE(404, "Not Found")
+PHP_HTTP_RESPONSE_CODE(405, "Method Not Allowed")
+PHP_HTTP_RESPONSE_CODE(406, "Not Acceptable")
+PHP_HTTP_RESPONSE_CODE(407, "Proxy Authentication Required")
+PHP_HTTP_RESPONSE_CODE(408, "Request Timeout")
+PHP_HTTP_RESPONSE_CODE(409, "Conflict")
+PHP_HTTP_RESPONSE_CODE(410, "Gone")
+PHP_HTTP_RESPONSE_CODE(411, "Length Required")
+PHP_HTTP_RESPONSE_CODE(412, "Precondition Failed")
+PHP_HTTP_RESPONSE_CODE(413, "Request Entity Too Large")
+PHP_HTTP_RESPONSE_CODE(414, "Request URI Too Long")
+PHP_HTTP_RESPONSE_CODE(415, "Unsupported Media Type")
+PHP_HTTP_RESPONSE_CODE(416, "Requested Range Not Satisfiable")
+PHP_HTTP_RESPONSE_CODE(417, "Expectation Failed")
+PHP_HTTP_RESPONSE_CODE(422, "Unprocessible Entity")
+PHP_HTTP_RESPONSE_CODE(423, "Locked")
+PHP_HTTP_RESPONSE_CODE(424, "Failed Dependency")
+PHP_HTTP_RESPONSE_CODE(426, "Upgrade Required")
+PHP_HTTP_RESPONSE_CODE(428, "Precondition Required")
+PHP_HTTP_RESPONSE_CODE(429, "Too Many Requests")
+PHP_HTTP_RESPONSE_CODE(431, "Request Header Fields Too Large")
+PHP_HTTP_RESPONSE_CODE(500, "Internal Server Error")
+PHP_HTTP_RESPONSE_CODE(501, "Not Implemented")
+PHP_HTTP_RESPONSE_CODE(502, "Bad Gateway")
+PHP_HTTP_RESPONSE_CODE(503, "Service Unavailable")
+PHP_HTTP_RESPONSE_CODE(504, "Gateway Timeout")
+PHP_HTTP_RESPONSE_CODE(505, "HTTP Version Not Supported")
+PHP_HTTP_RESPONSE_CODE(506, "Variant Also Negotiates")
+PHP_HTTP_RESPONSE_CODE(507, "Insufficient Storage")
+PHP_HTTP_RESPONSE_CODE(508, "Loop Detected")
+PHP_HTTP_RESPONSE_CODE(510, "Not Extended")
+PHP_HTTP_RESPONSE_CODE(511, "Network Authentication Required")
+
+
+/*
+ * 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_url.c b/src/php_http_url.c
new file mode 100644 (file)
index 0000000..81b2d95
--- /dev/null
@@ -0,0 +1,1749 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_IDN2
+#      include <idn2.h>
+#elif PHP_HTTP_HAVE_IDN
+#      include <idna.h>
+#endif
+
+#ifdef PHP_HTTP_HAVE_WCHAR
+#      include <wchar.h>
+#      include <wctype.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#      include <arpa/inet.h>
+#endif
+
+#include "php_http_utf8.h"
+
+static inline char *localhostname(void)
+{
+       char hostname[1024] = {0};
+       
+#ifdef PHP_WIN32
+       if (SUCCESS == gethostname(hostname, lenof(hostname))) {
+               return estrdup(hostname);
+       }
+#elif defined(HAVE_GETHOSTNAME)
+       if (SUCCESS == gethostname(hostname, lenof(hostname))) {
+#      if defined(HAVE_GETDOMAINNAME)
+               size_t hlen = strlen(hostname);
+               if (hlen <= lenof(hostname) - lenof("(none)")) {
+                       hostname[hlen++] = '.';
+                       if (SUCCESS == getdomainname(&hostname[hlen], lenof(hostname) - hlen)) {
+                               if (!strcmp(&hostname[hlen], "(none)")) {
+                                       hostname[hlen - 1] = '\0';
+                               }
+                               return estrdup(hostname);
+                       }
+               }
+#      endif
+               if (strcmp(hostname, "(none)")) {
+                       return estrdup(hostname);
+               }
+       }
+#endif
+       return estrndup("localhost", lenof("localhost"));
+}
+
+#define url(buf) ((php_http_url_t *) (buf).data)
+
+static php_http_url_t *php_http_url_from_env(TSRMLS_D)
+{
+       zval *https, *zhost, *zport;
+       long port;
+       php_http_buffer_t buf;
+
+       php_http_buffer_init_ex(&buf, MAX(PHP_HTTP_BUFFER_DEFAULT_SIZE, sizeof(php_http_url_t)<<2), PHP_HTTP_BUFFER_INIT_PREALLOC);
+       php_http_buffer_account(&buf, sizeof(php_http_url_t));
+       memset(buf.data, 0, buf.used);
+
+       /* scheme */
+       url(buf)->scheme = &buf.data[buf.used];
+       https = php_http_env_get_server_var(ZEND_STRL("HTTPS"), 1 TSRMLS_CC);
+       if (https && !strcasecmp(Z_STRVAL_P(https), "ON")) {
+               php_http_buffer_append(&buf, "https", sizeof("https"));
+       } else {
+               php_http_buffer_append(&buf, "http", sizeof("http"));
+       }
+
+       /* host */
+       url(buf)->host = &buf.data[buf.used];
+       if ((((zhost = php_http_env_get_server_var(ZEND_STRL("HTTP_HOST"), 1 TSRMLS_CC)) ||
+                       (zhost = php_http_env_get_server_var(ZEND_STRL("SERVER_NAME"), 1 TSRMLS_CC)) ||
+                       (zhost = php_http_env_get_server_var(ZEND_STRL("SERVER_ADDR"), 1 TSRMLS_CC)))) && Z_STRLEN_P(zhost)) {
+               size_t stop_at = strspn(Z_STRVAL_P(zhost), "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-.");
+
+               php_http_buffer_append(&buf, Z_STRVAL_P(zhost), stop_at);
+               php_http_buffer_append(&buf, "", 1);
+       } else {
+               char *host_str = localhostname();
+
+               php_http_buffer_append(&buf, host_str, strlen(host_str) + 1);
+               efree(host_str);
+       }
+
+       /* port */
+       zport = php_http_env_get_server_var(ZEND_STRL("SERVER_PORT"), 1 TSRMLS_CC);
+       if (zport && IS_LONG == is_numeric_string(Z_STRVAL_P(zport), Z_STRLEN_P(zport), &port, NULL, 0)) {
+               url(buf)->port = port;
+       }
+
+       /* path */
+       if (SG(request_info).request_uri && SG(request_info).request_uri[0]) {
+               const char *q = strchr(SG(request_info).request_uri, '?');
+
+               url(buf)->path = &buf.data[buf.used];
+
+               if (q) {
+                       php_http_buffer_append(&buf, SG(request_info).request_uri, q - SG(request_info).request_uri);
+                       php_http_buffer_append(&buf, "", 1);
+               } else {
+                       php_http_buffer_append(&buf, SG(request_info).request_uri, strlen(SG(request_info).request_uri) + 1);
+               }
+       }
+
+       /* query */
+       if (SG(request_info).query_string && SG(request_info).query_string[0]) {
+               url(buf)->query = &buf.data[buf.used];
+               php_http_buffer_append(&buf, SG(request_info).query_string, strlen(SG(request_info).query_string) + 1);
+       }
+
+       return url(buf);
+}
+
+#define url_isset(u,n) \
+       ((u)&&(u)->n)
+#define url_append(buf, append) do { \
+       char *_ptr = (buf)->data; \
+       php_http_url_t *_url = (php_http_url_t *) _ptr, _mem = *_url; \
+       append; \
+       /* relocate */ \
+       if (_ptr != (buf)->data) { \
+               ptrdiff_t diff = (buf)->data - _ptr; \
+               _url = (php_http_url_t *) (buf)->data; \
+               if (_mem.scheme)        _url->scheme += diff; \
+               if (_mem.user)          _url->user += diff; \
+               if (_mem.pass)          _url->pass += diff; \
+               if (_mem.host)          _url->host += diff; \
+               if (_mem.path)          _url->path += diff; \
+               if (_mem.query)         _url->query += diff; \
+               if (_mem.fragment)      _url->fragment += diff; \
+       } \
+} while (0)
+#define url_copy(n) do { \
+       if (url_isset(new_url, n)) { \
+               url(buf)->n = &buf.data[buf.used]; \
+               url_append(&buf, php_http_buffer_append(&buf, new_url->n, strlen(new_url->n) + 1)); \
+       } else if (url_isset(old_url, n)) { \
+               url(buf)->n = &buf.data[buf.used]; \
+               url_append(&buf, php_http_buffer_append(&buf, old_url->n, strlen(old_url->n) + 1)); \
+       } \
+} while (0)
+
+php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_url_t *new_url, unsigned flags TSRMLS_DC)
+{
+       php_http_url_t *tmp_url = NULL;
+       php_http_buffer_t buf;
+
+       php_http_buffer_init_ex(&buf, MAX(PHP_HTTP_BUFFER_DEFAULT_SIZE, sizeof(php_http_url_t)<<2), PHP_HTTP_BUFFER_INIT_PREALLOC);
+       php_http_buffer_account(&buf, sizeof(php_http_url_t));
+       memset(buf.data, 0, buf.used);
+
+       /* set from env if requested */
+       if (flags & PHP_HTTP_URL_FROM_ENV) {
+               php_http_url_t *env_url = php_http_url_from_env(TSRMLS_C);
+
+               old_url = tmp_url = php_http_url_mod(env_url, old_url, flags ^ PHP_HTTP_URL_FROM_ENV TSRMLS_CC);
+               php_http_url_free(&env_url);
+       }
+
+       url_copy(scheme);
+
+       if (!(flags & PHP_HTTP_URL_STRIP_USER)) {
+               url_copy(user);
+       }
+
+       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);
+       }
+
+       if (!(flags & PHP_HTTP_URL_STRIP_PATH)) {
+               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));
+                       }
+                       url_append(&buf, php_http_buffer_append(&buf, path, strlen(path) + 1));
+                       efree(path);
+               } else {
+                       const char *path = NULL;
+
+                       if (url_isset(new_url, path)) {
+                               path = new_url->path;
+                       } else if (url_isset(old_url, path)) {
+                               path = old_url->path;
+                       }
+
+                       if (path) {
+                               url(buf)->path = &buf.data[buf.used];
+
+                               url_append(&buf, php_http_buffer_append(&buf, path, strlen(path) + 1));
+                       }
+
+
+               }
+       }
+
+       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;
+                       
+                       INIT_PZVAL(&qstr);
+                       INIT_PZVAL(&qarr);
+                       array_init(&qarr);
+                       
+                       ZVAL_STRING(&qstr, old_url->query, 0);
+                       php_http_querystring_update(&qarr, &qstr, NULL TSRMLS_CC);
+                       ZVAL_STRING(&qstr, new_url->query, 0);
+                       php_http_querystring_update(&qarr, &qstr, NULL TSRMLS_CC);
+                       
+                       ZVAL_NULL(&qstr);
+                       php_http_querystring_update(&qarr, NULL, &qstr TSRMLS_CC);
+
+                       url(buf)->query = &buf.data[buf.used];
+                       url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL(qstr), Z_STRLEN(qstr) + 1));
+
+                       zval_dtor(&qstr);
+                       zval_dtor(&qarr);
+               } else {
+                       url_copy(query);
+               }
+       }
+
+       if (!(flags & PHP_HTTP_URL_STRIP_FRAGMENT)) {
+               url_copy(fragment);
+       }
+       
+       /* done with copy & combine & strip */
+
+       if (flags & PHP_HTTP_URL_FROM_ENV) {
+               /* free old_url we tainted above */
+               php_http_url_free(&tmp_url);
+       }
+
+       /* replace directory references if path is not a single slash */
+       if ((flags & PHP_HTTP_URL_SANITIZE_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':
+                                                       ptr[1] = '\0';
+                                                       break;
+
+                                               case '/':
+                                                       memmove(&ptr[1], &ptr[3], end - &ptr[3]);
+                                                       break;
+
+                                               case '.':
+                                                       if (ptr[3] == '/') {
+                                                               char *pos = &ptr[4];
+                                                               while (ptr != url(buf)->path) {
+                                                                       if (*--ptr == '/') {
+                                                                               break;
+                                                                       }
+                                                               }
+                                                               memmove(&ptr[1], pos, end - pos);
+                                                               break;
+                                                       } else if (!ptr[3]) {
+                                                               /* .. at the end */
+                                                               ptr[1] = '\0';
+                                                       }
+                                                       /* no break */
+
+                                               default:
+                                                       /* something else */
+                                                       ++ptr;
+                                                       break;
+                                       }
+                                       break;
+
+                               default:
+                                       ++ptr;
+                                       break;
+                       }
+               }
+       }
+       /* unset default ports */
+       if (url(buf)->port) {
+               if (    ((url(buf)->port == 80) && url(buf)->scheme && !strcmp(url(buf)->scheme, "http"))
+                       ||      ((url(buf)->port ==443) && url(buf)->scheme && !strcmp(url(buf)->scheme, "https"))
+               ) {
+                       url(buf)->port = 0;
+               }
+       }
+       
+       return url(buf);
+}
+
+char *php_http_url_to_string(const php_http_url_t *url, char **url_str, size_t *url_len, zend_bool persistent)
+{
+       php_http_buffer_t buf;
+
+       php_http_buffer_init_ex(&buf, PHP_HTTP_BUFFER_DEFAULT_SIZE, persistent ?
+                       PHP_HTTP_BUFFER_INIT_PERSISTENT : 0);
+
+       if (url->scheme && *url->scheme) {
+               php_http_buffer_appendl(&buf, url->scheme);
+               php_http_buffer_appends(&buf, "://");
+       } else if ((url->user && *url->user) || (url->host && *url->host)) {
+               php_http_buffer_appends(&buf, "//");
+       }
+
+       if (url->user && *url->user) {
+               php_http_buffer_appendl(&buf, url->user);
+               if (url->pass && *url->pass) {
+                       php_http_buffer_appends(&buf, ":");
+                       php_http_buffer_appendl(&buf, url->pass);
+               }
+               php_http_buffer_appends(&buf, "@");
+       }
+
+       if (url->host && *url->host) {
+               php_http_buffer_appendl(&buf, url->host);
+               if (url->port) {
+                       php_http_buffer_appendf(&buf, ":%hu", url->port);
+               }
+       }
+
+       if (url->path && *url->path) {
+               if (*url->path != '/') {
+                       php_http_buffer_appends(&buf, "/");
+               }
+               php_http_buffer_appendl(&buf, url->path);
+       } else if (buf.used) {
+               php_http_buffer_appends(&buf, "/");
+       }
+
+       if (url->query && *url->query) {
+               php_http_buffer_appends(&buf, "?");
+               php_http_buffer_appendl(&buf, url->query);
+       }
+
+       if (url->fragment && *url->fragment) {
+               php_http_buffer_appends(&buf, "#");
+               php_http_buffer_appendl(&buf, url->fragment);
+       }
+
+       php_http_buffer_shrink(&buf);
+       php_http_buffer_fix(&buf);
+
+       if (url_len) {
+               *url_len = buf.used;
+       }
+
+       if (url_str) {
+               *url_str = buf.data;
+       }
+
+       return buf.data;
+}
+
+char *php_http_url_authority_to_string(const php_http_url_t *url, char **url_str, size_t *url_len)
+{
+       php_http_buffer_t buf;
+
+       php_http_buffer_init(&buf);
+
+       if (url->user && *url->user) {
+               php_http_buffer_appendl(&buf, url->user);
+               if (url->pass && *url->pass) {
+                       php_http_buffer_appends(&buf, ":");
+                       php_http_buffer_appendl(&buf, url->pass);
+               }
+               php_http_buffer_appends(&buf, "@");
+       }
+
+       if (url->host && *url->host) {
+               php_http_buffer_appendl(&buf, url->host);
+               if (url->port) {
+                       php_http_buffer_appendf(&buf, ":%hu", url->port);
+               }
+       }
+
+       php_http_buffer_shrink(&buf);
+       php_http_buffer_fix(&buf);
+
+       if (url_len) {
+               *url_len = buf.used;
+       }
+
+       if (url_str) {
+               *url_str = buf.data;
+       }
+
+       return buf.data;
+}
+
+php_http_url_t *php_http_url_from_zval(zval *value, unsigned flags TSRMLS_DC)
+{
+       zval *zcpy;
+       php_http_url_t *purl;
+
+       switch (Z_TYPE_P(value)) {
+       case IS_ARRAY:
+       case IS_OBJECT:
+               purl = php_http_url_from_struct(HASH_OF(value));
+               break;
+
+       default:
+               zcpy = php_http_ztyp(IS_STRING, value);
+               purl = php_http_url_parse(Z_STRVAL_P(zcpy), Z_STRLEN_P(zcpy), flags TSRMLS_CC);
+               zval_ptr_dtor(&zcpy);
+       }
+
+       return purl;
+}
+
+php_http_url_t *php_http_url_from_struct(HashTable *ht)
+{
+       zval **e;
+       php_http_buffer_t buf;
+
+       php_http_buffer_init_ex(&buf, MAX(PHP_HTTP_BUFFER_DEFAULT_SIZE, sizeof(php_http_url_t)<<2), PHP_HTTP_BUFFER_INIT_PREALLOC);
+       php_http_buffer_account(&buf, sizeof(php_http_url_t));
+       memset(buf.data, 0, buf.used);
+
+       if (SUCCESS == zend_hash_find(ht, "scheme", sizeof("scheme"), (void *) &e)) {
+               zval *cpy = php_http_ztyp(IS_STRING, *e);
+               url(buf)->scheme = &buf.data[buf.used];
+               url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
+               zval_ptr_dtor(&cpy);
+       }
+       if (SUCCESS == zend_hash_find(ht, "user", sizeof("user"), (void *) &e)) {
+               zval *cpy = php_http_ztyp(IS_STRING, *e);
+               url(buf)->user = &buf.data[buf.used];
+               url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
+               zval_ptr_dtor(&cpy);
+       }
+       if (SUCCESS == zend_hash_find(ht, "pass", sizeof("pass"), (void *) &e)) {
+               zval *cpy = php_http_ztyp(IS_STRING, *e);
+               url(buf)->pass = &buf.data[buf.used];
+               url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
+               zval_ptr_dtor(&cpy);
+       }
+       if (SUCCESS == zend_hash_find(ht, "host", sizeof("host"), (void *) &e)) {
+               zval *cpy = php_http_ztyp(IS_STRING, *e);
+               url(buf)->host = &buf.data[buf.used];
+               url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
+               zval_ptr_dtor(&cpy);
+       }
+       if (SUCCESS == zend_hash_find(ht, "port", sizeof("port"), (void *) &e)) {
+               zval *cpy = php_http_ztyp(IS_LONG, *e);
+               url(buf)->port = (unsigned short) Z_LVAL_P(cpy);
+               zval_ptr_dtor(&cpy);
+       }
+       if (SUCCESS == zend_hash_find(ht, "path", sizeof("path"), (void *) &e)) {
+               zval *cpy = php_http_ztyp(IS_STRING, *e);
+               url(buf)->path = &buf.data[buf.used];
+               url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
+               zval_ptr_dtor(&cpy);
+       }
+       if (SUCCESS == zend_hash_find(ht, "query", sizeof("query"), (void *) &e)) {
+               zval *cpy = php_http_ztyp(IS_STRING, *e);
+               url(buf)->query = &buf.data[buf.used];
+               url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
+               zval_ptr_dtor(&cpy);
+       }
+       if (SUCCESS == zend_hash_find(ht, "fragment", sizeof("fragment"), (void *) &e)) {
+               zval *cpy = php_http_ztyp(IS_STRING, *e);
+               url(buf)->fragment = &buf.data[buf.used];
+               url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1));
+               zval_ptr_dtor(&cpy);
+       }
+
+       return url(buf);
+}
+
+HashTable *php_http_url_to_struct(const php_http_url_t *url, zval *strct TSRMLS_DC)
+{
+       zval arr;
+
+       if (strct) {
+               switch (Z_TYPE_P(strct)) {
+                       default:
+                               zval_dtor(strct);
+                               array_init(strct);
+                               /* no break */
+                       case IS_ARRAY:
+                       case IS_OBJECT:
+                               INIT_PZVAL_ARRAY((&arr), HASH_OF(strct));
+                               break;
+               }
+       } else {
+               INIT_PZVAL(&arr);
+               array_init(&arr);
+       }
+
+       if (url) {
+               if (url->scheme) {
+                       add_assoc_string(&arr, "scheme", url->scheme, 1);
+               }
+               if (url->user) {
+                       add_assoc_string(&arr, "user", url->user, 1);
+               }
+               if (url->pass) {
+                       add_assoc_string(&arr, "pass", url->pass, 1);
+               }
+               if (url->host) {
+                       add_assoc_string(&arr, "host", url->host, 1);
+               }
+               if (url->port) {
+                       add_assoc_long(&arr, "port", (long) url->port);
+               }
+               if (url->path) {
+                       add_assoc_string(&arr, "path", url->path, 1);
+               }
+               if (url->query) {
+                       add_assoc_string(&arr, "query", url->query, 1);
+               }
+               if (url->fragment) {
+                       add_assoc_string(&arr, "fragment", url->fragment, 1);
+               }
+       }
+
+       return Z_ARRVAL(arr);
+}
+
+ZEND_RESULT_CODE php_http_url_encode_hash(HashTable *hash, const char *pre_encoded_str, size_t pre_encoded_len, char **encoded_str, size_t *encoded_len TSRMLS_DC)
+{
+       const char *arg_sep_str = "&";
+       size_t arg_sep_len = 1;
+       php_http_buffer_t *qstr = php_http_buffer_new();
+
+       php_http_url_argsep(&arg_sep_str, &arg_sep_len TSRMLS_CC);
+
+       if (SUCCESS != php_http_url_encode_hash_ex(hash, qstr, arg_sep_str, arg_sep_len, "=", 1, pre_encoded_str, pre_encoded_len TSRMLS_CC)) {
+               php_http_buffer_free(&qstr);
+               return FAILURE;
+       }
+
+       php_http_buffer_data(qstr, encoded_str, encoded_len);
+       php_http_buffer_free(&qstr);
+
+       return SUCCESS;
+}
+
+ZEND_RESULT_CODE php_http_url_encode_hash_ex(HashTable *hash, php_http_buffer_t *qstr, const char *arg_sep_str, size_t arg_sep_len, const char *val_sep_str, size_t val_sep_len, const char *pre_encoded_str, size_t pre_encoded_len TSRMLS_DC)
+{
+       if (pre_encoded_len && pre_encoded_str) {
+               php_http_buffer_append(qstr, pre_encoded_str, pre_encoded_len);
+       }
+
+       if (!php_http_params_to_string(qstr, hash, arg_sep_str, arg_sep_len, "", 0, val_sep_str, val_sep_len, PHP_HTTP_PARAMS_QUERY TSRMLS_CC)) {
+               return FAILURE;
+       }
+
+       return SUCCESS;
+}
+
+struct parse_state {
+       php_http_url_t url;
+#ifdef ZTS
+       void ***ts;
+#endif
+       const char *ptr;
+       const char *end;
+       size_t maxlen;
+       off_t offset;
+       unsigned flags;
+       char buffer[1]; /* last member */
+};
+
+void php_http_url_free(php_http_url_t **url)
+{
+       if (*url) {
+               efree(*url);
+               *url = NULL;
+       }
+}
+
+php_http_url_t *php_http_url_copy(const php_http_url_t *url, zend_bool persistent)
+{
+       php_http_url_t *cpy;
+       const char *end = NULL, *url_ptr = (const char *) url;
+       char *cpy_ptr;
+
+       end = MAX(url->scheme, end);
+       end = MAX(url->pass, end);
+       end = MAX(url->user, end);
+       end = MAX(url->host, end);
+       end = MAX(url->path, end);
+       end = MAX(url->query, end);
+       end = MAX(url->fragment, end);
+
+       if (end) {
+               end += strlen(end) + 1;
+               cpy_ptr = pecalloc(1, end - url_ptr, persistent);
+               cpy = (php_http_url_t *) cpy_ptr;
+
+               memcpy(cpy_ptr + sizeof(*cpy), url_ptr + sizeof(*url), end - url_ptr - sizeof(*url));
+
+               cpy->scheme = url->scheme ? cpy_ptr + (url->scheme - url_ptr) : NULL;
+               cpy->pass = url->pass ? cpy_ptr + (url->pass - url_ptr) : NULL;
+               cpy->user = url->user ? cpy_ptr + (url->user - url_ptr) : NULL;
+               cpy->host = url->host ? cpy_ptr + (url->host - url_ptr) : NULL;
+               cpy->path = url->path ? cpy_ptr + (url->path - url_ptr) : NULL;
+               cpy->query = url->query ? cpy_ptr + (url->query - url_ptr) : NULL;
+               cpy->fragment = url->fragment ? cpy_ptr + (url->fragment - url_ptr) : NULL;
+       } else {
+               cpy = ecalloc(1, sizeof(*url));
+       }
+
+       cpy->port = url->port;
+
+       return cpy;
+}
+
+static 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);
+
+       if (!consumed || consumed == (size_t) -1) {
+               return 0;
+       }
+
+       if (wc) {
+               *wc = wchar;
+       }
+       return consumed;
+}
+
+#ifdef PHP_HTTP_HAVE_WCHAR
+static size_t parse_mb_loc(unsigned *wc, const char *ptr, const char *end)
+{
+       wchar_t wchar;
+       size_t consumed = 0;
+#if defined(HAVE_MBRTOWC)
+       mbstate_t ps;
+
+       memset(&ps, 0, sizeof(ps));
+       consumed = mbrtowc(&wchar, ptr, end - ptr, &ps);
+#elif defined(HAVE_MBTOWC)
+       consumed = mbtowc(&wchar, ptr, end - ptr);
+#endif
+
+       if (!consumed || consumed == (size_t) -1) {
+               return 0;
+       }
+
+       if (wc) {
+               *wc = wchar;
+       }
+       return consumed;
+}
+#endif
+
+typedef enum parse_mb_what {
+       PARSE_SCHEME,
+       PARSE_USERINFO,
+       PARSE_HOSTINFO,
+       PARSE_PATH,
+       PARSE_QUERY,
+       PARSE_FRAGMENT
+} parse_mb_what_t;
+
+static const char * const parse_what[] = {
+       "scheme",
+       "userinfo",
+       "hostinfo",
+       "path",
+       "query",
+       "fragment"
+};
+
+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 silent)
+{
+       unsigned wchar;
+       size_t consumed = 0;
+
+       if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
+               consumed = parse_mb_utf8(&wchar, ptr, end);
+       }
+#ifdef PHP_HTTP_HAVE_WCHAR
+       else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
+               consumed = parse_mb_loc(&wchar, ptr, end);
+       }
+#endif
+
+       while (consumed) {
+               if (!(state->flags & PHP_HTTP_URL_PARSE_TOPCT) || what == PARSE_HOSTINFO || what == PARSE_SCHEME) {
+                       if (what == PARSE_HOSTINFO && (state->flags & PHP_HTTP_URL_PARSE_TOIDN)) {
+                               /* idna */
+                       } else if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
+                               if (!isualnum(wchar)) {
+                                       break;
+                               }
+#ifdef PHP_HTTP_HAVE_WCHAR
+                       } else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
+                               if (!iswalnum(wchar)) {
+                                       break;
+                               }
+#endif
+                       }
+                       PHP_HTTP_DUFF(consumed, state->buffer[state->offset++] = *ptr++);
+               } 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;
+                       );
+               }
+
+               return consumed;
+       }
+
+       if (!silent) {
+               TSRMLS_FETCH_FROM_CTX(state->ts);
+               if (consumed) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                                       "Failed to parse %s; unexpected multibyte sequence 0x%x at pos %u in '%s'",
+                                       parse_what[what], wchar, (unsigned) (ptr - begin), begin);
+               } else {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                                       "Failed to parse %s; unexpected byte 0x%02x at pos %u in '%s'",
+                                       parse_what[what], (unsigned char) *ptr, (unsigned) (ptr - begin), begin);
+               }
+       }
+
+       return 0;
+}
+
+static ZEND_RESULT_CODE parse_userinfo(struct parse_state *state, const char *ptr)
+{
+       size_t mb;
+       const char *password = NULL, *end = state->ptr, *tmp = ptr;
+       TSRMLS_FETCH_FROM_CTX(state->ts);
+
+       state->url.user = &state->buffer[state->offset];
+
+       do {
+               switch (*ptr) {
+               case ':':
+                       if (password) {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                                               "Failed to parse password; duplicate ':' at pos %u in '%s'",
+                                               (unsigned) (ptr - tmp), tmp);
+                               return FAILURE;
+                       }
+                       password = ptr + 1;
+                       state->buffer[state->offset++] = 0;
+                       state->url.pass = &state->buffer[state->offset];
+                       break;
+
+               case '%':
+                       if (ptr[1] != '%' && (end - ptr <= 2 || !isxdigit(*(ptr+1)) || !isxdigit(*(ptr+2)))) {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                                               "Failed to parse userinfo; invalid percent encoding at pos %u in '%s'",
+                                               (unsigned) (ptr - tmp), tmp);
+                               return FAILURE;
+                       }
+                       state->buffer[state->offset++] = *ptr++;
+                       state->buffer[state->offset++] = *ptr++;
+                       state->buffer[state->offset++] = *ptr;
+                       break;
+
+               case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
+               case '+': case ',': case ';': case '=': /* sub-delims */
+               case '-': case '.': case '_': case '~': /* unreserved */
+               case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+               case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+               case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+               case 'V': case 'W': case 'X': case 'Y': case 'Z':
+               case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+               case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+               case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+               case 'v': case 'w': case 'x': case 'y': case 'z':
+               case '0': case '1': case '2': case '3': case '4': case '5': case '6':
+               case '7': case '8': case '9':
+                       /* allowed */
+                       state->buffer[state->offset++] = *ptr;
+                       break;
+
+               default:
+                       if (!(mb = parse_mb(state, PARSE_USERINFO, ptr, end, tmp, 0))) {
+                               return FAILURE;
+                       }
+                       ptr += mb - 1;
+               }
+       } while(++ptr != end);
+
+
+       state->buffer[state->offset++] = 0;
+
+       return SUCCESS;
+}
+
+#if defined(PHP_WIN32) || defined(HAVE_UIDNA_IDNTOASCII)
+typedef size_t (*parse_mb_func)(unsigned *wc, const char *ptr, const char *end);
+static ZEND_RESULT_CODE to_utf16(parse_mb_func fn, const char *u8, uint16_t **u16, size_t *len TSRMLS_DC)
+{
+       size_t offset = 0, u8_len = strlen(u8);
+
+       *u16 = ecalloc(4 * sizeof(uint16_t), u8_len + 1);
+       *len = 0;
+
+       while (offset < u8_len) {
+               unsigned wc;
+               uint16_t buf[2], *ptr = buf;
+               size_t consumed = fn(&wc, &u8[offset], &u8[u8_len]);
+
+               if (!consumed) {
+                       efree(*u16);
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse UTF-8 at pos %zu of '%s'", offset, u8);
+                       return FAILURE;
+               } else {
+                       offset += consumed;
+               }
+
+               switch (wctoutf16(buf, wc)) {
+               case 2:
+                       (*u16)[(*len)++] = *ptr++;
+                       /* no break */
+               case 1:
+                       (*u16)[(*len)++] = *ptr++;
+                       break;
+               case 0:
+               default:
+                       efree(*u16);
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to convert UTF-32 'U+%X' to UTF-16", wc);
+                       return FAILURE;
+               }
+       }
+
+       return SUCCESS;
+}
+#endif
+
+#ifndef MAXHOSTNAMELEN
+#      define MAXHOSTNAMELEN 256
+#endif
+
+#if PHP_HTTP_HAVE_IDN2
+static ZEND_RESULT_CODE parse_idn2(struct parse_state *state, size_t prev_len)
+{
+       char *idn = NULL;
+       int rv = -1;
+       TSRMLS_FETCH_FROM_CTX(state->ts);
+
+       if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
+               rv = idn2_lookup_u8((const unsigned char *) state->url.host, (unsigned char **) &idn, IDN2_NFC_INPUT);
+       }
+#      ifdef PHP_HTTP_HAVE_WCHAR
+       else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
+               rv = idn2_lookup_ul(state->url.host, &idn, 0);
+       }
+#      endif
+       if (rv != IDN2_OK) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN; %s", idn2_strerror(rv));
+               return FAILURE;
+       } else {
+               size_t idnlen = strlen(idn);
+               memcpy(state->url.host, idn, idnlen + 1);
+               free(idn);
+               state->offset += idnlen - prev_len;
+               return SUCCESS;
+       }
+}
+#elif PHP_HTTP_HAVE_IDN
+static ZEND_RESULT_CODE parse_idn(struct parse_state *state, size_t prev_len)
+{
+       char *idn = NULL;
+       int rv = -1;
+       TSRMLS_FETCH_FROM_CTX(state->ts);
+
+       if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
+               rv = idna_to_ascii_8z(state->url.host, &idn, IDNA_ALLOW_UNASSIGNED|IDNA_USE_STD3_ASCII_RULES);
+       }
+#      ifdef PHP_HTTP_HAVE_WCHAR
+       else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
+               rv = idna_to_ascii_lz(state->url.host, &idn, IDNA_ALLOW_UNASSIGNED|IDNA_USE_STD3_ASCII_RULES);
+       }
+#      endif
+       if (rv != IDNA_SUCCESS) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN; %s", idna_strerror(rv));
+               return FAILURE;
+       } else {
+               size_t idnlen = strlen(idn);
+               memcpy(state->url.host, idn, idnlen + 1);
+               free(idn);
+               state->offset += idnlen - prev_len;
+               return SUCCESS;
+       }
+}
+#endif
+
+#ifdef HAVE_UIDNA_IDNTOASCII
+#      if HAVE_UNICODE_UIDNA_H
+#              include <unicode/uidna.h>
+#      else
+typedef uint16_t UChar;
+typedef enum { U_ZERO_ERROR = 0 } UErrorCode;
+int32_t uidna_IDNToASCII(const UChar *src, int32_t srcLength, UChar *dest, int32_t destCapacity, int32_t options, void *parseError, UErrorCode *status);
+#      endif
+static ZEND_RESULT_CODE parse_uidn(struct parse_state *state)
+{
+       char *host_ptr;
+       uint16_t *uhost_str, ahost_str[MAXHOSTNAMELEN], *ahost_ptr;
+       size_t uhost_len, ahost_len;
+       UErrorCode error = U_ZERO_ERROR;
+       TSRMLS_FETCH_FROM_CTX(state->ts);
+
+       if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
+               if (SUCCESS != to_utf16(parse_mb_utf8, state->url.host, &uhost_str, &uhost_len TSRMLS_CC)) {
+                       return FAILURE;
+               }
+#ifdef PHP_HTTP_HAVE_WCHAR
+       } else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
+               if (SUCCESS != to_utf16(parse_mb_loc, state->url.host, &uhost_str, &uhost_len TSRMLS_CC)) {
+                       return FAILURE;
+               }
+#endif
+       } else {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN; codepage not specified");
+               return FAILURE;
+       }
+
+       ahost_len = uidna_IDNToASCII(uhost_str, uhost_len, ahost_str, MAXHOSTNAMELEN, 3, NULL, &error);
+       efree(uhost_str);
+
+       if (error != U_ZERO_ERROR) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN; ICU error %d", error);
+               return FAILURE;
+       }
+
+       host_ptr = state->url.host;
+       ahost_ptr = ahost_str;
+       PHP_HTTP_DUFF(ahost_len, *host_ptr++ = *ahost_ptr++);
+
+       *host_ptr = '\0';
+       state->offset += host_ptr - state->url.host;
+
+       return SUCCESS;
+}
+#endif
+
+#if 0 && defined(PHP_WIN32)
+static ZEND_RESULT_CODE parse_widn(struct parse_state *state)
+{
+       char *host_ptr;
+       uint16_t *uhost_str, ahost_str[MAXHOSTNAMELEN], *ahost_ptr;
+       size_t uhost_len;
+       TSRMLS_FETCH_FROM_CTX(state->ts);
+
+       if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
+               if (SUCCESS != to_utf16(parse_mb_utf8, state->url.host, &uhost_str, &uhost_len)) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN");
+                       return FAILURE;
+               }
+#ifdef PHP_HTTP_HAVE_WCHAR
+       } else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
+               if (SUCCESS != to_utf16(parse_mb_loc, state->url.host, &uhost_str, &uhost_len)) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN");
+                       return FAILURE;
+               }
+#endif
+       } else {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN");
+               return FAILURE;
+       }
+
+       if (!IdnToAscii(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES, uhost_str, uhost_len, ahost_str, MAXHOSTNAMELEN)) {
+               efree(uhost_str);
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN");
+               return FAILURE;
+       }
+
+       efree(uhost_str);
+       host_ptr = state->url.host;
+       ahost_ptr = ahost_str;
+       PHP_HTTP_DUFF(wcslen(ahost_str), *host_ptr++ = *ahost_ptr++);
+       efree(ahost_str);
+
+       *host_ptr = '\0';
+       state->offset += host_ptr - state->url.host;
+
+       return SUCCESS;
+}
+#endif
+
+#ifdef HAVE_INET_PTON
+static const char *parse_ip6(struct parse_state *state, const char *ptr)
+{
+       const char *error = NULL, *end = state->ptr, *tmp = memchr(ptr, ']', end - ptr);
+       TSRMLS_FETCH_FROM_CTX(state->ts);
+
+       if (tmp) {
+               size_t addrlen = tmp - ptr + 1;
+               char buf[16], *addr = estrndup(ptr + 1, addrlen - 2);
+               int rv = inet_pton(AF_INET6, addr, buf);
+
+               if (rv == 1) {
+                       state->buffer[state->offset] = '[';
+                       state->url.host = &state->buffer[state->offset];
+                       inet_ntop(AF_INET6, buf, state->url.host + 1, state->maxlen - state->offset);
+                       state->offset += strlen(state->url.host);
+                       state->buffer[state->offset++] = ']';
+                       state->buffer[state->offset++] = 0;
+                       ptr = tmp + 1;
+               } else if (rv == -1) {
+                       error = strerror(errno);
+               } else {
+                       error = "unexpected '['";
+               }
+               efree(addr);
+       } else {
+               error = "expected ']'";
+       }
+
+       if (error) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse hostinfo; %s", error);
+               return NULL;
+       }
+
+       return ptr;
+}
+#endif
+
+static ZEND_RESULT_CODE parse_hostinfo(struct parse_state *state, const char *ptr)
+{
+       size_t mb, len;
+       const char *end = state->ptr, *tmp = ptr, *port = NULL, *label = NULL;
+       TSRMLS_FETCH_FROM_CTX(state->ts);
+
+#ifdef HAVE_INET_PTON
+       if (*ptr == '[' && !(ptr = parse_ip6(state, ptr))) {
+               return FAILURE;
+       }
+#endif
+
+       if (ptr != end) do {
+               switch (*ptr) {
+               case ':':
+                       if (port) {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                                               "Failed to parse port; unexpected ':' at pos %u in '%s'",
+                                               (unsigned) (ptr - tmp), tmp);
+                               return FAILURE;
+                       }
+                       port = ptr + 1;
+                       break;
+
+               case '%':
+                       if (ptr[1] != '%' && (end - ptr <= 2 || !isxdigit(*(ptr+1)) || !isxdigit(*(ptr+2)))) {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                                               "Failed to parse hostinfo; invalid percent encoding at pos %u in '%s'",
+                                               (unsigned) (ptr - tmp), tmp);
+                               return FAILURE;
+                       }
+                       state->buffer[state->offset++] = *ptr++;
+                       state->buffer[state->offset++] = *ptr++;
+                       state->buffer[state->offset++] = *ptr;
+                       break;
+
+               case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
+               case '+': case ',': case ';': case '=': /* sub-delims */
+               case '-': case '.': case '_': case '~': /* unreserved */
+                       if (port || !label) {
+                               /* sort of a compromise, just ensure we don't end up
+                                * with a dot at the beginning or two consecutive dots
+                                */
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                                               "Failed to parse %s; unexpected '%c' at pos %u in '%s'",
+                                               port ? "port" : "host",
+                                               (unsigned char) *ptr, (unsigned) (ptr - tmp), tmp);
+                               return FAILURE;
+                       }
+                       state->buffer[state->offset++] = *ptr;
+                       label = NULL;
+                       break;
+
+               case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+               case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+               case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+               case 'V': case 'W': case 'X': case 'Y': case 'Z':
+               case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+               case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+               case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+               case 'v': case 'w': case 'x': case 'y': case 'z':
+                       if (port) {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                                               "Failed to parse port; unexpected char '%c' at pos %u in '%s'",
+                                               (unsigned char) *ptr, (unsigned) (ptr - tmp), tmp);
+                               return FAILURE;
+                       }
+                       /* no break */
+               case '0': case '1': case '2': case '3': case '4': case '5': case '6':
+               case '7': case '8': case '9':
+                       /* allowed */
+                       if (port) {
+                               state->url.port *= 10;
+                               state->url.port += *ptr - '0';
+                       } else {
+                               label = ptr;
+                               state->buffer[state->offset++] = *ptr;
+                       }
+                       break;
+
+               default:
+                       if (ptr == end) {
+                               break;
+                       } else if (port) {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                                               "Failed to parse port; unexpected byte 0x%02x at pos %u in '%s'",
+                                               (unsigned char) *ptr, (unsigned) (ptr - tmp), tmp);
+                               return FAILURE;
+                       } else if (!(mb = parse_mb(state, PARSE_HOSTINFO, ptr, end, tmp, 0))) {
+                               return FAILURE;
+                       }
+                       label = ptr;
+                       ptr += mb - 1;
+               }
+       } while (++ptr != end);
+
+       if (!state->url.host) {
+               len = (port ? port - tmp - 1 : end - tmp);
+               state->url.host = &state->buffer[state->offset - len];
+               state->buffer[state->offset++] = 0;
+       }
+
+       if (state->flags & PHP_HTTP_URL_PARSE_TOIDN) {
+#if PHP_HTTP_HAVE_IDN2
+               return parse_idn2(state, len);
+#elif PHP_HTTP_HAVE_IDN
+               return parse_idn(state, len);
+#endif
+#ifdef HAVE_UIDNA_IDNTOASCII
+               return parse_uidn(state);
+#endif
+#if 0 && defined(PHP_WIN32)
+               return parse_widn(state);
+#endif
+       }
+
+       return SUCCESS;
+}
+
+static const char *parse_authority(struct parse_state *state)
+{
+       const char *tmp = state->ptr, *host = NULL;
+
+       do {
+               switch (*state->ptr) {
+               case '@':
+                       /* userinfo delimiter */
+                       if (host) {
+                               TSRMLS_FETCH_FROM_CTX(state->ts);
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                                               "Failed to parse userinfo; unexpected '@'");
+                               return NULL;
+                       }
+                       host = state->ptr + 1;
+                       if (tmp != state->ptr && SUCCESS != parse_userinfo(state, tmp)) {
+                               return NULL;
+                       }
+                       tmp = state->ptr + 1;
+                       break;
+
+               case '/':
+               case '?':
+               case '#':
+               case '\0':
+                       EOD:
+                       /* host delimiter */
+                       if (tmp != state->ptr && SUCCESS != parse_hostinfo(state, tmp)) {
+                               return NULL;
+                       }
+                       return state->ptr;
+               }
+       } while (++state->ptr <= state->end);
+
+       --state->ptr;
+       goto EOD;
+}
+
+static const char *parse_path(struct parse_state *state)
+{
+       size_t mb;
+       const char *tmp;
+       TSRMLS_FETCH_FROM_CTX(state->ts);
+
+       /* is there actually a path to parse? */
+       if (!*state->ptr) {
+               return state->ptr;
+       }
+       tmp = state->ptr;
+       state->url.path = &state->buffer[state->offset];
+
+       do {
+               switch (*state->ptr) {
+               case '#':
+               case '?':
+                       goto done;
+
+               case '%':
+                       if (state->ptr[1] != '%' && (state->end - state->ptr <= 2 || !isxdigit(*(state->ptr+1)) || !isxdigit(*(state->ptr+2)))) {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                                               "Failed to parse path; invalid percent encoding at pos %u in '%s'",
+                                               (unsigned) (state->ptr - tmp), tmp);
+                               return NULL;
+                       }
+                       state->buffer[state->offset++] = *state->ptr++;
+                       state->buffer[state->offset++] = *state->ptr++;
+                       state->buffer[state->offset++] = *state->ptr;
+                       break;
+
+               case '/': /* yeah, well */
+               case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
+               case '+': case ',': case ';': case '=': /* sub-delims */
+               case '-': case '.': case '_': case '~': /* unreserved */
+               case ':': case '@': /* pchar */
+               case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+               case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+               case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+               case 'V': case 'W': case 'X': case 'Y': case 'Z':
+               case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+               case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+               case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+               case 'v': case 'w': case 'x': case 'y': case 'z':
+               case '0': case '1': case '2': case '3': case '4': case '5': case '6':
+               case '7': case '8': case '9':
+                       /* allowed */
+                       state->buffer[state->offset++] = *state->ptr;
+                       break;
+
+               default:
+                       if (!(mb = parse_mb(state, PARSE_PATH, state->ptr, state->end, tmp, 0))) {
+                               return NULL;
+                       }
+                       state->ptr += mb - 1;
+               }
+       } while (++state->ptr < state->end);
+
+       done:
+       /* did we have any path component ? */
+       if (tmp != state->ptr) {
+               state->buffer[state->offset++] = 0;
+       } else {
+               state->url.path = NULL;
+       }
+       return state->ptr;
+}
+
+static const char *parse_query(struct parse_state *state)
+{
+       size_t mb;
+       const char *tmp = state->ptr + !!*state->ptr;
+       TSRMLS_FETCH_FROM_CTX(state->ts);
+
+       /* is there actually a query to parse? */
+       if (*state->ptr != '?') {
+               return state->ptr;
+       }
+
+       /* skip initial '?' */
+       tmp = ++state->ptr;
+       state->url.query = &state->buffer[state->offset];
+
+       while (state->ptr < state->end) {
+               switch (*state->ptr) {
+               case '#':
+                       goto done;
+
+               case '%':
+                       if (state->ptr[1] != '%' && (state->end - state->ptr <= 2 || !isxdigit(*(state->ptr+1)) || !isxdigit(*(state->ptr+2)))) {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                                               "Failed to parse query; invalid percent encoding at pos %u in '%s'",
+                                               (unsigned) (state->ptr - tmp), tmp);
+                               return NULL;
+                       }
+                       state->buffer[state->offset++] = *state->ptr++;
+                       state->buffer[state->offset++] = *state->ptr++;
+                       state->buffer[state->offset++] = *state->ptr;
+                       break;
+
+               /* RFC1738 unsafe */
+               case '{': case '}':
+               case '<': case '>':
+               case '[': case ']':
+               case '|': case '\\': case '^': case '`': case '"': case ' ':
+                       if (state->flags & PHP_HTTP_URL_PARSE_TOPCT) {
+                               state->buffer[state->offset++] = '%';
+                               state->buffer[state->offset++] = parse_xdigits[((unsigned char) *state->ptr) >> 4];
+                               state->buffer[state->offset++] = parse_xdigits[((unsigned char) *state->ptr) & 0xf];
+                               break;
+                       }
+                       /* no break */
+
+               case '?': case '/': /* yeah, well */
+               case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
+               case '+': case ',': case ';': case '=': /* sub-delims */
+               case '-': case '.': case '_': case '~': /* unreserved */
+               case ':': case '@': /* pchar */
+               case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+               case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+               case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+               case 'V': case 'W': case 'X': case 'Y': case 'Z':
+               case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+               case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+               case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+               case 'v': case 'w': case 'x': case 'y': case 'z':
+               case '0': case '1': case '2': case '3': case '4': case '5': case '6':
+               case '7': case '8': case '9':
+                       /* allowed */
+                       state->buffer[state->offset++] = *state->ptr;
+                       break;
+
+               default:
+                       if (!(mb = parse_mb(state, PARSE_QUERY, state->ptr, state->end, tmp, 0))) {
+                               return NULL;
+                       }
+                       state->ptr += mb - 1;
+               }
+
+               ++state->ptr;
+       }
+
+       done:
+       state->buffer[state->offset++] = 0;
+       return state->ptr;
+}
+
+static const char *parse_fragment(struct parse_state *state)
+{
+       size_t mb;
+       const char *tmp;
+       TSRMLS_FETCH_FROM_CTX(state->ts);
+
+       /* is there actually a fragment to parse? */
+       if (*state->ptr != '#') {
+               return state->ptr;
+       }
+
+       /* skip initial '#' */
+       tmp = ++state->ptr;
+       state->url.fragment = &state->buffer[state->offset];
+
+       do {
+               switch (*state->ptr) {
+               case '%':
+                       if (state->ptr[1] != '%' && (state->end - state->ptr <= 2 || !isxdigit(*(state->ptr+1)) || !isxdigit(*(state->ptr+2)))) {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                                               "Failed to parse fragment; invalid percent encoding at pos %u in '%s'",
+                                               (unsigned) (state->ptr - tmp), tmp);
+                               return NULL;
+                       }
+                       state->buffer[state->offset++] = *state->ptr++;
+                       state->buffer[state->offset++] = *state->ptr++;
+                       state->buffer[state->offset++] = *state->ptr;
+                       break;
+
+               /* RFC1738 unsafe */
+               case '{': case '}':
+               case '<': case '>':
+               case '[': case ']':
+               case '|': case '\\': case '^': case '`': case '"': case ' ':
+                       if (state->flags & PHP_HTTP_URL_PARSE_TOPCT) {
+                               state->buffer[state->offset++] = '%';
+                               state->buffer[state->offset++] = parse_xdigits[((unsigned char) *state->ptr) >> 4];
+                               state->buffer[state->offset++] = parse_xdigits[((unsigned char) *state->ptr) & 0xf];
+                               break;
+                       }
+                       /* no break */
+
+               case '?': case '/':
+               case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
+               case '+': case ',': case ';': case '=': /* sub-delims */
+               case '-': case '.': case '_': case '~': /* unreserved */
+               case ':': case '@': /* pchar */
+               case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+               case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+               case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+               case 'V': case 'W': case 'X': case 'Y': case 'Z':
+               case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+               case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+               case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+               case 'v': case 'w': case 'x': case 'y': case 'z':
+               case '0': case '1': case '2': case '3': case '4': case '5': case '6':
+               case '7': case '8': case '9':
+                       /* allowed */
+                       state->buffer[state->offset++] = *state->ptr;
+                       break;
+
+               default:
+                       if (!(mb = parse_mb(state, PARSE_FRAGMENT, state->ptr, state->end, tmp, 0))) {
+                               return NULL;
+                       }
+                       state->ptr += mb - 1;
+               }
+       } while (++state->ptr < state->end);
+
+       state->buffer[state->offset++] = 0;
+       return state->ptr;
+}
+
+static const char *parse_hier(struct parse_state *state)
+{
+       if (*state->ptr == '/') {
+               if (state->end - state->ptr > 1) {
+                       if (*(state->ptr + 1) == '/') {
+                               state->ptr += 2;
+                               if (!(state->ptr = parse_authority(state))) {
+                                       return NULL;
+                               }
+                       }
+               }
+       }
+       return parse_path(state);
+}
+
+static const char *parse_scheme(struct parse_state *state)
+{
+       size_t mb;
+       const char *tmp = state->ptr;
+
+       do {
+               switch (*state->ptr) {
+               case ':':
+                       /* scheme delimiter */
+                       state->url.scheme = &state->buffer[0];
+                       state->buffer[state->offset++] = 0;
+                       return ++state->ptr;
+
+               case '0': case '1': case '2': case '3': case '4': case '5': case '6':
+               case '7': case '8': case '9':
+               case '+': case '-': case '.':
+                       if (state->ptr == tmp) {
+                               return tmp;
+                       }
+                       /* no break */
+               case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+               case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+               case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+               case 'V': case 'W': case 'X': case 'Y': case 'Z':
+               case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+               case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+               case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+               case 'v': case 'w': case 'x': case 'y': case 'z':
+                       /* scheme part */
+                       state->buffer[state->offset++] = *state->ptr;
+                       break;
+
+               default:
+                       if (!(mb = parse_mb(state, PARSE_SCHEME, state->ptr, state->end, tmp, 1))) {
+                               /* soft fail; parse path next */
+                               return tmp;
+                       }
+                       state->ptr += mb - 1;
+               }
+       } while (++state->ptr != state->end);
+
+       return state->ptr = tmp;
+}
+
+php_http_url_t *php_http_url_parse(const char *str, size_t len, unsigned flags TSRMLS_DC)
+{
+       size_t maxlen = 3 * len;
+       struct parse_state *state = ecalloc(1, sizeof(*state) + maxlen);
+
+       state->end = str + len;
+       state->ptr = str;
+       state->flags = flags;
+       state->maxlen = maxlen;
+       TSRMLS_SET_CTX(state->ts);
+
+       if (!parse_scheme(state)) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse URL scheme: '%s'", state->ptr);
+               efree(state);
+               return NULL;
+       }
+
+       if (!parse_hier(state)) {
+               efree(state);
+               return NULL;
+       }
+
+       if (!parse_query(state)) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse URL query: '%s'", state->ptr);
+               efree(state);
+               return NULL;
+       }
+
+       if (!parse_fragment(state)) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse URL fragment: '%s'", state->ptr);
+               efree(state);
+               return NULL;
+       }
+
+       return (php_http_url_t *) state;
+}
+
+php_http_url_t *php_http_url_parse_authority(const char *str, size_t len, unsigned flags TSRMLS_DC)
+{
+       size_t maxlen = 3 * len;
+       struct parse_state *state = ecalloc(1, sizeof(*state) + maxlen);
+
+       state->end = str + len;
+       state->ptr = str;
+       state->flags = flags;
+       state->maxlen = maxlen;
+       TSRMLS_SET_CTX(state->ts);
+
+       if (!(state->ptr = parse_authority(state))) {
+               efree(state);
+               return NULL;
+       }
+
+       if (state->ptr != state->end) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                               "Failed to parse URL authority, unexpected character at pos %u in '%s'",
+                               (unsigned) (state->ptr - str), str);
+               efree(state);
+               return NULL;
+       }
+
+       return (php_http_url_t *) state;
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl___construct, 0, 0, 0)
+       ZEND_ARG_INFO(0, old_url)
+       ZEND_ARG_INFO(0, new_url)
+       ZEND_ARG_INFO(0, flags)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpUrl, __construct)
+{
+       zval *new_url = NULL, *old_url = NULL;
+       long flags = PHP_HTTP_URL_FROM_ENV;
+       zend_error_handling zeh;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!z!l", &old_url, &new_url, &flags), invalid_arg, return);
+
+       zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC);
+       {
+               php_http_url_t *res_purl, *new_purl = NULL, *old_purl = NULL;
+
+               if (new_url) {
+                       new_purl = php_http_url_from_zval(new_url, flags TSRMLS_CC);
+                       if (!new_purl) {
+                               zend_restore_error_handling(&zeh TSRMLS_CC);
+                               return;
+                       }
+               }
+               if (old_url) {
+                       old_purl = php_http_url_from_zval(old_url, flags TSRMLS_CC);
+                       if (!old_purl) {
+                               if (new_purl) {
+                                       php_http_url_free(&new_purl);
+                               }
+                               zend_restore_error_handling(&zeh TSRMLS_CC);
+                               return;
+                       }
+               }
+
+               res_purl = php_http_url_mod(old_purl, new_purl, flags TSRMLS_CC);
+               php_http_url_to_struct(res_purl, getThis() TSRMLS_CC);
+
+               php_http_url_free(&res_purl);
+               if (old_purl) {
+                       php_http_url_free(&old_purl);
+               }
+               if (new_purl) {
+                       php_http_url_free(&new_purl);
+               }
+       }
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_mod, 0, 0, 1)
+       ZEND_ARG_INFO(0, more_url_parts)
+       ZEND_ARG_INFO(0, flags)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpUrl, mod)
+{
+       zval *new_url = NULL;
+       long flags = PHP_HTTP_URL_JOIN_PATH | PHP_HTTP_URL_JOIN_QUERY | PHP_HTTP_URL_SANITIZE_PATH;
+       zend_error_handling zeh;
+
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z!|l", &new_url, &flags), invalid_arg, return);
+
+       zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC);
+       {
+               php_http_url_t *new_purl = NULL, *old_purl = NULL;
+
+               if (new_url) {
+                       new_purl = php_http_url_from_zval(new_url, flags TSRMLS_CC);
+                       if (!new_purl) {
+                               zend_restore_error_handling(&zeh TSRMLS_CC);
+                               return;
+                       }
+               }
+
+               if ((old_purl = php_http_url_from_struct(HASH_OF(getThis())))) {
+                       php_http_url_t *res_purl;
+
+                       ZVAL_OBJVAL(return_value, zend_objects_clone_obj(getThis() TSRMLS_CC), 0);
+
+                       res_purl = php_http_url_mod(old_purl, new_purl, flags TSRMLS_CC);
+                       php_http_url_to_struct(res_purl, return_value TSRMLS_CC);
+
+                       php_http_url_free(&res_purl);
+                       php_http_url_free(&old_purl);
+               }
+               if (new_purl) {
+                       php_http_url_free(&new_purl);
+               }
+       }
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_toString, 0, 0, 0)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpUrl, toString)
+{
+       if (SUCCESS == zend_parse_parameters_none()) {
+               php_http_url_t *purl;
+
+               if ((purl = php_http_url_from_struct(HASH_OF(getThis())))) {
+                       char *str;
+                       size_t len;
+
+                       php_http_url_to_string(purl, &str, &len, 0);
+                       php_http_url_free(&purl);
+                       RETURN_STRINGL(str, len, 0);
+               }
+       }
+       RETURN_EMPTY_STRING();
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_toArray, 0, 0, 0)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpUrl, toArray)
+{
+       php_http_url_t *purl;
+
+       if (SUCCESS != zend_parse_parameters_none()) {
+               return;
+       }
+
+       /* strip any non-URL properties */
+       purl = php_http_url_from_struct(HASH_OF(getThis()));
+       php_http_url_to_struct(purl, return_value TSRMLS_CC);
+       php_http_url_free(&purl);
+}
+
+static zend_function_entry php_http_url_methods[] = {
+       PHP_ME(HttpUrl, __construct,  ai_HttpUrl___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+       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)
+       PHP_ME(HttpUrl, toArray,      ai_HttpUrl_toArray, ZEND_ACC_PUBLIC)
+       EMPTY_FUNCTION_ENTRY
+};
+
+zend_class_entry *php_http_url_class_entry;
+
+PHP_MINIT_FUNCTION(http_url)
+{
+       zend_class_entry ce = {0};
+
+       INIT_NS_CLASS_ENTRY(ce, "http", "Url", php_http_url_methods);
+       php_http_url_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
+
+       zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("scheme"), ZEND_ACC_PUBLIC TSRMLS_CC);
+       zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("user"), ZEND_ACC_PUBLIC TSRMLS_CC);
+       zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("pass"), ZEND_ACC_PUBLIC TSRMLS_CC);
+       zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("host"), ZEND_ACC_PUBLIC TSRMLS_CC);
+       zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("port"), ZEND_ACC_PUBLIC TSRMLS_CC);
+       zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("path"), ZEND_ACC_PUBLIC TSRMLS_CC);
+       zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("query"), ZEND_ACC_PUBLIC TSRMLS_CC);
+       zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("fragment"), ZEND_ACC_PUBLIC TSRMLS_CC);
+
+       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("REPLACE"), PHP_HTTP_URL_REPLACE TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("JOIN_PATH"), PHP_HTTP_URL_JOIN_PATH TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("JOIN_QUERY"), PHP_HTTP_URL_JOIN_QUERY TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_USER"), PHP_HTTP_URL_STRIP_USER TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_PASS"), PHP_HTTP_URL_STRIP_PASS TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_AUTH"), PHP_HTTP_URL_STRIP_AUTH TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_PORT"), PHP_HTTP_URL_STRIP_PORT TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_PATH"), PHP_HTTP_URL_STRIP_PATH TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_QUERY"), PHP_HTTP_URL_STRIP_QUERY TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_FRAGMENT"), PHP_HTTP_URL_STRIP_FRAGMENT TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_ALL"), PHP_HTTP_URL_STRIP_ALL TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("FROM_ENV"), PHP_HTTP_URL_FROM_ENV TSRMLS_CC);
+       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("SANITIZE_PATH"), PHP_HTTP_URL_SANITIZE_PATH TSRMLS_CC);
+
+#ifdef PHP_HTTP_HAVE_WCHAR
+       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_MBLOC"), PHP_HTTP_URL_PARSE_MBLOC TSRMLS_CC);
+#endif
+       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_MBUTF8"), PHP_HTTP_URL_PARSE_MBUTF8 TSRMLS_CC);
+#if defined(PHP_HTTP_HAVE_IDN2) || defined(PHP_HTTP_HAVE_IDN) || defined(HAVE_UIDNA_IDNTOASCII)
+       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_TOIDN"), PHP_HTTP_URL_PARSE_TOIDN TSRMLS_CC);
+#endif
+       zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_TOPCT"), PHP_HTTP_URL_PARSE_TOPCT TSRMLS_CC);
+
+       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_url.h b/src/php_http_url.h
new file mode 100644 (file)
index 0000000..636efb5
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_URL_H
+#define PHP_HTTP_URL_H
+
+#include <ext/standard/url.h>
+
+/* php_http_url_mod() */
+#define PHP_HTTP_URL_REPLACE           0x000
+#define PHP_HTTP_URL_JOIN_PATH         0x001
+#define PHP_HTTP_URL_JOIN_QUERY                0x002
+#define PHP_HTTP_URL_STRIP_USER                0x004
+#define PHP_HTTP_URL_STRIP_PASS                0x008
+#define PHP_HTTP_URL_STRIP_AUTH                (PHP_HTTP_URL_STRIP_USER|PHP_HTTP_URL_STRIP_PASS)
+#define PHP_HTTP_URL_STRIP_PORT                0x020
+#define PHP_HTTP_URL_STRIP_PATH                0x040
+#define PHP_HTTP_URL_STRIP_QUERY       0x080
+#define PHP_HTTP_URL_STRIP_FRAGMENT    0x100
+#define PHP_HTTP_URL_STRIP_ALL ( \
+       PHP_HTTP_URL_STRIP_AUTH | \
+       PHP_HTTP_URL_STRIP_PORT | \
+       PHP_HTTP_URL_STRIP_PATH | \
+       PHP_HTTP_URL_STRIP_QUERY | \
+       PHP_HTTP_URL_STRIP_FRAGMENT \
+)
+#define PHP_HTTP_URL_FROM_ENV          0x1000
+#define PHP_HTTP_URL_SANITIZE_PATH     0x2000
+
+/* parse multibyte according to locale */
+#define PHP_HTTP_URL_PARSE_MBLOC       0x10000
+/* parse utf8 multibyte sequences */
+#define PHP_HTTP_URL_PARSE_MBUTF8      0x20000
+/* convert multibyte hostnames to IDNA */
+#define PHP_HTTP_URL_PARSE_TOIDN       0x100000
+/* percent encode multibyte sequences in userinfo, path, query and fragment */
+#define PHP_HTTP_URL_PARSE_TOPCT       0x200000
+
+typedef struct php_http_url {
+       /* compatible to php_url, but do not use php_url_free() */
+       char *scheme;
+       char *user;
+       char *pass;
+       char *host;
+       unsigned short port;
+       char *path;
+       char *query;
+       char *fragment;
+} php_http_url_t;
+
+PHP_HTTP_API php_http_url_t *php_http_url_parse(const char *str, size_t len, unsigned flags TSRMLS_DC);
+PHP_HTTP_API php_http_url_t *php_http_url_parse_authority(const char *str, size_t len, unsigned flags TSRMLS_DC);
+PHP_HTTP_API php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_url_t *new_url, unsigned flags TSRMLS_DC);
+PHP_HTTP_API php_http_url_t *php_http_url_copy(const php_http_url_t *url, zend_bool persistent);
+PHP_HTTP_API php_http_url_t *php_http_url_from_struct(HashTable *ht);
+PHP_HTTP_API php_http_url_t *php_http_url_from_zval(zval *value, unsigned flags TSRMLS_DC);
+PHP_HTTP_API HashTable *php_http_url_to_struct(const php_http_url_t *url, zval *strct TSRMLS_DC);
+PHP_HTTP_API char *php_http_url_to_string(const php_http_url_t *url, char **url_str, size_t *url_len, zend_bool persistent);
+PHP_HTTP_API char *php_http_url_authority_to_string(const php_http_url_t *url, char **url_str, size_t *url_len);
+PHP_HTTP_API void php_http_url_free(php_http_url_t **url);
+
+PHP_HTTP_API ZEND_RESULT_CODE php_http_url_encode_hash(HashTable *hash, const char *pre_encoded_str, size_t pre_encoded_len, char **encoded_str, size_t *encoded_len TSRMLS_DC);
+PHP_HTTP_API ZEND_RESULT_CODE php_http_url_encode_hash_ex(HashTable *hash, php_http_buffer_t *qstr, const char *arg_sep_str, size_t arg_sep_len, const char *val_sep_str, size_t val_sep_len, const char *pre_encoded_str, size_t pre_encoded_len TSRMLS_DC);
+
+static inline void php_http_url_argsep(const char **str, size_t *len TSRMLS_DC)
+{
+       php_http_ini_entry(ZEND_STRL("arg_separator.output"), str, len, 0 TSRMLS_CC);
+}
+
+static inline zend_bool php_http_url_is_empty(const php_http_url_t *url) {
+       return !(url->scheme || url->pass || url->user || url->host || url->port ||     url->path || url->query || url->fragment);
+}
+
+PHP_HTTP_API zend_class_entry *php_http_url_class_entry;
+PHP_MINIT_FUNCTION(http_url);
+
+#define php_http_url_object_new php_http_object_new
+#define php_http_url_object_new_ex php_http_object_new_ex
+
+#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_utf8.h b/src/php_http_utf8.h
new file mode 100644 (file)
index 0000000..f900803
--- /dev/null
@@ -0,0 +1,670 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_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,
+    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,
+    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,
+    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,
+    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,
+    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,
+    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+    3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+    4,4,4,4,4,4,4,4,5,5,5,5,6,6,6,6
+};
+
+static const unsigned char utf8_mask[] = {
+               0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01
+};
+
+static const utf8_range_t utf8_ranges[] = {
+/* 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,     0x0CCD, 1},
+       {    0x0CD5,     0x0CD6, 1},
+       {    0x0CDE,          0, 0},
+       {    0x0CE0,     0x0CE3, 1},
+       {    0x0CF1,          0, 0},
+       {    0x0CF2,          0, 0},
+       {    0x0D02,     0x0D03, 1},
+       {    0x0D05,     0x0D0C, 1},
+       {    0x0D0E,     0x0D10, 1},
+       {    0x0D12,     0x0D28, 1},
+       {    0x0D2A,     0x0D39, 1},
+       {    0x0D3D,     0x0D44, 1},
+       {    0x0D46,     0x0D48, 1},
+       {    0x0D4A,     0x0D4D, 1},
+       {    0x0D57,          0, 0},
+       {    0x0D60,     0x0D63, 1},
+       {    0x0D79,     0x0D7F, 1},
+       {    0x0D82,     0x0D83, 1},
+       {    0x0D85,     0x0D96, 1},
+       {    0x0D9A,     0x0DB1, 1},
+       {    0x0DB3,     0x0DBB, 1},
+       {    0x0DBD,          0, 0},
+       {    0x0DC0,     0x0DC6, 1},
+       {    0x0DCA,          0, 0},
+       {    0x0DCF,     0x0DD4, 1},
+       {    0x0DD6,          0, 0},
+       {    0x0DD8,     0x0DDF, 1},
+       {    0x0DF2,     0x0DF4, 1},
+       {    0x0E01,     0x0E2E, 1},
+       {    0x0E30,     0x0E3A, 1},
+       {    0x0E40,     0x0E45, 1},
+       {    0x0E47,     0x0E4E, 1},
+       {    0x0E81,     0x0E82, 1},
+       {    0x0E84,          0, 0},
+       {    0x0E87,     0x0E88, 1},
+       {    0x0E8A,          0, 0},
+       {    0x0E8D,          0, 0},
+       {    0x0E94,     0x0E97, 1},
+       {    0x0E99,     0x0E9F, 1},
+       {    0x0EA1,     0x0EA3, 1},
+       {    0x0EA5,          0, 0},
+       {    0x0EA7,          0, 0},
+       {    0x0EAA,     0x0EAB, 1},
+       {    0x0EAD,     0x0EB0, 1},
+       {    0x0EB2,     0x0EB3, 1},
+       {    0x0EBD,          0, 0},
+       {    0x0EC0,     0x0EC4, 1},
+       {    0x0EC6,          0, 0},
+       {    0x0EDC,     0x0EDD, 1},
+       {    0x0F00,          0, 0},
+       {    0x0F40,     0x0F47, 1},
+       {    0x0F49,     0x0F6C, 1},
+       {    0x0F88,     0x0F8B, 1},
+       {    0x1000,     0x102A, 1},
+       {    0x1050,     0x1055, 1},
+       {    0x105A,     0x105D, 1},
+       {    0x1061,          0, 0},
+       {    0x0165,          0, 0},
+       {    0x1066,          0, 0},
+       {    0x106E,     0x1070, 1},
+       {    0x1075,     0x1081, 1},
+       {    0x108E,          0, 0},
+       {    0x10A0,     0x10C5, 1},
+       {    0x10D0,     0x10FA, 1},
+       {    0x10FC,          0, 0},
+       {    0x1100,     0x1159, 1},
+       {    0x115F,     0x11A2, 1},
+       {    0x11A8,     0x11F9, 1},
+       {    0x1200,     0x1248, 1},
+       {    0x124A,     0x124D, 1},
+       {    0x1250,     0x1256, 1},
+       {    0x1258,          0, 0},
+       {    0x125A,     0x125D, 1},
+       {    0x1260,     0x1288, 1},
+       {    0x128A,     0x128D, 1},
+       {    0x1290,     0x12B0, 1},
+       {    0x12B2,     0x12B5, 1},
+       {    0x12B8,     0x12BE, 1},
+       {    0x12C0,          0, 0},
+       {    0x12C2,     0x12C5, 1},
+       {    0x12C8,     0x12D6, 1},
+       {    0x12D8,     0x1310, 1},
+       {    0x1312,     0x1315, 1},
+       {    0x1318,     0x135A, 1},
+       {    0x1380,     0x138F, 1},
+       {    0x13A0,     0x13F4, 1},
+       {    0x1401,     0x166C, 1},
+       {    0x166F,     0x1676, 1},
+       {    0x1681,     0x169A, 1},
+       {    0x16A0,     0x16EA, 1},
+       {    0x16EE,     0x16F0, 1},
+       {    0x1700,     0x170C, 1},
+       {    0x170E,     0x1711, 1},
+       {    0x1720,     0x1731, 1},
+       {    0x1740,     0x1751, 1},
+       {    0x1760,     0x176C, 1},
+       {    0x176E,     0x1770, 1},
+       {    0x1780,     0x17B3, 1},
+       {    0x17D7,          0, 0},
+       {    0x17DC,          0, 0},
+       {    0x1820,     0x1877, 1},
+       {    0x1880,     0x18A8, 1},
+       {    0x18AA,          0, 0},
+       {    0x1900,     0x191C, 1},
+       {    0x1946,     0x194F, 1},
+       {    0x1950,     0x196D, 1},
+       {    0x1970,     0x1974, 1},
+       {    0x1980,     0x19A9, 1},
+       {    0x19C1,     0x19C7, 1},
+       {    0x19D0,     0x19D9, 1},
+       {    0x1A00,     0x1A16, 1},
+       {    0x1B05,     0x1B33, 1},
+       {    0x1B45,     0x1B4B, 1},
+       {    0x1B50,     0x1B59, 1},
+       {    0x1B83,     0x1BA0, 1},
+       {    0x1BAE,     0x1BAF, 1},
+       {    0x1C00,     0x1C23, 1},
+       {    0x1C4D,     0x1C4F, 1},
+       {    0x1C5A,     0x1C7D, 1},
+       {    0x1D00,     0x1DBF, 1},
+       {    0x1E00,     0x1E9F, 1},
+       {    0x1EA0,     0x1EFF, 1},
+       {    0x1F00,     0x1F15, 1},
+       {    0x1F18,     0x1F1D, 1},
+       {    0x1F20,     0x1F45, 1},
+       {    0x1F48,     0x1F4D, 1},
+       {    0x1F50,     0x1F57, 1},
+       {    0x1F59,          0, 0},
+       {    0x1F5B,          0, 0},
+       {    0x1F5D,          0, 0},
+       {    0x1F5F,     0x1F7D, 1},
+       {    0x1F80,     0x1FB4, 1},
+       {    0x1FB6,     0x1FBC, 1},
+       {    0x1FBE,          0, 0},
+       {    0x1FC2,     0x1FC4, 1},
+       {    0x1FC6,     0x1FCC, 1},
+       {    0x1FD0,     0x1FD3, 1},
+       {    0x1FD6,     0x1FDB, 1},
+       {    0x1FE0,     0x1FEC, 1},
+       {    0x1FF2,     0x1FF4, 1},
+       {    0x1FF6,     0x1FFC, 1},
+       {    0x2071,          0, 0},
+       {    0x207F,          0, 0},
+       {    0x2090,     0x2094, 1},
+       {    0x2102,          0, 0},
+       {    0x2107,          0, 0},
+       {    0x210A,     0x2113, 1},
+       {    0x2115,          0, 0},
+       {    0x2119,     0x211D, 1},
+       {    0x2124,          0, 0},
+       {    0x2126,          0, 0},
+       {    0x2128,     0x212D, 1},
+       {    0x212F,     0x2139, 1},
+       {    0x213C,     0x213F, 1},
+       {    0x2145,     0x2149, 1},
+       {    0x214E,          0, 0},
+       {    0x2160,     0x2188, 1},
+       {    0x249C,     0x24E9, 1},
+       {    0x2C00,     0x2C2E, 1},
+       {    0x2C30,     0x2C5E, 1},
+       {    0x2C60,     0x2C6F, 1},
+       {    0x2C71,     0x2C7D, 1},
+       {    0x2C80,     0x2CE4, 1},
+       {    0x2D00,     0x2D25, 1},
+       {    0x2D30,     0x2D65, 1},
+       {    0x2D6F,          0, 0},
+       {    0x2D80,     0x2D96, 1},
+       {    0x2DA0,     0x2DA6, 1},
+       {    0x2DA8,     0x2DAE, 1},
+       {    0x2DB0,     0x2DB6, 1},
+       {    0x2DB8,     0x2DBE, 1},
+       {    0x2DC0,     0x2DC6, 1},
+       {    0x2DC8,     0x2DCE, 1},
+       {    0x2DD0,     0x2DD6, 1},
+       {    0x2DD8,     0x2DDE, 1},
+       {    0x3005,     0x3007, 1},
+       {    0x3021,     0x3029, 1},
+       {    0x3031,     0x3035, 1},
+       {    0x3038,     0x303C, 1},
+       {    0x3041,     0x3096, 1},
+       {    0x309D,     0x309F, 1},
+       {    0x30A1,     0x30FA, 1},
+       {    0x30FC,     0x30FF, 1},
+       {    0x3105,     0x312D, 1},
+       {    0x3131,     0x318E, 1},
+       {    0x31A0,     0x31B7, 1},
+       {    0x31F0,     0x31FF, 1},
+       {    0x3400,     0x4DB5, 1},
+       {    0x4E00,     0x9FBB, 1},
+       {    0xA000,     0xA48C, 1},
+       {    0xA500,     0xA60B, 1},
+       {    0xA610,     0xA61F, 1},
+       {    0xA62A,     0xA62B, 1},
+       {    0xA640,     0xA65F, 1},
+       {    0xA662,     0xA66E, 1},
+       {    0xA680,     0xA697, 1},
+       {    0xA717,     0xA71F, 1},
+       {    0xA722,     0xA78C, 1},
+       {    0xA7FB,     0xA7FF, 1},
+       {    0xA800,          0, 0},
+       {    0xA801,          0, 0},
+       {    0xA803,     0xA805, 1},
+       {    0xA807,     0xA80A, 1},
+       {    0xA80C,     0xA822, 1},
+       {    0xA840,     0xA873, 1},
+       {    0xA882,     0xA8B3, 1},
+       {    0xA90A,     0xA92D, 1},
+       {    0xA930,     0xA946, 1},
+       {    0xAA00,     0xAA28, 1},
+       {    0xAA40,     0xAA42, 1},
+       {    0xAA44,     0xAA4B, 1},
+       {    0xAC00,     0xD7A3, 1},
+       {    0xF900,     0xFA2D, 1},
+       {    0xFA30,     0xFA6A, 1},
+       {    0xFA70,     0xFAD9, 1},
+       {    0xFB00,     0xFB06, 1},
+       {    0xFB13,     0xFB17, 1},
+       {    0xFB1D,          0, 0},
+       {    0xFB1F,     0xFB28, 1},
+       {    0xFB2A,     0xFB36, 1},
+       {    0xFB38,     0xFB3C, 1},
+       {    0xFB3E,          0, 0},
+       {    0xFB40,          0, 0},
+       {    0xFB41,          0, 0},
+       {    0xFB43,          0, 0},
+       {    0xFB44,          0, 0},
+       {    0xFB46,     0xFB4F, 1},
+       {    0xFB50,     0xFBB1, 1},
+       {    0xFBD3,     0xFD3D, 1},
+       {    0xFD50,     0xFD8F, 1},
+       {    0xFD92,     0xFDC7, 1},
+       {    0xFDF0,     0xFDFB, 1},
+       {    0xFE70,     0xFE74, 1},
+       {    0xFE76,     0xFEFC, 1},
+       {    0xFF21,     0xFF3A, 1},
+       {    0xFF41,     0xFF5A, 1},
+       {    0xFF66,     0xFFBE, 1},
+       {    0xFFC2,     0xFFC7, 1},
+       {    0xFFCA,     0xFFCF, 1},
+       {    0xFFD2,     0xFFD7, 1},
+       {    0xFFDA,     0xFFDC, 1},
+       {0x00010000, 0x0001000B, 1},
+       {0x0001000D, 0x00010026, 1},
+       {0x00010028, 0x0001003A, 1},
+       {0x0001003C, 0x0001003D, 1},
+       {0x0001003F, 0x0001004D, 1},
+       {0x00010050, 0x0001005D, 1},
+       {0x00010080, 0x000100FA, 1},
+       {0x00010140, 0x00010174, 1},
+       {0x00010280, 0x0001029C, 1},
+       {0x000102A0, 0x000102D0, 1},
+       {0x00010300, 0x0001031E, 1},
+       {0x00010330, 0x0001034A, 1},
+       {0x00010380, 0x0001039D, 1},
+       {0x000103A0, 0x000103C3, 1},
+       {0x000103C8, 0x000103CF, 1},
+       {0x000103D1, 0x000103D5, 1},
+       {0x00010400, 0x0001044F, 1},
+       {0x00010450, 0x0001047F, 1},
+       {0x00010480, 0x0001049D, 1},
+       {0x000104A0, 0x000104A9, 1},
+       {0x00010800, 0x00010805, 1},
+       {0x00010808,          0, 0},
+       {0x0001080A, 0x00010835, 1},
+       {0x00010837, 0x00010838, 1},
+       {0x0001083C,          0, 0},
+       {0x0001083F,          0, 0},
+       {0x00010900, 0x00010915, 1},
+       {0x00010A00,          0, 0},
+       {0x00010A10, 0x00010A13, 1},
+       {0x00010A15, 0x00010A17, 1},
+       {0x00010A19, 0x00010A33, 1},
+       {0x00012000, 0x0001236E, 1},
+       {0x00012400, 0x00012462, 1},
+       {0x0001D400, 0x0001D454, 1},
+       {0x0001D456, 0x0001D49C, 1},
+       {0x0001D49E, 0x0001D49F, 1},
+       {0x0001D4A2,          0, 0},
+       {0x0001D4A5, 0x0001D4A6, 1},
+       {0x0001D4A9, 0x0001D4AC, 1},
+       {0x0001D4AE, 0x0001D4B9, 1},
+       {0x0001D4BB,          0, 0},
+       {0x0001D4BD, 0x0001D4C3, 1},
+       {0x0001D4C5, 0x0001D505, 1},
+       {0x0001D507, 0x0001D50A, 1},
+       {0x0001D50D, 0x0001D514, 1},
+       {0x0001D516, 0x0001D51C, 1},
+       {0x0001D51E, 0x0001D539, 1},
+       {0x0001D53B, 0x0001D53E, 1},
+       {0x0001D540, 0x0001D544, 1},
+       {0x0001D546,          0, 0},
+       {0x0001D54A, 0x0001D550, 1},
+       {0x0001D552, 0x0001D6A5, 1},
+       {0x0001D6A8, 0x0001D6C0, 1},
+       {0x0001D6C2, 0x0001D6DA, 1},
+       {0x0001D6DC, 0x0001D6FA, 1},
+       {0x0001D6FC, 0x0001D714, 1},
+       {0x0001D716, 0x0001D734, 1},
+       {0x0001D736, 0x0001D74E, 1},
+       {0x0001D750, 0x0001D76E, 1},
+       {0x0001D770, 0x0001D788, 1},
+       {0x0001D78A, 0x0001D7A8, 1},
+       {0x0001D7AA, 0x0001D7C2, 1},
+       {0x0001D7C4, 0x0001D7CB, 1},
+       {0x0001D7CE, 0x0001D7FF, 1},
+       {0x00020000, 0x0002A6D6, 1},
+       {0x0002F800, 0x0002FA1D, 1},
+       {    0x0660,     0x0669, 1},
+       {    0x06F0,     0x06F9, 1},
+       {    0x0966,     0x096F, 1},
+       {    0x09E6,     0x09EF, 1},
+       {    0x0A66,     0x0A6F, 1},
+       {    0x0AE6,     0x0AEF, 1},
+       {    0x0B66,     0x0B6F, 1},
+       {    0x0BE6,     0x0BEF, 1},
+       {    0x0C66,     0x0C6F, 1},
+       {    0x0C78,     0x0C7F, 1},
+       {    0x0CE6,     0x0CEF, 1},
+       {    0x0D66,     0x0D75, 1},
+       {    0x0D70,     0x0D75, 1},
+       {    0x0E50,     0x0E59, 1},
+       {    0x0ED0,     0x0ED9, 1},
+       {    0x0F20,     0x0F29, 1},
+       {    0x1040,     0x1049, 1},
+       {    0x17E0,     0x17E9, 1},
+       {    0x1810,     0x1819, 1},
+       {    0x1BB0,     0x1BB9, 1},
+       {    0x1C40,     0x1C49, 1},
+       {    0x1C50,     0x1C59, 1},
+       {    0xA620,     0xA629, 1},
+       {    0xA8D0,     0xA8D9, 1},
+       {    0xA900,     0xA909, 1},
+       {    0xAA50,     0xAA59, 1},
+       {    0xFF10,     0xFF19, 1},
+
+/* END::UTF8TABLE */
+};
+
+static inline size_t utf8towc(unsigned *wc, const unsigned char *uc, size_t len)
+{
+       unsigned char ub = utf8_mblen[*uc];
+
+       if (!ub || ub > len || ub > 4) {
+               return 0;
+       }
+
+       *wc = *uc & utf8_mask[ub];
+
+       switch (ub) {
+       case 4:
+               if ((uc[1] & 0xc0) != 0x80) {
+                       return 0;
+               }
+               *wc <<= 6;
+               *wc += *++uc & 0x3f;
+               /* no break */
+       case 3:
+               if ((uc[1] & 0xc0) != 0x80) {
+                       return 0;
+               }
+               *wc <<= 6;
+               *wc += *++uc & 0x3f;
+               /* no break */
+       case 2:
+               if ((uc[1] & 0xc0) != 0x80) {
+                       return 0;
+               }
+               *wc <<= 6;
+               *wc += *++uc & 0x3f;
+               /* no break */
+       case 1:
+               break;
+
+       default:
+               return 0;
+       }
+
+       return ub;
+}
+
+static inline zend_bool isualpha(unsigned ch)
+{
+       unsigned i = 0, j;
+
+       PHP_HTTP_DUFF(sizeof(utf8_ranges)/sizeof(utf8_range_t),
+               if (utf8_ranges[i].start == ch) {
+                       return 1;
+               } else if (utf8_ranges[i].start <= ch && utf8_ranges[i].end >= ch) {
+                       if (utf8_ranges[i].step == 1) {
+                               return 1;
+                       }
+                       for (j = utf8_ranges[i].start; j <= utf8_ranges[i].end; j+= utf8_ranges[i].step) {
+                               if (ch == j) {
+                                       return 1;
+                               }
+                       }
+                       return 0;
+               }
+               ++i;
+       );
+       return 0;
+}
+
+static inline zend_bool isualnum(unsigned ch)
+{
+       /* digits */
+       if (ch >= 0x30 && ch <= 0x39) {
+               return 1;
+       }
+       return isualpha(ch);
+}
+
+static inline size_t wctoutf16(unsigned short u16[2], unsigned wc)
+{
+       if (wc > 0x10ffff || (wc >= 0xd800 && wc <= 0xdfff)) {
+               return 0;
+       }
+
+       if (wc <= 0xffff) {
+               u16[0] = (unsigned short) wc;
+               return 1;
+       }
+
+       wc -= 0x10000;
+       u16[0] = (unsigned short) ((wc >> 10) + 0xd800);
+       u16[1] = (unsigned short) ((wc & 0x3ff) + 0xdc00);
+       return 2;
+}
+
+static inline size_t utf16towc(unsigned *wc, unsigned short *u16_str, size_t u16_len)
+{
+       if (u16_len < 1) {
+               return 0;
+       }
+       if (u16_str[0] - 0xd800 >= 0x800) {
+               *wc = u16_str[0];
+               return 1;
+       }
+       if (u16_len < 2 || (u16_str[0] & 0xfffffc00) != 0xd800 || (u16_str[1] & 0xfffffc00) != 0xdc00) {
+               return 0;
+       }
+       *wc = (u16_str[0] << 10) + u16_str[1] - 0x35fdc00;
+       return 2;
+}
+
+#endif /* PHP_HTTP_UTF8_H */
+
+/*
+ * 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_version.c b/src/php_http_version.c
new file mode 100644 (file)
index 0000000..7adef9d
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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"
+
+php_http_version_t *php_http_version_init(php_http_version_t *v, unsigned major, unsigned minor TSRMLS_DC)
+{
+       if (!v) {
+               v = emalloc(sizeof(*v));
+       }
+
+       v->major = major;
+       v->minor = minor;
+
+       return v;
+}
+
+php_http_version_t *php_http_version_parse(php_http_version_t *v, const char *str TSRMLS_DC)
+{
+       long major, minor;
+       char separator = 0;
+       register const char *ptr = str;
+
+       switch (*ptr) {
+       case 'h':
+       case 'H':
+               ++ptr; if (*ptr != 't' && *ptr != 'T') break;
+               ++ptr; if (*ptr != 't' && *ptr != 'T') break;
+               ++ptr; if (*ptr != 'p' && *ptr != 'P') break;
+               ++ptr; if (*ptr != '/') break;
+               ++ptr;
+               /* no break */
+       default:
+               /* rfc7230#2.6 The HTTP version number consists of two decimal digits separated by a "." (period or decimal point) */
+               major = *ptr++ - '0';
+               if (major >= 0 && major <= 9) {
+                       separator = *ptr++;
+                       if (separator) {
+                               if (separator != '.' && separator != ',') {
+                                       php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Non-standard version separator '%c' in HTTP protocol version '%s'", separator, ptr - 2);
+                               }
+                               minor = *ptr - '0';
+                               if (minor >= 0 && minor <= 9) {
+                                       return php_http_version_init(v, major, minor TSRMLS_CC);
+                               }
+                       }
+               }
+       }
+
+       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not parse HTTP protocol version '%s'", str);
+       return NULL;
+}
+
+void php_http_version_to_string(php_http_version_t *v, char **str, size_t *len, const char *pre, const char *post TSRMLS_DC)
+{
+       *len = spprintf(str, 0, "%s%u.%u%s", pre ? pre : "", v->major, v->minor, post ? post : "");
+}
+
+void php_http_version_dtor(php_http_version_t *v)
+{
+       (void) v;
+}
+
+void php_http_version_free(php_http_version_t **v)
+{
+       if (*v) {
+               php_http_version_dtor(*v);
+               efree(*v);
+               *v = NULL;
+       }
+}
+
+/*
+ * 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_version.h b/src/php_http_version.h
new file mode 100644 (file)
index 0000000..40b833e
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+    +--------------------------------------------------------------------+
+    | 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_VERSION_H
+#define        PHP_HTTP_VERSION_H
+
+typedef struct php_http_version {
+       unsigned major;
+       unsigned minor;
+} php_http_version_t;
+
+PHP_HTTP_API php_http_version_t *php_http_version_init(php_http_version_t *v, unsigned major, unsigned minor TSRMLS_DC);
+PHP_HTTP_API php_http_version_t *php_http_version_parse(php_http_version_t *v, const char *str TSRMLS_DC);
+PHP_HTTP_API void php_http_version_to_string(php_http_version_t *v, char **str, size_t *len, const char *pre, const char *post TSRMLS_DC);
+PHP_HTTP_API void php_http_version_dtor(php_http_version_t *v);
+PHP_HTTP_API void php_http_version_free(php_http_version_t **v);
+
+#endif /* PHP_HTTP_VERSION_H */
+
+
+/*
+ * 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
+ */
+