--- /dev/null +++ b/ext/http/CREDITS @@ -0,0 +1,2 @@ +HTTP extension for PHP +Michael Wallner --- /dev/null +++ b/ext/http/KnownIssues.txt @@ -0,0 +1,33 @@ +Known Issues +============ +$Id: KnownIssues.txt 292753 2009-12-29 12:30:43Z mike $ + +PHP < 5.1.3: + HttpResponse::getHeader() does not work with Apache2 SAPIs. + Using an encoding stream filter on a stream you read from doesn't work. + +Windows: + If you keep getting "SSL connect error" when trying to issue + requests, try another (newer) libeay32.dll/ssleay32.dll pair. + +Internals: + Our http_urlencode_hash() does not differentiate between prefixes + for numeric or string keys. + 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 http.persistent.handles.limit = 0 + * call http_persistent_handles_clean() every request + * call $HttpRequest->flushCookies(), which is available + since libcurl v7.17.1 and does not work with the + procedural API + Anyway, none of these options is really perfect. + 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. --- /dev/null +++ b/ext/http/LICENSE @@ -0,0 +1,47 @@ +Copyright (c) 2004-2010, Michael Wallner . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +=============================================================================== + +The date parser in file http_date_api.c is derived from the implementation +found in the original libcurl source, licensed under the following conditions: + +Copyright (c) 1996 - 2006, Daniel Stenberg, . +All rights reserved. + +Permission to use, copy, modify, and distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright +notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall not +be used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization of the copyright holder. + --- /dev/null +++ b/ext/http/Makefile.frag @@ -0,0 +1,23 @@ +# vim: noet ts=1 sw=1 + +phpincludedir=$(prefix)/include/php + +install-http: install install-http-headers + +install-http-headers: + @echo "Installing HTTP headers: $(INSTALL_ROOT)$(phpincludedir)/ext/http/" + @$(mkinstalldirs) $(INSTALL_ROOT)$(phpincludedir)/ext/http + @for f in $(PHP_HTTP_HEADERS); do \ + if test -f "$(top_srcdir)/$$f"; then \ + $(INSTALL_DATA) $(top_srcdir)/$$f $(INSTALL_ROOT)$(phpincludedir)/ext/http; \ + elif test -f "$(top_builddir)/$$f"; then \ + $(INSTALL_DATA) $(top_builddir)/$$f $(INSTALL_ROOT)$(phpincludedir)/ext/http; \ + elif test -f "$(top_srcdir)/ext/http/$$f"; then \ + $(INSTALL_DATA) $(top_srcdir)/ext/http/$$f $(INSTALL_ROOT)$(phpincludedir)/ext/http; \ + elif test -f "$(top_builddir)/ext/http/$$f"; then \ + $(INSTALL_DATA) $(top_builddir)/ext/http/$$f $(INSTALL_ROOT)$(phpincludedir)/ext/http; \ + else \ + echo "WTF? $$f"; \ + fi \ + done; + --- /dev/null +++ b/ext/http/ThanksTo.txt @@ -0,0 +1,20 @@ +Thanks To +========= +$Id: ThanksTo.txt 275653 2009-02-12 13:11:05Z mike $ + +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) + Petr Czaderna (petr at hroch dot info) + 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! --- /dev/null +++ b/ext/http/config.m4 @@ -0,0 +1,5 @@ +dnl phpize stub of config9.m4 for pecl/http +dnl $Id: config.m4 214417 2006-06-07 21:05:34Z mike $ +dnl vim: noet ts=1 sw=1 + +sinclude(config9.m4) --- /dev/null +++ b/ext/http/config.w32 @@ -0,0 +1,129 @@ +// config.w32 for pecl/http +// $Id: config.w32 287971 2009-09-02 14:36:08Z pajoye $ + +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(""); + 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(""); + return false; +} + +if (PHP_HTTP != "no") { + + EXTENSION("http", + "missing.c http.c http_functions.c http_exception_object.c "+ + "http_util_object.c http_message_object.c http_requestpool_object.c "+ + "http_request_object.c http_response_object.c "+ + "http_api.c http_cache_api.c http_request_pool_api.c "+ + "http_request_api.c http_date_api.c http_headers_api.c "+ + "http_message_api.c http_send_api.c http_url_api.c "+ + "http_info_api.c http_request_method_api.c http_encoding_api.c "+ + "http_filter_api.c http_request_body_api.c http_querystring_object.c "+ + "http_deflatestream_object.c http_inflatestream_object.c "+ + "http_cookie_api.c http_querystring_api.c http_request_datashare_api.c "+ + "http_requestdatashare_object.c http_request_info.c http_persistent_handle_api.c", + null, + "/I\"" + configure_module_dirname + "/phpstr\""); + ADD_SOURCES(configure_module_dirname + "/phpstr", "phpstr.c", "http"); + AC_DEFINE("HAVE_HTTP", 1, "Have extended HTTP support"); + AC_DEFINE("HTTP_SHARED_DEPS", 1, "Depend on shared extensions"); + + AC_DEFINE("HAVE_GETHOSTNAME", 1); + AC_DEFINE("HAVE_GETSERVBYPORT", 1); + AC_DEFINE("HAVE_GETSERVBYNAME", 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); + } + + 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("winmm.lib", "http", PHP_HTTP)) { + AC_DEFINE("HTTP_HAVE_CURL", 1, "Have CURL library"); + AC_DEFINE("HTTP_HAVE_SSL", 1, "Have SSL"); + AC_DEFINE("HAVE_CURL_MULTI_STRERROR", 1, ""); + AC_DEFINE("HAVE_CURL_SHARE_STRERROR", 1, ""); + AC_DEFINE("HAVE_CURL_EASY_STRERROR", 1, ""); + AC_DEFINE("HAVE_CURL_EASY_RESET", 1, ""); + AC_DEFINE("HAVE_CURL_GETFORMDATA", 1, ""); + AC_DEFINE("HAVE_CURL_FORMGET", 1, ""); + AC_DEFINE("HAVE_CURL_MULTI_SETOPT", 1, ""); + AC_DEFINE("HAVE_CURL_MULTI_TIMEOUT", 1, ""); + } else { + WARNING("curl convenience functions not enabled; libraries and headers not found"); + } +/* +// MAGIC_LIB = PHP_DEBUG != "no" ? "libmagic-staticd.lib":"libmagic-static.lib"; +// if (CHECK_HEADER_ADD_INCLUDE("magic.h", "CFLAGS_HTTP") && +// CHECK_LIB(MAGIC_LIB, "http", PHP_HTTP)) { +// AC_DEFINE("HTTP_HAVE_MAGIC", 1, "Have magic library"); +// AC_DEFINE("USE_MAGIC_STATIC", "", ""); +// } else { +// WARNING("content type guessing not enabled; libraries and headers not found"); +// } +*/ +} --- /dev/null +++ b/ext/http/config9.m4 @@ -0,0 +1,468 @@ +dnl config.m4 for pecl/http +dnl $Id: config9.m4 242664 2007-09-18 19:13:37Z mike $ +dnl vim: noet ts=1 sw=1 + +PHP_ARG_ENABLE([http], [whether to enable extended HTTP support], +[ --enable-http Enable extended HTTP support]) +PHP_ARG_WITH([http-shared-deps], [whether to depend on extensions which have been built shared], +[ --with-http-shared-deps + HTTP: disable to not depend on extensions like hash, + iconv and session (when built shared)], $PHP_HTTP, $PHP_HTTP) +PHP_ARG_WITH([http-curl-requests], [whether to enable cURL HTTP request support], +[ --with-http-curl-requests[=LIBCURLDIR] + HTTP: with cURL request support], $PHP_HTTP, $PHP_HTTP) +PHP_ARG_WITH([http-curl-libevent], [whether to enable libevent support fur cURL], +[ --with-http-curl-libevent[=LIBEVENTDIR] + HTTP: libevent install directory], $PHP_HTTP_CURL_REQUESTS, "") +PHP_ARG_WITH([http-zlib-compression], [whether to enable zlib encodings support], +[ --with-http-zlib-compression[=LIBZDIR] + HTTP: with zlib encodings support], $PHP_HTTP, $PHP_HTTP) +PHP_ARG_WITH([http-magic-mime], [whether to enable response content type guessing], +[ --with-http-magic-mime[=LIBMAGICDIR] + HTTP: with magic mime response content type guessing], "no", "no") + +if test "$PHP_HTTP" != "no"; then + + ifdef([AC_PROG_EGREP], [ + AC_PROG_EGREP + ], [ + AC_CHECK_PROG(EGREP, egrep, egrep) + ]) + ifdef([AC_PROG_SED], [ + AC_PROG_SED + ], [ + ifdef([LT_AC_PROG_SED], [ + LT_AC_PROG_SED + ], [ + AC_CHECK_PROG(SED, sed, sed) + ]) + ]) + + AC_PROG_CPP + + if test "$PHP_HTTP_SHARED_DEPS" != "no"; then + AC_DEFINE([HTTP_SHARED_DEPS], [1], [ ]) + else + AC_DEFINE([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=$[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]) + $3 + elif test "$haveext"; then + AC_MSG_RESULT([yes]) + ifdef([PHP_ADD_EXTENSION_DEP], [ + PHP_ADD_EXTENSION_DEP([http], $1, true) + ]) + $2 + else + AC_MSG_RESULT([no]) + $3 + fi + ]) + + dnl + dnl HTTP_HAVE_PHP_EXT(name[, code-if-yes[, code-if-not]]) + dnl + AC_DEFUN([HTTP_HAVE_PHP_EXT], [ + extname=$1 + haveext=$[PHP_]translit($1,a-z_-,A-Z__) + + AC_MSG_CHECKING([for ext/$extname support]) + if test -x "$PHP_EXECUTABLE"; then + grepext=`$PHP_EXECUTABLE -m | $EGREP ^$extname\$` + if test "$grepext" = "$extname"; then + [HTTP_HAVE_EXT_]translit($1,a-z_-,A-Z__)=1 + AC_MSG_RESULT([yes]) + $2 + else + [HTTP_HAVE_EXT_]translit($1,a-z_-,A-Z__)= + AC_MSG_RESULT([no]) + $3 + fi + elif test "$haveext" != "no" && test "x$haveext" != "x"; then + [HTTP_HAVE_EXT_]translit($1,a-z_-,A-Z__)=1 + AC_MSG_RESULT([yes]) + $2 + else + [HTTP_HAVE_EXT_]translit($1,a-z_-,A-Z__)= + AC_MSG_RESULT([no]) + $3 + fi + ]) + + dnl + dnl odd PHP4 fix + dnl + if test "x$PHP_LIBDIR" = "x"; then + PHP_LIBDIR=lib + fi + +dnl ---- +dnl STDC +dnl ---- + AC_CHECK_HEADERS([netdb.h unistd.h]) + PHP_CHECK_FUNC(gethostname, nsl) + PHP_CHECK_FUNC(getdomainname, nsl) + PHP_CHECK_FUNC(getservbyport, nsl) + PHP_CHECK_FUNC(getservbyname, nsl) + +dnl ---- +dnl ZLIB +dnl ---- + if test "$PHP_HTTP_ZLIB_COMPRESSION" != "no"; then + AC_MSG_CHECKING([for zlib.h]) + ZLIB_DIR= + for i in "$PHP_HTTP_ZLIB_COMPRESSION" "$PHP_ZLIB_DIR" "$PHP_ZLIB" /usr/local /usr /opt; do + if test -f "$i/include/zlib.h"; then + ZLIB_DIR=$i + break; + fi + done + if test "x$ZLIB_DIR" = "x"; then + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([could not find zlib.h]) + else + AC_MSG_RESULT([found in $ZLIB_DIR]) + AC_MSG_CHECKING([for zlib version >= 1.2.0.4]) + ZLIB_VERSION=`$EGREP "define ZLIB_VERSION" $ZLIB_DIR/include/zlib.h | $SED -e 's/[[^0-9\.]]//g'` + AC_MSG_RESULT([$ZLIB_VERSION]) + if test `echo $ZLIB_VERSION | $SED -e 's/[[^0-9]]/ /g' | $AWK '{print $1*1000000 + $2*10000 + $3*100 + $4}'` -lt 1020004; then + AC_MSG_ERROR([libz version greater or equal to 1.2.0.4 required]) + else + PHP_ADD_INCLUDE($ZLIB_DIR/include) + PHP_ADD_LIBRARY_WITH_PATH(z, $ZLIB_DIR/$PHP_LIBDIR, HTTP_SHARED_LIBADD) + AC_DEFINE([HTTP_HAVE_ZLIB], [1], [Have zlib support]) + fi + fi + fi + +dnl ---- +dnl CURL +dnl ---- + if test "$PHP_HTTP_CURL_REQUESTS" != "no"; then + AC_MSG_CHECKING([for curl/curl.h]) + CURL_DIR= + for i in "$PHP_HTTP_CURL_REQUESTS" /usr/local /usr /opt; do + if test -f "$i/include/curl/curl.h"; then + CURL_DIR=$i + break + fi + done + if test "x$CURL_DIR" = "x"; then + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([could not find curl/curl.h]) + else + AC_MSG_RESULT([found in $CURL_DIR]) + fi + + AC_MSG_CHECKING([for curl-config]) + CURL_CONFIG= + for i in "$CURL_DIR/bin/curl-config" "$CURL_DIR/curl-config" `which curl-config`; do + if test -x "$i"; then + CURL_CONFIG=$i + break + fi + done + if test "x$CURL_CONFIG" = "x"; then + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([could not find curl-config]) + else + AC_MSG_RESULT([found: $CURL_CONFIG]) + fi + + dnl Debian stable has currently 7.13.2 (this is not a typo) + AC_MSG_CHECKING([for curl version >= 7.12.3]) + CURL_VERSION=`$CURL_CONFIG --version | $SED -e 's/[[^0-9\.]]//g'` + AC_MSG_RESULT([$CURL_VERSION]) + if test `echo $CURL_VERSION | $SED -e 's/[[^0-9]]/ /g' | $AWK '{print $1*10000 + $2*100 + $3}'` -lt 71203; then + AC_MSG_ERROR([libcurl version greater or equal to 7.12.3 required]) + fi + + dnl + dnl compile tests + dnl + + save_INCLUDES="$INCLUDES" + INCLUDES= + save_LIBS="$LIBS" + LIBS= + save_CFLAGS="$CFLAGS" + CFLAGS=`$CURL_CONFIG --cflags` + save_LDFLAGS="$LDFLAGS" + LDFLAGS=`$CURL_CONFIG --libs` + LDFLAGS="$LDFLAGS $ld_runpath_switch$CURL_DIR/$PHP_LIBDIR" + + AC_MSG_CHECKING([for SSL support in libcurl]) + CURL_SSL=`$CURL_CONFIG --feature | $EGREP SSL` + if test "$CURL_SSL" = "SSL"; then + AC_MSG_RESULT([yes]) + AC_DEFINE([HTTP_HAVE_SSL], [1], [ ]) + + AC_MSG_CHECKING([for openssl support in libcurl]) + AC_TRY_RUN([ + #include + int main(int argc, char *argv[]) { + curl_version_info_data *data = curl_version_info(CURLVERSION_NOW); + if (data && data->ssl_version && *data->ssl_version) { + const char *ptr = data->ssl_version; + while(*ptr == ' ') ++ptr; + return strncasecmp(ptr, "OpenSSL", sizeof("OpenSSL")-1); + } + return 1; + } + ], [ + AC_MSG_RESULT([yes]) + AC_CHECK_HEADER([openssl/crypto.h], [ + AC_DEFINE([HTTP_HAVE_OPENSSL], [1], [ ]) + ]) + ], [ + AC_MSG_RESULT([no]) + ], [ + AC_MSG_RESULT([no]) + ]) + + AC_MSG_CHECKING([for gnutls support in libcurl]) + AC_TRY_RUN([ + #include + int main(int argc, char *argv[]) { + curl_version_info_data *data = curl_version_info(CURLVERSION_NOW); + if (data && data->ssl_version && *data->ssl_version) { + const char *ptr = data->ssl_version; + while(*ptr == ' ') ++ptr; + return strncasecmp(ptr, "GnuTLS", sizeof("GnuTLS")-1); + } + return 1; + } + ], [ + AC_MSG_RESULT([yes]) + AC_CHECK_HEADER([gcrypt.h], [ + AC_DEFINE([HTTP_HAVE_GNUTLS], [1], [ ]) + ]) + ], [ + AC_MSG_RESULT([no]) + ], [ + AC_MSG_RESULT([no]) + ]) + else + AC_MSG_RESULT([no]) + fi + + INCLUDES="$save_INCLUDES" + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + LDFLAGS="$save_LDFLAGS" + + 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"; do + if test -f "$i"; then + CURL_CAINFO="$i" + break + fi + done + if test "x$CURL_CAINFO" = "x"; then + AC_MSG_RESULT([not found]) + else + AC_MSG_RESULT([$CURL_CAINFO]) + AC_DEFINE_UNQUOTED([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) + AC_DEFINE([HTTP_HAVE_CURL], [1], [Have cURL support]) + + PHP_CHECK_LIBRARY(curl, curl_share_strerror, + [AC_DEFINE([HAVE_CURL_SHARE_STRERROR], [1], [ ])], [ ], + [$CURL_LIBS -L$CURL_DIR/$PHP_LIBDIR] + ) + PHP_CHECK_LIBRARY(curl, curl_multi_strerror, + [AC_DEFINE([HAVE_CURL_MULTI_STRERROR], [1], [ ])], [ ], + [$CURL_LIBS -L$CURL_DIR/$PHP_LIBDIR] + ) + PHP_CHECK_LIBRARY(curl, curl_easy_strerror, + [AC_DEFINE([HAVE_CURL_EASY_STRERROR], [1], [ ])], [ ], + [$CURL_LIBS -L$CURL_DIR/$PHP_LIBDIR] + ) + PHP_CHECK_LIBRARY(curl, curl_easy_reset, + [AC_DEFINE([HAVE_CURL_EASY_RESET], [1], [ ])], [ ], + [$CURL_LIBS -L$CURL_DIR/$PHP_LIBDIR] + ) + PHP_CHECK_LIBRARY(curl, curl_formget, + [AC_DEFINE([HAVE_CURL_FORMGET], [1], [ ])], [ ], + [$CURL_LIBS -L$CURL_DIR/$PHP_LIBDIR] + ) + PHP_CHECK_LIBRARY(curl, curl_multi_setopt, + [AC_DEFINE([HAVE_CURL_MULTI_SETOPT], [1], [ ])], [ ], + [$CURL_LIBS -L$CURL_DIR/$PHP_LIBDIR] + ) + PHP_CHECK_LIBRARY(curl, curl_multi_timeout, + [AC_DEFINE([HAVE_CURL_MULTI_TIMEOUT], [1], [ ])], [ ], + [$CURL_LIBS -L$CURL_DIR/$PHP_LIBDIR] + ) + + dnl ---- + dnl EVENT + dnl ---- + + if test "$PHP_HTTP_CURL_LIBEVENT" != "no"; then + HTTP_HAVE_PHP_EXT([event], [ + AC_MSG_WARN([event support is incompatible with pecl/event; continuing without libevent support]) + ], [ + AC_MSG_CHECKING([for event.h]) + EVENT_DIR= + for i in "$PHP_HTTP_CURL_LIBEVENT" /usr/local /usr /opt; do + if test -f "$i/include/event.h"; then + EVENT_DIR=$i + break + fi + done + if test "x$EVENT_DIR" = "x"; then + AC_MSG_RESULT([not found]) + AC_MSG_WARN([continuing without libevent support]) + else + AC_MSG_RESULT([found in $EVENT_DIR]) + + AC_MSG_CHECKING([for libevent version, roughly]) + EVENT_VER="1.1b or lower" + if test -f "$EVENT_DIR/include/evhttp.h" && test -f "$EVENT_DIR/include/evdns.h"; then + if test -f "$EVENT_DIR/include/evrpc.h"; then + EVENT_VER="1.4 or greater" + else + EVENT_VER="1.2 or greater" + fi + fi + AC_DEFINE_UNQUOTED([HTTP_EVENT_VERSION], ["$EVENT_VER"], [ ]) + AC_MSG_RESULT([$EVENT_VER]) + + AC_MSG_CHECKING([for libcurl version >= 7.16.0]) + AC_MSG_RESULT([$CURL_VERSION]) + if test `echo $CURL_VERSION | $SED -e 's/[[^0-9]]/ /g' | $AWK '{print $1*10000 + $2*100 + $3}'` -lt 71600; then + AC_MSG_WARN([libcurl version greater or equal to 7.16.0 required; continuing without libevent support]) + else + PHP_ADD_INCLUDE($EVENT_DIR/include) + PHP_ADD_LIBRARY_WITH_PATH(event, $EVENT_DIR/$PHP_LIBDIR, HTTP_SHARED_LIBADD) + AC_DEFINE([HTTP_HAVE_EVENT], [1], [Have libevent support for cURL]) + PHP_CHECK_LIBRARY(curl, curl_multi_socket_action, + [AC_DEFINE([HAVE_CURL_MULTI_SOCKET_ACTION], [1], [ ])], [ ], + [$CURL_LIBS -L$CURL_DIR/$PHP_LIBDIR] + ) + fi + fi + ]) + fi + fi + +dnl ---- +dnl MAGIC +dnl ---- + if test "$PHP_HTTP_MAGIC_MIME" != "no"; then + AC_MSG_CHECKING([for magic.h]) + MAGIC_DIR= + for i in "$PHP_HTTP_MAGIC_MIME" /usr/local /usr /opt; do + if test -f "$i/include/magic.h"; then + MAGIC_DIR=$i + break + fi + done + if test "x$MAGIC_DIR" = "x"; then + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([could not find magic.h]) + else + AC_MSG_RESULT([found in $MAGIC_DIR]) + fi + + PHP_ADD_INCLUDE($MAGIC_DIR/include) + PHP_ADD_LIBRARY_WITH_PATH(magic, $MAGIC_DIR/$PHP_LIBDIR, HTTP_SHARED_LIBADD) + AC_DEFINE([HTTP_HAVE_MAGIC], [1], [Have magic mime support]) + fi + +dnl ---- +dnl HASH +dnl ---- + HTTP_HAVE_PHP_EXT([hash], [ + AC_MSG_CHECKING([for php_hash.h]) + HTTP_EXT_HASH_INCDIR= + for i in `echo $INCLUDES | $SED -e's/-I//g'` $abs_srcdir ../hash; do + if test -d $i; then + if test -f $i/php_hash.h; then + HTTP_EXT_HASH_INCDIR=$i + break + elif test -f $i/ext/hash/php_hash.h; then + HTTP_EXT_HASH_INCDIR=$i/ext/hash + break + fi + fi + done + if test "x$HTTP_EXT_HASH_INCDIR" = "x"; then + AC_MSG_RESULT([not found]) + else + AC_MSG_RESULT([$HTTP_EXT_HASH_INCDIR]) + AC_DEFINE([HTTP_HAVE_PHP_HASH_H], [1], [Have ext/hash support]) + PHP_ADD_INCLUDE([$HTTP_EXT_HASH_INCDIR]) + fi + ]) + +dnl ---- +dnl ICONV +dnl ---- + HTTP_HAVE_PHP_EXT([iconv]) + +dnl ---- +dnl SESSION +dnl ---- + HTTP_HAVE_PHP_EXT([session]) + +dnl ---- +dnl DONE +dnl ---- + PHP_HTTP_SOURCES="missing.c http.c http_functions.c phpstr/phpstr.c \ + http_util_object.c http_message_object.c http_request_object.c http_request_pool_api.c \ + http_response_object.c http_exception_object.c http_requestpool_object.c \ + http_api.c http_cache_api.c http_request_api.c http_request_info.c http_date_api.c \ + http_headers_api.c http_message_api.c http_send_api.c http_url_api.c \ + http_info_api.c http_request_method_api.c http_encoding_api.c \ + http_filter_api.c http_request_body_api.c http_querystring_object.c \ + http_deflatestream_object.c http_inflatestream_object.c http_cookie_api.c \ + http_querystring_api.c http_request_datashare_api.c http_requestdatashare_object.c \ + http_persistent_handle_api.c" + + PHP_NEW_EXTENSION([http], $PHP_HTTP_SOURCES, $ext_shared) + + dnl shared extension deps + HTTP_SHARED_DEP([hash]) + HTTP_SHARED_DEP([iconv]) + HTTP_SHARED_DEP([session]) + + PHP_ADD_BUILD_DIR($ext_builddir/phpstr, 1) + PHP_SUBST([HTTP_SHARED_LIBADD]) + + PHP_HTTP_HEADERS="php_http_std_defs.h php_http.h php_http_api.h php_http_cache_api.h \ + php_http_date_api.h php_http_headers_api.h php_http_info_api.h php_http_message_api.h \ + php_http_request_api.h php_http_request_method_api.h php_http_send_api.h php_http_url_api.h \ + php_http_encoding_api.h phpstr/phpstr.h missing.h php_http_request_body_api.h \ + php_http_exception_object.h php_http_message_object.h php_http_request_object.h \ + php_http_requestpool_object.h php_http_response_object.h php_http_util_object.h \ + php_http_querystring_object.h php_http_deflatestream_object.h php_http_inflatestream_object.h \ + php_http_cookie_api.h php_http_querystring_api.h php_http_request_datashare_api.h php_http_requestdatashare_object.h \ + php_http_persistent_handle_api.h" + ifdef([PHP_INSTALL_HEADERS], [ + PHP_INSTALL_HEADERS(ext/http, $PHP_HTTP_HEADERS) + ], [ + PHP_SUBST([PHP_HTTP_HEADERS]) + PHP_ADD_MAKEFILE_FRAGMENT + ]) + + AC_DEFINE([HAVE_HTTP], [1], [Have extended HTTP support]) +fi --- /dev/null +++ b/ext/http/docs/examples/tutorial.txt @@ -0,0 +1,175 @@ + +A Beginners Tutorial +-------------------- +$Revision: 208773 $ + + +- GET Queries + + The HttpRequest class can be used to execute any HTTP request method. + The following example shows a simple GET request where a few query + parameters are supplied. Additionally potential cookies will be + read from and written to a file. + +setOptions( + array( 'cookiestore' => 'google.txt', + ) +); + +$r->setQueryData( + array( 'q' => '+"pecl_http" -msg -cvs -list', + 'hl' => 'de' + ) +); + +// HttpRequest::send() returns an HttpMessage object +// of type HttpMessage::TYPE_RESPONSE or throws an exception +try { + print $r->send()->getBody(); +} catch (HttpException $e) { + print $e; +} +?> + +- Multipart Posts + + The following example shows an multipart POST request, with two form + fields and an image that's supposed to be uploaded to the server. + It's a bad habit as well as common practice to issue a redirect after + an received POST request, so we'll allow a redirect by enabling the + redirect option. + +setOptions( + array( 'cookies' => array('MyCookie' => 'has a value'), + 'redirect' => true, + ) +); + +// common form data +$r->setPostFields( + array( 'name' => 'Mike', + 'mail' => 'mike@php.net', + ) +); +// add the file to post (form name, file name, file type) +touch('profile.jpg'); +$r->addPostFile('image', 'profile.jpg', 'image/jpeg'); + +try { + print $r->send()->getBody(); +} catch (HttpException $e) { + print $e; +} +?> + +- Parallel Requests + + It's possible to execute several HttpRequests in parallel with the + HttpRequestPool class. HttpRequests to send, do not need to perform + the same request method, but can only be attached to one HttpRequestPool + at the same time. + +attach(new HttpRequest('http://pear.php.net', HTTP_METH_HEAD)); + $p->attach(new HttpRequest('http://pecl.php.net', HTTP_METH_HEAD)); +} catch (HttpException $e) { + print $e; + exit; +} + +try { + $p->send(); + // HttpRequestPool implements an iterator over attached HttpRequest objects + foreach ($p as $r) { + echo "Checking ", $r->getUrl(), " reported ", $r->getResponseCode(), "\n"; + } +} catch (HttpException $e) { + print $e; +} +?> + +- Parallel Requests? + + You can use a more advanced approach by using the protected interface of + the HttpRequestPool class. This allows you to perform some other tasks + while the requests are executed. + +socketPerform(); $i++) { + $i % 10 or print "."; + if (!$this->socketSelect()) { + throw new HttpException("Socket error!"); + } + } + print "\nDone!\n"; + } +} + +try { + foreach (new Pool as $r) { + echo "Checking ", $r->getUrl(), " reported ", $r->getResponseCode(), "\n"; + } +} catch (HttpException $ex) { + print $e; +} +?> + +- Cached Responses + + One of the main key features of HttpResponse is HTTP caching. HttpResponse + will calculate an ETag based on the http.etag_mode INI setting as well as + it will determine the last modification time of the sent entity. It uses + those two indicators to decide if the cache entry on the client side is + still valid and will emit an "304 Not Modified" response if applicable. + + + +- Bandwidth Throttling + + HttpResponse supports a basic throttling mechanism, which is enabled by + setting a throttle delay and a buffer size. PHP will sleep the specified + amount of seconds after each sent chunk of specified bytes. + + --- /dev/null +++ b/ext/http/docs/http.ini @@ -0,0 +1,61 @@ +; example INI file for pecl/http +; $Id: http.ini 229420 2007-02-09 14:19:40Z mike $ + +[http] +; enable if you want to transform all errors to exceptions (PHP >= 5 only) +;http.only_exceptions = 1 + +; disable if you don't want php to exit in case of redirects and cache hits; +; a "NULL" output handler will be started instead, which discards all output +;http.force_exit = 0 + +; disable if you don't want 404 Not found status messages being sent, +; if a file attempted to be sent with http_send_file() etc. cannot be found +;http.send.not_found_404 = 0 + +; the hashing algorithm with wich ETags are generated (MD5, SHA1, CRC32B); +; if ext/hash is available, this can be set to any hash algorithm ext/hash supports +; MD5 is the default and fallback algorithm +;http.etag.mode = "MD5" + +; allowed request methods +; by default PHP ignores unkown request methods +; PHP will exit with a response status of 405 and an Allow header +; if it encounters a request method not contained in the specified list +;http.request.methods.allowed = "HEAD, GET, POST" + +; custom request methods +;http.request.methods.custom = "KICK, BANN" + +; log file for positive cache hits +;http.log.cache = + +; log file for redirects +;http.log.redirect = + +; log file for responses with http_send_file() etc. where the file's not been found +;http.log.not_found = + +; log file for requests with an unallowed request method +;http.log.allowed_methods = + +; composite log file (i.e. log all messages to this file) +;http.log.composite = + +; automatically deflate content if requested/supported by client +;http.send.deflate.start_auto = 1 +;http.send.deflate.start_flags = HTTP_DEFLATE_LEVEL_DEF + +; automatically inflate sent content +;http.send.inflate.start_auto = 0 +;http.send.inflate.start_flags = + +; global HttpRequestDataShare settings +;http.request.datashare.cookie = 0 +;http.request.datashare.dns = 1 + +; limit of idle persistent handles per provider +;http.persistent.handles.limit = -1 + +; default ident of persistent handles +;http.persistent.handles.ident = "GLOBAL" --- /dev/null +++ b/ext/http/http.c @@ -0,0 +1,546 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http.c 300300 2010-06-09 07:29:35Z mike $ */ + +#define HTTP_WANT_SAPI +#define HTTP_WANT_CURL +#define HTTP_WANT_EVENT +#define HTTP_WANT_ZLIB +#define HTTP_WANT_MAGIC +#include "php_http.h" + +#include "php_ini.h" +#include "ext/standard/info.h" +#include "zend_extensions.h" + +#include "php_http_api.h" +#include "php_http_cache_api.h" +#include "php_http_cookie_api.h" +#include "php_http_encoding_api.h" +#include "php_http_filter_api.h" +#include "php_http_message_api.h" +#include "php_http_persistent_handle_api.h" +#include "php_http_request_api.h" +#include "php_http_request_datashare_api.h" +#include "php_http_request_method_api.h" +#include "php_http_request_pool_api.h" +#include "php_http_send_api.h" +#include "php_http_url_api.h" + +#include "php_http_deflatestream_object.h" +#include "php_http_exception_object.h" +#include "php_http_inflatestream_object.h" +#include "php_http_message_object.h" +#include "php_http_querystring_object.h" +#include "php_http_request_object.h" +#include "php_http_requestdatashare_object.h" +#include "php_http_requestpool_object.h" +#include "php_http_response_object.h" +#include "php_http_util_object.h" + +ZEND_DECLARE_MODULE_GLOBALS(http); +HTTP_DECLARE_ARG_PASS_INFO(); + +#ifdef COMPILE_DL_HTTP +ZEND_GET_MODULE(http) +#endif + +/* {{{ http_functions[] */ +zend_function_entry http_functions[] = { + PHP_FE(http_date, NULL) + PHP_FE(http_build_url, http_arg_pass_ref_4) + PHP_FE(http_build_str, NULL) +#ifndef ZEND_ENGINE_2 + PHP_FALIAS(http_build_query, http_build_str, NULL) +#endif + PHP_FE(http_negotiate_language, http_arg_pass_ref_2) + PHP_FE(http_negotiate_charset, http_arg_pass_ref_2) + PHP_FE(http_negotiate_content_type, http_arg_pass_ref_2) + PHP_FE(http_negotiate, http_arg_pass_ref_3) + PHP_FE(http_redirect, NULL) + PHP_FE(http_throttle, NULL) + PHP_FE(http_send_status, NULL) + PHP_FE(http_send_last_modified, NULL) + PHP_FE(http_send_content_type, NULL) + PHP_FE(http_send_content_disposition, NULL) + PHP_FE(http_match_modified, NULL) + PHP_FE(http_match_etag, NULL) + PHP_FE(http_cache_last_modified, NULL) + PHP_FE(http_cache_etag, NULL) + PHP_FE(http_send_data, NULL) + PHP_FE(http_send_file, NULL) + PHP_FE(http_send_stream, NULL) + PHP_FE(http_chunked_decode, NULL) + PHP_FE(http_parse_message, NULL) + PHP_FE(http_parse_headers, NULL) + PHP_FE(http_parse_cookie, NULL) + PHP_FE(http_build_cookie, NULL) + PHP_FE(http_parse_params, NULL) + PHP_FE(http_get_request_headers, NULL) + PHP_FE(http_get_request_body, NULL) + PHP_FE(http_get_request_body_stream, NULL) + PHP_FE(http_match_request_header, NULL) + PHP_FE(http_persistent_handles_count, NULL) + PHP_FE(http_persistent_handles_clean, NULL) + PHP_FE(http_persistent_handles_ident, NULL) +#ifdef HTTP_HAVE_CURL + PHP_FE(http_get, http_arg_pass_ref_3) + PHP_FE(http_head, http_arg_pass_ref_3) + PHP_FE(http_post_data, http_arg_pass_ref_4) + PHP_FE(http_post_fields, http_arg_pass_ref_5) + PHP_FE(http_put_data, http_arg_pass_ref_4) + PHP_FE(http_put_file, http_arg_pass_ref_4) + PHP_FE(http_put_stream, http_arg_pass_ref_4) + PHP_FE(http_request, http_arg_pass_ref_5) + PHP_FE(http_request_body_encode, NULL) +#endif + PHP_FE(http_request_method_register, NULL) + PHP_FE(http_request_method_unregister, NULL) + PHP_FE(http_request_method_exists, NULL) + PHP_FE(http_request_method_name, NULL) + PHP_FE(ob_etaghandler, NULL) +#ifdef HTTP_HAVE_ZLIB + PHP_FE(http_deflate, NULL) + PHP_FE(http_inflate, NULL) + PHP_FE(ob_deflatehandler, NULL) + PHP_FE(ob_inflatehandler, NULL) +#endif + PHP_FE(http_support, NULL) + + EMPTY_FUNCTION_ENTRY +}; +/* }}} */ + +PHP_MINIT_FUNCTION(http); +PHP_MSHUTDOWN_FUNCTION(http); +PHP_RINIT_FUNCTION(http); +PHP_RSHUTDOWN_FUNCTION(http); +PHP_MINFO_FUNCTION(http); + +/* {{{ http_module_dep */ +#if ZEND_EXTENSION_API_NO >= 220050617 +static zend_module_dep http_module_deps[] = { +# ifdef HTTP_HAVE_SPL + ZEND_MOD_REQUIRED("spl") +# endif +# ifdef HTTP_HAVE_HASH + ZEND_MOD_REQUIRED("hash") +# endif +# ifdef HTTP_HAVE_SESSION + ZEND_MOD_REQUIRED("session") +# endif +# ifdef HTTP_HAVE_ICONV + ZEND_MOD_REQUIRED("iconv") +# endif +# ifdef HTTP_HAVE_EVENT + ZEND_MOD_CONFLICTS("event") +#endif + {NULL, NULL, NULL, 0} +}; +#endif +/* }}} */ + +/* {{{ http_module_entry */ +zend_module_entry http_module_entry = { +#if ZEND_EXTENSION_API_NO >= 220050617 + STANDARD_MODULE_HEADER_EX, NULL, + http_module_deps, +#else + STANDARD_MODULE_HEADER, +#endif + "http", + http_functions, + PHP_MINIT(http), + PHP_MSHUTDOWN(http), + PHP_RINIT(http), + PHP_RSHUTDOWN(http), + PHP_MINFO(http), + PHP_HTTP_VERSION, + STANDARD_MODULE_PROPERTIES +}; +/* }}} */ + +int http_module_number; + +/* {{{ http_globals */ +static void http_globals_init_once(zend_http_globals *G) +{ + memset(G, 0, sizeof(zend_http_globals)); +} + +#define http_globals_init(g) _http_globals_init((g) TSRMLS_CC) +static inline void _http_globals_init(zend_http_globals *G TSRMLS_DC) +{ +#ifdef HTTP_HAVE_SAPI_RTIME + G->request.time = sapi_get_request_time(TSRMLS_C); +#else + G->request.time = time(NULL); +#endif + G->send.buffer_size = 0; + G->read_post_data = 0; +} + +#define http_globals_free(g) _http_globals_free((g) TSRMLS_CC) +static inline void _http_globals_free(zend_http_globals *G TSRMLS_DC) +{ + if (G->request.headers) { + zend_hash_destroy(G->request.headers); + FREE_HASHTABLE(G->request.headers); + G->request.headers = NULL; + } + STR_SET(G->send.content_type, NULL); + STR_SET(G->send.unquoted_etag, NULL); + if (G->server_var) { + zval_ptr_dtor(&G->server_var); + G->server_var = NULL; + } +} + +#if defined(ZTS) && defined(PHP_DEBUG) +#if ZTS && PHP_DEBUG +zend_http_globals *http_globals(void) +{ + TSRMLS_FETCH(); + return HTTP_G; +} +#endif +#endif +/* }}} */ + +/* {{{ static inline void http_check_allowed_methods(char *) */ +#define http_check_allowed_methods(m) _http_check_allowed_methods((m) TSRMLS_CC) +static inline void _http_check_allowed_methods(const char *methods TSRMLS_DC) +{ + if (*methods && SG(request_info).request_method) { + if (SUCCESS != http_check_method_ex(SG(request_info).request_method, methods)) { + char *header; + spprintf(&header, 0, "Allow: %s", methods); + http_exit(405, header); + } + } +} +/* }}} */ + +/* {{{ PHP_INI */ +PHP_INI_MH(http_update_allowed_methods) +{ + if (*new_value) { + http_check_allowed_methods(new_value); + } + return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); +} +PHP_INI_MH(http_update_persistent_handle_ident) +{ + HTTP_G->persistent.handles.ident.h = zend_hash_func(new_value, HTTP_G->persistent.handles.ident.l = new_value_length+1); + return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); +} + +#ifndef ZEND_ENGINE_2 +# define OnUpdateLong OnUpdateInt +#endif + +PHP_INI_BEGIN() + HTTP_PHP_INI_ENTRY("http.etag.mode", "MD5", PHP_INI_ALL, OnUpdateString, etag.mode) + HTTP_PHP_INI_ENTRY("http.log.cache", "", PHP_INI_ALL, OnUpdateString, log.cache) + HTTP_PHP_INI_ENTRY("http.log.redirect", "", PHP_INI_ALL, OnUpdateString, log.redirect) + HTTP_PHP_INI_ENTRY("http.log.not_found", "", PHP_INI_ALL, OnUpdateString, log.not_found) + HTTP_PHP_INI_ENTRY("http.log.allowed_methods", "", PHP_INI_ALL, OnUpdateString, log.allowed_methods) + HTTP_PHP_INI_ENTRY("http.log.composite", "", PHP_INI_ALL, OnUpdateString, log.composite) + HTTP_PHP_INI_ENTRY("http.request.methods.allowed", "", PHP_INI_ALL, http_update_allowed_methods, request.methods.allowed) + HTTP_PHP_INI_ENTRY("http.request.methods.custom", "", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateString, request.methods.custom) +#if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL) + HTTP_PHP_INI_ENTRY("http.request.datashare.cookie", "0", PHP_INI_SYSTEM, OnUpdateBool, request.datashare.cookie) + HTTP_PHP_INI_ENTRY("http.request.datashare.dns", "1", PHP_INI_SYSTEM, OnUpdateBool, request.datashare.dns) + HTTP_PHP_INI_ENTRY("http.request.datashare.ssl", "0", PHP_INI_SYSTEM, OnUpdateBool, request.datashare.ssl) + HTTP_PHP_INI_ENTRY("http.request.datashare.connect", "0", PHP_INI_SYSTEM, OnUpdateBool, request.datashare.connect) +#endif +#ifdef HTTP_HAVE_ZLIB + HTTP_PHP_INI_ENTRY("http.send.inflate.start_auto", "0", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateBool, send.inflate.start_auto) + HTTP_PHP_INI_ENTRY("http.send.inflate.start_flags", "0", PHP_INI_ALL, OnUpdateLong, send.inflate.start_flags) + HTTP_PHP_INI_ENTRY("http.send.deflate.start_auto", "0", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateBool, send.deflate.start_auto) + HTTP_PHP_INI_ENTRY("http.send.deflate.start_flags", "0", PHP_INI_ALL, OnUpdateLong, send.deflate.start_flags) +#endif + HTTP_PHP_INI_ENTRY("http.persistent.handles.limit", "-1", PHP_INI_SYSTEM, OnUpdateLong, persistent.handles.limit) + HTTP_PHP_INI_ENTRY("http.persistent.handles.ident", "GLOBAL", PHP_INI_ALL, http_update_persistent_handle_ident, persistent.handles.ident.s) + HTTP_PHP_INI_ENTRY("http.send.not_found_404", "1", PHP_INI_ALL, OnUpdateBool, send.not_found_404) +#ifdef ZEND_ENGINE_2 + HTTP_PHP_INI_ENTRY("http.only_exceptions", "0", PHP_INI_ALL, OnUpdateBool, only_exceptions) +#endif + HTTP_PHP_INI_ENTRY("http.force_exit", "1", PHP_INI_ALL, OnUpdateBool, force_exit) +PHP_INI_END() +/* }}} */ + +/* {{{ PHP_MINIT_FUNCTION */ +PHP_MINIT_FUNCTION(http) +{ + http_module_number = module_number; + ZEND_INIT_MODULE_GLOBALS(http, http_globals_init_once, NULL); + REGISTER_INI_ENTRIES(); + + if (0 + || SUCCESS != PHP_MINIT_CALL(http_persistent_handle) /* first */ + || SUCCESS != PHP_MINIT_CALL(http_cookie) +#ifdef HTTP_HAVE_ZLIB + || SUCCESS != PHP_MINIT_CALL(http_encoding) +#endif +#ifdef HTTP_HAVE_CURL + || SUCCESS != PHP_MINIT_CALL(http_request) +# ifdef ZEND_ENGINE_2 +# endif +#endif + || SUCCESS != PHP_MINIT_CALL(http_request_method) + || SUCCESS != PHP_MINIT_CALL(http_send) + || SUCCESS != PHP_MINIT_CALL(http_support) + || SUCCESS != PHP_MINIT_CALL(http_url) + +#ifdef ZEND_ENGINE_2 + || SUCCESS != PHP_MINIT_CALL(http_filter) + || SUCCESS != PHP_MINIT_CALL(http_exception_object) +# ifdef HTTP_HAVE_ZLIB + || SUCCESS != PHP_MINIT_CALL(http_deflatestream_object) + || SUCCESS != PHP_MINIT_CALL(http_inflatestream_object) +# endif + || SUCCESS != PHP_MINIT_CALL(http_message_object) + || SUCCESS != PHP_MINIT_CALL(http_querystring_object) +# ifdef HTTP_HAVE_CURL + || SUCCESS != PHP_MINIT_CALL(http_request_datashare) + || SUCCESS != PHP_MINIT_CALL(http_request_pool) + || SUCCESS != PHP_MINIT_CALL(http_request_object) + || SUCCESS != PHP_MINIT_CALL(http_requestdatashare_object) + || SUCCESS != PHP_MINIT_CALL(http_requestpool_object) +# endif +# ifndef WONKY + || SUCCESS != PHP_MINIT_CALL(http_response_object) +# endif + || SUCCESS != PHP_MINIT_CALL(http_util_object) +#endif + ) { + return FAILURE; + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION */ +PHP_MSHUTDOWN_FUNCTION(http) +{ + UNREGISTER_INI_ENTRIES(); + + if (0 +#ifdef HTTP_HAVE_CURL + || SUCCESS != PHP_MSHUTDOWN_CALL(http_request) +# ifdef ZEND_ENGINE_2 + || SUCCESS != PHP_MSHUTDOWN_CALL(http_request_datashare) +# endif +#endif + || SUCCESS != PHP_MSHUTDOWN_CALL(http_message_object) + || SUCCESS != PHP_MSHUTDOWN_CALL(http_persistent_handle) /* last */ + ) { + return FAILURE; + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RINIT_FUNCTION */ +PHP_RINIT_FUNCTION(http) +{ + http_globals_init(HTTP_G); + + if (HTTP_G->request.methods.allowed && *HTTP_G->request.methods.allowed) { + http_check_allowed_methods(HTTP_G->request.methods.allowed); + } + + if (0 +#ifdef HTTP_HAVE_ZLIB + || SUCCESS != PHP_RINIT_CALL(http_encoding) +#endif +#ifdef HTTP_HAVE_CURL +# ifdef ZEND_ENGINE_2 +# ifdef HTTP_HAVE_EVENT + || SUCCESS != PHP_RINIT_CALL(http_request_pool) +# endif + || SUCCESS != PHP_RINIT_CALL(http_request_datashare) +# endif +#endif + || SUCCESS != PHP_RINIT_CALL(http_request_method) + ) { + return FAILURE; + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RSHUTDOWN_FUNCTION */ +PHP_RSHUTDOWN_FUNCTION(http) +{ + STATUS status = SUCCESS; + + if (0 +#ifdef HTTP_HAVE_ZLIB + || SUCCESS != PHP_RSHUTDOWN_CALL(http_encoding) +#endif +#ifdef HTTP_HAVE_CURL +# ifdef ZEND_ENGINE_2 + || SUCCESS != PHP_RSHUTDOWN_CALL(http_request_datashare) +# endif +#endif + || SUCCESS != PHP_RSHUTDOWN_CALL(http_request_method) + ) { + status = FAILURE; + } + + http_globals_free(HTTP_G); + return status; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION */ +PHP_MINFO_FUNCTION(http) +{ + php_info_print_table_start(); + { + php_info_print_table_header(2, "HTTP Support", "enabled"); + php_info_print_table_row(2, "Extension Version", PHP_HTTP_VERSION); + php_info_print_table_row(2, "Registered Classes", +#ifndef ZEND_ENGINE_2 + "none" +#else + "HttpUtil, " + "HttpMessage, " +# ifdef HTTP_HAVE_CURL + "HttpRequest, " + "HttpRequestPool, " + "HttpRequestDataShare, " +# endif +# ifdef HTTP_HAVE_ZLIB + "HttpDeflateStream, " + "HttpInflateStream, " +# endif +# ifndef WONKY + "HttpResponse, " +# endif + "HttpQueryString" +#endif + ); + php_info_print_table_row(2, "Output Handlers", "ob_deflatehandler, ob_inflatehandler, ob_etaghandler"); + php_info_print_table_row(2, "Stream Filters", +#ifndef ZEND_ENGINE_2 + "none" +#else + "http.chunked_decode, http.chunked_encode, http.deflate, http.inflate" +#endif + ); + } + php_info_print_table_end(); + + php_info_print_table_start(); + php_info_print_table_header(3, "Used Library", "Compiled", "Linked"); + { +#ifdef 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(2, "libcurl", "disabled", "disabled"); +#endif +#ifdef HTTP_HAVE_EVENT + php_info_print_table_row(3, "libevent", HTTP_EVENT_VERSION, event_get_version()); +#else + php_info_print_table_row(3, "libevent", "disabled", "disabled"); +#endif +#ifdef HTTP_HAVE_ZLIB + php_info_print_table_row(3, "libz", ZLIB_VERSION, zlibVersion()); +#else + php_info_print_table_row(3, "libz", "disabled", "disabled"); +#endif +#if defined(HTTP_HAVE_MAGIC) + php_info_print_table_row(3, "libmagic", "unknown", "unknown"); +#else + php_info_print_table_row(3, "libmagic", "disabled", "disabled"); +#endif + } + php_info_print_table_end(); + + php_info_print_table_start(); + php_info_print_table_colspan_header(4, "Persistent Handles"); + php_info_print_table_header(4, "Provider", "Ident", "Used", "Free"); + { + HashTable *ht; + HashPosition pos1, pos2; + HashKey provider = initHashKey(0), ident = initHashKey(0); + zval **val, **sub, **zused, **zfree; + + if ((ht = http_persistent_handle_statall()) && zend_hash_num_elements(ht)) { + FOREACH_HASH_KEYVAL(pos1, ht, provider, val) { + if (zend_hash_num_elements(Z_ARRVAL_PP(val))) { + FOREACH_KEYVAL(pos2, *val, ident, sub) { + if ( SUCCESS == zend_hash_find(Z_ARRVAL_PP(sub), ZEND_STRS("used"), (void *) &zused) && + SUCCESS == zend_hash_find(Z_ARRVAL_PP(sub), ZEND_STRS("free"), (void *) &zfree)) { + zval *used = http_zsep(IS_STRING, *zused); + zval *free = http_zsep(IS_STRING, *zfree); + php_info_print_table_row(4, provider.str, ident.str, Z_STRVAL_P(used), Z_STRVAL_P(free)); + zval_ptr_dtor(&used); + zval_ptr_dtor(&free); + } else { + php_info_print_table_row(4, provider.str, ident.str, "0", "0"); + } + } + } else { + php_info_print_table_row(4, provider.str, "N/A", "0", "0"); + } + } + } else { + php_info_print_table_row(4, "N/A", "N/A", "0", "0"); + } + if (ht) { + zend_hash_destroy(ht); + FREE_HASHTABLE(ht); + } + } + php_info_print_table_end(); + + php_info_print_table_start(); + php_info_print_table_colspan_header(2, "Request Methods"); + { + HashPosition pos; + phpstr *methods = phpstr_new(); + char **name; + + FOREACH_HASH_VAL(pos, &HTTP_G->request.methods.registered, name) { + if (pos->h) { + phpstr_appendf(methods, "%s, ", *name); + } + } + phpstr_fix(methods); + php_info_print_table_row(2, "Registered", PHPSTR_VAL(methods)); + php_info_print_table_row(2, "Allowed", *HTTP_G->request.methods.allowed ? HTTP_G->request.methods.allowed : "(ANY)"); + phpstr_free(&methods); + } + 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 + */ + --- /dev/null +++ b/ext/http/http.dsp @@ -0,0 +1,257 @@ +# Microsoft Developer Studio Project File - Name="http" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=http - Win32 Release_TS +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "http.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "http.mak" CFG="http - Win32 Release_TS" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "http - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "http - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "http - Win32 Release_TS" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release_TS" +# PROP BASE Intermediate_Dir "Release_TS" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release_TS" +# PROP Intermediate_Dir "Release_TS" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /I "..\.." /I "..\..\..\Zend" /I "..\..\..\bindlib_w32" /I "..\..\..\TSRM" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COMPILE_DL_HTTP" /D ZTS=1 /YX /FD /c +# ADD CPP /nologo /Gd /MD /W3 /GX /O2 /I "..\.." /I "..\..\main" /I "..\..\Zend" /I "..\..\..\bindlib_w32" /I "..\..\TSRM" /D ZEND_DEBUG=0 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "HTTP_EXPORTS" /D "COMPILE_DL_HTTP" /D ZTS=1 /D "ZEND_WIN32" /D "PHP_WIN32" /D HAVE_HTTP=1 /D HTTP_HAVE_CURL=1 /D HAVE_CURL_EASY_STRERROR=1 /D HAVE_CURL_SHARE_STRERROR=1 /D HAVE_CURL_MULTI_STRERROR=1 /D HAVE_CURL_EASY_RESET=1 /D HAVE_CURL_FORMGET=1 /D HAVE_GETHOSTNAME=1 /D HAVE_GETSERVBYPORT=1 /D HAVE_GETSERVBYNAME=1 /D "_WINSOCKAPI_=" /FR /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x406 /d "NDEBUG" +# ADD RSC /l 0x406 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php4ts.lib /nologo /dll /machine:I386 +# ADD LINK32 libcurl.lib ssleay32.lib libeay32.lib zlib.lib winmm.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php4ts.lib wsock32.lib /nologo /dll /machine:I386 /out:"..\..\Release_TS/php_http.dll" /libpath:"..\..\Release_TS" /libpath:"..\..\Release_TS_Inline" /libpath:"..\..\..\php_build\curl\lib" /libpath:"..\..\..\php4\Release_TS" /libpath:"..\..\..\php4\Release_TS_Inline" + +!ELSEIF "$(CFG)" == "http - Win32 Debug_TS" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Debug_TS" +# PROP BASE Intermediate_Dir "Debug_TS" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Debug_TS" +# PROP Intermediate_Dir "Debug_TS" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /I "..\.." /I "..\..\Zend" /I "..\..\..\bindlib_w32" /I "..\..\TSRM" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COMPILE_DL_HTTP" /D ZTS=1 /YX /FD /c +# ADD CPP /nologo /MDd /W3 /GX /O2 /I "..\.." /I "..\..\main" /I "..\..\Zend" /I "..\..\..\bindlib_w32" /I "..\..\TSRM" /D ZEND_DEBUG=1 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "HTTP_EXPORTS" /D "COMPILE_DL_HTTP" /D ZTS=1 /D "ZEND_WIN32" /D "PHP_WIN32" /D HAVE_HTTP=1 /D HTTP_HAVE_CURL=1 /D HAVE_CURL_EASY_STRERROR=1 /D HAVE_CURL_SHARE_STRERROR=1 /D HAVE_CURL_MULTI_STRERROR=1 /D HAVE_CURL_EASY_RESET=1 /D HAVE_CURL_FORMGET=1 /D HAVE_GETHOSTNAME=1 /D HAVE_GETSERVBYPORT=1 /D HAVE_GETSERVBYNAME=1 /D "_WINSOCKAPI_=" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x406 /d "NDEBUG" +# ADD RSC /l 0x406 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php4ts.lib /nologo /dll /machine:I386 +# ADD LINK32 libcurl.lib ssleay32.lib libeay32.lib zlib.lib winmm.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php4ts_debug.lib wsock32.lib /nologo /dll /machine:I386 /out:"..\..\Debug_TS/http.dll" /libpath:"..\..\Debug_TS" /libpath:"..\..\..\php_build\curl\lib" /libpath:"..\..\..\php4\Release_TS" /libpath:"..\..\..\php4\Release_TS_Inline" + +!ENDIF + +# Begin Target + +# Name "http - Win32 Release_TS" +# Name "http - Win32 Debug_TS" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\http.c +# End Source File +# Begin Source File + +SOURCE=.\http_api.c +# End Source File +# Begin Source File + +SOURCE=.\http_encoding_api.c +# End Source File +# Begin Source File + +SOURCE=.\http_request_api.c +# End Source File +# Begin Source File + +SOURCE=.\http_request_info.c +# End Source File +# Begin Source File + +SOURCE=.\http_request_body_api.c +# End Source File +# Begin Source File + +SOURCE=.\http_request_method_api.c +# End Source File +# Begin Source File + +SOURCE=.\http_functions.c +# End Source File +# Begin Source File + +SOURCE=.\http_persistent_handle_api.c +# End Source File +# Begin Source File + +SOURCE=.\http_cache_api.c +# End Source File +# Begin Source File + +SOURCE=.\http_cookie_api.c +# End Source File +# Begin Source File + +SOURCE=.\http_date_api.c +# End Source File +# Begin Source File + +SOURCE=.\http_headers_api.c +# End Source File +# Begin Source File + +SOURCE=.\http_message_api.c +# End Source File +# Begin Source File + +SOURCE=.\http_send_api.c +# End Source File +# Begin Source File + +SOURCE=.\http_url_api.c +# End Source File +# Begin Source File + +SOURCE=.\http_querystring_api.c +# End Source File +# Begin Source File + +SOURCE=.\http_info_api.c +# End Source File +# Begin Source File + +SOURCE=.\phpstr\phpstr.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\php_http.h +# End Source File +# Begin Source File + +SOURCE=.\php_http_api.h +# End Source File +# Begin Source File + +SOURCE=.\php_http_encoding_api.h +# End Source File +# Begin Source File + +SOURCE=.\php_http_request_api.h +# End Source File +# Begin Source File + +SOURCE=.\php_http_request_int.h +# End Source File +# Begin Source File + +SOURCE=.\php_http_request_body_api.h +# End Source File +# Begin Source File + +SOURCE=.\php_http_request_method_api.h +# End Source File +# Begin Source File + +SOURCE=.\php_http_persistent_handle_api.h +# End Source File +# Begin Source File + +SOURCE=.\php_http_cache_api.h +# End Source File +# Begin Source File + +SOURCE=.\php_http_cookie_api.h +# End Source File +# Begin Source File + +SOURCE=.\php_http_date_api.h +# End Source File +# Begin Source File + +SOURCE=.\php_http_message_api.h +# End Source File +# Begin Source File + +SOURCE=.\php_http_send_api.h +# End Source File +# Begin Source File + +SOURCE=.\php_http_headers_api.h +# End Source File +# Begin Source File + +SOURCE=.\php_http_url_api.h +# End Source File +# Begin Source File + +SOURCE=.\php_http_querystring_api.h +# End Source File +# Begin Source File + +SOURCE=.\php_http_info_api.h +# End Source File +# Begin Source File + +SOURCE=.\php_http_std_defs.h +# End Source File +# Begin Source File + +SOURCE=.\phpstr\phpstr.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project --- /dev/null +++ b/ext/http/http_api.c @@ -0,0 +1,745 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_api.c 310302 2011-04-18 08:24:49Z rrichards $ */ + +#define HTTP_WANT_SAPI +#include "php_http.h" + +#include "php_output.h" +#include "ext/standard/url.h" +#include "ext/standard/php_lcg.h" + +#include "php_http_api.h" +#include "php_http_send_api.h" + +#ifdef ZEND_ENGINE_2 +# include "php_http_exception_object.h" +#endif + +PHP_MINIT_FUNCTION(http_support) +{ + HTTP_LONG_CONSTANT("HTTP_SUPPORT", HTTP_SUPPORT); + HTTP_LONG_CONSTANT("HTTP_SUPPORT_REQUESTS", HTTP_SUPPORT_REQUESTS); + HTTP_LONG_CONSTANT("HTTP_SUPPORT_MAGICMIME", HTTP_SUPPORT_MAGICMIME); + HTTP_LONG_CONSTANT("HTTP_SUPPORT_ENCODINGS", HTTP_SUPPORT_ENCODINGS); + HTTP_LONG_CONSTANT("HTTP_SUPPORT_SSLREQUESTS", HTTP_SUPPORT_SSLREQUESTS); + HTTP_LONG_CONSTANT("HTTP_SUPPORT_EVENTS", HTTP_SUPPORT_EVENTS); + + HTTP_LONG_CONSTANT("HTTP_PARAMS_ALLOW_COMMA", HTTP_PARAMS_ALLOW_COMMA); + HTTP_LONG_CONSTANT("HTTP_PARAMS_ALLOW_FAILURE", HTTP_PARAMS_ALLOW_FAILURE); + HTTP_LONG_CONSTANT("HTTP_PARAMS_RAISE_ERROR", HTTP_PARAMS_RAISE_ERROR); + HTTP_LONG_CONSTANT("HTTP_PARAMS_DEFAULT", HTTP_PARAMS_DEFAULT); + + return SUCCESS; +} + +PHP_HTTP_API long _http_support(long feature) +{ + long support = HTTP_SUPPORT; + +#ifdef HTTP_HAVE_CURL + support |= HTTP_SUPPORT_REQUESTS; +# ifdef HTTP_HAVE_SSL + support |= HTTP_SUPPORT_SSLREQUESTS; +# endif +# ifdef HTTP_HAVE_EVENT + support |= HTTP_SUPPORT_EVENTS; +# endif +#endif +#ifdef HTTP_HAVE_MAGIC + support |= HTTP_SUPPORT_MAGICMIME; +#endif +#ifdef HTTP_HAVE_ZLIB + support |= HTTP_SUPPORT_ENCODINGS; +#endif + + if (feature) { + return (feature == (support & feature)); + } + return support; +} + +/* char *pretty_key(char *, size_t, zend_bool, zend_bool) */ +char *_http_pretty_key(char *key, size_t key_len, zend_bool uctitle, zend_bool xhyphen) +{ + size_t i; + int wasalpha; + + if (key && key_len) { + if ((wasalpha = HTTP_IS_CTYPE(alpha, key[0]))) { + key[0] = (char) (uctitle ? HTTP_TO_CTYPE(upper, key[0]) : HTTP_TO_CTYPE(lower, key[0])); + } + for (i = 1; i < key_len; i++) { + if (HTTP_IS_CTYPE(alpha, key[i])) { + key[i] = (char) (((!wasalpha) && uctitle) ? HTTP_TO_CTYPE(upper, key[i]) : HTTP_TO_CTYPE(lower, key[i])); + wasalpha = 1; + } else { + if (xhyphen && (key[i] == '_')) { + key[i] = '-'; + } + wasalpha = 0; + } + } + } + return key; +} +/* }}} */ + +/* {{{ http_boundary(char *, size_t) */ +size_t _http_boundary(char *buf, size_t buf_len TSRMLS_DC) +{ + return snprintf(buf, buf_len, "%lu%0.9f", (ulong) HTTP_G->request.time, (float) php_combined_lcg(TSRMLS_C)); +} +/* }}} */ + +/* {{{ void http_error(long, long, char*) */ +void _http_error_ex(long type TSRMLS_DC, long code, const char *format, ...) +{ + va_list args; + + va_start(args, format); +#ifdef ZEND_ENGINE_2 + if ((type == E_THROW) || (GLOBAL_ERROR_HANDLING == EH_THROW)) { + char *message; + zend_class_entry *ce = http_exception_get_for_code(code); + + http_try { + vspprintf(&message, 0, format, args); + zend_throw_exception(ce, message, code TSRMLS_CC); + efree(message); + } http_catch(GLOBAL_EXCEPTION_CLASS ? GLOBAL_EXCEPTION_CLASS : HTTP_EX_DEF_CE); + } else +#endif + php_verror(NULL, "", type, format, args TSRMLS_CC); + va_end(args); +} +/* }}} */ + +#ifdef ZEND_ENGINE_2 +static inline void copy_bt_args(zval *from, zval *to TSRMLS_DC) +{ + zval **args, **trace_0, *old_trace_0, *trace = NULL; + + if ((trace = zend_read_property(ZEND_EXCEPTION_GET_DEFAULT(), from, "trace", lenof("trace"), 0 TSRMLS_CC))) { + if (Z_TYPE_P(trace) == IS_ARRAY && SUCCESS == zend_hash_index_find(Z_ARRVAL_P(trace), 0, (void *) &trace_0)) { + old_trace_0 = *trace_0; + if (Z_TYPE_PP(trace_0) == IS_ARRAY && SUCCESS == zend_hash_find(Z_ARRVAL_PP(trace_0), "args", sizeof("args"), (void *) &args)) { + if ((trace = zend_read_property(ZEND_EXCEPTION_GET_DEFAULT(), to, "trace", lenof("trace"), 0 TSRMLS_CC))) { + if (Z_TYPE_P(trace) == IS_ARRAY && SUCCESS == zend_hash_index_find(Z_ARRVAL_P(trace), 0, (void *) &trace_0)) { + ZVAL_ADDREF(*args); + add_assoc_zval(*trace_0, "args", *args); + } + } + } + } + } +} + +/* {{{ zval *http_exception_wrap(zval *, zval *, zend_class_entry *) */ +zval *_http_exception_wrap(zval *old_exception, zval *new_exception, zend_class_entry *ce TSRMLS_DC) +{ + int inner = 1; + char *message; + zval *sub_exception, *tmp_exception; + + if (!new_exception) { + MAKE_STD_ZVAL(new_exception); + object_init_ex(new_exception, ce); + + zend_update_property(ce, new_exception, "innerException", lenof("innerException"), old_exception TSRMLS_CC); + copy_bt_args(old_exception, new_exception TSRMLS_CC); + + sub_exception = old_exception; + + while ((sub_exception = zend_read_property(Z_OBJCE_P(sub_exception), sub_exception, "innerException", lenof("innerException"), 0 TSRMLS_CC)) && Z_TYPE_P(sub_exception) == IS_OBJECT) { + ++inner; + } + + spprintf(&message, 0, "Exception caused by %d inner exception(s)", inner); + zend_update_property_string(ZEND_EXCEPTION_GET_DEFAULT(), new_exception, "message", lenof("message"), message TSRMLS_CC); + efree(message); + } else { + sub_exception = new_exception; + tmp_exception = new_exception; + + while ((tmp_exception = zend_read_property(Z_OBJCE_P(tmp_exception), tmp_exception, "innerException", lenof("innerException"), 0 TSRMLS_CC)) && Z_TYPE_P(tmp_exception) == IS_OBJECT) { + sub_exception = tmp_exception; + } + + zend_update_property(Z_OBJCE_P(sub_exception), sub_exception, "innerException", lenof("innerException"), old_exception TSRMLS_CC); + copy_bt_args(old_exception, new_exception TSRMLS_CC); + copy_bt_args(old_exception, sub_exception TSRMLS_CC); + } +#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 3 + Z_ADDREF_P(old_exception); + zend_exception_set_previous(new_exception, old_exception TSRMLS_CC); +#endif + zval_ptr_dtor(&old_exception); + return new_exception; +} +/* }}} */ + +/* {{{ STATUS http_object_new(zend_object_value *, const char *, uint, http_object_new_t, zend_class_entry *, void *, void **) */ +STATUS _http_object_new(zend_object_value *ov, const char *cname_str, uint cname_len, http_object_new_t create, zend_class_entry *parent_ce, void *intern_ptr, void **obj_ptr TSRMLS_DC) +{ + zend_class_entry *ce = parent_ce; + + if (cname_str && cname_len) { + if (!(ce = zend_fetch_class(HTTP_ZAPI_CONST_CAST(char *) cname_str, cname_len, ZEND_FETCH_CLASS_DEFAULT TSRMLS_CC))) { + return FAILURE; + } + if (!instanceof_function(ce, parent_ce TSRMLS_CC)) { + http_error_ex(HE_WARNING, HTTP_E_RUNTIME, "Class %s does not extend %s", cname_str, parent_ce->name); + return FAILURE; + } + } + + *ov = create(ce, intern_ptr, obj_ptr TSRMLS_CC); + return SUCCESS; +} +/* }}} */ +#endif /* ZEND_ENGINE_2 */ + +/* {{{ void http_log(char *, char *, char *) */ +void _http_log_ex(char *file, const char *ident, const char *message TSRMLS_DC) +{ + time_t now; + struct tm nowtm; + char datetime[20] = {0}; + + now = HTTP_G->request.time; + strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S", php_localtime_r(&now, &nowtm)); + +#define HTTP_LOG_WRITE(file, type, msg) \ + if (file && *file) { \ + php_stream *log = php_stream_open_wrapper_ex(file, "ab", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL, HTTP_DEFAULT_STREAM_CONTEXT); \ + \ + if (log) { \ + php_stream_printf(log TSRMLS_CC, "%s\t[%s]\t%s\t<%s>%s", datetime, type, msg, SG(request_info).request_uri, PHP_EOL); \ + php_stream_close(log); \ + } \ + \ + } + + HTTP_LOG_WRITE(file, ident, message); + HTTP_LOG_WRITE(HTTP_G->log.composite, ident, message); +} +/* }}} */ + +static void http_ob_blackhole(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC) +{ + *handled_output = ecalloc(1,1); + *handled_output_len = 0; +} + +/* {{{ STATUS http_exit(int, char*, char*) */ +STATUS _http_exit_ex(int status, char *header, char *body, zend_bool send_header TSRMLS_DC) +{ + if ( (send_header && (SUCCESS != http_send_status_header(status, header))) || + (status && (SUCCESS != http_send_status(status)))) { + http_error_ex(HE_WARNING, HTTP_E_HEADER, "Failed to exit with status/header: %d - %s", status, STR_PTR(header)); + STR_FREE(header); + STR_FREE(body); + return FAILURE; + } + + if (!php_ob_handler_used("zlib output compression") && !php_ob_handler_used("ob_gzhandler") && !OG(ob_lock)) { + php_end_ob_buffers(0 TSRMLS_CC); + } + if ((SUCCESS == sapi_send_headers(TSRMLS_C)) && body) { + PHPWRITE(body, strlen(body)); + } + + switch (status) { + case 301: http_log(HTTP_G->log.redirect, "301-REDIRECT", header); break; + case 302: http_log(HTTP_G->log.redirect, "302-REDIRECT", header); break; + case 303: http_log(HTTP_G->log.redirect, "303-REDIRECT", header); break; + case 305: http_log(HTTP_G->log.redirect, "305-REDIRECT", header); break; + case 307: http_log(HTTP_G->log.redirect, "307-REDIRECT", header); break; + case 304: http_log(HTTP_G->log.cache, "304-CACHE", header); break; + case 404: http_log(HTTP_G->log.not_found, "404-NOTFOUND", NULL); break; + case 405: http_log(HTTP_G->log.allowed_methods, "405-ALLOWED", header); break; + default: http_log(NULL, header, body); break; + } + + STR_FREE(header); + STR_FREE(body); + + if (HTTP_G->force_exit) { + zend_bailout(); + } else { + php_ob_set_internal_handler(http_ob_blackhole, 4096, "blackhole", 0 TSRMLS_CC); + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ STATUS http_check_method(char *) */ +STATUS _http_check_method_ex(const char *method, const char *methods) +{ + const char *found; + + if ( (found = strstr(methods, method)) && + (found == method || !HTTP_IS_CTYPE(alpha, found[-1])) && + (strlen(found) >= strlen(method) && !HTTP_IS_CTYPE(alpha, found[strlen(method)]))) { + return SUCCESS; + } + return FAILURE; +} +/* }}} */ + +/* {{{ zval *http_get_server_var_ex(char *, size_t) */ +PHP_HTTP_API zval *_http_get_server_var_ex(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 (HTTP_G->server_var) { + zval_ptr_dtor(&HTTP_G->server_var); + } + MAKE_STD_ZVAL(HTTP_G->server_var); + ZVAL_STRING(HTTP_G->server_var, env, 1); + return HTTP_G->server_var; + } + +#ifdef ZEND_ENGINE_2 + zend_is_auto_global("_SERVER", lenof("_SERVER") TSRMLS_CC); +#endif + + if ((SUCCESS != zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void *) &hsv)) || (Z_TYPE_PP(hsv) != IS_ARRAY)) { + return NULL; + } + if ((SUCCESS != zend_hash_find(Z_ARRVAL_PP(hsv), HTTP_ZAPI_CONST_CAST(char *) 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; +} +/* }}} */ + +/* {{{ STATUS http_get_request_body(char **, size_t *) */ +PHP_HTTP_API STATUS _http_get_request_body_ex(char **body, size_t *length, zend_bool dup TSRMLS_DC) +{ + *length = 0; + *body = NULL; + + if (SG(request_info).raw_post_data) { + *length = SG(request_info).raw_post_data_length; + *body = SG(request_info).raw_post_data; + + if (dup) { + *body = estrndup(*body, *length); + } + return SUCCESS; + } else if (sapi_module.read_post && !HTTP_G->read_post_data) { + char *buf = emalloc(4096); + int len; + + HTTP_G->read_post_data = 1; + + while (0 < (len = sapi_module.read_post(buf, 4096 TSRMLS_CC))) { + SG(read_post_bytes) += len; + *body = erealloc(*body, *length + len + 1); + memcpy(*body + *length, buf, len); + *length += len; + (*body)[*length] = '\0'; + if (len < 4096) { + break; + } + } + efree(buf); + + /* check for error */ + if (len < 0) { + STR_FREE(*body); + *length = 0; + return FAILURE; + } + + SG(request_info).raw_post_data = *body; + SG(request_info).raw_post_data_length = *length; + + if (dup) { + *body = estrndup(*body, *length); + } + return SUCCESS; + } + + return FAILURE; +} +/* }}} */ + +/* {{{ php_stream *http_get_request_body_stream(void) */ +PHP_HTTP_API php_stream *_http_get_request_body_stream(TSRMLS_D) +{ + php_stream *s = NULL; + + if (SG(request_info).raw_post_data) { + s = php_stream_open_wrapper("php://input", "rb", 0, NULL); + } else if (sapi_module.read_post && !HTTP_G->read_post_data) { + HTTP_G->read_post_data = 1; + + if ((s = php_stream_temp_new())) { + char *buf = emalloc(4096); + int len; + + while (0 < (len = sapi_module.read_post(buf, 4096 TSRMLS_CC))) { + php_stream_write(s, buf, len); + if (len < 4096) { + break; + } + } + efree(buf); + + if (len < 0) { + php_stream_close(s); + s = NULL; + } else { + php_stream_rewind(s); + } + } + } + + return s; +} +/* }}} */ + +/* {{{ void http_parse_params_default_callback(...) */ +PHP_HTTP_API void _http_parse_params_default_callback(void *arg, const char *key, int keylen, const char *val, int vallen TSRMLS_DC) +{ + char *kdup; + zval tmp, *entry; + HashTable *ht = (HashTable *) arg; + + if (ht) { + INIT_ZARR(tmp, ht); + + if (vallen) { + MAKE_STD_ZVAL(entry); + array_init(entry); + if (keylen) { + kdup = estrndup(key, keylen); + add_assoc_stringl_ex(entry, kdup, keylen + 1, (char *) val, vallen, 1); + efree(kdup); + } else { + add_next_index_stringl(entry, (char *) val, vallen, 1); + } + add_next_index_zval(&tmp, entry); + } else { + add_next_index_stringl(&tmp, (char *) key, keylen, 1); + } + } +} +/* }}} */ + +/* {{{ STATUS http_parse_params(const char *, HashTable *) */ +PHP_HTTP_API STATUS _http_parse_params_ex(const char *param, int flags, http_parse_params_callback cb, void *cb_arg TSRMLS_DC) +{ +#define ST_QUOTE 1 +#define ST_VALUE 2 +#define ST_KEY 3 +#define ST_ASSIGN 4 +#define ST_ADD 5 + + int st = ST_KEY, keylen = 0, vallen = 0; + char *s, *c, *key = NULL, *val = NULL; + + for(c = s = estrdup(param);;) { + continued: +#if 0 + { + char *tk = NULL, *tv = NULL; + + if (key) { + if (keylen) { + tk= estrndup(key, keylen); + } else { + tk = ecalloc(1, 7); + memcpy(tk, key, 3); + tk[3]='.'; tk[4]='.'; tk[5]='.'; + } + } + if (val) { + if (vallen) { + tv = estrndup(val, vallen); + } else { + tv = ecalloc(1, 7); + memcpy(tv, val, 3); + tv[3]='.'; tv[4]='.'; tv[5]='.'; + } + } + fprintf(stderr, "[%6s] %c \"%s=%s\"\n", + ( + st == ST_QUOTE ? "QUOTE" : + st == ST_VALUE ? "VALUE" : + st == ST_KEY ? "KEY" : + st == ST_ASSIGN ? "ASSIGN" : + st == ST_ADD ? "ADD": + "HUH?" + ), *c?*c:'0', tk, tv + ); + STR_FREE(tk); STR_FREE(tv); + } +#endif + switch (st) { + case ST_QUOTE: + quote: + if (*c == '"') { + if (*(c-1) == '\\') { + memmove(c-1, c, strlen(c)+1); + goto quote; + } else { + goto add; + } + } else { + if (!val) { + val = c; + } + if (!*c) { + --val; + st = ST_ADD; + } + } + break; + + case ST_VALUE: + switch (*c) { + case '"': + if (!val) { + st = ST_QUOTE; + } + break; + + case ' ': + break; + + case ';': + case '\0': + goto add; + break; + case ',': + if (flags & HTTP_PARAMS_ALLOW_COMMA) { + goto add; + } + default: + if (!val) { + val = c; + } + break; + } + break; + + case ST_KEY: + switch (*c) { + case ',': + if (flags & HTTP_PARAMS_ALLOW_COMMA) { + goto allow_comma; + } + case '\r': + case '\n': + case '\t': + case '\013': + case '\014': + goto failure; + break; + + case ' ': + if (key) { + keylen = c - key; + st = ST_ASSIGN; + } + break; + + case ';': + case '\0': + allow_comma: + if (key) { + keylen = c-- - key; + st = ST_ADD; + } + break; + + case ':': + if (!(flags & HTTP_PARAMS_COLON_SEPARATOR)) { + goto not_separator; + } + if (key) { + keylen = c - key; + st = ST_VALUE; + } else { + goto failure; + } + break; + + case '=': + if (flags & HTTP_PARAMS_COLON_SEPARATOR) { + goto not_separator; + } + if (key) { + keylen = c - key; + st = ST_VALUE; + } else { + goto failure; + } + break; + + default: + not_separator: + if (!key) { + key = c; + } + break; + } + break; + + case ST_ASSIGN: + if (*c == '=') { + st = ST_VALUE; + } else if (!*c || *c == ';' || ((flags & HTTP_PARAMS_ALLOW_COMMA) && *c == ',')) { + st = ST_ADD; + } else if (*c != ' ') { + goto failure; + } + break; + + case ST_ADD: + add: + if (val) { + vallen = c - val; + if (st != ST_QUOTE) { + while (val[vallen-1] == ' ') --vallen; + } + } else { + val = ""; + vallen = 0; + } + + cb(cb_arg, key, keylen, val, vallen TSRMLS_CC); + + st = ST_KEY; + key = val = NULL; + keylen = vallen = 0; + break; + } + if (*c) { + ++c; + } else if (st == ST_ADD) { + goto add; + } else { + break; + } + } + + efree(s); + return SUCCESS; + +failure: + if (flags & HTTP_PARAMS_RAISE_ERROR) { + http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Unexpected character (%c) at pos %tu of %zu", *c, c-s, strlen(s)); + } + if (flags & HTTP_PARAMS_ALLOW_FAILURE) { + if (st == ST_KEY) { + if (key) { + keylen = c - key; + } else { + key = c; + } + } else { + --c; + } + st = ST_ADD; + goto continued; + } + efree(s); + return FAILURE; +} +/* }}} */ + +/* {{{ array_join */ +int apply_array_append_func(void *pDest HTTP_ZAPI_HASH_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 = 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 { + zend_hash_quick_find(dst, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &data); + } + + ZVAL_ADDREF(*value); + if (data) { + add_next_index_zval(http_zset(IS_ARRAY, *data), *value); + } else if (key) { + zend_hash_add(dst, key, hash_key->nKeyLength, value, sizeof(zval *), NULL); + } else { + zend_hash_quick_add(dst, hash_key->arKey, hash_key->nKeyLength, hash_key->h, value, sizeof(zval *), NULL); + } + + if (key) { + efree(key); + } + } + + return ZEND_HASH_APPLY_KEEP; +} + +int apply_array_merge_func(void *pDest HTTP_ZAPI_HASH_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) { + ZVAL_ADDREF(*value); + if ((flags & ARRAY_JOIN_PRETTIFY) && hash_key->nKeyLength) { + key = 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 { + zend_hash_quick_update(dst, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) value, sizeof(zval *), NULL); + } + } + + return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + +/* + * 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 + */ + --- /dev/null +++ b/ext/http/http_cache_api.c @@ -0,0 +1,256 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_cache_api.c 292841 2009-12-31 08:48:57Z mike $ */ + +#define HTTP_WANT_SAPI +#include "php_http.h" + +#include "php_output.h" +#include "php_streams.h" + +#include "php_http_api.h" +#include "php_http_cache_api.h" +#include "php_http_date_api.h" +#include "php_http_send_api.h" + +/* {{{ char *http_etag(void *, size_t, http_send_mode) */ +PHP_HTTP_API char *_http_etag(const void *data_ptr, size_t data_len, http_send_mode data_mode TSRMLS_DC) +{ + void *ctx = http_etag_init(); + + if (data_mode == SEND_DATA) { + http_etag_update(ctx, data_ptr, data_len); + } else { + STATUS ss = FAILURE; + php_stream_statbuf ssb; + + if (data_mode == SEND_RSRC) { + ss = php_stream_stat((php_stream *) data_ptr, &ssb); + } else { + ss = php_stream_stat_path((char *) data_ptr, &ssb); + } + + if (SUCCESS != ss) { + efree(ctx); + return NULL; + } else { + size_t ssb_len; + char ssb_buf[128]; + + ssb_len = snprintf(ssb_buf, sizeof(ssb_buf), "%ld=%ld=%ld", (long) ssb.sb.st_mtime, + (long) ssb.sb.st_ino, + (long) ssb.sb.st_size); + http_etag_update(ctx, ssb_buf, ssb_len); + } + } + + return http_etag_finish(ctx); +} +/* }}} */ + +/* {{{ time_t http_last_modified(void *, http_send_mode) */ +PHP_HTTP_API time_t _http_last_modified(const void *data_ptr, http_send_mode data_mode TSRMLS_DC) +{ + php_stream_statbuf ssb; + + switch (data_mode) { + case SEND_DATA: return HTTP_G->request.time; + case SEND_RSRC: return php_stream_stat((php_stream *) data_ptr, &ssb) ? 0 : ssb.sb.st_mtime; + default: return php_stream_stat_path((char *) data_ptr, &ssb) ? 0 : ssb.sb.st_mtime; + } +} +/* }}} */ + +/* {{{ zend_bool http_match_last_modified(char *, time_t) */ +PHP_HTTP_API zend_bool _http_match_last_modified_ex(const char *entry, time_t t, zend_bool enforce_presence TSRMLS_DC) +{ + zend_bool retval; + zval *zmodified; + char *modified, *chr_ptr; + + if (!(zmodified = http_get_server_var(entry, 1))) { + return !enforce_presence; + } + + modified = estrndup(Z_STRVAL_P(zmodified), Z_STRLEN_P(zmodified)); + if ((chr_ptr = strrchr(modified, ';'))) { + chr_ptr = 0; + } + + retval = (t <= http_parse_date_ex(modified, 1)); + efree(modified); + return retval; +} +/* }}} */ + +/* {{{ zend_bool http_match_etag(char *, char *) */ +PHP_HTTP_API zend_bool _http_match_etag_ex(const char *entry, const char *etag, zend_bool enforce_presence TSRMLS_DC) +{ + zval *zetag; + char *quoted_etag; + zend_bool result; + + if (!(zetag = http_get_server_var_ex(entry, strlen(entry)+1, 1))) { + return !enforce_presence; + } + + if (NULL != strchr(Z_STRVAL_P(zetag), '*')) { + return 1; + } + + spprintf("ed_etag, 0, "\"%s\"", etag); + if (!strchr(Z_STRVAL_P(zetag), ',')) { + result = !strcmp(Z_STRVAL_P(zetag), quoted_etag); + } else { + result = (NULL != strstr(Z_STRVAL_P(zetag), quoted_etag)); + } + efree(quoted_etag); + + return result; +} +/* }}} */ + +/* {{{ STATUS http_cache_last_modified(time_t, time_t, char *, size_t) */ +PHP_HTTP_API STATUS _http_cache_last_modified(time_t last_modified, + time_t send_modified, const char *cache_control, size_t cc_len TSRMLS_DC) +{ + char *sent_header = NULL; + + if (SG(headers_sent)) { + return FAILURE; + } + + if (cc_len && (SUCCESS != http_send_cache_control(cache_control, cc_len))) { + return FAILURE; + } + + if (SUCCESS != http_send_last_modified_ex(send_modified, &sent_header)) { + return FAILURE; + } + + if (http_match_last_modified("HTTP_IF_MODIFIED_SINCE", last_modified)) { + http_exit_ex(304, sent_header, NULL, 0); + } else { + STR_FREE(sent_header); + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ STATUS http_cache_etag(char *, size_t, char *, size_t) */ +PHP_HTTP_API STATUS _http_cache_etag(const char *etag, size_t etag_len, + const char *cache_control, size_t cc_len TSRMLS_DC) +{ + char *sent_header = NULL; + + if (SG(headers_sent)) { + return FAILURE; + } + + if (cc_len && (SUCCESS != http_send_cache_control(cache_control, cc_len))) { + return FAILURE; + } + + if (etag_len) { + if (SUCCESS != http_send_etag_ex(etag, etag_len, &sent_header)) { + return FAILURE; + } + if (http_match_etag("HTTP_IF_NONE_MATCH", etag)) { + http_exit_ex(304, sent_header, NULL, 0); + } else { + STR_FREE(sent_header); + } + return SUCCESS; + } + + /* start ob_etaghandler */ + return http_start_ob_etaghandler(); +} +/* }}} */ + +PHP_HTTP_API STATUS _http_start_ob_etaghandler(TSRMLS_D) +{ + /* already running? */ + if (php_ob_handler_used("ob_etaghandler" TSRMLS_CC)) { + http_error(HE_WARNING, HTTP_E_RUNTIME, "ob_etaghandler can only be used once"); + return FAILURE; + } + + HTTP_G->etag.started = 1; + return php_start_ob_buffer_named("ob_etaghandler", HTTP_G->send.buffer_size, 0 TSRMLS_CC); +} + +PHP_HTTP_API zend_bool _http_interrupt_ob_etaghandler(TSRMLS_D) +{ + if (HTTP_G->etag.started) { + HTTP_G->etag.started = 0; + if (HTTP_G->etag.ctx) { + efree(HTTP_G->etag.ctx); + HTTP_G->etag.ctx = NULL; + } + return 1; + } + return 0; +} + +/* {{{ void http_ob_etaghandler(char *, uint, char **, uint *, int) */ +void _http_ob_etaghandler(char *output, uint output_len, + char **handled_output, uint *handled_output_len, int mode TSRMLS_DC) +{ + /* passthru */ + *handled_output_len = output_len; + *handled_output = estrndup(output, output_len); + + /* are we supposed to run? */ + if (HTTP_G->etag.started) { + /* initialize the etag context */ + if (mode & PHP_OUTPUT_HANDLER_START) { + HTTP_G->etag.ctx = http_etag_init(); + } + + /* update */ + http_etag_update(HTTP_G->etag.ctx, output, output_len); + + /* finish */ + if (mode & PHP_OUTPUT_HANDLER_END) { + char *sent_header = NULL; + char *etag = http_etag_finish(HTTP_G->etag.ctx); + + HTTP_G->etag.ctx = NULL; + + http_send_cache_control(HTTP_DEFAULT_CACHECONTROL, lenof(HTTP_DEFAULT_CACHECONTROL)); + http_send_etag_ex(etag, strlen(etag), &sent_header); + + if (http_match_etag("HTTP_IF_NONE_MATCH", etag)) { + /* force exit; ob within ob does not work */ + HTTP_G->force_exit = 1; + http_exit_ex(304, sent_header, etag, 0); + } + + STR_FREE(sent_header); + STR_FREE(etag); + } + } +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/http_cookie_api.c @@ -0,0 +1,371 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_cookie_api.c 298662 2010-04-27 13:42:32Z mike $ */ + +#include "php_http.h" +#include "php_http_api.h" +#include "php_http_date_api.h" +#include "php_http_cookie_api.h" + +#include "ext/standard/url.h" + +/* {{{ PHP_MINIT_FUNCTION(http_cookie) */ +PHP_MINIT_FUNCTION(http_cookie) +{ + HTTP_LONG_CONSTANT("HTTP_COOKIE_PARSE_RAW", HTTP_COOKIE_PARSE_RAW); + HTTP_LONG_CONSTANT("HTTP_COOKIE_SECURE", HTTP_COOKIE_SECURE); + HTTP_LONG_CONSTANT("HTTP_COOKIE_HTTPONLY", HTTP_COOKIE_HTTPONLY); + + return SUCCESS; +} +/* }}} */ + +/* {{{ http_cookie_list *http_cookie_list_init(http_cookie_list *) */ +PHP_HTTP_API http_cookie_list *_http_cookie_list_init(http_cookie_list *list ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC) +{ + if (!list) { + list = emalloc_rel(sizeof(http_cookie_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 = 0; + list->flags = 0; + + return list; +} +/* }}} */ + +/* {{{ void http_cookie_list_dtor(http_cookie_list *) */ +PHP_HTTP_API void _http_cookie_list_dtor(http_cookie_list *list TSRMLS_DC) +{ + if (list) { + zend_hash_destroy(&list->cookies); + zend_hash_destroy(&list->extras); + + STR_SET(list->path, NULL); + STR_SET(list->domain, NULL); + } +} +/* }}} */ + +/* {{{ void http_cookie_list_free(http_cookie_list **) */ +PHP_HTTP_API void _http_cookie_list_free(http_cookie_list **list TSRMLS_DC) +{ + if (list) { + http_cookie_list_dtor(*list); + efree(*list); + *list = NULL; + } +} +/* }}} */ + +/* {{{ const char *http_cookie_list_get_cookie(http_cookie_list *, const char*, size_t) */ +PHP_HTTP_API const char *_http_cookie_list_get_cookie(http_cookie_list *list, const char *name, size_t name_len TSRMLS_DC) +{ + zval **cookie = NULL; + if ((SUCCESS != zend_hash_find(&list->cookies, HTTP_ZAPI_CONST_CAST(char *) name, name_len + 1, (void *) &cookie)) || (Z_TYPE_PP(cookie) != IS_STRING)) { + return NULL; + } + return Z_STRVAL_PP(cookie); +} +/* }}} */ + +/* {{{ const char *http_cookie_list_get_extra(http_cookie_list *, const char *, size_t) */ +PHP_HTTP_API const char *_http_cookie_list_get_extra(http_cookie_list *list, const char *name, size_t name_len TSRMLS_DC) +{ + zval **extra = NULL; + if ((SUCCESS != zend_hash_find(&list->extras, HTTP_ZAPI_CONST_CAST(char *) name, name_len + 1, (void *) &extra)) || (Z_TYPE_PP(extra) != IS_STRING)) { + return NULL; + } + return Z_STRVAL_PP(extra); +} +/* }}} */ + +/* {{{ void http_cookie_list_add_cookie(http_cookie_list *, const char *, size_t, const char *, size_t) */ +PHP_HTTP_API void _http_cookie_list_add_cookie(http_cookie_list *list, const char *name, size_t name_len, const char *value, size_t value_len TSRMLS_DC) +{ + zval *cookie_value; + char *key = estrndup(name, name_len); + MAKE_STD_ZVAL(cookie_value); + ZVAL_STRINGL(cookie_value, estrndup(value, value_len), value_len, 0); + zend_hash_update(&list->cookies, key, name_len + 1, (void *) &cookie_value, sizeof(zval *), NULL); + efree(key); +} +/* }}} */ + +/* {{{ void http_cookie_list_add_extr(http_cookie_list *, const char *, size_t, const char *, size_t) */ +PHP_HTTP_API void _http_cookie_list_add_extra(http_cookie_list *list, const char *name, size_t name_len, const char *value, size_t value_len TSRMLS_DC) +{ + zval *cookie_value; + char *key = estrndup(name, name_len); + MAKE_STD_ZVAL(cookie_value); + ZVAL_STRINGL(cookie_value, estrndup(value, value_len), value_len, 0); + zend_hash_update(&list->extras, key, name_len + 1, (void *) &cookie_value, sizeof(zval *), NULL); + efree(key); +} +/* }}} */ + +typedef struct _http_parse_param_cb_arg_t { + http_cookie_list *list; + long flags; + char **allowed_extras; +} http_parse_param_cb_arg; + +/* {{{ static void http_parse_cookie_callback */ +static void http_parse_cookie_callback(void *ptr, const char *key, int keylen, const char *val, int vallen TSRMLS_DC) +{ + http_parse_param_cb_arg *arg = (http_parse_param_cb_arg *) ptr; + +#define _KEY_IS(s) (keylen == lenof(s) && !strncasecmp(key, (s), keylen)) + if _KEY_IS("path") { + STR_SET(arg->list->path, estrndup(val, vallen)); + } else if _KEY_IS("domain") { + STR_SET(arg->list->domain, estrndup(val, vallen)); + } else if _KEY_IS("expires") { + char *date = estrndup(val, vallen); + arg->list->expires = http_parse_date(date); + efree(date); + } else if _KEY_IS("secure") { + arg->list->flags |= HTTP_COOKIE_SECURE; + } else if _KEY_IS("httpOnly") { + arg->list->flags |= HTTP_COOKIE_HTTPONLY; + } else { + /* check for extra */ + if (arg->allowed_extras) { + char **ae = arg->allowed_extras; + + for (; *ae; ++ae) { + if ((size_t) keylen == strlen(*ae) && !strncasecmp(key, *ae, keylen)) { + if (arg->flags & HTTP_COOKIE_PARSE_RAW) { + http_cookie_list_add_extra(arg->list, key, keylen, val, vallen); + } else { + char *dec = estrndup(val, vallen); + int declen = php_url_decode(dec, vallen); + + http_cookie_list_add_extra(arg->list, key, keylen, dec, declen); + efree(dec); + } + return; + } + } + } + /* new cookie */ + if (arg->flags & HTTP_COOKIE_PARSE_RAW) { + http_cookie_list_add_cookie(arg->list, key, keylen, val, vallen); + } else { + char *dec = estrndup(val, vallen); + int declen = php_url_decode(dec, vallen); + + http_cookie_list_add_cookie(arg->list, key, keylen, dec, declen); + efree(dec); + } + } +} +/* }}} */ + +/* {{{ http_cookie_list *http_parse_cookie(char *, long) */ +PHP_HTTP_API http_cookie_list *_http_parse_cookie_ex(http_cookie_list *list, const char *string, long flags, char **allowed_extras TSRMLS_DC) +{ + int free_list = !list; + http_parse_param_cb_arg arg; + + list = http_cookie_list_init(list); + + arg.list = list; + arg.flags = flags; + arg.allowed_extras = allowed_extras; + + if (SUCCESS != http_parse_params_ex(string, HTTP_PARAMS_RAISE_ERROR, http_parse_cookie_callback, &arg)) { + if (free_list) { + http_cookie_list_free(&list); + } else { + http_cookie_list_dtor(list); + } + list = NULL; + } + + return list; +} +/* }}} */ + +/* {{{ void http_cookie_list_tostruct(http_cookie_list *, zval *) */ +PHP_HTTP_API void _http_cookie_list_tostruct(http_cookie_list *list, zval *strct TSRMLS_DC) +{ + zval array, *cookies, *extras; + + INIT_ZARR(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_string(&array, "path", STR_PTR(list->path), 1); + add_assoc_string(&array, "domain", STR_PTR(list->domain), 1); +} +/* }}} */ + +/* {{{ http_cookie_list *http_cookie_list_fromstruct(http_cookie_list *, zval *strct) */ +PHP_HTTP_API http_cookie_list *_http_cookie_list_fromstruct(http_cookie_list *list, zval *strct TSRMLS_DC) +{ + zval **tmp, *cpy; + HashTable *ht = HASH_OF(strct); + + list = http_cookie_list_init(list); + + 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)) { + switch (Z_TYPE_PP(tmp)) { + case IS_LONG: + list->flags = Z_LVAL_PP(tmp); + break; + case IS_DOUBLE: + list->flags = (long) Z_DVAL_PP(tmp); + break; + case IS_STRING: + cpy = http_zsep(IS_LONG, *tmp); + list->flags = Z_LVAL_P(cpy); + zval_ptr_dtor(&cpy); + break; + default: + break; + } + } + if (SUCCESS == zend_hash_find(ht, "expires", sizeof("expires"), (void *) &tmp)) { + switch (Z_TYPE_PP(tmp)) { + case IS_LONG: + list->expires = Z_LVAL_PP(tmp); + break; + case IS_DOUBLE: + list->expires = (long) Z_DVAL_PP(tmp); + break; + case IS_STRING: + cpy = http_zsep(IS_LONG, *tmp); + if (Z_LVAL_P(cpy)) { + list->expires = Z_LVAL_P(cpy); + } else { + time_t expires = http_parse_date(Z_STRVAL_PP(tmp)); + if (expires > 0) { + list->expires = expires; + } + } + zval_ptr_dtor(&cpy); + break; + default: + break; + } + } + 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; +} +/* }}} */ + +/* {{{ inline append_encoded */ +static inline void append_encoded(phpstr *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_url_encode(key, key_len, &enc_len[0]); + enc_str[1] = php_url_encode(val, val_len, &enc_len[1]); + + phpstr_append(buf, enc_str[0], enc_len[0]); + phpstr_appends(buf, "="); + phpstr_append(buf, enc_str[1], enc_len[1]); + phpstr_appends(buf, "; "); + + efree(enc_str[0]); + efree(enc_str[1]); +} +/* }}} */ + +/* {{{ void http_cookie_list_tostring(http_cookie_list *, char **, size_t *) */ +PHP_HTTP_API void _http_cookie_list_tostring(http_cookie_list *list, char **str, size_t *len TSRMLS_DC) +{ + phpstr buf; + zval **val; + HashKey key = initHashKey(0); + HashPosition pos; + + phpstr_init(&buf); + + FOREACH_HASH_KEYVAL(pos, &list->cookies, key, val) { + if (key.type == HASH_KEY_IS_STRING && key.len) { + zval *tmp = http_zsep(IS_STRING, *val); + append_encoded(&buf, key.str, key.len-1, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); + zval_ptr_dtor(&tmp); + } + } + + if (list->domain && *list->domain) { + phpstr_appendf(&buf, "domain=%s; ", list->domain); + } + if (list->path && *list->path) { + phpstr_appendf(&buf, "path=%s; ", list->path); + } + if (list->expires) { + char *date = http_date(list->expires); + phpstr_appendf(&buf, "expires=%s; ", date); + efree(date); + } + + FOREACH_HASH_KEYVAL(pos, &list->extras, key, val) { + if (key.type == HASH_KEY_IS_STRING && key.len) { + zval *tmp = http_zsep(IS_STRING, *val); + append_encoded(&buf, key.str, key.len-1, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); + } + } + + if (list->flags & HTTP_COOKIE_SECURE) { + phpstr_appends(&buf, "secure; "); + } + if (list->flags & HTTP_COOKIE_HTTPONLY) { + phpstr_appends(&buf, "httpOnly; "); + } + + phpstr_fix(&buf); + *str = PHPSTR_VAL(&buf); + *len = PHPSTR_LEN(&buf); +} +/* }}} */ + +/* + * 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 + */ --- /dev/null +++ b/ext/http/http_date_api.c @@ -0,0 +1,357 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_date_api.c 292841 2009-12-31 08:48:57Z mike $ */ + +#include "php_http.h" + +#include "php_http_api.h" +#include "php_http_date_api.h" + +static inline int check_day(const char *day, size_t len); +static inline int check_month(const char *month); +static inline int check_tzone(const char *tzone); +static inline time_t parse_date(const char *month); + +/* {{{ day/month names */ +static const char *days[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; +static const char *wkdays[] = { + "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" +}; +static const char *weekdays[] = { + "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday", "Sunday" +}; +static const char *months[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; +enum assume_next { + DATE_MDAY, + DATE_YEAR, + DATE_TIME +}; +#define DS -60 +static const struct time_zone { + const char *name; + const int offset; +} time_zones[] = { + {"GMT", 0}, /* Greenwich Mean */ + {"UTC", 0}, /* Universal (Coordinated) */ + {"WET", 0}, /* Western European */ + {"BST", 0 DS}, /* British Summer */ + {"WAT", 60}, /* West Africa */ + {"AST", 240}, /* Atlantic Standard */ + {"ADT", 240 DS},/* Atlantic Daylight */ + {"EST", 300}, /* Eastern Standard */ + {"EDT", 300 DS},/* Eastern Daylight */ + {"CST", 360}, /* Central Standard */ + {"CDT", 360 DS},/* Central Daylight */ + {"MST", 420}, /* Mountain Standard */ + {"MDT", 420 DS},/* Mountain Daylight */ + {"PST", 480}, /* Pacific Standard */ + {"PDT", 480 DS},/* Pacific Daylight */ + {"YST", 540}, /* Yukon Standard */ + {"YDT", 540 DS},/* Yukon Daylight */ + {"HST", 600}, /* Hawaii Standard */ + {"HDT", 600 DS},/* Hawaii Daylight */ + {"CAT", 600}, /* Central Alaska */ + {"AHST", 600}, /* Alaska-Hawaii Standard */ + {"NT", 660}, /* Nome */ + {"IDLW", 720}, /* International Date Line West */ + {"CET", -60}, /* Central European */ + {"MET", -60}, /* Middle European */ + {"MEWT", -60}, /* Middle European Winter */ + {"MEST", -60 DS},/* Middle European Summer */ + {"CEST", -60 DS},/* Central European Summer */ + {"MESZ", -60 DS},/* Middle European Summer */ + {"FWT", -60}, /* French Winter */ + {"FST", -60 DS},/* French Summer */ + {"EET", -120}, /* Eastern Europe, USSR Zone 1 */ + {"WAST", -420}, /* West Australian Standard */ + {"WADT", -420 DS},/* West Australian Daylight */ + {"CCT", -480}, /* China Coast, USSR Zone 7 */ + {"JST", -540}, /* Japan Standard, USSR Zone 8 */ + {"EAST", -600}, /* Eastern Australian Standard */ + {"EADT", -600 DS},/* Eastern Australian Daylight */ + {"GST", -600}, /* Guam Standard, USSR Zone 9 */ + {"NZT", -720}, /* New Zealand */ + {"NZST", -720}, /* New Zealand Standard */ + {"NZDT", -720 DS},/* New Zealand Daylight */ + {"IDLE", -720}, /* International Date Line East */ +}; +/* }}} */ + +/* {{{ Day/Month/TZ checks for http_parse_date() + Originally by libcurl, Copyright (C) 1998 - 2004, Daniel Stenberg, , et al. */ +static inline int check_day(const char *day, size_t len) +{ + int i; + const char * const *check = (len > 3) ? &weekdays[0] : &wkdays[0]; + for (i = 0; i < 7; i++) { + if (!strcmp(day, check[0])) { + return i; + } + check++; + } + return -1; +} + +static inline int check_month(const char *month) +{ + int i; + const char * const *check = &months[0]; + for (i = 0; i < 12; i++) { + if (!strcmp(month, check[0])) { + return i; + } + check++; + } + return -1; +} + +/* return the time zone offset between GMT and the input one, in number + of seconds or -1 if the timezone wasn't found/legal */ + +static inline int check_tzone(const char *tzone) +{ + unsigned i; + const struct time_zone *check = time_zones; + for (i = 0; i < sizeof(time_zones) / sizeof(time_zones[0]); i++) { + if (!strcmp(tzone, check->name)) { + return check->offset * 60; + } + check++; + } + return -1; +} +/* }}} */ + +/* {{{ char *http_date(time_t) */ +PHP_HTTP_API char *_http_date(time_t t TSRMLS_DC) +{ + char *date = NULL; + struct tm *gmtime = NULL, tmbuf; + + memset(&tmbuf, 0, sizeof(tmbuf)); + if ((gmtime = php_gmtime_r(&t, &tmbuf))) { + spprintf(&date, 0, + "%s, %02d %s %04d %02d:%02d:%02d GMT", + days[gmtime->tm_wday], gmtime->tm_mday, + months[gmtime->tm_mon], gmtime->tm_year + 1900, + gmtime->tm_hour, gmtime->tm_min, gmtime->tm_sec + ); + } + + return date; +} +/* }}} */ + +/* {{{ time_t http_parse_date(char *) */ +PHP_HTTP_API time_t _http_parse_date_ex(const char *date, zend_bool silent TSRMLS_DC) +{ + time_t t = parse_date(date); + + if (-1 == t && !silent) { + http_error_ex(HE_NOTICE, HTTP_E_RUNTIME, "Could not parse date: %s", date); + } + + return t; +} +/* }}} */ + +/* time_t parse_date(char *) + Originally by libcurl, Copyright (C) 1998 - 2004, Daniel Stenberg, , et al. */ +static inline time_t parse_date(const char *date) +{ + time_t t = 0; + int tz_offset = -1, year = -1, month = -1, monthday = -1, weekday = -1, + hours = -1, minutes = -1, seconds = -1; + struct tm tm; + enum assume_next dignext = DATE_MDAY; + const char *indate = date; + + int part = 0; /* max 6 parts */ + + while (*date && (part < 6)) { + int found = 0; + + while (*date && !HTTP_IS_CTYPE(alnum, *date)) { + date++; + } + + if (HTTP_IS_CTYPE(alpha, *date)) { + /* a name coming up */ + char buf[32] = ""; + size_t len; + sscanf(date, "%31[A-Za-z]", buf); + len = strlen(buf); + + if (weekday == -1) { + weekday = check_day(buf, len); + if (weekday != -1) { + found = 1; + } + } + + if (!found && (month == -1)) { + month = check_month(buf); + if (month != -1) { + found = 1; + } + } + + if (!found && (tz_offset == -1)) { + /* this just must be a time zone string */ + tz_offset = check_tzone(buf); + if (tz_offset != -1) { + found = 1; + } + } + + if (!found) { + return -1; /* bad string */ + } + date += len; + } + else if (HTTP_IS_CTYPE(digit, *date)) { + /* a digit */ + int val; + char *end; + if ((seconds == -1) && + (3 == sscanf(date, "%02d:%02d:%02d", &hours, &minutes, &seconds))) { + /* time stamp! */ + date += 8; + found = 1; + } + else { + val = (int) strtol(date, &end, 10); + + if ((tz_offset == -1) && ((end - date) == 4) && (val < 1300) && + (indate < date) && ((date[-1] == '+' || date[-1] == '-'))) { + /* four digits and a value less than 1300 and it is preceeded with + a plus or minus. This is a time zone indication. */ + found = 1; + tz_offset = (val / 100 * 60 + val % 100) * 60; + + /* the + and - prefix indicates the local time compared to GMT, + this we need ther reversed math to get what we want */ + tz_offset = date[-1] == '+' ? -tz_offset : tz_offset; + } + + if (((end - date) == 8) && (year == -1) && (month == -1) && (monthday == -1)) { + /* 8 digits, no year, month or day yet. This is YYYYMMDD */ + found = 1; + year = val / 10000; + month = (val % 10000) / 100 - 1; /* month is 0 - 11 */ + monthday = val % 100; + } + + if (!found && (dignext == DATE_MDAY) && (monthday == -1)) { + if ((val > 0) && (val < 32)) { + monthday = val; + found = 1; + } + dignext = DATE_YEAR; + } + + if (!found && (dignext == DATE_YEAR) && (year == -1)) { + year = val; + found = 1; + if (year < 1900) { + year += year > 70 ? 1900 : 2000; + } + if(monthday == -1) { + dignext = DATE_MDAY; + } + } + + if (!found) { + return -1; + } + + date = end; + } + } + + part++; + } + + if (-1 == seconds) { + seconds = minutes = hours = 0; /* no time, make it zero */ + } + + if ((-1 == monthday) || (-1 == month) || (-1 == year)) { + /* lacks vital info, fail */ + return -1; + } + + if (sizeof(time_t) < 5) { + /* 32 bit time_t can only hold dates to the beginning of 2038 */ + if (year > 2037) { + return 0x7fffffff; + } + } + + tm.tm_sec = seconds; + tm.tm_min = minutes; + tm.tm_hour = hours; + tm.tm_mday = monthday; + tm.tm_mon = month; + tm.tm_year = year - 1900; + tm.tm_wday = 0; + tm.tm_yday = 0; + tm.tm_isdst = 0; + + t = mktime(&tm); + + /* time zone adjust */ + if (t != -1) { + struct tm *gmt, keeptime2; + long delta; + time_t t2; + + if((gmt = php_gmtime_r(&t, &keeptime2))) { + tm = *gmt; /* MSVC quirks */ + } else { + return -1; /* illegal date/time */ + } + + t2 = mktime(&tm); + + /* Add the time zone diff (between the given timezone and GMT) and the + diff between the local time zone and GMT. */ + delta = (tz_offset != -1 ? tz_offset : 0) + (t - t2); + + if((delta > 0) && (t + delta < t)) { + return -1; /* time_t overflow */ + } + + t += delta; + } + + return t; +} +/* }}} */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/http_deflatestream_object.c @@ -0,0 +1,312 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_deflatestream_object.c 300299 2010-06-09 06:23:16Z mike $ */ + +#define HTTP_WANT_ZLIB +#include "php_http.h" + +#if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_ZLIB) + +#include "php_http_api.h" +#include "php_http_encoding_api.h" +#include "php_http_exception_object.h" +#include "php_http_deflatestream_object.h" + +#define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpDeflateStream, method, 0, req_args) +#define HTTP_EMPTY_ARGS(method) HTTP_EMPTY_ARGS_EX(HttpDeflateStream, method, 0) +#define HTTP_DEFLATE_ME(method, visibility) PHP_ME(HttpDeflateStream, method, HTTP_ARGS(HttpDeflateStream, method), visibility) + +HTTP_BEGIN_ARGS(__construct, 0) + HTTP_ARG_VAL(flags, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(factory, 0) + HTTP_ARG_VAL(flags, 0) + HTTP_ARG_VAL(class_name, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(update, 1) + HTTP_ARG_VAL(data, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(flush, 0) + HTTP_ARG_VAL(data, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(finish, 0) + HTTP_ARG_VAL(data, 0) +HTTP_END_ARGS; + +#define THIS_CE http_deflatestream_object_ce +zend_class_entry *http_deflatestream_object_ce; +zend_function_entry http_deflatestream_object_fe[] = { + HTTP_DEFLATE_ME(__construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) + HTTP_DEFLATE_ME(update, ZEND_ACC_PUBLIC) + HTTP_DEFLATE_ME(flush, ZEND_ACC_PUBLIC) + HTTP_DEFLATE_ME(finish, ZEND_ACC_PUBLIC) + + HTTP_DEFLATE_ME(factory, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + + EMPTY_FUNCTION_ENTRY +}; +static zend_object_handlers http_deflatestream_object_handlers; + +PHP_MINIT_FUNCTION(http_deflatestream_object) +{ + HTTP_REGISTER_CLASS_EX(HttpDeflateStream, http_deflatestream_object, NULL, 0); + http_deflatestream_object_handlers.clone_obj = _http_deflatestream_object_clone_obj; + +#ifndef WONKY + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("TYPE_GZIP")-1, HTTP_DEFLATE_TYPE_GZIP TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("TYPE_ZLIB")-1, HTTP_DEFLATE_TYPE_ZLIB TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("TYPE_RAW")-1, HTTP_DEFLATE_TYPE_RAW TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("LEVEL_DEF")-1, HTTP_DEFLATE_LEVEL_DEF TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("LEVEL_MIN")-1, HTTP_DEFLATE_LEVEL_MIN TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("LEVEL_MAX")-1, HTTP_DEFLATE_LEVEL_MAX TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("STRATEGY_DEF")-1, HTTP_DEFLATE_STRATEGY_DEF TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("STRATEGY_FILT")-1, HTTP_DEFLATE_STRATEGY_FILT TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("STRATEGY_HUFF")-1, HTTP_DEFLATE_STRATEGY_HUFF TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("STRATEGY_RLE")-1, HTTP_DEFLATE_STRATEGY_RLE TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("STRATEGY_FIXED")-1, HTTP_DEFLATE_STRATEGY_FIXED TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("FLUSH_NONE")-1, HTTP_ENCODING_STREAM_FLUSH_NONE TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("FLUSH_SYNC")-1, HTTP_ENCODING_STREAM_FLUSH_SYNC TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("FLUSH_FULL")-1, HTTP_ENCODING_STREAM_FLUSH_FULL TSRMLS_CC); +#endif + + return SUCCESS; +} + +zend_object_value _http_deflatestream_object_new(zend_class_entry *ce TSRMLS_DC) +{ + return http_deflatestream_object_new_ex(ce, NULL, NULL); +} + +zend_object_value _http_deflatestream_object_new_ex(zend_class_entry *ce, http_encoding_stream *s, http_deflatestream_object **ptr TSRMLS_DC) +{ + zend_object_value ov; + http_deflatestream_object *o; + + o = ecalloc(1, sizeof(http_deflatestream_object)); + o->zo.ce = ce; + + if (ptr) { + *ptr = o; + } + + if (s) { + o->stream = s; + } + + ALLOC_HASHTABLE(OBJ_PROP(o)); + zend_hash_init(OBJ_PROP(o), zend_hash_num_elements(&ce->default_properties), NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(OBJ_PROP(o), &ce->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + + ov.handle = putObject(http_deflatestream_object, o); + ov.handlers = &http_deflatestream_object_handlers; + + return ov; +} + +zend_object_value _http_deflatestream_object_clone_obj(zval *this_ptr TSRMLS_DC) +{ + http_encoding_stream *s; + zend_object_value new_ov; + http_deflatestream_object *new_obj = NULL; + getObject(http_deflatestream_object, old_obj); + + s = ecalloc(1, sizeof(http_encoding_stream)); + s->flags = old_obj->stream->flags; + deflateCopy(&s->stream, &old_obj->stream->stream); + s->stream.opaque = phpstr_dup(s->stream.opaque); + + new_ov = http_deflatestream_object_new_ex(old_obj->zo.ce, s, &new_obj); + zend_objects_clone_members(&new_obj->zo, new_ov, &old_obj->zo, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC); + + return new_ov; +} + +void _http_deflatestream_object_free(zend_object *object TSRMLS_DC) +{ + http_deflatestream_object *o = (http_deflatestream_object *) object; + + if (o->stream) { + http_encoding_deflate_stream_free(&o->stream); + } + freeObject(o); +} + +/* {{{ proto void HttpDeflateStream::__construct([int flags = 0]) + Creates a new HttpDeflateStream object instance. */ +PHP_METHOD(HttpDeflateStream, __construct) +{ + long flags = 0; + + SET_EH_THROW_HTTP(); + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flags)) { + getObject(http_deflatestream_object, obj); + + if (!obj->stream) { + obj->stream = http_encoding_deflate_stream_init(NULL, flags & 0x0fffffff); + } else { + http_error_ex(HE_WARNING, HTTP_E_ENCODING, "HttpDeflateStream cannot be initialized twice"); + } + } + SET_EH_NORMAL(); +} +/* }}} */ + +/* {{{ proto HttpDeflateStream HttpDeflateStream::factory([int flags[, string class = "HttpDeflateStream"]]) + Creates a new HttpDeflateStream object instance. */ +PHP_METHOD(HttpDeflateStream, factory) +{ + long flags = 0; + char *cn = NULL; + int cl = 0; + + SET_EH_THROW_HTTP(); + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ls", &flags, &cn, &cl)) { + zend_object_value ov; + http_encoding_stream *s = http_encoding_deflate_stream_init(NULL, flags & 0x0fffffff); + + if (SUCCESS == http_object_new(&ov, cn, cl, _http_deflatestream_object_new_ex, http_deflatestream_object_ce, s, NULL)) { + RETVAL_OBJVAL(ov, 0); + } + } + SET_EH_NORMAL(); +} +/* }}} */ + +/* {{{ proto string HttpDeflateStream::update(string data) + Passes more data through the deflate stream. */ +PHP_METHOD(HttpDeflateStream, update) +{ + int data_len; + size_t encoded_len = 0; + char *data, *encoded = NULL; + getObject(http_deflatestream_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &data_len)) { + RETURN_FALSE; + } + + if (!obj->stream && !(obj->stream = http_encoding_deflate_stream_init(NULL, 0))) { + RETURN_FALSE; + } + + if (SUCCESS == http_encoding_deflate_stream_update(obj->stream, data, data_len, &encoded, &encoded_len)) { + RETURN_STRINGL(encoded, encoded_len, 0); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto string HttpDeflateStream::flush([string data]) + Flushes the deflate stream. */ +PHP_METHOD(HttpDeflateStream, flush) +{ + int data_len = 0; + size_t updated_len = 0, encoded_len = 0; + char *updated = NULL, *encoded = NULL, *data = NULL; + getObject(http_deflatestream_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &data, &data_len)) { + RETURN_FALSE; + } + + if (!obj->stream && !(obj->stream = http_encoding_deflate_stream_init(NULL, 0))) { + RETURN_FALSE; + } + + if (data_len) { + if (SUCCESS != http_encoding_deflate_stream_update(obj->stream, data, data_len, &updated, &updated_len)) { + RETURN_FALSE; + } + } + + if (SUCCESS == http_encoding_deflate_stream_flush(obj->stream, &encoded, &encoded_len)) { + if (updated_len) { + updated = erealloc(updated, updated_len + encoded_len + 1); + updated[updated_len + encoded_len] = '\0'; + memcpy(updated + updated_len, encoded, encoded_len); + STR_FREE(encoded); + updated_len += encoded_len; + RETURN_STRINGL(updated, updated_len, 0); + } else if (encoded) { + RETVAL_STRINGL(encoded, encoded_len, 0); + } else { + RETVAL_NULL(); + } + } else { + RETVAL_FALSE; + } + STR_FREE(updated); +} +/* }}} */ + +/* {{{ proto string HttpDeflateStream::finish([string data]) + Finalizes the deflate stream. The deflate stream can be reused after finalizing. */ +PHP_METHOD(HttpDeflateStream, finish) +{ + int data_len = 0; + size_t updated_len = 0, encoded_len = 0; + char *updated = NULL, *encoded = NULL, *data = NULL; + getObject(http_deflatestream_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &data, &data_len)) { + RETURN_FALSE; + } + + if (!obj->stream && !(obj->stream = http_encoding_deflate_stream_init(NULL, 0))) { + RETURN_FALSE; + } + + if (data_len) { + if (SUCCESS != http_encoding_deflate_stream_update(obj->stream, data, data_len, &updated, &updated_len)) { + RETURN_FALSE; + } + } + + if (SUCCESS == http_encoding_deflate_stream_finish(obj->stream, &encoded, &encoded_len)) { + if (updated_len) { + updated = erealloc(updated, updated_len + encoded_len + 1); + updated[updated_len + encoded_len] = '\0'; + memcpy(updated + updated_len, encoded, encoded_len); + STR_FREE(encoded); + updated_len += encoded_len; + RETVAL_STRINGL(updated, updated_len, 0); + } else { + STR_FREE(updated); + RETVAL_STRINGL(encoded, encoded_len, 0); + } + } else { + STR_FREE(updated); + RETVAL_FALSE; + } + + http_encoding_deflate_stream_dtor(obj->stream); + http_encoding_deflate_stream_init(obj->stream, obj->stream->flags); +} +/* }}} */ + +#endif /* ZEND_ENGINE_2 && HTTP_HAVE_ZLIB*/ + +/* + * 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 + */ + --- /dev/null +++ b/ext/http/http_encoding_api.c @@ -0,0 +1,780 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_encoding_api.c 305668 2010-11-22 20:17:41Z iliaa $ */ + +#define HTTP_WANT_ZLIB +#include "php_http.h" + +#include "php_http_api.h" +#include "php_http_encoding_api.h" +#include "php_http_send_api.h" +#include "php_http_headers_api.h" + +/* {{{ */ +#ifdef HTTP_HAVE_ZLIB +PHP_MINIT_FUNCTION(http_encoding) +{ + HTTP_LONG_CONSTANT("HTTP_DEFLATE_LEVEL_DEF", HTTP_DEFLATE_LEVEL_DEF); + HTTP_LONG_CONSTANT("HTTP_DEFLATE_LEVEL_MIN", HTTP_DEFLATE_LEVEL_MIN); + HTTP_LONG_CONSTANT("HTTP_DEFLATE_LEVEL_MAX", HTTP_DEFLATE_LEVEL_MAX); + HTTP_LONG_CONSTANT("HTTP_DEFLATE_TYPE_ZLIB", HTTP_DEFLATE_TYPE_ZLIB); + HTTP_LONG_CONSTANT("HTTP_DEFLATE_TYPE_GZIP", HTTP_DEFLATE_TYPE_GZIP); + HTTP_LONG_CONSTANT("HTTP_DEFLATE_TYPE_RAW", HTTP_DEFLATE_TYPE_RAW); + HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_DEF", HTTP_DEFLATE_STRATEGY_DEF); + HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_FILT", HTTP_DEFLATE_STRATEGY_FILT); + HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_HUFF", HTTP_DEFLATE_STRATEGY_HUFF); + HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_RLE", HTTP_DEFLATE_STRATEGY_RLE); + HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_FIXED", HTTP_DEFLATE_STRATEGY_FIXED); + + HTTP_LONG_CONSTANT("HTTP_ENCODING_STREAM_FLUSH_NONE", HTTP_ENCODING_STREAM_FLUSH_NONE); + HTTP_LONG_CONSTANT("HTTP_ENCODING_STREAM_FLUSH_SYNC", HTTP_ENCODING_STREAM_FLUSH_SYNC); + HTTP_LONG_CONSTANT("HTTP_ENCODING_STREAM_FLUSH_FULL", HTTP_ENCODING_STREAM_FLUSH_FULL); + + return SUCCESS; +} + +PHP_RINIT_FUNCTION(http_encoding) +{ + if (HTTP_G->send.inflate.start_auto) { + php_ob_set_internal_handler(_http_ob_inflatehandler, HTTP_INFLATE_BUFFER_SIZE, "http inflate", 0 TSRMLS_CC); + } + if (HTTP_G->send.deflate.start_auto) { + php_ob_set_internal_handler(_http_ob_deflatehandler, HTTP_DEFLATE_BUFFER_SIZE, "http deflate", 0 TSRMLS_CC); + } + return SUCCESS; +} + +PHP_RSHUTDOWN_FUNCTION(http_encoding) +{ + if (HTTP_G->send.deflate.stream) { + http_encoding_deflate_stream_free((http_encoding_stream **) &HTTP_G->send.deflate.stream); + } + if (HTTP_G->send.inflate.stream) { + http_encoding_inflate_stream_free((http_encoding_stream **) &HTTP_G->send.inflate.stream); + } + return SUCCESS; +} +#endif +/* }}} */ + +/* {{{ eol_match(char **, int *) */ +static inline int eol_match(char **line, int *eol_len) +{ + char *ptr = *line; + + while (' ' == *ptr) ++ptr; + + if (ptr == http_locate_eol(*line, eol_len)) { + *line = ptr; + return 1; + } else { + return 0; + } +} +/* }}} */ + +/* {{{ char *http_encoding_dechunk(char *, size_t, char **, size_t *) */ +PHP_HTTP_API const char *_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); + + 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) { + http_error(HE_NOTICE, HTTP_E_ENCODING, "Data does not seem to be chunked encoded"); + memcpy(*decoded, encoded, encoded_len); + *decoded_len = encoded_len; + return encoded + encoded_len; + } else { + efree(*decoded); + http_error_ex(HE_WARNING, HTTP_E_ENCODING, "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 */ + while (*e_ptr == '0') ++e_ptr; + 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) { + http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Expected CRLF at pos %tu of %zu but got 0x%02X 0x%02X", n_ptr - encoded, encoded_len, *n_ptr, *(n_ptr + 1)); + } else { + http_error_ex(HE_WARNING, HTTP_E_ENCODING, "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)) { + http_error_ex(HE_WARNING, HTTP_E_ENCODING, "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; +} +/* }}} */ + +/* {{{ int http_encoding_response_start(size_t) */ +PHP_HTTP_API int _http_encoding_response_start(size_t content_length, zend_bool ignore_http_ohandler TSRMLS_DC) +{ + int response = HTTP_G->send.deflate.response; + int ohandler = php_ob_handler_used("ob_gzhandler" TSRMLS_CC) || php_ob_handler_used("zlib output compression" TSRMLS_CC); + + if (!ohandler && !ignore_http_ohandler) { + ohandler = php_ob_handler_used("ob_deflatehandler" TSRMLS_CC) || php_ob_handler_used("http deflate" TSRMLS_CC); + } + + if (response && !ohandler) { +#ifdef HTTP_HAVE_ZLIB + HashTable *selected; + zval zsupported; + + HTTP_G->send.deflate.encoding = 0; + + INIT_PZVAL(&zsupported); + array_init(&zsupported); + add_next_index_stringl(&zsupported, "gzip", lenof("gzip"), 1); + add_next_index_stringl(&zsupported, "x-gzip", lenof("x-gzip"), 1); + add_next_index_stringl(&zsupported, "deflate", lenof("deflate"), 1); + + if ((selected = http_negotiate_encoding(&zsupported))) { + STATUS hs = FAILURE; + char *encoding = NULL; + ulong idx; + + if (HASH_KEY_IS_STRING == zend_hash_get_current_key(selected, &encoding, &idx, 0) && encoding) { + if (!strcmp(encoding, "gzip") || !strcmp(encoding, "x-gzip")) { + if (SUCCESS == (hs = http_send_header_string("Content-Encoding: gzip"))) { + HTTP_G->send.deflate.encoding = HTTP_ENCODING_GZIP; + } + } else if (!strcmp(encoding, "deflate")) { + if (SUCCESS == (hs = http_send_header_string("Content-Encoding: deflate"))) { + HTTP_G->send.deflate.encoding = HTTP_ENCODING_DEFLATE; + } + } + if (SUCCESS == hs) { + http_send_header_string("Vary: Accept-Encoding"); + } + } + + zend_hash_destroy(selected); + FREE_HASHTABLE(selected); + } + + zval_dtor(&zsupported); +#else + HTTP_G->send.deflate.encoding = 0; + php_start_ob_buffer_named("ob_gzhandler", 0, 0 TSRMLS_CC); +#endif /* HTTP_HAVE_ZLIB */ + } else if (content_length && !ohandler) { + /* emit a content-length header */ + phpstr header; + + phpstr_init(&header); + phpstr_appendf(&header, "Content-Length: %zu", content_length); + phpstr_fix(&header); + http_send_header_string_ex(PHPSTR_VAL(&header), PHPSTR_LEN(&header), 1); + phpstr_dtor(&header); + } else { + HTTP_G->send.deflate.encoding = 0; + } + + return HTTP_G->send.deflate.encoding; +} +/* }}} */ + +#ifdef HTTP_HAVE_ZLIB + +/* {{{ inline int http_inflate_rounds */ +static inline int http_inflate_rounds(z_stream *Z, int flush, char **buf, size_t *len) +{ + int status = 0, round = 0; + phpstr buffer; + + *buf = NULL; + *len = 0; + + phpstr_init_ex(&buffer, Z->avail_in, PHPSTR_INIT_PREALLOC); + + do { + if (PHPSTR_NOMEM == phpstr_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); + + buffer.used += buffer.free - Z->avail_out; + 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 + HTTP_INFLATE_BUFFER_SIZE_ALIGN(buffer.size); + } + } while ((Z_BUF_ERROR == status || (Z_OK == status && Z->avail_in)) && ++round < HTTP_INFLATE_ROUNDS); + + if (status == Z_OK || status == Z_STREAM_END) { + phpstr_shrink(&buffer); + phpstr_fix(&buffer); + *buf = buffer.data; + *len = buffer.used; + } else { + phpstr_dtor(&buffer); + } + + return status; +} +/* }}} */ + +/* {{{ STATUS http_encoding_deflate(int, char *, size_t, char **, size_t *) */ +PHP_HTTP_API STATUS _http_encoding_deflate(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC) +{ + int status, level, wbits, strategy; + z_stream Z; + + HTTP_DEFLATE_LEVEL_SET(flags, level); + HTTP_DEFLATE_WBITS_SET(flags, wbits); + 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 = HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len); + *encoded = emalloc_rel(*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_rel(*encoded, Z.total_out + 1); + (*encoded)[*encoded_len = Z.total_out] = '\0'; + return SUCCESS; + } else { + STR_SET(*encoded, NULL); + *encoded_len = 0; + } + } + + http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not deflate data: %s", zError(status)); + return FAILURE; +} +/* }}} */ + +/* {{{ STATUS http_encoding_inflate(char *, size_t, char **, size_t) */ +PHP_HTTP_API STATUS _http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC) +{ + z_stream Z; + int status, wbits = 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; + + switch (status = 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 (HTTP_WINDOW_BITS_ANY == wbits) { + inflateEnd(&Z); + wbits = HTTP_WINDOW_BITS_RAW; + goto retry_raw_inflate; + } + } + inflateEnd(&Z); + } + + http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not inflate data: %s", zError(status)); + return FAILURE; +} +/* }}} */ + +/* {{{ http_encoding_stream *_http_encoding_deflate_stream_init(http_encoding_stream *, int) */ +PHP_HTTP_API http_encoding_stream *_http_encoding_deflate_stream_init(http_encoding_stream *s, int flags ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC) +{ + int status, level, wbits, strategy, free_stream; + + if ((free_stream = !s)) { + s = pemalloc_rel(sizeof(http_encoding_stream), (flags & HTTP_ENCODING_STREAM_PERSISTENT)); + } + memset(s, 0, sizeof(http_encoding_stream)); + s->flags = flags; + + HTTP_DEFLATE_LEVEL_SET(flags, level); + HTTP_DEFLATE_WBITS_SET(flags, wbits); + HTTP_DEFLATE_STRATEGY_SET(flags, strategy); + + if (Z_OK == (status = deflateInit2(&s->stream, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy))) { + int p = (flags & HTTP_ENCODING_STREAM_PERSISTENT) ? PHPSTR_INIT_PERSISTENT:0; + + if ((s->stream.opaque = phpstr_init_ex(NULL, HTTP_DEFLATE_BUFFER_SIZE, p))) { + return s; + } + deflateEnd(&s->stream); + status = Z_MEM_ERROR; + } + + http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to initialize deflate encoding stream: %s", zError(status)); + if (free_stream) { + efree(s); + } + return NULL; +} +/* }}} */ + +/* {{{ http_encoding_stream *http_encoding_inflate_stream_init(http_encoding_stream *, int) */ +PHP_HTTP_API http_encoding_stream *_http_encoding_inflate_stream_init(http_encoding_stream *s, int flags ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC) +{ + int status, wbits, free_stream; + + if ((free_stream = !s)) { + s = pemalloc_rel(sizeof(http_encoding_stream), (flags & HTTP_ENCODING_STREAM_PERSISTENT)); + } + memset(s, 0, sizeof(http_encoding_stream)); + s->flags = flags; + + HTTP_INFLATE_WBITS_SET(flags, wbits); + + if (Z_OK == (status = inflateInit2(&s->stream, wbits))) { + int p = (flags & HTTP_ENCODING_STREAM_PERSISTENT) ? PHPSTR_INIT_PERSISTENT:0; + + if ((s->stream.opaque = phpstr_init_ex(NULL, HTTP_DEFLATE_BUFFER_SIZE, p))) { + return s; + } + inflateEnd(&s->stream); + status = Z_MEM_ERROR; + } + + http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to initialize inflate stream: %s", zError(status)); + if (free_stream) { + efree(s); + } + return NULL; +} +/* }}} */ + +/* {{{ STATUS http_encoding_deflate_stream_update(http_encoding_stream *, char *, size_t, char **, size_t *) */ +PHP_HTTP_API STATUS _http_encoding_deflate_stream_update(http_encoding_stream *s, const char *data, size_t data_len, char **encoded, size_t *encoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC) +{ + int status; + + /* append input to our buffer */ + phpstr_append(PHPSTR(s->stream.opaque), data, data_len); + + s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque); + s->stream.avail_in = PHPSTR_LEN(s->stream.opaque); + + /* deflate */ + *encoded_len = HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len); + *encoded = emalloc_rel(*encoded_len); + s->stream.avail_out = *encoded_len; + s->stream.next_out = (Bytef *) *encoded; + + switch (status = deflate(&s->stream, HTTP_ENCODING_STREAM_FLUSH_FLAG(s->flags))) { + case Z_OK: + case Z_STREAM_END: + /* cut processed chunk off the buffer */ + if (s->stream.avail_in) { + phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in); + } else { + phpstr_reset(PHPSTR(s->stream.opaque)); + } + + /* size buffer down to actual size */ + *encoded_len -= s->stream.avail_out; + *encoded = erealloc_rel(*encoded, *encoded_len + 1); + (*encoded)[*encoded_len] = '\0'; + return SUCCESS; + } + + STR_SET(*encoded, NULL); + *encoded_len = 0; + http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to update deflate stream: %s", zError(status)); + return FAILURE; +} +/* }}} */ + +/* {{{ STATUS http_encoding_inflate_stream_update(http_encoding_stream *, char *, size_t, char **, size_t *) */ +PHP_HTTP_API STATUS _http_encoding_inflate_stream_update(http_encoding_stream *s, const char *data, size_t data_len, char **decoded, size_t *decoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC) +{ + int status; + + /* append input to buffer */ + phpstr_append(PHPSTR(s->stream.opaque), data, data_len); + +retry_raw_inflate: + s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque); + s->stream.avail_in = PHPSTR_LEN(s->stream.opaque); + + switch (status = http_inflate_rounds(&s->stream, HTTP_ENCODING_STREAM_FLUSH_FLAG(s->flags), decoded, decoded_len)) { + case Z_OK: + case Z_STREAM_END: + /* cut off */ + if (s->stream.avail_in) { + phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in); + } else { + phpstr_reset(PHPSTR(s->stream.opaque)); + } + return SUCCESS; + + case Z_DATA_ERROR: + /* raw deflated data ? */ + if (!(s->flags & HTTP_INFLATE_TYPE_RAW) && !s->stream.total_out) { + inflateEnd(&s->stream); + s->flags |= HTTP_INFLATE_TYPE_RAW; + inflateInit2(&s->stream, HTTP_WINDOW_BITS_RAW); + goto retry_raw_inflate; + } + } + + http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to update inflate stream: %s", zError(status)); + return FAILURE; +} +/* }}} */ + +/* {{{ STATUS http_encoding_deflate_stream_flush(http_encoding_stream *, char **, size_t *) */ +PHP_HTTP_API STATUS _http_encoding_deflate_stream_flush(http_encoding_stream *s, char **encoded, size_t *encoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC) +{ + int status; + + *encoded_len = HTTP_DEFLATE_BUFFER_SIZE; + *encoded = emalloc_rel(*encoded_len); + + s->stream.avail_in = 0; + s->stream.next_in = NULL; + s->stream.avail_out = *encoded_len; + s->stream.next_out = (Bytef *) *encoded; + + switch (status = deflate(&s->stream, Z_FULL_FLUSH)) { + case Z_OK: + case Z_STREAM_END: + *encoded_len = HTTP_DEFLATE_BUFFER_SIZE - s->stream.avail_out; + *encoded = erealloc_rel(*encoded, *encoded_len + 1); + (*encoded)[*encoded_len] = '\0'; + return SUCCESS; + } + + STR_SET(*encoded, NULL); + *encoded_len = 0; + http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to flush deflate stream: %s", zError(status)); + return FAILURE; +} +/* }}} */ + +/* {{{ STATUS http_encoding_inflate_straem_flush(http_encoding_stream *, char **, size_t *) */ +PHP_HTTP_API STATUS _http_encoding_inflate_stream_flush(http_encoding_stream *s, char **decoded, size_t *decoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC) +{ + /* noop */ + *decoded = estrndup("", *decoded_len = 0); + return SUCCESS; +} +/* }}} */ + +/* {{{ STATUS http_encoding_deflate_stream_finish(http_encoding_stream *, char **, size_t *) */ +PHP_HTTP_API STATUS _http_encoding_deflate_stream_finish(http_encoding_stream *s, char **encoded, size_t *encoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC) +{ + int status; + + *encoded_len = HTTP_DEFLATE_BUFFER_SIZE; + *encoded = emalloc_rel(*encoded_len); + + /* deflate remaining input */ + s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque); + s->stream.avail_in = PHPSTR_LEN(s->stream.opaque); + + s->stream.avail_out = *encoded_len; + s->stream.next_out = (Bytef *) *encoded; + + do { + status = deflate(&s->stream, Z_FINISH); + } while (Z_OK == status); + + if (Z_STREAM_END == status) { + /* cut processed intp off */ + phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in); + + /* size down */ + *encoded_len -= s->stream.avail_out; + *encoded = erealloc_rel(*encoded, *encoded_len + 1); + (*encoded)[*encoded_len] = '\0'; + return SUCCESS; + } + + STR_SET(*encoded, NULL); + *encoded_len = 0; + http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to finish deflate stream: %s", zError(status)); + return FAILURE; +} +/* }}} */ + +/* {{{ STATUS http_encoding_inflate_stream_finish(http_encoding_stream *, char **, size_t *) */ +PHP_HTTP_API STATUS _http_encoding_inflate_stream_finish(http_encoding_stream *s, char **decoded, size_t *decoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC) +{ + int status; + + if (!PHPSTR_LEN(s->stream.opaque)) { + *decoded = NULL; + *decoded_len = 0; + return SUCCESS; + } + + *decoded_len = (PHPSTR_LEN(s->stream.opaque) + 1) * HTTP_INFLATE_ROUNDS; + *decoded = emalloc_rel(*decoded_len); + + /* inflate remaining input */ + s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque); + s->stream.avail_in = PHPSTR_LEN(s->stream.opaque); + + s->stream.avail_out = *decoded_len; + s->stream.next_out = (Bytef *) *decoded; + + if (Z_STREAM_END == (status = inflate(&s->stream, Z_FINISH))) { + /* cut processed input off */ + phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in); + + /* size down */ + *decoded_len -= s->stream.avail_out; + *decoded = erealloc_rel(*decoded, *decoded_len + 1); + (*decoded)[*decoded_len] = '\0'; + return SUCCESS; + } + + STR_SET(*decoded, NULL); + *decoded_len = 0; + http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to finish inflate stream: %s", zError(status)); + return FAILURE; +} +/* }}} */ + +/* {{{ void http_encoding_deflate_stream_dtor(http_encoding_stream *) */ +PHP_HTTP_API void _http_encoding_deflate_stream_dtor(http_encoding_stream *s TSRMLS_DC) +{ + if (s) { + if (s->stream.opaque) { + phpstr_free((phpstr **) &s->stream.opaque); + } + deflateEnd(&s->stream); + } +} +/* }}} */ + +/* {{{ void http_encoding_inflate_stream_dtor(http_encoding_stream *) */ +PHP_HTTP_API void _http_encoding_inflate_stream_dtor(http_encoding_stream *s TSRMLS_DC) +{ + if (s) { + if (s->stream.opaque) { + phpstr_free((phpstr **) &s->stream.opaque); + } + inflateEnd(&s->stream); + } +} +/* }}} */ + +/* {{{ void http_encoding_deflate_stream_free(http_encoding_stream **) */ +PHP_HTTP_API void _http_encoding_deflate_stream_free(http_encoding_stream **s TSRMLS_DC) +{ + if (s) { + http_encoding_deflate_stream_dtor(*s); + if (*s) { + pefree(*s, (*s)->flags & HTTP_ENCODING_STREAM_PERSISTENT); + } + *s = NULL; + } +} +/* }}} */ + +/* {{{ void http_encoding_inflate_stream_free(http_encoding_stream **) */ +PHP_HTTP_API void _http_encoding_inflate_stream_free(http_encoding_stream **s TSRMLS_DC) +{ + if (s) { + http_encoding_inflate_stream_dtor(*s); + if (*s) { + pefree(*s, (*s)->flags & HTTP_ENCODING_STREAM_PERSISTENT); + } + *s = NULL; + } +} +/* }}} */ + +/* {{{ void http_ob_deflatehandler(char *, uint, char **, uint *, int) */ +void _http_ob_deflatehandler(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC) +{ + int encoding; + + *handled_output = NULL; + *handled_output_len = 0; + + if (mode & PHP_OUTPUT_HANDLER_START) { + int flags; + + if (HTTP_G->send.deflate.stream) { + zend_error(E_ERROR, "ob_deflatehandler() can only be used once"); + return; + } + + HTTP_G->send.deflate.response = 1; + encoding = http_encoding_response_start(0, 1); + HTTP_G->send.deflate.response = 0; + + switch (encoding) { + case HTTP_ENCODING_GZIP: + flags = HTTP_DEFLATE_TYPE_GZIP; + break; + + case HTTP_ENCODING_DEFLATE: + flags = HTTP_DEFLATE_TYPE_ZLIB; + break; + + default: + goto deflate_passthru_plain; + } + + flags |= (HTTP_G->send.deflate.start_flags &~ 0xf0); + HTTP_G->send.deflate.stream = http_encoding_deflate_stream_init(NULL, flags); + } + + if (HTTP_G->send.deflate.stream) { + if (output_len) { + size_t tmp_len; + + http_encoding_deflate_stream_update((http_encoding_stream *) HTTP_G->send.deflate.stream, output, output_len, handled_output, &tmp_len); + *handled_output_len = tmp_len; + } + + if (mode & PHP_OUTPUT_HANDLER_END) { + char *remaining = NULL; + size_t remaining_len = 0; + + http_encoding_deflate_stream_finish((http_encoding_stream *) HTTP_G->send.deflate.stream, &remaining, &remaining_len); + http_encoding_deflate_stream_free((http_encoding_stream **) &HTTP_G->send.deflate.stream); + if (remaining) { + *handled_output = erealloc(*handled_output, *handled_output_len + remaining_len + 1); + memcpy(*handled_output + *handled_output_len, remaining, remaining_len); + (*handled_output)[*handled_output_len += remaining_len] = '\0'; + efree(remaining); + } + } + } else { +deflate_passthru_plain: + *handled_output = estrndup(output, *handled_output_len = output_len); + } +} +/* }}} */ + +/* {{{ void http_ob_inflatehandler(char *, uint, char **, uint *, int) */ +void _http_ob_inflatehandler(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC) +{ + *handled_output = NULL; + *handled_output_len = 0; + + if (mode & PHP_OUTPUT_HANDLER_START) { + if (HTTP_G->send.inflate.stream) { + zend_error(E_ERROR, "ob_inflatehandler() can only be used once"); + return; + } + HTTP_G->send.inflate.stream = http_encoding_inflate_stream_init(NULL, (HTTP_G->send.inflate.start_flags &~ 0xf0)); + } + + if (HTTP_G->send.inflate.stream) { + if (output_len) { + size_t tmp_len; + + http_encoding_inflate_stream_update((http_encoding_stream *) HTTP_G->send.inflate.stream, output, output_len, handled_output, &tmp_len); + *handled_output_len = tmp_len; + } + + if (mode & PHP_OUTPUT_HANDLER_END) { + char *remaining = NULL; + size_t remaining_len = 0; + + http_encoding_inflate_stream_finish((http_encoding_stream *) HTTP_G->send.inflate.stream, &remaining, &remaining_len); + http_encoding_inflate_stream_free((http_encoding_stream **) &HTTP_G->send.inflate.stream); + if (remaining) { + *handled_output = erealloc(*handled_output, *handled_output_len + remaining_len + 1); + memcpy(*handled_output + *handled_output_len, remaining, remaining_len); + (*handled_output)[*handled_output_len += remaining_len] = '\0'; + efree(remaining); + } + } + } else { + *handled_output = estrndup(output, *handled_output_len = output_len); + } +} +/* }}} */ + +#endif /* HTTP_HAVE_ZLIB */ + +/* + * 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 + */ + --- /dev/null +++ b/ext/http/http_exception_object.c @@ -0,0 +1,186 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_exception_object.c 292841 2009-12-31 08:48:57Z mike $ */ + +#include "php_http.h" + +#ifdef ZEND_ENGINE_2 + +#ifndef HTTP_DBG_EXCEPTIONS +# define HTTP_DBG_EXCEPTIONS 0 +#endif + +#include "zend_interfaces.h" +#include "zend_exceptions.h" +#include "php_http_exception_object.h" + +zend_class_entry *http_exception_object_ce; +zend_class_entry *HTTP_EX_CE(runtime); +zend_class_entry *HTTP_EX_CE(header); +zend_class_entry *HTTP_EX_CE(malformed_headers); +zend_class_entry *HTTP_EX_CE(request_method); +zend_class_entry *HTTP_EX_CE(message_type); +zend_class_entry *HTTP_EX_CE(invalid_param); +zend_class_entry *HTTP_EX_CE(encoding); +zend_class_entry *HTTP_EX_CE(request); +zend_class_entry *HTTP_EX_CE(request_pool); +zend_class_entry *HTTP_EX_CE(socket); +zend_class_entry *HTTP_EX_CE(response); +zend_class_entry *HTTP_EX_CE(url); +zend_class_entry *HTTP_EX_CE(querystring); + +#define HTTP_EMPTY_ARGS(method) HTTP_EMPTY_ARGS_EX(HttpException, method, 0) +#define HTTP_EXCEPTION_ME(method, visibility) PHP_ME(HttpException, method, HTTP_ARGS(HttpException, method), visibility) + +HTTP_EMPTY_ARGS(__toString); + +zend_function_entry http_exception_object_fe[] = { + HTTP_EXCEPTION_ME(__toString, ZEND_ACC_PUBLIC) + + EMPTY_FUNCTION_ENTRY +}; + +#if HTTP_DBG_EXCEPTIONS +static void 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 + +PHP_MINIT_FUNCTION(http_exception_object) +{ + HTTP_REGISTER_CLASS(HttpException, http_exception_object, ZEND_EXCEPTION_GET_DEFAULT(), 0); + + zend_declare_property_null(HTTP_EX_DEF_CE, "innerException", lenof("innerException"), ZEND_ACC_PUBLIC TSRMLS_CC); + + HTTP_REGISTER_EXCEPTION(HttpRuntimeException, HTTP_EX_CE(runtime), HTTP_EX_DEF_CE); + HTTP_REGISTER_EXCEPTION(HttpInvalidParamException, HTTP_EX_CE(invalid_param), HTTP_EX_DEF_CE); + HTTP_REGISTER_EXCEPTION(HttpHeaderException, HTTP_EX_CE(header), HTTP_EX_DEF_CE); + HTTP_REGISTER_EXCEPTION(HttpMalformedHeadersException, HTTP_EX_CE(malformed_headers), HTTP_EX_DEF_CE); + HTTP_REGISTER_EXCEPTION(HttpRequestMethodException, HTTP_EX_CE(request_method), HTTP_EX_DEF_CE); + HTTP_REGISTER_EXCEPTION(HttpMessageTypeException, HTTP_EX_CE(message_type), HTTP_EX_DEF_CE); + HTTP_REGISTER_EXCEPTION(HttpEncodingException, HTTP_EX_CE(encoding), HTTP_EX_DEF_CE); + HTTP_REGISTER_EXCEPTION(HttpRequestException, HTTP_EX_CE(request), HTTP_EX_DEF_CE); + + zend_declare_property_long(HTTP_EX_CE(request), "curlCode", lenof("curlCode"), 0, ZEND_ACC_PUBLIC TSRMLS_CC); + + HTTP_REGISTER_EXCEPTION(HttpRequestPoolException, HTTP_EX_CE(request_pool), HTTP_EX_DEF_CE); + HTTP_REGISTER_EXCEPTION(HttpSocketException, HTTP_EX_CE(socket), HTTP_EX_DEF_CE); + HTTP_REGISTER_EXCEPTION(HttpResponseException, HTTP_EX_CE(response), HTTP_EX_DEF_CE); + HTTP_REGISTER_EXCEPTION(HttpUrlException, HTTP_EX_CE(url), HTTP_EX_DEF_CE); + HTTP_REGISTER_EXCEPTION(HttpQueryStringException, HTTP_EX_CE(querystring), HTTP_EX_DEF_CE); + + HTTP_LONG_CONSTANT("HTTP_E_RUNTIME", HTTP_E_RUNTIME); + HTTP_LONG_CONSTANT("HTTP_E_INVALID_PARAM", HTTP_E_INVALID_PARAM); + HTTP_LONG_CONSTANT("HTTP_E_HEADER", HTTP_E_HEADER); + HTTP_LONG_CONSTANT("HTTP_E_MALFORMED_HEADERS", HTTP_E_MALFORMED_HEADERS); + HTTP_LONG_CONSTANT("HTTP_E_REQUEST_METHOD", HTTP_E_REQUEST_METHOD); + HTTP_LONG_CONSTANT("HTTP_E_MESSAGE_TYPE", HTTP_E_MESSAGE_TYPE); + HTTP_LONG_CONSTANT("HTTP_E_ENCODING", HTTP_E_ENCODING); + HTTP_LONG_CONSTANT("HTTP_E_REQUEST", HTTP_E_REQUEST); + HTTP_LONG_CONSTANT("HTTP_E_REQUEST_POOL", HTTP_E_REQUEST_POOL); + HTTP_LONG_CONSTANT("HTTP_E_SOCKET", HTTP_E_SOCKET); + HTTP_LONG_CONSTANT("HTTP_E_RESPONSE", HTTP_E_RESPONSE); + HTTP_LONG_CONSTANT("HTTP_E_URL", HTTP_E_URL); + HTTP_LONG_CONSTANT("HTTP_E_QUERYSTRING", HTTP_E_QUERYSTRING); + +#if HTTP_DBG_EXCEPTIONS + zend_throw_exception_hook=http_exception_hook; +#endif + + return SUCCESS; +} + +zend_class_entry *_http_exception_get_default() +{ + return http_exception_object_ce; +} + +zend_class_entry *_http_exception_get_for_code(long code) +{ + zend_class_entry *ex = http_exception_object_ce; + + switch (code) { + case HTTP_E_RUNTIME: ex = HTTP_EX_CE(runtime); break; + case HTTP_E_INVALID_PARAM: ex = HTTP_EX_CE(invalid_param); break; + case HTTP_E_HEADER: ex = HTTP_EX_CE(header); break; + case HTTP_E_MALFORMED_HEADERS: ex = HTTP_EX_CE(malformed_headers); break; + case HTTP_E_REQUEST_METHOD: ex = HTTP_EX_CE(request_method); break; + case HTTP_E_MESSAGE_TYPE: ex = HTTP_EX_CE(message_type); break; + case HTTP_E_ENCODING: ex = HTTP_EX_CE(encoding); break; + case HTTP_E_REQUEST: ex = HTTP_EX_CE(request); break; + case HTTP_E_REQUEST_POOL: ex = HTTP_EX_CE(request_pool); break; + case HTTP_E_SOCKET: ex = HTTP_EX_CE(socket); break; + case HTTP_E_RESPONSE: ex = HTTP_EX_CE(response); break; + case HTTP_E_URL: ex = HTTP_EX_CE(url); break; + case HTTP_E_QUERYSTRING: ex = HTTP_EX_CE(querystring); break; + } + + return ex; +} + +PHP_METHOD(HttpException, __toString) +{ + phpstr full_str; + zend_class_entry *ce; + zval *zobj = getThis(), *retval = NULL, *m, *f, *l; + + phpstr_init(&full_str); + + do { + ce = Z_OBJCE_P(zobj); + + m = zend_read_property(ce, zobj, "message", lenof("message"), 0 TSRMLS_CC); + f = zend_read_property(ce, zobj, "file", lenof("file"), 0 TSRMLS_CC); + l = zend_read_property(ce, zobj, "line", lenof("line"), 0 TSRMLS_CC); + + if (m && f && l && Z_TYPE_P(m) == IS_STRING && Z_TYPE_P(f) == IS_STRING && Z_TYPE_P(l) == IS_LONG) { + if (zobj != getThis()) { + phpstr_appends(&full_str, " inner "); + } + + phpstr_appendf(&full_str, "exception '%.*s' with message '%.*s' in %.*s:%ld" PHP_EOL, + ce->name_length, ce->name, Z_STRLEN_P(m), Z_STRVAL_P(m), Z_STRLEN_P(f), Z_STRVAL_P(f), Z_LVAL_P(l) + ); + } else { + break; + } + + zobj = zend_read_property(ce, zobj, "innerException", lenof("innerException"), 0 TSRMLS_CC); + } while (Z_TYPE_P(zobj) == IS_OBJECT); + + if (zend_call_method_with_0_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "gettraceasstring", &retval) && Z_TYPE_P(retval) == IS_STRING) { + phpstr_appends(&full_str, "Stack trace:" PHP_EOL); + phpstr_append(&full_str, Z_STRVAL_P(retval), Z_STRLEN_P(retval)); + zval_ptr_dtor(&retval); + } + + RETURN_PHPSTR_VAL(&full_str); +} + +#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 + */ + --- /dev/null +++ b/ext/http/http_filter_api.c @@ -0,0 +1,534 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_filter_api.c 292841 2009-12-31 08:48:57Z mike $ */ + +#define HTTP_WANT_ZLIB +#include "php_http.h" + +#ifdef ZEND_ENGINE_2 + +#include "php_streams.h" +#include "php_http_api.h" +#include "php_http_encoding_api.h" +#include "php_http_filter_api.h" + +PHP_MINIT_FUNCTION(http_filter) +{ + php_stream_filter_register_factory("http.*", &http_filter_factory TSRMLS_CC); + return SUCCESS; +} + +/* + - +*/ + +#define 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 HTTP_FILTER_OP(filter) \ + http_filter_op_ ##filter +#define HTTP_FILTER_OPS(filter) \ + php_stream_filter_ops HTTP_FILTER_OP(filter) +#define HTTP_FILTER_DTOR(filter) \ + http_filter_ ##filter## _dtor +#define HTTP_FILTER_DESTRUCTOR(filter) \ + void HTTP_FILTER_DTOR(filter)(php_stream_filter *this TSRMLS_DC) +#define HTTP_FILTER_FUNC(filter) \ + http_filter_ ##filter +#define HTTP_FILTER_FUNCTION(filter) \ + php_stream_filter_status_t HTTP_FILTER_FUNC(filter)(HTTP_FILTER_PARAMS) +#define HTTP_FILTER_BUFFER(filter) \ + http_filter_ ##filter## _buffer + +#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 { + phpstr buffer; + ulong hexlen; +} HTTP_FILTER_BUFFER(chunked_decode); + +#ifdef HTTP_HAVE_ZLIB +typedef http_encoding_stream HTTP_FILTER_BUFFER(deflate); +typedef http_encoding_stream HTTP_FILTER_BUFFER(inflate); +#endif /* HTTP_HAVE_ZLIB */ + + +static HTTP_FILTER_FUNCTION(chunked_decode) +{ + int out_avail = 0; + php_stream_bucket *ptr, *nxt; + HTTP_FILTER_BUFFER(chunked_decode) *buffer = (HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract); + + if (bytes_consumed) { + *bytes_consumed = 0; + } + + /* new data available? */ + if (buckets_in->head) { + + /* fetch available bucket data */ + for (ptr = buckets_in->head; ptr; ptr = nxt) { + nxt = ptr->next; + if (bytes_consumed) { + *bytes_consumed += ptr->buflen; + } + + if (PHPSTR_NOMEM == phpstr_append(PHPSTR(buffer), ptr->buf, ptr->buflen)) { + return PSFS_ERR_FATAL; + } + + php_stream_bucket_unlink(ptr TSRMLS_CC); + php_stream_bucket_delref(ptr TSRMLS_CC); + } + } + if (!phpstr_fix(PHPSTR(buffer))) { + return PSFS_ERR_FATAL; + } + + /* we have data in our buffer */ + while (PHPSTR_LEN(buffer)) { + + /* we already know the size of the chunk and are waiting for data */ + if (buffer->hexlen) { + + /* not enough data buffered */ + if (PHPSTR_LEN(buffer) < buffer->hexlen) { + + /* flush anyway? */ + if (flags & PSFS_FLAG_FLUSH_INC) { + + /* flush all data (should only be chunk data) */ + out_avail = 1; + NEW_BUCKET(PHPSTR_VAL(buffer), PHPSTR_LEN(buffer)); + + /* waiting for less data now */ + buffer->hexlen -= PHPSTR_LEN(buffer); + /* no more buffered data */ + phpstr_reset(PHPSTR(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(PHPSTR_VAL(buffer), buffer->hexlen); + + /* remove outgoing data from the buffer */ + phpstr_cut(PHPSTR(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 < PHPSTR_LEN(buffer) && ( + PHPSTR_VAL(buffer)[off] == '\n' || + PHPSTR_VAL(buffer)[off] == '\r')) { + ++off; + } + if (off) { + phpstr_cut(PHPSTR(buffer), 0, off); + } + + /* still data there? */ + if (PHPSTR_LEN(buffer)) { + int eollen; + const char *eolstr; + + /* we need eol, so we can be sure we have all hex digits */ + phpstr_fix(PHPSTR(buffer)); + if ((eolstr = http_locate_eol(PHPSTR_VAL(buffer), &eollen))) { + char *stop = NULL; + + /* read in chunk size */ + buffer->hexlen = strtoul(PHPSTR_VAL(buffer), &stop, 16); + + /* if strtoul() stops at the beginning of the buffered data + there's domething oddly wrong, i.e. bad input */ + if (stop == PHPSTR_VAL(buffer)) { + return PSFS_ERR_FATAL; + } + + /* cut out */ + phpstr_cut(PHPSTR(buffer), 0, eolstr + eollen - PHPSTR_VAL(buffer)); + /* buffer->hexlen is 0 now or contains the size of the next chunk */ + /* 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 ((flags & PSFS_FLAG_FLUSH_CLOSE) && buffer->hexlen && PHPSTR_LEN(buffer)) { + out_avail = 1; + NEW_BUCKET(PHPSTR_VAL(buffer), PHPSTR_LEN(buffer)); + phpstr_reset(PHPSTR(buffer)); + buffer->hexlen = 0; + } + + return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME; +} + +static HTTP_FILTER_DESTRUCTOR(chunked_decode) +{ + HTTP_FILTER_BUFFER(chunked_decode) *b = (HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract); + + phpstr_dtor(PHPSTR(b)); + pefree(b, this->is_persistent); +} + +static HTTP_FILTER_FUNCTION(chunked_encode) +{ + int out_avail = 0; + php_stream_bucket *ptr, *nxt; + + if (bytes_consumed) { + *bytes_consumed = 0; + } + + /* new data available? */ + if (buckets_in->head) { + phpstr buf; + out_avail = 1; + + phpstr_init(&buf); + + /* fetch available bucket data */ + for (ptr = buckets_in->head; ptr; ptr = nxt) { + nxt = ptr->next; + if (bytes_consumed) { + *bytes_consumed += ptr->buflen; + } + + phpstr_appendf(&buf, "%x" HTTP_CRLF, ptr->buflen); + phpstr_append(&buf, ptr->buf, ptr->buflen); + phpstr_appends(&buf, HTTP_CRLF); + + /* pass through */ + NEW_BUCKET(PHPSTR_VAL(&buf), PHPSTR_LEN(&buf)); + /* reset */ + phpstr_reset(&buf); + + php_stream_bucket_unlink(ptr TSRMLS_CC); + php_stream_bucket_delref(ptr TSRMLS_CC); + } + + /* free buffer */ + phpstr_dtor(&buf); + } + + /* terminate with "0" */ + if (flags & PSFS_FLAG_FLUSH_CLOSE) { + out_avail = 1; + NEW_BUCKET("0" HTTP_CRLF, lenof("0" HTTP_CRLF)); + } + + return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME; +} + +static HTTP_FILTER_OPS(chunked_decode) = { + HTTP_FILTER_FUNC(chunked_decode), + HTTP_FILTER_DTOR(chunked_decode), + "http.chunked_decode" +}; + +static HTTP_FILTER_OPS(chunked_encode) = { + HTTP_FILTER_FUNC(chunked_encode), + NULL, + "http.chunked_encode" +}; + +#ifdef HTTP_HAVE_ZLIB + +static HTTP_FILTER_FUNCTION(deflate) +{ + int out_avail = 0; + php_stream_bucket *ptr, *nxt; + HTTP_FILTER_BUFFER(inflate) *buffer = (HTTP_FILTER_BUFFER(deflate) *) this->abstract; + + if (bytes_consumed) { + *bytes_consumed = 0; + } + + /* new data available? */ + if (buckets_in->head) { + + /* fetch available bucket data */ + for (ptr = buckets_in->head; ptr; ptr = nxt) { + char *encoded = NULL; + size_t encoded_len = 0; + + nxt = ptr->next; + if (bytes_consumed) { + *bytes_consumed += ptr->buflen; + } + + if (ptr->buflen) { + http_encoding_deflate_stream_update(buffer, ptr->buf, ptr->buflen, &encoded, &encoded_len); + if (encoded) { + if (encoded_len) { + out_avail = 1; + NEW_BUCKET(encoded, encoded_len); + } + efree(encoded); + } + } + + php_stream_bucket_unlink(ptr TSRMLS_CC); + php_stream_bucket_delref(ptr TSRMLS_CC); + } + } + + /* flush & close */ + if (flags & PSFS_FLAG_FLUSH_INC) { + char *encoded = NULL; + size_t encoded_len = 0; + + http_encoding_deflate_stream_flush(buffer, &encoded, &encoded_len); + if (encoded) { + if (encoded_len) { + out_avail = 1; + NEW_BUCKET(encoded, encoded_len); + } + efree(encoded); + } + } + + if (flags & PSFS_FLAG_FLUSH_CLOSE) { + char *encoded = NULL; + size_t encoded_len = 0; + + http_encoding_deflate_stream_finish(buffer, &encoded, &encoded_len); + if (encoded) { + if (encoded_len) { + out_avail = 1; + NEW_BUCKET(encoded, encoded_len); + } + efree(encoded); + } + } + + return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME; +} + +static HTTP_FILTER_FUNCTION(inflate) +{ + int out_avail = 0; + php_stream_bucket *ptr, *nxt; + HTTP_FILTER_BUFFER(inflate) *buffer = (HTTP_FILTER_BUFFER(inflate) *) this->abstract; + + if (bytes_consumed) { + *bytes_consumed = 0; + } + + /* new data available? */ + if (buckets_in->head) { + + /* fetch available bucket data */ + for (ptr = buckets_in->head; ptr; ptr = nxt) { + char *decoded = NULL; + size_t decoded_len = 0; + + nxt = ptr->next; + if (bytes_consumed) { + *bytes_consumed += ptr->buflen; + } + + if (ptr->buflen) { + http_encoding_inflate_stream_update(buffer, ptr->buf, ptr->buflen, &decoded, &decoded_len); + if (decoded) { + if (decoded_len) { + out_avail = 1; + NEW_BUCKET(decoded, decoded_len); + } + efree(decoded); + } + } + + php_stream_bucket_unlink(ptr TSRMLS_CC); + php_stream_bucket_delref(ptr TSRMLS_CC); + } + } + + /* flush & close */ + if (flags & PSFS_FLAG_FLUSH_INC) { + char *decoded = NULL; + size_t decoded_len = 0; + + http_encoding_inflate_stream_flush(buffer, &decoded, &decoded_len); + if (decoded) { + if (decoded_len) { + out_avail = 1; + NEW_BUCKET(decoded, decoded_len); + } + efree(decoded); + } + } + + if (flags & PSFS_FLAG_FLUSH_CLOSE) { + char *decoded = NULL; + size_t decoded_len = 0; + + http_encoding_inflate_stream_finish(buffer, &decoded, &decoded_len); + if (decoded) { + if (decoded_len) { + out_avail = 1; + NEW_BUCKET(decoded, decoded_len); + } + efree(decoded); + } + } + + return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME; +} + +static HTTP_FILTER_DESTRUCTOR(deflate) +{ + HTTP_FILTER_BUFFER(deflate) *buffer = (HTTP_FILTER_BUFFER(deflate) *) this->abstract; + http_encoding_deflate_stream_free(&buffer); +} + +static HTTP_FILTER_DESTRUCTOR(inflate) +{ + HTTP_FILTER_BUFFER(inflate) *buffer = (HTTP_FILTER_BUFFER(inflate) *) this->abstract; + http_encoding_inflate_stream_free(&buffer); +} + +static HTTP_FILTER_OPS(deflate) = { + HTTP_FILTER_FUNC(deflate), + HTTP_FILTER_DTOR(deflate), + "http.deflate" +}; + +static HTTP_FILTER_OPS(inflate) = { + HTTP_FILTER_FUNC(inflate), + HTTP_FILTER_DTOR(inflate), + "http.inflate" +}; + +#endif /* HTTP_HAVE_ZLIB */ + +static php_stream_filter *http_filter_create(const char *name, zval *params, int p TSRMLS_DC) +{ + zval **tmp = ¶ms; + php_stream_filter *f = NULL; + + if (!strcasecmp(name, "http.chunked_decode")) { + HTTP_FILTER_BUFFER(chunked_decode) *b = NULL; + + if ((b = pecalloc(1, sizeof(HTTP_FILTER_BUFFER(chunked_decode)), p))) { + phpstr_init_ex(PHPSTR(b), 4096, p ? PHPSTR_INIT_PERSISTENT : 0); + if (!(f = php_stream_filter_alloc(&HTTP_FILTER_OP(chunked_decode), b, p))) { + pefree(b, p); + } + } + } else + + if (!strcasecmp(name, "http.chunked_encode")) { + f = php_stream_filter_alloc(&HTTP_FILTER_OP(chunked_encode), NULL, p); +#ifdef HTTP_HAVE_ZLIB + } else + + if (!strcasecmp(name, "http.inflate")) { + int flags = p ? HTTP_ENCODING_STREAM_PERSISTENT : 0; + HTTP_FILTER_BUFFER(inflate) *b = NULL; + + if ((b = http_encoding_inflate_stream_init(NULL, flags))) { + if (!(f = php_stream_filter_alloc(&HTTP_FILTER_OP(inflate), b, p))) { + http_encoding_inflate_stream_free(&b); + } + } + } else + + if (!strcasecmp(name, "http.deflate")) { + int flags = p ? HTTP_ENCODING_STREAM_PERSISTENT : 0; + HTTP_FILTER_BUFFER(deflate) *b = NULL; + + 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; + } + default: + { + zval *num = http_zsep(IS_LONG, *tmp); + + flags |= (Z_LVAL_P(num) & 0x0fffffff); + zval_ptr_dtor(&num); + } + } + } + if ((b = http_encoding_deflate_stream_init(NULL, flags))) { + if (!(f = php_stream_filter_alloc(&HTTP_FILTER_OP(deflate), b, p))) { + http_encoding_deflate_stream_free(&b); + } + } +#endif /* HTTP_HAVE_ZLIB */ + } + + return f; +} + +php_stream_filter_factory http_filter_factory = { + http_filter_create +}; + +#endif /* ZEND_ENGINE_2 */ + +/* + * 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 + */ --- /dev/null +++ b/ext/http/http_functions.c @@ -0,0 +1,1424 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_functions.c 300301 2010-06-09 08:30:34Z mike $ */ + +#define HTTP_WANT_SAPI +#define HTTP_WANT_CURL +#define HTTP_WANT_ZLIB +#include "php_http.h" + +#include "php_ini.h" +#include "ext/standard/php_string.h" +#include "zend_operators.h" + +#ifdef HTTP_HAVE_SESSION +# include "ext/session/php_session.h" +#endif + +#include "php_http_api.h" +#include "php_http_cache_api.h" +#include "php_http_cookie_api.h" +#include "php_http_date_api.h" +#include "php_http_encoding_api.h" +#include "php_http_headers_api.h" +#include "php_http_message_api.h" +#include "php_http_request_api.h" +#include "php_http_request_method_api.h" +#include "php_http_persistent_handle_api.h" +#include "php_http_send_api.h" +#include "php_http_url_api.h" + +/* {{{ proto string http_date([int timestamp]) + Compose a valid HTTP date regarding RFC 1123 looking like: "Wed, 22 Dec 2004 11:34:47 GMT" */ +PHP_FUNCTION(http_date) +{ + long t = -1; + char *date; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &t) != SUCCESS) { + RETURN_FALSE; + } + + if (t == -1) { + t = HTTP_G->request.time; + } + + if (!(date = http_date(t))) { + http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Could not compose date of timestamp %ld", t); + RETURN_FALSE; + } + + RETURN_STRING(date, 0); +} +/* }}} */ + +/* {{{ proto string http_build_url([mixed url[, mixed parts[, int flags = HTTP_URL_REPLACE|HTTP_URL_FROM_ENV[, array &new_url]]]]) + Build an URL. */ +PHP_FUNCTION(http_build_url) +{ + char *url_str = NULL; + size_t url_len = 0; + long flags = HTTP_URL_REPLACE|HTTP_URL_FROM_ENV; + zval *z_old_url = NULL, *z_new_url = NULL, *z_composed_url = NULL; + php_url *old_url = NULL, *new_url = NULL, *composed_url = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!/z!/lz", &z_old_url, &z_new_url, &flags, &z_composed_url) != SUCCESS) { + RETURN_FALSE; + } + + if (z_new_url) { + if (Z_TYPE_P(z_new_url) == IS_ARRAY || Z_TYPE_P(z_new_url) == IS_OBJECT) { + new_url = http_url_from_struct(NULL, HASH_OF(z_new_url)); + } else { + convert_to_string(z_new_url); + if (!(new_url = php_url_parse_ex(Z_STRVAL_P(z_new_url), Z_STRLEN_P(z_new_url)))) { + http_error_ex(HE_WARNING, HTTP_E_URL, "Could not parse URL (%s)", Z_STRVAL_P(z_new_url)); + RETURN_FALSE; + } + } + } + + if (z_old_url) { + if (Z_TYPE_P(z_old_url) == IS_ARRAY || Z_TYPE_P(z_old_url) == IS_OBJECT) { + old_url = http_url_from_struct(NULL, HASH_OF(z_old_url)); + } else { + convert_to_string(z_old_url); + if (!(old_url = php_url_parse_ex(Z_STRVAL_P(z_old_url), Z_STRLEN_P(z_old_url)))) { + if (new_url) { + php_url_free(new_url); + } + http_error_ex(HE_WARNING, HTTP_E_URL, "Could not parse URL (%s)", Z_STRVAL_P(z_old_url)); + RETURN_FALSE; + } + } + } + + if (z_composed_url) { + http_build_url(flags, old_url, new_url, &composed_url, &url_str, &url_len); + http_url_tostruct(composed_url, z_composed_url); + php_url_free(composed_url); + } else { + http_build_url(flags, old_url, new_url, NULL, &url_str, &url_len); + } + + if (new_url) { + php_url_free(new_url); + } + if (old_url) { + php_url_free(old_url); + } + + RETURN_STRINGL(url_str, url_len, 0); +} +/* }}} */ + +/* {{{ proto string http_build_str(array query [, string prefix[, string arg_separator]]) + Opponent to parse_str(). */ +PHP_FUNCTION(http_build_str) +{ + zval *formdata; + char *prefix = NULL, *arg_sep = INI_STR("arg_separator.output"); + int prefix_len = 0, arg_sep_len = strlen(arg_sep); + phpstr formstr; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|ss", &formdata, &prefix, &prefix_len, &arg_sep, &arg_sep_len) != SUCCESS) { + RETURN_FALSE; + } + + if (!arg_sep_len) { + arg_sep = HTTP_URL_ARGSEP; + arg_sep_len = lenof(HTTP_URL_ARGSEP); + } + + phpstr_init(&formstr); + if (SUCCESS != http_urlencode_hash_recursive(HASH_OF(formdata), &formstr, arg_sep, arg_sep_len, prefix, prefix_len)) { + RETURN_FALSE; + } + + if (!formstr.used) { + phpstr_dtor(&formstr); + RETURN_NULL(); + } + + RETURN_PHPSTR_VAL(&formstr); +} +/* }}} */ + +#define HTTP_DO_NEGOTIATE_DEFAULT(supported) \ + { \ + zval **value; \ + \ + zend_hash_internal_pointer_reset(Z_ARRVAL_P(supported)); \ + if (SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(supported), (void *) &value)) { \ + RETVAL_ZVAL(*value, 1, 0); \ + } else { \ + RETVAL_NULL(); \ + } \ + } + +#define HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported, rs_array) \ + HTTP_DO_NEGOTIATE_DEFAULT(supported); \ + if (rs_array) { \ + HashPosition pos; \ + zval **value_ptr; \ + \ + FOREACH_VAL(pos, supported, value_ptr) { \ + zval *value = http_zsep(IS_STRING, *value_ptr); \ + add_assoc_double(rs_array, Z_STRVAL_P(value), 1.0); \ + zval_ptr_dtor(&value); \ + } \ + } + +#define 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 { \ + 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 HTTP_DO_NEGOTIATE(type, supported, rs_array) \ + { \ + HashTable *result; \ + if ((result = http_negotiate_ ##type(supported))) { \ + HTTP_DO_NEGOTIATE_HANDLE_RESULT(result, supported, rs_array); \ + } else { \ + HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported, rs_array); \ + } \ + } + +/* {{{ proto string http_negotiate_language(array supported[, array &result]) + Negotiate the clients preferred language. */ +PHP_FUNCTION(http_negotiate_language) +{ + zval *supported, *rs_array = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|z", &supported, &rs_array) != SUCCESS) { + RETURN_FALSE; + } + + if (rs_array) { + zval_dtor(rs_array); + array_init(rs_array); + } + + HTTP_DO_NEGOTIATE(language, supported, rs_array); +} +/* }}} */ + +/* {{{ proto string http_negotiate_charset(array supported[, array &result]) + Negotiate the clients preferred charset. */ +PHP_FUNCTION(http_negotiate_charset) +{ + zval *supported, *rs_array = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|z", &supported, &rs_array) != SUCCESS) { + RETURN_FALSE; + } + + if (rs_array) { + zval_dtor(rs_array); + array_init(rs_array); + } + + HTTP_DO_NEGOTIATE(charset, supported, rs_array); +} +/* }}} */ + +/* {{{ proto string http_negotiate_content_type(array supported[, array &result]) + Negotiate the clients preferred content type. */ +PHP_FUNCTION(http_negotiate_content_type) +{ + zval *supported, *rs_array = NULL; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|z", &supported, &rs_array)) { + RETURN_FALSE; + } + + if (rs_array) { + zval_dtor(rs_array); + array_init(rs_array); + } + + HTTP_DO_NEGOTIATE(content_type, supported, rs_array); +} +/* }}} */ + +/* {{{ proto string http_negotiate(mixed value, array supported[, array &result]) + Negotiate the user supplied value. */ +PHP_FUNCTION(http_negotiate) +{ + zval *value, *supported, *rs_array = NULL; + HashTable *rs; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "za|z", &value, &supported, &rs_array)) { + RETURN_FALSE; + } + + if (rs_array) { + zval_dtor(rs_array); + array_init(rs_array); + } + + if ((rs = http_negotiate_z(value, Z_ARRVAL_P(supported), http_negotiate_default_func))) { + HTTP_DO_NEGOTIATE_HANDLE_RESULT(rs, supported, rs_array); + } else { + HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported, rs_array); + } +} +/* }}} */ + +/* {{{ proto bool http_send_status(int status) + Send HTTP status code. */ +PHP_FUNCTION(http_send_status) +{ + long status = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &status) != SUCCESS) { + RETURN_FALSE; + } + if (status < 100 || status > 510) { + http_error_ex(HE_WARNING, HTTP_E_HEADER, "Invalid HTTP status code (100-510): %d", status); + RETURN_FALSE; + } + + RETURN_SUCCESS(http_send_status(status)); +} +/* }}} */ + +/* {{{ proto bool http_send_last_modified([int timestamp]) + Send a "Last-Modified" header with a valid HTTP date. */ +PHP_FUNCTION(http_send_last_modified) +{ + long t = -1; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &t) != SUCCESS) { + RETURN_FALSE; + } + + if (t == -1) { + t = HTTP_G->request.time; + } + + RETURN_SUCCESS(http_send_last_modified(t)); +} +/* }}} */ + +/* {{{ proto bool http_send_content_type([string content_type = 'application/x-octetstream']) + Send the Content-Type of the sent entity. This is particularly important if you use the http_send() API. */ +PHP_FUNCTION(http_send_content_type) +{ + char *ct = "application/x-octetstream"; + int ct_len = lenof("application/x-octetstream"); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &ct, &ct_len) != SUCCESS) { + RETURN_FALSE; + } + + RETURN_SUCCESS(http_send_content_type(ct, ct_len)); +} +/* }}} */ + +/* {{{ proto bool http_send_content_disposition(string filename[, bool inline = false]) + Send the Content-Disposition. */ +PHP_FUNCTION(http_send_content_disposition) +{ + char *filename; + int f_len; + zend_bool send_inline = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &filename, &f_len, &send_inline) != SUCCESS) { + RETURN_FALSE; + } + RETURN_SUCCESS(http_send_content_disposition(filename, f_len, send_inline)); +} +/* }}} */ + +/* {{{ proto bool http_match_modified([int timestamp[, bool for_range = false]]) + Matches the given unix timestamp against the clients "If-Modified-Since" resp. "If-Unmodified-Since" HTTP headers. */ +PHP_FUNCTION(http_match_modified) +{ + long t = -1; + zend_bool for_range = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lb", &t, &for_range) != SUCCESS) { + RETURN_FALSE; + } + + // current time if not supplied (senseless though) + if (t == -1) { + t = HTTP_G->request.time; + } + + if (for_range) { + RETURN_BOOL(http_match_last_modified("HTTP_IF_UNMODIFIED_SINCE", t)); + } + RETURN_BOOL(http_match_last_modified("HTTP_IF_MODIFIED_SINCE", t)); +} +/* }}} */ + +/* {{{ proto bool http_match_etag(string etag[, bool for_range = false]) + Matches the given ETag against the clients "If-Match" resp. "If-None-Match" HTTP headers. */ +PHP_FUNCTION(http_match_etag) +{ + int etag_len; + char *etag; + zend_bool for_range = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &etag, &etag_len, &for_range) != SUCCESS) { + RETURN_FALSE; + } + + if (for_range) { + RETURN_BOOL(http_match_etag("HTTP_IF_MATCH", etag)); + } + RETURN_BOOL(http_match_etag("HTTP_IF_NONE_MATCH", etag)); +} +/* }}} */ + +/* {{{ proto bool http_cache_last_modified([int timestamp_or_expires]]) + Attempts to cache the sent entity by its last modification date. */ +PHP_FUNCTION(http_cache_last_modified) +{ + long last_modified = 0, send_modified = 0, t; + zval *zlm; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &last_modified) != SUCCESS) { + RETURN_FALSE; + } + + HTTP_CHECK_HEADERS_SENT(RETURN_FALSE); + + t = HTTP_G->request.time; + + /* 0 or omitted */ + if (!last_modified) { + /* does the client have? (att: caching "forever") */ + if ((zlm = http_get_server_var("HTTP_IF_MODIFIED_SINCE", 1))) { + last_modified = send_modified = http_parse_date(Z_STRVAL_P(zlm)); + /* send current time */ + } else { + send_modified = t; + } + /* negative value is supposed to be expiration time */ + } else if (last_modified < 0) { + last_modified += t; + send_modified = t; + /* send supplied time explicitly */ + } else { + send_modified = last_modified; + } + + RETURN_SUCCESS(http_cache_last_modified(last_modified, send_modified, HTTP_DEFAULT_CACHECONTROL, lenof(HTTP_DEFAULT_CACHECONTROL))); +} +/* }}} */ + +/* {{{ proto bool http_cache_etag([string etag]) + Attempts to cache the sent entity by its ETag, either supplied or generated by the hash algorithm specified by the INI setting "http.etag.mode". */ +PHP_FUNCTION(http_cache_etag) +{ + char *etag = NULL; + int etag_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &etag, &etag_len) != SUCCESS) { + RETURN_FALSE; + } + + HTTP_CHECK_HEADERS_SENT(RETURN_FALSE); + + RETURN_SUCCESS(http_cache_etag(etag, etag_len, HTTP_DEFAULT_CACHECONTROL, lenof(HTTP_DEFAULT_CACHECONTROL))); +} +/* }}} */ + +/* {{{ proto string ob_etaghandler(string data, int mode) + For use with ob_start(). Output buffer handler generating an ETag with the hash algorithm specified with the INI setting "http.etag.mode". */ +PHP_FUNCTION(ob_etaghandler) +{ + char *data; + int data_len; + long mode; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &data, &data_len, &mode)) { + RETURN_FALSE; + } + + Z_TYPE_P(return_value) = IS_STRING; + http_ob_etaghandler(data, data_len, &Z_STRVAL_P(return_value), (uint *) &Z_STRLEN_P(return_value), mode); +} +/* }}} */ + +/* {{{ proto void http_throttle(double sec[, int bytes = 40960]) + Sets the throttle delay and send buffer size for use with http_send() API. */ +PHP_FUNCTION(http_throttle) +{ + long chunk_size = HTTP_SENDBUF_SIZE; + double interval; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|l", &interval, &chunk_size)) { + return; + } + + HTTP_G->send.throttle_delay = interval; + HTTP_G->send.buffer_size = chunk_size; +} +/* }}} */ + +/* {{{ proto void http_redirect([string url[, array params[, bool session = false[, int status = 302]]]]) + Redirect to the given url. */ +PHP_FUNCTION(http_redirect) +{ + int url_len = 0; + size_t query_len = 0; + zend_bool session = 0, free_params = 0; + zval *params = NULL; + long status = HTTP_REDIRECT; + char *query = NULL, *url = NULL, *URI, *LOC, *RED = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sa!/bl", &url, &url_len, ¶ms, &session, &status) != SUCCESS) { + RETURN_FALSE; + } + +#ifdef HTTP_HAVE_SESSION + /* append session info */ + if (session) { + if (!params) { + free_params = 1; + MAKE_STD_ZVAL(params); + array_init(params); + } + if (PS(session_status) == php_session_active) { + if (add_assoc_string(params, PS(session_name), PS(id), 1) != SUCCESS) { + http_error(HE_WARNING, HTTP_E_RUNTIME, "Could not append session information"); + } + } + } +#endif + + /* treat params array with http_build_query() */ + if (params) { + if (SUCCESS != http_urlencode_hash_ex(Z_ARRVAL_P(params), 0, NULL, 0, &query, &query_len)) { + if (free_params) { + zval_dtor(params); + FREE_ZVAL(params); + } + if (query) { + efree(query); + } + RETURN_FALSE; + } + } + + URI = http_absolute_url_ex(url, HTTP_URL_FROM_ENV); + + if (query_len) { + spprintf(&LOC, 0, "Location: %s?%s", URI, query); + if (status != 300) { + spprintf(&RED, 0, "Redirecting to %s?%s.\n", URI, query, URI, query); + } + } else { + spprintf(&LOC, 0, "Location: %s", URI); + if (status != 300) { + spprintf(&RED, 0, "Redirecting to %s.\n", URI, URI); + } + } + + efree(URI); + if (query) { + efree(query); + } + if (free_params) { + zval_dtor(params); + FREE_ZVAL(params); + } + + switch (status) { + case 300: + RETVAL_SUCCESS(http_send_status_header(status, LOC)); + efree(LOC); + return; + + case HTTP_REDIRECT_PERM: + case HTTP_REDIRECT_FOUND: + case HTTP_REDIRECT_POST: + case HTTP_REDIRECT_PROXY: + case HTTP_REDIRECT_TEMP: + break; + + case 306: + default: + http_error_ex(HE_NOTICE, HTTP_E_RUNTIME, "Unsupported redirection status code: %ld", status); + case HTTP_REDIRECT: + if ( SG(request_info).request_method && + strcasecmp(SG(request_info).request_method, "HEAD") && + strcasecmp(SG(request_info).request_method, "GET")) { + status = HTTP_REDIRECT_POST; + } else { + status = HTTP_REDIRECT_FOUND; + } + break; + } + + RETURN_SUCCESS(http_exit_ex(status, LOC, RED, 1)); +} +/* }}} */ + +/* {{{ proto bool http_send_data(string data) + Sends raw data with support for (multiple) range requests. */ +PHP_FUNCTION(http_send_data) +{ + int data_len; + char *data_buf; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data_buf, &data_len) != SUCCESS) { + RETURN_FALSE; + } + + RETURN_SUCCESS(http_send_data(data_buf, data_len)); +} +/* }}} */ + +/* {{{ proto bool http_send_file(string file) + Sends a file with support for (multiple) range requests. */ +PHP_FUNCTION(http_send_file) +{ + char *file; + int flen = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &file, &flen) != SUCCESS) { + RETURN_FALSE; + } + if (!flen) { + RETURN_FALSE; + } + + RETURN_SUCCESS(http_send_file(file)); +} +/* }}} */ + +/* {{{ proto bool http_send_stream(resource stream) + Sends an already opened stream with support for (multiple) range requests. */ +PHP_FUNCTION(http_send_stream) +{ + zval *zstream; + php_stream *file; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zstream) != SUCCESS) { + RETURN_FALSE; + } + + php_stream_from_zval(file, &zstream); + RETURN_SUCCESS(http_send_stream(file)); +} +/* }}} */ + +/* {{{ proto string http_chunked_decode(string encoded) + Decodes a string that was HTTP-chunked encoded. */ +PHP_FUNCTION(http_chunked_decode) +{ + char *encoded = NULL, *decoded = NULL; + size_t decoded_len = 0; + int encoded_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &encoded, &encoded_len) != SUCCESS) { + RETURN_FALSE; + } + + if (NULL != http_encoding_dechunk(encoded, encoded_len, &decoded, &decoded_len)) { + RETURN_STRINGL(decoded, (int) decoded_len, 0); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto object http_parse_message(string message) + Parses (a) http_message(s) into a simple recursive object structure. */ +PHP_FUNCTION(http_parse_message) +{ + char *message; + int message_len; + http_message *msg = NULL; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &message, &message_len)) { + RETURN_NULL(); + } + + if ((msg = http_message_parse(message, message_len))) { + object_init(return_value); + http_message_tostruct_recursive(msg, return_value); + http_message_free(&msg); + } else { + RETURN_NULL(); + } +} +/* }}} */ + +/* {{{ proto array http_parse_headers(string header) + Parses HTTP headers into an associative array. */ +PHP_FUNCTION(http_parse_headers) +{ + char *header; + int header_len; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &header, &header_len)) { + RETURN_FALSE; + } + + array_init(return_value); + if (SUCCESS != http_parse_headers(header, return_value)) { + zval_dtor(return_value); + http_error(HE_WARNING, HTTP_E_MALFORMED_HEADERS, "Failed to parse headers"); + RETURN_FALSE; + } +} +/* }}}*/ + +/* {{{ proto object http_parse_cookie(string cookie[, int flags[, array allowed_extras]]) + Parses HTTP cookies like sent in a response into a struct. */ +PHP_FUNCTION(http_parse_cookie) +{ + char *cookie, **allowed_extras = NULL; + int i = 0, cookie_len; + long flags = 0; + zval *allowed_extras_array = NULL, **entry = NULL; + HashPosition pos; + http_cookie_list list; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|la!", &cookie, &cookie_len, &flags, &allowed_extras_array)) { + RETURN_FALSE; + } + + 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 = http_zsep(IS_STRING, *entry); + allowed_extras[i++] = estrndup(Z_STRVAL_P(data), Z_STRLEN_P(data)); + zval_ptr_dtor(&data); + } + } + + if (http_parse_cookie_ex(&list, cookie, flags, allowed_extras)) { + object_init(return_value); + http_cookie_list_tostruct(&list, return_value); + http_cookie_list_dtor(&list); + } else { + RETVAL_FALSE; + } + + if (allowed_extras) { + for (i = 0; allowed_extras[i]; ++i) { + efree(allowed_extras[i]); + } + efree(allowed_extras); + } +} +/* }}} */ + +/* {{{ proto string http_build_cookie(array cookie) + Build a cookie string from an array/object like returned by http_parse_cookie(). */ +PHP_FUNCTION(http_build_cookie) +{ + char *str = NULL; + size_t len = 0; + zval *strct; + http_cookie_list list; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &strct)) { + RETURN_FALSE; + } + + http_cookie_list_fromstruct(&list, strct); + http_cookie_list_tostring(&list, &str, &len); + http_cookie_list_dtor(&list); + + RETURN_STRINGL(str, len, 0); +} +/* }}} */ + +/* {{{ proto object http_parse_params(string param[, int flags = HTTP_PARAMS_DEFAULT]) + Parse parameter list. */ +PHP_FUNCTION(http_parse_params) +{ + char *param; + int param_len; + zval *params; + long flags = HTTP_PARAMS_DEFAULT; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", ¶m, ¶m_len, &flags)) { + RETURN_FALSE; + } + + MAKE_STD_ZVAL(params); + array_init(params); + if (SUCCESS != http_parse_params(param, flags, Z_ARRVAL_P(params))) { + zval_ptr_dtor(¶ms); + RETURN_FALSE; + } + + object_init(return_value); + add_property_zval(return_value, "params", params); +#ifdef ZEND_ENGINE_2 + zval_ptr_dtor(¶ms); +#endif +} +/* }}} */ + +/* {{{ proto array http_get_request_headers(void) + Get a list of incoming HTTP headers. */ +PHP_FUNCTION(http_get_request_headers) +{ + NO_ARGS; + + array_init(return_value); + http_get_request_headers(Z_ARRVAL_P(return_value)); +} +/* }}} */ + +/* {{{ proto string http_get_request_body(void) + Get the raw request body (e.g. POST or PUT data). */ +PHP_FUNCTION(http_get_request_body) +{ + char *body; + size_t length; + + NO_ARGS; + + if (SUCCESS == http_get_request_body(&body, &length)) { + RETURN_STRINGL(body, (int) length, 0); + } else { + RETURN_NULL(); + } +} +/* }}} */ + +/* {{{ proto resource http_get_request_body_stream(void) + Create a stream to read the raw request body (e.g. POST or PUT data). This function can only be used once if the request method was another than POST. */ +PHP_FUNCTION(http_get_request_body_stream) +{ + php_stream *s; + + NO_ARGS; + + if ((s = http_get_request_body_stream())) { + php_stream_to_zval(s, return_value); + } else { + http_error(HE_WARNING, HTTP_E_RUNTIME, "Failed to create request body stream"); + RETURN_NULL(); + } +} +/* }}} */ + +/* {{{ proto bool http_match_request_header(string header, string value[, bool match_case = false]) + Match an incoming HTTP header. */ +PHP_FUNCTION(http_match_request_header) +{ + char *header, *value; + int header_len, value_len; + zend_bool match_case = 0; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|b", &header, &header_len, &value, &value_len, &match_case)) { + RETURN_FALSE; + } + + RETURN_BOOL(http_match_request_header_ex(header, value, match_case)); +} +/* }}} */ + +/* {{{ proto object http_persistent_handles_count() */ +PHP_FUNCTION(http_persistent_handles_count) +{ + NO_ARGS; + object_init(return_value); + if (!http_persistent_handle_statall_ex(HASH_OF(return_value))) { + zval_dtor(return_value); + RETURN_NULL(); + } +} +/* }}} */ + +/* {{{ proto void http_persistent_handles_clean([string name]) */ +PHP_FUNCTION(http_persistent_handles_clean) +{ + char *name_str = NULL; + int name_len = 0; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &name_str, &name_len)) { + http_persistent_handle_cleanup_ex(name_str, name_len, 1); + } +} +/* }}} */ + +/* {{{ proto string http_persistent_handles_ident([string ident]) */ +PHP_FUNCTION(http_persistent_handles_ident) +{ + char *ident_str = NULL; + int ident_len = 0; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &ident_str, &ident_len)) { + RETVAL_STRING(zend_ini_string(ZEND_STRS("http.persistent.handles.ident"), 0), 1); + if (ident_str && ident_len) { + zend_alter_ini_entry(ZEND_STRS("http.persistent.handles.ident"), ident_str, ident_len, ZEND_INI_USER, PHP_INI_STAGE_RUNTIME); + } + } +} +/* }}} */ + +/* {{{ HAVE_CURL */ +#ifdef HTTP_HAVE_CURL + +#define RETVAL_RESPONSE_OR_BODY(request) \ + { \ + zval **bodyonly; \ + \ + /* check if only the body should be returned */ \ + if (options && (SUCCESS == zend_hash_find(Z_ARRVAL_P(options), "bodyonly", sizeof("bodyonly"), (void *) &bodyonly)) && i_zend_is_true(*bodyonly)) { \ + http_message *msg = http_message_parse(PHPSTR_VAL(&request.conv.response), PHPSTR_LEN(&request.conv.response)); \ + \ + if (msg) { \ + RETVAL_STRINGL(PHPSTR_VAL(&msg->body), PHPSTR_LEN(&msg->body), 1); \ + http_message_free(&msg); \ + } \ + } else { \ + RETVAL_STRINGL(request.conv.response.data, request.conv.response.used, 1); \ + } \ + } + +/* {{{ proto string http_get(string url[, array options[, array &info]]) + Performs an HTTP GET request on the supplied url. */ +PHP_FUNCTION(http_get) +{ + zval *options = NULL, *info = NULL; + char *URL; + int URL_len; + http_request request; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a/!z", &URL, &URL_len, &options, &info) != SUCCESS) { + RETURN_FALSE; + } + + if (info) { + zval_dtor(info); + array_init(info); + } + + RETVAL_FALSE; + + http_request_init_ex(&request, NULL, HTTP_GET, URL); + if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) { + http_request_exec(&request); + if (info) { + http_request_info(&request, Z_ARRVAL_P(info)); + } + RETVAL_RESPONSE_OR_BODY(request); + } + http_request_dtor(&request); +} +/* }}} */ + +/* {{{ proto string http_head(string url[, array options[, array &info]]) + Performs an HTTP HEAD request on the supplied url. */ +PHP_FUNCTION(http_head) +{ + zval *options = NULL, *info = NULL; + char *URL; + int URL_len; + http_request request; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a/!z", &URL, &URL_len, &options, &info) != SUCCESS) { + RETURN_FALSE; + } + + if (info) { + zval_dtor(info); + array_init(info); + } + + RETVAL_FALSE; + + http_request_init_ex(&request, NULL, HTTP_HEAD, URL); + if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) { + http_request_exec(&request); + if (info) { + http_request_info(&request, Z_ARRVAL_P(info)); + } + RETVAL_RESPONSE_OR_BODY(request); + } + http_request_dtor(&request); +} +/* }}} */ + +/* {{{ proto string http_post_data(string url, string data[, array options[, array &info]]) + Performs an HTTP POST request on the supplied url. */ +PHP_FUNCTION(http_post_data) +{ + zval *options = NULL, *info = NULL; + char *URL, *postdata; + int postdata_len, URL_len; + http_request_body body; + http_request request; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!z", &URL, &URL_len, &postdata, &postdata_len, &options, &info) != SUCCESS) { + RETURN_FALSE; + } + + if (info) { + zval_dtor(info); + array_init(info); + } + + RETVAL_FALSE; + + http_request_init_ex(&request, NULL, HTTP_POST, URL); + request.body = http_request_body_init_ex(&body, HTTP_REQUEST_BODY_CSTRING, postdata, postdata_len, 0); + if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) { + http_request_exec(&request); + if (info) { + http_request_info(&request, Z_ARRVAL_P(info)); + } + RETVAL_RESPONSE_OR_BODY(request); + } + http_request_dtor(&request); +} +/* }}} */ + +/* {{{ proto string http_post_fields(string url, array data[, array files[, array options[, array &info]]]) + Performs an HTTP POST request on the supplied url. */ +PHP_FUNCTION(http_post_fields) +{ + zval *options = NULL, *info = NULL, *fields = NULL, *files = NULL; + char *URL; + int URL_len; + http_request_body body; + http_request request; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa!|a!a/!z", &URL, &URL_len, &fields, &files, &options, &info) != SUCCESS) { + RETURN_FALSE; + } + + if (!http_request_body_fill(&body, fields ? Z_ARRVAL_P(fields) : NULL, files ? Z_ARRVAL_P(files) : NULL)) { + RETURN_FALSE; + } + + if (info) { + zval_dtor(info); + array_init(info); + } + + RETVAL_FALSE; + + http_request_init_ex(&request, NULL, HTTP_POST, URL); + request.body = &body; + if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) { + http_request_exec(&request); + if (info) { + http_request_info(&request, Z_ARRVAL_P(info)); + } + RETVAL_RESPONSE_OR_BODY(request); + } + http_request_dtor(&request); +} +/* }}} */ + +/* {{{ proto string http_put_file(string url, string file[, array options[, array &info]]) + Performs an HTTP PUT request on the supplied url. */ +PHP_FUNCTION(http_put_file) +{ + char *URL, *file; + int URL_len, f_len; + zval *options = NULL, *info = NULL; + php_stream *stream; + php_stream_statbuf ssb; + http_request_body body; + http_request request; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!z", &URL, &URL_len, &file, &f_len, &options, &info)) { + RETURN_FALSE; + } + + if (!(stream = php_stream_open_wrapper_ex(file, "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL, HTTP_DEFAULT_STREAM_CONTEXT))) { + RETURN_FALSE; + } + if (php_stream_stat(stream, &ssb)) { + php_stream_close(stream); + RETURN_FALSE; + } + + if (info) { + zval_dtor(info); + array_init(info); + } + + RETVAL_FALSE; + + http_request_init_ex(&request, NULL, HTTP_PUT, URL); + request.body = http_request_body_init_ex(&body, HTTP_REQUEST_BODY_UPLOADFILE, stream, ssb.sb.st_size, 1); + if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) { + http_request_exec(&request); + if (info) { + http_request_info(&request, Z_ARRVAL_P(info)); + } + RETVAL_RESPONSE_OR_BODY(request); + } + http_request_dtor(&request); +} +/* }}} */ + +/* {{{ proto string http_put_stream(string url, resource stream[, array options[, array &info]]) + Performs an HTTP PUT request on the supplied url. */ +PHP_FUNCTION(http_put_stream) +{ + zval *resource, *options = NULL, *info = NULL; + char *URL; + int URL_len; + php_stream *stream; + php_stream_statbuf ssb; + http_request_body body; + http_request request; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sr|a/!z", &URL, &URL_len, &resource, &options, &info)) { + RETURN_FALSE; + } + + php_stream_from_zval(stream, &resource); + if (php_stream_stat(stream, &ssb)) { + RETURN_FALSE; + } + + if (info) { + zval_dtor(info); + array_init(info); + } + + RETVAL_FALSE; + + http_request_init_ex(&request, NULL, HTTP_PUT, URL); + request.body = http_request_body_init_ex(&body, HTTP_REQUEST_BODY_UPLOADFILE, stream, ssb.sb.st_size, 0); + if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) { + http_request_exec(&request); + if (info) { + http_request_info(&request, Z_ARRVAL_P(info)); + } + RETVAL_RESPONSE_OR_BODY(request); + } + http_request_dtor(&request); +} +/* }}} */ + +/* {{{ proto string http_put_data(string url, string data[, array options[, array &info]]) + Performs an HTTP PUT request on the supplied url. */ +PHP_FUNCTION(http_put_data) +{ + char *URL, *data; + int URL_len, data_len; + zval *options = NULL, *info = NULL; + http_request_body body; + http_request request; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!z", &URL, &URL_len, &data, &data_len, &options, &info)) { + RETURN_FALSE; + } + + if (info) { + zval_dtor(info); + array_init(info); + } + + RETVAL_FALSE; + + http_request_init_ex(&request, NULL, HTTP_PUT, URL); + request.body = http_request_body_init_ex(&body, HTTP_REQUEST_BODY_CSTRING, data, data_len, 0); + if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) { + http_request_exec(&request); + if (info) { + http_request_info(&request, Z_ARRVAL_P(info)); + } + RETVAL_RESPONSE_OR_BODY(request); + } + http_request_dtor(&request); +} +/* }}} */ + +/* {{{ proto string http_request(int method, string url[, string body[, array options[, array &info]]]) + Performs a custom HTTP request on the supplied url. */ +PHP_FUNCTION(http_request) +{ + long meth; + char *URL, *data = NULL; + int URL_len, data_len = 0; + zval *options = NULL, *info = NULL; + http_request_body body; + http_request request; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls|sa/!z", &meth, &URL, &URL_len, &data, &data_len, &options, &info)) { + RETURN_FALSE; + } + + if (info) { + zval_dtor(info); + array_init(info); + } + + RETVAL_FALSE; + + http_request_init_ex(&request, NULL, meth, URL); + request.body = http_request_body_init_ex(&body, HTTP_REQUEST_BODY_CSTRING, data, data_len, 0); + if (SUCCESS == http_request_prepare(&request, options?Z_ARRVAL_P(options):NULL)) { + http_request_exec(&request); + if (info) { + http_request_info(&request, Z_ARRVAL_P(info)); + } + RETVAL_RESPONSE_OR_BODY(request); + } + http_request_dtor(&request); +} +/* }}} */ + +/* {{{ proto string http_request_body_encode(array fields, array files) + Generate x-www-form-urlencoded resp. form-data encoded request body. */ +PHP_FUNCTION(http_request_body_encode) +{ + zval *fields = NULL, *files = NULL; + HashTable *fields_ht, *files_ht; + http_request_body body; + char *buf; + size_t len; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a!a!", &fields, &files)) { + RETURN_FALSE; + } + + fields_ht = (fields && Z_TYPE_P(fields) == IS_ARRAY) ? Z_ARRVAL_P(fields) : NULL; + files_ht = (files && Z_TYPE_P(files) == IS_ARRAY) ? Z_ARRVAL_P(files) : NULL; + if (http_request_body_fill(&body, fields_ht, files_ht) && (SUCCESS == http_request_body_encode(&body, &buf, &len))) { + RETVAL_STRINGL(buf, len, 0); + } else { + http_error(HE_WARNING, HTTP_E_RUNTIME, "Could not encode request body"); + RETVAL_FALSE; + } + http_request_body_dtor(&body); +} +#endif /* HTTP_HAVE_CURL */ +/* }}} HAVE_CURL */ + +/* {{{ proto int http_request_method_register(string method) + Register a custom request method. */ +PHP_FUNCTION(http_request_method_register) +{ + char *method; + int method_len; + ulong existing; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len)) { + RETURN_FALSE; + } + if ((existing = http_request_method_exists(1, 0, method))) { + RETURN_LONG((long) existing); + } + + RETVAL_LONG((long) http_request_method_register(method, method_len)); +} +/* }}} */ + +/* {{{ proto bool http_request_method_unregister(mixed method) + Unregister a previously registered custom request method. */ +PHP_FUNCTION(http_request_method_unregister) +{ + zval *method; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &method)) { + RETURN_FALSE; + } + + switch (Z_TYPE_P(method)) { + case IS_OBJECT: + convert_to_string(method); + case IS_STRING: + if (is_numeric_string(Z_STRVAL_P(method), Z_STRLEN_P(method), NULL, NULL, 1)) { + convert_to_long(method); + } else { + int mn; + if (!(mn = http_request_method_exists(1, 0, Z_STRVAL_P(method)))) { + RETURN_FALSE; + } + zval_dtor(method); + ZVAL_LONG(method, (long)mn); + } + case IS_LONG: + RETURN_SUCCESS(http_request_method_unregister(Z_LVAL_P(method))); + default: + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto int http_request_method_exists(mixed method) + Check if a request method is registered (or available by default). */ +PHP_FUNCTION(http_request_method_exists) +{ + if (return_value_used) { + zval *method; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &method)) { + RETURN_FALSE; + } + + switch (Z_TYPE_P(method)) { + case IS_OBJECT: + convert_to_string(method); + case IS_STRING: + if (is_numeric_string(Z_STRVAL_P(method), Z_STRLEN_P(method), NULL, NULL, 1)) { + convert_to_long(method); + } else { + RETURN_LONG((long) http_request_method_exists(1, 0, Z_STRVAL_P(method))); + } + case IS_LONG: + RETURN_LONG((long) http_request_method_exists(0, (int) Z_LVAL_P(method), NULL)); + default: + RETURN_FALSE; + } + } +} +/* }}} */ + +/* {{{ proto string http_request_method_name(int method) + Get the literal string representation of a standard or registered request method. */ +PHP_FUNCTION(http_request_method_name) +{ + if (return_value_used) { + long method; + + if ((SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &method)) || (method < 0)) { + RETURN_FALSE; + } + + RETURN_STRING(estrdup(http_request_method_name((int) method)), 0); + } +} +/* }}} */ + +/* {{{ */ +#ifdef HTTP_HAVE_ZLIB + +/* {{{ proto string http_deflate(string data[, int flags = 0]) + Compress data with gzip, zlib AKA deflate or raw deflate encoding. */ +PHP_FUNCTION(http_deflate) +{ + char *data; + int data_len; + long flags = 0; + + RETVAL_NULL(); + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &data, &data_len, &flags)) { + char *encoded; + size_t encoded_len; + + if (SUCCESS == http_encoding_deflate(flags, data, data_len, &encoded, &encoded_len)) { + RETURN_STRINGL(encoded, (int) encoded_len, 0); + } + } +} +/* }}} */ + +/* {{{ proto string http_inflate(string data) + Decompress data compressed with either gzip, deflate AKA zlib or raw deflate encoding. */ +PHP_FUNCTION(http_inflate) +{ + char *data; + int data_len; + + RETVAL_NULL(); + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &data_len)) { + char *decoded; + size_t decoded_len; + + if (SUCCESS == http_encoding_inflate(data, data_len, &decoded, &decoded_len)) { + RETURN_STRINGL(decoded, (int) decoded_len, 0); + } + } +} +/* }}} */ + +/* {{{ proto string ob_deflatehandler(string data, int mode) + For use with ob_start(). The deflate output buffer handler can only be used once. */ +PHP_FUNCTION(ob_deflatehandler) +{ + char *data; + int data_len; + long mode; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &data, &data_len, &mode)) { + RETURN_FALSE; + } + + http_ob_deflatehandler(data, data_len, &Z_STRVAL_P(return_value), (uint *) &Z_STRLEN_P(return_value), mode); + Z_TYPE_P(return_value) = Z_STRVAL_P(return_value) ? IS_STRING : IS_NULL; +} +/* }}} */ + +/* {{{ proto string ob_inflatehandler(string data, int mode) + For use with ob_start(). Same restrictions as with ob_deflatehandler apply. */ +PHP_FUNCTION(ob_inflatehandler) +{ + char *data; + int data_len; + long mode; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &data, &data_len, &mode)) { + RETURN_FALSE; + } + + http_ob_inflatehandler(data, data_len, &Z_STRVAL_P(return_value), (uint *) &Z_STRLEN_P(return_value), mode); + Z_TYPE_P(return_value) = Z_STRVAL_P(return_value) ? IS_STRING : IS_NULL; +} +/* }}} */ + +#endif /* HTTP_HAVE_ZLIB */ +/* }}} */ + +/* {{{ proto int http_support([int feature = 0]) + Check for feature that require external libraries. */ +PHP_FUNCTION(http_support) +{ + long feature = 0; + + RETVAL_LONG(0L); + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &feature)) { + RETVAL_LONG(http_support(feature)); + } +} +/* }}} */ + +/* + * 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 + */ + --- /dev/null +++ b/ext/http/http_headers_api.c @@ -0,0 +1,534 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_headers_api.c 300300 2010-06-09 07:29:35Z mike $ */ + +#define HTTP_WANT_SAPI +#include "php_http.h" + +#include "ext/standard/url.h" +#include "ext/standard/php_string.h" + +#include "php_http_api.h" +#include "php_http_headers_api.h" + +#ifndef HTTP_DBG_NEG +# define HTTP_DBG_NEG 0 +#endif + +/* {{{ static void http_grab_response_headers(void *, void *) */ +static void http_grab_response_headers(void *data, void *arg TSRMLS_DC) +{ + phpstr_appendl(PHPSTR(arg), ((sapi_header_struct *)data)->header); + phpstr_appends(PHPSTR(arg), HTTP_CRLF); +} +/* }}} */ + +/* {{{ static int http_sort_q(const void *, const void *) */ +static int http_sort_q(const void *a, const void *b TSRMLS_DC) +{ + Bucket *f, *s; + zval result, *first, *second; + + f = *((Bucket **) a); + s = *((Bucket **) b); + + first = *((zval **) f->pData); + second= *((zval **) s->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)); +} +/* }}} */ + +/* {{{ char *http_negotiate_language_func */ +char *_http_negotiate_language_func(const char *test, double *quality, HashTable *supported TSRMLS_DC) +{ + zval **value; + HashPosition pos; + const char *dash_test; + + FOREACH_HASH_VAL(pos, supported, value) { +#if HTTP_DBG_NEG + fprintf(stderr, "strcasecmp('%s', '%s')\n", Z_STRVAL_PP(value), test); +#endif + if (!strcasecmp(Z_STRVAL_PP(value), test)) { + return Z_STRVAL_PP(value); + } + } + + /* no distinct match found, so try primaries */ + if ((dash_test = strchr(test, '-'))) { + FOREACH_HASH_VAL(pos, supported, value) { + int len = dash_test - test; +#if HTTP_DBG_NEG + fprintf(stderr, "strncasecmp('%s', '%s', %d)\n", Z_STRVAL_PP(value), test, len); +#endif + if ( (!strncasecmp(Z_STRVAL_PP(value), test, len)) && + ( (Z_STRVAL_PP(value)[len] == '\0') || + (Z_STRVAL_PP(value)[len] == '-'))) { + *quality *= .9; + return Z_STRVAL_PP(value); + } + } + } + + return NULL; +} +/* }}} */ + +/* {{{ char *http_negotiate_default_func */ +char *_http_negotiate_default_func(const char *test, double *quality, HashTable *supported TSRMLS_DC) +{ + zval **value; + HashPosition pos; + + FOREACH_HASH_VAL(pos, supported, value) { +#if HTTP_DBG_NEG + fprintf(stderr, "strcasecmp('%s', '%s')\n", Z_STRVAL_PP(value), test); +#endif + if (!strcasecmp(Z_STRVAL_PP(value), test)) { + return Z_STRVAL_PP(value); + } + } + + return NULL; +} +/* }}} */ + +/* {{{ HashTable *http_negotiate_z(zval *, HashTable *, negotiate_func_t) */ +PHP_HTTP_API HashTable *_http_negotiate_z(zval *value, HashTable *supported, negotiate_func_t neg TSRMLS_DC) +{ + zval *accept = http_zsep(IS_STRING, value); + HashTable *result = NULL; + + if (Z_STRLEN_P(accept)) { + zval ex_arr, ex_del; + + INIT_PZVAL(&ex_del); + INIT_PZVAL(&ex_arr); + ZVAL_STRINGL(&ex_del, ",", 1, 0); + array_init(&ex_arr); + + php_explode(&ex_del, accept, &ex_arr, INT_MAX); + + if (zend_hash_num_elements(Z_ARRVAL(ex_arr)) > 0) { + int i = 0; + HashPosition pos; + zval **entry, array; + + INIT_PZVAL(&array); + array_init(&array); + + FOREACH_HASH_VAL(pos, Z_ARRVAL(ex_arr), entry) { + int ident_len; + double quality; + char *selected, *identifier, *freeme; + const char *separator; + +#if HTTP_DBG_NEG + fprintf(stderr, "Checking %s\n", Z_STRVAL_PP(entry)); +#endif + + if ((separator = strchr(Z_STRVAL_PP(entry), ';'))) { + const char *ptr = separator; + + while (*++ptr && !HTTP_IS_CTYPE(digit, *ptr) && '.' != *ptr); + + quality = zend_strtod(ptr, NULL); + identifier = estrndup(Z_STRVAL_PP(entry), ident_len = separator - Z_STRVAL_PP(entry)); + } else { + quality = 1000.0 - i++; + identifier = estrndup(Z_STRVAL_PP(entry), ident_len = Z_STRLEN_PP(entry)); + } + freeme = identifier; + + while (HTTP_IS_CTYPE(space, *identifier)) { + ++identifier; + --ident_len; + } + while (ident_len && HTTP_IS_CTYPE(space, identifier[ident_len - 1])) { + identifier[--ident_len] = '\0'; + } + + if ((selected = neg(identifier, &quality, supported TSRMLS_CC))) { + /* don't overwrite previously set with higher quality */ + if (!zend_hash_exists(Z_ARRVAL(array), selected, strlen(selected) + 1)) { + add_assoc_double(&array, selected, quality); + } + } + + efree(freeme); + } + + result = Z_ARRVAL(array); + zend_hash_sort(result, zend_qsort, http_sort_q, 0 TSRMLS_CC); + } + + zval_dtor(&ex_arr); + } + + zval_ptr_dtor(&accept); + + return result; +} +/* }}} */ + +/* {{{ HashTable *http_negotiate_q(const char *, HashTable *, negotiate_func_t) */ +PHP_HTTP_API HashTable *_http_negotiate_q(const char *header, HashTable *supported, negotiate_func_t neg TSRMLS_DC) +{ + zval *accept; + +#if HTTP_DBG_NEG + fprintf(stderr, "Reading header %s: ", header); +#endif + if (!(accept = http_get_server_var(header, 1))) { + return NULL; + } +#if HTTP_DBG_NEG + fprintf(stderr, "%s\n", Z_STRVAL_P(accept)); +#endif + + return http_negotiate_z(accept, supported, neg); +} +/* }}} */ + +/* {{{ http_range_status http_get_request_ranges(HashTable *ranges, size_t) */ +PHP_HTTP_API http_range_status _http_get_request_ranges(HashTable *ranges, size_t length TSRMLS_DC) +{ + zval *zrange; + char *range, c; + long begin = -1, end = -1, *ptr; + + if ( !(zrange = http_get_server_var("HTTP_RANGE", 1)) || + (size_t) Z_STRLEN_P(zrange) < lenof("bytes=") || strncmp(Z_STRVAL_P(zrange), "bytes=", lenof("bytes="))) { + return RANGE_NO; + } + range = Z_STRVAL_P(zrange) + lenof("bytes="); + ptr = &begin; + + do { + switch (c = *(range++)) { + 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: + return RANGE_NO; + + /* "0-0" */ + case -10: + end = 0; + break; + + default: + if (length <= (size_t) end) { + return RANGE_ERR; + } + break; + } + begin = 0; + break; + + /* "-12345" */ + case -1: + /* "-", "-0" or overflow */ + if (end == -1 || end == -10 || length <= (size_t) end) { + return RANGE_ERR; + } + begin = length - end; + end = length - 1; + break; + + /* "12345-(xxx)" */ + default: + switch (end) { + /* "12345-0" */ + case -10: + return RANGE_ERR; + + /* "12345-" */ + case -1: + if (length <= (size_t) begin) { + return RANGE_ERR; + } + end = length - 1; + break; + + /* "12345-67890" */ + default: + if ( (length <= (size_t) begin) || + (length <= (size_t) end) || + (end < begin)) { + return RANGE_ERR; + } + break; + } + break; + } + } + { + zval *zentry; + 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: + return RANGE_NO; + } + } while (c != 0); + + return RANGE_OK; +} +/* }}} */ + +/* {{{ STATUS http_parse_headers(char *, HashTable *, zend_bool) */ +PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, HashTable *headers, zend_bool prettify, + http_info_callback callback_func, void **callback_data TSRMLS_DC) +{ + const char *colon = NULL, *line = NULL; + zval array; + + INIT_ZARR(array, headers); + + /* skip leading ws */ + while (HTTP_IS_CTYPE(space, *header)) ++header; + line = header; + +#define MORE_HEADERS (*(line-1) && !(*(line-1) == '\n' && (*line == '\n' || *line == '\r'))) + do { + int value_len = 0; + + switch (*line++) { + case ':': + if (!colon) { + colon = line - 1; + } + break; + + case 0: + --value_len; /* we don't have CR so value length is one char less */ + case '\n': + if ((!*(line - 1)) || ((*line != ' ') && (*line != '\t'))) { + http_info i; + + if (SUCCESS == http_info_parse(header, &i)) { + /* response/request line */ + callback_func(callback_data, &headers, &i TSRMLS_CC); + http_info_dtor(&i); + Z_ARRVAL(array) = headers; + } else if (colon) { + /* "header: value" pair */ + if (header != colon) { + int keylen = colon - header; + const char *key = header; + + /* skip leading ws */ + while (keylen && HTTP_IS_CTYPE(space, *key)) --keylen, ++key; + /* skip trailing ws */ + while (keylen && HTTP_IS_CTYPE(space, key[keylen - 1])) --keylen; + + if (keylen > 0) { + zval **previous = NULL; + char *value; + char *keydup = estrndup(key, keylen); + + if (prettify) { + keydup = pretty_key(keydup, keylen, 1, 1); + } + + value_len += line - colon - 1; + + /* skip leading ws */ + while (HTTP_IS_CTYPE(space, *(++colon))) --value_len; + /* skip trailing ws */ + while (HTTP_IS_CTYPE(space, colon[value_len - 1])) --value_len; + + if (value_len > 0) { + value = estrndup(colon, value_len); + } else { + value = estrdup(""); + value_len = 0; + } + + /* if we already have got such a header make an array of those */ + if (SUCCESS == zend_hash_find(headers, keydup, keylen + 1, (void *) &previous)) { + /* convert to array */ + if (Z_TYPE_PP(previous) != IS_ARRAY) { + convert_to_array(*previous); + } + add_next_index_stringl(*previous, value, value_len, 0); + } else { + add_assoc_stringl(&array, keydup, value, value_len, 0); + } + efree(keydup); + } else { + /* empty key (" : ...") */ + return FAILURE; + } + } else { + /* empty key (": ...") */ + return FAILURE; + } + } else if (MORE_HEADERS) { + /* a line without a colon */ + return FAILURE; + } + colon = NULL; + value_len = 0; + header += line - header; + } + break; + } + } while (MORE_HEADERS); + + return SUCCESS; +} +/* }}} */ + +/* {{{ void http_get_request_headers(HashTable *) */ +PHP_HTTP_API void _http_get_request_headers(HashTable *headers TSRMLS_DC) +{ + HashKey key = initHashKey(0); + zval **hsv, **header; + HashPosition pos; + + if (!HTTP_G->request.headers) { + ALLOC_HASHTABLE(HTTP_G->request.headers); + zend_hash_init(HTTP_G->request.headers, 0, NULL, ZVAL_PTR_DTOR, 0); + +#ifdef ZEND_ENGINE_2 + zend_is_auto_global("_SERVER", lenof("_SERVER") TSRMLS_CC); +#endif + + 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 && !strncmp(key.str, "HTTP_", 5)) { + key.len -= 5; + key.str = 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); + ZVAL_ADDREF(*header); + zend_hash_add(HTTP_G->request.headers, key.str, key.len, (void *) header, sizeof(zval *), NULL); + + efree(key.str); + } + } + } + } + + if (headers) { + zend_hash_copy(headers, HTTP_G->request.headers, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + } +} +/* }}} */ + +/* {{{ STATUS http_get_response_headers(HashTable *) */ +PHP_HTTP_API STATUS _http_get_response_headers(HashTable *headers_ht TSRMLS_DC) +{ + STATUS status; + phpstr headers; + + phpstr_init(&headers); + zend_llist_apply_with_argument(&SG(sapi_headers).headers, http_grab_response_headers, &headers TSRMLS_CC); + phpstr_fix(&headers); + + status = http_parse_headers_ex(PHPSTR_VAL(&headers), headers_ht, 1); + phpstr_dtor(&headers); + + return status; +} +/* }}} */ + +/* {{{ zend_bool http_match_request_header(char *, char *) */ +PHP_HTTP_API zend_bool _http_match_request_header_ex(const char *header, const char *value, zend_bool match_case TSRMLS_DC) +{ + char *name; + uint name_len = strlen(header); + zend_bool result = 0; + zval **data, *zvalue; + + http_get_request_headers(NULL); + name = pretty_key(estrndup(header, name_len), name_len, 1, 1); + if (SUCCESS == zend_hash_find(HTTP_G->request.headers, name, name_len+1, (void *) &data)) { + zvalue = http_zsep(IS_STRING, *data); + result = (match_case ? strcmp(Z_STRVAL_P(zvalue), value) : strcasecmp(Z_STRVAL_P(zvalue), value)) ? 0 : 1; + zval_ptr_dtor(&zvalue); + } + efree(name); + + 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 + */ + --- /dev/null +++ b/ext/http/http_inflatestream_object.c @@ -0,0 +1,292 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_inflatestream_object.c 300299 2010-06-09 06:23:16Z mike $ */ + +#define HTTP_WANT_ZLIB +#include "php_http.h" + +#if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_ZLIB) + +#include "php_http_api.h" +#include "php_http_encoding_api.h" +#include "php_http_exception_object.h" +#include "php_http_inflatestream_object.h" + +#define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpInflateStream, method, 0, req_args) +#define HTTP_EMPTY_ARGS(method) HTTP_EMPTY_ARGS_EX(HttpInflateStream, method, 0) +#define HTTP_INFLATE_ME(method, visibility) PHP_ME(HttpInflateStream, method, HTTP_ARGS(HttpInflateStream, method), visibility) + +HTTP_BEGIN_ARGS(__construct, 0) + HTTP_ARG_VAL(flags, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(factory, 0) + HTTP_ARG_VAL(flags, 0) + HTTP_ARG_VAL(class_name, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(update, 1) + HTTP_ARG_VAL(data, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(flush, 0) + HTTP_ARG_VAL(data, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(finish, 0) + HTTP_ARG_VAL(data, 0) +HTTP_END_ARGS; + +#define THIS_CE http_inflatestream_object_ce +zend_class_entry *http_inflatestream_object_ce; +zend_function_entry http_inflatestream_object_fe[] = { + HTTP_INFLATE_ME(__construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) + HTTP_INFLATE_ME(update, ZEND_ACC_PUBLIC) + HTTP_INFLATE_ME(flush, ZEND_ACC_PUBLIC) + HTTP_INFLATE_ME(finish, ZEND_ACC_PUBLIC) + + HTTP_INFLATE_ME(factory, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + + EMPTY_FUNCTION_ENTRY +}; +static zend_object_handlers http_inflatestream_object_handlers; + +PHP_MINIT_FUNCTION(http_inflatestream_object) +{ + HTTP_REGISTER_CLASS_EX(HttpInflateStream, http_inflatestream_object, NULL, 0); + http_inflatestream_object_handlers.clone_obj = _http_inflatestream_object_clone_obj; + +#ifndef WONKY + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("FLUSH_NONE")-1, HTTP_ENCODING_STREAM_FLUSH_NONE TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("FLUSH_SYNC")-1, HTTP_ENCODING_STREAM_FLUSH_SYNC TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("FLUSH_FULL")-1, HTTP_ENCODING_STREAM_FLUSH_FULL TSRMLS_CC); +#endif + + return SUCCESS; +} + +zend_object_value _http_inflatestream_object_new(zend_class_entry *ce TSRMLS_DC) +{ + return http_inflatestream_object_new_ex(ce, NULL, NULL); +} + +zend_object_value _http_inflatestream_object_new_ex(zend_class_entry *ce, http_encoding_stream *s, http_inflatestream_object **ptr TSRMLS_DC) +{ + zend_object_value ov; + http_inflatestream_object *o; + + o = ecalloc(1, sizeof(http_inflatestream_object)); + o->zo.ce = ce; + + if (ptr) { + *ptr = o; + } + + if (s) { + o->stream = s; + } + + ALLOC_HASHTABLE(OBJ_PROP(o)); + zend_hash_init(OBJ_PROP(o), zend_hash_num_elements(&ce->default_properties), NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(OBJ_PROP(o), &ce->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + + ov.handle = putObject(http_inflatestream_object, o); + ov.handlers = &http_inflatestream_object_handlers; + + return ov; +} + +zend_object_value _http_inflatestream_object_clone_obj(zval *this_ptr TSRMLS_DC) +{ + http_encoding_stream *s; + zend_object_value new_ov; + http_inflatestream_object *new_obj = NULL; + getObject(http_inflatestream_object, old_obj); + + s = ecalloc(1, sizeof(http_encoding_stream)); + s->flags = old_obj->stream->flags; + inflateCopy(&s->stream, &old_obj->stream->stream); + s->stream.opaque = phpstr_dup(s->stream.opaque); + + new_ov = http_inflatestream_object_new_ex(old_obj->zo.ce, s, &new_obj); + zend_objects_clone_members(&new_obj->zo, new_ov, &old_obj->zo, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC); + + return new_ov; +} + +void _http_inflatestream_object_free(zend_object *object TSRMLS_DC) +{ + http_inflatestream_object *o = (http_inflatestream_object *) object; + + if (o->stream) { + http_encoding_inflate_stream_free(&o->stream); + } + freeObject(o); +} + +/* {{{ proto void HttpInflateStream::__construct([int flags = 0]) + Creates a new HttpInflateStream object instance. */ +PHP_METHOD(HttpInflateStream, __construct) +{ + long flags = 0; + + SET_EH_THROW_HTTP(); + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flags)) { + getObject(http_inflatestream_object, obj); + + if (!obj->stream) { + obj->stream = http_encoding_inflate_stream_init(NULL, flags & 0x0fffffff); + } else { + http_error_ex(HE_WARNING, HTTP_E_ENCODING, "HttpInflateStream cannot be initialized twice"); + } + } + SET_EH_NORMAL(); +} +/* }}} */ + +/* {{{ proto HttpInflateStream HttpInflateStream::factory([int flags[, string class = "HttpInflateStream"]]) + Creates a new HttpInflateStream object instance. */ +PHP_METHOD(HttpInflateStream, factory) +{ + long flags = 0; + char *cn = NULL; + int cl = 0; + + SET_EH_THROW_HTTP(); + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ls", &flags, &cn, &cl)) { + zend_object_value ov; + http_encoding_stream *s = http_encoding_inflate_stream_init(NULL, flags & 0x0fffffff); + + if (SUCCESS == http_object_new(&ov, cn, cl, _http_inflatestream_object_new_ex, http_inflatestream_object_ce, s, NULL)) { + RETVAL_OBJVAL(ov, 0); + } + } + SET_EH_NORMAL(); +} +/* }}} */ + +/* {{{ proto string HttpInflateStream::update(string data) + Passes more data through the inflate stream. */ +PHP_METHOD(HttpInflateStream, update) +{ + int data_len; + size_t decoded_len = 0; + char *data, *decoded = NULL; + getObject(http_inflatestream_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &data_len)) { + RETURN_FALSE; + } + + if (!data_len) { + RETURN_STRING("", 1); + } + + if (!obj->stream && !(obj->stream = http_encoding_inflate_stream_init(NULL, 0))) { + RETURN_FALSE; + } + + if (SUCCESS == http_encoding_inflate_stream_update(obj->stream, data, data_len, &decoded, &decoded_len)) { + RETURN_STRINGL(decoded, decoded_len, 0); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto string HttpInflateStream::flush([string data]) + Flush the inflate stream. */ +PHP_METHOD(HttpInflateStream, flush) +{ + int data_len = 0; + size_t decoded_len = 0; + char *decoded = NULL, *data = NULL; + getObject(http_inflatestream_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &data, &data_len)) { + RETURN_FALSE; + } + + if (!obj->stream && !(obj->stream = http_encoding_inflate_stream_init(NULL, 0))) { + RETURN_FALSE; + } + + /* flushing the inflate stream is a no-op */ + if (!data_len) { + RETURN_STRINGL("", 0, 1); + } else if (SUCCESS == http_encoding_inflate_stream_update(obj->stream, data, data_len, &decoded, &decoded_len)) { + RETURN_STRINGL(decoded, decoded_len, 0); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto string HttpInflateStream::finish([string data]) + Finalizes the inflate stream. The inflate stream can be reused after finalizing. */ +PHP_METHOD(HttpInflateStream, finish) +{ + int data_len = 0; + size_t updated_len = 0, decoded_len = 0; + char *updated = NULL, *decoded = NULL, *data = NULL; + getObject(http_inflatestream_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &data, &data_len)) { + RETURN_FALSE; + } + + if (!obj->stream && !(obj->stream = http_encoding_inflate_stream_init(NULL, 0))) { + RETURN_FALSE; + } + + if (data_len) { + if (SUCCESS != http_encoding_inflate_stream_update(obj->stream, data, data_len, &updated, &updated_len)) { + RETURN_FALSE; + } + } + + if (SUCCESS == http_encoding_inflate_stream_finish(obj->stream, &decoded, &decoded_len)) { + if (updated_len) { + updated = erealloc(updated, updated_len + decoded_len + 1); + updated[updated_len + decoded_len] = '\0'; + memcpy(updated + updated_len, decoded, decoded_len); + STR_FREE(decoded); + updated_len += decoded_len; + RETVAL_STRINGL(updated, updated_len, 0); + } else if (decoded) { + STR_FREE(updated); + RETVAL_STRINGL(decoded, decoded_len, 0); + } else { + RETVAL_NULL(); + } + } else { + STR_FREE(updated); + RETVAL_FALSE; + } + + http_encoding_inflate_stream_dtor(obj->stream); + http_encoding_inflate_stream_init(obj->stream, obj->stream->flags); +} +/* }}} */ + +#endif /* ZEND_ENGINE_2 && HTTP_HAVE_ZLIB*/ + +/* + * 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 + */ + --- /dev/null +++ b/ext/http/http_info_api.c @@ -0,0 +1,155 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_info_api.c 304921 2010-10-26 15:27:36Z iliaa $ */ + +#include "php_http.h" + +#include "php_http_api.h" +#include "php_http_info_api.h" + +PHP_HTTP_API void _http_info_default_callback(void **nothing, HashTable **headers, http_info *info TSRMLS_DC) +{ + zval array; + + INIT_ZARR(array, *headers); + + switch (info->type) { + case IS_HTTP_REQUEST: + add_assoc_string(&array, "Request Method", HTTP_INFO(info).request.method, 1); + add_assoc_string(&array, "Request Url", HTTP_INFO(info).request.url, 1); + break; + + case IS_HTTP_RESPONSE: + add_assoc_long(&array, "Response Code", (long) HTTP_INFO(info).response.code); + if (HTTP_INFO(info).response.status) { + add_assoc_string(&array, "Response Status", HTTP_INFO(info).response.status, 1); + } + break; + } +} + +PHP_HTTP_API void _http_info_dtor(http_info *i) +{ + switch (i->type) { + case IS_HTTP_REQUEST: + STR_SET(HTTP_INFO(i).request.method, NULL); + STR_SET(HTTP_INFO(i).request.url, NULL); + break; + + case IS_HTTP_RESPONSE: + STR_SET(HTTP_INFO(i).response.status, NULL); + break; + + default: + break; + } +} + +PHP_HTTP_API STATUS _http_info_parse_ex(const char *pre_header, http_info *info, zend_bool silent TSRMLS_DC) +{ + const char *end, *http; + + /* sane parameter */ + if ((!pre_header) || (!*pre_header)) { + return FAILURE; + } + + /* where's the end of the line */ + if (!(end = http_locate_eol(pre_header, NULL))) { + end = pre_header + strlen(pre_header); + } + + /* there must be HTTP/1.x in the line */ + if (!(http = http_locate_str(pre_header, end - pre_header, "HTTP/1.", lenof("HTTP/1.")))) { + return FAILURE; + } + + /* and nothing than SPACE or NUL after HTTP/1.x */ + if ( (!HTTP_IS_CTYPE(digit, http[lenof("HTTP/1.")])) || + (http[lenof("HTTP/1.1")] && (!HTTP_IS_CTYPE(space, http[lenof("HTTP/1.1")])))) { + if (!silent) { + http_error(HE_WARNING, HTTP_E_MALFORMED_HEADERS, "Invalid HTTP/1.x protocol identification"); + } + return FAILURE; + } + +#if 0 + { + char *line = estrndup(pre_header, end - pre_header); + fprintf(stderr, "http_parse_info('%s')\n", line); + efree(line); + } +#endif + + info->http.version = zend_strtod(http + lenof("HTTP/"), NULL); + + /* is response */ + if (pre_header == http) { + char *status = NULL; + const char *code = http + sizeof("HTTP/1.1"); + + info->type = IS_HTTP_RESPONSE; + while (' ' == *code) ++code; + if (code && end > code) { + HTTP_INFO(info).response.code = strtol(code, &status, 10); + } else { + HTTP_INFO(info).response.code = 0; + } + if (status && end > status) { + while (' ' == *status) ++status; + HTTP_INFO(info).response.status = estrndup(status, end - status); + } else { + HTTP_INFO(info).response.status = NULL; + } + + return SUCCESS; + } + + /* is request */ + else if (!http[lenof("HTTP/1.x")] || http[lenof("HTTP/1.x")] == '\r' || http[lenof("HTTP/1.x")] == '\n') { + const char *url = strchr(pre_header, ' '); + + info->type = IS_HTTP_REQUEST; + if (url && http > url) { + HTTP_INFO(info).request.method = estrndup(pre_header, url - pre_header); + while (' ' == *url) ++url; + while (' ' == *(http-1)) --http; + if (http > url) { + HTTP_INFO(info).request.url = estrndup(url, http - url); + } else { + efree(HTTP_INFO(info).request.method); + return FAILURE; + } + } else { + HTTP_INFO(info).request.method = NULL; + HTTP_INFO(info).request.url = NULL; + } + + return SUCCESS; + } + + /* some darn header containing HTTP/1.x */ + else { + return FAILURE; + } +} + +/* + * 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 + */ + --- /dev/null +++ b/ext/http/http_message_api.c @@ -0,0 +1,735 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_message_api.c 298689 2010-04-28 06:50:06Z mike $ */ + +#define HTTP_WANT_SAPI +#define HTTP_WANT_CURL +#define HTTP_WANT_ZLIB +#include "php_http.h" + +#include "php_http_api.h" +#include "php_http_encoding_api.h" +#include "php_http_headers_api.h" +#include "php_http_message_api.h" +#include "php_http_request_api.h" +#include "php_http_send_api.h" +#include "php_http_url_api.h" + +#define http_message_info_callback _http_message_info_callback +static void _http_message_info_callback(http_message **message, HashTable **headers, http_info *info TSRMLS_DC) +{ + http_message *old = *message; + + /* advance message */ + if (old->type || zend_hash_num_elements(&old->hdrs) || PHPSTR_LEN(old)) { + (*message) = http_message_new(); + (*message)->parent = old; + (*headers) = &((*message)->hdrs); + } + + http_message_set_info(*message, info); +} + +#define http_message_init_type _http_message_init_type +static inline void _http_message_init_type(http_message *message, http_message_type type) +{ + message->http.version = .0; + + switch (message->type = type) { + case HTTP_MSG_RESPONSE: + message->http.info.response.code = 0; + message->http.info.response.status = NULL; + break; + + case HTTP_MSG_REQUEST: + message->http.info.request.method = NULL; + message->http.info.request.url = NULL; + break; + + case HTTP_MSG_NONE: + default: + break; + } +} + +PHP_HTTP_API http_message *_http_message_init_ex(http_message *message, http_message_type type ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +{ + if (!message) { + message = ecalloc_rel(1, sizeof(http_message)); + } + + http_message_init_type(message, type); + message->parent = NULL; + phpstr_init(&message->body); + zend_hash_init(&message->hdrs, 0, NULL, ZVAL_PTR_DTOR, 0); + + return message; +} + +PHP_HTTP_API http_message *_http_message_init_env(http_message *message, http_message_type type TSRMLS_DC ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +{ + int free_msg; + http_info inf; + zval *sval, tval; + char *body_str; + size_t body_len; + + if ((free_msg = !message)) { + message = http_message_init_rel(NULL, HTTP_MSG_NONE); + } + + memset(&inf, 0, sizeof(http_info)); + switch (inf.type = type) { + case HTTP_MSG_REQUEST: + if ((sval = http_get_server_var("SERVER_PROTOCOL", 1)) && !strncmp(Z_STRVAL_P(sval), "HTTP/", lenof("HTTP/"))) { + inf.http.version = zend_strtod(Z_STRVAL_P(sval) + lenof("HTTP/"), NULL); + } else { + inf.http.version = 1.1; + } + if ((sval = http_get_server_var("REQUEST_METHOD", 1))) { + inf.http.info.request.method = estrdup(Z_STRVAL_P(sval)); + } + if ((sval = http_get_server_var("REQUEST_URI", 1))) { + inf.http.info.request.url = estrdup(Z_STRVAL_P(sval)); + } + + http_message_set_info(message, &inf); + http_get_request_headers(&message->hdrs); + if (SUCCESS == http_get_request_body_ex(&body_str, &body_len, 0)) { + phpstr_from_string_ex(&message->body, body_str, body_len); + } + break; + + case HTTP_MSG_RESPONSE: + if (!SG(sapi_headers).http_status_line || SUCCESS != http_info_parse_ex(SG(sapi_headers).http_status_line, &inf, 0)) { + inf.http.version = 1.1; + inf.http.info.response.code = 200; + inf.http.info.response.status = estrdup("Ok"); + } + + http_message_set_info(message, &inf); + http_get_response_headers(&message->hdrs); + if (SUCCESS == php_ob_get_buffer(&tval TSRMLS_CC)) { + message->body.data = Z_STRVAL(tval); + message->body.used = Z_STRLEN(tval); + message->body.free = 1; /* "\0" */ + } + break; + + default: + if (free_msg) { + http_message_free(&message); + } else { + message = NULL; + } + break; + } + http_info_dtor(&inf); + + return message; +} + +PHP_HTTP_API void _http_message_set_type(http_message *message, http_message_type type) +{ + /* just act if different */ + if (type != message->type) { + + /* free request info */ + switch (message->type) { + case HTTP_MSG_REQUEST: + STR_FREE(message->http.info.request.method); + STR_FREE(message->http.info.request.url); + break; + + case HTTP_MSG_RESPONSE: + STR_FREE(message->http.info.response.status); + break; + + default: + break; + } + + /* init */ + http_message_init_type(message, type); + } +} + +PHP_HTTP_API void _http_message_set_info(http_message *message, http_info *info) +{ + http_message_set_type(message, info->type); + message->http.version = info->http.version; + switch (message->type) { + case IS_HTTP_REQUEST: + STR_SET(HTTP_INFO(message).request.url, HTTP_INFO(info).request.url ? estrdup(HTTP_INFO(info).request.url) : NULL); + STR_SET(HTTP_INFO(message).request.method, HTTP_INFO(info).request.method ? estrdup(HTTP_INFO(info).request.method) : NULL); + break; + + case IS_HTTP_RESPONSE: + HTTP_INFO(message).response.code = HTTP_INFO(info).response.code; + STR_SET(HTTP_INFO(message).response.status, HTTP_INFO(info).response.status ? estrdup(HTTP_INFO(info).response.status) : NULL); + break; + + default: + break; + } +} + +#define http_message_body_parse(m, ms, ml, c) _http_message_body_parse((m), (ms), (ml), (c) TSRMLS_CC) +static inline void _http_message_body_parse(http_message *msg, const char *message, size_t message_length, const char **continue_at TSRMLS_DC) +{ + zval *c; + size_t remaining; + const char *body; + + *continue_at = NULL; + if ((body = http_locate_body(message))) { + remaining = message + message_length - body; + + if ((c = http_message_header(msg, "Transfer-Encoding"))) { + if (strstr(Z_STRVAL_P(c), "chunked")) { + /* message has chunked transfer encoding */ + char *decoded; + size_t decoded_len; + + /* decode and replace Transfer-Encoding with Content-Length header */ + if ((*continue_at = http_encoding_dechunk(body, message + message_length - body, &decoded, &decoded_len))) { + zval *len; + char *tmp; + int tmp_len; + + tmp_len = (int) spprintf(&tmp, 0, "%zu", decoded_len); + MAKE_STD_ZVAL(len); + ZVAL_STRINGL(len, tmp, tmp_len, 0); + + ZVAL_ADDREF(c); + zend_hash_update(&msg->hdrs, "X-Original-Transfer-Encoding", sizeof("X-Original-Transfer-Encoding"), (void *) &c, sizeof(zval *), NULL); + zend_hash_del(&msg->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding")); + zend_hash_del(&msg->hdrs, "Content-Length", sizeof("Content-Length")); + zend_hash_update(&msg->hdrs, "Content-Length", sizeof("Content-Length"), (void *) &len, sizeof(zval *), NULL); + + phpstr_from_string_ex(PHPSTR(msg), decoded, decoded_len); + efree(decoded); + } + } + zval_ptr_dtor(&c); + } + + if (!*continue_at && (c = http_message_header(msg, "Content-Length"))) { + /* message has content-length header */ + ulong len = strtoul(Z_STRVAL_P(c), NULL, 10); + if (len > remaining) { + http_error_ex(HE_NOTICE, HTTP_E_MALFORMED_HEADERS, "The Content-Length header pretends a larger body than actually received (expected %lu bytes; got %lu bytes)", len, remaining); + len = remaining; + } + phpstr_from_string_ex(PHPSTR(msg), body, len); + *continue_at = body + len; + zval_ptr_dtor(&c); + } + + if (!*continue_at && (c = http_message_header(msg, "Content-Range"))) { + /* message has content-range header */ + ulong total = 0, start = 0, end = 0, len = 0; + + if (!strncasecmp(Z_STRVAL_P(c), "bytes", lenof("bytes")) && + ( Z_STRVAL_P(c)[lenof("bytes")] == ':' || + Z_STRVAL_P(c)[lenof("bytes")] == ' ' || + Z_STRVAL_P(c)[lenof("bytes")] == '=')) { + char *total_at = NULL, *end_at = NULL; + char *start_at = Z_STRVAL_P(c) + 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 ((len = (end + 1 - start)) > remaining) { + http_error_ex(HE_NOTICE, HTTP_E_MALFORMED_HEADERS, "The Content-Range header pretends a larger body than actually received (expected %lu bytes; got %lu bytes)", len, remaining); + len = remaining; + } + if (end >= start && (!total || end < total)) { + phpstr_from_string_ex(PHPSTR(msg), body, len); + *continue_at = body + len; + } + } + } + + if (!*continue_at) { + http_error_ex(HE_WARNING, HTTP_E_MALFORMED_HEADERS, "Invalid Content-Range header: %s", Z_STRVAL_P(c)); + } + zval_ptr_dtor(&c); + } + + if (!*continue_at) { + /* no headers that indicate content length */ + if (HTTP_MSG_TYPE(RESPONSE, msg)) { + phpstr_from_string_ex(PHPSTR(msg), body, remaining); + } else { + *continue_at = body; + } + } + +#ifdef HTTP_HAVE_ZLIB + /* check for compressed data */ + if ((c = http_message_header(msg, "Content-Encoding"))) { + char *decoded = NULL; + size_t decoded_len = 0; + + if ( !strcasecmp(Z_STRVAL_P(c), "gzip") || + !strcasecmp(Z_STRVAL_P(c), "x-gzip") || + !strcasecmp(Z_STRVAL_P(c), "deflate")) { + http_encoding_inflate(PHPSTR_VAL(msg), PHPSTR_LEN(msg), &decoded, &decoded_len); + } + + if (decoded) { + zval *len, **original_len; + char *tmp; + int tmp_len; + + tmp_len = (int) spprintf(&tmp, 0, "%zu", decoded_len); + MAKE_STD_ZVAL(len); + ZVAL_STRINGL(len, tmp, tmp_len, 0); + + ZVAL_ADDREF(c); + zend_hash_update(&msg->hdrs, "X-Original-Content-Encoding", sizeof("X-Original-Content-Encoding"), (void *) &c, sizeof(zval *), NULL); + zend_hash_del(&msg->hdrs, "Content-Encoding", sizeof("Content-Encoding")); + if (SUCCESS == zend_hash_find(&msg->hdrs, "Content-Length", sizeof("Content-Length"), (void *) &original_len)) { + ZVAL_ADDREF(*original_len); + zend_hash_update(&msg->hdrs, "X-Original-Content-Length", sizeof("X-Original-Content-Length"), (void *) original_len, sizeof(zval *), NULL); + zend_hash_update(&msg->hdrs, "Content-Length", sizeof("Content-Length"), (void *) &len, sizeof(zval *), NULL); + } else { + zend_hash_update(&msg->hdrs, "Content-Length", sizeof("Content-Length"), (void *) &len, sizeof(zval *), NULL); + } + + phpstr_dtor(PHPSTR(msg)); + PHPSTR(msg)->data = decoded; + PHPSTR(msg)->used = decoded_len; + PHPSTR(msg)->free = 1; + } + + zval_ptr_dtor(&c); + } +#endif /* HTTP_HAVE_ZLIB */ + } +} + +PHP_HTTP_API http_message *_http_message_parse_ex(http_message *msg, const char *message, size_t message_length ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC) +{ + const char *continue_at; + zend_bool free_msg = msg ? 0 : 1; + + if ((!message) || (message_length < HTTP_MSG_MIN_SIZE)) { + http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Empty or too short HTTP message: '%s'", message); + return NULL; + } + + msg = http_message_init_rel(msg, 0); + + if (SUCCESS != http_parse_headers_cb(message, &msg->hdrs, 1, (http_info_callback) http_message_info_callback, (void *) &msg)) { + if (free_msg) { + http_message_free(&msg); + } + http_error(HE_WARNING, HTTP_E_MALFORMED_HEADERS, "Failed to parse message headers"); + return NULL; + } + + http_message_body_parse(msg, message, message_length, &continue_at); + + /* check for following messages */ + if (continue_at && (continue_at < (message + message_length))) { + while (HTTP_IS_CTYPE(space, *continue_at)) ++continue_at; + if (continue_at < (message + message_length)) { + http_message *next = NULL, *most = NULL; + + /* set current message to parent of most parent following messages and return deepest */ + if ((most = next = http_message_parse_rel(NULL, continue_at, message + message_length - continue_at))) { + while (most->parent) most = most->parent; + most->parent = msg; + msg = next; + } + } + } + + return msg; +} + +PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_t *length) +{ + phpstr str; + HashKey key = initHashKey(0); + zval **header; + char *data; + HashPosition pos1; + + phpstr_init_ex(&str, 4096, 0); + + switch (msg->type) { + case HTTP_MSG_REQUEST: + phpstr_appendf(&str, HTTP_INFO_REQUEST_FMT_ARGS(&msg->http, HTTP_CRLF)); + break; + + case HTTP_MSG_RESPONSE: + phpstr_appendf(&str, HTTP_INFO_RESPONSE_FMT_ARGS(&msg->http, HTTP_CRLF)); + break; + + case HTTP_MSG_NONE: + default: + break; + } + + FOREACH_HASH_KEYVAL(pos1, &msg->hdrs, key, header) { + if (key.type == HASH_KEY_IS_STRING) { + HashPosition pos2; + zval **single_header; + + switch (Z_TYPE_PP(header)) { + case IS_BOOL: + phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key.str, Z_BVAL_PP(header)?"true":"false"); + break; + + case IS_LONG: + phpstr_appendf(&str, "%s: %ld" HTTP_CRLF, key.str, Z_LVAL_PP(header)); + break; + + case IS_DOUBLE: + phpstr_appendf(&str, "%s: %f" HTTP_CRLF, key.str, Z_DVAL_PP(header)); + break; + + case IS_STRING: + phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key.str, Z_STRVAL_PP(header)); + break; + + case IS_ARRAY: + FOREACH_VAL(pos2, *header, single_header) { + switch (Z_TYPE_PP(single_header)) { + case IS_BOOL: + phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key.str, Z_BVAL_PP(single_header)?"true":"false"); + break; + + case IS_LONG: + phpstr_appendf(&str, "%s: %ld" HTTP_CRLF, key.str, Z_LVAL_PP(single_header)); + break; + + case IS_DOUBLE: + phpstr_appendf(&str, "%s: %f" HTTP_CRLF, key.str, Z_DVAL_PP(single_header)); + break; + + case IS_STRING: + phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key.str, Z_STRVAL_PP(single_header)); + break; + } + } + break; + } + } + } + + if (PHPSTR_LEN(msg)) { + phpstr_appends(&str, HTTP_CRLF); + phpstr_append(&str, PHPSTR_VAL(msg), PHPSTR_LEN(msg)); + phpstr_appends(&str, HTTP_CRLF); + } + + data = phpstr_data(&str, string, length); + if (!string) { + efree(data); + } + + phpstr_dtor(&str); +} + +PHP_HTTP_API void _http_message_serialize(http_message *message, char **string, size_t *length) +{ + char *buf; + size_t len; + phpstr str; + + phpstr_init(&str); + + do { + http_message_tostring(message, &buf, &len); + phpstr_prepend(&str, buf, len); + efree(buf); + } while ((message = message->parent)); + + buf = phpstr_data(&str, string, length); + if (!string) { + efree(buf); + } + + phpstr_dtor(&str); +} + +PHP_HTTP_API http_message *_http_message_reverse(http_message *msg) +{ + int i, c; + + http_message_count(c, msg); + + if (c > 1) { + http_message *tmp = msg, **arr = ecalloc(c, sizeof(http_message *)); + + 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_API http_message *_http_message_interconnect(http_message *m1, http_message *m2) +{ + if (m1 && m2) { + int i = 0, c1, c2; + http_message *t1 = m1, *t2 = m2, *p1, *p2; + + http_message_count(c1, m1); + http_message_count(c2, m2); + + while (i++ < (c1 - c2)) { + t1 = t1->parent; + } + while (i++ <= c1) { + p1 = t1->parent; + p2 = t2->parent; + t1->parent = t2; + t2->parent = p1; + t1 = p1; + t2 = p2; + } + } else if (!m1 && m2) { + m1 = m2; + } + return m1; +} + +PHP_HTTP_API void _http_message_tostruct_recursive(http_message *msg, zval *obj TSRMLS_DC) +{ + zval strct; + zval *headers; + + INIT_ZARR(strct, HASH_OF(obj)); + + add_assoc_long(&strct, "type", msg->type); + add_assoc_double(&strct, "httpVersion", msg->http.version); + switch (msg->type) + { + case HTTP_MSG_RESPONSE: + add_assoc_long(&strct, "responseCode", msg->http.info.response.code); + add_assoc_string(&strct, "responseStatus", STR_PTR(msg->http.info.response.status), 1); + break; + + case HTTP_MSG_REQUEST: + add_assoc_string(&strct, "requestMethod", STR_PTR(msg->http.info.request.method), 1); + add_assoc_string(&strct, "requestUrl", STR_PTR(msg->http.info.request.url), 1); + break; + + case HTTP_MSG_NONE: + /* avoid compiler warning */ + break; + } + + MAKE_STD_ZVAL(headers); + array_init(headers); + zend_hash_copy(Z_ARRVAL_P(headers), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + add_assoc_zval(&strct, "headers", headers); + + add_assoc_stringl(&strct, "body", PHPSTR_VAL(msg), PHPSTR_LEN(msg), 1); + + if (msg->parent) { + zval *parent; + + MAKE_STD_ZVAL(parent); + if (Z_TYPE_P(obj) == IS_ARRAY) { + array_init(parent); + } else { + object_init(parent); + } + add_assoc_zval(&strct, "parentMessage", parent); + http_message_tostruct_recursive(msg->parent, parent); + } else { + add_assoc_null(&strct, "parentMessage"); + } +} + +PHP_HTTP_API STATUS _http_message_send(http_message *message TSRMLS_DC) +{ + STATUS rs = FAILURE; + + switch (message->type) { + case HTTP_MSG_RESPONSE: + { + HashKey key = initHashKey(0); + zval **val; + HashPosition pos; + + FOREACH_HASH_KEYVAL(pos, &message->hdrs, key, val) { + if (key.type == HASH_KEY_IS_STRING) { + http_send_header_zval_ex(key.str, key.len-1, val, 1); + } + } + rs = SUCCESS == http_send_status(message->http.info.response.code) && + SUCCESS == http_send_data(PHPSTR_VAL(message), PHPSTR_LEN(message)) ? + SUCCESS : FAILURE; + break; + } + + case HTTP_MSG_REQUEST: + { +#ifdef HTTP_HAVE_CURL + char *uri = NULL; + http_request request; + zval **zhost, *options, *headers; + + MAKE_STD_ZVAL(options); + MAKE_STD_ZVAL(headers); + array_init(options); + array_init(headers); + zend_hash_copy(Z_ARRVAL_P(headers), &message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + add_assoc_zval(options, "headers", headers); + + /* check host header */ + if (SUCCESS == zend_hash_find(&message->hdrs, "Host", sizeof("Host"), (void *) &zhost) && Z_TYPE_PP(zhost) == IS_STRING) { + char *colon = NULL; + php_url parts, *url = php_url_parse(message->http.info.request.url); + + memset(&parts, 0, sizeof(php_url)); + + /* check for port */ + if ((colon = strchr(Z_STRVAL_PP(zhost), ':'))) { + parts.port = atoi(colon + 1); + parts.host = estrndup(Z_STRVAL_PP(zhost), (Z_STRVAL_PP(zhost) - colon - 1)); + } else { + parts.host = estrndup(Z_STRVAL_PP(zhost), Z_STRLEN_PP(zhost)); + } + + http_build_url(HTTP_URL_REPLACE, url, &parts, NULL, &uri, NULL); + php_url_free(url); + efree(parts.host); + } else { + uri = http_absolute_url(message->http.info.request.url); + } + + if ((request.meth = http_request_method_exists(1, 0, message->http.info.request.method))) { + http_request_body body; + + http_request_init_ex(&request, NULL, request.meth, uri); + request.body = http_request_body_init_ex(&body, HTTP_REQUEST_BODY_CSTRING, PHPSTR_VAL(message), PHPSTR_LEN(message), 0); + if (SUCCESS == (rs = http_request_prepare(&request, Z_ARRVAL_P(options)))) { + http_request_exec(&request); + } + http_request_dtor(&request); + } else { + http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, + "Cannot send HttpMessage. Request method %s not supported", + message->http.info.request.method); + } + efree(uri); + zval_ptr_dtor(&options); +#else + http_error(HE_WARNING, HTTP_E_RUNTIME, "HTTP requests not supported - ext/http was not linked against libcurl."); +#endif + break; + } + + case HTTP_MSG_NONE: + default: + http_error(HE_WARNING, HTTP_E_MESSAGE_TYPE, "HttpMessage is neither of type HTTP_MSG_REQUEST nor HTTP_MSG_RESPONSE"); + break; + } + + return rs; +} + +PHP_HTTP_API http_message *_http_message_dup(http_message *orig TSRMLS_DC) +{ + http_message *temp, *copy = NULL; + http_info info; + + if (orig) { + info.type = orig->type; + info.http = orig->http; + + copy = temp = http_message_new(); + http_message_set_info(temp, &info); + zend_hash_copy(&temp->hdrs, &orig->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + phpstr_append(&temp->body, orig->body.data, orig->body.used); + + while (orig->parent) { + info.type = orig->parent->type; + info.http = orig->parent->http; + + temp->parent = http_message_new(); + http_message_set_info(temp->parent, &info); + zend_hash_copy(&temp->parent->hdrs, &orig->parent->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + phpstr_append(&temp->parent->body, orig->parent->body.data, orig->parent->body.used); + + temp = temp->parent; + orig = orig->parent; + } + } + + return copy; +} + +PHP_HTTP_API void _http_message_dtor(http_message *message) +{ + if (message) { + zend_hash_destroy(&message->hdrs); + phpstr_dtor(PHPSTR(message)); + + switch (message->type) { + case HTTP_MSG_REQUEST: + STR_SET(message->http.info.request.method, NULL); + STR_SET(message->http.info.request.url, NULL); + break; + + case HTTP_MSG_RESPONSE: + STR_SET(message->http.info.response.status, NULL); + break; + + default: + break; + } + } +} + +PHP_HTTP_API void _http_message_free(http_message **message) +{ + if (*message) { + if ((*message)->parent) { + http_message_free(&(*message)->parent); + } + http_message_dtor(*message); + efree(*message); + *message = 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 + */ + --- /dev/null +++ b/ext/http/http_message_object.c @@ -0,0 +1,1546 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_message_object.c 309640 2011-03-24 09:26:11Z mike $ */ + +#define HTTP_WANT_SAPI +#define HTTP_WANT_CURL +#define HTTP_WANT_MAGIC +#include "php_http.h" + +#ifdef ZEND_ENGINE_2 + +#include "zend_interfaces.h" +#include "ext/standard/url.h" +#include "php_variables.h" + +#include "php_http_api.h" +#include "php_http_send_api.h" +#include "php_http_url_api.h" +#include "php_http_message_api.h" +#include "php_http_message_object.h" +#include "php_http_exception_object.h" +#include "php_http_response_object.h" +#include "php_http_request_method_api.h" +#include "php_http_request_api.h" +#include "php_http_request_object.h" +#include "php_http_headers_api.h" + +#if defined(HTTP_HAVE_SPL) && !defined(WONKY) +/* SPL doesn't install its headers */ +extern PHPAPI zend_class_entry *spl_ce_Countable; +#endif + +#define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpMessage, method, 0, req_args) +#define HTTP_EMPTY_ARGS(method) HTTP_EMPTY_ARGS_EX(HttpMessage, method, 0) +#define HTTP_MESSAGE_ME(method, visibility) PHP_ME(HttpMessage, method, HTTP_ARGS(HttpMessage, method), visibility) + +HTTP_BEGIN_ARGS(__construct, 0) + HTTP_ARG_VAL(message, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(factory, 0) + HTTP_ARG_VAL(message, 0) + HTTP_ARG_VAL(class_name, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(fromEnv, 1) + HTTP_ARG_VAL(type, 0) + HTTP_ARG_VAL(class_name, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getBody); +HTTP_BEGIN_ARGS(setBody, 1) + HTTP_ARG_VAL(body, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(getHeader, 1) + HTTP_ARG_VAL(header, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getHeaders); +HTTP_BEGIN_ARGS(setHeaders, 1) + HTTP_ARG_VAL(headers, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(addHeaders, 1) + HTTP_ARG_VAL(headers, 0) + HTTP_ARG_VAL(append, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getType); +HTTP_BEGIN_ARGS(setType, 1) + HTTP_ARG_VAL(type, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getInfo); +HTTP_BEGIN_ARGS(setInfo, 1) + HTTP_ARG_VAL(http_info, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getResponseCode); +HTTP_BEGIN_ARGS(setResponseCode, 1) + HTTP_ARG_VAL(response_code, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getResponseStatus); +HTTP_BEGIN_ARGS(setResponseStatus, 1) + HTTP_ARG_VAL(response_status, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getRequestMethod); +HTTP_BEGIN_ARGS(setRequestMethod, 1) + HTTP_ARG_VAL(request_method, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getRequestUrl); +HTTP_BEGIN_ARGS(setRequestUrl, 1) + HTTP_ARG_VAL(url, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getHttpVersion); +HTTP_BEGIN_ARGS(setHttpVersion, 1) + HTTP_ARG_VAL(http_version, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(guessContentType, 1) + HTTP_ARG_VAL(magic_file, 0) + HTTP_ARG_VAL(magic_mode, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getParentMessage); +HTTP_EMPTY_ARGS(send); +HTTP_EMPTY_ARGS(__toString); +HTTP_BEGIN_ARGS(toString, 0) + HTTP_ARG_VAL(include_parent, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(toMessageTypeObject); + +HTTP_EMPTY_ARGS(count); + +HTTP_EMPTY_ARGS(serialize); +HTTP_BEGIN_ARGS(unserialize, 1) + HTTP_ARG_VAL(serialized, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(rewind); +HTTP_EMPTY_ARGS(valid); +HTTP_EMPTY_ARGS(key); +HTTP_EMPTY_ARGS(current); +HTTP_EMPTY_ARGS(next); + +HTTP_EMPTY_ARGS(detach); +HTTP_BEGIN_ARGS(prepend, 1) + HTTP_ARG_OBJ(HttpMessage, message, 0) +HTTP_END_ARGS; +HTTP_EMPTY_ARGS(reverse); + +#define http_message_object_read_prop _http_message_object_read_prop +static zval *_http_message_object_read_prop(zval *object, zval *member, int type ZEND_LITERAL_KEY_DC TSRMLS_DC); +#define http_message_object_write_prop _http_message_object_write_prop +static void _http_message_object_write_prop(zval *object, zval *member, zval *value ZEND_LITERAL_KEY_DC TSRMLS_DC); +#define http_message_object_get_prop_ptr _http_message_object_get_prop_ptr +static zval **_http_message_object_get_prop_ptr(zval *object, zval *member ZEND_LITERAL_KEY_DC TSRMLS_DC); +#define http_message_object_get_props _http_message_object_get_props +static HashTable *_http_message_object_get_props(zval *object TSRMLS_DC); + +#define THIS_CE http_message_object_ce +zend_class_entry *http_message_object_ce; +zend_function_entry http_message_object_fe[] = { + HTTP_MESSAGE_ME(__construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) + HTTP_MESSAGE_ME(getBody, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(setBody, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(getHeader, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(getHeaders, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(setHeaders, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(addHeaders, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(getType, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(setType, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(getInfo, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(setInfo, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(getResponseCode, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(setResponseCode, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(getResponseStatus, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(setResponseStatus, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(getRequestMethod, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(setRequestMethod, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(getRequestUrl, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(setRequestUrl, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(getHttpVersion, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(setHttpVersion, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(guessContentType, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(getParentMessage, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(send, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(toString, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(toMessageTypeObject, ZEND_ACC_PUBLIC) + + /* implements Countable */ + HTTP_MESSAGE_ME(count, ZEND_ACC_PUBLIC) + + /* implements Serializable */ + HTTP_MESSAGE_ME(serialize, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(unserialize, ZEND_ACC_PUBLIC) + + /* implements Iterator */ + HTTP_MESSAGE_ME(rewind, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(valid, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(current, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(key, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(next, ZEND_ACC_PUBLIC) + + ZEND_MALIAS(HttpMessage, __toString, toString, HTTP_ARGS(HttpMessage, __toString), ZEND_ACC_PUBLIC) + + HTTP_MESSAGE_ME(factory, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + ZEND_MALIAS(HttpMessage, fromString, factory, HTTP_ARGS(HttpMessage, factory), ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + HTTP_MESSAGE_ME(fromEnv, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + + HTTP_MESSAGE_ME(detach, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(prepend, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(reverse, ZEND_ACC_PUBLIC) + + EMPTY_FUNCTION_ENTRY +}; +static zend_object_handlers http_message_object_handlers; + +static HashTable http_message_object_prophandlers; + +typedef void (*http_message_object_prophandler_func)(http_message_object *o, zval *v TSRMLS_DC); + +typedef struct _http_message_object_prophandler { + http_message_object_prophandler_func read; + http_message_object_prophandler_func write; +} http_message_object_prophandler; + +static STATUS http_message_object_add_prophandler(const char *prop_str, size_t prop_len, http_message_object_prophandler_func read, http_message_object_prophandler_func write) { + http_message_object_prophandler h = { read, write }; + return zend_hash_add(&http_message_object_prophandlers, prop_str, prop_len, (void *) &h, sizeof(h), NULL); +} +static STATUS http_message_object_get_prophandler(const char *prop_str, size_t prop_len, http_message_object_prophandler **handler) { + return zend_hash_find(&http_message_object_prophandlers, prop_str, prop_len, (void *) handler); +} +static void http_message_object_prophandler_get_type(http_message_object *obj, zval *return_value TSRMLS_DC) { + RETVAL_LONG(obj->message->type); +} +static void http_message_object_prophandler_set_type(http_message_object *obj, zval *value TSRMLS_DC) { + zval *cpy = http_zsep(IS_LONG, value); + http_message_set_type(obj->message, Z_LVAL_P(cpy)); + zval_ptr_dtor(&cpy); +} +static void http_message_object_prophandler_get_body(http_message_object *obj, zval *return_value TSRMLS_DC) { + phpstr_fix(PHPSTR(obj->message)); + RETVAL_PHPSTR(PHPSTR(obj->message), 0, 1); +} +static void http_message_object_prophandler_set_body(http_message_object *obj, zval *value TSRMLS_DC) { + zval *cpy = http_zsep(IS_STRING, value); + phpstr_dtor(PHPSTR(obj->message)); + phpstr_from_string_ex(PHPSTR(obj->message), Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)); + zval_ptr_dtor(&cpy); +} +static void http_message_object_prophandler_get_request_method(http_message_object *obj, zval *return_value TSRMLS_DC) { + if (HTTP_MSG_TYPE(REQUEST, obj->message) && obj->message->http.info.request.method) { + RETVAL_STRING(obj->message->http.info.request.method, 1); + } else { + RETVAL_NULL(); + } +} +static void http_message_object_prophandler_set_request_method(http_message_object *obj, zval *value TSRMLS_DC) { + if (HTTP_MSG_TYPE(REQUEST, obj->message)) { + zval *cpy = http_zsep(IS_STRING, value); + STR_SET(obj->message->http.info.request.method, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy))); + zval_ptr_dtor(&cpy); + } +} +static void http_message_object_prophandler_get_request_url(http_message_object *obj, zval *return_value TSRMLS_DC) { + if (HTTP_MSG_TYPE(REQUEST, obj->message) && obj->message->http.info.request.url) { + RETVAL_STRING(obj->message->http.info.request.url, 1); + } else { + RETVAL_NULL(); + } +} +static void http_message_object_prophandler_set_request_url(http_message_object *obj, zval *value TSRMLS_DC) { + if (HTTP_MSG_TYPE(REQUEST, obj->message)) { + zval *cpy = http_zsep(IS_STRING, value); + STR_SET(obj->message->http.info.request.url, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy))); + zval_ptr_dtor(&cpy); + } +} +static void http_message_object_prophandler_get_response_status(http_message_object *obj, zval *return_value TSRMLS_DC) { + if (HTTP_MSG_TYPE(RESPONSE, obj->message) && obj->message->http.info.response.status) { + RETVAL_STRING(obj->message->http.info.response.status, 1); + } else { + RETVAL_NULL(); + } +} +static void http_message_object_prophandler_set_response_status(http_message_object *obj, zval *value TSRMLS_DC) { + if (HTTP_MSG_TYPE(RESPONSE, obj->message)) { + zval *cpy = http_zsep(IS_STRING, value); + STR_SET(obj->message->http.info.response.status, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy))); + zval_ptr_dtor(&cpy); + } +} +static void http_message_object_prophandler_get_response_code(http_message_object *obj, zval *return_value TSRMLS_DC) { + if (HTTP_MSG_TYPE(RESPONSE, obj->message)) { + RETVAL_LONG(obj->message->http.info.response.code); + } else { + RETVAL_NULL(); + } +} +static void http_message_object_prophandler_set_response_code(http_message_object *obj, zval *value TSRMLS_DC) { + if (HTTP_MSG_TYPE(RESPONSE, obj->message)) { + zval *cpy = http_zsep(IS_LONG, value); + obj->message->http.info.response.code = Z_LVAL_P(cpy); + zval_ptr_dtor(&cpy); + } +} +static void http_message_object_prophandler_get_http_version(http_message_object *obj, zval *return_value TSRMLS_DC) { + RETVAL_DOUBLE(obj->message->http.version); +} +static void http_message_object_prophandler_set_http_version(http_message_object *obj, zval *value TSRMLS_DC) { + zval *cpy = http_zsep(IS_DOUBLE, value); + obj->message->http.version = Z_DVAL_P(cpy); + zval_ptr_dtor(&cpy); +} +static void http_message_object_prophandler_get_headers(http_message_object *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 http_message_object_prophandler_set_headers(http_message_object *obj, zval *value TSRMLS_DC) { + zval *cpy = http_zsep(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 http_message_object_prophandler_get_parent_message(http_message_object *obj, zval *return_value TSRMLS_DC) { + if (obj->message->parent) { + RETVAL_OBJVAL(obj->parent, 1); + } else { + RETVAL_NULL(); + } +} +static void http_message_object_prophandler_set_parent_message(http_message_object *obj, zval *value TSRMLS_DC) { + if (Z_TYPE_P(value) == IS_OBJECT && instanceof_function(Z_OBJCE_P(value), http_message_object_ce TSRMLS_CC)) { + if (obj->message->parent) { + zval tmp; + tmp.value.obj = obj->parent; + Z_OBJ_DELREF(tmp); + } + Z_OBJ_ADDREF_P(value); + obj->parent = value->value.obj; + } +} + +PHP_MINIT_FUNCTION(http_message_object) +{ + HTTP_REGISTER_CLASS_EX(HttpMessage, http_message_object, NULL, 0); + +#ifndef WONKY +# ifdef HTTP_HAVE_SPL + zend_class_implements(http_message_object_ce TSRMLS_CC, 3, spl_ce_Countable, zend_ce_serializable, zend_ce_iterator); +# else + zend_class_implements(http_message_object_ce TSRMLS_CC, 2, zend_ce_serializable, zend_ce_iterator); +# endif +#else + zend_class_implements(http_message_object_ce TSRMLS_CC, 1, zend_ce_iterator); +#endif + + http_message_object_handlers.clone_obj = _http_message_object_clone_obj; + http_message_object_handlers.read_property = http_message_object_read_prop; + http_message_object_handlers.write_property = http_message_object_write_prop; + http_message_object_handlers.get_properties = http_message_object_get_props; + http_message_object_handlers.get_property_ptr_ptr = http_message_object_get_prop_ptr; + + zend_hash_init(&http_message_object_prophandlers, 9, NULL, NULL, 1); + zend_declare_property_long(THIS_CE, ZEND_STRS("type")-1, HTTP_MSG_NONE, ZEND_ACC_PROTECTED TSRMLS_CC); + http_message_object_add_prophandler(ZEND_STRS("type")-1, http_message_object_prophandler_get_type, http_message_object_prophandler_set_type); + zend_declare_property_string(THIS_CE, ZEND_STRS("body")-1, "", ZEND_ACC_PROTECTED TSRMLS_CC); + http_message_object_add_prophandler(ZEND_STRS("body")-1, http_message_object_prophandler_get_body, http_message_object_prophandler_set_body); + zend_declare_property_string(THIS_CE, ZEND_STRS("requestMethod")-1, "", ZEND_ACC_PROTECTED TSRMLS_CC); + http_message_object_add_prophandler(ZEND_STRS("requestMethod")-1, http_message_object_prophandler_get_request_method, http_message_object_prophandler_set_request_method); + zend_declare_property_string(THIS_CE, ZEND_STRS("requestUrl")-1, "", ZEND_ACC_PROTECTED TSRMLS_CC); + http_message_object_add_prophandler(ZEND_STRS("requestUrl")-1, http_message_object_prophandler_get_request_url, http_message_object_prophandler_set_request_url); + zend_declare_property_string(THIS_CE, ZEND_STRS("responseStatus")-1, "", ZEND_ACC_PROTECTED TSRMLS_CC); + http_message_object_add_prophandler(ZEND_STRS("responseStatus")-1, http_message_object_prophandler_get_response_status, http_message_object_prophandler_set_response_status); + zend_declare_property_long(THIS_CE, ZEND_STRS("responseCode")-1, 0, ZEND_ACC_PROTECTED TSRMLS_CC); + http_message_object_add_prophandler(ZEND_STRS("responseCode")-1, http_message_object_prophandler_get_response_code, http_message_object_prophandler_set_response_code); + zend_declare_property_null(THIS_CE, ZEND_STRS("httpVersion")-1, ZEND_ACC_PROTECTED TSRMLS_CC); + http_message_object_add_prophandler(ZEND_STRS("httpVersion")-1, http_message_object_prophandler_get_http_version, http_message_object_prophandler_set_http_version); + zend_declare_property_null(THIS_CE, ZEND_STRS("headers")-1, ZEND_ACC_PROTECTED TSRMLS_CC); + http_message_object_add_prophandler(ZEND_STRS("headers")-1, http_message_object_prophandler_get_headers, http_message_object_prophandler_set_headers); + zend_declare_property_null(THIS_CE, ZEND_STRS("parentMessage")-1, ZEND_ACC_PROTECTED TSRMLS_CC); + http_message_object_add_prophandler(ZEND_STRS("parentMessage")-1, http_message_object_prophandler_get_parent_message, http_message_object_prophandler_set_parent_message); + +#ifndef WONKY + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("TYPE_NONE")-1, HTTP_MSG_NONE TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("TYPE_REQUEST")-1, HTTP_MSG_REQUEST TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("TYPE_RESPONSE")-1, HTTP_MSG_RESPONSE TSRMLS_CC); +#endif + + HTTP_LONG_CONSTANT("HTTP_MSG_NONE", HTTP_MSG_NONE); + HTTP_LONG_CONSTANT("HTTP_MSG_REQUEST", HTTP_MSG_REQUEST); + HTTP_LONG_CONSTANT("HTTP_MSG_RESPONSE", HTTP_MSG_RESPONSE); + + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(http_message_object) +{ + zend_hash_destroy(&http_message_object_prophandlers); + + return SUCCESS; +} + +void _http_message_object_reverse(zval *this_ptr, zval *return_value TSRMLS_DC) +{ + int i; + getObject(http_message_object, obj); + + /* count */ + http_message_count(i, obj->message); + + if (i > 1) { + zval o; + zend_object_value *ovalues = NULL; + http_message_object **objects = NULL; + int last = i - 1; + + objects = ecalloc(i, sizeof(http_message_object *)); + ovalues = ecalloc(i, sizeof(zend_object_value)); + + /* we are the first message */ + objects[0] = obj; + ovalues[0] = getThis()->value.obj; + + /* fetch parents */ + INIT_PZVAL(&o); + o.type = IS_OBJECT; + for (i = 1; obj->parent.handle; ++i) { + o.value.obj = obj->parent; + ovalues[i] = o.value.obj; + objects[i] = obj = zend_object_store_get_object(&o TSRMLS_CC); + } + + /* reorder parents */ + for (last = --i; i; --i) { + objects[i]->message->parent = objects[i-1]->message; + objects[i]->parent = ovalues[i-1]; + } + objects[0]->message->parent = NULL; + objects[0]->parent.handle = 0; + objects[0]->parent.handlers = NULL; + + /* add ref (why?) */ + Z_OBJ_ADDREF_P(getThis()); + RETVAL_OBJVAL(ovalues[last], 1); + + efree(objects); + efree(ovalues); + } else { + RETURN_ZVAL(getThis(), 1, 0); + } +} + +void _http_message_object_prepend_ex(zval *this_ptr, zval *prepend, zend_bool top TSRMLS_DC) +{ + zval m; + http_message *save_parent_msg = NULL; + zend_object_value save_parent_obj = {0, NULL}; + getObject(http_message_object, obj); + getObjectEx(http_message_object, prepend_obj, prepend); + + 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.handle) { + m.value.obj = obj->parent; + obj = zend_object_store_get_object(&m TSRMLS_CC); + } + } + + /* prepend */ + obj->parent = prepend->value.obj; + obj->message->parent = prepend_obj->message; + + /* add ref */ + zend_objects_store_add_ref(prepend TSRMLS_CC); + while (prepend_obj->parent.handle) { + m.value.obj = prepend_obj->parent; + zend_objects_store_add_ref(&m TSRMLS_CC); + prepend_obj = zend_object_store_get_object(&m TSRMLS_CC); + } + + if (!top) { + prepend_obj->parent = save_parent_obj; + prepend_obj->message->parent = save_parent_msg; + } +} + +zend_object_value _http_message_object_new(zend_class_entry *ce TSRMLS_DC) +{ + return http_message_object_new_ex(ce, NULL, NULL); +} + +zend_object_value _http_message_object_new_ex(zend_class_entry *ce, http_message *msg, http_message_object **ptr TSRMLS_DC) +{ + zend_object_value ov; + http_message_object *o; + + o = ecalloc(1, sizeof(http_message_object)); + o->zo.ce = ce; + + if (ptr) { + *ptr = o; + } + + if (msg) { + o->message = msg; + if (msg->parent) { + o->parent = http_message_object_new_ex(ce, msg->parent, NULL); + } + } + + ALLOC_HASHTABLE(OBJ_PROP(o)); + zend_hash_init(OBJ_PROP(o), zend_hash_num_elements(&ce->default_properties), NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(OBJ_PROP(o), &ce->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + + ov.handle = putObject(http_message_object, o); + ov.handlers = &http_message_object_handlers; + + return ov; +} + +zend_object_value _http_message_object_clone_obj(zval *this_ptr TSRMLS_DC) +{ + zend_object_value new_ov; + http_message_object *new_obj = NULL; + getObject(http_message_object, old_obj); + + new_ov = http_message_object_new_ex(old_obj->zo.ce, http_message_dup(old_obj->message), &new_obj); + zend_objects_clone_members(&new_obj->zo, new_ov, &old_obj->zo, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC); + + return new_ov; +} + +void _http_message_object_free(zend_object *object TSRMLS_DC) +{ + http_message_object *o = (http_message_object *) object; + + if (o->iterator) { + zval_ptr_dtor(&o->iterator); + o->iterator = NULL; + } + if (o->message) { + http_message_dtor(o->message); + efree(o->message); + } + if (o->parent.handle) { + zval p; + + INIT_PZVAL(&p); + p.type = IS_OBJECT; + p.value.obj = o->parent; + zend_objects_store_del_ref(&p TSRMLS_CC); + } + freeObject(o); +} + +static zval **_http_message_object_get_prop_ptr(zval *object, zval *member ZEND_LITERAL_KEY_DC TSRMLS_DC) { + getObjectEx(http_message_object, obj, object); + http_message_object_prophandler *handler; + + if (SUCCESS == http_message_object_get_prophandler(Z_STRVAL_P(member), Z_STRLEN_P(member), &handler)) { + zend_error(E_ERROR, "Cannot access HttpMessage properties by reference or array key/index"); + return NULL; + } + + return zend_get_std_object_handlers()->get_property_ptr_ptr(object, member ZEND_LITERAL_KEY_CC TSRMLS_CC); +} + +static zval *_http_message_object_read_prop(zval *object, zval *member, int type ZEND_LITERAL_KEY_DC TSRMLS_DC) +{ + getObjectEx(http_message_object, obj, object); + http_message_object_prophandler *handler; + zval *return_value; + + if (SUCCESS == http_message_object_get_prophandler(Z_STRVAL_P(member), Z_STRLEN_P(member), &handler)) { + if (type == BP_VAR_W) { + zend_error(E_ERROR, "Cannot access HttpMessage properties by reference or array key/index"); + return NULL; + } + + ALLOC_ZVAL(return_value); +#ifdef Z_SET_REFCOUNT + Z_SET_REFCOUNT_P(return_value, 0); + Z_UNSET_ISREF_P(return_value); +#else + return_value->refcount = 0; + return_value->is_ref = 0; +#endif + + handler->read(obj, return_value TSRMLS_CC); + + } else { + return_value = zend_get_std_object_handlers()->read_property(object, member, type ZEND_LITERAL_KEY_CC TSRMLS_CC); + } + + return return_value; +} + +static void _http_message_object_write_prop(zval *object, zval *member, zval *value ZEND_LITERAL_KEY_DC TSRMLS_DC) +{ + getObjectEx(http_message_object, obj, object); + http_message_object_prophandler *handler; + + if (SUCCESS == http_message_object_get_prophandler(Z_STRVAL_P(member), Z_STRLEN_P(member), &handler)) { + handler->write(obj, value TSRMLS_CC); + } else { + zend_get_std_object_handlers()->write_property(object, member, value ZEND_LITERAL_KEY_CC TSRMLS_CC); + } +} + +static HashTable *_http_message_object_get_props(zval *object TSRMLS_DC) +{ + zval *headers; + getObjectEx(http_message_object, obj, object); + http_message *msg = obj->message; + HashTable *props = OBJ_PROP(obj); + zval array, *parent; + + INIT_ZARR(array, props); + +#define ASSOC_PROP(array, ptype, name, val) \ + { \ + char *m_prop_name; \ + int m_prop_len; \ + zend_mangle_property_name(&m_prop_name, &m_prop_len, "*", 1, name, lenof(name), 0); \ + add_assoc_ ##ptype## _ex(&array, m_prop_name, sizeof(name)+3, val); \ + efree(m_prop_name); \ + } +#define ASSOC_STRING(array, name, val) ASSOC_STRINGL(array, name, val, strlen(val)) +#define ASSOC_STRINGL(array, name, val, len) \ + { \ + char *m_prop_name; \ + int m_prop_len; \ + zend_mangle_property_name(&m_prop_name, &m_prop_len, "*", 1, name, lenof(name), 0); \ + add_assoc_stringl_ex(&array, m_prop_name, sizeof(name)+3, val, len, 1); \ + efree(m_prop_name); \ + } + + ASSOC_PROP(array, long, "type", msg->type); + ASSOC_PROP(array, double, "httpVersion", msg->http.version); + + switch (msg->type) { + case HTTP_MSG_REQUEST: + ASSOC_PROP(array, long, "responseCode", 0); + ASSOC_STRINGL(array, "responseStatus", "", 0); + ASSOC_STRING(array, "requestMethod", STR_PTR(msg->http.info.request.method)); + ASSOC_STRING(array, "requestUrl", STR_PTR(msg->http.info.request.url)); + break; + + case HTTP_MSG_RESPONSE: + ASSOC_PROP(array, long, "responseCode", msg->http.info.response.code); + ASSOC_STRING(array, "responseStatus", STR_PTR(msg->http.info.response.status)); + ASSOC_STRINGL(array, "requestMethod", "", 0); + ASSOC_STRINGL(array, "requestUrl", "", 0); + break; + + case HTTP_MSG_NONE: + default: + ASSOC_PROP(array, long, "responseCode", 0); + ASSOC_STRINGL(array, "responseStatus", "", 0); + ASSOC_STRINGL(array, "requestMethod", "", 0); + ASSOC_STRINGL(array, "requestUrl", "", 0); + break; + } + + MAKE_STD_ZVAL(headers); + array_init(headers); + zend_hash_copy(Z_ARRVAL_P(headers), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + ASSOC_PROP(array, zval, "headers", headers); + ASSOC_STRINGL(array, "body", PHPSTR_VAL(msg), PHPSTR_LEN(msg)); + + MAKE_STD_ZVAL(parent); + if (msg->parent) { + ZVAL_OBJVAL(parent, obj->parent, 1); + } else { + ZVAL_NULL(parent); + } + ASSOC_PROP(array, zval, "parentMessage", parent); + + return OBJ_PROP(obj); +} + +/* ### USERLAND ### */ + +/* {{{ proto void HttpMessage::__construct([string message]) + Create a new HttpMessage object instance. */ +PHP_METHOD(HttpMessage, __construct) +{ + int length = 0; + char *message = NULL; + + getObject(http_message_object, obj); + + SET_EH_THROW_HTTP(); + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &message, &length) && message && length) { + http_message *msg = obj->message; + + http_message_dtor(msg); + if ((obj->message = http_message_parse_ex(msg, message, length))) { + if (obj->message->parent) { + obj->parent = http_message_object_new_ex(Z_OBJCE_P(getThis()), obj->message->parent, NULL); + } + } else { + obj->message = http_message_init(msg); + } + } + if (!obj->message) { + obj->message = http_message_new(); + } + SET_EH_NORMAL(); +} +/* }}} */ + +/* {{{ proto static HttpMessage HttpMessage::factory([string raw_message[, string class_name = "HttpMessage"]]) + Create a new HttpMessage object instance. */ +PHP_METHOD(HttpMessage, factory) +{ + char *string = NULL, *cn = NULL; + int length = 0, cl = 0; + http_message *msg = NULL; + zend_object_value ov; + http_message_object *obj = NULL; + + RETVAL_NULL(); + + SET_EH_THROW_HTTP(); + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ss", &string, &length, &cn, &cl)) { + if (length) { + msg = http_message_parse(string, length); + } + if ((msg || !length) && SUCCESS == http_object_new(&ov, cn, cl, _http_message_object_new_ex, http_message_object_ce, msg, &obj)) { + RETVAL_OBJVAL(ov, 0); + } + if (obj && !obj->message) { + obj->message = http_message_new(); + } + } + SET_EH_NORMAL(); +} +/* }}} */ + +/* {{{ proto static HttpMessage HttpMessage::fromEnv(int type[, string class_name = "HttpMessage"]) + Create a new HttpMessage object from environment representing either current request or response */ +PHP_METHOD(HttpMessage, fromEnv) +{ + char *cn = NULL; + int cl = 0; + long type; + http_message_object *obj = NULL; + zend_object_value ov; + + RETVAL_NULL(); + SET_EH_THROW_HTTP(); + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|s", &type, &cn, &cl)) { + if (SUCCESS == http_object_new(&ov, cn, cl, _http_message_object_new_ex, http_message_object_ce, http_message_init_env(NULL, type), &obj)) { + RETVAL_OBJVAL(ov, 0); + } + if (obj && !obj->message) { + obj->message = http_message_new(); + } + } + SET_EH_NORMAL(); +} +/* }}} */ + +/* {{{ proto string HttpMessage::getBody() + Get the body of the parsed HttpMessage. */ +PHP_METHOD(HttpMessage, getBody) +{ + NO_ARGS; + + if (return_value_used) { + getObject(http_message_object, obj); + RETURN_PHPSTR(&obj->message->body, PHPSTR_FREE_NOT, 1); + } +} +/* }}} */ + +/* {{{ proto void HttpMessage::setBody(string body) + Set the body of the HttpMessage. NOTE: Don't forget to update any headers accordingly. */ +PHP_METHOD(HttpMessage, setBody) +{ + char *body; + int len; + getObject(http_message_object, obj); + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &body, &len)) { + phpstr_dtor(PHPSTR(obj->message)); + phpstr_from_string_ex(PHPSTR(obj->message), body, len); + } +} +/* }}} */ + +/* {{{ proto string HttpMessage::getHeader(string header) + Get message header. */ +PHP_METHOD(HttpMessage, getHeader) +{ + zval *header; + char *orig_header, *nice_header; + int header_len; + getObject(http_message_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &orig_header, &header_len)) { + RETURN_FALSE; + } + + nice_header = pretty_key(estrndup(orig_header, header_len), header_len, 1, 1); + if ((header = http_message_header_ex(obj->message, nice_header, header_len + 1, 0))) { + RETVAL_ZVAL(header, 1, 1); + } + efree(nice_header); +} +/* }}} */ + +/* {{{ proto array HttpMessage::getHeaders() + Get Message Headers. */ +PHP_METHOD(HttpMessage, getHeaders) +{ + NO_ARGS; + + if (return_value_used) { + getObject(http_message_object, obj); + + array_init(return_value); + array_copy(&obj->message->hdrs, Z_ARRVAL_P(return_value)); + } +} +/* }}} */ + +/* {{{ proto void HttpMessage::setHeaders(array headers) + Sets new headers. */ +PHP_METHOD(HttpMessage, setHeaders) +{ + zval *new_headers = NULL; + getObject(http_message_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/!", &new_headers)) { + return; + } + + zend_hash_clean(&obj->message->hdrs); + if (new_headers) { + array_copy(Z_ARRVAL_P(new_headers), &obj->message->hdrs); + } +} +/* }}} */ + +/* {{{ proto void HttpMessage::addHeaders(array headers[, bool append = false]) + Add headers. If append is true, headers with the same name will be separated, else overwritten. */ +PHP_METHOD(HttpMessage, addHeaders) +{ + zval *new_headers; + zend_bool append = 0; + getObject(http_message_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &new_headers, &append)) { + return; + } + + array_join(Z_ARRVAL_P(new_headers), &obj->message->hdrs, append, ARRAY_JOIN_STRONLY|ARRAY_JOIN_PRETTIFY); +} +/* }}} */ + +/* {{{ proto int HttpMessage::getType() + Get Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE) */ +PHP_METHOD(HttpMessage, getType) +{ + NO_ARGS; + + if (return_value_used) { + getObject(http_message_object, obj); + RETURN_LONG(obj->message->type); + } +} +/* }}} */ + +/* {{{ proto void HttpMessage::setType(int type) + Set Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE) */ +PHP_METHOD(HttpMessage, setType) +{ + long type; + getObject(http_message_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type)) { + return; + } + http_message_set_type(obj->message, type); +} +/* }}} */ + +/* {{{ proto string HttpMessage::getInfo(void) + Get the HTTP request/response line */ +PHP_METHOD(HttpMessage, getInfo) +{ + NO_ARGS; + + if (return_value_used) { + getObject(http_message_object, obj); + + switch (obj->message->type) { + case HTTP_MSG_REQUEST: + Z_STRLEN_P(return_value) = spprintf(&Z_STRVAL_P(return_value), 0, HTTP_INFO_REQUEST_FMT_ARGS(&obj->message->http, "")); + break; + case HTTP_MSG_RESPONSE: + Z_STRLEN_P(return_value) = spprintf(&Z_STRVAL_P(return_value), 0, HTTP_INFO_RESPONSE_FMT_ARGS(&obj->message->http, "")); + break; + default: + RETURN_NULL(); + break; + } + Z_TYPE_P(return_value) = IS_STRING; + } +} +/* }}} */ + +/* {{{ proto bool HttpMessage::setInfo(string http_info) + Set type and request or response info with a standard HTTP request or response line */ +PHP_METHOD(HttpMessage, setInfo) +{ + char *str; + int len; + http_info inf; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len) && SUCCESS == http_info_parse_ex(str, &inf, 0)) { + getObject(http_message_object, obj); + + http_message_set_info(obj->message, &inf); + http_info_dtor(&inf); + RETURN_TRUE; + } + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto int HttpMessage::getResponseCode() + Get the Response Code of the Message. */ +PHP_METHOD(HttpMessage, getResponseCode) +{ + NO_ARGS; + + if (return_value_used) { + getObject(http_message_object, obj); + HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE); + RETURN_LONG(obj->message->http.info.response.code); + } +} +/* }}} */ + +/* {{{ proto bool HttpMessage::setResponseCode(int code) + Set the response code of an HTTP Response Message. */ +PHP_METHOD(HttpMessage, setResponseCode) +{ + long code; + getObject(http_message_object, obj); + + HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) { + RETURN_FALSE; + } + if (code < 100 || code > 599) { + http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid response code (100-599): %ld", code); + RETURN_FALSE; + } + + obj->message->http.info.response.code = code; + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HttpMessage::getResponseStatus() + Get the Response Status of the message (i.e. the string following the response code). */ +PHP_METHOD(HttpMessage, getResponseStatus) +{ + NO_ARGS; + + if (return_value_used) { + getObject(http_message_object, obj); + HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE); + if (obj->message->http.info.response.status) { + RETURN_STRING(obj->message->http.info.response.status, 1); + } else { + RETURN_EMPTY_STRING(); + } + } +} +/* }}} */ + +/* {{{ proto bool HttpMessage::setResponseStatus(string status) + Set the Response Status of the HTTP message (i.e. the string following the response code). */ +PHP_METHOD(HttpMessage, setResponseStatus) +{ + char *status; + int status_len; + getObject(http_message_object, obj); + + HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &status, &status_len)) { + RETURN_FALSE; + } + STR_SET(obj->message->http.info.response.status, estrndup(status, status_len)); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HttpMessage::getRequestMethod() + Get the Request Method of the Message. */ +PHP_METHOD(HttpMessage, getRequestMethod) +{ + NO_ARGS; + + if (return_value_used) { + getObject(http_message_object, obj); + HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE); + if (obj->message->http.info.request.method) { + RETURN_STRING(obj->message->http.info.request.method, 1); + } else { + RETURN_EMPTY_STRING(); + } + } +} +/* }}} */ + +/* {{{ proto bool HttpMessage::setRequestMethod(string method) + Set the Request Method of the HTTP Message. */ +PHP_METHOD(HttpMessage, setRequestMethod) +{ + char *method; + int method_len; + getObject(http_message_object, obj); + + HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len)) { + RETURN_FALSE; + } + if (method_len < 1) { + http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestMethod to an empty string"); + RETURN_FALSE; + } + if (!http_request_method_exists(1, 0, method)) { + http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Unknown request method: %s", method); + RETURN_FALSE; + } + + STR_SET(obj->message->http.info.request.method, estrndup(method, method_len)); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HttpMessage::getRequestUrl() + Get the Request URL of the Message. */ +PHP_METHOD(HttpMessage, getRequestUrl) +{ + NO_ARGS; + + if (return_value_used) { + getObject(http_message_object, obj); + HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE); + if (obj->message->http.info.request.url) { + RETURN_STRING(obj->message->http.info.request.url, 1); + } else { + RETURN_EMPTY_STRING(); + } + } +} +/* }}} */ + +/* {{{ proto bool HttpMessage::setRequestUrl(string url) + Set the Request URL of the HTTP Message. */ +PHP_METHOD(HttpMessage, setRequestUrl) +{ + char *URI; + int URIlen; + getObject(http_message_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &URI, &URIlen)) { + RETURN_FALSE; + } + HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE); + if (URIlen < 1) { + http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestUrl to an empty string"); + RETURN_FALSE; + } + + STR_SET(obj->message->http.info.request.url, estrndup(URI, URIlen)); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HttpMessage::getHttpVersion() + Get the HTTP Protocol Version of the Message. */ +PHP_METHOD(HttpMessage, getHttpVersion) +{ + NO_ARGS; + + if (return_value_used) { + char *version; + getObject(http_message_object, obj); + + spprintf(&version, 0, "%1.1F", obj->message->http.version); + RETURN_STRING(version, 0); + } +} +/* }}} */ + +/* {{{ proto bool HttpMessage::setHttpVersion(string version) + Set the HTTP Protocol version of the Message. */ +PHP_METHOD(HttpMessage, setHttpVersion) +{ + zval *zv; + char *version; + getObject(http_message_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &zv)) { + return; + } + + convert_to_double(zv); + spprintf(&version, 0, "%1.1F", Z_DVAL_P(zv)); + if (strcmp(version, "1.0") && strcmp(version, "1.1")) { + efree(version); + http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid HTTP protocol version (1.0 or 1.1): %g", Z_DVAL_P(zv)); + RETURN_FALSE; + } + efree(version); + obj->message->http.version = Z_DVAL_P(zv); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HttpMessage::guessContentType(string magic_file[, int magic_mode = MAGIC_MIME]) + Attempts to guess the content type of supplied payload through libmagic. */ +PHP_METHOD(HttpMessage, guessContentType) +{ +#ifdef HTTP_HAVE_MAGIC + char *magic_file, *ct = NULL; + int magic_file_len; + long magic_mode = MAGIC_MIME; + + RETVAL_FALSE; + SET_EH_THROW_HTTP(); + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &magic_file, &magic_file_len, &magic_mode)) { + getObject(http_message_object, obj); + if ((ct = http_guess_content_type(magic_file, magic_mode, PHPSTR_VAL(&obj->message->body), PHPSTR_LEN(&obj->message->body), SEND_DATA))) { + RETVAL_STRING(ct, 0); + } + } + SET_EH_NORMAL(); +#else + http_error(HE_THROW, HTTP_E_RUNTIME, "Cannot guess Content-Type; libmagic not available"); + RETURN_FALSE; +#endif +} +/* }}} */ + +/* {{{ proto HttpMessage HttpMessage::getParentMessage() + Get parent Message. */ +PHP_METHOD(HttpMessage, getParentMessage) +{ + SET_EH_THROW_HTTP(); + NO_ARGS { + getObject(http_message_object, obj); + + if (obj->message->parent) { + RETVAL_OBJVAL(obj->parent, 1); + } else { + http_error(HE_WARNING, HTTP_E_RUNTIME, "HttpMessage does not have a parent message"); + } + } + SET_EH_NORMAL(); +} +/* }}} */ + +/* {{{ proto bool HttpMessage::send() + Send the Message according to its type as Response or Request. */ +PHP_METHOD(HttpMessage, send) +{ + getObject(http_message_object, obj); + + NO_ARGS; + + RETURN_SUCCESS(http_message_send(obj->message)); +} +/* }}} */ + +/* {{{ proto string HttpMessage::toString([bool include_parent = false]) + Get the string representation of the Message. */ +PHP_METHOD(HttpMessage, toString) +{ + if (return_value_used) { + char *string; + size_t length; + zend_bool include_parent = 0; + getObject(http_message_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &include_parent)) { + RETURN_FALSE; + } + + if (include_parent) { + http_message_serialize(obj->message, &string, &length); + } else { + http_message_tostring(obj->message, &string, &length); + } + RETURN_STRINGL(string, length, 0); + } +} +/* }}} */ + +/* {{{ proto HttpRequest|HttpResponse HttpMessage::toMessageTypeObject(void) + Creates an object regarding to the type of the message. Returns either an HttpRequest or HttpResponse object on success, or NULL on failure. */ +PHP_METHOD(HttpMessage, toMessageTypeObject) +{ + SET_EH_THROW_HTTP(); + + NO_ARGS; + + if (return_value_used) { + getObject(http_message_object, obj); + + switch (obj->message->type) { + case HTTP_MSG_REQUEST: + { +#ifdef HTTP_HAVE_CURL + int method; + char *url; + zval post, body, *array, *headers, *host = http_message_header(obj->message, "Host"); + php_url hurl, *purl = php_url_parse(STR_PTR(obj->message->http.info.request.url)); + + MAKE_STD_ZVAL(array); + array_init(array); + + memset(&hurl, 0, sizeof(php_url)); + if (host) { + hurl.host = Z_STRVAL_P(host); + zval_ptr_dtor(&host); + } + http_build_url(HTTP_URL_REPLACE, purl, &hurl, NULL, &url, NULL); + php_url_free(purl); + add_assoc_string(array, "url", url, 0); + + if ( obj->message->http.info.request.method && + ((method = http_request_method_exists(1, 0, obj->message->http.info.request.method)) || + (method = http_request_method_register(obj->message->http.info.request.method, strlen(obj->message->http.info.request.method))))) { + add_assoc_long(array, "method", method); + } + + if (10 == (int) (obj->message->http.version * 10)) { + add_assoc_long(array, "protocol", CURL_HTTP_VERSION_1_0); + } + + MAKE_STD_ZVAL(headers); + array_init(headers); + array_copy(&obj->message->hdrs, Z_ARRVAL_P(headers)); + add_assoc_zval(array, "headers", headers); + + object_init_ex(return_value, http_request_object_ce); + zend_call_method_with_1_params(&return_value, http_request_object_ce, NULL, "setoptions", NULL, array); + zval_ptr_dtor(&array); + + if (PHPSTR_VAL(obj->message) && PHPSTR_LEN(obj->message)) { + phpstr_fix(PHPSTR(obj->message)); + INIT_PZVAL(&body); + ZVAL_STRINGL(&body, PHPSTR_VAL(obj->message), PHPSTR_LEN(obj->message), 0); + if (method != HTTP_POST) { + zend_call_method_with_1_params(&return_value, http_request_object_ce, NULL, "setbody", NULL, &body); + } else { + INIT_PZVAL(&post); + array_init(&post); + + zval_copy_ctor(&body); + sapi_module.treat_data(PARSE_STRING, Z_STRVAL(body), &post TSRMLS_CC); + zend_call_method_with_1_params(&return_value, http_request_object_ce, NULL, "setpostfields", NULL, &post); + zval_dtor(&post); + } + } +#else + http_error(HE_WARNING, HTTP_E_RUNTIME, "Cannot transform HttpMessage to HttpRequest (missing curl support)"); +#endif + break; + } + + case HTTP_MSG_RESPONSE: + { +#ifndef WONKY + HashPosition pos1, pos2; + HashKey key = initHashKey(0); + zval **header, **h, *body; + + if (obj->message->http.info.response.code) { + http_send_status(obj->message->http.info.response.code); + } + + object_init_ex(return_value, http_response_object_ce); + + FOREACH_HASH_KEYVAL(pos1, &obj->message->hdrs, key, header) { + if (key.type == HASH_KEY_IS_STRING) { + zval *zkey; + + MAKE_STD_ZVAL(zkey); + ZVAL_STRINGL(zkey, key.str, key.len - 1, 1); + + switch (Z_TYPE_PP(header)) { + case IS_ARRAY: + case IS_OBJECT: + FOREACH_HASH_VAL(pos2, HASH_OF(*header), h) { + ZVAL_ADDREF(*h); + zend_call_method_with_2_params(&return_value, http_response_object_ce, NULL, "setheader", NULL, zkey, *h); + zval_ptr_dtor(h); + } + break; + + default: + ZVAL_ADDREF(*header); + zend_call_method_with_2_params(&return_value, http_response_object_ce, NULL, "setheader", NULL, zkey, *header); + zval_ptr_dtor(header); + break; + } + zval_ptr_dtor(&zkey); + } + } + + MAKE_STD_ZVAL(body); + ZVAL_STRINGL(body, PHPSTR_VAL(obj->message), PHPSTR_LEN(obj->message), 1); + zend_call_method_with_1_params(&return_value, http_response_object_ce, NULL, "setdata", NULL, body); + zval_ptr_dtor(&body); +#else + http_error(HE_WARNING, HTTP_E_RUNTIME, "Cannot transform HttpMessage to HttpResponse (need PHP 5.1+)"); +#endif + break; + } + + default: + http_error(HE_WARNING, HTTP_E_MESSAGE_TYPE, "HttpMessage is neither of type HttpMessage::TYPE_REQUEST nor HttpMessage::TYPE_RESPONSE"); + break; + } + } + SET_EH_NORMAL(); +} +/* }}} */ + +/* {{{ proto int HttpMessage::count() + Implements Countable::count(). Returns the number of parent messages + 1. */ +PHP_METHOD(HttpMessage, count) +{ + NO_ARGS { + long i; + getObject(http_message_object, obj); + + http_message_count(i, obj->message); + RETURN_LONG(i); + } +} +/* }}} */ + +/* {{{ proto string HttpMessage::serialize() + Implements Serializable::serialize(). Returns the serialized representation of the HttpMessage. */ +PHP_METHOD(HttpMessage, serialize) +{ + NO_ARGS { + char *string; + size_t length; + getObject(http_message_object, obj); + + http_message_serialize(obj->message, &string, &length); + RETURN_STRINGL(string, length, 0); + } +} +/* }}} */ + +/* {{{ proto void HttpMessage::unserialize(string serialized) + Implements Serializable::unserialize(). Re-constructs the HttpMessage based upon the serialized string. */ +PHP_METHOD(HttpMessage, unserialize) +{ + int length; + char *serialized; + getObject(http_message_object, obj); + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &serialized, &length)) { + http_message *msg; + + http_message_dtor(obj->message); + if ((msg = http_message_parse_ex(obj->message, serialized, (size_t) length))) { + obj->message = msg; + } else { + http_message_init(obj->message); + http_error(HE_ERROR, HTTP_E_RUNTIME, "Could not unserialize HttpMessage"); + } + } +} +/* }}} */ + +/* {{{ proto HttpMessage HttpMessage::detach(void) + Returns a clone of an HttpMessage object detached from any parent messages. */ +PHP_METHOD(HttpMessage, detach) +{ + http_info info; + http_message *msg; + getObject(http_message_object, obj); + + NO_ARGS; + + info.type = obj->message->type; + memcpy(&HTTP_INFO(&info), &HTTP_INFO(obj->message), sizeof(struct http_info)); + + msg = http_message_new(); + http_message_set_info(msg, &info); + + zend_hash_copy(&msg->hdrs, &obj->message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + phpstr_append(&msg->body, PHPSTR_VAL(obj->message), PHPSTR_LEN(obj->message)); + + RETVAL_OBJVAL(http_message_object_new_ex(Z_OBJCE_P(getThis()), msg, NULL), 0); +} +/* }}} */ + +/* {{{ proto void HttpMessage::prepend(HttpMessage message[, bool top = true]) + Prepends message(s) to the HTTP message. Throws HttpInvalidParamException if the message is located within the same message chain. */ +PHP_METHOD(HttpMessage, prepend) +{ + zval *prepend; + zend_bool top = 1; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|b", &prepend, http_message_object_ce, &top)) { + http_message *msg[2]; + getObject(http_message_object, obj); + getObjectEx(http_message_object, prepend_obj, prepend); + + /* 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]) { + http_error(HE_THROW, HTTP_E_INVALID_PARAM, "Cannot prepend a message located within the same message chain"); + return; + } + } + } + + http_message_object_prepend_ex(getThis(), prepend, top); + } +} +/* }}} */ + +/* {{{ proto HttpMessage HttpMessage::reverse() + Reorders the message chain in reverse order. Returns the most parent HttpMessage object. */ +PHP_METHOD(HttpMessage, reverse) +{ + NO_ARGS { + http_message_object_reverse(getThis(), return_value); + } +} +/* }}} */ + +/* {{{ proto void HttpMessage::rewind(void) + Implements Iterator::rewind(). */ +PHP_METHOD(HttpMessage, rewind) +{ + NO_ARGS { + getObject(http_message_object, obj); + + if (obj->iterator) { + zval_ptr_dtor(&obj->iterator); + } + ZVAL_ADDREF(getThis()); + obj->iterator = getThis(); + } +} +/* }}} */ + +/* {{{ proto bool HttpMessage::valid(void) + Implements Iterator::valid(). */ +PHP_METHOD(HttpMessage, valid) +{ + NO_ARGS { + getObject(http_message_object, obj); + + RETURN_BOOL(obj->iterator != NULL); + } +} +/* }}} */ + +/* {{{ proto void HttpMessage::next(void) + Implements Iterator::next(). */ +PHP_METHOD(HttpMessage, next) +{ + NO_ARGS { + getObject(http_message_object, obj); + if (obj->iterator) { + getObjectEx(http_message_object, itr, obj->iterator); + + if (itr && itr->parent.handle) { + zval *old = obj->iterator; + MAKE_STD_ZVAL(obj->iterator); + ZVAL_OBJVAL(obj->iterator, itr->parent, 1); + zval_ptr_dtor(&old); + } else { + zval_ptr_dtor(&obj->iterator); + obj->iterator = NULL; + } + } + } +} +/* }}} */ + +/* {{{ proto int HttpMessage::key(void) + Implements Iterator::key(). */ +PHP_METHOD(HttpMessage, key) +{ + NO_ARGS { + getObject(http_message_object, obj); + + RETURN_LONG(obj->iterator ? obj->iterator->value.obj.handle:0); + } +} +/* }}} */ + +/* {{{ proto HttpMessage HttpMessage::current(void) + Implements Iterator::current(). */ +PHP_METHOD(HttpMessage, current) +{ + NO_ARGS { + getObject(http_message_object, obj); + + if (obj->iterator) { + RETURN_ZVAL(obj->iterator, 1, 0); + } + } +} +/* }}} */ + +#endif /* ZEND_ENGINE_2 */ + +/* + * 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 + */ + --- /dev/null +++ b/ext/http/http_persistent_handle_api.c @@ -0,0 +1,388 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_persistent_handle_api.c 292841 2009-12-31 08:48:57Z mike $ */ + +#include "php_http.h" +#include "php_http_api.h" + +#include "php_http_persistent_handle_api.h" + +#ifndef HTTP_DEBUG_PHANDLES +# define HTTP_DEBUG_PHANDLES 0 +#endif +#if HTTP_DEBUG_PHANDLES +# undef inline +# define inline +#endif + +static HashTable http_persistent_handles_hash; +#ifdef ZTS +# define LOCK() tsrm_mutex_lock(http_persistent_handles_lock) +# define UNLOCK() tsrm_mutex_unlock(http_persistent_handles_lock) +static MUTEX_T http_persistent_handles_lock; +#else +# define LOCK() +# define UNLOCK() +#endif + +typedef struct _http_persistent_handle_list_t { + HashTable free; + ulong used; +} http_persistent_handle_list; + +typedef struct _http_persistent_handle_provider_t { + http_persistent_handle_list list; /* "ident" => array(handles) entries */ + http_persistent_handle_ctor ctor; + http_persistent_handle_dtor dtor; + http_persistent_handle_copy copy; +} http_persistent_handle_provider; + +static inline http_persistent_handle_list *http_persistent_handle_list_init(http_persistent_handle_list *list) +{ + int free_list; + + if ((free_list = !list)) { + list = pemalloc(sizeof(http_persistent_handle_list), 1); + } + + list->used = 0; + + if (SUCCESS != zend_hash_init(&list->free, 0, NULL, NULL, 1)) { + if (free_list) { + pefree(list, 1); + } + list = NULL; + } + + return list; +} + +static inline void http_persistent_handle_list_dtor(http_persistent_handle_list *list, http_persistent_handle_dtor dtor) +{ + HashPosition pos; + void **handle; + +#if HTTP_DEBUG_PHANDLES + fprintf(stderr, "LSTDTOR: %p\n", list); +#endif + FOREACH_HASH_VAL(pos, &list->free, handle) { +#if HTTP_DEBUG_PHANDLES + fprintf(stderr, "DESTROY: %p\n", *handle); +#endif + + dtor(*handle); + } + zend_hash_destroy(&list->free); +} + +static inline void http_persistent_handle_list_free(http_persistent_handle_list **list, http_persistent_handle_dtor dtor) +{ + http_persistent_handle_list_dtor(*list, dtor); +#if HTTP_DEBUG_PHANDLES + fprintf(stderr, "LSTFREE: %p\n", *list); +#endif + pefree(*list, 1); + *list = NULL; +} + +static inline http_persistent_handle_list *http_persistent_handle_list_find(http_persistent_handle_provider *provider TSRMLS_DC) +{ + http_persistent_handle_list **list, *new_list; + + if (SUCCESS == zend_hash_quick_find(&provider->list.free, HTTP_G->persistent.handles.ident.s, HTTP_G->persistent.handles.ident.l, HTTP_G->persistent.handles.ident.h, (void *) &list)) { +#if HTTP_DEBUG_PHANDLES + fprintf(stderr, "LSTFIND: %p\n", *list); +#endif + return *list; + } + + if ((new_list = http_persistent_handle_list_init(NULL))) { + if (SUCCESS == zend_hash_quick_add(&provider->list.free, HTTP_G->persistent.handles.ident.s, HTTP_G->persistent.handles.ident.l, HTTP_G->persistent.handles.ident.h, (void *) &new_list, sizeof(http_persistent_handle_list *), (void *) &list)) { +#if HTTP_DEBUG_PHANDLES + fprintf(stderr, "LSTFIND: %p (new)\n", *list); +#endif + return *list; + } + http_persistent_handle_list_free(&new_list, provider->dtor); + } + + return NULL; +} + +static inline STATUS http_persistent_handle_do_acquire(http_persistent_handle_provider *provider, void **handle TSRMLS_DC) +{ + ulong index; + void **handle_ptr; + http_persistent_handle_list *list; + + if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) { + zend_hash_internal_pointer_end(&list->free); + if (HASH_KEY_NON_EXISTANT != zend_hash_get_current_key(&list->free, NULL, &index, 0) && SUCCESS == zend_hash_get_current_data(&list->free, (void *) &handle_ptr)) { + *handle = *handle_ptr; + zend_hash_index_del(&list->free, index); + } else { + *handle = provider->ctor(); + } + + if (*handle) { + ++provider->list.used; + ++list->used; + return SUCCESS; + } + } else { + *handle = NULL; + } + + return FAILURE; +} + +static inline STATUS http_persistent_handle_do_release(http_persistent_handle_provider *provider, void **handle TSRMLS_DC) +{ + http_persistent_handle_list *list; + + if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) { + if (provider->list.used >= HTTP_G->persistent.handles.limit) { + provider->dtor(*handle); + } else { + if (SUCCESS != zend_hash_next_index_insert(&list->free, (void *) handle, sizeof(void *), NULL)) { + return FAILURE; + } + } + + *handle = NULL; + --provider->list.used; + --list->used; + return SUCCESS; + } + + return FAILURE; +} + +static inline STATUS http_persistent_handle_do_accrete(http_persistent_handle_provider *provider, void *old_handle, void **new_handle TSRMLS_DC) +{ + http_persistent_handle_list *list; + + if (provider->copy && (*new_handle = provider->copy(old_handle))) { + if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) { + ++list->used; + } + ++provider->list.used; + return SUCCESS; + } + return FAILURE; +} + +static void http_persistent_handles_hash_dtor(void *p) +{ + http_persistent_handle_provider *provider = (http_persistent_handle_provider *) p; + http_persistent_handle_list **list, *list_tmp; + HashPosition pos; + + FOREACH_HASH_VAL(pos, &provider->list.free, list) { + /* fix shutdown crash in PHP4 */ + list_tmp = *list; + http_persistent_handle_list_free(&list_tmp, provider->dtor); + } + + zend_hash_destroy(&provider->list.free); +} + +PHP_MINIT_FUNCTION(http_persistent_handle) +{ + zend_hash_init(&http_persistent_handles_hash, 0, NULL, http_persistent_handles_hash_dtor, 1); +#ifdef ZTS + http_persistent_handles_lock = tsrm_mutex_alloc(); +#endif + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(http_persistent_handle) +{ + zend_hash_destroy(&http_persistent_handles_hash); +#ifdef ZTS + tsrm_mutex_free(http_persistent_handles_lock); +#endif + return SUCCESS; +} + +PHP_HTTP_API STATUS _http_persistent_handle_provide_ex(const char *name_str, size_t name_len, http_persistent_handle_ctor ctor, http_persistent_handle_dtor dtor, http_persistent_handle_copy copy) +{ + STATUS status = FAILURE; + http_persistent_handle_provider provider; + + LOCK(); + if (http_persistent_handle_list_init(&provider.list)) { + provider.ctor = ctor; + provider.dtor = dtor; + provider.copy = copy; + +#if HTTP_DEBUG_PHANDLES + fprintf(stderr, "PROVIDE: %s\n", name_str); +#endif + + if (SUCCESS == zend_hash_add(&http_persistent_handles_hash, HTTP_ZAPI_CONST_CAST(char *) name_str, name_len+1, (void *) &provider, sizeof(http_persistent_handle_provider), NULL)) { + status = SUCCESS; + } + } + UNLOCK(); + + return status; +} + +PHP_HTTP_API STATUS _http_persistent_handle_acquire_ex(const char *name_str, size_t name_len, void **handle TSRMLS_DC) +{ + STATUS status = FAILURE; + http_persistent_handle_provider *provider; + + *handle = NULL; + LOCK(); + if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, HTTP_ZAPI_CONST_CAST(char *) name_str, name_len+1, (void *) &provider)) { + status = http_persistent_handle_do_acquire(provider, handle TSRMLS_CC); + } + UNLOCK(); + +#if HTTP_DEBUG_PHANDLES + fprintf(stderr, "ACQUIRE: %p (%s)\n", *handle, name_str); +#endif + + return status; +} + +PHP_HTTP_API STATUS _http_persistent_handle_release_ex(const char *name_str, size_t name_len, void **handle TSRMLS_DC) +{ + STATUS status = FAILURE; + http_persistent_handle_provider *provider; +#if HTTP_DEBUG_PHANDLES + void *handle_tmp = *handle; +#endif + + LOCK(); + if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, HTTP_ZAPI_CONST_CAST(char *) name_str, name_len+1, (void *) &provider)) { + status = http_persistent_handle_do_release(provider, handle TSRMLS_CC); + } + UNLOCK(); + +#if HTTP_DEBUG_PHANDLES + fprintf(stderr, "RELEASE: %p (%s)\n", handle_tmp, name_str); +#endif + + return status; +} + +PHP_HTTP_API STATUS _http_persistent_handle_accrete_ex(const char *name_str, size_t name_len, void *old_handle, void **new_handle TSRMLS_DC) +{ + STATUS status = FAILURE; + http_persistent_handle_provider *provider; + + *new_handle = NULL; + LOCK(); + if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, HTTP_ZAPI_CONST_CAST(char *) name_str, name_len+1, (void *) &provider)) { + status = http_persistent_handle_do_accrete(provider, old_handle, new_handle TSRMLS_CC); + } + UNLOCK(); + +#if HTTP_DEBUG_PHANDLES + fprintf(stderr, "ACCRETE: %p > %p (%s)\n", old_handle, *new_handle, name_str); +#endif + + return status; +} + +PHP_HTTP_API void _http_persistent_handle_cleanup_ex(const char *name_str, size_t name_len, int current_ident_only TSRMLS_DC) +{ + http_persistent_handle_provider *provider; + http_persistent_handle_list *list, **listp; + HashPosition pos1, pos2; + + LOCK(); + if (name_str && name_len) { + if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, HTTP_ZAPI_CONST_CAST(char *) name_str, name_len+1, (void *) &provider)) { + if (current_ident_only) { + if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) { + http_persistent_handle_list_dtor(list, provider->dtor); + http_persistent_handle_list_init(list); + } + } else { + FOREACH_HASH_VAL(pos1, &provider->list.free, listp) { + http_persistent_handle_list_dtor(*listp, provider->dtor); + http_persistent_handle_list_init(*listp); + } + } + } + } else { + FOREACH_HASH_VAL(pos1, &http_persistent_handles_hash, provider) { + if (current_ident_only) { + if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) { + http_persistent_handle_list_dtor(list, provider->dtor); + http_persistent_handle_list_init(list); + } + } else { + FOREACH_HASH_VAL(pos2, &provider->list.free, listp) { + http_persistent_handle_list_dtor(*listp, provider->dtor); + http_persistent_handle_list_init(*listp); + } + } + } + } + UNLOCK(); +} + +PHP_HTTP_API HashTable *_http_persistent_handle_statall_ex(HashTable *ht TSRMLS_DC) +{ + zval *zentry[2]; + HashPosition pos1, pos2; + HashKey key1 = initHashKey(0), key2 = initHashKey(0); + http_persistent_handle_provider *provider; + http_persistent_handle_list **list; + + LOCK(); + if (zend_hash_num_elements(&http_persistent_handles_hash)) { + if (!ht) { + ALLOC_HASHTABLE(ht); + zend_hash_init(ht, 0, NULL, ZVAL_PTR_DTOR, 0); + } + + FOREACH_HASH_KEYVAL(pos1, &http_persistent_handles_hash, key1, provider) { + MAKE_STD_ZVAL(zentry[0]); + array_init(zentry[0]); + + FOREACH_HASH_KEYVAL(pos2, &provider->list.free, key2, list) { + MAKE_STD_ZVAL(zentry[1]); + array_init(zentry[1]); + add_assoc_long_ex(zentry[1], ZEND_STRS("used"), (*list)->used); + add_assoc_long_ex(zentry[1], ZEND_STRS("free"), zend_hash_num_elements(&(*list)->free)); + + /* use zend_hash_* not add_assoc_* (which is zend_symtable_*) as we want a string even for numbers */ + zend_hash_add(Z_ARRVAL_P(zentry[0]), key2.str, key2.len, &zentry[1], sizeof(zval *), NULL); + } + + zend_hash_add(ht, key1.str, key1.len, &zentry[0], sizeof(zval *), NULL); + } + } else if (ht) { + ht = NULL; + } + UNLOCK(); + + return ht; +} + + +/* + * 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 + */ + --- /dev/null +++ b/ext/http/http_querystring_api.c @@ -0,0 +1,220 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_querystring_api.c 292841 2009-12-31 08:48:57Z mike $ */ + +#define HTTP_WANT_SAPI +#include "php_http.h" + +#include "php_variables.h" +#ifdef HTTP_HAVE_ICONV +# undef PHP_ATOM_INC +# include "ext/iconv/php_iconv.h" +# include "ext/standard/url.h" +#endif + +#include "php_http_api.h" +#include "php_http_url_api.h" +#include "php_http_querystring_api.h" + +#ifdef ZEND_ENGINE_2 +#define THIS_CE http_querystring_object_ce +extern zend_class_entry *http_querystring_object_ce; +#endif + + +#define http_querystring_modify_array_ex(q, t, k, kl, i, pe) _http_querystring_modify_array_ex((q), (t), (k), (kl), (i), (pe) TSRMLS_CC) +static inline int _http_querystring_modify_array_ex(zval *qarray, int key_type, char *key, int keylen, ulong idx, zval *params_entry TSRMLS_DC); +#define http_querystring_modify_array(q, p) _http_querystring_modify_array((q), (p) TSRMLS_CC) +static inline int _http_querystring_modify_array(zval *qarray, zval *params TSRMLS_DC); + + +#ifdef HTTP_HAVE_ICONV +PHP_HTTP_API int _http_querystring_xlate(zval *array, zval *param, const char *ie, const char *oe TSRMLS_DC) +{ + HashPosition pos; + zval **entry = NULL; + char *xlate_str = NULL, *xkey; + size_t xlate_len = 0, xlen; + HashKey key = initHashKey(0); + + FOREACH_KEYVAL(pos, param, 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)) { + http_error_ex(HE_WARNING, HTTP_E_QUERYSTRING, "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); + } + http_error_ex(HE_WARNING, HTTP_E_QUERYSTRING, "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(array, xkey, xlen+1, xlate_str, xlate_len, 0); + } else { + add_index_stringl(array, 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(array, xkey, xlen+1, subarray); + } else { + add_index_zval(array, key.num, subarray); + } + if (SUCCESS != http_querystring_xlate(subarray, *entry, ie, oe)) { + 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 */ + +PHP_HTTP_API void _http_querystring_update(zval *qarray, zval *qstring TSRMLS_DC) +{ + char *s = NULL; + size_t l = 0; + + if (Z_TYPE_P(qarray) != IS_ARRAY) { + convert_to_array(qarray); + } + if (SUCCESS == http_urlencode_hash_ex(Z_ARRVAL_P(qarray), 0, NULL, 0, &s, &l)) { + zval_dtor(qstring); + ZVAL_STRINGL(qstring, s, l, 0); + } else { + http_error(HE_WARNING, HTTP_E_QUERYSTRING, "Failed to update query string"); + } +} + +PHP_HTTP_API int _http_querystring_modify(zval *qarray, zval *params TSRMLS_DC) +{ + if (Z_TYPE_P(params) == IS_ARRAY) { + return http_querystring_modify_array(qarray, params); + } else if (Z_TYPE_P(params) == IS_OBJECT) { +#ifdef ZEND_ENGINE_2 + if (instanceof_function(Z_OBJCE_P(params), http_querystring_object_ce TSRMLS_CC)) { + return http_querystring_modify_array(qarray, zend_read_property(THIS_CE, params, ZEND_STRS("queryArray")-1, 0 TSRMLS_CC)); + } else { +#endif + return http_querystring_modify_array(qarray, params); +#ifdef ZEND_ENGINE_2 + } +#endif + } else { + int rv; + zval array; + zval *qstring = http_zsep(IS_STRING, params); + + INIT_PZVAL(&array); + array_init(&array); + + sapi_module.treat_data(PARSE_STRING, estrdup(Z_STRVAL_P(qstring)), &array TSRMLS_CC); + zval_ptr_dtor(&qstring); + + rv = http_querystring_modify_array(qarray, &array); + zval_dtor(&array); + return rv; + } +} + +static inline int _http_querystring_modify_array(zval *qarray, zval *params TSRMLS_DC) +{ + int rv = 0; + HashKey key = initHashKey(0); + HashPosition pos; + zval **params_entry = NULL; + + FOREACH_HASH_KEYVAL(pos, HASH_OF(params), key, params_entry) { + /* only public properties */ + if ((key.type != HASH_KEY_IS_STRING || *key.str) && http_querystring_modify_array_ex(qarray, key.type, key.str, key.len, key.num, *params_entry)) { + rv = 1; + } + } + + return rv; +} + +static inline int _http_querystring_modify_array_ex(zval *qarray, int key_type, char *key, int keylen, ulong idx, zval *params_entry TSRMLS_DC) +{ + zval **qarray_entry; + + /* ensure array type */ + if (Z_TYPE_P(qarray) != IS_ARRAY) { + convert_to_array(qarray); + } + + /* delete */ + if (Z_TYPE_P(params_entry) == IS_NULL) { + if (key_type == HASH_KEY_IS_STRING) { + return (SUCCESS == zend_hash_del(Z_ARRVAL_P(qarray), key, keylen)); + } else { + return (SUCCESS == zend_hash_index_del(Z_ARRVAL_P(qarray), idx)); + } + } + + /* update */ + if ( ((key_type == HASH_KEY_IS_STRING) && (SUCCESS == zend_hash_find(Z_ARRVAL_P(qarray), key, keylen, (void *) &qarray_entry))) || + ((key_type == HASH_KEY_IS_LONG) && (SUCCESS == zend_hash_index_find(Z_ARRVAL_P(qarray), idx, (void *) &qarray_entry)))) { + zval equal; + + /* recursive */ + if (Z_TYPE_P(params_entry) == IS_ARRAY || Z_TYPE_P(params_entry) == IS_OBJECT) { + return http_querystring_modify(*qarray_entry, params_entry); + } + /* equal */ + if ((SUCCESS == is_equal_function(&equal, *qarray_entry, params_entry TSRMLS_CC)) && Z_BVAL(equal)) { + return 0; + } + } + + /* add */ + if (Z_TYPE_P(params_entry) == IS_OBJECT) { + zval *new_array; + + MAKE_STD_ZVAL(new_array); + array_init(new_array); + http_querystring_modify_array(new_array, params_entry); + params_entry = new_array; + } else { + ZVAL_ADDREF(params_entry); + } + if (key_type == HASH_KEY_IS_STRING) { + add_assoc_zval_ex(qarray, key, keylen, params_entry); + } else { + add_index_zval(qarray, idx, params_entry); + } + return 1; +} + +/* + * 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 + */ --- /dev/null +++ b/ext/http/http_querystring_object.c @@ -0,0 +1,628 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_querystring_object.c 300299 2010-06-09 06:23:16Z mike $ */ + +#define HTTP_WANT_SAPI +#include "php_http.h" + +#ifdef ZEND_ENGINE_2 + +#include "php_variables.h" +#include "zend_interfaces.h" + +#include "php_http_api.h" +#include "php_http_querystring_api.h" +#include "php_http_querystring_object.h" +#include "php_http_exception_object.h" + +#define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpQueryString, method, 0, req_args) +#define HTTP_EMPTY_ARGS(method) HTTP_EMPTY_ARGS_EX(HttpQueryString, method, 0) +#define HTTP_QUERYSTRING_ME(method, visibility) PHP_ME(HttpQueryString, method, HTTP_ARGS(HttpQueryString, method), visibility) +#define HTTP_QUERYSTRING_GME(method, visibility) PHP_ME(HttpQueryString, method, HTTP_ARGS(HttpQueryString, __getter), visibility) + +HTTP_BEGIN_ARGS(__construct, 0) + HTTP_ARG_VAL(global, 0) + HTTP_ARG_VAL(params, 0) +HTTP_END_ARGS; + +#ifndef WONKY +HTTP_BEGIN_ARGS(singleton, 0) + HTTP_ARG_VAL(global, 0) +HTTP_END_ARGS; +#endif + +HTTP_BEGIN_ARGS(factory, 0) + HTTP_ARG_VAL(global, 0) + HTTP_ARG_VAL(params, 0) + HTTP_ARG_VAL(class_name, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(toArray); +HTTP_EMPTY_ARGS(toString); + +HTTP_BEGIN_ARGS(get, 0) + HTTP_ARG_VAL(name, 0) + HTTP_ARG_VAL(type, 0) + HTTP_ARG_VAL(defval, 0) + HTTP_ARG_VAL(delete, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(set, 1) + HTTP_ARG_VAL(params, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(mod, 0) + HTTP_ARG_VAL(params, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(__getter, 1) + HTTP_ARG_VAL(name, 0) + HTTP_ARG_VAL(defval, 0) + HTTP_ARG_VAL(delete, 0) +HTTP_END_ARGS; + +#ifdef HTTP_HAVE_ICONV +HTTP_BEGIN_ARGS(xlate, 2) + HTTP_ARG_VAL(from_encoding, 0) + HTTP_ARG_VAL(to_encoding, 0) +HTTP_END_ARGS; +#endif + +HTTP_EMPTY_ARGS(serialize); +HTTP_BEGIN_ARGS(unserialize, 1) + HTTP_ARG_VAL(serialized, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(offsetGet, 1) + HTTP_ARG_VAL(offset, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(offsetSet, 2) + HTTP_ARG_VAL(offset, 0) + HTTP_ARG_VAL(value, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(offsetExists, 1) + HTTP_ARG_VAL(offset, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(offsetUnset, 1) + HTTP_ARG_VAL(offset, 0) +HTTP_END_ARGS; + + +#define THIS_CE http_querystring_object_ce +zend_class_entry *http_querystring_object_ce; +zend_function_entry http_querystring_object_fe[] = { + HTTP_QUERYSTRING_ME(__construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR|ZEND_ACC_FINAL) + + HTTP_QUERYSTRING_ME(toArray, ZEND_ACC_PUBLIC) + HTTP_QUERYSTRING_ME(toString, ZEND_ACC_PUBLIC) + ZEND_MALIAS(HttpQueryString, __toString, toString, HTTP_ARGS(HttpQueryString, toString), ZEND_ACC_PUBLIC) + + HTTP_QUERYSTRING_ME(get, ZEND_ACC_PUBLIC) + HTTP_QUERYSTRING_ME(set, ZEND_ACC_PUBLIC) + HTTP_QUERYSTRING_ME(mod, ZEND_ACC_PUBLIC) + + HTTP_QUERYSTRING_GME(getBool, ZEND_ACC_PUBLIC) + HTTP_QUERYSTRING_GME(getInt, ZEND_ACC_PUBLIC) + HTTP_QUERYSTRING_GME(getFloat, ZEND_ACC_PUBLIC) + HTTP_QUERYSTRING_GME(getString, ZEND_ACC_PUBLIC) + HTTP_QUERYSTRING_GME(getArray, ZEND_ACC_PUBLIC) + HTTP_QUERYSTRING_GME(getObject, ZEND_ACC_PUBLIC) + + HTTP_QUERYSTRING_ME(factory, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) +#ifndef WONKY + HTTP_QUERYSTRING_ME(singleton, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) +#endif +#ifdef HTTP_HAVE_ICONV + HTTP_QUERYSTRING_ME(xlate, ZEND_ACC_PUBLIC) +#endif + + /* Implements Serializable */ + HTTP_QUERYSTRING_ME(serialize, ZEND_ACC_PUBLIC) + HTTP_QUERYSTRING_ME(unserialize, ZEND_ACC_PUBLIC) + + /* Implements ArrayAccess */ + HTTP_QUERYSTRING_ME(offsetGet, ZEND_ACC_PUBLIC) + HTTP_QUERYSTRING_ME(offsetSet, ZEND_ACC_PUBLIC) + HTTP_QUERYSTRING_ME(offsetExists, ZEND_ACC_PUBLIC) + HTTP_QUERYSTRING_ME(offsetUnset, ZEND_ACC_PUBLIC) + + EMPTY_FUNCTION_ENTRY +}; +static zend_object_handlers http_querystring_object_handlers; + +PHP_MINIT_FUNCTION(http_querystring_object) +{ + HTTP_REGISTER_CLASS_EX(HttpQueryString, http_querystring_object, NULL, 0); + +#ifndef WONKY + zend_class_implements(http_querystring_object_ce TSRMLS_CC, 2, zend_ce_serializable, zend_ce_arrayaccess); +#endif + + zend_declare_property_null(THIS_CE, ZEND_STRS("instance")-1, (ZEND_ACC_STATIC|ZEND_ACC_PRIVATE) TSRMLS_CC); + zend_declare_property_null(THIS_CE, ZEND_STRS("queryArray")-1, ZEND_ACC_PRIVATE TSRMLS_CC); + zend_declare_property_string(THIS_CE, ZEND_STRS("queryString")-1, "", ZEND_ACC_PRIVATE TSRMLS_CC); + +#ifndef WONKY + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("TYPE_BOOL")-1, HTTP_QUERYSTRING_TYPE_BOOL TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("TYPE_INT")-1, HTTP_QUERYSTRING_TYPE_INT TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("TYPE_FLOAT")-1, HTTP_QUERYSTRING_TYPE_FLOAT TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("TYPE_STRING")-1, HTTP_QUERYSTRING_TYPE_STRING TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("TYPE_ARRAY")-1, HTTP_QUERYSTRING_TYPE_ARRAY TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("TYPE_OBJECT")-1, HTTP_QUERYSTRING_TYPE_OBJECT TSRMLS_CC); +#endif + + HTTP_LONG_CONSTANT("HTTP_QUERYSTRING_TYPE_BOOL", HTTP_QUERYSTRING_TYPE_BOOL); + HTTP_LONG_CONSTANT("HTTP_QUERYSTRING_TYPE_INT", HTTP_QUERYSTRING_TYPE_INT); + HTTP_LONG_CONSTANT("HTTP_QUERYSTRING_TYPE_FLOAT", HTTP_QUERYSTRING_TYPE_FLOAT); + HTTP_LONG_CONSTANT("HTTP_QUERYSTRING_TYPE_STRING", HTTP_QUERYSTRING_TYPE_STRING); + HTTP_LONG_CONSTANT("HTTP_QUERYSTRING_TYPE_ARRAY", HTTP_QUERYSTRING_TYPE_ARRAY); + HTTP_LONG_CONSTANT("HTTP_QUERYSTRING_TYPE_OBJECT", HTTP_QUERYSTRING_TYPE_OBJECT); + + return SUCCESS; +} + +zend_object_value _http_querystring_object_new(zend_class_entry *ce TSRMLS_DC) +{ + return http_querystring_object_new_ex(ce, NULL, NULL); +} + +zend_object_value _http_querystring_object_new_ex(zend_class_entry *ce, void *nothing, http_querystring_object **ptr TSRMLS_DC) +{ + zend_object_value ov; + http_querystring_object *o; + + o = ecalloc(1, sizeof(http_querystring_object)); + o->zo.ce = ce; + + if (ptr) { + *ptr = o; + } + + ALLOC_HASHTABLE(OBJ_PROP(o)); + zend_hash_init(OBJ_PROP(o), zend_hash_num_elements(&ce->default_properties), NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(OBJ_PROP(o), &ce->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + + ov.handle = putObject(http_querystring_object, o); + ov.handlers = &http_querystring_object_handlers; + + return ov; +} + +void _http_querystring_object_free(zend_object *object TSRMLS_DC) +{ + http_querystring_object *o = (http_querystring_object *) object; + + freeObject(o); +} + +/* {{{ querystring helpers */ +#define http_querystring_instantiate(t, g, p, u) _http_querystring_instantiate((t), (g), (p), (u) TSRMLS_CC) +static inline zval *_http_querystring_instantiate(zval *this_ptr, zend_bool global, zval *params, zend_bool defer_update TSRMLS_DC) +{ + zval *qarray = NULL, *qstring = NULL, **_SERVER = NULL, **_GET = NULL, **QUERY_STRING = NULL;; + + if (!this_ptr) { + MAKE_STD_ZVAL(this_ptr); + Z_TYPE_P(this_ptr) = IS_OBJECT; + this_ptr->value.obj = http_querystring_object_new(http_querystring_object_ce); + } + if (global) { +#ifdef ZEND_ENGINE_2 + zend_is_auto_global("_SERVER", lenof("_SERVER") TSRMLS_CC); +#endif + if ( (SUCCESS == zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void *) &_SERVER)) && + (Z_TYPE_PP(_SERVER) == IS_ARRAY) && + (SUCCESS == zend_hash_find(Z_ARRVAL_PP(_SERVER), "QUERY_STRING", sizeof("QUERY_STRING"), (void *) &QUERY_STRING))) { + + qstring = *QUERY_STRING; +#ifdef ZEND_ENGINE_2 + zend_is_auto_global("_GET", lenof("_GET") TSRMLS_CC); +#endif + if ((SUCCESS == zend_hash_find(&EG(symbol_table), "_GET", sizeof("_GET"), (void *) &_GET)) && (Z_TYPE_PP(_GET) == IS_ARRAY)) { + qarray = *_GET; + } else { + http_error(HE_WARNING, HTTP_E_QUERYSTRING, "Could not acquire reference to superglobal GET array"); + } + } else { + http_error(HE_WARNING, HTTP_E_QUERYSTRING, "Could not acquire reference to QUERY_STRING"); + } + + if (qarray && qstring) { + if (Z_TYPE_P(qstring) != IS_STRING) { + convert_to_string(qstring); + } + + zend_update_property(THIS_CE, getThis(), ZEND_STRS("queryArray")-1, qarray TSRMLS_CC); + zend_update_property(THIS_CE, getThis(), ZEND_STRS("queryString")-1, qstring TSRMLS_CC); +#ifdef Z_SET_ISREF + Z_SET_ISREF_P(zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryArray")-1, 0 TSRMLS_CC)); + Z_SET_ISREF_P(zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryString")-1, 0 TSRMLS_CC)); +#else + zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryArray")-1, 0 TSRMLS_CC)->is_ref = 1; + zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryString")-1, 0 TSRMLS_CC)->is_ref = 1; +#endif + + if (params) { + http_querystring_modify(zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryArray")-1, 0 TSRMLS_CC), params); + } + if (!defer_update) { + http_querystring_update(zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryArray")-1, 0 TSRMLS_CC), zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryString")-1, 0 TSRMLS_CC)); + } + } + } else { + MAKE_STD_ZVAL(qarray); + array_init(qarray); + + zend_update_property(THIS_CE, getThis(), ZEND_STRS("queryArray")-1, qarray TSRMLS_CC); + zend_update_property_stringl(THIS_CE, getThis(), ZEND_STRS("queryString")-1, "", 0 TSRMLS_CC); + + if (params && http_querystring_modify(qarray, params) && !defer_update) { + http_querystring_update(qarray, zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryString")-1, 0 TSRMLS_CC)); + } + + zval_ptr_dtor(&qarray); + } + + return this_ptr; +} + +#define http_querystring_get(o, t, n, l, def, del, r) _http_querystring_get((o), (t), (n), (l), (def), (del), (r) TSRMLS_CC) +static inline void _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(THIS_CE, getThis(), ZEND_STRS("queryArray")-1, 0 TSRMLS_CC); + + if ((Z_TYPE_P(qarray) == IS_ARRAY) && (SUCCESS == zend_hash_find(Z_ARRVAL_P(qarray), name, name_len + 1, (void *) &arrval))) { + if (type) { + zval *value = http_zsep(type, *arrval); + RETVAL_ZVAL(value, 1, 1); + } else { + RETVAL_ZVAL(*arrval, 1, 0); + } + + if (del && (SUCCESS == zend_hash_del(Z_ARRVAL_P(qarray), name, name_len + 1))) { + http_querystring_update(qarray, zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryString")-1, 0 TSRMLS_CC)); + } + } else if(defval) { + RETURN_ZVAL(defval, 1, 0); + } +} +/* }}} */ + +/* {{{ proto final void HttpQueryString::__construct([bool global = true[, mixed add]) + Creates a new HttpQueryString object instance. Operates on and modifies $_GET and $_SERVER['QUERY_STRING'] if global is TRUE. */ +PHP_METHOD(HttpQueryString, __construct) +{ + zend_bool global = 1; + zval *params = NULL; + + SET_EH_THROW_HTTP(); + if (!sapi_module.treat_data) { + http_error(HE_ERROR, HTTP_E_QUERYSTRING, "The SAPI does not have a treat_data function registered"); + } else if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|bz", &global, ¶ms)) { + http_querystring_instantiate(getThis(), global, params, 0); + } + SET_EH_NORMAL(); +} +/* }}} */ + +/* {{{ proto HttpQueryString HttpQueryString::factory([bool global = TRUE[, mixed params[, string class_name = "HttpQueryString"]) + Creates a new HttpQueryString object instance. */ +PHP_METHOD(HttpQueryString, factory) +{ + zend_bool global = 1; + zval *params = NULL; + char *cn = NULL; + int cl = 0; + zend_object_value ov; + + SET_EH_THROW_HTTP(); + if (!sapi_module.treat_data) { + http_error(HE_ERROR, HTTP_E_QUERYSTRING, "The SAPI does not have a treat_data function registered"); + } else if ( SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|bzs", &global, ¶ms, &cn, &cl) && + SUCCESS == http_object_new(&ov, cn, cl, _http_querystring_object_new_ex, http_querystring_object_ce, NULL, NULL)) { + RETVAL_OBJVAL(ov, 0); + http_querystring_instantiate(return_value, global, params, 0); + } + SET_EH_NORMAL(); +} +/* }}} */ + +/* {{{ proto string HttpQueryString::toString() + Returns the string representation. */ +PHP_METHOD(HttpQueryString, toString) +{ + NO_ARGS; + RETURN_PROP(queryString); +} +/* }}} */ + +/* {{{ proto array HttpQueryString::toArray() + Returns the array representation. */ +PHP_METHOD(HttpQueryString, toArray) +{ + NO_ARGS; + RETURN_PROP(queryArray); +} +/* }}} */ + +/* {{{ proto mixed HttpQueryString::get([string key[, mixed type = 0[, mixed defval = NULL[, bool delete = false]]]]) + Get (part of) the query string. The type parameter is either one of the HttpQueryString::TYPE_* constants or a type abbreviation like "b" for bool, "i" for int, "f" for float, "s" for string, "a" for array and "o" for a stdClass object. */ +PHP_METHOD(HttpQueryString, get) +{ + char *name = 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, &name_len, &ztype, &defval, &del)) { + if (name && 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 = HTTP_QUERYSTRING_TYPE_BOOL; break; + case 'I': + case 'i': type = HTTP_QUERYSTRING_TYPE_INT; break; + case 'F': + case 'f': type = HTTP_QUERYSTRING_TYPE_FLOAT; break; + case 'S': + case 's': type = HTTP_QUERYSTRING_TYPE_STRING; break; + case 'A': + case 'a': type = HTTP_QUERYSTRING_TYPE_ARRAY; break; + case 'O': + case 'o': type = HTTP_QUERYSTRING_TYPE_OBJECT; break; + } + } + } + http_querystring_get(getThis(), type, name, name_len, defval, del, return_value); + } else { + RETURN_PROP(queryString); + } + } +} +/* }}} */ + +/* {{{ proto string HttpQueryString::set(mixed params) + Set query string entry/entries. NULL values will unset the variable. */ +PHP_METHOD(HttpQueryString, set) +{ + zval *params; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", ¶ms)) { + zval *qarray = zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryArray")-1, 0 TSRMLS_CC); + if (http_querystring_modify(qarray, params)) { + http_querystring_update(qarray, zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryString")-1, 0 TSRMLS_CC)); + } + } + + if (return_value_used) { + RETURN_PROP(queryString); + } +} +/* }}} */ + +/* {{{ proto HttpQueryString HttpQueryString::mod(mixed params) + Copies the query string object and sets provided params at the clone. */ +PHP_METHOD(HttpQueryString, mod) +{ + zval *zobj, *qarr, *qstr, *params; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", ¶ms)) { + zobj = http_querystring_instantiate(NULL, 0, zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryArray")-1, 0 TSRMLS_CC), 1); + qarr = zend_read_property(THIS_CE, zobj, ZEND_STRS("queryArray")-1, 0 TSRMLS_CC); + qstr = zend_read_property(THIS_CE, zobj, ZEND_STRS("queryString")-1, 0 TSRMLS_CC); + + http_querystring_modify(qarr, params); + http_querystring_update(qarr, qstr); + + RETURN_ZVAL(zobj, 1, 1); + } +} +/* }}} */ + +#ifndef WONKY +/* {{{ proto static HttpQueryString HttpQueryString::singleton([bool global = true]) + Get a single instance (differentiates between the global setting). */ +PHP_METHOD(HttpQueryString, singleton) +{ + zend_bool global = 1; + zval *instance = *zend_std_get_static_property(THIS_CE, ZEND_STRS("instance")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC); + + SET_EH_THROW_HTTP(); + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &global)) { + zval **zobj_ptr = NULL, *zobj = NULL; + + if (Z_TYPE_P(instance) == IS_ARRAY) { + if (SUCCESS == zend_hash_index_find(Z_ARRVAL_P(instance), global, (void *) &zobj_ptr)) { + RETVAL_ZVAL(*zobj_ptr, 1, 0); + } else { + zobj = http_querystring_instantiate(NULL, global, NULL, (zend_bool) !global); + add_index_zval(instance, global, zobj); + RETVAL_OBJECT(zobj, 1); + } + } else { + MAKE_STD_ZVAL(instance); + array_init(instance); + + zobj = http_querystring_instantiate(NULL, global, NULL, (zend_bool) !global); + add_index_zval(instance, global, zobj); + RETVAL_OBJECT(zobj, 1); + + zend_update_static_property(THIS_CE, ZEND_STRS("instance")-1, instance TSRMLS_CC); + zval_ptr_dtor(&instance); + } + } + SET_EH_NORMAL(); +} +/* }}} */ +#endif + +/* {{{ Getters by type */ +#define 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)) { \ + http_querystring_get(getThis(), TYPE, name, name_len, defval, del, return_value); \ + } \ +} +HTTP_QUERYSTRING_GETTER(getBool, IS_BOOL); +HTTP_QUERYSTRING_GETTER(getInt, IS_LONG); +HTTP_QUERYSTRING_GETTER(getFloat, IS_DOUBLE); +HTTP_QUERYSTRING_GETTER(getString, IS_STRING); +HTTP_QUERYSTRING_GETTER(getArray, IS_ARRAY); +HTTP_QUERYSTRING_GETTER(getObject, IS_OBJECT); +/* }}} */ + +#ifdef HTTP_HAVE_ICONV +/* {{{ proto bool HttpQueryString::xlate(string ie, string oe) + Converts the query string from the source encoding ie to the target encoding oe. WARNING: Don't use any character set that can contain NUL bytes like UTF-16. */ +PHP_METHOD(HttpQueryString, xlate) +{ + char *ie, *oe; + int ie_len, oe_len; + zval xa, *qa, *qs; + STATUS rs; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &ie, &ie_len, &oe, &oe_len)) { + RETURN_FALSE; + } + + qa = zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryArray")-1, 0 TSRMLS_CC); + qs = zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryString")-1, 0 TSRMLS_CC); + INIT_PZVAL(&xa); + array_init(&xa); + + if (SUCCESS == (rs = http_querystring_xlate(&xa, qa, ie, oe))) { + zend_hash_clean(Z_ARRVAL_P(qa)); + zend_hash_copy(Z_ARRVAL_P(qa), Z_ARRVAL(xa), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + http_querystring_update(qa, qs); + } + zval_dtor(&xa); + + RETURN_SUCCESS(rs); +} +/* }}} */ +#endif /* HAVE_ICONV */ + +/* {{{ proto string HttpQueryString::serialize() + Implements Serializable::serialize(). */ +PHP_METHOD(HttpQueryString, serialize) +{ + NO_ARGS; + RETURN_PROP(queryString); +} +/* }}} */ + +/* {{{ proto void HttpQueryString::unserialize(string serialized) + Implements Serializable::unserialize(). */ +PHP_METHOD(HttpQueryString, unserialize) +{ + zval *serialized; + + SET_EH_THROW_HTTP(); + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &serialized)) { + if (Z_TYPE_P(serialized) == IS_STRING) { + http_querystring_instantiate(getThis(), 0, serialized, 0); + } else { + http_error(HE_WARNING, HTTP_E_QUERYSTRING, "Expected a string as parameter"); + } + } + SET_EH_NORMAL(); +} +/* }}} */ + +/* {{{ proto mixed HttpQueryString::offsetGet(string offset) + Implements ArrayAccess::offsetGet(). */ +PHP_METHOD(HttpQueryString, offsetGet) +{ + char *offset_str; + int offset_len; + zval **value; + + if ( (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset_str, &offset_len)) && + (SUCCESS == zend_hash_find(Z_ARRVAL_P(zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryArray")-1, 0 TSRMLS_CC)), offset_str, offset_len + 1, (void *) &value))) { + RETVAL_ZVAL(*value, 1, 0); + } +} +/* }}} */ + +/* {{{ proto void HttpQueryString::offsetSet(string offset, mixed value) + Implements ArrayAccess::offsetGet(). */ +PHP_METHOD(HttpQueryString, offsetSet) +{ + char *offset_str; + int offset_len; + zval *value; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &offset_str, &offset_len, &value)) { + zval *qarr = zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryArray")-1, 0 TSRMLS_CC), *qstr = zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryString")-1, 0 TSRMLS_CC); + + ZVAL_ADDREF(value); + add_assoc_zval_ex(qarr, offset_str, offset_len + 1, value); + http_querystring_update(qarr, qstr); + } +} +/* }}} */ + +/* {{{ proto bool HttpQueryString::offsetExists(string offset) + Implements ArrayAccess::offsetExists(). */ +PHP_METHOD(HttpQueryString, offsetExists) +{ + char *offset_str; + int offset_len; + zval **value; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset_str, &offset_len)) { + RETURN_BOOL((SUCCESS == zend_hash_find(Z_ARRVAL_P(zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryArray")-1, 0 TSRMLS_CC)), offset_str, offset_len + 1, (void *) &value)) && (Z_TYPE_PP(value) != IS_NULL)); + } +} +/* }}} */ + +/* {{{ proto void HttpQueryString::offsetUnset(string offset) + Implements ArrayAccess::offsetUnset(). */ +PHP_METHOD(HttpQueryString, offsetUnset) +{ + char *offset_str; + int offset_len; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset_str, &offset_len)) { + zval *qarr = zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryArray")-1, 0 TSRMLS_CC); + + if (SUCCESS == zend_hash_del(Z_ARRVAL_P(qarr), offset_str, offset_len + 1)) { + http_querystring_update(qarr, zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryString")-1, 0 TSRMLS_CC)); + } + } +} +/* }}} */ + +#endif /* ZEND_ENGINE_2 */ + +/* + * 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 + */ + --- /dev/null +++ b/ext/http/http_request_api.c @@ -0,0 +1,1326 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_request_api.c 310775 2011-05-05 06:02:50Z mike $ */ + +#define HTTP_WANT_SAPI +#define HTTP_WANT_CURL +#include "php_http.h" + +#ifdef HTTP_HAVE_CURL + +#include "php_http_api.h" +#include "php_http_persistent_handle_api.h" +#include "php_http_request_api.h" +#include "php_http_url_api.h" + +#ifdef ZEND_ENGINE_2 +# include "php_http_request_object.h" +#endif + +#include "php_http_request_int.h" + +/* {{{ cruft for thread safe SSL crypto locks */ +#ifdef HTTP_NEED_OPENSSL_TSL +static MUTEX_T *http_openssl_tsl = NULL; + +static void http_openssl_thread_lock(int mode, int n, const char * file, int line) +{ + if (mode & CRYPTO_LOCK) { + tsrm_mutex_lock(http_openssl_tsl[n]); + } else { + tsrm_mutex_unlock(http_openssl_tsl[n]); + } +} + +static ulong http_openssl_thread_id(void) +{ + return (ulong) tsrm_thread_id(); +} +#endif +#ifdef HTTP_NEED_GNUTLS_TSL +static int http_gnutls_mutex_create(void **m) +{ + if (*((MUTEX_T *) m) = tsrm_mutex_alloc()) { + return SUCCESS; + } else { + return FAILURE; + } +} + +static int http_gnutls_mutex_destroy(void **m) +{ + tsrm_mutex_free(*((MUTEX_T *) m)); + return SUCCESS; +} + +static int http_gnutls_mutex_lock(void **m) +{ + return tsrm_mutex_lock(*((MUTEX_T *) m)); +} + +static int http_gnutls_mutex_unlock(void **m) +{ + return tsrm_mutex_unlock(*((MUTEX_T *) m)); +} + +static struct gcry_thread_cbs http_gnutls_tsl = { + GCRY_THREAD_OPTION_USER, + NULL, + http_gnutls_mutex_create, + http_gnutls_mutex_destroy, + http_gnutls_mutex_lock, + http_gnutls_mutex_unlock +}; +#endif +/* }}} */ + +/* safe curl wrappers */ +#define init_curl_storage(ch) \ + {\ + http_request_storage *st = pecalloc(1, sizeof(http_request_storage), 1); \ + curl_easy_setopt(ch, CURLOPT_PRIVATE, st); \ + curl_easy_setopt(ch, CURLOPT_ERRORBUFFER, st->errorbuffer); \ + } + +static void *safe_curl_init(void) +{ + CURL *ch; + + if ((ch = curl_easy_init())) { + init_curl_storage(ch); + return ch; + } + return NULL; +} +static void *safe_curl_copy(void *p) +{ + CURL *ch; + + if ((ch = curl_easy_duphandle(p))) { + init_curl_storage(ch); + return ch; + } + return NULL; +} +static void safe_curl_dtor(void *p) { + http_request_storage *st = http_request_storage_get(p); + + curl_easy_cleanup(p); + + if (st) { + if (st->url) { + pefree(st->url, 1); + } + if (st->cookiestore) { + pefree(st->cookiestore, 1); + } + pefree(st, 1); + } +} +/* }}} */ + +/* {{{ MINIT */ +PHP_MINIT_FUNCTION(http_request) +{ +#ifdef 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(); + + http_openssl_tsl = malloc(c * sizeof(MUTEX_T)); + + for (i = 0; i < c; ++i) { + http_openssl_tsl[i] = tsrm_mutex_alloc(); + } + + CRYPTO_set_id_callback(http_openssl_thread_id); + CRYPTO_set_locking_callback(http_openssl_thread_lock); + } +#endif +#ifdef HTTP_NEED_GNUTLS_TSL + gcry_control(GCRYCTL_SET_THREAD_CBS, &http_gnutls_tsl); +#endif + + if (CURLE_OK != curl_global_init(CURL_GLOBAL_ALL)) { + return FAILURE; + } + + if (SUCCESS != http_persistent_handle_provide("http_request", safe_curl_init, safe_curl_dtor, safe_curl_copy)) { + return FAILURE; + } + + HTTP_LONG_CONSTANT("HTTP_AUTH_BASIC", CURLAUTH_BASIC); + HTTP_LONG_CONSTANT("HTTP_AUTH_DIGEST", CURLAUTH_DIGEST); +#if HTTP_CURL_VERSION(7,19,3) + HTTP_LONG_CONSTANT("HTTP_AUTH_DIGEST_IE", CURLAUTH_DIGEST_IE); +#endif + HTTP_LONG_CONSTANT("HTTP_AUTH_NTLM", CURLAUTH_NTLM); + HTTP_LONG_CONSTANT("HTTP_AUTH_GSSNEG", CURLAUTH_GSSNEGOTIATE); + HTTP_LONG_CONSTANT("HTTP_AUTH_ANY", CURLAUTH_ANY); + + HTTP_LONG_CONSTANT("HTTP_VERSION_NONE", CURL_HTTP_VERSION_NONE); /* to be removed */ + HTTP_LONG_CONSTANT("HTTP_VERSION_1_0", CURL_HTTP_VERSION_1_0); + HTTP_LONG_CONSTANT("HTTP_VERSION_1_1", CURL_HTTP_VERSION_1_1); + HTTP_LONG_CONSTANT("HTTP_VERSION_ANY", CURL_HTTP_VERSION_NONE); + + HTTP_LONG_CONSTANT("HTTP_SSL_VERSION_TLSv1", CURL_SSLVERSION_TLSv1); + HTTP_LONG_CONSTANT("HTTP_SSL_VERSION_SSLv2", CURL_SSLVERSION_SSLv2); + HTTP_LONG_CONSTANT("HTTP_SSL_VERSION_SSLv3", CURL_SSLVERSION_SSLv3); + HTTP_LONG_CONSTANT("HTTP_SSL_VERSION_ANY", CURL_SSLVERSION_DEFAULT); + + HTTP_LONG_CONSTANT("HTTP_IPRESOLVE_V4", CURL_IPRESOLVE_V4); + HTTP_LONG_CONSTANT("HTTP_IPRESOLVE_V6", CURL_IPRESOLVE_V6); + HTTP_LONG_CONSTANT("HTTP_IPRESOLVE_ANY", CURL_IPRESOLVE_WHATEVER); + +#if HTTP_CURL_VERSION(7,15,2) + HTTP_LONG_CONSTANT("HTTP_PROXY_SOCKS4", CURLPROXY_SOCKS4); +#endif +#if HTTP_CURL_VERSION(7,18,0) + HTTP_LONG_CONSTANT("HTTP_PROXY_SOCKS4A", CURLPROXY_SOCKS4A); + HTTP_LONG_CONSTANT("HTTP_PROXY_SOCKS5_HOSTNAME", CURLPROXY_SOCKS5_HOSTNAME); +#endif + HTTP_LONG_CONSTANT("HTTP_PROXY_SOCKS5", CURLPROXY_SOCKS5); + HTTP_LONG_CONSTANT("HTTP_PROXY_HTTP", CURLPROXY_HTTP); +#if HTTP_CURL_VERSION(7,19,4) + HTTP_LONG_CONSTANT("HTTP_PROXY_HTTP_1_0", CURLPROXY_HTTP_1_0); +#endif + +#if HTTP_CURL_VERSION(7,19,1) + HTTP_LONG_CONSTANT("HTTP_POSTREDIR_301", CURL_REDIR_POST_301); + HTTP_LONG_CONSTANT("HTTP_POSTREDIR_302", CURL_REDIR_POST_302); + HTTP_LONG_CONSTANT("HTTP_POSTREDIR_ALL", CURL_REDIR_POST_ALL); +#endif + return SUCCESS; +} +/* }}} */ + +/* {{{ MSHUTDOWN */ +PHP_MSHUTDOWN_FUNCTION(http_request) +{ + curl_global_cleanup(); +#ifdef HTTP_NEED_OPENSSL_TSL + if (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(http_openssl_tsl[i]); + } + + free(http_openssl_tsl); + http_openssl_tsl = NULL; + } +#endif + return SUCCESS; +} +/* }}} */ + +/* {{{ forward declarations */ +#define http_request_option(r, o, k, t) _http_request_option_ex((r), (o), (k), sizeof(k), (t) TSRMLS_CC) +#define http_request_option_ex(r, o, k, l, t) _http_request_option_ex((r), (o), (k), (l), (t) TSRMLS_CC) +static inline zval *_http_request_option_ex(http_request *request, HashTable *options, char *key, size_t keylen, int type TSRMLS_DC); +#define http_request_option_cache(r, k, z) _http_request_option_cache_ex((r), (k), sizeof(k), 0, (z) TSRMLS_CC) +#define http_request_option_cache_ex(r, k, kl, h, z) _http_request_option_cache_ex((r), (k), (kl), (h), (z) TSRMLS_CC) +static inline zval *_http_request_option_cache_ex(http_request *r, char *key, size_t keylen, ulong h, zval *opt TSRMLS_DC); + +#define http_request_cookies_enabled(r) _http_request_cookies_enabled((r)) +static inline int _http_request_cookies_enabled(http_request *r); + +static size_t http_curl_read_callback(void *, size_t, size_t, void *); +static int http_curl_progress_callback(void *, double, double, double, double); +static int http_curl_raw_callback(CURL *, curl_infotype, char *, size_t, void *); +static int http_curl_dummy_callback(char *data, size_t n, size_t l, void *s) { return n*l; } +static curlioerr http_curl_ioctl_callback(CURL *, curliocmd, void *); +/* }}} */ + +/* {{{ CURL *http_curl_init(http_request *) */ +PHP_HTTP_API CURL * _http_curl_init_ex(CURL *ch, http_request *request TSRMLS_DC) +{ + if (ch || (SUCCESS == http_persistent_handle_acquire("http_request", &ch))) { +#if defined(ZTS) + curl_easy_setopt(ch, CURLOPT_NOSIGNAL, 1L); +#endif + curl_easy_setopt(ch, CURLOPT_HEADER, 0L); + curl_easy_setopt(ch, CURLOPT_FILETIME, 1L); + curl_easy_setopt(ch, CURLOPT_AUTOREFERER, 1L); + curl_easy_setopt(ch, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(ch, CURLOPT_HEADERFUNCTION, NULL); + curl_easy_setopt(ch, CURLOPT_DEBUGFUNCTION, http_curl_raw_callback); + curl_easy_setopt(ch, CURLOPT_READFUNCTION, http_curl_read_callback); + curl_easy_setopt(ch, CURLOPT_IOCTLFUNCTION, http_curl_ioctl_callback); + curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, http_curl_dummy_callback); + + /* set context */ + if (request) { + curl_easy_setopt(ch, CURLOPT_DEBUGDATA, request); + + /* attach curl handle */ + request->ch = ch; + /* set defaults (also in http_request_reset()) */ + http_request_defaults(request); + } + } + + return ch; +} +/* }}} */ + +/* {{{ CURL *http_curl_copy(CURL *) */ +PHP_HTTP_API CURL *_http_curl_copy(CURL *ch TSRMLS_DC) +{ + CURL *copy; + + if (SUCCESS == http_persistent_handle_accrete("http_request", ch, ©)) { + return copy; + } + return NULL; +} +/* }}} */ + +/* {{{ void http_curl_free(CURL **) */ +PHP_HTTP_API void _http_curl_free(CURL **ch TSRMLS_DC) +{ + if (*ch) { + curl_easy_setopt(*ch, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(*ch, CURLOPT_PROGRESSFUNCTION, NULL); + curl_easy_setopt(*ch, CURLOPT_VERBOSE, 0L); + curl_easy_setopt(*ch, CURLOPT_DEBUGFUNCTION, NULL); + + http_persistent_handle_release("http_request", ch); + } +} +/* }}} */ + +/* {{{ http_request *http_request_init(http_request *) */ +PHP_HTTP_API http_request *_http_request_init_ex(http_request *request, CURL *ch, http_request_method meth, const char *url ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC) +{ + http_request *r; + + if (request) { + r = request; + } else { + r = emalloc_rel(sizeof(http_request)); + } + memset(r, 0, sizeof(http_request)); + + r->ch = ch; + r->url = (url) ? http_absolute_url(url) : NULL; + r->meth = (meth > 0) ? meth : HTTP_GET; + + phpstr_init(&r->conv.request); + phpstr_init_ex(&r->conv.response, HTTP_CURLBUF_SIZE, 0); + phpstr_init(&r->_cache.cookies); + zend_hash_init(&r->_cache.options, 0, NULL, ZVAL_PTR_DTOR, 0); + + TSRMLS_SET_CTX(r->tsrm_ls); + + return r; +} +/* }}} */ + +/* {{{ void http_request_dtor(http_request *) */ +PHP_HTTP_API void _http_request_dtor(http_request *request) +{ + TSRMLS_FETCH_FROM_CTX(request->tsrm_ls); + + http_request_reset(request); + http_curl_free(&request->ch); + + phpstr_dtor(&request->_cache.cookies); + zend_hash_destroy(&request->_cache.options); + if (request->_cache.headers) { + curl_slist_free_all(request->_cache.headers); + request->_cache.headers = NULL; + } + if (request->_progress_callback) { + zval_ptr_dtor(&request->_progress_callback); + request->_progress_callback = NULL; + } +} +/* }}} */ + +/* {{{ void http_request_free(http_request **) */ +PHP_HTTP_API void _http_request_free(http_request **request) +{ + if (*request) { + TSRMLS_FETCH_FROM_CTX((*request)->tsrm_ls); + http_request_body_free(&(*request)->body); + http_request_dtor(*request); + efree(*request); + *request = NULL; + } +} +/* }}} */ + +/* {{{ void http_request_reset(http_request *) */ +PHP_HTTP_API void _http_request_reset(http_request *request) +{ + TSRMLS_FETCH_FROM_CTX(request->tsrm_ls); + STR_SET(request->url, NULL); + request->conv.last_type = 0; + phpstr_dtor(&request->conv.request); + phpstr_dtor(&request->conv.response); + http_request_body_dtor(request->body); + http_request_defaults(request); + + if (request->ch) { + http_request_storage *st = http_request_storage_get(request->ch); + + if (st) { + if (st->url) { + pefree(st->url, 1); + st->url = NULL; + } + if (st->cookiestore) { + pefree(st->cookiestore, 1); + st->cookiestore = NULL; + } + st->errorbuffer[0] = '\0'; + } + } +} +/* }}} */ + +/* {{{ STATUS http_request_enable_cookies(http_request *) */ +PHP_HTTP_API STATUS _http_request_enable_cookies(http_request *request) +{ + int initialized = 1; + TSRMLS_FETCH_FROM_CTX(request->tsrm_ls); + + HTTP_CHECK_CURL_INIT(request->ch, http_curl_init_ex(request->ch, request), initialized = 0); + if (initialized && (http_request_cookies_enabled(request) || (CURLE_OK == curl_easy_setopt(request->ch, CURLOPT_COOKIEFILE, "")))) { + return SUCCESS; + } + http_error(HE_WARNING, HTTP_E_REQUEST, "Could not enable cookies for this session"); + return FAILURE; +} +/* }}} */ + +/* {{{ STATUS http_request_reset_cookies(http_request *, int) */ +PHP_HTTP_API STATUS _http_request_reset_cookies(http_request *request, int session_only) +{ + int initialized = 1; + TSRMLS_FETCH_FROM_CTX(request->tsrm_ls); + + HTTP_CHECK_CURL_INIT(request->ch, http_curl_init_ex(request->ch, request), initialized = 0); + if (initialized) { + if (!http_request_cookies_enabled(request)) { + if (SUCCESS != http_request_enable_cookies(request)) { + return FAILURE; + } + } + if (session_only) { +#if HTTP_CURL_VERSION(7,15,4) + if (CURLE_OK == curl_easy_setopt(request->ch, CURLOPT_COOKIELIST, "SESS")) { + return SUCCESS; + } +#else + http_error(HE_WARNING, HTTP_E_REQUEST, "Could not reset session cookies (need libcurl >= v7.15.4)"); +#endif + } else { +#if HTTP_CURL_VERSION(7,14,1) + if (CURLE_OK == curl_easy_setopt(request->ch, CURLOPT_COOKIELIST, "ALL")) { + return SUCCESS; + } +#else + http_error(HE_WARNING, HTTP_E_REQUEST, "Could not reset cookies (need libcurl >= v7.14.1)"); +#endif + } + } + return FAILURE; +} +/* }}} */ + +PHP_HTTP_API STATUS _http_request_flush_cookies(http_request *request) +{ + int initialized = 1; + TSRMLS_FETCH_FROM_CTX(request->tsrm_ls); + + HTTP_CHECK_CURL_INIT(request->ch, http_curl_init_ex(request->ch, request), initialized = 0); + if (initialized) { + if (!http_request_cookies_enabled(request)) { + return FAILURE; + } +#if HTTP_CURL_VERSION(7,17,1) + if (CURLE_OK == curl_easy_setopt(request->ch, CURLOPT_COOKIELIST, "FLUSH")) { + return SUCCESS; + } +#else + http_error(HE_WARNING, HTTP_E_REQUEST, "Could not flush cookies (need libcurl >= v7.17.1)"); +#endif + } + return FAILURE; +} + +/* {{{ void http_request_defaults(http_request *) */ +PHP_HTTP_API void _http_request_defaults(http_request *request) +{ + if (request->ch) { + HTTP_CURL_OPT(CURLOPT_PROGRESSFUNCTION, NULL); + HTTP_CURL_OPT(CURLOPT_URL, NULL); + HTTP_CURL_OPT(CURLOPT_NOPROGRESS, 1L); +#if HTTP_CURL_VERSION(7,19,4) + HTTP_CURL_OPT(CURLOPT_NOPROXY, NULL); +#endif + HTTP_CURL_OPT(CURLOPT_PROXY, NULL); + HTTP_CURL_OPT(CURLOPT_PROXYPORT, 0L); + HTTP_CURL_OPT(CURLOPT_PROXYTYPE, 0L); + /* libcurl < 7.19.6 does not clear auth info with USERPWD set to NULL */ +#if HTTP_CURL_VERSION(7,19,1) + HTTP_CURL_OPT(CURLOPT_PROXYUSERNAME, NULL); + HTTP_CURL_OPT(CURLOPT_PROXYPASSWORD, NULL); +#endif + HTTP_CURL_OPT(CURLOPT_PROXYAUTH, 0L); + HTTP_CURL_OPT(CURLOPT_HTTPPROXYTUNNEL, 0L); + HTTP_CURL_OPT(CURLOPT_DNS_CACHE_TIMEOUT, 60L); + HTTP_CURL_OPT(CURLOPT_IPRESOLVE, 0); + HTTP_CURL_OPT(CURLOPT_LOW_SPEED_LIMIT, 0L); + HTTP_CURL_OPT(CURLOPT_LOW_SPEED_TIME, 0L); +#if HTTP_CURL_VERSION(7,15,5) + /* LFS weirdance + HTTP_CURL_OPT(CURLOPT_MAX_SEND_SPEED_LARGE, (curl_off_t) 0); + HTTP_CURL_OPT(CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t) 0); + */ +#endif + /* crashes + HTTP_CURL_OPT(CURLOPT_MAXCONNECTS, 5L); */ + HTTP_CURL_OPT(CURLOPT_FRESH_CONNECT, 0L); + HTTP_CURL_OPT(CURLOPT_FORBID_REUSE, 0L); + HTTP_CURL_OPT(CURLOPT_INTERFACE, NULL); + HTTP_CURL_OPT(CURLOPT_PORT, 0L); +#if HTTP_CURL_VERSION(7,19,0) + HTTP_CURL_OPT(CURLOPT_ADDRESS_SCOPE, 0L); +#endif +#if HTTP_CURL_VERSION(7,15,2) + HTTP_CURL_OPT(CURLOPT_LOCALPORT, 0L); + HTTP_CURL_OPT(CURLOPT_LOCALPORTRANGE, 0L); +#endif + /* libcurl < 7.19.6 does not clear auth info with USERPWD set to NULL */ +#if HTTP_CURL_VERSION(7,19,1) + HTTP_CURL_OPT(CURLOPT_USERNAME, NULL); + HTTP_CURL_OPT(CURLOPT_PASSWORD, NULL); +#endif + HTTP_CURL_OPT(CURLOPT_HTTPAUTH, 0L); + HTTP_CURL_OPT(CURLOPT_ENCODING, NULL); +#if HTTP_CURL_VERSION(7,16,2) + /* we do this ourself anyway */ + HTTP_CURL_OPT(CURLOPT_HTTP_CONTENT_DECODING, 0L); + HTTP_CURL_OPT(CURLOPT_HTTP_TRANSFER_DECODING, 0L); +#endif + HTTP_CURL_OPT(CURLOPT_FOLLOWLOCATION, 0L); +#if HTTP_CURL_VERSION(7,19,1) + HTTP_CURL_OPT(CURLOPT_POSTREDIR, 0L); +#elif HTTP_CURL_VERSION(7,17,1) + HTTP_CURL_OPT(CURLOPT_POST301, 0L); +#endif + HTTP_CURL_OPT(CURLOPT_UNRESTRICTED_AUTH, 0L); + HTTP_CURL_OPT(CURLOPT_REFERER, NULL); + HTTP_CURL_OPT(CURLOPT_USERAGENT, "PECL::HTTP/" PHP_HTTP_VERSION " (PHP/" PHP_VERSION ")"); + HTTP_CURL_OPT(CURLOPT_HTTPHEADER, NULL); + HTTP_CURL_OPT(CURLOPT_COOKIE, NULL); + HTTP_CURL_OPT(CURLOPT_COOKIESESSION, 0L); + /* these options would enable curl's cookie engine by default which we don't want + HTTP_CURL_OPT(CURLOPT_COOKIEFILE, NULL); + HTTP_CURL_OPT(CURLOPT_COOKIEJAR, NULL); */ +#if HTTP_CURL_VERSION(7,14,1) + HTTP_CURL_OPT(CURLOPT_COOKIELIST, NULL); +#endif + HTTP_CURL_OPT(CURLOPT_RANGE, NULL); + HTTP_CURL_OPT(CURLOPT_RESUME_FROM, 0L); + HTTP_CURL_OPT(CURLOPT_MAXFILESIZE, 0L); + HTTP_CURL_OPT(CURLOPT_TIMECONDITION, 0L); + HTTP_CURL_OPT(CURLOPT_TIMEVALUE, 0L); + HTTP_CURL_OPT(CURLOPT_TIMEOUT, 0L); + HTTP_CURL_OPT(CURLOPT_CONNECTTIMEOUT, 3); + HTTP_CURL_OPT(CURLOPT_SSLCERT, NULL); + HTTP_CURL_OPT(CURLOPT_SSLCERTTYPE, NULL); + HTTP_CURL_OPT(CURLOPT_SSLCERTPASSWD, NULL); + HTTP_CURL_OPT(CURLOPT_SSLKEY, NULL); + HTTP_CURL_OPT(CURLOPT_SSLKEYTYPE, NULL); + HTTP_CURL_OPT(CURLOPT_SSLKEYPASSWD, NULL); + HTTP_CURL_OPT(CURLOPT_SSLENGINE, NULL); + HTTP_CURL_OPT(CURLOPT_SSLVERSION, 0L); + HTTP_CURL_OPT(CURLOPT_SSL_VERIFYPEER, 0L); + HTTP_CURL_OPT(CURLOPT_SSL_VERIFYHOST, 0L); + HTTP_CURL_OPT(CURLOPT_SSL_CIPHER_LIST, NULL); +#if HTTP_CURL_VERSION(7,19,0) + HTTP_CURL_OPT(CURLOPT_ISSUERCERT, NULL); + #if defined(HTTP_HAVE_OPENSSL) + HTTP_CURL_OPT(CURLOPT_CRLFILE, NULL); + #endif +#endif +#if HTTP_CURL_VERSION(7,19,1) && defined(HTTP_HAVE_OPENSSL) + HTTP_CURL_OPT(CURLOPT_CERTINFO, NULL); +#endif +#ifdef HTTP_CURL_CAINFO + HTTP_CURL_OPT(CURLOPT_CAINFO, HTTP_CURL_CAINFO); +#else + HTTP_CURL_OPT(CURLOPT_CAINFO, NULL); +#endif + HTTP_CURL_OPT(CURLOPT_CAPATH, NULL); + HTTP_CURL_OPT(CURLOPT_RANDOM_FILE, NULL); + HTTP_CURL_OPT(CURLOPT_EGDSOCKET, NULL); + HTTP_CURL_OPT(CURLOPT_POSTFIELDS, NULL); + HTTP_CURL_OPT(CURLOPT_POSTFIELDSIZE, 0L); + HTTP_CURL_OPT(CURLOPT_HTTPPOST, NULL); + HTTP_CURL_OPT(CURLOPT_IOCTLDATA, NULL); + HTTP_CURL_OPT(CURLOPT_READDATA, NULL); + HTTP_CURL_OPT(CURLOPT_INFILESIZE, 0L); + HTTP_CURL_OPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_NONE); + HTTP_CURL_OPT(CURLOPT_CUSTOMREQUEST, NULL); + HTTP_CURL_OPT(CURLOPT_NOBODY, 0L); + HTTP_CURL_OPT(CURLOPT_POST, 0L); + HTTP_CURL_OPT(CURLOPT_UPLOAD, 0L); + HTTP_CURL_OPT(CURLOPT_HTTPGET, 1L); + } +} +/* }}} */ + +PHP_HTTP_API void _http_request_set_progress_callback(http_request *request, zval *cb) +{ + if (request->_progress_callback) { + zval_ptr_dtor(&request->_progress_callback); + } + if ((request->_progress_callback = cb)) { + ZVAL_ADDREF(cb); + HTTP_CURL_OPT(CURLOPT_NOPROGRESS, 0); + HTTP_CURL_OPT(CURLOPT_PROGRESSDATA, request); + HTTP_CURL_OPT(CURLOPT_PROGRESSFUNCTION, http_curl_progress_callback); + } else { + HTTP_CURL_OPT(CURLOPT_NOPROGRESS, 1); + HTTP_CURL_OPT(CURLOPT_PROGRESSDATA, NULL); + HTTP_CURL_OPT(CURLOPT_PROGRESSFUNCTION, NULL); + } +} + +/* {{{ STATUS http_request_prepare(http_request *, HashTable *) */ +PHP_HTTP_API STATUS _http_request_prepare(http_request *request, HashTable *options) +{ + zval *zoption; + zend_bool range_req = 0; + http_request_storage *storage; + + TSRMLS_FETCH_FROM_CTX(request->tsrm_ls); + + HTTP_CHECK_CURL_INIT(request->ch, http_curl_init(request), return FAILURE); + + if (!request->url) { + return FAILURE; + } + if (!(storage = http_request_storage_get(request->ch))) { + return FAILURE; + } + storage->errorbuffer[0] = '\0'; + /* set options */ + if (storage->url) { + pefree(storage->url, 1); + } + storage->url = pestrdup(request->url, 1); + HTTP_CURL_OPT(CURLOPT_URL, storage->url); + + /* progress callback */ + if ((zoption = http_request_option(request, options, "onprogress", -1))) { + http_request_set_progress_callback(request, zoption); + } + + /* proxy */ + if ((zoption = http_request_option(request, options, "proxyhost", IS_STRING))) { + HTTP_CURL_OPT(CURLOPT_PROXY, Z_STRVAL_P(zoption)); + /* type */ + if ((zoption = http_request_option(request, options, "proxytype", IS_LONG))) { + HTTP_CURL_OPT(CURLOPT_PROXYTYPE, Z_LVAL_P(zoption)); + } + /* port */ + if ((zoption = http_request_option(request, options, "proxyport", IS_LONG))) { + HTTP_CURL_OPT(CURLOPT_PROXYPORT, Z_LVAL_P(zoption)); + } + /* user:pass */ + if ((zoption = http_request_option(request, options, "proxyauth", IS_STRING)) && Z_STRLEN_P(zoption)) { + HTTP_CURL_OPT(CURLOPT_PROXYUSERPWD, Z_STRVAL_P(zoption)); + } + /* auth method */ + if ((zoption = http_request_option(request, options, "proxyauthtype", IS_LONG))) { + HTTP_CURL_OPT(CURLOPT_PROXYAUTH, Z_LVAL_P(zoption)); + } + /* tunnel */ + if ((zoption = http_request_option(request, options, "proxytunnel", IS_BOOL)) && Z_BVAL_P(zoption)) { + HTTP_CURL_OPT(CURLOPT_HTTPPROXYTUNNEL, 1L); + } + } +#if HTTP_CURL_VERSION(7,19,4) + if ((zoption = http_request_option(request, options, "noproxy", IS_STRING))) { + HTTP_CURL_OPT(CURLOPT_NOPROXY, Z_STRVAL_P(zoption)); + } +#endif + + /* dns */ + if ((zoption = http_request_option(request, options, "dns_cache_timeout", IS_LONG))) { + HTTP_CURL_OPT(CURLOPT_DNS_CACHE_TIMEOUT, Z_LVAL_P(zoption)); + } + if ((zoption = http_request_option(request, options, "ipresolve", IS_LONG)) && Z_LVAL_P(zoption)) { + HTTP_CURL_OPT(CURLOPT_IPRESOLVE, Z_LVAL_P(zoption)); + } + + /* limits */ + if ((zoption = http_request_option(request, options, "low_speed_limit", IS_LONG))) { + HTTP_CURL_OPT(CURLOPT_LOW_SPEED_LIMIT, Z_LVAL_P(zoption)); + } + if ((zoption = http_request_option(request, options, "low_speed_time", IS_LONG))) { + HTTP_CURL_OPT(CURLOPT_LOW_SPEED_TIME, Z_LVAL_P(zoption)); + } +#if HTTP_CURL_VERSION(7,15,5) + /* LSF weirdance + if ((zoption = http_request_option(request, options, "max_send_speed", IS_LONG))) { + HTTP_CURL_OPT(CURLOPT_MAX_SEND_SPEED_LARGE, (curl_off_t) Z_LVAL_P(zoption)); + } + if ((zoption = http_request_option(request, options, "max_recv_speed", IS_LONG))) { + HTTP_CURL_OPT(CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t) Z_LVAL_P(zoption)); + } + */ +#endif + /* crashes + if ((zoption = http_request_option(request, options, "maxconnects", IS_LONG))) { + HTTP_CURL_OPT(CURLOPT_MAXCONNECTS, Z_LVAL_P(zoption)); + } */ + if ((zoption = http_request_option(request, options, "fresh_connect", IS_BOOL)) && Z_BVAL_P(zoption)) { + HTTP_CURL_OPT(CURLOPT_FRESH_CONNECT, 1L); + } + if ((zoption = http_request_option(request, options, "forbid_reuse", IS_BOOL)) && Z_BVAL_P(zoption)) { + HTTP_CURL_OPT(CURLOPT_FORBID_REUSE, 1L); + } + + /* outgoing interface */ + if ((zoption = http_request_option(request, options, "interface", IS_STRING))) { + HTTP_CURL_OPT(CURLOPT_INTERFACE, Z_STRVAL_P(zoption)); + +#if HTTP_CURL_VERSION(7,15,2) + if ((zoption = http_request_option(request, options, "portrange", IS_ARRAY))) { + zval **prs, **pre; + + zend_hash_internal_pointer_reset(Z_ARRVAL_P(zoption)); + if (SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(zoption), (void *) &prs)) { + zend_hash_move_forward(Z_ARRVAL_P(zoption)); + if (SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(zoption), (void *) &pre)) { + zval *prs_cpy = http_zsep(IS_LONG, *prs); + zval *pre_cpy = http_zsep(IS_LONG, *pre); + + if (Z_LVAL_P(prs_cpy) && Z_LVAL_P(pre_cpy)) { + HTTP_CURL_OPT(CURLOPT_LOCALPORT, MIN(Z_LVAL_P(prs_cpy), Z_LVAL_P(pre_cpy))); + HTTP_CURL_OPT(CURLOPT_LOCALPORTRANGE, labs(Z_LVAL_P(prs_cpy)-Z_LVAL_P(pre_cpy))+1L); + } + zval_ptr_dtor(&prs_cpy); + zval_ptr_dtor(&pre_cpy); + } + } + } +#endif + } + + /* another port */ + if ((zoption = http_request_option(request, options, "port", IS_LONG))) { + HTTP_CURL_OPT(CURLOPT_PORT, Z_LVAL_P(zoption)); + } + + /* RFC4007 zone_id */ +#if HTTP_CURL_VERSION(7,19,0) + if ((zoption = http_request_option(request, options, "address_scope", IS_LONG))) { + HTTP_CURL_OPT(CURLOPT_ADDRESS_SCOPE, Z_LVAL_P(zoption)); + } +#endif + + /* auth */ + if ((zoption = http_request_option(request, options, "httpauth", IS_STRING)) && Z_STRLEN_P(zoption)) { + HTTP_CURL_OPT(CURLOPT_USERPWD, Z_STRVAL_P(zoption)); + } + if ((zoption = http_request_option(request, options, "httpauthtype", IS_LONG))) { + HTTP_CURL_OPT(CURLOPT_HTTPAUTH, Z_LVAL_P(zoption)); + } + + /* redirects, defaults to 0 */ + if ((zoption = http_request_option(request, options, "redirect", IS_LONG))) { + HTTP_CURL_OPT(CURLOPT_FOLLOWLOCATION, Z_LVAL_P(zoption) ? 1L : 0L); + HTTP_CURL_OPT(CURLOPT_MAXREDIRS, Z_LVAL_P(zoption)); + if ((zoption = http_request_option(request, options, "unrestrictedauth", IS_BOOL))) { + HTTP_CURL_OPT(CURLOPT_UNRESTRICTED_AUTH, Z_LVAL_P(zoption)); + } +#if HTTP_CURL_VERSION(7,17,1) + if ((zoption = http_request_option(request, options, "postredir", IS_BOOL))) { +# if HTTP_CURL_VERSION(7,19,1) + HTTP_CURL_OPT(CURLOPT_POSTREDIR, Z_BVAL_P(zoption) ? 1L : 0L); +# else + HTTP_CURL_OPT(CURLOPT_POST301, Z_BVAL_P(zoption) ? 1L : 0L); +# endif + } +#endif + } + + /* retries, defaults to 0 */ + if ((zoption = http_request_option(request, options, "retrycount", IS_LONG))) { + request->_retry.count = Z_LVAL_P(zoption); + if ((zoption = http_request_option(request, options, "retrydelay", IS_DOUBLE))) { + request->_retry.delay = Z_DVAL_P(zoption); + } else { + request->_retry.delay = 0; + } + } else { + request->_retry.count = 0; + } + + /* referer */ + if ((zoption = http_request_option(request, options, "referer", IS_STRING)) && Z_STRLEN_P(zoption)) { + HTTP_CURL_OPT(CURLOPT_REFERER, Z_STRVAL_P(zoption)); + } + + /* useragent, default "PECL::HTTP/version (PHP/version)" */ + if ((zoption = http_request_option(request, options, "useragent", IS_STRING))) { + /* allow to send no user agent, not even default one */ + if (Z_STRLEN_P(zoption)) { + HTTP_CURL_OPT(CURLOPT_USERAGENT, Z_STRVAL_P(zoption)); + } else { + HTTP_CURL_OPT(CURLOPT_USERAGENT, NULL); + } + } + + /* resume */ + if ((zoption = http_request_option(request, options, "resume", IS_LONG)) && (Z_LVAL_P(zoption) > 0)) { + range_req = 1; + HTTP_CURL_OPT(CURLOPT_RESUME_FROM, Z_LVAL_P(zoption)); + } + /* or range of kind array(array(0,499), array(100,1499)) */ + else if ((zoption = http_request_option(request, options, "range", IS_ARRAY)) && zend_hash_num_elements(Z_ARRVAL_P(zoption))) { + HashPosition pos1, pos2; + zval **rr, **rb, **re; + phpstr rs; + + phpstr_init(&rs); + FOREACH_VAL(pos1, zoption, rr) { + if (Z_TYPE_PP(rr) == IS_ARRAY) { + zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(rr), &pos2); + if (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(rr), (void *) &rb, &pos2)) { + zend_hash_move_forward_ex(Z_ARRVAL_PP(rr), &pos2); + if (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(rr), (void *) &re, &pos2)) { + 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 = http_zsep(IS_LONG, *rb); + zval *rel = http_zsep(IS_LONG, *re); + + if ((Z_LVAL_P(rbl) >= 0) && (Z_LVAL_P(rel) >= 0)) { + phpstr_appendf(&rs, "%ld-%ld,", Z_LVAL_P(rbl), Z_LVAL_P(rel)); + } + zval_ptr_dtor(&rbl); + zval_ptr_dtor(&rel); + } + } + } + } + } + + if (PHPSTR_LEN(&rs)) { + zval *cached_range; + + /* ditch last comma */ + PHPSTR_VAL(&rs)[PHPSTR_LEN(&rs)-- -1] = '\0'; + /* cache string */ + MAKE_STD_ZVAL(cached_range); + ZVAL_STRINGL(cached_range, PHPSTR_VAL(&rs), PHPSTR_LEN(&rs), 0); + HTTP_CURL_OPT(CURLOPT_RANGE, Z_STRVAL_P(http_request_option_cache(request, "range", cached_range))); + zval_ptr_dtor(&cached_range); + } + } + + /* additional headers, array('name' => 'value') */ + if (request->_cache.headers) { + curl_slist_free_all(request->_cache.headers); + request->_cache.headers = NULL; + } + if ((zoption = http_request_option(request, options, "headers", IS_ARRAY))) { + HashKey header_key = initHashKey(0); + zval **header_val; + HashPosition pos; + phpstr header; + + phpstr_init(&header); + FOREACH_KEYVAL(pos, zoption, header_key, header_val) { + if (header_key.type == HASH_KEY_IS_STRING) { + zval *header_cpy = http_zsep(IS_STRING, *header_val); + + if (!strcasecmp(header_key.str, "range")) { + range_req = 1; + } + + phpstr_appendf(&header, "%s: %s", header_key.str, Z_STRVAL_P(header_cpy)); + phpstr_fix(&header); + request->_cache.headers = curl_slist_append(request->_cache.headers, PHPSTR_VAL(&header)); + phpstr_reset(&header); + + zval_ptr_dtor(&header_cpy); + } + } + phpstr_dtor(&header); + } + /* etag */ + if ((zoption = http_request_option(request, options, "etag", IS_STRING)) && Z_STRLEN_P(zoption)) { + zend_bool is_quoted = !((Z_STRVAL_P(zoption)[0] != '"') || (Z_STRVAL_P(zoption)[Z_STRLEN_P(zoption)-1] != '"')); + phpstr header; + + phpstr_init(&header); + phpstr_appendf(&header, is_quoted?"%s: %s":"%s: \"%s\"", range_req?"If-Match":"If-None-Match", Z_STRVAL_P(zoption)); + phpstr_fix(&header); + request->_cache.headers = curl_slist_append(request->_cache.headers, PHPSTR_VAL(&header)); + phpstr_dtor(&header); + } + /* compression */ + if ((zoption = http_request_option(request, options, "compress", IS_BOOL)) && Z_LVAL_P(zoption)) { + request->_cache.headers = curl_slist_append(request->_cache.headers, "Accept-Encoding: gzip;q=1.0,deflate;q=0.5"); + } + HTTP_CURL_OPT(CURLOPT_HTTPHEADER, request->_cache.headers); + + /* lastmodified */ + if ((zoption = http_request_option(request, options, "lastmodified", IS_LONG))) { + if (Z_LVAL_P(zoption)) { + if (Z_LVAL_P(zoption) > 0) { + HTTP_CURL_OPT(CURLOPT_TIMEVALUE, Z_LVAL_P(zoption)); + } else { + HTTP_CURL_OPT(CURLOPT_TIMEVALUE, (long) HTTP_G->request.time + Z_LVAL_P(zoption)); + } + HTTP_CURL_OPT(CURLOPT_TIMECONDITION, (long) (range_req ? CURL_TIMECOND_IFUNMODSINCE : CURL_TIMECOND_IFMODSINCE)); + } else { + HTTP_CURL_OPT(CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE); + } + } + + /* cookies, array('name' => 'value') */ + if ((zoption = http_request_option(request, options, "cookies", IS_ARRAY))) { + phpstr_dtor(&request->_cache.cookies); + if (zend_hash_num_elements(Z_ARRVAL_P(zoption))) { + zval *urlenc_cookies = NULL; + /* check whether cookies should not be urlencoded; default is to urlencode them */ + if ((!(urlenc_cookies = http_request_option(request, options, "encodecookies", IS_BOOL))) || Z_BVAL_P(urlenc_cookies)) { + if (SUCCESS == http_urlencode_hash_recursive(HASH_OF(zoption), &request->_cache.cookies, "; ", lenof("; "), NULL, 0)) { + phpstr_fix(&request->_cache.cookies); + HTTP_CURL_OPT(CURLOPT_COOKIE, request->_cache.cookies.data); + } + } else { + HashPosition pos; + HashKey cookie_key = initHashKey(0); + zval **cookie_val; + + FOREACH_KEYVAL(pos, zoption, cookie_key, cookie_val) { + if (cookie_key.type == HASH_KEY_IS_STRING) { + zval *val = http_zsep(IS_STRING, *cookie_val); + phpstr_appendf(&request->_cache.cookies, "%s=%s; ", cookie_key.str, Z_STRVAL_P(val)); + zval_ptr_dtor(&val); + } + } + + phpstr_fix(&request->_cache.cookies); + if (PHPSTR_LEN(&request->_cache.cookies)) { + HTTP_CURL_OPT(CURLOPT_COOKIE, PHPSTR_VAL(&request->_cache.cookies)); + } + } + } + } + + /* don't load session cookies from cookiestore */ + if ((zoption = http_request_option(request, options, "cookiesession", IS_BOOL)) && Z_BVAL_P(zoption)) { + HTTP_CURL_OPT(CURLOPT_COOKIESESSION, 1L); + } + + /* cookiestore, read initial cookies from that file and store cookies back into that file */ + if ((zoption = http_request_option(request, options, "cookiestore", IS_STRING))) { + if (Z_STRLEN_P(zoption)) { + HTTP_CHECK_OPEN_BASEDIR(Z_STRVAL_P(zoption), return FAILURE); + } + if (storage->cookiestore) { + pefree(storage->cookiestore, 1); + } + storage->cookiestore = pestrndup(Z_STRVAL_P(zoption), Z_STRLEN_P(zoption), 1); + HTTP_CURL_OPT(CURLOPT_COOKIEFILE, storage->cookiestore); + HTTP_CURL_OPT(CURLOPT_COOKIEJAR, storage->cookiestore); + } + + /* maxfilesize */ + if ((zoption = http_request_option(request, options, "maxfilesize", IS_LONG))) { + HTTP_CURL_OPT(CURLOPT_MAXFILESIZE, Z_LVAL_P(zoption)); + } + + /* http protocol */ + if ((zoption = http_request_option(request, options, "protocol", IS_LONG))) { + HTTP_CURL_OPT(CURLOPT_HTTP_VERSION, Z_LVAL_P(zoption)); + } + +#if HTTP_CURL_VERSION(7,16,2) + /* timeout, defaults to 0 */ + if ((zoption = http_request_option(request, options, "timeout", IS_DOUBLE))) { + HTTP_CURL_OPT(CURLOPT_TIMEOUT_MS, (long)(Z_DVAL_P(zoption)*1000)); + } + /* connecttimeout, defaults to 0 */ + if ((zoption = http_request_option(request, options, "connecttimeout", IS_DOUBLE))) { + HTTP_CURL_OPT(CURLOPT_CONNECTTIMEOUT_MS, (long)(Z_DVAL_P(zoption)*1000)); + } +#else + /* timeout, defaults to 0 */ + if ((zoption = http_request_option(request, options, "timeout", IS_LONG))) { + HTTP_CURL_OPT(CURLOPT_TIMEOUT, Z_LVAL_P(zoption)); + } + /* connecttimeout, defaults to 0 */ + if ((zoption = http_request_option(request, options, "connecttimeout", IS_LONG))) { + HTTP_CURL_OPT(CURLOPT_CONNECTTIMEOUT, Z_LVAL_P(zoption)); + } +#endif + + /* ssl */ + if ((zoption = http_request_option(request, options, "ssl", IS_ARRAY))) { + HashKey key = initHashKey(0); + zval **param; + HashPosition pos; + + FOREACH_KEYVAL(pos, zoption, key, param) { + if (key.type == HASH_KEY_IS_STRING) { + HTTP_CURL_OPT_STRING(CURLOPT_SSLCERT, 0, 1); + HTTP_CURL_OPT_STRING(CURLOPT_SSLCERTTYPE, 0, 0); + HTTP_CURL_OPT_STRING(CURLOPT_SSLCERTPASSWD, 0, 0); + + HTTP_CURL_OPT_STRING(CURLOPT_SSLKEY, 0, 0); + HTTP_CURL_OPT_STRING(CURLOPT_SSLKEYTYPE, 0, 0); + HTTP_CURL_OPT_STRING(CURLOPT_SSLKEYPASSWD, 0, 0); + + HTTP_CURL_OPT_STRING(CURLOPT_SSLENGINE, 0, 0); + HTTP_CURL_OPT_LONG(CURLOPT_SSLVERSION, 0); + + HTTP_CURL_OPT_LONG(CURLOPT_SSL_VERIFYPEER, 1); + HTTP_CURL_OPT_LONG(CURLOPT_SSL_VERIFYHOST, 1); + HTTP_CURL_OPT_STRING(CURLOPT_SSL_CIPHER_LIST, 1, 0); + + HTTP_CURL_OPT_STRING(CURLOPT_CAINFO, -3, 1); + HTTP_CURL_OPT_STRING(CURLOPT_CAPATH, -3, 1); + HTTP_CURL_OPT_STRING(CURLOPT_RANDOM_FILE, -3, 1); + HTTP_CURL_OPT_STRING(CURLOPT_EGDSOCKET, -3, 1); +#if HTTP_CURL_VERSION(7,19,0) + HTTP_CURL_OPT_STRING(CURLOPT_ISSUERCERT, -3, 1); + #if defined(HTTP_HAVE_OPENSSL) + HTTP_CURL_OPT_STRING(CURLOPT_CRLFILE, -3, 1); + #endif +#endif +#if HTTP_CURL_VERSION(7,19,1) && defined(HTTP_HAVE_OPENSSL) + HTTP_CURL_OPT_LONG(CURLOPT_CERTINFO, -3); +#endif + } + } + } + + /* request method */ + switch (request->meth) { + case HTTP_GET: + HTTP_CURL_OPT(CURLOPT_HTTPGET, 1L); + break; + + case HTTP_HEAD: + HTTP_CURL_OPT(CURLOPT_NOBODY, 1L); + break; + + case HTTP_POST: + HTTP_CURL_OPT(CURLOPT_POST, 1L); + break; + + case HTTP_PUT: + HTTP_CURL_OPT(CURLOPT_UPLOAD, 1L); + break; + + default: + if (http_request_method_exists(0, request->meth, NULL)) { + HTTP_CURL_OPT(CURLOPT_CUSTOMREQUEST, http_request_method_name(request->meth)); + } else { + http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Unsupported request method: %d (%s)", request->meth, request->url); + return FAILURE; + } + break; + } + + /* attach request body */ + if (request->body && (request->meth != HTTP_GET) && (request->meth != HTTP_HEAD) && (request->meth != HTTP_OPTIONS)) { + switch (request->body->type) { + case HTTP_REQUEST_BODY_EMPTY: + /* nothing */ + break; + + case HTTP_REQUEST_BODY_CURLPOST: + HTTP_CURL_OPT(CURLOPT_HTTPPOST, (struct curl_httppost *) request->body->data); + break; + + case HTTP_REQUEST_BODY_CSTRING: + if (request->meth != HTTP_PUT) { + HTTP_CURL_OPT(CURLOPT_POSTFIELDS, request->body->data); + HTTP_CURL_OPT(CURLOPT_POSTFIELDSIZE, request->body->size); + break; + } + /* fallthrough, PUT/UPLOAD _needs_ READDATA */ + case HTTP_REQUEST_BODY_UPLOADFILE: + HTTP_CURL_OPT(CURLOPT_IOCTLDATA, request); + HTTP_CURL_OPT(CURLOPT_READDATA, request); + HTTP_CURL_OPT(CURLOPT_INFILESIZE, request->body->size); + break; + + default: + /* shouldn't ever happen */ + http_error_ex(HE_ERROR, 0, "Unknown request body type: %d (%s)", request->body->type, request->url); + return FAILURE; + } + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ void http_request_exec(http_request *) */ +PHP_HTTP_API void _http_request_exec(http_request *request) +{ + uint tries = 0; + CURLcode result; + TSRMLS_FETCH_FROM_CTX(request->tsrm_ls); + +retry: + if (CURLE_OK != (result = curl_easy_perform(request->ch))) { + http_error_ex(HE_WARNING, HTTP_E_REQUEST, "%s; %s (%s)", curl_easy_strerror(result), http_request_storage_get(request->ch)->errorbuffer, request->url); +#ifdef ZEND_ENGINE_2 + if ((HTTP_G->only_exceptions || GLOBAL_ERROR_HANDLING == EH_THROW) && EG(exception)) { + add_property_long(EG(exception), "curlCode", result); + } +#endif + + if (request->_retry.count > tries++) { + switch (result) { + case CURLE_COULDNT_RESOLVE_PROXY: + case CURLE_COULDNT_RESOLVE_HOST: + case CURLE_COULDNT_CONNECT: + case CURLE_WRITE_ERROR: + case CURLE_READ_ERROR: + case CURLE_OPERATION_TIMEDOUT: + case CURLE_SSL_CONNECT_ERROR: + case CURLE_GOT_NOTHING: + case CURLE_SSL_ENGINE_SETFAILED: + case CURLE_SEND_ERROR: + case CURLE_RECV_ERROR: + case CURLE_SSL_ENGINE_INITFAILED: + case CURLE_LOGIN_DENIED: + if (request->_retry.delay >= HTTP_DIFFSEC) { + http_sleep(request->_retry.delay); + } + goto retry; + default: + break; + } + } + } +} +/* }}} */ + +/* {{{ static size_t http_curl_read_callback(void *, size_t, size_t, void *) */ +static size_t http_curl_read_callback(void *data, size_t len, size_t n, void *ctx) +{ + http_request *request = (http_request *) ctx; + TSRMLS_FETCH_FROM_CTX(request->tsrm_ls); + + if (request->body) { + switch (request->body->type) { + case HTTP_REQUEST_BODY_CSTRING: + { + size_t out = MIN(len * n, request->body->size - request->body->priv); + + if (out) { + memcpy(data, ((char *) request->body->data) + request->body->priv, out); + request->body->priv += out; + return out; + } + break; + } + + case HTTP_REQUEST_BODY_UPLOADFILE: + return php_stream_read((php_stream *) request->body->data, data, len * n); + } + } + return 0; +} +/* }}} */ + +/* {{{ static int http_curl_progress_callback(void *, double, double, double, double) */ +static int http_curl_progress_callback(void *ctx, double dltotal, double dlnow, double ultotal, double ulnow) +{ + zval *param, retval; + http_request *request = (http_request *) ctx; + TSRMLS_FETCH_FROM_CTX(request->tsrm_ls); + + INIT_PZVAL(&retval); + ZVAL_NULL(&retval); + + MAKE_STD_ZVAL(param); + array_init(param); + add_assoc_double(param, "dltotal", dltotal); + add_assoc_double(param, "dlnow", dlnow); + add_assoc_double(param, "ultotal", ultotal); + add_assoc_double(param, "ulnow", ulnow); + + with_error_handling(EH_NORMAL, NULL) { + request->_in_progress_cb = 1; + call_user_function(EG(function_table), NULL, request->_progress_callback, &retval, 1, ¶m TSRMLS_CC); + request->_in_progress_cb = 0; + } end_error_handling(); + + zval_ptr_dtor(¶m); + zval_dtor(&retval); + + return 0; +} +/* }}} */ + +/* {{{ static curlioerr http_curl_ioctl_callback(CURL *, curliocmd, void *) */ +static curlioerr http_curl_ioctl_callback(CURL *ch, curliocmd cmd, void *ctx) +{ + http_request *request = (http_request *) ctx; + TSRMLS_FETCH_FROM_CTX(request->tsrm_ls); + + if (cmd != CURLIOCMD_RESTARTREAD) { + return CURLIOE_UNKNOWNCMD; + } + + if (request->body) { + switch (request->body->type) { + case HTTP_REQUEST_BODY_CSTRING: + request->body->priv = 0; + return CURLIOE_OK; + break; + + case HTTP_REQUEST_BODY_UPLOADFILE: + if (SUCCESS == php_stream_rewind((php_stream *) request->body->data)) { + return CURLIOE_OK; + } + break; + } + } + + return CURLIOE_FAILRESTART; +} +/* }}} */ + +/* {{{ static int http_curl_raw_callback(CURL *, curl_infotype, char *, size_t, void *) */ +static int http_curl_raw_callback(CURL *ch, curl_infotype type, char *data, size_t length, void *ctx) +{ + http_request *request = (http_request *) ctx; + +#define EMPTY_HEADER(d, l) (!l || (l == 1 && d[0] == '\n') || (l == 2 && d[0] == '\r' && d[1] == '\n')) + switch (type) { + case CURLINFO_DATA_IN: + if (request->conv.last_type == CURLINFO_HEADER_IN) { + phpstr_appends(&request->conv.response, HTTP_CRLF); + } + phpstr_append(&request->conv.response, data, length); + break; + case CURLINFO_HEADER_IN: + if (!EMPTY_HEADER(data, length)) { + phpstr_append(&request->conv.response, data, length); + } + break; + case CURLINFO_DATA_OUT: + case CURLINFO_HEADER_OUT: + phpstr_append(&request->conv.request, data, length); + break; + default: + break; + } + +#if 0 + { + const char _sym[] = "><><><"; + if (type) { + for (fprintf(stderr, "%c ", _sym[type-1]); length--; data++) { + fprintf(stderr, HTTP_IS_CTYPE(print, *data)?"%c":"\\x%02X", (int) *data); + if (*data == '\n' && length) { + fprintf(stderr, "\n%c ", _sym[type-1]); + } + } + fprintf(stderr, "\n"); + } else { + fprintf(stderr, "# %s", data); + } + } +#endif + + if (type) { + request->conv.last_type = type; + } + return 0; +} +/* }}} */ + +/* {{{ static inline zval *http_request_option(http_request *, HashTable *, char *, size_t, int) */ +static inline zval *_http_request_option_ex(http_request *r, HashTable *options, char *key, size_t keylen, int type TSRMLS_DC) +{ + if (options) { + zval **zoption; + ulong h = zend_hash_func(key, keylen); + + if (SUCCESS == zend_hash_quick_find(options, key, keylen, h, (void *) &zoption)) { + zval *option, *cached; + + option = http_zsep(type, *zoption); + cached = http_request_option_cache_ex(r, key, keylen, h, option); + + zval_ptr_dtor(&option); + return cached; + } + } + + return NULL; +} +/* }}} */ + +/* {{{ static inline zval *http_request_option_cache(http_request *, char *key, zval *) */ +static inline zval *_http_request_option_cache_ex(http_request *r, char *key, size_t keylen, ulong h, zval *opt TSRMLS_DC) +{ + ZVAL_ADDREF(opt); + + if (h) { + zend_hash_quick_update(&r->_cache.options, key, keylen, h, &opt, sizeof(zval *), NULL); + } else { + zend_hash_update(&r->_cache.options, key, keylen, &opt, sizeof(zval *), NULL); + } + + return opt; +} +/* }}} */ + +/* {{{ static inline int http_request_cookies_enabled(http_request *) */ +static inline int _http_request_cookies_enabled(http_request *request) { + http_request_storage *st; + + if (request->ch && (st = http_request_storage_get(request->ch)) && st->cookiestore) { + /* cookies are enabled */ + return 1; + } + return 0; +} +/* }}} */ + +#endif /* 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 + */ + --- /dev/null +++ b/ext/http/http_request_body_api.c @@ -0,0 +1,332 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_request_body_api.c 292841 2009-12-31 08:48:57Z mike $ */ + +#define HTTP_WANT_CURL +#include "php_http.h" + +#ifdef HTTP_HAVE_CURL + +#include "php_http_api.h" +#include "php_http_url_api.h" +#include "php_http_request_body_api.h" + +/* {{{ */ +typedef struct curl_httppost *post_data[2]; +static STATUS recursive_fields(post_data http_post_data, HashTable *fields, const char *prefix TSRMLS_DC); +static STATUS recursive_files(post_data http_post_data, HashTable *files, const char *prefix TSRMLS_DC); +/* }}} */ + +/* {{{ http_request_body *http_request_body_new() */ +PHP_HTTP_API http_request_body *_http_request_body_init_ex(http_request_body *body, int type, void *data, size_t size, zend_bool free ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC) +{ + if (!body) { + body = emalloc_rel(sizeof(http_request_body)); + } + + body->type = type; + body->free = free; + body->priv = 0; + body->data = data; + body->size = size; + + return body; +} +/* }}} */ + +/* {{{ http_request_body *http_request_body_fill(http_request_body *body, HashTable *, HashTable *) */ +PHP_HTTP_API http_request_body *_http_request_body_fill(http_request_body *body, HashTable *fields, HashTable *files ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC) +{ + if (files && (zend_hash_num_elements(files) > 0)) { + struct curl_httppost *http_post_data[2] = {NULL, NULL}; + + if (fields && SUCCESS != recursive_fields(http_post_data, fields, NULL TSRMLS_CC)) { + return NULL; + } + if (SUCCESS != recursive_files(http_post_data, files, NULL TSRMLS_CC)) { + return NULL; + } + return http_request_body_init_rel(body, HTTP_REQUEST_BODY_CURLPOST, http_post_data[0], 0, 1); + } else if (fields) { + char *encoded; + size_t encoded_len; + + if (SUCCESS != http_urlencode_hash_ex(fields, 1, NULL, 0, &encoded, &encoded_len)) { + http_error(HE_WARNING, HTTP_E_ENCODING, "Could not encode post data"); + return NULL; + } + + return http_request_body_init_rel(body, HTTP_REQUEST_BODY_CSTRING, encoded, encoded_len, 1); + } else { + return http_request_body_init_rel(body, HTTP_REQUEST_BODY_CSTRING, estrndup("", 0), 0, 1); + } +} + +/* STATUS http_request_body_encode(http_request_body *, char**, size_t *) */ +PHP_HTTP_API STATUS _http_request_body_encode(http_request_body *body, char **buf, size_t *len TSRMLS_DC) +{ + switch (body->type) { + case HTTP_REQUEST_BODY_CURLPOST: + { +#ifdef HAVE_CURL_FORMGET + phpstr str; + + phpstr_init_ex(&str, 0x8000, 0); + if (curl_formget(body->data, &str, (curl_formget_callback) phpstr_append)) { + phpstr_dtor(&str); + } else { + phpstr_fix(&str); + *buf = PHPSTR_VAL(&str); + *len = PHPSTR_LEN(&str); + return SUCCESS; + } +#endif + break; + } + + case HTTP_REQUEST_BODY_CSTRING: + *buf = estrndup(body->data, *len = body->size); + return SUCCESS; + + default: + break; + } + return FAILURE; +} +/* }}} */ + +/* {{{ void http_request_body_dtor(http_request_body *) */ +PHP_HTTP_API void _http_request_body_dtor(http_request_body *body TSRMLS_DC) +{ + if (body) { + if (body->free) { + switch (body->type) { + case HTTP_REQUEST_BODY_CSTRING: + if (body->data) { + efree(body->data); + } + break; + + case HTTP_REQUEST_BODY_CURLPOST: + curl_formfree(body->data); + break; + + case HTTP_REQUEST_BODY_UPLOADFILE: + php_stream_close(body->data); + break; + } + } + memset(body, 0, sizeof(http_request_body)); + } +} +/* }}} */ + +/* {{{ void http_request_body_free(http_request_body *) */ +PHP_HTTP_API void _http_request_body_free(http_request_body **body TSRMLS_DC) +{ + if (*body) { + http_request_body_dtor(*body); + efree(*body); + *body = NULL; + } +} +/* }}} */ + +static inline char *format_key(uint type, char *str, ulong num, const char *prefix, int numeric_key_for_empty_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 if (numeric_key_for_empty_prefix) { + spprintf(&new_key, 0, "%lu", num); + } + + return new_key; +} + +/* {{{ static STATUS recursive_fields(post_data d, HashTable *f, const char *p TSRMLS_DC) */ +static STATUS recursive_fields(post_data http_post_data, HashTable *fields, const char *prefix TSRMLS_DC) { + HashKey key = initHashKey(0); + zval **data_ptr; + HashPosition pos; + char *new_key = NULL; + CURLcode err = 0; + + if (fields && !fields->nApplyCount) { + FOREACH_HASH_KEYVAL(pos, fields, key, data_ptr) { + if (key.type != HASH_KEY_IS_STRING || *key.str) { + new_key = format_key(key.type, key.str, key.num, prefix, 1); + + switch (Z_TYPE_PP(data_ptr)) { + case IS_ARRAY: + case IS_OBJECT: { + STATUS status; + + ++fields->nApplyCount; + status = recursive_fields(http_post_data, HASH_OF(*data_ptr), new_key TSRMLS_CC); + --fields->nApplyCount; + + if (SUCCESS != status) { + goto error; + } + break; + } + + default: { + zval *data = http_zsep(IS_STRING, *data_ptr); + + err = curl_formadd(&http_post_data[0], &http_post_data[1], + CURLFORM_COPYNAME, new_key, + CURLFORM_COPYCONTENTS, Z_STRVAL_P(data), + CURLFORM_CONTENTSLENGTH, (long) Z_STRLEN_P(data), + CURLFORM_END + ); + + zval_ptr_dtor(&data); + + if (CURLE_OK != err) { + goto error; + } + break; + } + } + STR_FREE(new_key); + } + } + } + + return SUCCESS; + +error: + if (new_key) { + efree(new_key); + } + if (http_post_data[0]) { + curl_formfree(http_post_data[0]); + } + if (err) { + http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not encode post fields: %s", curl_easy_strerror(err)); + } else { + http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not encode post fields: unknown error"); + } + return FAILURE; +} +/* }}} */ + +/* {{{ static STATUS recursive_files(post_data d, HashTable *f, const char *p TSRMLS_DC) */ +static STATUS recursive_files(post_data http_post_data, HashTable *files, const char *prefix TSRMLS_DC) { + HashKey key = initHashKey(0); + zval **data_ptr; + HashPosition pos; + char *new_key = NULL; + CURLcode err = 0; + + if (files && !files->nApplyCount) { + FOREACH_HASH_KEYVAL(pos, files, key, data_ptr) { + zval **file_ptr, **type_ptr, **name_ptr; + + if (key.type != HASH_KEY_IS_STRING || *key.str) { + new_key = format_key(key.type, key.str, key.num, prefix, 0); + + if (Z_TYPE_PP(data_ptr) != IS_ARRAY && Z_TYPE_PP(data_ptr) != IS_OBJECT) { + if (new_key || key.type == HASH_KEY_IS_STRING) { + http_error_ex(HE_NOTICE, HTTP_E_INVALID_PARAM, "Unrecognized type of post file array entry '%s'", new_key ? new_key : key.str); + } else { + http_error_ex(HE_NOTICE, HTTP_E_INVALID_PARAM, "Unrecognized type of post file array entry '%lu'", key.num); + } + } else if ( SUCCESS != zend_hash_find(HASH_OF(*data_ptr), "name", sizeof("name"), (void *) &name_ptr) || + SUCCESS != zend_hash_find(HASH_OF(*data_ptr), "type", sizeof("type"), (void *) &type_ptr) || + SUCCESS != zend_hash_find(HASH_OF(*data_ptr), "file", sizeof("file"), (void *) &file_ptr)) { + STATUS status; + + ++files->nApplyCount; + status = recursive_files(http_post_data, HASH_OF(*data_ptr), new_key TSRMLS_CC); + --files->nApplyCount; + + if (SUCCESS != status) { + goto error; + } + } else { + const char *path; + zval *file = http_zsep(IS_STRING, *file_ptr); + zval *type = http_zsep(IS_STRING, *type_ptr); + zval *name = http_zsep(IS_STRING, *name_ptr); + + HTTP_CHECK_OPEN_BASEDIR(Z_STRVAL_P(file), goto error); + + /* this is blatant but should be sufficient for most cases */ + if (strncasecmp(Z_STRVAL_P(file), "file://", lenof("file://"))) { + path = Z_STRVAL_P(file); + } else { + path = Z_STRVAL_P(file) + lenof("file://"); + } + + if (new_key) { + char *tmp_key = format_key(HASH_KEY_IS_STRING, Z_STRVAL_P(name), 0, new_key, 0); + STR_SET(new_key, tmp_key); + } + + err = curl_formadd(&http_post_data[0], &http_post_data[1], + CURLFORM_COPYNAME, new_key ? new_key : Z_STRVAL_P(name), + CURLFORM_FILE, path, + CURLFORM_CONTENTTYPE, Z_STRVAL_P(type), + CURLFORM_END + ); + + zval_ptr_dtor(&file); + zval_ptr_dtor(&type); + zval_ptr_dtor(&name); + + if (CURLE_OK != err) { + goto error; + } + } + STR_FREE(new_key); + } + } + } + + return SUCCESS; + +error: + if (new_key) { + efree(new_key); + } + if (http_post_data[0]) { + curl_formfree(http_post_data[0]); + } + if (err) { + http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not encode post files: %s", curl_easy_strerror(err)); + } else { + http_error(HE_WARNING, HTTP_E_ENCODING, "Could not encode post files: unknown error"); + } + return FAILURE; +} +/* }}} */ + +#endif /* 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 + */ --- /dev/null +++ b/ext/http/http_request_datashare_api.c @@ -0,0 +1,288 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_request_datashare_api.c 292841 2009-12-31 08:48:57Z mike $ */ + +#define HTTP_WANT_CURL +#include "php_http.h" + +#if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL) + +#include "php_http_api.h" +#include "php_http_persistent_handle_api.h" +#include "php_http_request_datashare_api.h" +#include "php_http_request_api.h" +#include "php_http_request_object.h" + +static HashTable http_request_datashare_options; +static http_request_datashare http_request_datashare_global; +static int http_request_datashare_compare_handles(void *h1, void *h2); +static void http_request_datashare_destroy_handles(void *el); +#ifdef ZTS +static void *http_request_datashare_locks_init(void); +static void http_request_datashare_locks_dtor(void *l); +static void http_request_datashare_lock_func(CURL *handle, curl_lock_data data, curl_lock_access locktype, void *userptr); +static void http_request_datashare_unlock_func(CURL *handle, curl_lock_data data, void *userptr); +#endif + +http_request_datashare *_http_request_datashare_global_get(void) +{ + return &http_request_datashare_global; +} + +PHP_MINIT_FUNCTION(http_request_datashare) +{ + curl_lock_data val; + + if (SUCCESS != http_persistent_handle_provide("http_request_datashare", curl_share_init, (http_persistent_handle_dtor) curl_share_cleanup, NULL)) { + return FAILURE; + } +#ifdef ZTS + if (SUCCESS != http_persistent_handle_provide("http_request_datashare_lock", http_request_datashare_locks_init, http_request_datashare_locks_dtor, NULL)) { + return FAILURE; + } +#endif + + if (!http_request_datashare_init_ex(&http_request_datashare_global, 1)) { + return FAILURE; + } + + zend_hash_init(&http_request_datashare_options, 4, NULL, NULL, 1); +#define ADD_DATASHARE_OPT(name, opt) \ + val = opt; \ + zend_hash_add(&http_request_datashare_options, name, sizeof(name), &val, sizeof(curl_lock_data), NULL) + ADD_DATASHARE_OPT("cookie", CURL_LOCK_DATA_COOKIE); + ADD_DATASHARE_OPT("dns", CURL_LOCK_DATA_DNS); + ADD_DATASHARE_OPT("ssl", CURL_LOCK_DATA_SSL_SESSION); + ADD_DATASHARE_OPT("connect", CURL_LOCK_DATA_CONNECT); + + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(http_request_datashare) +{ + http_request_datashare_dtor(&http_request_datashare_global); + zend_hash_destroy(&http_request_datashare_options); + + return SUCCESS; +} + +PHP_RINIT_FUNCTION(http_request_datashare) +{ + zend_llist_init(&HTTP_G->request.datashare.handles, sizeof(zval *), http_request_datashare_destroy_handles, 0); + + return SUCCESS; +} + +PHP_RSHUTDOWN_FUNCTION(http_request_datashare) +{ + zend_llist_destroy(&HTTP_G->request.datashare.handles); + + return SUCCESS; +} + +PHP_HTTP_API http_request_datashare *_http_request_datashare_init_ex(http_request_datashare *share, zend_bool persistent TSRMLS_DC) +{ + zend_bool free_share; + + if ((free_share = !share)) { + share = pemalloc(sizeof(http_request_datashare), persistent); + } + memset(share, 0, sizeof(http_request_datashare)); + + if (SUCCESS != http_persistent_handle_acquire("http_request_datashare", &share->ch)) { + if (free_share) { + pefree(share, persistent); + } + return NULL; + } + + if (!(share->persistent = persistent)) { + share->handle.list = emalloc(sizeof(zend_llist)); + zend_llist_init(share->handle.list, sizeof(zval *), ZVAL_PTR_DTOR, 0); +#ifdef ZTS + } else { + if (SUCCESS == http_persistent_handle_acquire("http_request_datashare_lock", (void *) &share->handle.locks)) { + curl_share_setopt(share->ch, CURLSHOPT_LOCKFUNC, http_request_datashare_lock_func); + curl_share_setopt(share->ch, CURLSHOPT_UNLOCKFUNC, http_request_datashare_unlock_func); + curl_share_setopt(share->ch, CURLSHOPT_USERDATA, share->handle.locks); + } +#endif + } + + return share; +} + +PHP_HTTP_API STATUS _http_request_datashare_attach(http_request_datashare *share, zval *request TSRMLS_DC) +{ + CURLcode rc; + getObjectEx(http_request_object, obj, request); + + if (obj->share) { + if (obj->share == share) { + return SUCCESS; + } else if (SUCCESS != http_request_datashare_detach(obj->share, request)) { + return FAILURE; + } + } + + HTTP_CHECK_CURL_INIT(obj->request->ch, http_curl_init_ex(obj->request->ch, obj->request), return FAILURE); + if (CURLE_OK != (rc = curl_easy_setopt(obj->request->ch, CURLOPT_SHARE, share->ch))) { + http_error_ex(HE_WARNING, HTTP_E_REQUEST, "Could not attach HttpRequest object(#%d) to the HttpRequestDataShare: %s", Z_OBJ_HANDLE_P(request), curl_easy_strerror(rc)); + return FAILURE; + } + + obj->share = share; + ZVAL_ADDREF(request); + zend_llist_add_element(HTTP_RSHARE_HANDLES(share), (void *) &request); + + return SUCCESS; +} + +PHP_HTTP_API STATUS _http_request_datashare_detach(http_request_datashare *share, zval *request TSRMLS_DC) +{ + CURLcode rc; + getObjectEx(http_request_object, obj, request); + + if (!obj->share) { + http_error_ex(HE_WARNING, HTTP_E_REQUEST, "HttpRequest object(#%d) is not attached to any HttpRequestDataShare", Z_OBJ_HANDLE_P(request)); + } else if (obj->share != share) { + http_error_ex(HE_WARNING, HTTP_E_REQUEST, "HttpRequest object(#%d) is not attached to this HttpRequestDataShare", Z_OBJ_HANDLE_P(request)); + } else if (CURLE_OK != (rc = curl_easy_setopt(obj->request->ch, CURLOPT_SHARE, NULL))) { + http_error_ex(HE_WARNING, HTTP_E_REQUEST, "Could not detach HttpRequest object(#%d) from the HttpRequestDataShare: %s", Z_OBJ_HANDLE_P(request), curl_share_strerror(rc)); + } else { + obj->share = NULL; + zend_llist_del_element(HTTP_RSHARE_HANDLES(share), request, http_request_datashare_compare_handles); + return SUCCESS; + } + return FAILURE; +} + +PHP_HTTP_API void _http_request_datashare_detach_all(http_request_datashare *share TSRMLS_DC) +{ + zval **r; + + while ((r = zend_llist_get_first(HTTP_RSHARE_HANDLES(share)))) { + http_request_datashare_detach(share, *r); + } +} + +PHP_HTTP_API void _http_request_datashare_dtor(http_request_datashare *share TSRMLS_DC) +{ + if (!share->persistent) { + zend_llist_destroy(share->handle.list); + efree(share->handle.list); + } + http_persistent_handle_release("http_request_datashare", &share->ch); +#ifdef ZTS + if (share->persistent) { + http_persistent_handle_release("http_request_datashare_lock", (void *) &share->handle.locks); + } +#endif +} + +PHP_HTTP_API void _http_request_datashare_free(http_request_datashare **share TSRMLS_DC) +{ + http_request_datashare_dtor(*share); + pefree(*share, (*share)->persistent); + *share = NULL; +} + +PHP_HTTP_API STATUS _http_request_datashare_set(http_request_datashare *share, const char *option, size_t option_len, zend_bool enable TSRMLS_DC) +{ + curl_lock_data *opt; + CURLSHcode rc; + + if (SUCCESS == zend_hash_find(&http_request_datashare_options, (char *) option, option_len + 1, (void *) &opt)) { + if (CURLSHE_OK == (rc = curl_share_setopt(share->ch, enable ? CURLSHOPT_SHARE : CURLSHOPT_UNSHARE, *opt))) { + return SUCCESS; + } + http_error_ex(HE_WARNING, HTTP_E_REQUEST, "Could not %s sharing of %s data: %s", enable ? "enable" : "disable", option, curl_share_strerror(rc)); + } + return FAILURE; +} + +static int http_request_datashare_compare_handles(void *h1, void *h2) +{ + return (Z_OBJ_HANDLE_PP((zval **) h1) == Z_OBJ_HANDLE_P((zval *) h2)); +} + +static void http_request_datashare_destroy_handles(void *el) +{ + zval **r = (zval **) el; + TSRMLS_FETCH(); + + { /* gcc 2.95 needs these braces */ + getObjectEx(http_request_object, obj, *r); + + curl_easy_setopt(obj->request->ch, CURLOPT_SHARE, NULL); + zval_ptr_dtor(r); + } +} + +#ifdef ZTS +static void *http_request_datashare_locks_init(void) +{ + int i; + http_request_datashare_lock *locks = pecalloc(CURL_LOCK_DATA_LAST, sizeof(http_request_datashare_lock), 1); + + if (locks) { + for (i = 0; i < CURL_LOCK_DATA_LAST; ++i) { + locks[i].mx = tsrm_mutex_alloc(); + } + } + + return locks; +} + +static void http_request_datashare_locks_dtor(void *l) +{ + int i; + http_request_datashare_lock *locks = (http_request_datashare_lock *) l; + + for (i = 0; i < CURL_LOCK_DATA_LAST; ++i) { + tsrm_mutex_free(locks[i].mx); + } + pefree(locks, 1); +} + +static void http_request_datashare_lock_func(CURL *handle, curl_lock_data data, curl_lock_access locktype, void *userptr) +{ + http_request_datashare_lock *locks = (http_request_datashare_lock *) userptr; + + /* TSRM can't distinguish shared/exclusive locks */ + tsrm_mutex_lock(locks[data].mx); + locks[data].ch = handle; +} + +static void http_request_datashare_unlock_func(CURL *handle, curl_lock_data data, void *userptr) +{ + http_request_datashare_lock *locks = (http_request_datashare_lock *) userptr; + + if (locks[data].ch == handle) { + tsrm_mutex_unlock(locks[data].mx); + } +} +#endif + +#endif /* ZEND_ENGINE_2 && 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 + */ + --- /dev/null +++ b/ext/http/http_request_info.c @@ -0,0 +1,198 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_request_info.c 293136 2010-01-05 08:48:52Z mike $ */ + +#define HTTP_WANT_CURL +#include "php_http.h" + +#ifdef HTTP_HAVE_CURL +#include "php_http_request_api.h" + +/* {{{ void http_request_info(http_request *, HashTable *) */ +PHP_HTTP_API void _http_request_info(http_request *request, HashTable *info) +{ + char *c; + long l; + double d; + struct curl_slist *s, *p; + zval *subarray, array; + INIT_ZARR(array, info); + + /* BEGIN */ + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_EFFECTIVE_URL, &c)) { + add_assoc_string_ex(&array, "effective_url", sizeof("effective_url"), c ? c : "", 1); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_RESPONSE_CODE, &l)) { + add_assoc_long_ex(&array, "response_code", sizeof("response_code"), l); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_TOTAL_TIME, &d)) { + add_assoc_double_ex(&array, "total_time", sizeof("total_time"), d); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_NAMELOOKUP_TIME, &d)) { + add_assoc_double_ex(&array, "namelookup_time", sizeof("namelookup_time"), d); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_CONNECT_TIME, &d)) { + add_assoc_double_ex(&array, "connect_time", sizeof("connect_time"), d); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_PRETRANSFER_TIME, &d)) { + add_assoc_double_ex(&array, "pretransfer_time", sizeof("pretransfer_time"), d); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_SIZE_UPLOAD, &d)) { + add_assoc_double_ex(&array, "size_upload", sizeof("size_upload"), d); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_SIZE_DOWNLOAD, &d)) { + add_assoc_double_ex(&array, "size_download", sizeof("size_download"), d); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_SPEED_DOWNLOAD, &d)) { + add_assoc_double_ex(&array, "speed_download", sizeof("speed_download"), d); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_SPEED_UPLOAD, &d)) { + add_assoc_double_ex(&array, "speed_upload", sizeof("speed_upload"), d); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_HEADER_SIZE, &l)) { + add_assoc_long_ex(&array, "header_size", sizeof("header_size"), l); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_REQUEST_SIZE, &l)) { + add_assoc_long_ex(&array, "request_size", sizeof("request_size"), l); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_SSL_VERIFYRESULT, &l)) { + add_assoc_long_ex(&array, "ssl_verifyresult", sizeof("ssl_verifyresult"), l); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_FILETIME, &l)) { + add_assoc_long_ex(&array, "filetime", sizeof("filetime"), l); + } + if (CURLE_OK == curl_easy_getinfo(request->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(request->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(request->ch, CURLINFO_STARTTRANSFER_TIME, &d)) { + add_assoc_double_ex(&array, "starttransfer_time", sizeof("starttransfer_time"), d); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_CONTENT_TYPE, &c)) { + add_assoc_string_ex(&array, "content_type", sizeof("content_type"), c ? c : "", 1); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_REDIRECT_TIME, &d)) { + add_assoc_double_ex(&array, "redirect_time", sizeof("redirect_time"), d); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_REDIRECT_COUNT, &l)) { + add_assoc_long_ex(&array, "redirect_count", sizeof("redirect_count"), l); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_HTTP_CONNECTCODE, &l)) { + add_assoc_long_ex(&array, "connect_code", sizeof("connect_code"), l); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_HTTPAUTH_AVAIL, &l)) { + add_assoc_long_ex(&array, "httpauth_avail", sizeof("httpauth_avail"), l); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_PROXYAUTH_AVAIL, &l)) { + add_assoc_long_ex(&array, "proxyauth_avail", sizeof("proxyauth_avail"), l); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_OS_ERRNO, &l)) { + add_assoc_long_ex(&array, "os_errno", sizeof("os_errno"), l); + } + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_NUM_CONNECTS, &l)) { + add_assoc_long_ex(&array, "num_connects", sizeof("num_connects"), l); + } + if (CURLE_OK == curl_easy_getinfo(request->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 HTTP_CURL_VERSION(7,14,1) + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_COOKIELIST, &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, "cookies", sizeof("cookies"), subarray); + curl_slist_free_all(s); + } +#endif +#if HTTP_CURL_VERSION(7,18,2) + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_REDIRECT_URL, &c)) { + add_assoc_string_ex(&array, "redirect_url", sizeof("redirect_url"), c ? c : "", 1); + } +#endif +#if HTTP_CURL_VERSION(7,19,0) + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_PRIMARY_IP, &c)) { + add_assoc_string_ex(&array, "primary_ip", sizeof("primary_ip"), c ? c : "", 1); + } +#endif +#if HTTP_CURL_VERSION(7,19,0) + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_APPCONNECT_TIME, &d)) { + add_assoc_double_ex(&array, "appconnect_time", sizeof("appconnect_time"), d); + } +#endif +#if HTTP_CURL_VERSION(7,19,4) + if (CURLE_OK == curl_easy_getinfo(request->ch, CURLINFO_CONDITION_UNMET, &l)) { + add_assoc_long_ex(&array, "condition_unmet", sizeof("condition_unmet"), l); + } +#endif +/* END */ +#if HTTP_CURL_VERSION(7,19,1) && defined(HTTP_HAVE_OPENSSL) + { + int i; + zval *ci_array; + struct curl_certinfo *ci; + char *colon, *keyname; + + if (CURLE_OK == curl_easy_getinfo(request->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 + add_assoc_string_ex(&array, "error", sizeof("error"), http_request_storage_get(request->ch)->errorbuffer, 1); +} +/* }}} */ + +#endif /* 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 + */ --- /dev/null +++ b/ext/http/http_request_method_api.c @@ -0,0 +1,321 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_request_method_api.c 292841 2009-12-31 08:48:57Z mike $ */ + +#define HTTP_WANT_CURL +#include "php_http.h" + +#include "php_http_api.h" +#include "php_http_request_api.h" +#include "php_http_request_method_api.h" + +#if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL) && !defined(WONKY) +# include "php_http_request_object.h" +#endif + +/* {{{ char *http_request_methods[] */ +static const char *const http_request_methods[] = { + "UNKNOWN", + /* HTTP/1.1 */ + "GET", + "HEAD", + "POST", + "PUT", + "DELETE", + "OPTIONS", + "TRACE", + "CONNECT", + /* WebDAV - RFC 2518 */ + "PROPFIND", + "PROPPATCH", + "MKCOL", + "COPY", + "MOVE", + "LOCK", + "UNLOCK", + /* WebDAV Versioning - RFC 3253 */ + "VERSION-CONTROL", + "REPORT", + "CHECKOUT", + "CHECKIN", + "UNCHECKOUT", + "MKWORKSPACE", + "UPDATE", + "LABEL", + "MERGE", + "BASELINE-CONTROL", + "MKACTIVITY", + /* WebDAV Access Control - RFC 3744 */ + "ACL", + NULL +}; +/* }}} */ + +/* {{{ */ +PHP_MINIT_FUNCTION(http_request_method) +{ + /* HTTP/1.1 */ + HTTP_LONG_CONSTANT("HTTP_METH_GET", HTTP_GET); + HTTP_LONG_CONSTANT("HTTP_METH_HEAD", HTTP_HEAD); + HTTP_LONG_CONSTANT("HTTP_METH_POST", HTTP_POST); + HTTP_LONG_CONSTANT("HTTP_METH_PUT", HTTP_PUT); + HTTP_LONG_CONSTANT("HTTP_METH_DELETE", HTTP_DELETE); + HTTP_LONG_CONSTANT("HTTP_METH_OPTIONS", HTTP_OPTIONS); + HTTP_LONG_CONSTANT("HTTP_METH_TRACE", HTTP_TRACE); + HTTP_LONG_CONSTANT("HTTP_METH_CONNECT", HTTP_CONNECT); + /* WebDAV - RFC 2518 */ + HTTP_LONG_CONSTANT("HTTP_METH_PROPFIND", HTTP_PROPFIND); + HTTP_LONG_CONSTANT("HTTP_METH_PROPPATCH", HTTP_PROPPATCH); + HTTP_LONG_CONSTANT("HTTP_METH_MKCOL", HTTP_MKCOL); + HTTP_LONG_CONSTANT("HTTP_METH_COPY", HTTP_COPY); + HTTP_LONG_CONSTANT("HTTP_METH_MOVE", HTTP_MOVE); + HTTP_LONG_CONSTANT("HTTP_METH_LOCK", HTTP_LOCK); + HTTP_LONG_CONSTANT("HTTP_METH_UNLOCK", HTTP_UNLOCK); + /* WebDAV Versioning - RFC 3253 */ + HTTP_LONG_CONSTANT("HTTP_METH_VERSION_CONTROL", HTTP_VERSION_CONTROL); + HTTP_LONG_CONSTANT("HTTP_METH_REPORT", HTTP_REPORT); + HTTP_LONG_CONSTANT("HTTP_METH_CHECKOUT", HTTP_CHECKOUT); + HTTP_LONG_CONSTANT("HTTP_METH_CHECKIN", HTTP_CHECKIN); + HTTP_LONG_CONSTANT("HTTP_METH_UNCHECKOUT", HTTP_UNCHECKOUT); + HTTP_LONG_CONSTANT("HTTP_METH_MKWORKSPACE", HTTP_MKWORKSPACE); + HTTP_LONG_CONSTANT("HTTP_METH_UPDATE", HTTP_UPDATE); + HTTP_LONG_CONSTANT("HTTP_METH_LABEL", HTTP_LABEL); + HTTP_LONG_CONSTANT("HTTP_METH_MERGE", HTTP_MERGE); + HTTP_LONG_CONSTANT("HTTP_METH_BASELINE_CONTROL", HTTP_BASELINE_CONTROL); + HTTP_LONG_CONSTANT("HTTP_METH_MKACTIVITY", HTTP_MKACTIVITY); + /* WebDAV Access Control - RFC 3744 */ + HTTP_LONG_CONSTANT("HTTP_METH_ACL", HTTP_ACL); + + return SUCCESS; +} + +static void free_method(void *el) +{ + efree(*(char **)el); +} + +static void unregister_method(const char *name TSRMLS_DC) +{ + char *ptr, tmp[sizeof("HTTP_METH_") + HTTP_REQUEST_METHOD_MAXLEN] = "HTTP_METH_"; + + strlcpy(tmp + lenof("HTTP_METH_"), name, HTTP_REQUEST_METHOD_MAXLEN); + for (ptr = tmp + lenof("HTTP_METH_"); *ptr; ++ptr) { + if (*ptr == '-') { + *ptr = '_'; + } + } + +#if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL) && !defined(WONKY) + if (SUCCESS != zend_hash_del(&http_request_object_ce->constants_table, tmp + lenof("HTTP_"), strlen(tmp + lenof("HTTP_")) + 1)) { + http_error_ex(HE_NOTICE, HTTP_E_REQUEST_METHOD, "Could not unregister request method: HttpRequest::%s", tmp + lenof("HTTP_")); + } +#endif + if (SUCCESS != zend_hash_del(EG(zend_constants), tmp, strlen(tmp) + 1)) { + http_error_ex(HE_NOTICE, HTTP_E_REQUEST_METHOD, "Could not unregister request method: %s", tmp); + } +} + +PHP_RINIT_FUNCTION(http_request_method) +{ + HashTable ht; + + zend_hash_init(&HTTP_G->request.methods.registered, 0, NULL, free_method, 0); +#define HTTP_METH_REG(m) \ + { \ + char *_m=estrdup(m); \ + zend_hash_next_index_insert(&HTTP_G->request.methods.registered, (void *) &_m, sizeof(char *), NULL); \ + } + HTTP_METH_REG("UNKNOWN"); + /* HTTP/1.1 */ + HTTP_METH_REG("GET"); + HTTP_METH_REG("HEAD"); + HTTP_METH_REG("POST"); + HTTP_METH_REG("PUT"); + HTTP_METH_REG("DELETE"); + HTTP_METH_REG("OPTIONS"); + HTTP_METH_REG("TRACE"); + HTTP_METH_REG("CONNECT"); + /* WebDAV - RFC 2518 */ + HTTP_METH_REG("PROPFIND"); + HTTP_METH_REG("PROPPATCH"); + HTTP_METH_REG("MKCOL"); + HTTP_METH_REG("COPY"); + HTTP_METH_REG("MOVE"); + HTTP_METH_REG("LOCK"); + HTTP_METH_REG("UNLOCK"); + /* WebDAV Versioning - RFC 3253 */ + HTTP_METH_REG("VERSION-CONTROL"); + HTTP_METH_REG("REPORT"); + HTTP_METH_REG("CHECKOUT"); + HTTP_METH_REG("CHECKIN"); + HTTP_METH_REG("UNCHECKOUT"); + HTTP_METH_REG("MKWORKSPACE"); + HTTP_METH_REG("UPDATE"); + HTTP_METH_REG("LABEL"); + HTTP_METH_REG("MERGE"); + HTTP_METH_REG("BASELINE-CONTROL"); + HTTP_METH_REG("MKACTIVITY"); + /* WebDAV Access Control - RFC 3744 */ + HTTP_METH_REG("ACL"); + + zend_hash_init(&ht, 0, NULL, ZVAL_PTR_DTOR, 0); + if (*HTTP_G->request.methods.custom && SUCCESS == http_parse_params(HTTP_G->request.methods.custom, HTTP_PARAMS_DEFAULT, &ht)) { + HashPosition pos; + zval **val; + + FOREACH_HASH_VAL(pos, &ht, val) { + if (Z_TYPE_PP(val) == IS_STRING) { + http_request_method_register(Z_STRVAL_PP(val), Z_STRLEN_PP(val)); + } + } + } + zend_hash_destroy(&ht); + + return SUCCESS; +} + +PHP_RSHUTDOWN_FUNCTION(http_request_method) +{ + char **name; + int i, c = zend_hash_next_free_element(&HTTP_G->request.methods.registered); + + for (i = HTTP_MAX_REQUEST_METHOD; i < c; ++i) { + if (SUCCESS == zend_hash_index_find(&HTTP_G->request.methods.registered, i, (void *) &name)) { + unregister_method(*name TSRMLS_CC); + } + } + + zend_hash_destroy(&HTTP_G->request.methods.registered); + return SUCCESS; +} + +#define http_request_method_cncl(m, c) _http_request_method_cncl_ex((m), strlen(m), (c) TSRMLS_CC) +#define http_request_method_cncl_ex(m, l, c) _http_request_method_cncl_ex((m), (l), (c) TSRMLS_CC) +static STATUS _http_request_method_cncl_ex(const char *method_name, int method_name_len, char **cnst TSRMLS_DC) +{ + int i; + char *cncl; + + if (method_name_len >= HTTP_REQUEST_METHOD_MAXLEN) { + http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Request method too long (%s)", method_name); + } + cncl = emalloc(method_name_len + 1); + + for (i = 0; i < method_name_len; ++i) { + switch (method_name[i]) { + case '-': + cncl[i] = '-'; + break; + + default: + if (!HTTP_IS_CTYPE(alnum, method_name[i])) { + efree(cncl); + http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Request method contains illegal characters (%s)", method_name); + return FAILURE; + } + cncl[i] = HTTP_TO_CTYPE(upper, method_name[i]); + break; + } + } + cncl[method_name_len] = '\0'; + + *cnst = cncl; + return SUCCESS; +} + +PHP_HTTP_API const char *_http_request_method_name(http_request_method m TSRMLS_DC) +{ + char **name; + + if (SUCCESS == zend_hash_index_find(&HTTP_G->request.methods.registered, m, (void *) &name)) { + return *name; + } + return "UNKNOWN"; +} + +PHP_HTTP_API int _http_request_method_exists(int by_name, http_request_method id_num, const char *id_str TSRMLS_DC) +{ + char *id_dup; + + if (by_name && (SUCCESS == http_request_method_cncl(id_str, &id_dup))) { + char **name; + HashPosition pos; + HashKey key = initHashKey(0); + + FOREACH_HASH_KEYVAL(pos, &HTTP_G->request.methods.registered, key, name) { + if (key.type == HASH_KEY_IS_LONG && !strcmp(*name, id_dup)) { + efree(id_dup); + return key.num; + } + } + efree(id_dup); + } else if (zend_hash_index_exists(&HTTP_G->request.methods.registered, id_num)){ + return id_num; + } + return 0; +} + +PHP_HTTP_API int _http_request_method_register(const char *method_str, int method_len TSRMLS_DC) +{ + char *method_dup, *ptr, tmp[sizeof("HTTP_METH_") + HTTP_REQUEST_METHOD_MAXLEN] = "HTTP_METH_"; + int method_num = http_request_method_exists(1, 0, method_str); + + if (!method_num && (SUCCESS == http_request_method_cncl_ex(method_str, method_len, &method_dup))) { + method_num = zend_hash_next_free_element(&HTTP_G->request.methods.registered); + zend_hash_index_update(&HTTP_G->request.methods.registered, method_num, (void *) &method_dup, sizeof(char *), NULL); + + strlcpy(tmp + lenof("HTTP_METH_"), method_dup, HTTP_REQUEST_METHOD_MAXLEN); + for (ptr = tmp + lenof("HTTP_METH_"); *ptr; ++ptr) { + if (*ptr == '-') { + *ptr = '_'; + } + } + + zend_register_long_constant(tmp, strlen(tmp) + 1, method_num, CONST_CS, http_module_number TSRMLS_CC); +#if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL) && !defined(WONKY) + zend_declare_class_constant_long(http_request_object_ce, tmp + lenof("HTTP_"), strlen(tmp + lenof("HTTP_")), method_num TSRMLS_CC); +#endif + } + + return method_num; +} + +PHP_HTTP_API STATUS _http_request_method_unregister(int method TSRMLS_DC) +{ + char **name; + + if (HTTP_STD_REQUEST_METHOD(method)) { + http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Standard request methods cannot be unregistered"); + return FAILURE; + } + + if (SUCCESS != zend_hash_index_find(&HTTP_G->request.methods.registered, method, (void *) &name)) { + http_error_ex(HE_NOTICE, HTTP_E_REQUEST_METHOD, "Custom request method with id %d does not exist", method); + return FAILURE; + } + + unregister_method(*name TSRMLS_CC); + + zend_hash_index_del(&HTTP_G->request.methods.registered, method); + 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 + */ + --- /dev/null +++ b/ext/http/http_request_object.c @@ -0,0 +1,1922 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_request_object.c 300299 2010-06-09 06:23:16Z mike $ */ + +#define HTTP_WANT_CURL +#include "php_http.h" + +#if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL) + +#include "zend_interfaces.h" + +#include "php_http_api.h" +#include "php_http_cookie_api.h" +#include "php_http_exception_object.h" +#include "php_http_message_api.h" +#include "php_http_message_object.h" +#include "php_http_request_api.h" +#include "php_http_request_object.h" +#include "php_http_request_pool_api.h" +#include "php_http_url_api.h" + +#define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpRequest, method, 0, req_args) +#define HTTP_EMPTY_ARGS(method) HTTP_EMPTY_ARGS_EX(HttpRequest, method, 0) +#define HTTP_REQUEST_ME(method, visibility) PHP_ME(HttpRequest, method, HTTP_ARGS(HttpRequest, method), visibility) +#define HTTP_REQUEST_ALIAS(method, func) HTTP_STATIC_ME_ALIAS(method, func, HTTP_ARGS(HttpRequest, method)) +#define HTTP_REQUEST_MALIAS(me, al, vis) ZEND_FENTRY(me, ZEND_MN(HttpRequest_##al), HTTP_ARGS(HttpRequest, al), vis) + +HTTP_BEGIN_ARGS(__construct, 0) + HTTP_ARG_VAL(url, 0) + HTTP_ARG_VAL(method, 0) + HTTP_ARG_VAL(options, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(factory, 0) + HTTP_ARG_VAL(url, 0) + HTTP_ARG_VAL(method, 0) + HTTP_ARG_VAL(options, 0) + HTTP_ARG_VAL(class_name, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getOptions); +HTTP_BEGIN_ARGS(setOptions, 0) + HTTP_ARG_VAL(options, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getSslOptions); +HTTP_BEGIN_ARGS(setSslOptions, 0) + HTTP_ARG_VAL(ssl_options, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(addSslOptions, 0) + HTTP_ARG_VAL(ssl_optins, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getHeaders); +HTTP_BEGIN_ARGS(setHeaders, 0) + HTTP_ARG_VAL(headers, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(addHeaders, 1) + HTTP_ARG_VAL(headers, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getCookies); +HTTP_BEGIN_ARGS(setCookies, 0) + HTTP_ARG_VAL(cookies, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(addCookies, 1) + HTTP_ARG_VAL(cookies, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(enableCookies); +HTTP_BEGIN_ARGS(resetCookies, 0) + HTTP_ARG_VAL(session_only, 0) +HTTP_END_ARGS; +HTTP_EMPTY_ARGS(flushCookies); + +HTTP_EMPTY_ARGS(getUrl); +HTTP_BEGIN_ARGS(setUrl, 1) + HTTP_ARG_VAL(url, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getMethod); +HTTP_BEGIN_ARGS(setMethod, 1) + HTTP_ARG_VAL(request_method, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getContentType); +HTTP_BEGIN_ARGS(setContentType, 1) + HTTP_ARG_VAL(content_type, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getQueryData); +HTTP_BEGIN_ARGS(setQueryData, 0) + HTTP_ARG_VAL(query_data, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(addQueryData, 1) + HTTP_ARG_VAL(query_data, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getPostFields); +HTTP_BEGIN_ARGS(setPostFields, 0) + HTTP_ARG_VAL(post_fields, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(addPostFields, 1) + HTTP_ARG_VAL(post_fields, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getPostFiles); +HTTP_BEGIN_ARGS(setPostFiles, 0) + HTTP_ARG_VAL(post_files, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(addPostFile, 2) + HTTP_ARG_VAL(formname, 0) + HTTP_ARG_VAL(filename, 0) + HTTP_ARG_VAL(content_type, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getBody); +HTTP_BEGIN_ARGS(setBody, 0) + HTTP_ARG_VAL(request_body_data, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(addBody, 1) + HTTP_ARG_VAL(request_body_data, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getPutFile); +HTTP_BEGIN_ARGS(setPutFile, 0) + HTTP_ARG_VAL(filename, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getPutData); +HTTP_BEGIN_ARGS(setPutData, 0) + HTTP_ARG_VAL(put_data, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(addPutData, 1) + HTTP_ARG_VAL(put_data, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getResponseData); +HTTP_BEGIN_ARGS(getResponseHeader, 0) + HTTP_ARG_VAL(name, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(getResponseCookies, 0) + HTTP_ARG_VAL(flags, 0) + HTTP_ARG_VAL(allowed_extras, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getResponseBody); +HTTP_EMPTY_ARGS(getResponseCode); +HTTP_EMPTY_ARGS(getResponseStatus); +HTTP_BEGIN_ARGS(getResponseInfo, 0) + HTTP_ARG_VAL(name, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getMessageClass); +HTTP_BEGIN_ARGS(setMessageClass, 1) + HTTP_ARG_VAL(message_class_name, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getResponseMessage); +HTTP_EMPTY_ARGS(getRawResponseMessage); +HTTP_EMPTY_ARGS(getRequestMessage); +HTTP_EMPTY_ARGS(getRawRequestMessage); +HTTP_EMPTY_ARGS(getHistory); +HTTP_EMPTY_ARGS(clearHistory); +HTTP_EMPTY_ARGS(send); + +HTTP_BEGIN_ARGS(get, 1) + HTTP_ARG_VAL(url, 0) + HTTP_ARG_VAL(options, 0) + HTTP_ARG_VAL(info, 1) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(head, 1) + HTTP_ARG_VAL(url, 0) + HTTP_ARG_VAL(options, 0) + HTTP_ARG_VAL(info, 1) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(postData, 2) + HTTP_ARG_VAL(url, 0) + HTTP_ARG_VAL(data, 0) + HTTP_ARG_VAL(options, 0) + HTTP_ARG_VAL(info, 1) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(postFields, 2) + HTTP_ARG_VAL(url, 0) + HTTP_ARG_VAL(data, 0) + HTTP_ARG_VAL(options, 0) + HTTP_ARG_VAL(info, 1) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(putData, 2) + HTTP_ARG_VAL(url, 0) + HTTP_ARG_VAL(data, 0) + HTTP_ARG_VAL(options, 0) + HTTP_ARG_VAL(info, 1) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(putFile, 2) + HTTP_ARG_VAL(url, 0) + HTTP_ARG_VAL(file, 0) + HTTP_ARG_VAL(options, 0) + HTTP_ARG_VAL(info, 1) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(putStream, 2) + HTTP_ARG_VAL(url, 0) + HTTP_ARG_VAL(stream, 0) + HTTP_ARG_VAL(options, 0) + HTTP_ARG_VAL(info, 1) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(methodRegister, 1) + HTTP_ARG_VAL(method_name, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(methodUnregister, 1) + HTTP_ARG_VAL(method, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(methodName, 1) + HTTP_ARG_VAL(method_id, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(methodExists, 1) + HTTP_ARG_VAL(method, 0) +HTTP_END_ARGS; + +#ifdef HAVE_CURL_FORMGET +HTTP_BEGIN_ARGS(encodeBody, 2) + HTTP_ARG_VAL(fields, 0) + HTTP_ARG_VAL(files, 0) +HTTP_END_ARGS; +#endif + +#define THIS_CE http_request_object_ce +zend_class_entry *http_request_object_ce; +zend_function_entry http_request_object_fe[] = { + HTTP_REQUEST_ME(__construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) + + HTTP_REQUEST_ME(setOptions, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getOptions, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(setSslOptions, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getSslOptions, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(addSslOptions, ZEND_ACC_PUBLIC) + + HTTP_REQUEST_ME(addHeaders, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getHeaders, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(setHeaders, ZEND_ACC_PUBLIC) + + HTTP_REQUEST_ME(addCookies, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getCookies, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(setCookies, ZEND_ACC_PUBLIC) + + HTTP_REQUEST_ME(enableCookies, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(resetCookies, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(flushCookies, ZEND_ACC_PUBLIC) + + HTTP_REQUEST_ME(setMethod, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getMethod, ZEND_ACC_PUBLIC) + + HTTP_REQUEST_ME(setUrl, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getUrl, ZEND_ACC_PUBLIC) + + HTTP_REQUEST_ME(setContentType, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getContentType, ZEND_ACC_PUBLIC) + + HTTP_REQUEST_ME(setQueryData, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getQueryData, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(addQueryData, ZEND_ACC_PUBLIC) + + HTTP_REQUEST_ME(setPostFields, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getPostFields, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(addPostFields, ZEND_ACC_PUBLIC) + + HTTP_REQUEST_ME(setBody, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getBody, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(addBody, ZEND_ACC_PUBLIC) + HTTP_REQUEST_MALIAS(setRawPostData, setBody, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) + HTTP_REQUEST_MALIAS(getRawPostData, getBody, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) + HTTP_REQUEST_MALIAS(addRawPostData, addBody, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) + + HTTP_REQUEST_ME(setPostFiles, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(addPostFile, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getPostFiles, ZEND_ACC_PUBLIC) + + HTTP_REQUEST_ME(setPutFile, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getPutFile, ZEND_ACC_PUBLIC) + + HTTP_REQUEST_ME(setPutData, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getPutData, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(addPutData, ZEND_ACC_PUBLIC) + + HTTP_REQUEST_ME(send, ZEND_ACC_PUBLIC) + + HTTP_REQUEST_ME(getResponseData, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getResponseHeader, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getResponseCookies, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getResponseCode, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getResponseStatus, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getResponseBody, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getResponseInfo, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getResponseMessage, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getRawResponseMessage, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getRequestMessage, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getRawRequestMessage, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getHistory, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(clearHistory, ZEND_ACC_PUBLIC) + + HTTP_REQUEST_ME(getMessageClass, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(setMessageClass, ZEND_ACC_PUBLIC) + + HTTP_REQUEST_ME(factory, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + + HTTP_REQUEST_ALIAS(get, http_get) + HTTP_REQUEST_ALIAS(head, http_head) + HTTP_REQUEST_ALIAS(postData, http_post_data) + HTTP_REQUEST_ALIAS(postFields, http_post_fields) + HTTP_REQUEST_ALIAS(putData, http_put_data) + HTTP_REQUEST_ALIAS(putFile, http_put_file) + HTTP_REQUEST_ALIAS(putStream, http_put_stream) + + HTTP_REQUEST_ALIAS(methodRegister, http_request_method_register) + HTTP_REQUEST_ALIAS(methodUnregister, http_request_method_unregister) + HTTP_REQUEST_ALIAS(methodName, http_request_method_name) + HTTP_REQUEST_ALIAS(methodExists, http_request_method_exists) +#ifdef HAVE_CURL_FORMGET + HTTP_REQUEST_ALIAS(encodeBody, http_request_body_encode) +#endif + EMPTY_FUNCTION_ENTRY +}; +static zend_object_handlers http_request_object_handlers; + +PHP_MINIT_FUNCTION(http_request_object) +{ + HTTP_REGISTER_CLASS_EX(HttpRequest, http_request_object, NULL, 0); + http_request_object_handlers.clone_obj = _http_request_object_clone_obj; + + zend_declare_property_null(THIS_CE, ZEND_STRS("options")-1, ZEND_ACC_PRIVATE TSRMLS_CC); + zend_declare_property_null(THIS_CE, ZEND_STRS("postFields")-1, ZEND_ACC_PRIVATE TSRMLS_CC); + zend_declare_property_null(THIS_CE, ZEND_STRS("postFiles")-1, ZEND_ACC_PRIVATE TSRMLS_CC); + zend_declare_property_null(THIS_CE, ZEND_STRS("responseInfo")-1, ZEND_ACC_PRIVATE TSRMLS_CC); + zend_declare_property_null(THIS_CE, ZEND_STRS("responseMessage")-1, ZEND_ACC_PRIVATE TSRMLS_CC); + zend_declare_property_long(THIS_CE, ZEND_STRS("responseCode")-1, 0, ZEND_ACC_PRIVATE TSRMLS_CC); + zend_declare_property_string(THIS_CE, ZEND_STRS("responseStatus")-1, "", ZEND_ACC_PRIVATE TSRMLS_CC); + zend_declare_property_long(THIS_CE, ZEND_STRS("method")-1, HTTP_GET, ZEND_ACC_PRIVATE TSRMLS_CC); + zend_declare_property_string(THIS_CE, ZEND_STRS("url")-1, "", ZEND_ACC_PRIVATE TSRMLS_CC); + zend_declare_property_string(THIS_CE, ZEND_STRS("contentType")-1, "", ZEND_ACC_PRIVATE TSRMLS_CC); + zend_declare_property_string(THIS_CE, ZEND_STRS("requestBody")-1, "", ZEND_ACC_PRIVATE TSRMLS_CC); + zend_declare_property_string(THIS_CE, ZEND_STRS("queryData")-1, "", ZEND_ACC_PRIVATE TSRMLS_CC); + zend_declare_property_string(THIS_CE, ZEND_STRS("putFile")-1, "", ZEND_ACC_PRIVATE TSRMLS_CC); + zend_declare_property_string(THIS_CE, ZEND_STRS("putData")-1, "", ZEND_ACC_PRIVATE TSRMLS_CC); + zend_declare_property_null(THIS_CE, ZEND_STRS("history")-1, ZEND_ACC_PRIVATE TSRMLS_CC); + zend_declare_property_bool(THIS_CE, ZEND_STRS("recordHistory")-1, 0, ZEND_ACC_PUBLIC TSRMLS_CC); + zend_declare_property_string(THIS_CE, ZEND_STRS("messageClass")-1, "", ZEND_ACC_PRIVATE TSRMLS_CC); + +#ifndef WONKY + /* + * Request Method Constants + */ + /* HTTP/1.1 */ + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_GET")-1, HTTP_GET TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_HEAD")-1, HTTP_HEAD TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_POST")-1, HTTP_POST TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_PUT")-1, HTTP_PUT TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_DELETE")-1, HTTP_DELETE TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_OPTIONS")-1, HTTP_OPTIONS TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_TRACE")-1, HTTP_TRACE TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_CONNECT")-1, HTTP_CONNECT TSRMLS_CC); + /* WebDAV - RFC 2518 */ + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_PROPFIND")-1, HTTP_PROPFIND TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_PROPPATCH")-1, HTTP_PROPPATCH TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_MKCOL")-1, HTTP_MKCOL TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_COPY")-1, HTTP_COPY TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_MOVE")-1, HTTP_MOVE TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_LOCK")-1, HTTP_LOCK TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_UNLOCK")-1, HTTP_UNLOCK TSRMLS_CC); + /* WebDAV Versioning - RFC 3253 */ + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_VERSION_CONTROL")-1, HTTP_VERSION_CONTROL TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_REPORT")-1, HTTP_REPORT TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_CHECKOUT")-1, HTTP_CHECKOUT TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_CHECKIN")-1, HTTP_CHECKIN TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_UNCHECKOUT")-1, HTTP_UNCHECKOUT TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_MKWORKSPACE")-1, HTTP_MKWORKSPACE TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_UPDATE")-1, HTTP_UPDATE TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_LABEL")-1, HTTP_LABEL TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_MERGE")-1, HTTP_MERGE TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_BASELINE_CONTROL")-1, HTTP_BASELINE_CONTROL TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_MKACTIVITY")-1, HTTP_MKACTIVITY TSRMLS_CC); + /* WebDAV Access Control - RFC 3744 */ + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("METH_ACL")-1, HTTP_ACL TSRMLS_CC); + + /* + * HTTP Protocol Version Constants + */ + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("VERSION_1_0")-1, CURL_HTTP_VERSION_1_0 TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("VERSION_1_1")-1, CURL_HTTP_VERSION_1_1 TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("VERSION_NONE")-1, CURL_HTTP_VERSION_NONE TSRMLS_CC); /* to be removed */ + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("VERSION_ANY")-1, CURL_HTTP_VERSION_NONE TSRMLS_CC); + + /* + * SSL Version Constants + */ + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("SSL_VERSION_TLSv1")-1, CURL_SSLVERSION_TLSv1 TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("SSL_VERSION_SSLv2")-1, CURL_SSLVERSION_SSLv2 TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("SSL_VERSION_SSLv3")-1, CURL_SSLVERSION_SSLv3 TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("SSL_VERSION_ANY")-1, CURL_SSLVERSION_DEFAULT TSRMLS_CC); + + /* + * DNS IPvX resolving + */ + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("IPRESOLVE_V4")-1, CURL_IPRESOLVE_V4 TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("IPRESOLVE_V6")-1, CURL_IPRESOLVE_V6 TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("IPRESOLVE_ANY")-1, CURL_IPRESOLVE_WHATEVER TSRMLS_CC); + + /* + * Auth Constants + */ + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("AUTH_BASIC")-1, CURLAUTH_BASIC TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("AUTH_DIGEST")-1, CURLAUTH_DIGEST TSRMLS_CC); +#if HTTP_CURL_VERSION(7,19,3) + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("AUTH_DIGEST_IE")-1, CURLAUTH_DIGEST_IE TSRMLS_CC); +#endif + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("AUTH_NTLM")-1, CURLAUTH_NTLM TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("AUTH_GSSNEG")-1, CURLAUTH_GSSNEGOTIATE TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("AUTH_ANY")-1, CURLAUTH_ANY TSRMLS_CC); + + /* + * Proxy Type Constants + */ +# if HTTP_CURL_VERSION(7,15,2) + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("PROXY_SOCKS4")-1, CURLPROXY_SOCKS4 TSRMLS_CC); +# endif +#if HTTP_CURL_VERSION(7,18,0) + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("PROXY_SOCKS4A")-1, CURLPROXY_SOCKS5 TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("PROXY_SOCKS5_HOSTNAME")-1, CURLPROXY_SOCKS5 TSRMLS_CC); +#endif + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("PROXY_SOCKS5")-1, CURLPROXY_SOCKS5 TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("PROXY_HTTP")-1, CURLPROXY_HTTP TSRMLS_CC); +# if HTTP_CURL_VERSION(7,19,4) + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("PROXY_HTTP_1_0")-1, CURLPROXY_HTTP_1_0 TSRMLS_CC); +# endif +#endif /* WONKY */ + + /* + * Post Redirection Constants + */ +#if HTTP_CURL_VERSION(7,19,1) + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("POSTREDIR_301")-1, CURL_REDIR_POST_301 TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("POSTREDIR_302")-1, CURL_REDIR_POST_302 TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("POSTREDIR_ALL")-1, CURL_REDIR_POST_ALL TSRMLS_CC); +#endif + + return SUCCESS; +} + +zend_object_value _http_request_object_new(zend_class_entry *ce TSRMLS_DC) +{ + return http_request_object_new_ex(ce, NULL, NULL); +} + +zend_object_value _http_request_object_new_ex(zend_class_entry *ce, CURL *ch, http_request_object **ptr TSRMLS_DC) +{ + zend_object_value ov; + http_request_object *o; + + o = ecalloc(1, sizeof(http_request_object)); + o->zo.ce = ce; + o->request = http_request_init_ex(NULL, ch, 0, NULL); + + if (ptr) { + *ptr = o; + } + + ALLOC_HASHTABLE(OBJ_PROP(o)); + zend_hash_init(OBJ_PROP(o), zend_hash_num_elements(&ce->default_properties), NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(OBJ_PROP(o), &ce->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + + ov.handle = putObject(http_request_object, o); + ov.handlers = &http_request_object_handlers; + + return ov; +} + +zend_object_value _http_request_object_clone_obj(zval *this_ptr TSRMLS_DC) +{ + zend_object_value new_ov; + http_request_object *new_obj; + getObject(http_request_object, old_obj); + + new_ov = http_request_object_new_ex(old_obj->zo.ce, NULL, &new_obj); + if (old_obj->request->ch) { + http_curl_init_ex(http_curl_copy(old_obj->request->ch), new_obj->request); + } + + zend_objects_clone_members(&new_obj->zo, new_ov, &old_obj->zo, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC); + phpstr_append(&new_obj->request->conv.request, old_obj->request->conv.request.data, old_obj->request->conv.request.used); + phpstr_append(&new_obj->request->conv.response, old_obj->request->conv.response.data, old_obj->request->conv.response.used); + + return new_ov; +} + +void _http_request_object_free(zend_object *object TSRMLS_DC) +{ + http_request_object *o = (http_request_object *) object; + + http_request_free(&o->request); + freeObject(o); +} + +#define http_request_object_check_request_content_type(t) _http_request_object_check_request_content_type((t) TSRMLS_CC) +static inline void _http_request_object_check_request_content_type(zval *this_ptr TSRMLS_DC) +{ + zval *ctype = zend_read_property(THIS_CE, getThis(), ZEND_STRS("contentType")-1, 0 TSRMLS_CC); + + if (Z_STRLEN_P(ctype)) { + zval **headers, *opts = zend_read_property(THIS_CE, getThis(), ZEND_STRS("options")-1, 0 TSRMLS_CC); + + if ( (Z_TYPE_P(opts) == IS_ARRAY) && + (SUCCESS == zend_hash_find(Z_ARRVAL_P(opts), "headers", sizeof("headers"), (void *) &headers)) && + (Z_TYPE_PP(headers) == IS_ARRAY)) { + zval **ct_header; + + /* only override if not already set */ + if ((SUCCESS != zend_hash_find(Z_ARRVAL_PP(headers), "Content-Type", sizeof("Content-Type"), (void *) &ct_header))) { + add_assoc_stringl(*headers, "Content-Type", Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1); + } else + /* or not a string, zero length string or a string of spaces */ + if ((Z_TYPE_PP(ct_header) != IS_STRING) || !Z_STRLEN_PP(ct_header)) { + add_assoc_stringl(*headers, "Content-Type", Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1); + } else { + int i, only_space = 1; + + /* check for spaces only */ + for (i = 0; i < Z_STRLEN_PP(ct_header); ++i) { + if (!HTTP_IS_CTYPE(space, Z_STRVAL_PP(ct_header)[i])) { + only_space = 0; + break; + } + } + if (only_space) { + add_assoc_stringl(*headers, "Content-Type", Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1); + } + } + } else { + zval *headers; + + MAKE_STD_ZVAL(headers); + array_init(headers); + add_assoc_stringl(headers, "Content-Type", Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1); + zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "addheaders", NULL, headers); + zval_ptr_dtor(&headers); + } + } +} + +#define http_request_object_message(zo, msg) _http_request_object_message((zo), (msg) TSRMLS_CC) +static inline zend_object_value _http_request_object_message(zval *this_ptr, http_message *msg TSRMLS_DC) +{ + zend_object_value ov; + zval *zcn = zend_read_property(THIS_CE, getThis(), ZEND_STRS("messageClass")-1, 0 TSRMLS_CC); + + if (Z_STRLEN_P(zcn) && (SUCCESS == http_object_new(&ov, Z_STRVAL_P(zcn), Z_STRLEN_P(zcn), _http_message_object_new_ex, http_message_object_ce, msg, NULL))) { + return ov; + } else { + return http_message_object_new_ex(http_message_object_ce, msg, NULL); + } +} + +STATUS _http_request_object_requesthandler(http_request_object *obj, zval *this_ptr TSRMLS_DC) +{ + STATUS status = SUCCESS; + char *url = http_absolute_url(Z_STRVAL_P(zend_read_property(THIS_CE, getThis(), ZEND_STRS("url")-1, 0 TSRMLS_CC))); + + if (!url) { + return FAILURE; + } + + http_request_reset(obj->request); + obj->request->url = url; + HTTP_CHECK_CURL_INIT(obj->request->ch, http_curl_init(obj->request), return FAILURE); + + switch (obj->request->meth = Z_LVAL_P(zend_read_property(THIS_CE, getThis(), ZEND_STRS("method")-1, 0 TSRMLS_CC))) + { + case HTTP_GET: + case HTTP_HEAD: + break; + + case HTTP_PUT: + { + zval *put_file = zend_read_property(THIS_CE, getThis(), ZEND_STRS("putFile")-1, 0 TSRMLS_CC); + + http_request_object_check_request_content_type(getThis()); + + if (Z_STRLEN_P(put_file)) { + php_stream_statbuf ssb; + php_stream *stream = php_stream_open_wrapper_ex(Z_STRVAL_P(put_file), "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL, HTTP_DEFAULT_STREAM_CONTEXT); + + if (stream && SUCCESS == php_stream_stat(stream, &ssb)) { + obj->request->body = http_request_body_init_ex(obj->request->body, HTTP_REQUEST_BODY_UPLOADFILE, stream, ssb.sb.st_size, 1); + } else { + status = FAILURE; + } + } else { + zval *put_data = zend_read_property(THIS_CE, getThis(), ZEND_STRS("putData")-1, 0 TSRMLS_CC); + obj->request->body = http_request_body_init_ex(obj->request->body, HTTP_REQUEST_BODY_CSTRING, + estrndup(Z_STRVAL_P(put_data), Z_STRLEN_P(put_data)), Z_STRLEN_P(put_data), 1); + } + break; + } + + case HTTP_POST: + default: + { + /* check for raw request body */ + zval *raw_data = zend_read_property(THIS_CE, getThis(), ZEND_STRS("requestBody")-1, 0 TSRMLS_CC); + + if (Z_STRLEN_P(raw_data)) { + http_request_object_check_request_content_type(getThis()); + obj->request->body = http_request_body_init_ex(obj->request->body, HTTP_REQUEST_BODY_CSTRING, + estrndup(Z_STRVAL_P(raw_data), Z_STRLEN_P(raw_data)), Z_STRLEN_P(raw_data), 1); + } else { + zval *zfields = zend_read_property(THIS_CE, getThis(), ZEND_STRS("postFields")-1, 0 TSRMLS_CC), *zfiles = zend_read_property(THIS_CE, getThis(), ZEND_STRS("postFiles")-1, 0 TSRMLS_CC); + HashTable *fields; + HashTable *files; + + fields = (Z_TYPE_P(zfields) == IS_ARRAY) ? Z_ARRVAL_P(zfields) : NULL; + files = (Z_TYPE_P(zfiles) == IS_ARRAY) ? Z_ARRVAL_P(zfiles) : NULL; + + if ((fields && zend_hash_num_elements(fields)) || (files && zend_hash_num_elements(files))) { + if (!(obj->request->body = http_request_body_fill(obj->request->body, fields, files))) { + status = FAILURE; + } + } + } + break; + } + } + + if (status == SUCCESS) { + zval *qdata = zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryData")-1, 0 TSRMLS_CC); + zval *options = zend_read_property(THIS_CE, getThis(), ZEND_STRS("options")-1, 0 TSRMLS_CC); + + if (Z_STRLEN_P(qdata)) { + if (!strchr(obj->request->url, '?')) { + strlcat(obj->request->url, "?", HTTP_URL_MAXLEN); + } else { + strlcat(obj->request->url, "&", HTTP_URL_MAXLEN); + } + strlcat(obj->request->url, Z_STRVAL_P(qdata), HTTP_URL_MAXLEN); + } + + http_request_prepare(obj->request, Z_ARRVAL_P(options)); + + /* check if there's a onProgress method and add it as progress callback if one isn't already set */ + if (zend_hash_exists(&Z_OBJCE_P(getThis())->function_table, "onprogress", sizeof("onprogress"))) { + zval **entry, *pcb; + + if ( (Z_TYPE_P(options) != IS_ARRAY) + || (SUCCESS != zend_hash_find(Z_ARRVAL_P(options), "onprogress", sizeof("onprogress"), (void *) &entry) + || (!IS_CALLABLE(*entry, 0, NULL)))) { + MAKE_STD_ZVAL(pcb); + array_init(pcb); + ZVAL_ADDREF(getThis()); + add_next_index_zval(pcb, getThis()); + add_next_index_stringl(pcb, "onprogress", lenof("onprogress"), 1); + http_request_set_progress_callback(obj->request, pcb); + zval_ptr_dtor(&pcb); + } + } + } + + return status; +} + +STATUS _http_request_object_responsehandler(http_request_object *obj, zval *this_ptr TSRMLS_DC) +{ + STATUS ret; + zval *info; + http_message *msg; + + /* always fetch info */ + MAKE_STD_ZVAL(info); + array_init(info); + http_request_info(obj->request, Z_ARRVAL_P(info)); + zend_update_property(THIS_CE, getThis(), ZEND_STRS("responseInfo")-1, info TSRMLS_CC); + zval_ptr_dtor(&info); + + /* parse response message */ + phpstr_fix(&obj->request->conv.request); + phpstr_fix(&obj->request->conv.response); + + if ((msg = http_message_parse(PHPSTR_VAL(&obj->request->conv.response), PHPSTR_LEN(&obj->request->conv.response)))) { + zval *message; + + if (i_zend_is_true(zend_read_property(THIS_CE, getThis(), ZEND_STRS("recordHistory")-1, 0 TSRMLS_CC))) { + zval *hist, *history = zend_read_property(THIS_CE, getThis(), ZEND_STRS("history")-1, 0 TSRMLS_CC); + http_message *response = http_message_parse(PHPSTR_VAL(&obj->request->conv.response), PHPSTR_LEN(&obj->request->conv.response)); + http_message *request = http_message_parse(PHPSTR_VAL(&obj->request->conv.request), PHPSTR_LEN(&obj->request->conv.request)); + + MAKE_STD_ZVAL(hist); + ZVAL_OBJVAL(hist, http_request_object_message(getThis(), http_message_interconnect(response, request)), 0); + if (Z_TYPE_P(history) == IS_OBJECT) { + http_message_object_prepend(hist, history); + } + zend_update_property(THIS_CE, getThis(), ZEND_STRS("history")-1, hist TSRMLS_CC); + zval_ptr_dtor(&hist); + } + + zend_update_property_long(THIS_CE, getThis(), ZEND_STRS("responseCode")-1, msg->http.info.response.code TSRMLS_CC); + zend_update_property_string(THIS_CE, getThis(), ZEND_STRS("responseStatus")-1, STR_PTR(msg->http.info.response.status) TSRMLS_CC); + + MAKE_STD_ZVAL(message); + ZVAL_OBJVAL(message, http_request_object_message(getThis(), msg), 0); + zend_update_property(THIS_CE, getThis(), ZEND_STRS("responseMessage")-1, message TSRMLS_CC); + zval_ptr_dtor(&message); + + ret = SUCCESS; + } else { + /* update properties with empty values*/ + zval *znull; + + MAKE_STD_ZVAL(znull); + ZVAL_NULL(znull); + zend_update_property(THIS_CE, getThis(), ZEND_STRS("responseMessage")-1, znull TSRMLS_CC); + zval_ptr_dtor(&znull); + + zend_update_property_long(THIS_CE, getThis(), ZEND_STRS("responseCode")-1, 0 TSRMLS_CC); + zend_update_property_string(THIS_CE, getThis(), ZEND_STRS("responseStatus")-1, "" TSRMLS_CC); + + /* append request message to history */ + if (i_zend_is_true(zend_read_property(THIS_CE, getThis(), ZEND_STRS("recordHistory")-1, 0 TSRMLS_CC))) { + http_message *request; + + if ((request = http_message_parse(PHPSTR_VAL(&obj->request->conv.request), PHPSTR_LEN(&obj->request->conv.request)))) { + zval *hist, *history = zend_read_property(THIS_CE, getThis(), ZEND_STRS("history")-1, 0 TSRMLS_CC); + + MAKE_STD_ZVAL(hist); + ZVAL_OBJVAL(hist, http_request_object_message(getThis(), request), 0); + if (Z_TYPE_P(history) == IS_OBJECT) { + http_message_object_prepend(hist, history); + } + zend_update_property(THIS_CE, getThis(), ZEND_STRS("history")-1, hist TSRMLS_CC); + zval_ptr_dtor(&hist); + } + } + + ret = FAILURE; + } + + http_request_set_progress_callback(obj->request, NULL); + + if (!EG(exception) && zend_hash_exists(&Z_OBJCE_P(getThis())->function_table, "onfinish", sizeof("onfinish"))) { + zval *param; + + MAKE_STD_ZVAL(param); + ZVAL_BOOL(param, ret == SUCCESS); + with_error_handling(EH_NORMAL, NULL) { + zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "onfinish", NULL, param); + } end_error_handling(); + zval_ptr_dtor(¶m); + } + + return ret; +} + +static int apply_pretty_key(void *pDest, int num_args, va_list args, zend_hash_key *hash_key) +{ + if (hash_key->arKey && hash_key->nKeyLength > 1) { + hash_key->h = zend_hash_func(pretty_key(hash_key->arKey, hash_key->nKeyLength - 1, 1, 0), hash_key->nKeyLength); + } + return ZEND_HASH_APPLY_KEEP; +} + +#define http_request_object_set_options_subr(key, ow, pk) \ + _http_request_object_set_options_subr(INTERNAL_FUNCTION_PARAM_PASSTHRU, (key), sizeof(key), (ow), (pk)) +static inline void _http_request_object_set_options_subr(INTERNAL_FUNCTION_PARAMETERS, char *key, size_t len, int overwrite, int prettify_keys) +{ + zval *old_opts, *new_opts, *opts = NULL, **entry = NULL; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a/!", &opts)) { + RETURN_FALSE; + } + + MAKE_STD_ZVAL(new_opts); + array_init(new_opts); + old_opts = zend_read_property(THIS_CE, getThis(), ZEND_STRS("options")-1, 0 TSRMLS_CC); + if (Z_TYPE_P(old_opts) == IS_ARRAY) { + array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL_P(new_opts)); + } + + if (SUCCESS == zend_hash_find(Z_ARRVAL_P(new_opts), key, len, (void *) &entry)) { + if (overwrite) { + zend_hash_clean(Z_ARRVAL_PP(entry)); + } + if (opts && zend_hash_num_elements(Z_ARRVAL_P(opts))) { + if (overwrite) { + array_copy(Z_ARRVAL_P(opts), Z_ARRVAL_PP(entry)); + } else { + array_join(Z_ARRVAL_P(opts), Z_ARRVAL_PP(entry), 0, prettify_keys ? ARRAY_JOIN_PRETTIFY : 0); + } + } + } else if (opts) { + if (prettify_keys) { + zend_hash_apply_with_arguments(Z_ARRVAL_P(opts) HTTP_ZAPI_HASH_TSRMLS_CC, apply_pretty_key, 0, NULL); + } + ZVAL_ADDREF(opts); + add_assoc_zval_ex(new_opts, key, len, opts); + } + zend_update_property(THIS_CE, getThis(), ZEND_STRS("options")-1, new_opts TSRMLS_CC); + zval_ptr_dtor(&new_opts); + + RETURN_TRUE; +} + +#define http_request_object_get_options_subr(key) \ + _http_request_get_options_subr(INTERNAL_FUNCTION_PARAM_PASSTHRU, (key), sizeof(key)) +static inline void _http_request_get_options_subr(INTERNAL_FUNCTION_PARAMETERS, char *key, size_t len) +{ + NO_ARGS; + + if (return_value_used) { + zval *opts, **options; + + opts = zend_read_property(THIS_CE, getThis(), ZEND_STRS("options")-1, 0 TSRMLS_CC); + array_init(return_value); + + if ( (Z_TYPE_P(opts) == IS_ARRAY) && + (SUCCESS == zend_hash_find(Z_ARRVAL_P(opts), key, len, (void *) &options))) { + convert_to_array(*options); + array_copy(Z_ARRVAL_PP(options), Z_ARRVAL_P(return_value)); + } + } +} + + +/* ### USERLAND ### */ + +/* {{{ proto void HttpRequest::__construct([string url[, int request_method = HTTP_METH_GET[, array options]]]) + Create a new HttpRequest object instance. */ +PHP_METHOD(HttpRequest, __construct) +{ + char *URL = NULL; + int URL_len; + long meth = -1; + zval *options = NULL; + + SET_EH_THROW_HTTP(); + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sla!", &URL, &URL_len, &meth, &options)) { + if (URL) { + zend_update_property_stringl(THIS_CE, getThis(), ZEND_STRS("url")-1, URL, URL_len TSRMLS_CC); + } + if (meth > -1) { + zend_update_property_long(THIS_CE, getThis(), ZEND_STRS("method")-1, meth TSRMLS_CC); + } + if (options) { + zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "setoptions", NULL, options); + } + } + SET_EH_NORMAL(); +} +/* }}} */ + +/* {{{ proto HttpRequest HttpRequest::factory([string url[, int request_method HTTP_METH_GET[, array options[, string class_name = "HttpRequest"]]]]) + Create a new HttpRequest object instance. */ +PHP_METHOD(HttpRequest, factory) +{ + char *cn = NULL, *URL = NULL; + int cl = 0, URL_len = 0; + long meth = -1; + zval *options = NULL; + zend_object_value ov; + + SET_EH_THROW_HTTP(); + if ( SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sla!s", &URL, &URL_len, &meth, &options, &cn, &cl) && + SUCCESS == http_object_new(&ov, cn, cl, _http_request_object_new_ex, http_request_object_ce, NULL, NULL)) { + RETVAL_OBJVAL(ov, 0); + getThis() = return_value; + if (URL) { + zend_update_property_stringl(THIS_CE, getThis(), ZEND_STRS("url")-1, URL, URL_len TSRMLS_CC); + } + if (meth > -1) { + zend_update_property_long(THIS_CE, getThis(), ZEND_STRS("method")-1, meth TSRMLS_CC); + } + if (options) { + zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "setoptions", NULL, options); + } + } + SET_EH_NORMAL(); +} +/* }}} */ + +/* {{{ proto bool HttpRequest::setOptions([array options]) + Set the request options to use. See http_get() for a full list of available options. */ +PHP_METHOD(HttpRequest, setOptions) +{ + HashKey key = initHashKey(0); + HashPosition pos; + zval *opts = NULL, *old_opts, *new_opts, *add_opts, **opt; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts)) { + RETURN_FALSE; + } + + 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_STRS("options")-1, new_opts TSRMLS_CC); + zval_ptr_dtor(&new_opts); + RETURN_TRUE; + } + + 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 (KEYMATCH(key, "headers")) { + zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "addheaders", NULL, *opt); + } else if (KEYMATCH(key, "cookies")) { + zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "addcookies", NULL, *opt); + } else if (KEYMATCH(key, "ssl")) { + zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "addssloptions", NULL, *opt); + } else if (KEYMATCH(key, "url") || KEYMATCH(key, "uri")) { + zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "seturl", NULL, *opt); + } else if (KEYMATCH(key, "method")) { + zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "setmethod", NULL, *opt); + } else if (KEYMATCH(key, "flushcookies")) { + getObject(http_request_object, obj); + if (i_zend_is_true(*opt)) { + http_request_flush_cookies(obj->request); + } + } else if (KEYMATCH(key, "resetcookies")) { + getObject(http_request_object, obj); + http_request_reset_cookies(obj->request, (zend_bool) i_zend_is_true(*opt)); + } else if (KEYMATCH(key, "enablecookies")) { + getObject(http_request_object, obj); + http_request_enable_cookies(obj->request); + } else if (KEYMATCH(key, "recordHistory")) { + zend_update_property(THIS_CE, getThis(), ZEND_STRS("recordHistory")-1, *opt TSRMLS_CC); + } else if (KEYMATCH(key, "messageClass")) { + zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "setmessageclass", NULL, *opt); + } else if (Z_TYPE_PP(opt) == IS_NULL) { + old_opts = zend_read_property(THIS_CE, getThis(), ZEND_STRS("options")-1, 0 TSRMLS_CC); + if (Z_TYPE_P(old_opts) == IS_ARRAY) { + zend_hash_del(Z_ARRVAL_P(old_opts), key.str, key.len); + } + } else { + ZVAL_ADDREF(*opt); + add_assoc_zval_ex(add_opts, key.str, key.len, *opt); + } + } + } + + old_opts = zend_read_property(THIS_CE, getThis(), ZEND_STRS("options")-1, 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_STRS("options")-1, new_opts TSRMLS_CC); + zval_ptr_dtor(&new_opts); + zval_ptr_dtor(&add_opts); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto array HttpRequest::getOptions() + Get currently set options. */ +PHP_METHOD(HttpRequest, getOptions) +{ + NO_ARGS; + + if (return_value_used) { + RETURN_PROP(options); + } +} +/* }}} */ + +/* {{{ proto bool HttpRequest::setSslOptions([array options]) + Set SSL options. */ +PHP_METHOD(HttpRequest, setSslOptions) +{ + http_request_object_set_options_subr("ssl", 1, 0); +} +/* }}} */ + +/* {{{ proto bool HttpRequest::addSslOptions(array options) + Set additional SSL options. */ +PHP_METHOD(HttpRequest, addSslOptions) +{ + http_request_object_set_options_subr("ssl", 0, 0); +} +/* }}} */ + +/* {{{ proto array HttpRequest::getSslOtpions() + Get previously set SSL options. */ +PHP_METHOD(HttpRequest, getSslOptions) +{ + http_request_object_get_options_subr("ssl"); +} +/* }}} */ + +/* {{{ proto bool HttpRequest::addHeaders(array headers) + Add request header name/value pairs. */ +PHP_METHOD(HttpRequest, addHeaders) +{ + http_request_object_set_options_subr("headers", 0, 1); +} + +/* {{{ proto bool HttpRequest::setHeaders([array headers]) + Set request header name/value pairs. */ +PHP_METHOD(HttpRequest, setHeaders) +{ + http_request_object_set_options_subr("headers", 1, 1); +} +/* }}} */ + +/* {{{ proto array HttpRequest::getHeaders() + Get previously set request headers. */ +PHP_METHOD(HttpRequest, getHeaders) +{ + http_request_object_get_options_subr("headers"); +} +/* }}} */ + +/* {{{ proto bool HttpRequest::setCookies([array cookies]) + Set cookies. */ +PHP_METHOD(HttpRequest, setCookies) +{ + http_request_object_set_options_subr("cookies", 1, 0); +} +/* }}} */ + +/* {{{ proto bool HttpRequest::addCookies(array cookies) + Add cookies. */ +PHP_METHOD(HttpRequest, addCookies) +{ + http_request_object_set_options_subr("cookies", 0, 0); +} +/* }}} */ + +/* {{{ proto array HttpRequest::getCookies() + Get previously set cookies. */ +PHP_METHOD(HttpRequest, getCookies) +{ + http_request_object_get_options_subr("cookies"); +} +/* }}} */ + +/* {{{ proto bool HttpRequest::enableCookies() + Enable automatic sending of received cookies. Note that customly set cookies will be sent anyway. */ +PHP_METHOD(HttpRequest, enableCookies) +{ + NO_ARGS { + getObject(http_request_object, obj); + RETURN_SUCCESS(http_request_enable_cookies(obj->request)); + } + +} +/* }}} */ + +/* {{{ proto bool HttpRequest::resetCookies([bool session_only = FALSE]) + Reset all automatically received/sent cookies. Note that customly set cookies are not affected. */ +PHP_METHOD(HttpRequest, resetCookies) +{ + zend_bool session_only = 0; + getObject(http_request_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &session_only)) { + RETURN_FALSE; + } + RETURN_SUCCESS(http_request_reset_cookies(obj->request, session_only)); +} +/* }}} */ + +/* {{{ proto bool HttpRequest::flushCookies() + Flush internal cookies to the cookiestore file */ +PHP_METHOD(HttpRequest, flushCookies) +{ + NO_ARGS { + getObject(http_request_object, obj); + RETURN_SUCCESS(http_request_flush_cookies(obj->request)); + } +} +/* }}} */ + +/* {{{ proto bool HttpRequest::setUrl(string url) + Set the request URL. */ +PHP_METHOD(HttpRequest, setUrl) +{ + char *URL = NULL; + int URL_len; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &URL, &URL_len)) { + RETURN_FALSE; + } + + zend_update_property_stringl(THIS_CE, getThis(), ZEND_STRS("url")-1, URL, URL_len TSRMLS_CC); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HttpRequest::getUrl() + Get the previously set request URL. */ +PHP_METHOD(HttpRequest, getUrl) +{ + NO_ARGS; + + if (return_value_used) { + RETURN_PROP(url); + } +} +/* }}} */ + +/* {{{ proto bool HttpRequest::setMethod(int request_method) + Set the request method. */ +PHP_METHOD(HttpRequest, setMethod) +{ + long meth; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &meth)) { + RETURN_FALSE; + } + + zend_update_property_long(THIS_CE, getThis(), ZEND_STRS("method")-1, meth TSRMLS_CC); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto int HttpRequest::getMethod() + Get the previously set request method. */ +PHP_METHOD(HttpRequest, getMethod) +{ + NO_ARGS; + + if (return_value_used) { + RETURN_PROP(method); + } +} +/* }}} */ + +/* {{{ proto bool HttpRequest::setContentType(string content_type) + Set the content type the post request should have. */ +PHP_METHOD(HttpRequest, setContentType) +{ + char *ctype; + int ct_len; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ctype, &ct_len)) { + RETURN_FALSE; + } + + if (ct_len) { + HTTP_CHECK_CONTENT_TYPE(ctype, RETURN_FALSE); + } + zend_update_property_stringl(THIS_CE, getThis(), ZEND_STRS("contentType")-1, ctype, ct_len TSRMLS_CC); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HttpRequest::getContentType() + Get the previously content type. */ +PHP_METHOD(HttpRequest, getContentType) +{ + NO_ARGS; + + if (return_value_used) { + RETURN_PROP(contentType); + } +} +/* }}} */ + +/* {{{ proto bool HttpRequest::setQueryData([mixed query_data]) + Set the URL query parameters to use, overwriting previously set query parameters. */ +PHP_METHOD(HttpRequest, setQueryData) +{ + zval *qdata = NULL; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z!", &qdata)) { + RETURN_FALSE; + } + + if ((!qdata) || Z_TYPE_P(qdata) == IS_NULL) { + zend_update_property_stringl(THIS_CE, getThis(), ZEND_STRS("queryData")-1, "", 0 TSRMLS_CC); + } else if ((Z_TYPE_P(qdata) == IS_ARRAY) || (Z_TYPE_P(qdata) == IS_OBJECT)) { + char *query_data = NULL; + + if (SUCCESS != http_urlencode_hash(HASH_OF(qdata), &query_data)) { + RETURN_FALSE; + } + + zend_update_property_string(THIS_CE, getThis(), ZEND_STRS("queryData")-1, query_data TSRMLS_CC); + efree(query_data); + } else { + zval *data = http_zsep(IS_STRING, qdata); + + zend_update_property_stringl(THIS_CE, getThis(), ZEND_STRS("queryData")-1, Z_STRVAL_P(data), Z_STRLEN_P(data) TSRMLS_CC); + zval_ptr_dtor(&data); + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HttpRequest::getQueryData() + Get the current query data in form of an urlencoded query string. */ +PHP_METHOD(HttpRequest, getQueryData) +{ + NO_ARGS; + + if (return_value_used) { + RETURN_PROP(queryData); + } +} +/* }}} */ + +/* {{{ proto bool HttpRequest::addQueryData(array query_params) + Add parameters to the query parameter list, leaving previously set unchanged. */ +PHP_METHOD(HttpRequest, addQueryData) +{ + zval *qdata, *old_qdata; + char *query_data = NULL; + size_t query_data_len = 0; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &qdata)) { + RETURN_FALSE; + } + + old_qdata = zend_read_property(THIS_CE, getThis(), ZEND_STRS("queryData")-1, 0 TSRMLS_CC); + + if (SUCCESS != http_urlencode_hash_ex(HASH_OF(qdata), 1, Z_STRVAL_P(old_qdata), Z_STRLEN_P(old_qdata), &query_data, &query_data_len)) { + RETURN_FALSE; + } + + zend_update_property_stringl(THIS_CE, getThis(), ZEND_STRS("queryData")-1, query_data, query_data_len TSRMLS_CC); + efree(query_data); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool HttpRequest::addPostFields(array post_data) + Adds POST data entries, leaving previously set unchanged, unless a post entry with the same name already exists. */ +PHP_METHOD(HttpRequest, addPostFields) +{ + zval *post_data, *old_post, *new_post; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &post_data)) { + RETURN_FALSE; + } + + if (zend_hash_num_elements(Z_ARRVAL_P(post_data))) { + MAKE_STD_ZVAL(new_post); + array_init(new_post); + old_post = zend_read_property(THIS_CE, getThis(), ZEND_STRS("postFields")-1, 0 TSRMLS_CC); + if (Z_TYPE_P(old_post) == IS_ARRAY) { + array_copy(Z_ARRVAL_P(old_post), Z_ARRVAL_P(new_post)); + } + array_join(Z_ARRVAL_P(post_data), Z_ARRVAL_P(new_post), 0, 0); + zend_update_property(THIS_CE, getThis(), ZEND_STRS("postFields")-1, new_post TSRMLS_CC); + zval_ptr_dtor(&new_post); + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool HttpRequest::setPostFields([array post_data]) + Set the POST data entries, overwriting previously set POST data. */ +PHP_METHOD(HttpRequest, setPostFields) +{ + zval *post, *post_data = NULL; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/!", &post_data)) { + RETURN_FALSE; + } + + MAKE_STD_ZVAL(post); + array_init(post); + if (post_data && zend_hash_num_elements(Z_ARRVAL_P(post_data))) { + array_copy(Z_ARRVAL_P(post_data), Z_ARRVAL_P(post)); + } + zend_update_property(THIS_CE, getThis(), ZEND_STRS("postFields")-1, post TSRMLS_CC); + zval_ptr_dtor(&post); + + RETURN_TRUE; +} +/* }}}*/ + +/* {{{ proto array HttpRequest::getPostFields() + Get previously set POST data. */ +PHP_METHOD(HttpRequest, getPostFields) +{ + NO_ARGS; + + if (return_value_used) { + RETURN_PROP(postFields); + } +} +/* }}} */ + +/* {{{ proto bool HttpRequest::setBody([string request_body_data]) + Set request body to send, overwriting previously set request body. Don't forget to specify a content type. */ +PHP_METHOD(HttpRequest, setBody) +{ + char *raw_data = NULL; + int data_len = 0; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &raw_data, &data_len)) { + RETURN_FALSE; + } + + if (!raw_data) { + raw_data = ""; + } + + zend_update_property_stringl(THIS_CE, getThis(), ZEND_STRS("requestBody")-1, raw_data, data_len TSRMLS_CC); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool HttpRequest::addBody(string request_body_data) + Add request body data, leaving previously set request body data unchanged. */ +PHP_METHOD(HttpRequest, addBody) +{ + char *raw_data; + int data_len; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &raw_data, &data_len)) { + RETURN_FALSE; + } + + if (data_len) { + zval *data = zend_read_property(THIS_CE, getThis(), ZEND_STRS("requestBody")-1, 0 TSRMLS_CC); + + if (Z_STRLEN_P(data)) { + Z_STRVAL_P(data) = erealloc(Z_STRVAL_P(data), (Z_STRLEN_P(data) += data_len) + 1); + Z_STRVAL_P(data)[Z_STRLEN_P(data)] = '\0'; + memcpy(Z_STRVAL_P(data) + Z_STRLEN_P(data) - data_len, raw_data, data_len); + } else { + zend_update_property_stringl(THIS_CE, getThis(), ZEND_STRS("requestBody")-1, raw_data, data_len TSRMLS_CC); + } + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HttpRequest::getBody() + Get previously set request body data. */ +PHP_METHOD(HttpRequest, getBody) +{ + NO_ARGS; + + if (return_value_used) { + RETURN_PROP(requestBody); + } +} +/* }}} */ + +/* {{{ proto bool HttpRequest::addPostFile(string name, string file[, string content_type = "application/x-octetstream"]) + Add a file to the POST request, leaving previously set files unchanged. */ +PHP_METHOD(HttpRequest, addPostFile) +{ + zval *entry, *old_post, *new_post; + char *name, *file, *type = NULL; + int name_len, file_len, type_len = 0; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|s", &name, &name_len, &file, &file_len, &type, &type_len)) { + RETURN_FALSE; + } + + if (type_len) { + HTTP_CHECK_CONTENT_TYPE(type, RETURN_FALSE); + } else { + type = "application/x-octetstream"; + type_len = sizeof("application/x-octetstream") - 1; + } + + MAKE_STD_ZVAL(entry); + array_init(entry); + + add_assoc_stringl(entry, "name", name, name_len, 1); + add_assoc_stringl(entry, "type", type, type_len, 1); + add_assoc_stringl(entry, "file", file, file_len, 1); + + MAKE_STD_ZVAL(new_post); + array_init(new_post); + old_post = zend_read_property(THIS_CE, getThis(), ZEND_STRS("postFiles")-1, 0 TSRMLS_CC); + if (Z_TYPE_P(old_post) == IS_ARRAY) { + array_copy(Z_ARRVAL_P(old_post), Z_ARRVAL_P(new_post)); + } + add_next_index_zval(new_post, entry); + zend_update_property(THIS_CE, getThis(), ZEND_STRS("postFiles")-1, new_post TSRMLS_CC); + zval_ptr_dtor(&new_post); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool HttpRequest::setPostFiles([array post_files]) + Set files to post, overwriting previously set post files. */ +PHP_METHOD(HttpRequest, setPostFiles) +{ + zval *files = NULL, *post; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a!/", &files)) { + RETURN_FALSE; + } + + MAKE_STD_ZVAL(post); + array_init(post); + if (files && (Z_TYPE_P(files) == IS_ARRAY)) { + array_copy(Z_ARRVAL_P(files), Z_ARRVAL_P(post)); + } + zend_update_property(THIS_CE, getThis(), ZEND_STRS("postFiles")-1, post TSRMLS_CC); + zval_ptr_dtor(&post); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto array HttpRequest::getPostFiles() + Get all previously added POST files. */ +PHP_METHOD(HttpRequest, getPostFiles) +{ + NO_ARGS; + + if (return_value_used) { + RETURN_PROP(postFiles); + } +} +/* }}} */ + +/* {{{ proto bool HttpRequest::setPutFile([string file]) + Set file to put. Affects only PUT requests. */ +PHP_METHOD(HttpRequest, setPutFile) +{ + char *file = ""; + int file_len = 0; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &file, &file_len)) { + RETURN_FALSE; + } + + zend_update_property_stringl(THIS_CE, getThis(), ZEND_STRS("putFile")-1, file, file_len TSRMLS_CC); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HttpRequest::getPutFile() + Get previously set put file. */ +PHP_METHOD(HttpRequest, getPutFile) +{ + NO_ARGS; + + if (return_value_used) { + RETURN_PROP(putFile); + } +} +/* }}} */ + +/* {{{ proto bool HttpRequest::setPutData([string put_data]) + Set PUT data to send, overwriting previously set PUT data. */ +PHP_METHOD(HttpRequest, setPutData) +{ + char *put_data = NULL; + int data_len = 0; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &put_data, &data_len)) { + RETURN_FALSE; + } + + if (!put_data) { + put_data = ""; + } + + zend_update_property_stringl(THIS_CE, getThis(), ZEND_STRS("putData")-1, put_data, data_len TSRMLS_CC); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool HttpRequest::addPutData(string put_data) + Add PUT data, leaving previously set PUT data unchanged. */ +PHP_METHOD(HttpRequest, addPutData) +{ + char *put_data; + int data_len; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &put_data, &data_len)) { + RETURN_FALSE; + } + + if (data_len) { + zval *data = zend_read_property(THIS_CE, getThis(), ZEND_STRS("putData")-1, 0 TSRMLS_CC); + + if (Z_STRLEN_P(data)) { + Z_STRVAL_P(data) = erealloc(Z_STRVAL_P(data), (Z_STRLEN_P(data) += data_len) + 1); + Z_STRVAL_P(data)[Z_STRLEN_P(data)] = '\0'; + memcpy(Z_STRVAL_P(data) + Z_STRLEN_P(data) - data_len, put_data, data_len); + } else { + zend_update_property_stringl(THIS_CE, getThis(), ZEND_STRS("putData")-1, put_data, data_len TSRMLS_CC); + } + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HttpRequest::getPutData() + Get previously set PUT data. */ +PHP_METHOD(HttpRequest, getPutData) +{ + NO_ARGS; + + if (return_value_used) { + RETURN_PROP(putData); + } +} +/* }}} */ + +/* {{{ proto array HttpRequest::getResponseData() + Get all response data after the request has been sent. */ +PHP_METHOD(HttpRequest, getResponseData) +{ + NO_ARGS; + + if (return_value_used) { + char *body; + size_t body_len; + zval *headers, *message = zend_read_property(THIS_CE, getThis(), ZEND_STRS("responseMessage")-1, 0 TSRMLS_CC); + + if (Z_TYPE_P(message) == IS_OBJECT) { + getObjectEx(http_message_object, msg, message); + + array_init(return_value); + + MAKE_STD_ZVAL(headers); + array_init(headers); + zend_hash_copy(Z_ARRVAL_P(headers), &msg->message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + add_assoc_zval(return_value, "headers", headers); + + phpstr_data(PHPSTR(msg->message), &body, &body_len); + add_assoc_stringl(return_value, "body", body, body_len, 0); + } + } +} +/* }}} */ + +/* {{{ proto mixed HttpRequest::getResponseHeader([string name]) + Get response header(s) after the request has been sent. */ +PHP_METHOD(HttpRequest, getResponseHeader) +{ + if (return_value_used) { + zval *header; + char *header_name = NULL; + int header_len = 0; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &header_name, &header_len)) { + zval *message = zend_read_property(THIS_CE, getThis(), ZEND_STRS("responseMessage")-1, 0 TSRMLS_CC); + + if (Z_TYPE_P(message) == IS_OBJECT) { + getObjectEx(http_message_object, msg, message); + + if (header_len) { + if ((header = http_message_header_ex(msg->message, pretty_key(header_name, header_len, 1, 1), header_len + 1, 0))) { + RETURN_ZVAL(header, 1, 1); + } + } else { + array_init(return_value); + zend_hash_copy(Z_ARRVAL_P(return_value), &msg->message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + return; + } + } + } + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto array HttpRequest::getResponseCookies([int flags[, array allowed_extras]]) + Get response cookie(s) after the request has been sent. */ +PHP_METHOD(HttpRequest, getResponseCookies) +{ + if (return_value_used) { + long flags = 0; + zval *allowed_extras_array = NULL; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|la!", &flags, &allowed_extras_array)) { + int i = 0; + HashKey key = initHashKey(0); + char **allowed_extras = NULL; + zval **header = NULL, **entry = NULL, *message = zend_read_property(THIS_CE, getThis(), ZEND_STRS("responseMessage")-1, 0 TSRMLS_CC); + HashPosition pos, pos1, pos2; + + if (Z_TYPE_P(message) == IS_OBJECT) { + getObjectEx(http_message_object, msg, message); + + 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 = http_zsep(IS_STRING, *entry); + allowed_extras[i++] = estrndup(Z_STRVAL_P(data), Z_STRLEN_P(data)); + zval_ptr_dtor(&data); + } + } + + FOREACH_HASH_KEYVAL(pos1, &msg->message->hdrs, key, header) { + if (key.type == HASH_KEY_IS_STRING && !strcasecmp(key.str, "Set-Cookie")) { + http_cookie_list list; + + if (Z_TYPE_PP(header) == IS_ARRAY) { + zval **single_header; + + FOREACH_VAL(pos2, *header, single_header) { + zval *data = http_zsep(IS_STRING, *single_header); + + if (http_parse_cookie_ex(&list, Z_STRVAL_P(data), flags, allowed_extras)) { + zval *cookie; + + MAKE_STD_ZVAL(cookie); + object_init(cookie); + http_cookie_list_tostruct(&list, cookie); + add_next_index_zval(return_value, cookie); + http_cookie_list_dtor(&list); + } + zval_ptr_dtor(&data); + } + } else { + zval *data = http_zsep(IS_STRING, *header); + if (http_parse_cookie_ex(&list, Z_STRVAL_P(data), flags, allowed_extras)) { + zval *cookie; + + MAKE_STD_ZVAL(cookie); + object_init(cookie); + http_cookie_list_tostruct(&list, cookie); + add_next_index_zval(return_value, cookie); + http_cookie_list_dtor(&list); + } + zval_ptr_dtor(&data); + } + } + } + + if (allowed_extras) { + for (i = 0; allowed_extras[i]; ++i) { + efree(allowed_extras[i]); + } + efree(allowed_extras); + } + + return; + } + } + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto string HttpRequest::getResponseBody() + Get the response body after the request has been sent. */ +PHP_METHOD(HttpRequest, getResponseBody) +{ + NO_ARGS; + + if (return_value_used) { + zval *message = zend_read_property(THIS_CE, getThis(), ZEND_STRS("responseMessage")-1, 0 TSRMLS_CC); + + if (Z_TYPE_P(message) == IS_OBJECT) { + getObjectEx(http_message_object, msg, message); + RETURN_PHPSTR_DUP(&msg->message->body); + } else { + RETURN_FALSE; + } + } +} +/* }}} */ + +/* {{{ proto int HttpRequest::getResponseCode() + Get the response code after the request has been sent. */ +PHP_METHOD(HttpRequest, getResponseCode) +{ + NO_ARGS; + + if (return_value_used) { + RETURN_PROP(responseCode); + } +} +/* }}} */ + +/* {{{ proto string HttpRequest::getResponseStatus() + Get the response status (i.e. the string after the response code) after the message has been sent. */ +PHP_METHOD(HttpRequest, getResponseStatus) +{ + NO_ARGS; + + if (return_value_used) { + RETURN_PROP(responseStatus); + } +} +/* }}} */ + +/* {{{ proto mixed HttpRequest::getResponseInfo([string name]) + Get response info after the request has been sent. */ +PHP_METHOD(HttpRequest, getResponseInfo) +{ + if (return_value_used) { + zval *info, **infop; + char *info_name = NULL; + int info_len = 0; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &info_name, &info_len)) { + RETURN_FALSE; + } + + info = zend_read_property(THIS_CE, getThis(), ZEND_STRS("responseInfo")-1, 0 TSRMLS_CC); + + if (Z_TYPE_P(info) != IS_ARRAY) { + RETURN_FALSE; + } + + if (info_len && info_name) { + if (SUCCESS == zend_hash_find(Z_ARRVAL_P(info), pretty_key(info_name, info_len, 0, 0), info_len + 1, (void *) &infop)) { + RETURN_ZVAL(*infop, 1, 0); + } else { + http_error_ex(HE_NOTICE, HTTP_E_INVALID_PARAM, "Could not find response info named %s", info_name); + RETURN_FALSE; + } + } else { + RETURN_ZVAL(info, 1, 0); + } + } +} +/* }}}*/ + +/* {{{ proto HttpMessage HttpRequest::getResponseMessage() + Get the full response as HttpMessage object after the request has been sent. */ +PHP_METHOD(HttpRequest, getResponseMessage) +{ + NO_ARGS { + zval *message; + + SET_EH_THROW_HTTP(); + message = zend_read_property(THIS_CE, getThis(), ZEND_STRS("responseMessage")-1, 0 TSRMLS_CC); + if (Z_TYPE_P(message) == IS_OBJECT) { + RETVAL_OBJECT(message, 1); + } else { + http_error(HE_WARNING, HTTP_E_RUNTIME, "HttpRequest does not contain a response message"); + } + SET_EH_NORMAL(); + } +} +/* }}} */ + +/* {{{ proto HttpMessage HttpRequest::getRequestMessage() + Get sent HTTP message. */ +PHP_METHOD(HttpRequest, getRequestMessage) +{ + NO_ARGS; + + if (return_value_used) { + http_message *msg; + getObject(http_request_object, obj); + + SET_EH_THROW_HTTP(); + if ((msg = http_message_parse(PHPSTR_VAL(&obj->request->conv.request), PHPSTR_LEN(&obj->request->conv.request)))) { + RETVAL_OBJVAL(http_request_object_message(getThis(), msg), 0); + } + SET_EH_NORMAL(); + } +} +/* }}} */ + +/* {{{ proto string HttpRequest::getRawRequestMessage() + Get sent HTTP message. */ +PHP_METHOD(HttpRequest, getRawRequestMessage) +{ + NO_ARGS; + + if (return_value_used) { + getObject(http_request_object, obj); + + RETURN_PHPSTR_DUP(&obj->request->conv.request); + } +} +/* }}} */ + +/* {{{ proto string HttpRequest::getRawResponseMessage() + Get the entire HTTP response. */ +PHP_METHOD(HttpRequest, getRawResponseMessage) +{ + NO_ARGS; + + if (return_value_used) { + getObject(http_request_object, obj); + + RETURN_PHPSTR_DUP(&obj->request->conv.response); + } +} +/* }}} */ + +/* {{{ proto HttpMessage HttpRequest::getHistory() + Get all sent requests and received responses as an HttpMessage object. */ +PHP_METHOD(HttpRequest, getHistory) +{ + NO_ARGS; + + if (return_value_used) { + zval *hist; + + SET_EH_THROW_HTTP(); + hist = zend_read_property(THIS_CE, getThis(), ZEND_STRS("history")-1, 0 TSRMLS_CC); + if (Z_TYPE_P(hist) == IS_OBJECT) { + RETVAL_OBJECT(hist, 1); + } else { + http_error(HE_WARNING, HTTP_E_RUNTIME, "The history is empty"); + } + SET_EH_NORMAL(); + } +} +/* }}} */ + +/* {{{ proto void HttpRequest::clearHistory() + Clear the history. */ +PHP_METHOD(HttpRequest, clearHistory) +{ + NO_ARGS { + zval *hist; + + MAKE_STD_ZVAL(hist); + ZVAL_NULL(hist); + zend_update_property(THIS_CE, getThis(), ZEND_STRS("history")-1, hist TSRMLS_CC); + zval_ptr_dtor(&hist); + } +} +/* }}} */ + +/* {{{ proto string HttpRequest::getMessageClass() + Get the message class name. */ +PHP_METHOD(HttpRequest, getMessageClass) +{ + NO_ARGS; + + if (return_value_used) { + RETURN_PROP("messageClass"); + } +} +/* }}} */ + +/* {{{ proto void setMessageClass(string class_name) + Set the message class name. */ +PHP_METHOD(HttpRequest, setMessageClass) +{ + char *cn; + int cl; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &cn, &cl)) { + zend_update_property_stringl(THIS_CE, getThis(), ZEND_STRS("messageClass")-1, cn, cl TSRMLS_CC); + } +} +/* }}} */ + +/* {{{ proto HttpMessage HttpRequest::send() + Send the HTTP request. */ +PHP_METHOD(HttpRequest, send) +{ + getObject(http_request_object, obj); + + NO_ARGS; + + SET_EH_THROW_HTTP(); + + RETVAL_FALSE; + + if (obj->pool) { + http_error(HE_WARNING, HTTP_E_RUNTIME, "Cannot perform HttpRequest::send() while attached to an HttpRequestPool"); + } else if (SUCCESS == http_request_object_requesthandler(obj, getThis())) { + http_request_exec(obj->request); + if (SUCCESS == http_request_object_responsehandler(obj, getThis())) { + RETVAL_OBJECT(zend_read_property(THIS_CE, getThis(), ZEND_STRS("responseMessage")-1, 0 TSRMLS_CC), 1); + } + } + + SET_EH_NORMAL(); +} +/* }}} */ + +#endif /* ZEND_ENGINE_2 && 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 + */ + --- /dev/null +++ b/ext/http/http_request_pool_api.c @@ -0,0 +1,660 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_request_pool_api.c 292841 2009-12-31 08:48:57Z mike $ */ + +#define HTTP_WANT_CURL +#define HTTP_WANT_EVENT +#include "php_http.h" + +#if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL) + +#include "php_http_api.h" +#include "php_http_exception_object.h" +#include "php_http_persistent_handle_api.h" +#include "php_http_request_api.h" +#include "php_http_request_object.h" +#include "php_http_request_pool_api.h" +#include "php_http_requestpool_object.h" + +#ifndef HTTP_DEBUG_REQPOOLS +# define HTTP_DEBUG_REQPOOLS 0 +#endif + +#ifdef HTTP_HAVE_EVENT +typedef struct _http_request_pool_event_t { + struct event evnt; + http_request_pool *pool; +} http_request_pool_event; + +static void http_request_pool_timeout_callback(int socket, short action, void *event_data); +static void http_request_pool_event_callback(int socket, short action, void *event_data); +static int http_request_pool_socket_callback(CURL *easy, curl_socket_t s, int action, void *, void *); +static void http_request_pool_timer_callback(CURLM *multi, long timeout_ms, void *timer_data); +#endif + +static int http_request_pool_compare_handles(void *h1, void *h2); + +PHP_MINIT_FUNCTION(http_request_pool) +{ + if (SUCCESS != http_persistent_handle_provide("http_request_pool", curl_multi_init, (http_persistent_handle_dtor) curl_multi_cleanup, NULL)) { + return FAILURE; + } + return SUCCESS; +} + +#ifdef HTTP_HAVE_EVENT +PHP_RINIT_FUNCTION(http_request_pool) +{ + if (!HTTP_G->request.pool.event.base && !(HTTP_G->request.pool.event.base = event_init())) { + return FAILURE; + } + + return SUCCESS; +} +#endif + +/* {{{ http_request_pool *http_request_pool_init(http_request_pool *) */ +PHP_HTTP_API http_request_pool *_http_request_pool_init(http_request_pool *pool TSRMLS_DC) +{ + zend_bool free_pool; + +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "Initializing request pool %p\n", pool); +#endif + + if ((free_pool = (!pool))) { + pool = emalloc(sizeof(http_request_pool)); + pool->ch = NULL; + } + + if (SUCCESS != http_persistent_handle_acquire("http_request_pool", &pool->ch)) { + if (free_pool) { + efree(pool); + } + return NULL; + } + + TSRMLS_SET_CTX(pool->tsrm_ls); + +#ifdef HTTP_HAVE_EVENT + pool->timeout = ecalloc(1, sizeof(struct event)); + curl_multi_setopt(pool->ch, CURLMOPT_SOCKETDATA, pool); + curl_multi_setopt(pool->ch, CURLMOPT_SOCKETFUNCTION, http_request_pool_socket_callback); + curl_multi_setopt(pool->ch, CURLMOPT_TIMERDATA, pool); + curl_multi_setopt(pool->ch, CURLMOPT_TIMERFUNCTION, http_request_pool_timer_callback); +#endif + + pool->unfinished = 0; + zend_llist_init(&pool->finished, sizeof(zval *), (llist_dtor_func_t) ZVAL_PTR_DTOR, 0); + zend_llist_init(&pool->handles, sizeof(zval *), (llist_dtor_func_t) ZVAL_PTR_DTOR, 0); + +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "Initialized request pool %p\n", pool); +#endif + + return pool; +} +/* }}} */ + +/* {{{ STATUS http_request_pool_attach(http_request_pool *, zval *) */ +PHP_HTTP_API STATUS _http_request_pool_attach(http_request_pool *pool, zval *request) +{ +#ifdef ZTS + TSRMLS_FETCH_FROM_CTX(pool->tsrm_ls); +#endif + getObjectEx(http_request_object, req, request); + +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "Attaching HttpRequest(#%d) %p to pool %p\n", Z_OBJ_HANDLE_P(request), req, pool); +#endif + + if (req->pool) { + http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "HttpRequest object(#%d) is already member of %s HttpRequestPool", Z_OBJ_HANDLE_P(request), req->pool == pool ? "this" : "another"); + } else if (SUCCESS != http_request_object_requesthandler(req, request)) { + http_error_ex(HE_WARNING, HTTP_E_REQUEST, "Could not initialize HttpRequest object(#%d) for attaching to the HttpRequestPool", Z_OBJ_HANDLE_P(request)); + } else { + CURLMcode code = curl_multi_add_handle(pool->ch, req->request->ch); + + if (CURLM_OK != code) { + http_error_ex(HE_WARNING, HTTP_E_REQUEST_POOL, "Could not attach HttpRequest object(#%d) to the HttpRequestPool: %s", Z_OBJ_HANDLE_P(request), curl_multi_strerror(code)); + } else { + req->pool = pool; + + ZVAL_ADDREF(request); + zend_llist_add_element(&pool->handles, &request); + ++pool->unfinished; + +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "> %d HttpRequests attached to pool %p\n", zend_llist_count(&pool->handles), pool); +#endif + return SUCCESS; + } + } + return FAILURE; +} +/* }}} */ + +/* {{{ STATUS http_request_pool_detach(http_request_pool *, zval *) */ +PHP_HTTP_API STATUS _http_request_pool_detach(http_request_pool *pool, zval *request) +{ + CURLMcode code; +#ifdef ZTS + TSRMLS_FETCH_FROM_CTX(pool->tsrm_ls); +#endif + getObjectEx(http_request_object, req, request); + +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "Detaching HttpRequest(#%d) %p from pool %p\n", Z_OBJ_HANDLE_P(request), req, pool); +#endif + + if (!req->pool) { + /* not attached to any pool */ +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "HttpRequest object(#%d) %p is not attached to any HttpRequestPool\n", Z_OBJ_HANDLE_P(request), req); +#endif + } else if (req->pool != pool) { + http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "HttpRequest object(#%d) is not attached to this HttpRequestPool", Z_OBJ_HANDLE_P(request)); + } else if (req->request->_in_progress_cb) { + http_error_ex(HE_WARNING, HTTP_E_REQUEST_POOL, "HttpRequest object(#%d) cannot be detached from the HttpRequestPool while executing the progress callback", Z_OBJ_HANDLE_P(request)); + } else if (CURLM_OK != (code = curl_multi_remove_handle(pool->ch, req->request->ch))) { + http_error_ex(HE_WARNING, HTTP_E_REQUEST_POOL, "Could not detach HttpRequest object(#%d) from the HttpRequestPool: %s", Z_OBJ_HANDLE_P(request), curl_multi_strerror(code)); + } else { + req->pool = NULL; + zend_llist_del_element(&pool->finished, request, http_request_pool_compare_handles); + zend_llist_del_element(&pool->handles, request, http_request_pool_compare_handles); + +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "> %d HttpRequests remaining in pool %p\n", zend_llist_count(&pool->handles), pool); +#endif + + return SUCCESS; + } + return FAILURE; +} +/* }}} */ + +/* {{{ void http_request_pool_apply(http_request_pool *, http_request_pool_apply_func) */ +PHP_HTTP_API void _http_request_pool_apply(http_request_pool *pool, http_request_pool_apply_func cb) +{ + int count = zend_llist_count(&pool->handles); + + if (count) { + int i = 0; + zend_llist_position pos; + zval **handle, **handles = emalloc(count * sizeof(zval *)); + + for (handle = zend_llist_get_first_ex(&pool->handles, &pos); handle; handle = zend_llist_get_next_ex(&pool->handles, &pos)) { + handles[i++] = *handle; + } + + /* should never happen */ + if (i != count) { + zend_error(E_ERROR, "number of fetched request handles do not match overall count"); + count = i; + } + + for (i = 0; i < count; ++i) { + if (cb(pool, handles[i])) { + break; + } + } + efree(handles); + } +} +/* }}} */ + +/* {{{ void http_request_pool_apply_with_arg(http_request_pool *, http_request_pool_apply_with_arg_func, void *) */ +PHP_HTTP_API void _http_request_pool_apply_with_arg(http_request_pool *pool, http_request_pool_apply_with_arg_func cb, void *arg) +{ + int count = zend_llist_count(&pool->handles); + + if (count) { + int i = 0; + zend_llist_position pos; + zval **handle, **handles = emalloc(count * sizeof(zval *)); + + for (handle = zend_llist_get_first_ex(&pool->handles, &pos); handle; handle = zend_llist_get_next_ex(&pool->handles, &pos)) { + handles[i++] = *handle; + } + + /* should never happen */ + if (i != count) { + zend_error(E_ERROR, "number of fetched request handles do not match overall count"); + count = i; + } + + for (i = 0; i < count; ++i) { + if (cb(pool, handles[i], arg)) { + break; + } + } + efree(handles); + } +} +/* }}} */ + +/* {{{ void http_request_pool_detach_all(http_request_pool *) */ +PHP_HTTP_API void _http_request_pool_detach_all(http_request_pool *pool) +{ +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "Detaching %d requests from pool %p\n", zend_llist_count(&pool->handles), pool); +#endif + http_request_pool_apply(pool, _http_request_pool_detach); +} +/* }}} */ + +/* {{{ STATUS http_request_pool_send(http_request_pool *) */ +PHP_HTTP_API STATUS _http_request_pool_send(http_request_pool *pool) +{ + TSRMLS_FETCH_FROM_CTX(pool->tsrm_ls); + +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "Attempt to send %d requests of pool %p\n", zend_llist_count(&pool->handles), pool); +#endif + +#ifdef HTTP_HAVE_EVENT + if (pool->useevents) { + do { +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "& Starting event dispatcher of pool %p\n", pool); +#endif + event_base_dispatch(HTTP_G->request.pool.event.base); + } while (pool->unfinished); + } else +#endif + { + while (http_request_pool_perform(pool)) { + if (SUCCESS != http_request_pool_select(pool)) { +#ifdef PHP_WIN32 + /* see http://msdn.microsoft.com/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp */ + http_error_ex(HE_WARNING, HTTP_E_SOCKET, "WinSock error: %d", WSAGetLastError()); +#else + http_error(HE_WARNING, HTTP_E_SOCKET, strerror(errno)); +#endif + return FAILURE; + } + } + } + +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "Finished sending %d HttpRequests of pool %p (still unfinished: %d)\n", zend_llist_count(&pool->handles), pool, pool->unfinished); +#endif + + return SUCCESS; +} +/* }}} */ + +/* {{{ void http_request_pool_dtor(http_request_pool *) */ +PHP_HTTP_API void _http_request_pool_dtor(http_request_pool *pool) +{ + TSRMLS_FETCH_FROM_CTX(pool->tsrm_ls); + +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "Destructing request pool %p\n", pool); +#endif + +#ifdef HTTP_HAVE_EVENT + efree(pool->timeout); +#endif + + http_request_pool_detach_all(pool); + + pool->unfinished = 0; + zend_llist_clean(&pool->finished); + zend_llist_clean(&pool->handles); + http_persistent_handle_release("http_request_pool", &pool->ch); +} +/* }}} */ + +#ifdef PHP_WIN32 +# define SELECT_ERROR SOCKET_ERROR +#else +# define SELECT_ERROR -1 +#endif + +/* {{{ STATUS http_request_pool_select(http_request_pool *) */ +PHP_HTTP_API STATUS _http_request_pool_select(http_request_pool *pool) +{ + return http_request_pool_select_ex(pool, NULL); +} +/* }}} */ + +/* {{{ STATUS http_request_pool_select_ex(http_request_pool *, struct timeval *) */ +PHP_HTTP_API STATUS _http_request_pool_select_ex(http_request_pool *pool, struct timeval *custom_timeout) +{ + int MAX; + fd_set R, W, E; + struct timeval timeout; + +#ifdef HTTP_HAVE_EVENT + if (pool->useevents) { + TSRMLS_FETCH_FROM_CTX(pool->tsrm_ls); + http_error(HE_WARNING, HTTP_E_RUNTIME, "not implemented; use HttpRequest callbacks"); + return FAILURE; + } +#endif + + if (custom_timeout && timerisset(custom_timeout)) { + timeout = *custom_timeout; + } else { + http_request_pool_timeout(pool, &timeout); + } + + FD_ZERO(&R); + FD_ZERO(&W); + FD_ZERO(&E); + + if (CURLM_OK == curl_multi_fdset(pool->ch, &R, &W, &E, &MAX)) { + if (MAX == -1) { + http_sleep((double) timeout.tv_sec + (double) (timeout.tv_usec / HTTP_MCROSEC)); + return SUCCESS; + } else if (SELECT_ERROR != select(MAX + 1, &R, &W, &E, &timeout)) { + return SUCCESS; + } + } + return FAILURE; +} +/* }}} */ + +/* {{{ int http_request_pool_perform(http_request_pool *) */ +PHP_HTTP_API int _http_request_pool_perform(http_request_pool *pool) +{ + TSRMLS_FETCH_FROM_CTX(pool->tsrm_ls); + +#ifdef HTTP_HAVE_EVENT + if (pool->useevents) { + http_error(HE_WARNING, HTTP_E_RUNTIME, "not implemented; use HttpRequest callbacks"); + return FAILURE; + } +#endif + + while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(pool->ch, &pool->unfinished)); + +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "%u unfinished requests of pool %p remaining\n", pool->unfinished, pool); +#endif + + http_request_pool_responsehandler(pool); + + return pool->unfinished; +} +/* }}} */ + +/* {{{ void http_request_pool_responsehandler(http_request_pool *) */ +void _http_request_pool_responsehandler(http_request_pool *pool) +{ + CURLMsg *msg; + int remaining = 0; + TSRMLS_FETCH_FROM_CTX(pool->tsrm_ls); + + do { + msg = curl_multi_info_read(pool->ch, &remaining); + if (msg && CURLMSG_DONE == msg->msg) { + if (CURLE_OK != msg->data.result) { + http_request_storage *st = http_request_storage_get(msg->easy_handle); + http_error_ex(HE_WARNING, HTTP_E_REQUEST, "%s; %s (%s)", curl_easy_strerror(msg->data.result), st?st->errorbuffer:"", st?st->url:""); + } + http_request_pool_apply_with_arg(pool, _http_request_pool_apply_responsehandler, msg->easy_handle); + } + } while (remaining); +} +/* }}} */ + +/* {{{ int http_request_pool_apply_responsehandler(http_request_pool *, zval *, void *) */ +int _http_request_pool_apply_responsehandler(http_request_pool *pool, zval *req, void *ch) +{ +#ifdef ZTS + TSRMLS_FETCH_FROM_CTX(pool->tsrm_ls); +#endif + getObjectEx(http_request_object, obj, req); + + if ((!ch) || obj->request->ch == (CURL *) ch) { + +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "Fetching data from HttpRequest(#%d) %p of pool %p\n", Z_OBJ_HANDLE_P(req), obj, obj->pool); +#endif + + ZVAL_ADDREF(req); + zend_llist_add_element(&obj->pool->finished, &req); + http_request_object_responsehandler(obj, req); + return 1; + } + return 0; +} +/* }}} */ + +/* {{{ struct timeval *_http_request_pool_timeout(http_request_pool *, struct timeval *) */ +struct timeval *_http_request_pool_timeout(http_request_pool *pool, struct timeval *timeout) +{ +#ifdef HAVE_CURL_MULTI_TIMEOUT + long max_tout = 1000; + + if ((CURLM_OK == curl_multi_timeout(pool->ch, &max_tout)) && (max_tout > 0)) { + timeout->tv_sec = max_tout / 1000; + timeout->tv_usec = (max_tout % 1000) * 1000; + } else { +#endif + timeout->tv_sec = 0; + timeout->tv_usec = 1000; +#ifdef HAVE_CURL_MULTI_TIMEOUT + } +#endif + +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "Calculating timeout (%lu, %lu) of pool %p\n", (ulong) timeout->tv_sec, (ulong) timeout->tv_usec, pool); +#endif + + return timeout; +} +/* }}} */ + +/*#*/ + +/* {{{ static int http_request_pool_compare_handles(void *, void *) */ +static int http_request_pool_compare_handles(void *h1, void *h2) +{ + return (Z_OBJ_HANDLE_PP((zval **) h1) == Z_OBJ_HANDLE_P((zval *) h2)); +} +/* }}} */ + +#ifdef HTTP_HAVE_EVENT +/* {{{ static void http_request_pool_timeout_callback(int, short, void *) */ +static void http_request_pool_timeout_callback(int socket, short action, void *event_data) +{ + http_request_pool *pool = event_data; + + if (pool->useevents) { + CURLMcode rc; + TSRMLS_FETCH_FROM_CTX(pool->tsrm_ls); + +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "Timeout occurred of pool %p\n", pool); +#endif + + while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket(pool->ch, CURL_SOCKET_TIMEOUT, &pool->unfinished))); + + if (CURLM_OK != rc) { + http_error(HE_WARNING, HTTP_E_SOCKET, curl_multi_strerror(rc)); + } + + http_request_pool_responsehandler(pool); + } +} +/* }}} */ + +/* {{{ static void http_request_pool_event_callback(int, short, void *) */ +static void http_request_pool_event_callback(int socket, short action, void *event_data) +{ + http_request_pool_event *ev = event_data; + http_request_pool *pool = ev->pool; + + if (pool->useevents) { + CURLMcode rc = CURLE_OK; + TSRMLS_FETCH_FROM_CTX(ev->pool->tsrm_ls); + +#if HTTP_DEBUG_REQPOOLS + { + static const char event_strings[][20] = {"NONE","TIMEOUT","READ","TIMEOUT|READ","WRITE","TIMEOUT|WRITE","READ|WRITE","TIMEOUT|READ|WRITE","SIGNAL"}; + fprintf(stderr, "Event on socket %d (%s) event %p of pool %p\n", socket, event_strings[action], ev, pool); + } +#endif + + /* don't use 'ev' below this loop as it might 've been freed in the socket callback */ + do { +#ifdef HAVE_CURL_MULTI_SOCKET_ACTION + switch (action & (EV_READ|EV_WRITE)) { + case EV_READ: + rc = curl_multi_socket_action(pool->ch, socket, CURL_CSELECT_IN, &pool->unfinished); + break; + case EV_WRITE: + rc = curl_multi_socket_action(pool->ch, socket, CURL_CSELECT_OUT, &pool->unfinished); + break; + case EV_READ|EV_WRITE: + rc = curl_multi_socket_action(pool->ch, socket, CURL_CSELECT_IN|CURL_CSELECT_OUT, &pool->unfinished); + break; + default: + http_error_ex(HE_WARNING, HTTP_E_SOCKET, "Unknown event %d", (int) action); + return; + } +#else + rc = curl_multi_socket(pool->ch, socket, &pool->unfinished); +#endif + } while (CURLM_CALL_MULTI_PERFORM == rc); + + switch (rc) { + case CURLM_BAD_SOCKET: +#if 0 + fprintf(stderr, "!!! Bad socket: %d (%d)\n", socket, (int) action); +#endif + case CURLM_OK: + break; + default: + http_error(HE_WARNING, HTTP_E_SOCKET, curl_multi_strerror(rc)); + break; + } + + http_request_pool_responsehandler(pool); + + /* remove timeout if there are no transfers left */ + if (!pool->unfinished && event_initialized(pool->timeout) && event_pending(pool->timeout, EV_TIMEOUT, NULL)) { + event_del(pool->timeout); +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "Removed timeout of pool %p\n", pool); +#endif + } + } +} +/* }}} */ + +/* {{{ static int http_request_pool_socket_callback(CURL *, curl_socket_t, int, void *, void *) */ +static int http_request_pool_socket_callback(CURL *easy, curl_socket_t sock, int action, void *socket_data, void *assign_data) +{ + http_request_pool *pool = socket_data; + + if (pool->useevents) { + int events = EV_PERSIST; + http_request_pool_event *ev = assign_data; + TSRMLS_FETCH_FROM_CTX(pool->tsrm_ls); + + if (!ev) { + ev = ecalloc(1, sizeof(http_request_pool_event)); + ev->pool = pool; + curl_multi_assign(pool->ch, sock, ev); + event_base_set(HTTP_G->request.pool.event.base, &ev->evnt); + } else { + event_del(&ev->evnt); + } + +#if HTTP_DEBUG_REQPOOLS + { + static const char action_strings[][8] = {"NONE", "IN", "OUT", "INOUT", "REMOVE"}; + http_request *r; + curl_easy_getinfo(easy, CURLINFO_PRIVATE, &r); + fprintf(stderr, "Callback on socket %2d (%8s) event %p of pool %p (%d)\n", (int) sock, action_strings[action], ev, pool, pool->unfinished); + } +#endif + + 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); + case CURL_POLL_NONE: + return 0; + + default: + http_error_ex(HE_WARNING, HTTP_E_SOCKET, "Unknown socket action %d", action); + return -1; + } + + event_set(&ev->evnt, sock, events, http_request_pool_event_callback, ev); + event_add(&ev->evnt, NULL); + } + + return 0; +} +/* }}} */ + +/* {{{ static void http_request_pool_timer_callback(CURLM *, long, void*) */ +static void http_request_pool_timer_callback(CURLM *multi, long timeout_ms, void *timer_data) +{ + http_request_pool *pool = timer_data; + + if (pool->useevents) { + TSRMLS_FETCH_FROM_CTX(pool->tsrm_ls); + struct timeval timeout; + + if (!event_initialized(pool->timeout)) { + event_set(pool->timeout, -1, 0, http_request_pool_timeout_callback, pool); + event_base_set(HTTP_G->request.pool.event.base, pool->timeout); + } else if (event_pending(pool->timeout, EV_TIMEOUT, NULL)) { + event_del(pool->timeout); + } + + if (timeout_ms > 0) { + timeout.tv_sec = timeout_ms / 1000; + timeout.tv_usec = (timeout_ms % 1000) * 1000; + } else { + http_request_pool_timeout(pool, &timeout); + } + + event_add(pool->timeout, &timeout); + +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "Updating timeout %lu (%lu, %lu) of pool %p\n", (ulong) timeout_ms, (ulong) timeout.tv_sec, (ulong) timeout.tv_usec, pool); +#endif + } +} +/* }}} */ +#endif /* HTTP_HAVE_EVENT */ + +#endif /* ZEND_ENGINE_2 && 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 + */ + --- /dev/null +++ b/ext/http/http_requestdatashare_object.c @@ -0,0 +1,315 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_requestdatashare_object.c 300299 2010-06-09 06:23:16Z mike $ */ + +#define HTTP_WANT_CURL +#include "php_http.h" + +#if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL) + +#include "zend_interfaces.h" + +#include "php_http_api.h" +#include "php_http_exception_object.h" +#include "php_http_request_api.h" +#include "php_http_request_object.h" +#include "php_http_request_datashare_api.h" +#include "php_http_requestdatashare_object.h" + +#define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpRequestDataShare, method, 0, req_args) +#define HTTP_EMPTY_ARGS(method) HTTP_EMPTY_ARGS_EX(HttpRequestDataShare, method, 0) +#define HTTP_RSHARE_ME(method, visibility) PHP_ME(HttpRequestDataShare, method, HTTP_ARGS(HttpRequestDataShare, method), visibility) + +#if defined(HAVE_SPL) && !defined(WONKY) +/* SPL doesn't install its headers */ +extern PHPAPI zend_class_entry *spl_ce_Countable; +#endif + +HTTP_EMPTY_ARGS(__destruct); +HTTP_EMPTY_ARGS(count); + +HTTP_BEGIN_ARGS(attach, 1) + HTTP_ARG_OBJ(HttpRequest, request, 0) +HTTP_END_ARGS; +HTTP_BEGIN_ARGS(detach, 1) + HTTP_ARG_OBJ(HttpRequest, request, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(reset); + +HTTP_BEGIN_ARGS(factory, 0) + HTTP_ARG_VAL(global, 0) + HTTP_ARG_VAL(class_name, 0) +HTTP_END_ARGS; + +#ifndef WONKY +HTTP_BEGIN_ARGS(singleton, 0) + HTTP_ARG_VAL(global, 0) +HTTP_END_ARGS; +#endif + + +#define http_requestdatashare_object_read_prop _http_requestdatashare_object_read_prop +static zval *_http_requestdatashare_object_read_prop(zval *object, zval *member, int type ZEND_LITERAL_KEY_DC TSRMLS_DC); +#define http_requestdatashare_object_write_prop _http_requestdatashare_object_write_prop +static void _http_requestdatashare_object_write_prop(zval *object, zval *member, zval *value ZEND_LITERAL_KEY_DC TSRMLS_DC); +#define http_requestdatashare_instantiate(t, g) _http_requestdatashare_instantiate((t), (g) TSRMLS_CC) +static inline zval *_http_requestdatashare_instantiate(zval *this_ptr, zend_bool global TSRMLS_DC); + +#define THIS_CE http_requestdatashare_object_ce +zend_class_entry *http_requestdatashare_object_ce; +zend_function_entry http_requestdatashare_object_fe[] = { + HTTP_RSHARE_ME(__destruct, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR) + HTTP_RSHARE_ME(count, ZEND_ACC_PUBLIC) + HTTP_RSHARE_ME(attach, ZEND_ACC_PUBLIC) + HTTP_RSHARE_ME(detach, ZEND_ACC_PUBLIC) + HTTP_RSHARE_ME(reset, ZEND_ACC_PUBLIC) + HTTP_RSHARE_ME(factory, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) +#ifndef WONKY + HTTP_RSHARE_ME(singleton, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) +#endif + EMPTY_FUNCTION_ENTRY +}; +static zend_object_handlers http_requestdatashare_object_handlers; + +PHP_MINIT_FUNCTION(http_requestdatashare_object) +{ + HTTP_REGISTER_CLASS_EX(HttpRequestDataShare, http_requestdatashare_object, NULL, 0); + http_requestdatashare_object_handlers.clone_obj = NULL; + http_requestdatashare_object_handlers.read_property = http_requestdatashare_object_read_prop; + http_requestdatashare_object_handlers.write_property = http_requestdatashare_object_write_prop; + +#if defined(HAVE_SPL) && !defined(WONKY) + zend_class_implements(http_requestdatashare_object_ce TSRMLS_CC, 1, spl_ce_Countable); +#endif + + zend_declare_property_null(THIS_CE, ZEND_STRS("instance")-1, (ZEND_ACC_STATIC|ZEND_ACC_PRIVATE) TSRMLS_CC); + zend_declare_property_bool(THIS_CE, ZEND_STRS("cookie")-1, 0, ZEND_ACC_PUBLIC TSRMLS_CC); + zend_declare_property_bool(THIS_CE, ZEND_STRS("dns")-1, 0, ZEND_ACC_PUBLIC TSRMLS_CC); + zend_declare_property_bool(THIS_CE, ZEND_STRS("ssl")-1, 0, ZEND_ACC_PUBLIC TSRMLS_CC); + zend_declare_property_bool(THIS_CE, ZEND_STRS("connect")-1, 0, ZEND_ACC_PUBLIC TSRMLS_CC); + + return SUCCESS; +} + +zend_object_value _http_requestdatashare_object_new(zend_class_entry *ce TSRMLS_DC) +{ + return http_requestdatashare_object_new_ex(ce, NULL, NULL); +} + +zend_object_value _http_requestdatashare_object_new_ex(zend_class_entry *ce, http_request_datashare *share, http_requestdatashare_object **ptr TSRMLS_DC) +{ + zend_object_value ov; + http_requestdatashare_object *o; + + o = ecalloc(1, sizeof(http_requestdatashare_object)); + o->zo.ce = ce; + + if (share) { + o->share = share; + } else { + o->share = http_request_datashare_new(); + } + + if (ptr) { + *ptr = o; + } + + ALLOC_HASHTABLE(OBJ_PROP(o)); + zend_hash_init(OBJ_PROP(o), zend_hash_num_elements(&ce->default_properties), NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(OBJ_PROP(o), &ce->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + + ov.handle = putObject(http_requestdatashare_object, o); + ov.handlers = &http_requestdatashare_object_handlers; + + return ov; +} + +void _http_requestdatashare_object_free(zend_object *object TSRMLS_DC) +{ + http_requestdatashare_object *o = (http_requestdatashare_object *) object; + + if (!o->share->persistent) { + http_request_datashare_free(&o->share); + } + freeObject(o); +} + +static zval *_http_requestdatashare_object_read_prop(zval *object, zval *member, int type ZEND_LITERAL_KEY_DC TSRMLS_DC) +{ + if (type == BP_VAR_W && zend_hash_exists(&THIS_CE->default_properties, Z_STRVAL_P(member), Z_STRLEN_P(member)+1)) { + zend_error(E_ERROR, "Cannot access HttpRequestDataShare default properties by reference or array key/index"); + return NULL; + } + + return zend_get_std_object_handlers()->read_property(object, member, type ZEND_LITERAL_KEY_CC TSRMLS_CC); +} + +static void _http_requestdatashare_object_write_prop(zval *object, zval *member, zval *value ZEND_LITERAL_KEY_DC TSRMLS_DC) +{ + if (zend_hash_exists(&THIS_CE->default_properties, Z_STRVAL_P(member), Z_STRLEN_P(member)+1)) { + int status; + getObjectEx(http_requestdatashare_object, obj, object); + + status = http_request_datashare_set(obj->share, Z_STRVAL_P(member), Z_STRLEN_P(member), (zend_bool) i_zend_is_true(value)); + if (SUCCESS != status) { + return; + } + } + + zend_get_std_object_handlers()->write_property(object, member, value ZEND_LITERAL_KEY_CC TSRMLS_CC); +} + +/* {{{ proto void HttpRequestDataShare::__destruct() + Clean up HttpRequestDataShare object. */ +PHP_METHOD(HttpRequestDataShare, __destruct) +{ + NO_ARGS { + getObject(http_requestdatashare_object, obj); + http_request_datashare_detach_all(obj->share); + } +} +/* }}} */ + +/* {{{ proto int HttpRequestDataShare::count() + Implements Countable::count(). */ +PHP_METHOD(HttpRequestDataShare, count) +{ + getObject(http_requestdatashare_object, obj); + + NO_ARGS; + + RETURN_LONG(zend_llist_count(HTTP_RSHARE_HANDLES(obj->share))); +} +/* }}} */ + +PHP_METHOD(HttpRequestDataShare, attach) +{ + zval *request; + getObject(http_requestdatashare_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, http_request_object_ce)) { + RETURN_FALSE; + } + + RETURN_SUCCESS(http_request_datashare_attach(obj->share, request)); +} + +PHP_METHOD(HttpRequestDataShare, detach) +{ + zval *request; + getObject(http_requestdatashare_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, http_request_object_ce)) { + RETURN_FALSE; + } + + RETURN_SUCCESS(http_request_datashare_detach(obj->share, request)); +} + +PHP_METHOD(HttpRequestDataShare, reset) +{ + NO_ARGS { + getObject(http_requestdatashare_object, obj); + http_request_datashare_detach_all(obj->share); + } +} + +PHP_METHOD(HttpRequestDataShare, factory) +{ + zend_bool global = 0; + char *cn = NULL; + int cl = 0; + zend_object_value ov; + + SET_EH_THROW_HTTP(); + if ( SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|bs", &global, &cn, &cl) && + SUCCESS == http_object_new(&ov, cn, cl, _http_requestdatashare_object_new_ex, http_requestdatashare_object_ce, NULL, NULL)) { + RETVAL_OBJVAL(ov, 0); + http_requestdatashare_instantiate(return_value, global); + } + SET_EH_NORMAL(); +} + +#ifndef WONKY +/* {{{ proto static HttpRequestDataShare HttpRequestDataShare::singleton([bool global = false]) + Get a single instance (differentiates between the global setting). */ +PHP_METHOD(HttpRequestDataShare, singleton) +{ + zend_bool global = 0; + zval *instance = *zend_std_get_static_property(THIS_CE, ZEND_STRS("instance")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC); + + SET_EH_THROW_HTTP(); + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &global)) { + zval **zobj_ptr = NULL, *zobj = NULL; + + if (Z_TYPE_P(instance) == IS_ARRAY) { + if (SUCCESS == zend_hash_index_find(Z_ARRVAL_P(instance), global, (void *) &zobj_ptr)) { + RETVAL_ZVAL(*zobj_ptr, 1, 0); + } else { + zobj = http_requestdatashare_instantiate(NULL, global); + add_index_zval(instance, global, zobj); + RETVAL_OBJECT(zobj, 1); + } + } else { + MAKE_STD_ZVAL(instance); + array_init(instance); + + zobj = http_requestdatashare_instantiate(NULL, global); + add_index_zval(instance, global, zobj); + RETVAL_OBJECT(zobj, 1); + + zend_update_static_property(THIS_CE, ZEND_STRS("instance")-1, instance TSRMLS_CC); + zval_ptr_dtor(&instance); + } + } + SET_EH_NORMAL(); +} +/* }}} */ +#endif /* !WONKY */ + +static inline zval *_http_requestdatashare_instantiate(zval *this_ptr, zend_bool global TSRMLS_DC) +{ + if (!this_ptr) { + MAKE_STD_ZVAL(this_ptr); + Z_TYPE_P(this_ptr) = IS_OBJECT; + this_ptr->value.obj = http_requestdatashare_object_new_ex(http_requestdatashare_object_ce, global ? http_request_datashare_global_get() : NULL, NULL); + } + if (global) { + if (HTTP_G->request.datashare.cookie) { + zend_update_property_bool(THIS_CE, getThis(), ZEND_STRS("cookie")-1, HTTP_G->request.datashare.cookie TSRMLS_CC); + } + if (HTTP_G->request.datashare.dns) { + zend_update_property_bool(THIS_CE, getThis(), ZEND_STRS("dns")-1, HTTP_G->request.datashare.dns TSRMLS_CC); + } + if (HTTP_G->request.datashare.ssl) { + zend_update_property_bool(THIS_CE, getThis(), ZEND_STRS("ssl")-1, HTTP_G->request.datashare.ssl TSRMLS_CC); + } + if (HTTP_G->request.datashare.connect) { + zend_update_property_bool(THIS_CE, getThis(), ZEND_STRS("connect")-1, HTTP_G->request.datashare.connect TSRMLS_CC); + } + } + return this_ptr; +} + +#endif /* ZEND_ENGINE_2 && 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 + */ + --- /dev/null +++ b/ext/http/http_requestpool_object.c @@ -0,0 +1,466 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_requestpool_object.c 300299 2010-06-09 06:23:16Z mike $ */ + +#define HTTP_WANT_CURL +#include "php_http.h" + +#if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL) + +#include "zend_interfaces.h" + +#include "php_http_api.h" +#include "php_http_exception_object.h" +#include "php_http_request_api.h" +#include "php_http_request_object.h" +#include "php_http_request_pool_api.h" +#include "php_http_requestpool_object.h" + +#if defined(HAVE_SPL) && !defined(WONKY) +/* SPL doesn't install its headers */ +extern PHPAPI zend_class_entry *spl_ce_Countable; +#endif + +#define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpRequestPool, method, 0, req_args) +#define HTTP_EMPTY_ARGS(method) HTTP_EMPTY_ARGS_EX(HttpRequestPool, method, 0) +#define HTTP_REQPOOL_ME(method, visibility) PHP_ME(HttpRequestPool, method, HTTP_ARGS(HttpRequestPool, method), visibility) + +HTTP_EMPTY_ARGS(__construct); + +HTTP_EMPTY_ARGS(__destruct); +HTTP_EMPTY_ARGS(reset); + +HTTP_BEGIN_ARGS(attach, 1) + HTTP_ARG_OBJ(HttpRequest, request, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(detach, 1) + HTTP_ARG_OBJ(HttpRequest, request, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(send); +HTTP_EMPTY_ARGS(socketPerform); +HTTP_BEGIN_ARGS(socketSelect, 0) + HTTP_ARG_VAL(timeout, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(valid); +HTTP_EMPTY_ARGS(current); +HTTP_EMPTY_ARGS(key); +HTTP_EMPTY_ARGS(next); +HTTP_EMPTY_ARGS(rewind); + +HTTP_EMPTY_ARGS(count); + +HTTP_EMPTY_ARGS(getAttachedRequests); +HTTP_EMPTY_ARGS(getFinishedRequests); + +HTTP_BEGIN_ARGS(enablePipelining, 0) + HTTP_ARG_VAL(enable, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(enableEvents, 0) + HTTP_ARG_VAL(enable, 0) +HTTP_END_ARGS; + +zend_class_entry *http_requestpool_object_ce; +zend_function_entry http_requestpool_object_fe[] = { + HTTP_REQPOOL_ME(__construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) + HTTP_REQPOOL_ME(__destruct, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR) + HTTP_REQPOOL_ME(attach, ZEND_ACC_PUBLIC) + HTTP_REQPOOL_ME(detach, ZEND_ACC_PUBLIC) + HTTP_REQPOOL_ME(send, ZEND_ACC_PUBLIC) + HTTP_REQPOOL_ME(reset, ZEND_ACC_PUBLIC) + + HTTP_REQPOOL_ME(socketPerform, ZEND_ACC_PROTECTED) + HTTP_REQPOOL_ME(socketSelect, ZEND_ACC_PROTECTED) + + /* implements Iterator */ + HTTP_REQPOOL_ME(valid, ZEND_ACC_PUBLIC) + HTTP_REQPOOL_ME(current, ZEND_ACC_PUBLIC) + HTTP_REQPOOL_ME(key, ZEND_ACC_PUBLIC) + HTTP_REQPOOL_ME(next, ZEND_ACC_PUBLIC) + HTTP_REQPOOL_ME(rewind, ZEND_ACC_PUBLIC) + + /* implmenents Countable */ + HTTP_REQPOOL_ME(count, ZEND_ACC_PUBLIC) + + HTTP_REQPOOL_ME(getAttachedRequests, ZEND_ACC_PUBLIC) + HTTP_REQPOOL_ME(getFinishedRequests, ZEND_ACC_PUBLIC) + + HTTP_REQPOOL_ME(enablePipelining, ZEND_ACC_PUBLIC) + HTTP_REQPOOL_ME(enableEvents, ZEND_ACC_PUBLIC) + + EMPTY_FUNCTION_ENTRY +}; +static zend_object_handlers http_requestpool_object_handlers; + +PHP_MINIT_FUNCTION(http_requestpool_object) +{ + HTTP_REGISTER_CLASS_EX(HttpRequestPool, http_requestpool_object, NULL, 0); + http_requestpool_object_handlers.clone_obj = NULL; + +#if defined(HAVE_SPL) && !defined(WONKY) + zend_class_implements(http_requestpool_object_ce TSRMLS_CC, 2, spl_ce_Countable, zend_ce_iterator); +#else + zend_class_implements(http_requestpool_object_ce TSRMLS_CC, 1, zend_ce_iterator); +#endif + + return SUCCESS; +} + +zend_object_value _http_requestpool_object_new(zend_class_entry *ce TSRMLS_DC) +{ + zend_object_value ov; + http_requestpool_object *o; + + o = ecalloc(1, sizeof(http_requestpool_object)); + o->zo.ce = ce; + + http_request_pool_init(&o->pool); + + ALLOC_HASHTABLE(OBJ_PROP(o)); + zend_hash_init(OBJ_PROP(o), zend_hash_num_elements(&ce->default_properties), NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(OBJ_PROP(o), &ce->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + + ov.handle = putObject(http_requestpool_object, o); + ov.handlers = &http_requestpool_object_handlers; + + return ov; +} + +void _http_requestpool_object_free(zend_object *object TSRMLS_DC) +{ + http_requestpool_object *o = (http_requestpool_object *) object; + + http_request_pool_dtor(&o->pool); + freeObject(o); +} + +#define http_requestpool_object_llist2array _http_requestpool_object_llist2array +static void _http_requestpool_object_llist2array(zval **req, zval *array TSRMLS_DC) +{ + ZVAL_ADDREF(*req); + add_next_index_zval(array, *req); +} + +/* ### USERLAND ### */ + +/* {{{ proto void HttpRequestPool::__construct([HttpRequest request[, ...]]) + Creates a new HttpRequestPool object instance. */ +PHP_METHOD(HttpRequestPool, __construct) +{ + int argc = ZEND_NUM_ARGS(); + zval ***argv = safe_emalloc(argc, sizeof(zval *), 0); + getObject(http_requestpool_object, obj); + + SET_EH_THROW_HTTP(); + if (SUCCESS == zend_get_parameters_array_ex(argc, argv)) { + int i; + + for (i = 0; i < argc; ++i) { + if (Z_TYPE_PP(argv[i]) == IS_OBJECT && instanceof_function(Z_OBJCE_PP(argv[i]), http_request_object_ce TSRMLS_CC)) { + http_request_pool_attach(&obj->pool, *(argv[i])); + } + } + } + efree(argv); + http_final(HTTP_EX_CE(request_pool)); + SET_EH_NORMAL(); +} +/* }}} */ + +/* {{{ proto void HttpRequestPool::__destruct() + Clean up HttpRequestPool object. */ +PHP_METHOD(HttpRequestPool, __destruct) +{ + getObject(http_requestpool_object, obj); + + NO_ARGS; + + http_request_pool_detach_all(&obj->pool); +} +/* }}} */ + +/* {{{ proto void HttpRequestPool::reset() + Detach all attached HttpRequest objects. */ +PHP_METHOD(HttpRequestPool, reset) +{ + getObject(http_requestpool_object, obj); + + NO_ARGS; + + obj->iterator.pos = 0; + http_request_pool_detach_all(&obj->pool); +} + +/* {{{ proto bool HttpRequestPool::attach(HttpRequest request) + Attach an HttpRequest object to this HttpRequestPool. WARNING: set all options prior attaching! */ +PHP_METHOD(HttpRequestPool, attach) +{ + zval *request; + STATUS status = FAILURE; + getObject(http_requestpool_object, obj); + + SET_EH_THROW_HTTP(); + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, http_request_object_ce)) { + if (obj->iterator.pos > 0 && obj->iterator.pos < zend_llist_count(&obj->pool.handles)) { + http_error(HE_THROW, HTTP_E_REQUEST_POOL, "Cannot attach to the HttpRequestPool while the iterator is active"); + } else { + status = http_request_pool_attach(&obj->pool, request); + } + } + SET_EH_NORMAL(); + RETURN_SUCCESS(status); +} +/* }}} */ + +/* {{{ proto bool HttpRequestPool::detach(HttpRequest request) + Detach an HttpRequest object from this HttpRequestPool. */ +PHP_METHOD(HttpRequestPool, detach) +{ + zval *request; + STATUS status = FAILURE; + getObject(http_requestpool_object, obj); + + SET_EH_THROW_HTTP(); + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, http_request_object_ce)) { + obj->iterator.pos = -1; + status = http_request_pool_detach(&obj->pool, request); + } + SET_EH_NORMAL(); + RETURN_SUCCESS(status); +} +/* }}} */ + +/* {{{ proto bool HttpRequestPool::send() + Send all attached HttpRequest objects in parallel. */ +PHP_METHOD(HttpRequestPool, send) +{ + STATUS status; + getObject(http_requestpool_object, obj); + + NO_ARGS; + + SET_EH_THROW_HTTP(); + status = http_request_pool_send(&obj->pool); + SET_EH_NORMAL(); + + /* rethrow as HttpRequestPoolException */ + http_final(HTTP_EX_CE(request_pool)); + + RETURN_SUCCESS(status); +} +/* }}} */ + +/* {{{ proto protected bool HttpRequestPool::socketPerform() + Returns TRUE until each request has finished its transaction. */ +PHP_METHOD(HttpRequestPool, socketPerform) +{ + getObject(http_requestpool_object, obj); + + NO_ARGS; + + if (0 < http_request_pool_perform(&obj->pool)) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto protected bool HttpRequestPool::socketSelect([double timeout]) */ +PHP_METHOD(HttpRequestPool, socketSelect) +{ + double timeout = 0; + struct timeval custom_timeout, *custom_timeout_ptr = NULL; + getObject(http_requestpool_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|d", &timeout)) { + RETURN_FALSE; + } + if (ZEND_NUM_ARGS() && timeout > 0) { + custom_timeout.tv_sec = (time_t) timeout; + custom_timeout.tv_usec = HTTP_USEC(timeout) % HTTP_MCROSEC; + custom_timeout_ptr = &custom_timeout; + } + + RETURN_SUCCESS(http_request_pool_select_ex(&obj->pool, custom_timeout_ptr)); +} +/* }}} */ + +/* {{{ proto bool HttpRequestPool::valid() + Implements Iterator::valid(). */ +PHP_METHOD(HttpRequestPool, valid) +{ + NO_ARGS; + + if (return_value_used) { + getObject(http_requestpool_object, obj); + RETURN_BOOL(obj->iterator.pos >= 0 && obj->iterator.pos < zend_llist_count(&obj->pool.handles)); + } +} +/* }}} */ + +/* {{{ proto HttpRequest HttpRequestPool::current() + Implements Iterator::current(). */ +PHP_METHOD(HttpRequestPool, current) +{ + NO_ARGS; + + if (return_value_used) { + long pos = 0; + zval **current = NULL; + zend_llist_position lpos; + getObject(http_requestpool_object, obj); + + if (obj->iterator.pos < zend_llist_count(&obj->pool.handles)) { + for ( current = zend_llist_get_first_ex(&obj->pool.handles, &lpos); + current && obj->iterator.pos != pos++; + current = zend_llist_get_next_ex(&obj->pool.handles, &lpos)); + if (current) { + RETURN_OBJECT(*current, 1); + } + } + RETURN_NULL(); + } +} +/* }}} */ + +/* {{{ proto int HttpRequestPool::key() + Implements Iterator::key(). */ +PHP_METHOD(HttpRequestPool, key) +{ + NO_ARGS; + + if (return_value_used) { + getObject(http_requestpool_object, obj); + RETURN_LONG(obj->iterator.pos); + } +} +/* }}} */ + +/* {{{ proto void HttpRequestPool::next() + Implements Iterator::next(). */ +PHP_METHOD(HttpRequestPool, next) +{ + NO_ARGS { + getObject(http_requestpool_object, obj); + ++(obj->iterator.pos); + } +} +/* }}} */ + +/* {{{ proto void HttpRequestPool::rewind() + Implements Iterator::rewind(). */ +PHP_METHOD(HttpRequestPool, rewind) +{ + NO_ARGS { + getObject(http_requestpool_object, obj); + obj->iterator.pos = 0; + } +} +/* }}} */ + +/* {{{ proto int HttpRequestPool::count() + Implements Countable::count(). */ +PHP_METHOD(HttpRequestPool, count) +{ + NO_ARGS { + getObject(http_requestpool_object, obj); + RETURN_LONG((long) zend_llist_count(&obj->pool.handles)); + } +} +/* }}} */ + +/* {{{ proto array HttpRequestPool::getAttachedRequests() + Get attached HttpRequest objects. */ +PHP_METHOD(HttpRequestPool, getAttachedRequests) +{ + getObject(http_requestpool_object, obj); + + NO_ARGS; + + array_init(return_value); + zend_llist_apply_with_argument(&obj->pool.handles, + (llist_apply_with_arg_func_t) http_requestpool_object_llist2array, + return_value TSRMLS_CC); +} +/* }}} */ + +/* {{{ proto array HttpRequestPool::getFinishedRequests() + Get attached HttpRequest objects that already have finished their work. */ +PHP_METHOD(HttpRequestPool, getFinishedRequests) +{ + getObject(http_requestpool_object, obj); + + NO_ARGS; + + array_init(return_value); + zend_llist_apply_with_argument(&obj->pool.finished, + (llist_apply_with_arg_func_t) http_requestpool_object_llist2array, + return_value TSRMLS_CC); +} +/* }}} */ + +/* {{{ proto bool HttpRequestPool::enablePipelining([bool enable = true]) + Enables pipelining support for all attached requests if support in libcurl is given. */ +PHP_METHOD(HttpRequestPool, enablePipelining) +{ + zend_bool enable = 1; +#if defined(HAVE_CURL_MULTI_SETOPT) && HTTP_CURL_VERSION(7,16,0) + getObject(http_requestpool_object, obj); +#endif + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &enable)) { + RETURN_FALSE; + } +#if defined(HAVE_CURL_MULTI_SETOPT) && HTTP_CURL_VERSION(7,16,0) + if (CURLM_OK == curl_multi_setopt(obj->pool.ch, CURLMOPT_PIPELINING, (long) enable)) { + RETURN_TRUE; + } +#endif + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto bool HttpRequestPool::enableEvents([bool enable = true]) + Enables event-driven I/O if support in libcurl is given. */ +PHP_METHOD(HttpRequestPool, enableEvents) +{ + zend_bool enable = 1; +#if defined(HTTP_HAVE_EVENT) + getObject(http_requestpool_object, obj); +#endif + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &enable)) { +#if defined(HTTP_HAVE_EVENT) + obj->pool.useevents = enable; + RETURN_TRUE; +#endif + } + RETURN_FALSE; +} +/* }}} */ + +#endif /* ZEND_ENGINE_2 && 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 + */ + --- /dev/null +++ b/ext/http/http_response_object.c @@ -0,0 +1,915 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_response_object.c 298891 2010-05-03 08:26:38Z mike $ */ + +#define HTTP_WANT_SAPI +#define HTTP_WANT_MAGIC +#include "php_http.h" + +/* broken static properties in PHP 5.0 */ +#if defined(ZEND_ENGINE_2) && !defined(WONKY) + +#include "php_ini.h" + +#include "php_http_api.h" +#include "php_http_cache_api.h" +#include "php_http_exception_object.h" +#include "php_http_headers_api.h" +#include "php_http_response_object.h" +#include "php_http_send_api.h" + +#define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpResponse, method, 0, req_args) +#define HTTP_EMPTY_ARGS(method) HTTP_EMPTY_ARGS_EX(HttpResponse, method, 0) +#define HTTP_RESPONSE_ME(method, visibility) PHP_ME(HttpResponse, method, HTTP_ARGS(HttpResponse, method), visibility|ZEND_ACC_STATIC) +#define HTTP_RESPONSE_ALIAS(method, func) HTTP_STATIC_ME_ALIAS(method, func, HTTP_ARGS(HttpResponse, method)) + +HTTP_BEGIN_ARGS(setHeader, 1) + HTTP_ARG_VAL(name, 0) + HTTP_ARG_VAL(value, 0) + HTTP_ARG_VAL(replace, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(getHeader, 0) + HTTP_ARG_VAL(name, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getETag); +HTTP_BEGIN_ARGS(setETag, 1) + HTTP_ARG_VAL(etag, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getLastModified); +HTTP_BEGIN_ARGS(setLastModified, 1) + HTTP_ARG_VAL(timestamp, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getCache); +HTTP_BEGIN_ARGS(setCache, 1) + HTTP_ARG_VAL(cache, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getGzip); +HTTP_BEGIN_ARGS(setGzip, 1) + HTTP_ARG_VAL(gzip, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getCacheControl); +HTTP_BEGIN_ARGS(setCacheControl, 1) + HTTP_ARG_VAL(cache_control, 0) + HTTP_ARG_VAL(max_age, 0) + HTTP_ARG_VAL(must_revalidate, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getContentType); +HTTP_BEGIN_ARGS(setContentType, 1) + HTTP_ARG_VAL(content_type, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(guessContentType, 1) + HTTP_ARG_VAL(magic_file, 0) + HTTP_ARG_VAL(magic_mode, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getContentDisposition); +HTTP_BEGIN_ARGS(setContentDisposition, 1) + HTTP_ARG_VAL(filename, 0) + HTTP_ARG_VAL(send_inline, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getThrottleDelay); +HTTP_BEGIN_ARGS(setThrottleDelay, 1) + HTTP_ARG_VAL(seconds, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getBufferSize); +HTTP_BEGIN_ARGS(setBufferSize, 1) + HTTP_ARG_VAL(bytes, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getData); +HTTP_BEGIN_ARGS(setData, 1) + HTTP_ARG_VAL(data, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getStream); +HTTP_BEGIN_ARGS(setStream, 1) + HTTP_ARG_VAL(stream, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getFile); +HTTP_BEGIN_ARGS(setFile, 1) + HTTP_ARG_VAL(filepath, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(send, 0) + HTTP_ARG_VAL(clean_ob, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(capture); + +HTTP_BEGIN_ARGS(redirect, 0) + HTTP_ARG_VAL(url, 0) + HTTP_ARG_VAL(params, 0) + HTTP_ARG_VAL(session, 0) + HTTP_ARG_VAL(permanent, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(status, 1) + HTTP_ARG_VAL(code, 0) +HTTP_END_ARGS; + +HTTP_EMPTY_ARGS(getRequestHeaders); +HTTP_EMPTY_ARGS(getRequestBody); +HTTP_EMPTY_ARGS(getRequestBodyStream); + +#define THIS_CE http_response_object_ce +zend_class_entry *http_response_object_ce; +zend_function_entry http_response_object_fe[] = { + + HTTP_RESPONSE_ME(setHeader, ZEND_ACC_PUBLIC) + HTTP_RESPONSE_ME(getHeader, ZEND_ACC_PUBLIC) + + HTTP_RESPONSE_ME(setETag, ZEND_ACC_PUBLIC) + HTTP_RESPONSE_ME(getETag, ZEND_ACC_PUBLIC) + + HTTP_RESPONSE_ME(setLastModified, ZEND_ACC_PUBLIC) + HTTP_RESPONSE_ME(getLastModified, ZEND_ACC_PUBLIC) + + HTTP_RESPONSE_ME(setContentDisposition, ZEND_ACC_PUBLIC) + HTTP_RESPONSE_ME(getContentDisposition, ZEND_ACC_PUBLIC) + + HTTP_RESPONSE_ME(setContentType, ZEND_ACC_PUBLIC) + HTTP_RESPONSE_ME(getContentType, ZEND_ACC_PUBLIC) + + HTTP_RESPONSE_ME(guessContentType, ZEND_ACC_PUBLIC) + + HTTP_RESPONSE_ME(setCache, ZEND_ACC_PUBLIC) + HTTP_RESPONSE_ME(getCache, ZEND_ACC_PUBLIC) + + HTTP_RESPONSE_ME(setCacheControl, ZEND_ACC_PUBLIC) + HTTP_RESPONSE_ME(getCacheControl, ZEND_ACC_PUBLIC) + + HTTP_RESPONSE_ME(setGzip, ZEND_ACC_PUBLIC) + HTTP_RESPONSE_ME(getGzip, ZEND_ACC_PUBLIC) + + HTTP_RESPONSE_ME(setThrottleDelay, ZEND_ACC_PUBLIC) + HTTP_RESPONSE_ME(getThrottleDelay, ZEND_ACC_PUBLIC) + + HTTP_RESPONSE_ME(setBufferSize, ZEND_ACC_PUBLIC) + HTTP_RESPONSE_ME(getBufferSize, ZEND_ACC_PUBLIC) + + HTTP_RESPONSE_ME(setData, ZEND_ACC_PUBLIC) + HTTP_RESPONSE_ME(getData, ZEND_ACC_PUBLIC) + + HTTP_RESPONSE_ME(setFile, ZEND_ACC_PUBLIC) + HTTP_RESPONSE_ME(getFile, ZEND_ACC_PUBLIC) + + HTTP_RESPONSE_ME(setStream, ZEND_ACC_PUBLIC) + HTTP_RESPONSE_ME(getStream, ZEND_ACC_PUBLIC) + + HTTP_RESPONSE_ME(send, ZEND_ACC_PUBLIC) + HTTP_RESPONSE_ME(capture, ZEND_ACC_PUBLIC) + + HTTP_RESPONSE_ALIAS(redirect, http_redirect) + HTTP_RESPONSE_ALIAS(status, http_send_status) + HTTP_RESPONSE_ALIAS(getRequestHeaders, http_get_request_headers) + HTTP_RESPONSE_ALIAS(getRequestBody, http_get_request_body) + HTTP_RESPONSE_ALIAS(getRequestBodyStream, http_get_request_body_stream) + + EMPTY_FUNCTION_ENTRY +}; + +PHP_MINIT_FUNCTION(http_response_object) +{ + HTTP_REGISTER_CLASS(HttpResponse, http_response_object, NULL, 0); + + zend_declare_property_bool(THIS_CE, ZEND_STRS("sent")-1, 0, (ZEND_ACC_STATIC|ZEND_ACC_PRIVATE) TSRMLS_CC); + zend_declare_property_bool(THIS_CE, ZEND_STRS("catch")-1, 0, (ZEND_ACC_STATIC|ZEND_ACC_PRIVATE) TSRMLS_CC); + zend_declare_property_long(THIS_CE, ZEND_STRS("mode")-1, -1, (ZEND_ACC_STATIC|ZEND_ACC_PRIVATE) TSRMLS_CC); + zend_declare_property_long(THIS_CE, ZEND_STRS("stream")-1, 0, (ZEND_ACC_STATIC|ZEND_ACC_PRIVATE) TSRMLS_CC); + zend_declare_property_null(THIS_CE, ZEND_STRS("file")-1, (ZEND_ACC_STATIC|ZEND_ACC_PRIVATE) TSRMLS_CC); + zend_declare_property_null(THIS_CE, ZEND_STRS("data")-1, (ZEND_ACC_STATIC|ZEND_ACC_PRIVATE) TSRMLS_CC); + zend_declare_property_bool(THIS_CE, ZEND_STRS("cache")-1, 0, (ZEND_ACC_STATIC|ZEND_ACC_PROTECTED) TSRMLS_CC); + zend_declare_property_bool(THIS_CE, ZEND_STRS("gzip")-1, 0, (ZEND_ACC_STATIC|ZEND_ACC_PROTECTED) TSRMLS_CC); + zend_declare_property_null(THIS_CE, ZEND_STRS("eTag")-1, (ZEND_ACC_STATIC|ZEND_ACC_PROTECTED) TSRMLS_CC); + zend_declare_property_long(THIS_CE, ZEND_STRS("lastModified")-1, 0, (ZEND_ACC_STATIC|ZEND_ACC_PROTECTED) TSRMLS_CC); + zend_declare_property_null(THIS_CE, ZEND_STRS("cacheControl")-1, (ZEND_ACC_STATIC|ZEND_ACC_PROTECTED) TSRMLS_CC); + zend_declare_property_null(THIS_CE, ZEND_STRS("contentType")-1, (ZEND_ACC_STATIC|ZEND_ACC_PROTECTED) TSRMLS_CC); + zend_declare_property_null(THIS_CE, ZEND_STRS("contentDisposition")-1, (ZEND_ACC_STATIC|ZEND_ACC_PROTECTED) TSRMLS_CC); + zend_declare_property_long(THIS_CE, ZEND_STRS("bufferSize")-1, 0, (ZEND_ACC_STATIC|ZEND_ACC_PROTECTED) TSRMLS_CC); + zend_declare_property_double(THIS_CE, ZEND_STRS("throttleDelay")-1, 0.0, (ZEND_ACC_STATIC|ZEND_ACC_PROTECTED) TSRMLS_CC); + +#ifndef WONKY + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("REDIRECT")-1, HTTP_REDIRECT TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("REDIRECT_PERM")-1, HTTP_REDIRECT_PERM TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("REDIRECT_FOUND")-1, HTTP_REDIRECT_FOUND TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("REDIRECT_POST")-1, HTTP_REDIRECT_POST TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("REDIRECT_PROXY")-1, HTTP_REDIRECT_PROXY TSRMLS_CC); + zend_declare_class_constant_long(THIS_CE, ZEND_STRS("REDIRECT_TEMP")-1, HTTP_REDIRECT_TEMP TSRMLS_CC); +#endif /* WONKY */ + + return SUCCESS; +} + +/* ### USERLAND ### */ + +/* {{{ proto static bool HttpResponse::setHeader(string name[, mixed value[, bool replace = true]]) + Send an HTTP header. */ +PHP_METHOD(HttpResponse, setHeader) +{ + zend_bool replace = 1; + char *name; + int name_len = 0; + zval *value = NULL; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z/!b", &name, &name_len, &value, &replace)) { + RETURN_FALSE; + } + if (SG(headers_sent)) { + http_error(HE_WARNING, HTTP_E_HEADER, "Cannot add another header when headers have already been sent"); + RETURN_FALSE; + } + if (!name_len) { + http_error(HE_WARNING, HTTP_E_HEADER, "Cannot send anonymous headers"); + RETURN_FALSE; + } + http_send_header_zval_ex(name, name_len, &value, replace); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto static mixed HttpResponse::getHeader([string name]) + Get header(s) about to be sent. */ +PHP_METHOD(HttpResponse, getHeader) +{ + char *name = NULL; + int name_len = 0; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &name, &name_len)) { + RETURN_FALSE; + } + + if (name && name_len) { + zval **header; + HashTable headers_ht; + + zend_hash_init(&headers_ht, sizeof(zval *), NULL, ZVAL_PTR_DTOR, 0); + if ( (SUCCESS == http_get_response_headers(&headers_ht)) && + (SUCCESS == zend_hash_find(&headers_ht, name, name_len + 1, (void *) &header))) { + RETVAL_ZVAL(*header, 1, 0); + } else { + RETVAL_NULL(); + } + zend_hash_destroy(&headers_ht); + } else { + array_init(return_value); + http_get_response_headers(Z_ARRVAL_P(return_value)); + } +} +/* }}} */ + +/* {{{ proto static bool HttpResponse::setCache(bool cache) + Whether it should be attempted to cache the entity. */ +PHP_METHOD(HttpResponse, setCache) +{ + zend_bool do_cache = 0; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_cache)) { + RETURN_FALSE; + } + + RETURN_SUCCESS(zend_update_static_property_bool(THIS_CE, ZEND_STRS("cache")-1, do_cache TSRMLS_CC)); +} +/* }}} */ + +/* {{{ proto static bool HttpResponse::getCache() + Get current caching setting. */ +PHP_METHOD(HttpResponse, getCache) +{ + NO_ARGS; + + if (return_value_used) { + zval *cache = http_zsep(IS_BOOL, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("cache")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + RETVAL_ZVAL(cache, 1, 1); + } +} +/* }}}*/ + +/* {{{ proto static bool HttpResponse::setGzip(bool gzip) + Enable on-thy-fly gzipping of the sent entity. */ +PHP_METHOD(HttpResponse, setGzip) +{ + zend_bool do_gzip = 0; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_gzip)) { + RETURN_FALSE; + } + + RETURN_SUCCESS(zend_update_static_property_bool(THIS_CE, ZEND_STRS("gzip")-1, do_gzip TSRMLS_CC)); +} +/* }}} */ + +/* {{{ proto static bool HttpResponse::getGzip() + Get current gzipping setting. */ +PHP_METHOD(HttpResponse, getGzip) +{ + NO_ARGS; + + if (return_value_used) { + zval *gzip = http_zsep(IS_BOOL, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("gzip")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + RETVAL_ZVAL(gzip, 1, 1); + } +} +/* }}} */ + +/* {{{ proto static bool HttpResponse::setCacheControl(string control[, int max_age = 0[, bool must_revalidate = true]]) + Set a custom cache-control header, usually being "private" or "public"; The max_age parameter controls how long the cache entry is valid on the client side. */ +PHP_METHOD(HttpResponse, setCacheControl) +{ + char *ccontrol, *cctl; + int cc_len; + long max_age = 0; + zend_bool must_revalidate = 1; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lb", &ccontrol, &cc_len, &max_age, &must_revalidate)) { + RETURN_FALSE; + } + + if (strcmp(ccontrol, "public") && strcmp(ccontrol, "private") && strcmp(ccontrol, "no-cache")) { + http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Cache-Control '%s' doesn't match public, private or no-cache", ccontrol); + RETURN_FALSE; + } else { + size_t cctl_len = spprintf(&cctl, 0, "%s,%s max-age=%ld", ccontrol, must_revalidate?" must-revalidate,":"", max_age); + RETVAL_SUCCESS(zend_update_static_property_stringl(THIS_CE, ZEND_STRS("cacheControl")-1, cctl, cctl_len TSRMLS_CC)); + efree(cctl); + } +} +/* }}} */ + +/* {{{ proto static string HttpResponse::getCacheControl() + Get current Cache-Control header setting. */ +PHP_METHOD(HttpResponse, getCacheControl) +{ + NO_ARGS; + + if (return_value_used) { + zval *cctl = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("cacheControl")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + RETVAL_ZVAL(cctl, 1, 1); + } +} +/* }}} */ + +/* {{{ proto static bool HttpResponse::setContentType(string content_type) + Set the content-type of the sent entity. */ +PHP_METHOD(HttpResponse, setContentType) +{ + char *ctype; + int ctype_len; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ctype, &ctype_len)) { + RETURN_FALSE; + } + + HTTP_CHECK_CONTENT_TYPE(ctype, RETURN_FALSE); + RETURN_SUCCESS(zend_update_static_property_stringl(THIS_CE, ZEND_STRS("contentType")-1, ctype, ctype_len TSRMLS_CC)); +} +/* }}} */ + +/* {{{ proto static string HttpResponse::getContentType() + Get current Content-Type header setting. */ +PHP_METHOD(HttpResponse, getContentType) +{ + NO_ARGS; + + if (return_value_used) { + zval *ctype = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("contentType")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + RETVAL_ZVAL(ctype, 1, 1); + } +} +/* }}} */ + +/* {{{ proto static string HttpResponse::guessContentType(string magic_file[, int magic_mode = MAGIC_MIME]) + Attempts to guess the content type of supplied payload through libmagic. */ +PHP_METHOD(HttpResponse, guessContentType) +{ +#ifdef HTTP_HAVE_MAGIC + char *magic_file, *ct = NULL; + int magic_file_len; + long magic_mode = MAGIC_MIME; + + RETVAL_FALSE; + SET_EH_THROW_HTTP(); + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &magic_file, &magic_file_len, &magic_mode)) { + switch (Z_LVAL_P(*zend_std_get_static_property(THIS_CE, ZEND_STRS("mode")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))) { + case SEND_DATA: + { + zval *data = *zend_std_get_static_property(THIS_CE, ZEND_STRS("data")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC); + ct = http_guess_content_type(magic_file, magic_mode, Z_STRVAL_P(data), Z_STRLEN_P(data), SEND_DATA); + break; + } + + case SEND_RSRC: + { + php_stream *s; + zval *z = *zend_std_get_static_property(THIS_CE, ZEND_STRS("stream")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC); + z->type = IS_RESOURCE; + php_stream_from_zval(s, &z); + ct = http_guess_content_type(magic_file, magic_mode, s, 0, SEND_RSRC); + break; + } + + default: + ct = http_guess_content_type(magic_file, magic_mode, Z_STRVAL_P(*zend_std_get_static_property(THIS_CE, ZEND_STRS("file")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC)), 0, -1); + break; + } + if (ct) { + zend_update_static_property_string(THIS_CE, ZEND_STRS("contentType")-1, ct TSRMLS_CC); + RETVAL_STRING(ct, 0); + } + } + SET_EH_NORMAL(); +#else + http_error(HE_THROW, HTTP_E_RUNTIME, "Cannot guess Content-Type; libmagic not available"); + RETURN_FALSE; +#endif +} +/* }}} */ + +/* {{{ proto static bool HttpResponse::setContentDisposition(string filename[, bool inline = false]) + Set the Content-Disposition. */ +PHP_METHOD(HttpResponse, setContentDisposition) +{ + char *file, *cd; + int file_len; + size_t cd_len; + zend_bool send_inline = 0; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &file, &file_len, &send_inline)) { + RETURN_FALSE; + } + + cd_len = spprintf(&cd, 0, "%s; filename=\"%s\"", send_inline ? "inline" : "attachment", file); + RETVAL_SUCCESS(zend_update_static_property_stringl(THIS_CE, ZEND_STRS("contentDisposition")-1, cd, cd_len TSRMLS_CC)); + efree(cd); +} +/* }}} */ + +/* {{{ proto static string HttpResponse::getContentDisposition() + Get current Content-Disposition setting. */ +PHP_METHOD(HttpResponse, getContentDisposition) +{ + NO_ARGS; + + if (return_value_used) { + zval *cdisp = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("contentDisposition")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + RETVAL_ZVAL(cdisp, 1, 1); + } +} +/* }}} */ + +/* {{{ proto static bool HttpResponse::setETag(string etag) + Set a custom ETag. Use this only if you know what you're doing. */ +PHP_METHOD(HttpResponse, setETag) +{ + char *etag; + int etag_len; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &etag, &etag_len)) { + RETURN_FALSE; + } + + RETURN_SUCCESS(zend_update_static_property_stringl(THIS_CE, ZEND_STRS("eTag")-1, etag, etag_len TSRMLS_CC)); +} +/* }}} */ + +/* {{{ proto static string HttpResponse::getETag() + Get calculated or previously set custom ETag. */ +PHP_METHOD(HttpResponse, getETag) +{ + NO_ARGS; + + if (return_value_used) { + zval *etag = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("eTag")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + RETVAL_ZVAL(etag, 1, 1); + } +} +/* }}} */ + +/* {{{ proto static bool HttpResponse::setLastModified(int timestamp) + Set a custom Last-Modified date. */ +PHP_METHOD(HttpResponse, setLastModified) +{ + long lm; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &lm)) { + RETURN_FALSE; + } + + RETURN_SUCCESS(zend_update_static_property_long(THIS_CE, ZEND_STRS("lastModified")-1, lm TSRMLS_CC)); +} +/* }}} */ + +/* {{{ proto static int HttpResponse::getLastModified() + Get calculated or previously set custom Last-Modified date. */ +PHP_METHOD(HttpResponse, getLastModified) +{ + NO_ARGS; + + if (return_value_used) { + zval *lmod = http_zsep(IS_LONG, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("lastModified")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + RETVAL_ZVAL(lmod, 1, 1); + } +} +/* }}} */ + +/* {{{ proto static bool HttpResponse::setThrottleDelay(double seconds) + Sets the throttle delay for use with HttpResponse::setBufferSize(). */ +PHP_METHOD(HttpResponse, setThrottleDelay) +{ + double seconds; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &seconds)) { + RETURN_FALSE; + } + RETURN_SUCCESS(zend_update_static_property_double(THIS_CE, ZEND_STRS("throttleDelay")-1, seconds TSRMLS_CC)); +} +/* }}} */ + +/* {{{ proto static double HttpResponse::getThrottleDelay() + Get the current throttle delay. */ +PHP_METHOD(HttpResponse, getThrottleDelay) +{ + NO_ARGS; + + if (return_value_used) { + zval *tdel = http_zsep(IS_DOUBLE, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("throttleDelay")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + RETVAL_ZVAL(tdel, 1, 1); + } +} +/* }}} */ + +/* {{{ proto static bool HttpResponse::setBufferSize(int bytes) + Sets the send buffer size for use with HttpResponse::setThrottleDelay(). */ +PHP_METHOD(HttpResponse, setBufferSize) +{ + long bytes; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &bytes)) { + RETURN_FALSE; + } + RETURN_SUCCESS(zend_update_static_property_long(THIS_CE, ZEND_STRS("bufferSize")-1, bytes TSRMLS_CC)); +} +/* }}} */ + +/* {{{ proto static int HttpResponse::getBufferSize() + Get current buffer size. */ +PHP_METHOD(HttpResponse, getBufferSize) +{ + NO_ARGS; + + if (return_value_used) { + zval *bsize = http_zsep(IS_LONG, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("bufferSize")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + RETVAL_ZVAL(bsize, 1, 1); + } +} +/* }}} */ + +/* {{{ proto static bool HttpResponse::setData(mixed data) + Set the data to be sent. */ +PHP_METHOD(HttpResponse, setData) +{ + char *etag; + zval *the_data; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &the_data)) { + RETURN_FALSE; + } + if (Z_TYPE_P(the_data) != IS_STRING) { + convert_to_string(the_data); + } + + if ( (SUCCESS != zend_update_static_property(THIS_CE, ZEND_STRS("data")-1, the_data TSRMLS_CC)) || + (SUCCESS != zend_update_static_property_long(THIS_CE, ZEND_STRS("mode")-1, SEND_DATA TSRMLS_CC))) { + RETURN_FALSE; + } + + zend_update_static_property_long(THIS_CE, ZEND_STRS("lastModified")-1, http_last_modified(the_data, SEND_DATA) TSRMLS_CC); + if ((etag = http_etag(Z_STRVAL_P(the_data), Z_STRLEN_P(the_data), SEND_DATA))) { + zend_update_static_property_string(THIS_CE, ZEND_STRS("eTag")-1, etag TSRMLS_CC); + efree(etag); + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto static string HttpResponse::getData() + Get the previously set data to be sent. */ +PHP_METHOD(HttpResponse, getData) +{ + NO_ARGS; + + if (return_value_used) { + zval *the_data = *zend_std_get_static_property(THIS_CE, ZEND_STRS("data")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC); + + RETURN_ZVAL(the_data, 1, 0); + } +} +/* }}} */ + +/* {{{ proto static bool HttpResponse::setStream(resource stream) + Set the resource to be sent. */ +PHP_METHOD(HttpResponse, setStream) +{ + char *etag; + zval *the_stream; + php_stream *the_real_stream; + php_stream_statbuf ssb; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &the_stream)) { + RETURN_FALSE; + } + + php_stream_from_zval(the_real_stream, &the_stream); + if (php_stream_stat(the_real_stream, &ssb)) { + RETURN_FALSE; + } + + if ( (SUCCESS != zend_update_static_property_long(THIS_CE, ZEND_STRS("stream")-1, Z_LVAL_P(the_stream) TSRMLS_CC)) || + (SUCCESS != zend_update_static_property_long(THIS_CE, ZEND_STRS("mode")-1, SEND_RSRC TSRMLS_CC))) { + RETURN_FALSE; + } + zend_list_addref(Z_LVAL_P(the_stream)); + + zend_update_static_property_long(THIS_CE, ZEND_STRS("lastModified")-1, http_last_modified(the_real_stream, SEND_RSRC) TSRMLS_CC); + if ((etag = http_etag(the_real_stream, 0, SEND_RSRC))) { + zend_update_static_property_string(THIS_CE, ZEND_STRS("eTag")-1, etag TSRMLS_CC); + efree(etag); + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto static resource HttpResponse::getStream() + Get the previously set resource to be sent. */ +PHP_METHOD(HttpResponse, getStream) +{ + NO_ARGS; + + if (return_value_used) { + zval *stream = http_zsep(IS_LONG, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("stream")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + RETVAL_RESOURCE(Z_LVAL_P(stream)); + zval_ptr_dtor(&stream); + } +} +/* }}} */ + +/* {{{ proto static bool HttpResponse::setFile(string file) + Set the file to be sent. */ +PHP_METHOD(HttpResponse, setFile) +{ + char *the_file, *etag; + int file_len; + php_stream_statbuf ssb; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &the_file, &file_len)) { + RETURN_FALSE; + } + + if (php_stream_stat_path(the_file, &ssb)) { + RETURN_FALSE; + } + + if ( (SUCCESS != zend_update_static_property_stringl(THIS_CE, ZEND_STRS("file")-1, the_file, file_len TSRMLS_CC)) || + (SUCCESS != zend_update_static_property_long(THIS_CE, ZEND_STRS("mode")-1, -1 TSRMLS_CC))) { + RETURN_FALSE; + } + + zend_update_static_property_long(THIS_CE, ZEND_STRS("lastModified")-1, http_last_modified(the_file, -1) TSRMLS_CC); + if ((etag = http_etag(the_file, 0, -1))) { + zend_update_static_property_string(THIS_CE, ZEND_STRS("eTag")-1, etag TSRMLS_CC); + efree(etag); + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto static string HttpResponse::getFile() + Get the previously set file to be sent. */ +PHP_METHOD(HttpResponse, getFile) +{ + NO_ARGS; + + if (return_value_used) { + zval *file = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("file")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + RETVAL_ZVAL(file, 1, 1); + } +} +/* }}} */ + +/* {{{ proto static bool HttpResponse::send([bool clean_ob = true]) + Finally send the entity. */ +PHP_METHOD(HttpResponse, send) +{ + zval *sent; + zend_bool clean_ob = 1; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &clean_ob)) { + RETURN_FALSE; + } + + HTTP_CHECK_HEADERS_SENT(RETURN_FALSE); + + sent = *zend_std_get_static_property(THIS_CE, ZEND_STRS("sent")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC); + if (Z_LVAL_P(sent)) { + http_error(HE_WARNING, HTTP_E_RESPONSE, "Cannot send HttpResponse, response has already been sent"); + RETURN_FALSE; + } else { + Z_LVAL_P(sent) = 1; + } + + /* capture mode */ + if (i_zend_is_true(*zend_std_get_static_property(THIS_CE, ZEND_STRS("catch")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))) { + zval *zetag, *the_data; + + MAKE_STD_ZVAL(the_data); + php_ob_get_buffer(the_data TSRMLS_CC); + zend_update_static_property(THIS_CE, ZEND_STRS("data")-1, the_data TSRMLS_CC); + ZVAL_LONG(*zend_std_get_static_property(THIS_CE, ZEND_STRS("mode")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC), SEND_DATA); + + zetag = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("eTag")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + if (!Z_STRLEN_P(zetag)) { + char *etag = http_etag(Z_STRVAL_P(the_data), Z_STRLEN_P(the_data), SEND_DATA); + if (etag) { + zend_update_static_property_string(THIS_CE, ZEND_STRS("eTag")-1, etag TSRMLS_CC); + efree(etag); + } + } + zval_ptr_dtor(&the_data); + zval_ptr_dtor(&zetag); + + clean_ob = 1; + } + + if (clean_ob) { + /* interrupt on-the-fly etag generation */ + HTTP_G->etag.started = 0; + /* discard previous output buffers */ + php_end_ob_buffers(0 TSRMLS_CC); + } + + /* caching */ + if (i_zend_is_true(*zend_std_get_static_property(THIS_CE, ZEND_STRS("cache")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))) { + zval *cctl, *etag, *lmod; + + lmod = http_zsep(IS_LONG, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("lastModified")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + etag = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("eTag")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + cctl = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("cacheControl")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + + if (Z_LVAL_P(lmod) || Z_STRLEN_P(etag)) { + if (Z_STRLEN_P(cctl)) { + http_send_cache_control(Z_STRVAL_P(cctl), Z_STRLEN_P(cctl)); + } else { + http_send_cache_control(HTTP_DEFAULT_CACHECONTROL, lenof(HTTP_DEFAULT_CACHECONTROL)); + } + if (Z_STRLEN_P(etag)) { + http_send_etag(Z_STRVAL_P(etag), Z_STRLEN_P(etag)); + } + if (Z_LVAL_P(lmod)) { + http_send_last_modified(Z_LVAL_P(lmod)); + } + } + + zval_ptr_dtor(&etag); + zval_ptr_dtor(&lmod); + zval_ptr_dtor(&cctl); + } + + /* content type */ + { + zval *ctype = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("contentType")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + if (Z_STRLEN_P(ctype)) { + http_send_content_type(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype)); + } else { + char *ctypes = INI_STR("default_mimetype"); + size_t ctlen = ctypes ? strlen(ctypes) : 0; + + if (ctlen) { + http_send_content_type(ctypes, ctlen); + } else { + http_send_content_type("application/x-octetstream", lenof("application/x-octetstream")); + } + } + zval_ptr_dtor(&ctype); + } + + /* content disposition */ + { + zval *cd = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("contentDisposition")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + if (Z_STRLEN_P(cd)) { + http_send_header_ex("Content-Disposition", lenof("Content-Disposition"), Z_STRVAL_P(cd), Z_STRLEN_P(cd), 1, NULL); + } + zval_ptr_dtor(&cd); + } + + /* throttling */ + { + zval *bsize = http_zsep(IS_LONG, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("bufferSize")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + zval *delay = http_zsep(IS_DOUBLE, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("throttleDelay")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + HTTP_G->send.buffer_size = Z_LVAL_P(bsize); + HTTP_G->send.throttle_delay = Z_DVAL_P(delay); + zval_ptr_dtor(&bsize); + zval_ptr_dtor(&delay); + } + + /* gzip */ + HTTP_G->send.deflate.response = i_zend_is_true(*zend_std_get_static_property(THIS_CE, ZEND_STRS("gzip")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC)); + + /* send */ + switch (Z_LVAL_P(*zend_std_get_static_property(THIS_CE, ZEND_STRS("mode")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))) { + case SEND_DATA: + { + zval *zdata = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("data")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + RETVAL_SUCCESS(http_send_data(Z_STRVAL_P(zdata), Z_STRLEN_P(zdata))); + zval_ptr_dtor(&zdata); + return; + } + + case SEND_RSRC: + { + php_stream *the_real_stream; + zval *the_stream = http_zsep(IS_LONG, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("stream")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + the_stream->type = IS_RESOURCE; + php_stream_from_zval(the_real_stream, &the_stream); + RETVAL_SUCCESS(http_send_stream(the_real_stream)); + zval_ptr_dtor(&the_stream); + return; + } + + default: + { + zval *file = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("file")-1, 0 ZEND_LITERAL_NIL_CC TSRMLS_CC))); + RETVAL_SUCCESS(http_send_file(Z_STRVAL_P(file))); + zval_ptr_dtor(&file); + return; + } + } +} +/* }}} */ + +/* {{{ proto static void HttpResponse::capture() + Capture script output. + */ +PHP_METHOD(HttpResponse, capture) +{ + NO_ARGS; + + HTTP_CHECK_HEADERS_SENT(RETURN_FALSE); + + zend_update_static_property_long(THIS_CE, ZEND_STRS("catch")-1, 1 TSRMLS_CC); + + php_end_ob_buffers(0 TSRMLS_CC); + php_start_ob_buffer(NULL, 0, 0 TSRMLS_CC); + + /* register shutdown function */ + { + zval func, retval, arg, *argp[1]; + + INIT_PZVAL(&arg); + INIT_PZVAL(&func); + INIT_PZVAL(&retval); + ZVAL_STRINGL(&func, "register_shutdown_function", lenof("register_shutdown_function"), 0); + + array_init(&arg); + add_next_index_stringl(&arg, "HttpResponse", lenof("HttpResponse"), 1); + add_next_index_stringl(&arg, "send", lenof("send"), 1); + argp[0] = &arg; + call_user_function(EG(function_table), NULL, &func, &retval, 1, argp TSRMLS_CC); + zval_dtor(&arg); + } +} +/* }}} */ + +#endif /* ZEND_ENGINE_2 && !WONKY */ + +/* + * 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 + */ + --- /dev/null +++ b/ext/http/http_send_api.c @@ -0,0 +1,607 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_send_api.c 300299 2010-06-09 06:23:16Z mike $ */ + +#define HTTP_WANT_SAPI +#define HTTP_WANT_ZLIB +#define HTTP_WANT_MAGIC +#include "php_http.h" + +#include "php_streams.h" + +#include "php_http_api.h" +#include "php_http_cache_api.h" +#include "php_http_date_api.h" +#include "php_http_encoding_api.h" +#include "php_http_headers_api.h" +#include "php_http_send_api.h" + +/* {{{ http_flush() */ +#define http_flush(d, l) _http_flush(NULL, (d), (l) TSRMLS_CC) +static inline void _http_flush(void *nothing, const char *data, size_t data_len TSRMLS_DC) +{ + PHPWRITE(data, data_len); + /* we really only need to flush when throttling is enabled, + because we push the data as fast as possible anyway if not */ + if (HTTP_G->send.throttle_delay >= HTTP_DIFFSEC) { + if (OG(ob_nesting_level)) { + php_end_ob_buffer(1, 1 TSRMLS_CC); + } + if (!OG(implicit_flush)) { + sapi_flush(TSRMLS_C); + } + http_sleep(HTTP_G->send.throttle_delay); + } +} +/* }}} */ + +/* {{{ http_send_response_start */ +#define http_send_response_start(b, cl) _http_send_response_start((b), (cl) TSRMLS_CC) +static inline void _http_send_response_start(void **buffer, size_t content_length TSRMLS_DC) +{ + int encoding; + + if ((encoding = http_encoding_response_start(content_length, 0))) { +#ifdef HTTP_HAVE_ZLIB + *((http_encoding_stream **) buffer) = http_encoding_deflate_stream_init(NULL, + (encoding == HTTP_ENCODING_GZIP) ? + HTTP_DEFLATE_TYPE_GZIP : HTTP_DEFLATE_TYPE_ZLIB); +#endif + } + /* flush headers */ + sapi_flush(TSRMLS_C); +} +/* }}} */ + +/* {{{ http_send_response_data_plain */ +#define http_send_response_data_plain(b, d, dl) _http_send_response_data_plain((b), (d), (dl) TSRMLS_CC) +static inline void _http_send_response_data_plain(void **buffer, const char *data, size_t data_len TSRMLS_DC) +{ + if (HTTP_G->send.deflate.response && HTTP_G->send.deflate.encoding) { +#ifdef HTTP_HAVE_ZLIB + char *encoded; + size_t encoded_len; + http_encoding_stream *s = *((http_encoding_stream **) buffer); + + http_encoding_deflate_stream_update(s, data, data_len, &encoded, &encoded_len); + if (HTTP_G->send.buffer_size) { + phpstr_chunked_output((phpstr **) &s->storage, encoded, encoded_len, HTTP_G->send.buffer_size, _http_flush, NULL TSRMLS_CC); + } else { + http_flush(encoded, encoded_len); + } + efree(encoded); +#else + http_error(HE_ERROR, HTTP_E_RESPONSE, "Attempt to send GZIP response despite being able to do so; please report this bug"); +#endif + } else if (HTTP_G->send.buffer_size) { + phpstr_chunked_output((phpstr **) buffer, data, data_len, HTTP_G->send.buffer_size, _http_flush, NULL TSRMLS_CC); + } else { + http_flush(data, data_len); + } +} +/* }}} */ + +/* {{{ http_send_response_data_fetch */ +#define http_send_response_data_fetch(b, d, l, m, s, e) _http_send_response_data_fetch((b), (d), (l), (m), (s), (e) TSRMLS_CC) +static inline void _http_send_response_data_fetch(void **buffer, const void *data, size_t data_len, http_send_mode mode, size_t begin, size_t end TSRMLS_DC) +{ + long bsz, got, len = end - begin; + + if (!(bsz = HTTP_G->send.buffer_size)) { + bsz = HTTP_SENDBUF_SIZE; + } + + switch (mode) { + case SEND_RSRC: { + php_stream *s = (php_stream *) data; + if (SUCCESS == php_stream_seek(s, begin, SEEK_SET)) { + char *buf = emalloc(bsz); + + while (len > 0) { + got = php_stream_read(s, buf, MIN(len, bsz)); + http_send_response_data_plain(buffer, buf, got); + len -= got; + } + + efree(buf); + } + break; + } + case SEND_DATA: { + const char *buf = ((const char *) data) + begin; + while (len > 0) { + got = MIN(len, bsz); + http_send_response_data_plain(buffer, buf, got); + len -= got; + buf += got; + } + break; + } + EMPTY_SWITCH_DEFAULT_CASE(); + } +} +/* }}} */ + +/* {{{ http_send_response_finish */ +#define http_send_response_finish(b) _http_send_response_finish((b) TSRMLS_CC) +static inline void _http_send_response_finish(void **buffer TSRMLS_DC) +{ + if (HTTP_G->send.deflate.response && HTTP_G->send.deflate.encoding) { +#ifdef HTTP_HAVE_ZLIB + char *encoded = NULL; + size_t encoded_len = 0; + http_encoding_stream *s = *((http_encoding_stream **) buffer); + + http_encoding_deflate_stream_finish(s, &encoded, &encoded_len); + if (HTTP_G->send.buffer_size) { + phpstr_chunked_output((phpstr **) &s->storage, encoded, encoded_len, 0, _http_flush, NULL TSRMLS_CC); + } else { + http_flush(encoded, encoded_len); + } + http_encoding_deflate_stream_free(&s); + STR_FREE(encoded); +#else + http_error(HE_ERROR, HTTP_E_RESPONSE, "Attempt to send GZIP response despite being able to do so; please report this bug"); +#endif + } else if (HTTP_G->send.buffer_size) { + phpstr_chunked_output((phpstr **) buffer, NULL, 0, 0, _http_flush, NULL TSRMLS_CC); + } +} +/* }}} */ + +/* {{{ */ +PHP_MINIT_FUNCTION(http_send) +{ + HTTP_LONG_CONSTANT("HTTP_REDIRECT", HTTP_REDIRECT); + HTTP_LONG_CONSTANT("HTTP_REDIRECT_PERM", HTTP_REDIRECT_PERM); + HTTP_LONG_CONSTANT("HTTP_REDIRECT_FOUND", HTTP_REDIRECT_FOUND); + HTTP_LONG_CONSTANT("HTTP_REDIRECT_POST", HTTP_REDIRECT_POST); + HTTP_LONG_CONSTANT("HTTP_REDIRECT_PROXY", HTTP_REDIRECT_PROXY); + HTTP_LONG_CONSTANT("HTTP_REDIRECT_TEMP", HTTP_REDIRECT_TEMP); + + return SUCCESS; +} +/* }}} */ + +/* {{{ http_find_header */ +typedef struct { + const char *h; + size_t l; +} http_response_header_t; + +static int http_find_header(void *data, void *arg) +{ + http_response_header_t *h = arg; + sapi_header_struct *s = data; + + return (!strncasecmp(s->header, h->h, h->l)) && s->header[h->l] == ':'; +} +/* }}} */ + +/* {{{ void http_hide_header(char *) */ +PHP_HTTP_API void _http_hide_header_ex(const char *name, size_t name_len TSRMLS_DC) +{ + http_response_header_t h = {name, name_len}; + zend_llist_del_element(&SG(sapi_headers).headers, (void *) &h, http_find_header); +} +/* }}} */ + +/* {{{ void http_send_header_zval(char*, zval **, zend_bool) */ +PHP_HTTP_API void _http_send_header_zval_ex(const char *name, size_t name_len, zval **val, zend_bool replace TSRMLS_DC) +{ + if (!val || !*val || Z_TYPE_PP(val) == IS_NULL || (Z_TYPE_PP(val) == IS_STRING && !Z_STRLEN_PP(val))) { + http_hide_header_ex(name, name_len); + } else if (Z_TYPE_PP(val) == IS_ARRAY || Z_TYPE_PP(val) == IS_OBJECT) { + zend_bool first = replace; + zval **data_ptr; + HashPosition pos; + + FOREACH_HASH_VAL(pos, HASH_OF(*val), data_ptr) { + zval *data = http_zsep(IS_STRING, *data_ptr); + + http_send_header_ex(name, name_len, Z_STRVAL_P(data), Z_STRLEN_P(data), first, NULL); + zval_ptr_dtor(&data); + first = 0; + } + } else { + zval *data = http_zsep(IS_STRING, *val); + + http_send_header_ex(name, name_len, Z_STRVAL_P(data), Z_STRLEN_P(data), replace, NULL); + zval_ptr_dtor(&data); + } +} +/* }}} */ + +/* {{{ STATUS http_send_header(char *, char *, zend_bool) */ +PHP_HTTP_API STATUS _http_send_header_ex(const char *name, size_t name_len, const char *value, size_t value_len, zend_bool replace, char **sent_header TSRMLS_DC) +{ + STATUS ret; + + if (value && value_len) { + size_t header_len = sizeof(": ") + name_len + value_len + 1; + char *header = emalloc(header_len + 1); + + header[header_len] = '\0'; + header_len = snprintf(header, header_len, "%s: %s", name, value); + ret = http_send_header_string_ex(header, header_len, replace); + if (sent_header) { + *sent_header = header; + } else { + efree(header); + } + } else { + http_hide_header_ex(name, name_len); + ret = SUCCESS; + } + return ret; +} +/* }}} */ + +/* {{{ STATUS http_send_status_header(int, char *) */ +PHP_HTTP_API STATUS _http_send_status_header_ex(int status, const char *header, size_t header_len, zend_bool replace TSRMLS_DC) +{ + STATUS ret; + sapi_header_line h = {(char *) header, header_len, status}; + if (SUCCESS != (ret = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, &h TSRMLS_CC))) { + http_error_ex(HE_WARNING, HTTP_E_HEADER, "Could not send header: %s (%d)", header, status); + } + return ret; +} +/* }}} */ + +/* {{{ STATUS http_send_last_modified(int) */ +PHP_HTTP_API STATUS _http_send_last_modified_ex(time_t t, char **sent_header TSRMLS_DC) +{ + STATUS ret; + char *date = http_date(t); + + if (!date) { + return FAILURE; + } + + ret = http_send_header_ex("Last-Modified", lenof("Last-Modified"), date, strlen(date), 1, sent_header); + efree(date); + + /* remember */ + HTTP_G->send.last_modified = t; + + return ret; +} +/* }}} */ + +/* {{{ STATUS http_send_etag(char *, size_t) */ +PHP_HTTP_API STATUS _http_send_etag_ex(const char *etag, size_t etag_len, char **sent_header TSRMLS_DC) +{ + STATUS status; + char *etag_header; + size_t etag_header_len; + + if (!etag_len){ + http_error_ex(HE_WARNING, HTTP_E_HEADER, "Attempt to send empty ETag (previous: %s)\n", HTTP_G->send.unquoted_etag); + return FAILURE; + } + + etag_header_len = spprintf(&etag_header, 0, "ETag: \"%s\"", etag); + status = http_send_header_string_ex(etag_header, etag_header_len, 1); + + /* remember */ + STR_SET(HTTP_G->send.unquoted_etag, estrndup(etag, etag_len)); + + if (sent_header) { + *sent_header = etag_header; + } else { + efree(etag_header); + } + + return status; +} +/* }}} */ + +/* {{{ STATUS http_send_content_type(char *, size_t) */ +PHP_HTTP_API STATUS _http_send_content_type(const char *content_type, size_t ct_len TSRMLS_DC) +{ + HTTP_CHECK_CONTENT_TYPE(content_type, return FAILURE); + + /* remember for multiple ranges */ + STR_FREE(HTTP_G->send.content_type); + HTTP_G->send.content_type = estrndup(content_type, ct_len); + + return http_send_header_ex("Content-Type", lenof("Content-Type"), content_type, ct_len, 1, NULL); +} +/* }}} */ + +/* {{{ STATUS http_send_content_disposition(char *, size_t, zend_bool) */ +PHP_HTTP_API STATUS _http_send_content_disposition(const char *filename, size_t f_len, zend_bool send_inline TSRMLS_DC) +{ + STATUS status; + char *cd_header; + + if (send_inline) { + cd_header = ecalloc(1, sizeof("Content-Disposition: inline; filename=\"\"") + f_len); + sprintf(cd_header, "Content-Disposition: inline; filename=\"%s\"", filename); + } else { + cd_header = ecalloc(1, sizeof("Content-Disposition: attachment; filename=\"\"") + f_len); + sprintf(cd_header, "Content-Disposition: attachment; filename=\"%s\"", filename); + } + + status = http_send_header_string(cd_header); + efree(cd_header); + return status; +} +/* }}} */ + +/* {{{ STATUS http_send(void *, size_t, http_send_mode) */ +PHP_HTTP_API STATUS _http_send_ex(const void *data_ptr, size_t data_size, http_send_mode data_mode, zend_bool no_cache TSRMLS_DC) +{ + void *s = NULL; + HashTable ranges; + http_range_status range_status; + + if (!data_ptr) { + return FAILURE; + } + if (!data_size) { + return SUCCESS; + } + + /* enable partial dl and resume */ + http_send_header_string("Accept-Ranges: bytes"); + + zend_hash_init(&ranges, 0, NULL, ZVAL_PTR_DTOR, 0); + range_status = http_get_request_ranges(&ranges, data_size); + + switch (range_status) { + case RANGE_ERR: + { + zend_hash_destroy(&ranges); + http_send_status(416); + return FAILURE; + } + case RANGE_OK: + { + /* Range Request - only send ranges if entity hasn't changed */ + if ( http_got_server_var("HTTP_IF_RANGE") && + !http_match_etag("HTTP_IF_RANGE", HTTP_G->send.unquoted_etag) && + !http_match_last_modified("HTTP_IF_RANGE", HTTP_G->send.last_modified)) { + /* fallthrough to send full entity with 200 Ok */ + no_cache = 1; + } else if ( !http_match_etag_ex("HTTP_IF_MATCH", HTTP_G->send.unquoted_etag, 0) || + !http_match_last_modified_ex("HTTP_IF_UNMODIFIED_SINCE", HTTP_G->send.last_modified, 0) || + !http_match_last_modified_ex("HTTP_UNLESS_MODIFIED_SINCE", HTTP_G->send.last_modified, 0)) { + /* 412 Precondition failed */ + zend_hash_destroy(&ranges); + http_send_status(412); + return FAILURE; + } else if (zend_hash_num_elements(&ranges) == 1) { + /* single range */ + zval **range, **begin, **end; + + if ( SUCCESS != zend_hash_index_find(&ranges, 0, (void *) &range) || + SUCCESS != zend_hash_index_find(Z_ARRVAL_PP(range), 0, (void *) &begin) || + SUCCESS != zend_hash_index_find(Z_ARRVAL_PP(range), 1, (void *) &end)) { + /* this should never happen */ + zend_hash_destroy(&ranges); + http_send_status(500); + return FAILURE; + } else { + phpstr header; + + phpstr_init(&header); + phpstr_appendf(&header, "Content-Range: bytes %ld-%ld/%zu", Z_LVAL_PP(begin), Z_LVAL_PP(end), data_size); + phpstr_fix(&header); + http_send_status_header_ex(206, PHPSTR_VAL(&header), PHPSTR_LEN(&header), 1); + phpstr_dtor(&header); + http_send_response_start(&s, Z_LVAL_PP(end)-Z_LVAL_PP(begin)+1); + http_send_response_data_fetch(&s, data_ptr, data_size, data_mode, Z_LVAL_PP(begin), Z_LVAL_PP(end) + 1); + http_send_response_finish(&s); + zend_hash_destroy(&ranges); + return SUCCESS; + } + } else { + /* multi range */ + HashPosition pos; + zval **range, **begin, **end; + const char *content_type = HTTP_G->send.content_type; + char boundary_str[32]; + size_t boundary_len; + phpstr header, preface; + + boundary_len = http_boundary(boundary_str, sizeof(boundary_str)); + phpstr_init(&header); + phpstr_appendf(&header, "Content-Type: multipart/byteranges; boundary=%s", boundary_str); + phpstr_fix(&header); + http_send_status_header_ex(206, PHPSTR_VAL(&header), PHPSTR_LEN(&header), 1); + phpstr_dtor(&header); + http_send_response_start(&s, 0); + + if (!content_type) { + content_type = "application/x-octetstream"; + } + + phpstr_init(&preface); + FOREACH_HASH_VAL(pos, &ranges, range) { + if ( SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(range), 0, (void *) &begin) && + SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(range), 1, (void *) &end)) { + +#define HTTP_RANGE_PREFACE \ + HTTP_CRLF "--%s" \ + HTTP_CRLF "Content-Type: %s" \ + HTTP_CRLF "Content-Range: bytes %ld-%ld/%zu" \ + HTTP_CRLF HTTP_CRLF + + phpstr_appendf(&preface, HTTP_RANGE_PREFACE, boundary_str, content_type, Z_LVAL_PP(begin), Z_LVAL_PP(end), data_size); + phpstr_fix(&preface); + http_send_response_data_plain(&s, PHPSTR_VAL(&preface), PHPSTR_LEN(&preface)); + phpstr_reset(&preface); + http_send_response_data_fetch(&s, data_ptr, data_size, data_mode, Z_LVAL_PP(begin), Z_LVAL_PP(end) + 1); + } + } + phpstr_dtor(&preface); + + http_send_response_data_plain(&s, HTTP_CRLF "--", lenof(HTTP_CRLF "--")); + http_send_response_data_plain(&s, boundary_str, boundary_len); + http_send_response_data_plain(&s, "--", lenof("--")); + + http_send_response_finish(&s); + zend_hash_destroy(&ranges); + return SUCCESS; + } + } + case RANGE_NO: + { + zend_hash_destroy(&ranges); + + /* send 304 Not Modified if etag matches - DON'T return on ETag generation failure */ + if (!no_cache && (http_interrupt_ob_etaghandler() || (HTTP_G->send.unquoted_etag != NULL))) { + char *etag = NULL; + + if (HTTP_G->send.unquoted_etag) { + etag = estrdup(HTTP_G->send.unquoted_etag); + } + + if (etag || (etag = http_etag(data_ptr, data_size, data_mode))) { + char *sent_header = NULL; + + http_send_etag_ex(etag, strlen(etag), &sent_header); + if (http_match_etag("HTTP_IF_NONE_MATCH", etag)) { + return http_exit_ex(304, sent_header, NULL, 0); + } else { + STR_FREE(sent_header); + /* no caching for Last-Modified if ETags really don't match */ + no_cache = http_got_server_var("HTTP_IF_NONE_MATCH"); + } + efree(etag); + } + } + + /* send 304 Not Modified if last modified matches */ + if (!no_cache && HTTP_G->send.last_modified && http_match_last_modified("HTTP_IF_MODIFIED_SINCE", HTTP_G->send.last_modified)) { + char *sent_header = NULL; + http_send_last_modified_ex(HTTP_G->send.last_modified, &sent_header); + return http_exit_ex(304, sent_header, NULL, 0); + } + + /* send full response */ + http_send_response_start(&s, data_size); + http_send_response_data_fetch(&s, data_ptr, data_size, data_mode, 0, data_size); + http_send_response_finish(&s); + return SUCCESS; + } + } + return FAILURE; +} +/* }}} */ + +/* {{{ STATUS http_send_stream(php_stream *) */ +PHP_HTTP_API STATUS _http_send_stream_ex(php_stream *file, zend_bool close_stream, zend_bool no_cache TSRMLS_DC) +{ + STATUS status; + php_stream_statbuf ssb; + int orig_flags; + + if ((!file) || php_stream_stat(file, &ssb)) { + char *defct = sapi_get_default_content_type(TSRMLS_C); + + http_hide_header("Content-Disposition"); + http_send_content_type(defct, strlen(defct)); + http_error(HE_WARNING, HTTP_E_RESPONSE, "File not found; stat failed"); + STR_FREE(defct); + + if (HTTP_G->send.not_found_404) { + http_exit_ex(404, NULL, estrdup("File not found\n"), 0); + } + return FAILURE; + } + + orig_flags = file->flags; + file->flags |= PHP_STREAM_FLAG_NO_BUFFER; + status = http_send_ex(file, ssb.sb.st_size, SEND_RSRC, no_cache); + file->flags = orig_flags; + + if (close_stream) { + php_stream_close(file); + } + + return status; +} +/* }}} */ + +/* {{{ char *http_guess_content_type(char *magic_file, long magic_mode, void *data, size_t size, http_send_mode mode) */ +PHP_HTTP_API char *_http_guess_content_type(const char *magicfile, long magicmode, void *data_ptr, size_t data_len, http_send_mode data_mode TSRMLS_DC) +{ + char *ct = NULL; + +#ifdef HTTP_HAVE_MAGIC + struct magic_set *magic = NULL; + + HTTP_CHECK_OPEN_BASEDIR(magicfile, return NULL); + + if (!data_ptr) { + http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Supplied payload is empty"); + } else if (!(magic = magic_open(magicmode &~ MAGIC_MIME))) { + http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid magic mode: %ld", magicmode); + } else if (-1 == magic_load(magic, magicfile)) { + http_error_ex(HE_WARNING, HTTP_E_RUNTIME, "Failed to load magic database '%s' (%s)", magicfile, magic_error(magic)); + } else { + const char *ctype = NULL; + + magic_setflags(magic, magicmode); + + switch (data_mode) { + case SEND_RSRC: + { + char *buffer; + size_t b_len; + + b_len = php_stream_copy_to_mem(data_ptr, &buffer, 65536, 0); + ctype = magic_buffer(magic, buffer, b_len); + efree(buffer); + break; + } + + case SEND_DATA: + ctype = magic_buffer(magic, data_ptr, data_len); + break; + + default: + HTTP_CHECK_OPEN_BASEDIR(data_ptr, magic_close(magic); return NULL); + ctype = magic_file(magic, data_ptr); + break; + } + + if (ctype) { + ct = estrdup(ctype); + } else { + http_error_ex(HE_WARNING, HTTP_E_RUNTIME, "Failed to guess Content-Type: %s", magic_error(magic)); + } + } + if (magic) { + magic_close(magic); + } +#else + http_error(HE_WARNING, HTTP_E_RUNTIME, "Cannot guess Content-Type; libmagic not available"); +#endif + + return ct; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/http_url_api.c @@ -0,0 +1,482 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_url_api.c 292841 2009-12-31 08:48:57Z mike $ */ + +#define HTTP_WANT_SAPI +#define HTTP_WANT_NETDB +#include "php_http.h" + +#include "zend_ini.h" +#include "php_output.h" +#include "ext/standard/php_string.h" + +#include "php_http_api.h" +#include "php_http_querystring_api.h" +#include "php_http_url_api.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")); +} + +PHP_MINIT_FUNCTION(http_url) +{ + HTTP_LONG_CONSTANT("HTTP_URL_REPLACE", HTTP_URL_REPLACE); + HTTP_LONG_CONSTANT("HTTP_URL_JOIN_PATH", HTTP_URL_JOIN_PATH); + HTTP_LONG_CONSTANT("HTTP_URL_JOIN_QUERY", HTTP_URL_JOIN_QUERY); + HTTP_LONG_CONSTANT("HTTP_URL_STRIP_USER", HTTP_URL_STRIP_USER); + HTTP_LONG_CONSTANT("HTTP_URL_STRIP_PASS", HTTP_URL_STRIP_PASS); + HTTP_LONG_CONSTANT("HTTP_URL_STRIP_AUTH", HTTP_URL_STRIP_AUTH); + HTTP_LONG_CONSTANT("HTTP_URL_STRIP_PORT", HTTP_URL_STRIP_PORT); + HTTP_LONG_CONSTANT("HTTP_URL_STRIP_PATH", HTTP_URL_STRIP_PATH); + HTTP_LONG_CONSTANT("HTTP_URL_STRIP_QUERY", HTTP_URL_STRIP_QUERY); + HTTP_LONG_CONSTANT("HTTP_URL_STRIP_FRAGMENT", HTTP_URL_STRIP_FRAGMENT); + HTTP_LONG_CONSTANT("HTTP_URL_STRIP_ALL", HTTP_URL_STRIP_ALL); + HTTP_LONG_CONSTANT("HTTP_URL_FROM_ENV", HTTP_URL_FROM_ENV); + return SUCCESS; +} + +PHP_HTTP_API char *_http_absolute_url_ex(const char *url, int flags TSRMLS_DC) +{ + char *abs = NULL; + php_url *purl = NULL; + + if (url) { + purl = php_url_parse(abs = estrdup(url)); + STR_SET(abs, NULL); + if (!purl) { + http_error_ex(HE_WARNING, HTTP_E_URL, "Could not parse URL (%s)", url); + return NULL; + } + } + + http_build_url(flags, purl, NULL, NULL, &abs, NULL); + + if (purl) { + php_url_free(purl); + } + + return abs; +} + +/* {{{ void http_build_url(int flags, const php_url *, const php_url *, php_url **, char **, size_t *) */ +PHP_HTTP_API void _http_build_url(int flags, const php_url *old_url, const php_url *new_url, php_url **url_ptr, char **url_str, size_t *url_len TSRMLS_DC) +{ +#if defined(HAVE_GETSERVBYPORT) || defined(HAVE_GETSERVBYNAME) + struct servent *se; +#endif + php_url *url = ecalloc(1, sizeof(php_url)); + +#define __URLSET(u,n) \ + ((u)&&(u)->n) +#define __URLCPY(n) \ + url->n = __URLSET(new_url,n) ? estrdup(new_url->n) : (__URLSET(old_url,n) ? estrdup(old_url->n) : NULL) + + if (!(flags & HTTP_URL_STRIP_PORT)) { + url->port = __URLSET(new_url, port) ? new_url->port : ((old_url) ? old_url->port : 0); + } + if (!(flags & HTTP_URL_STRIP_USER)) { + __URLCPY(user); + } + if (!(flags & HTTP_URL_STRIP_PASS)) { + __URLCPY(pass); + } + + __URLCPY(scheme); + __URLCPY(host); + + if (!(flags & HTTP_URL_STRIP_PATH)) { + if ((flags & HTTP_URL_JOIN_PATH) && __URLSET(old_url, path) && __URLSET(new_url, path) && *new_url->path != '/') { + size_t old_path_len = strlen(old_url->path), new_path_len = strlen(new_url->path); + + url->path = ecalloc(1, old_path_len + new_path_len + 1 + 1); + + strcat(url->path, old_url->path); + if (url->path[old_path_len - 1] != '/') { + php_dirname(url->path, old_path_len); + strcat(url->path, "/"); + } + strcat(url->path, new_url->path); + } else { + __URLCPY(path); + } + } + if (!(flags & HTTP_URL_STRIP_QUERY)) { + if ((flags & HTTP_URL_JOIN_QUERY) && __URLSET(new_url, query) && __URLSET(old_url, query)) { + zval qarr, qstr; + + INIT_PZVAL(&qstr); + INIT_PZVAL(&qarr); + array_init(&qarr); + + ZVAL_STRING(&qstr, old_url->query, 0); + http_querystring_modify(&qarr, &qstr); + ZVAL_STRING(&qstr, new_url->query, 0); + http_querystring_modify(&qarr, &qstr); + + ZVAL_NULL(&qstr); + http_querystring_update(&qarr, &qstr); + url->query = Z_STRVAL(qstr); + zval_dtor(&qarr); + } else { + __URLCPY(query); + } + } + if (!(flags & HTTP_URL_STRIP_FRAGMENT)) { + __URLCPY(fragment); + } + + if (!url->scheme) { + if (flags & HTTP_URL_FROM_ENV) { + zval *https = http_get_server_var("HTTPS", 1); + if (https && !strcasecmp(Z_STRVAL_P(https), "ON")) { + url->scheme = estrndup("https", lenof("https")); + } else switch (url->port) { + case 443: + url->scheme = estrndup("https", lenof("https")); + break; + +#ifndef HAVE_GETSERVBYPORT + default: +#endif + case 80: + case 0: + url->scheme = estrndup("http", lenof("http")); + break; + +#ifdef HAVE_GETSERVBYPORT + default: + if ((se = getservbyport(htons(url->port), "tcp")) && se->s_name) { + url->scheme = estrdup(se->s_name); + } else { + url->scheme = estrndup("http", lenof("http")); + } + break; +#endif + } + } else { + url->scheme = estrndup("http", lenof("http")); + } + } + + if (!url->host) { + if (flags & HTTP_URL_FROM_ENV) { + zval *zhost; + + if ((((zhost = http_get_server_var("HTTP_HOST", 1)) || + (zhost = http_get_server_var("SERVER_NAME", 1)))) && Z_STRLEN_P(zhost)) { + url->host = estrndup(Z_STRVAL_P(zhost), Z_STRLEN_P(zhost)); + } else { + url->host = localhostname(); + } + } else { + url->host = estrndup("localhost", lenof("localhost")); + } + } + + if (!url->path) { + if ((flags & HTTP_URL_FROM_ENV) && SG(request_info).request_uri && SG(request_info).request_uri[0]) { + const char *q = strchr(SG(request_info).request_uri, '?'); + + if (q) { + url->path = estrndup(SG(request_info).request_uri, q - SG(request_info).request_uri); + } else { + url->path = estrdup(SG(request_info).request_uri); + } + } else { + url->path = estrndup("/", 1); + } + } else if (url->path[0] != '/') { + if ((flags & HTTP_URL_FROM_ENV) && SG(request_info).request_uri && SG(request_info).request_uri[0]) { + size_t ulen = strlen(SG(request_info).request_uri); + size_t plen = strlen(url->path); + char *path; + + if (SG(request_info).request_uri[ulen-1] != '/') { + for (--ulen; ulen && SG(request_info).request_uri[ulen - 1] != '/'; --ulen); + } + + path = emalloc(ulen + plen + 1); + memcpy(path, SG(request_info).request_uri, ulen); + memcpy(path + ulen, url->path, plen); + path[ulen + plen] = '\0'; + STR_SET(url->path, path); + } else { + size_t plen = strlen(url->path); + char *path = emalloc(plen + 1 + 1); + + path[0] = '/'; + memcpy(&path[1], url->path, plen + 1); + STR_SET(url->path, path); + } + } + /* replace directory references if path is not a single slash */ + if (url->path[0] && (url->path[0] != '/' || url->path[1])) { + char *ptr, *end = url->path + strlen(url->path) + 1; + + for (ptr = strstr(url->path, "/."); ptr; ptr = strstr(ptr, "/.")) { + 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->path) { + if (*--ptr == '/') { + break; + } + } + memmove(&ptr[1], pos, end - pos); + break; + } else if (!ptr[3]) { + /* .. at the end */ + ptr[1] = '\0'; + } + /* fallthrough */ + + default: + /* something else */ + ++ptr; + break; + } + } + } + + if (url->port) { + if ( ((url->port == 80) && !strcmp(url->scheme, "http")) + || ((url->port ==443) && !strcmp(url->scheme, "https")) +#ifdef HAVE_GETSERVBYNAME + || ((se = getservbyname(url->scheme, "tcp")) && se->s_port && + (url->port == ntohs(se->s_port))) +#endif + ) { + url->port = 0; + } + } + + if (url_str) { + size_t len; + + *url_str = emalloc(HTTP_URL_MAXLEN + 1); + + **url_str = '\0'; + strlcat(*url_str, url->scheme, HTTP_URL_MAXLEN); + strlcat(*url_str, "://", HTTP_URL_MAXLEN); + + if (url->user && *url->user) { + strlcat(*url_str, url->user, HTTP_URL_MAXLEN); + if (url->pass && *url->pass) { + strlcat(*url_str, ":", HTTP_URL_MAXLEN); + strlcat(*url_str, url->pass, HTTP_URL_MAXLEN); + } + strlcat(*url_str, "@", HTTP_URL_MAXLEN); + } + + strlcat(*url_str, url->host, HTTP_URL_MAXLEN); + + if (url->port) { + char port_str[8]; + + snprintf(port_str, sizeof(port_str), "%d", (int) url->port); + strlcat(*url_str, ":", HTTP_URL_MAXLEN); + strlcat(*url_str, port_str, HTTP_URL_MAXLEN); + } + + strlcat(*url_str, url->path, HTTP_URL_MAXLEN); + + if (url->query && *url->query) { + strlcat(*url_str, "?", HTTP_URL_MAXLEN); + strlcat(*url_str, url->query, HTTP_URL_MAXLEN); + } + + if (url->fragment && *url->fragment) { + strlcat(*url_str, "#", HTTP_URL_MAXLEN); + strlcat(*url_str, url->fragment, HTTP_URL_MAXLEN); + } + + if (HTTP_URL_MAXLEN == (len = strlen(*url_str))) { + http_error(HE_NOTICE, HTTP_E_URL, "Length of URL exceeds HTTP_URL_MAXLEN"); + } + if (url_len) { + *url_len = len; + } + } + + if (url_ptr) { + *url_ptr = url; + } else { + php_url_free(url); + } +} +/* }}} */ + +/* {{{ STATUS http_urlencode_hash_ex(HashTable *, zend_bool, char *, size_t, char **, size_t *) */ +PHP_HTTP_API STATUS _http_urlencode_hash_ex(HashTable *hash, zend_bool override_argsep, + char *pre_encoded_data, size_t pre_encoded_len, + char **encoded_data, size_t *encoded_len TSRMLS_DC) +{ + char *arg_sep; + size_t arg_sep_len; + phpstr *qstr = phpstr_new(); + + if (override_argsep || !(arg_sep_len = strlen(arg_sep = INI_STR("arg_separator.output")))) { + arg_sep = HTTP_URL_ARGSEP; + arg_sep_len = lenof(HTTP_URL_ARGSEP); + } + + if (pre_encoded_len && pre_encoded_data) { + phpstr_append(qstr, pre_encoded_data, pre_encoded_len); + } + + if (SUCCESS != http_urlencode_hash_recursive(hash, qstr, arg_sep, arg_sep_len, NULL, 0)) { + phpstr_free(&qstr); + return FAILURE; + } + + phpstr_data(qstr, encoded_data, encoded_len); + phpstr_free(&qstr); + + return SUCCESS; +} +/* }}} */ + +/* {{{ http_urlencode_hash_recursive */ +PHP_HTTP_API STATUS _http_urlencode_hash_recursive(HashTable *ht, phpstr *str, const char *arg_sep, size_t arg_sep_len, const char *prefix, size_t prefix_len TSRMLS_DC) +{ + HashKey key = initHashKey(0); + zval **data = NULL; + HashPosition pos; + + if (!ht || !str) { + http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid parameters"); + return FAILURE; + } + if (ht->nApplyCount > 0) { + return SUCCESS; + } + + FOREACH_HASH_KEYVAL(pos, ht, key, data) { + char *encoded_key; + int encoded_len; + phpstr new_prefix; + + if (!data || !*data) { + phpstr_dtor(str); + return FAILURE; + } + + if (key.type == HASH_KEY_IS_STRING) { + if (!*key.str) { + /* only public properties */ + continue; + } + if (key.len && key.str[key.len - 1] == '\0') { + --key.len; + } + encoded_key = php_url_encode(key.str, key.len, &encoded_len); + } else { + encoded_len = spprintf(&encoded_key, 0, "%ld", key.num); + } + + { + phpstr_init(&new_prefix); + if (prefix && prefix_len) { + phpstr_append(&new_prefix, prefix, prefix_len); + phpstr_appends(&new_prefix, "%5B"); + } + + phpstr_append(&new_prefix, encoded_key, encoded_len); + efree(encoded_key); + + if (prefix && prefix_len) { + phpstr_appends(&new_prefix, "%5D"); + } + phpstr_fix(&new_prefix); + } + + if (Z_TYPE_PP(data) == IS_ARRAY || Z_TYPE_PP(data) == IS_OBJECT) { + STATUS status; + ++ht->nApplyCount; + status = http_urlencode_hash_recursive(HASH_OF(*data), str, arg_sep, arg_sep_len, PHPSTR_VAL(&new_prefix), PHPSTR_LEN(&new_prefix)); + --ht->nApplyCount; + if (SUCCESS != status) { + phpstr_dtor(&new_prefix); + phpstr_dtor(str); + return FAILURE; + } + } else { + zval *val = http_zsep(IS_STRING, *data); + + if (PHPSTR_LEN(str)) { + phpstr_append(str, arg_sep, arg_sep_len); + } + phpstr_append(str, PHPSTR_VAL(&new_prefix), PHPSTR_LEN(&new_prefix)); + phpstr_appends(str, "="); + + if (Z_STRLEN_P(val) && Z_STRVAL_P(val)) { + char *encoded_val; + int encoded_len; + + encoded_val = php_url_encode(Z_STRVAL_P(val), Z_STRLEN_P(val), &encoded_len); + phpstr_append(str, encoded_val, encoded_len); + efree(encoded_val); + } + + zval_ptr_dtor(&val); + } + phpstr_dtor(&new_prefix); + } + 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 + */ + --- /dev/null +++ b/ext/http/http_util_object.c @@ -0,0 +1,158 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: http_util_object.c 292841 2009-12-31 08:48:57Z mike $ */ + +#include "php_http.h" + +#ifdef ZEND_ENGINE_2 + +#include "ext/standard/php_http.h" + +#include "php_http_util_object.h" + +#define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpUtil, method, 0, req_args) +#define HTTP_EMPTY_ARGS(method) HTTP_EMPTY_ARGS_EX(HttpUtil, method, 0) + +#define HTTP_UTIL_ALIAS(method, func) HTTP_STATIC_ME_ALIAS(method, func, HTTP_ARGS(HttpUtil, method)) + +HTTP_BEGIN_ARGS(date, 0) + HTTP_ARG_VAL(timestamp, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(buildStr, 1) + HTTP_ARG_VAL(query, 0) + HTTP_ARG_VAL(prefix, 0) + HTTP_ARG_VAL(arg_sep, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(buildUrl, 1) + HTTP_ARG_VAL(url, 0) + HTTP_ARG_VAL(parts, 0) + HTTP_ARG_VAL(flags, 0) + HTTP_ARG_VAL(composed, 1) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(negotiateLanguage, 1) + HTTP_ARG_VAL(supported, 0) + HTTP_ARG_VAL(result, 1) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(negotiateCharset, 1) + HTTP_ARG_VAL(supported, 0) + HTTP_ARG_VAL(result, 1) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(negotiateContentType, 1) + HTTP_ARG_VAL(supported, 0) + HTTP_ARG_VAL(result, 1) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(matchModified, 1) + HTTP_ARG_VAL(last_modified, 0) + HTTP_ARG_VAL(for_range, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(matchEtag, 1) + HTTP_ARG_VAL(plain_etag, 0) + HTTP_ARG_VAL(for_range, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(matchRequestHeader, 2) + HTTP_ARG_VAL(header_name, 0) + HTTP_ARG_VAL(header_value, 0) + HTTP_ARG_VAL(case_sensitive, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(parseMessage, 1) + HTTP_ARG_VAL(message_string, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(parseHeaders, 1) + HTTP_ARG_VAL(headers_string, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(parseCookie, 1) + HTTP_ARG_VAL(cookie_string, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(buildCookie, 1) + HTTP_ARG_VAL(cookie_array, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(parseParams, 1) + HTTP_ARG_VAL(param_string, 0) + HTTP_ARG_VAL(flags, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(chunkedDecode, 1) + HTTP_ARG_VAL(encoded_string, 0) +HTTP_END_ARGS; + +#ifdef HTTP_HAVE_ZLIB +HTTP_BEGIN_ARGS(deflate, 1) + HTTP_ARG_VAL(plain, 0) + HTTP_ARG_VAL(flags, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(inflate, 1) + HTTP_ARG_VAL(encoded, 0) +HTTP_END_ARGS; +#endif + +HTTP_BEGIN_ARGS(support, 0) + HTTP_ARG_VAL(feature, 0) +HTTP_END_ARGS; + +zend_class_entry *http_util_object_ce; +zend_function_entry http_util_object_fe[] = { + HTTP_UTIL_ALIAS(date, http_date) + HTTP_UTIL_ALIAS(buildUrl, http_build_url) + HTTP_UTIL_ALIAS(buildStr, http_build_str) + HTTP_UTIL_ALIAS(negotiateLanguage, http_negotiate_language) + HTTP_UTIL_ALIAS(negotiateCharset, http_negotiate_charset) + HTTP_UTIL_ALIAS(negotiateContentType, http_negotiate_content_type) + HTTP_UTIL_ALIAS(matchModified, http_match_modified) + HTTP_UTIL_ALIAS(matchEtag, http_match_etag) + HTTP_UTIL_ALIAS(matchRequestHeader, http_match_request_header) + HTTP_UTIL_ALIAS(parseMessage, http_parse_message) + HTTP_UTIL_ALIAS(parseHeaders, http_parse_headers) + HTTP_UTIL_ALIAS(parseCookie, http_parse_cookie) + HTTP_UTIL_ALIAS(buildCookie, http_build_cookie) + HTTP_UTIL_ALIAS(parseParams, http_parse_params) + HTTP_UTIL_ALIAS(chunkedDecode, http_chunked_decode) +#ifdef HTTP_HAVE_ZLIB + HTTP_UTIL_ALIAS(deflate, http_deflate) + HTTP_UTIL_ALIAS(inflate, http_inflate) +#endif /* HTTP_HAVE_ZLIB */ + HTTP_UTIL_ALIAS(support, http_support) + + EMPTY_FUNCTION_ENTRY +}; + +PHP_MINIT_FUNCTION(http_util_object) +{ + HTTP_REGISTER_CLASS(HttpUtil, http_util_object, NULL, 0); + return SUCCESS; +} + +#endif /* ZEND_ENGINE_2 */ + +/* + * 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 + */ + --- /dev/null +++ b/ext/http/lib/BigGet.php @@ -0,0 +1,213 @@ + + * @license BSD, revised + * @version $Revision: 220502 $ + */ +class BigGet extends HttpRequestPool +{ + /** + * File split size + */ + const SIZE = 1048576; + + /** + * Parallel Request count + */ + const RMAX = 5; + + /** + * Whether to output debug messages + * + * @var bool + */ + public $dbg = false; + + /** + * URL + * + * @var string + */ + private $url; + + /** + * Temp file prefix + * + * @var string + */ + private $tmp; + + /** + * Size of requested resource + * + * @var int + */ + private $size; + + /** + * Whether the requests have been sent + * + * @var bool + */ + private $sent = false; + + /** + * Request counter + * + * @var int + */ + private $count = 0; + + /** + * Static constructor + * + * @param string $url + * @param string $tmp + * @return BigGet + * @throws Exception + */ + public static function url($url, $tmp = '/tmp') + { + $head = new HttpRequest($url, HttpRequest::METH_HEAD); + $headers = $head->send()->getHeaders(); + + if (200 != $head->getResponseCode()) { + throw new HttpException("Did not receive '200 Ok' from HEAD $url"); + } + if (!isset($headers['Accept-Ranges'])) { + throw new HttpException("Did not receive an Accept-Ranges header from HEAD $url"); + } + if (!isset($headers['Content-Length'])) { + throw new HttpException("Did not receive a Content-Length header from HEAD $url"); + } + + $bigget = new BigGet; + $bigget->url = $url; + $bigget->tmp = tempnam($tmp, 'BigGet.'); + $bigget->size = $headers['Content-Length']; + return $bigget; + } + + /** + * Save the resource to a file + * + * @param string $file + * @return bool + * @throws Exception + */ + public function saveTo($file) + { + $this->sent or $this->send(); + + if ($w = fopen($this->tmp, 'wb')) { + + $this->dbg && print "\nCopying temp files to $file ...\n"; + + foreach (glob($this->tmp .".????") as $tmp) { + + $this->dbg && print "\t$tmp\n"; + + if ($r = fopen($tmp, 'rb')) { + stream_copy_to_stream($r, $w); + fclose($r); + } + unlink($tmp); + } + fclose($w); + rename($this->tmp, $file); + + $this->dbg && print "\nDone.\n"; + + return true; + } + return false; + } + + /** + * Overrides HttpRequestPool::send() + * + * @return void + * @throws Exception + */ + public function send() + { + $this->sent = true; + + // use max RMAX simultanous requests with a req size of SIZE + while ($this->count < self::RMAX && -1 != $offset = $this->getRangeOffset()) { + $this->attachNew($offset); + } + + while ($this->socketPerform()) { + if (!$this->socketSelect()) { + throw new HttpSocketException; + } + } + } + + /** + * Overrides HttpRequestPool::socketPerform() + * + * @return bool + */ + protected function socketPerform() + { + $rs = parent::socketPerform(); + + foreach ($this->getFinishedRequests() as $r) { + $this->detach($r); + + if (206 != $rc = $r->getResponseCode()) { + throw new HttpException("Unexpected response code: $rc"); + } + + file_put_contents(sprintf("%s.%04d", $this->tmp, $r->id), $r->getResponseBody()); + + if (-1 != $offset = $this->getRangeOffset()) { + $this->attachNew($offset); + } + } + + return $rs; + } + + private function attachNew($offset) + { + $stop = min($this->count * self::SIZE + self::SIZE, $this->size) - 1; + + $this->dbg && print "Attaching new request to get range: $offset-$stop\n"; + + $req = new BigGetRequest( + $this->url, + HttpRequest::METH_GET, + array( + 'headers' => array( + 'Range' => "bytes=$offset-$stop" + ) + ) + ); + $this->attach($req); + $req->id = $this->count++; + } + + private function getRangeOffset() + { + return ($this->size >= $start = $this->count * self::SIZE) ? $start : -1; + } +} + + +/** + * BigGet request + * @ignore + */ +class BigGetRequest extends HttpRequest +{ + public $id; +} + +?> --- /dev/null +++ b/ext/http/lib/FeedAggregator.php @@ -0,0 +1,187 @@ + + * @license BSD, revised + * @package pecl/http + * @version $Revision: 208774 $ + */ +class FeedAggregator +{ + /** + * Cache directory + * + * @var string + */ + public $directory; + + /** + * Feeds + * + * @var array + */ + protected $feeds = array(); + + /** + * Constructor + * + * @param string $directory + */ + public function __construct($directory = 'feeds') + { + $this->setDirectory($directory); + } + + /** + * Set cache directory + * + * @param string $directory + */ + public function setDirectory($directory) + { + $this->directory = $directory; + foreach (glob($this->directory .'/*.xml') as $feed) { + $this->feeds[basename($feed, '.xml')] = filemtime($feed); + } + } + + /** + * Strips all special chars + * + * @param string $url + * @return string + */ + public function url2name($url) + { + return preg_replace('/[^\w\.-]+/', '_', $url); + } + + /** + * Checks if $url is a known feed + * + * @param string $url + * @return bool + */ + public function hasFeed($url) + { + return isset($this->feeds[$this->url2name($url)]); + } + + /** + * Add an URL as feed + * + * @param string $url + * @return void + * @throws Exception + */ + public function addFeed($url) + { + $r = $this->setupRequest($url); + $r->send(); + $this->handleResponse($r); + } + + /** + * Add several URLs as feeds + * + * @param array $urls + * @return void + * @throws Exception + */ + public function addFeeds(array $urls) + { + $pool = new HttpRequestPool; + foreach ($urls as $url) { + $pool->attach($r = $this->setupRequest($url)); + } + $pool->send(); + + foreach ($pool as $request) { + $this->handleResponse($request); + } + } + + /** + * Load a feed (from cache) + * + * @param string $url + * @return string + * @throws Exception + */ + public function getFeed($url) + { + $this->addFeed($url); + return $this->loadFeed($this->url2name($url)); + } + + /** + * Load several feeds (from cache) + * + * @param array $urls + * @return array + * @throws Exception + */ + public function getFeeds(array $urls) + { + $feeds = array(); + $this->addFeeds($urls); + foreach ($urls as $url) { + $feeds[] = $this->loadFeed($this->url2name($url)); + } + return $feeds; + } + + protected function saveFeed($file, $contents) + { + if (file_put_contents($this->directory .'/'. $file .'.xml', $contents)) { + $this->feeds[$file] = time(); + } else { + throw new Exception("Could not save feed contents to $file.xml"); + } + } + + protected function loadFeed($file) + { + if (isset($this->feeds[$file])) { + if ($data = file_get_contents($this->directory .'/'. $file .'.xml')) { + return $data; + } else { + throw new Exception("Could not load feed contents from $file.xml"); + } + } else { + throw new Exception("Unknown feed/file $file.xml"); + } + } + + protected function setupRequest($url, $escape = true) + { + $r = new HttpRequest($url); + $r->setOptions(array('redirect' => true)); + + $file = $escape ? $this->url2name($url) : $url; + + if (isset($this->feeds[$file])) { + $r->setOptions(array('lastmodified' => $this->feeds[$file])); + } + + return $r; + } + + protected function handleResponse(HttpRequest $r) + { + if ($r->getResponseCode() != 304) { + if ($r->getResponseCode() != 200) { + throw new Exception("Unexpected response code ". $r->getResponseCode()); + } + if (!strlen($body = $r->getResponseBody())) { + throw new Exception("Received empty feed from ". $r->getUrl()); + } + $this->saveFeed($this->url2name($r->getUrl()), $body); + } + } +} + +?> --- /dev/null +++ b/ext/http/lib/PgLobStream.php @@ -0,0 +1,100 @@ + + * // GET /image.php?image=1234 + * if (PgLobStream::$loId = (int) $_GET['image']) { + * if ($lob = fopen('pglob://dbname=database user=mike', 'r')) { + * HttpResponse::setContentType('image/jpeg'); + * HttpResponse::setStream($lob); + * HttpResponse::send(); + * } + * } + * + * + * @copyright Michael Wallner, + * @license BSD, revised + * @package pecl/http + * @version $Revision: 210232 $ + */ +class PgLobStream +{ + private $dbh; + private $loh; + private $lon; + private $size = 0; + + public static $loId; + + function stream_open($path, $mode) + { + $path = trim(parse_url($path, PHP_URL_HOST)); + + if ($path) { + if ($this->dbh = pg_connect($path)) { + if (pg_query($this->dbh, 'BEGIN')) { + if (is_resource($this->loh = pg_lo_open($this->dbh, $this->lon = self::$loId, $mode))) { + pg_lo_seek($this->loh, 0, PGSQL_SEEK_END); + $this->size = (int) pg_lo_tell($this->loh); + pg_lo_seek($this->loh, 0, PGSQL_SEEK_SET); + return true; + } + } + } + } + return false; + } + + function stream_read($length) + { + return pg_lo_read($this->loh, $length); + } + + function stream_seek($offset, $whence = PGSQL_SEEK_SET) + { + return pg_lo_seek($this->loh, $offset, $whence); + } + + function stream_tell() + { + return pg_lo_tell($this->loh); + } + + function stream_eof() + { + return pg_lo_tell($this->loh) >= $this->size; + } + + function stream_flush() + { + return true; + } + + function stream_stat() + { + return array('size' => $this->size, 'ino' => $this->lon); + } + + function stream_write($data) + { + return pg_lo_write($this->loh, $data); + } + + function stream_close() + { + if (pg_lo_close($this->loh)) { + return pg_query($this->dbh, 'COMMIT'); + } else { + pg_query($this->dbh, 'ROLLBACK'); + return false; + } + } +} + +stream_register_wrapper('pglob', 'PgLobStream'); + +?> --- /dev/null +++ b/ext/http/lib/XmlRpcClient.php @@ -0,0 +1,119 @@ + + * __request->setOptions(array('compress' => true)); + * try { + * print_r($rpc->vpop->listdomain(array('domain' => 'example.com'))); + * } catch (Exception $ex) { + * echo $ex; + * } + * ?> + * + * + * @copyright Michael Wallner, + * @license BSD, revised + * @package pecl/http + * @version $Revision: 227268 $ + */ +class XmlRpcClient +{ + /** + * RPC namespace + * + * @var string + */ + public $__namespace; + + /** + * HttpRequest instance + * + * @var HttpRequest + */ + public $__request; + + /** + * Client charset + * + * @var string + */ + public $__encoding = "iso-8859-1"; + + /** + * RPC options + * + * @var array + */ + public $__options; + + /** + * Constructor + * + * @param string $url RPC endpoint + * @param string $namespace RPC namespace + * @param array $options HttpRequest options + */ + public function __construct($url, $namespace = '', array $options = null) + { + $this->__request = new HttpRequest($url, HttpRequest::METH_POST, (array) $options); + $this->__namespace = $namespace; + } + + /** + * RPC method proxy + * + * @param string $method RPC method name + * @param array $params RPC method arguments + * @return mixed decoded RPC response + * @throws Exception + */ + public function __call($method, array $params) + { + if (strlen($this->__namespace)) { + $method = $this->__namespace .'.'. $method; + } + $this->__request->setContentType("text/xml"); + $this->__request->setRawPostData( + xmlrpc_encode_request($method, $params, + array("encoding" => $this->__encoding) + (array) $this->__options)); + $response = $this->__request->send(); + if ($response->getResponseCode() != 200) { + throw new Exception( + $response->getResponseStatus(), + $response->getResponseCode() + ); + } + + $data = xmlrpc_decode($response->getBody(), $this->__encoding); + if (xmlrpc_is_fault($data)) { + throw new Exception( + (string) $data['faultString'], + (int) $data['faultCode'] + ); + } + + return $data; + } + + /** + * Returns self, where namespace is set to variable name + * + * @param string $ns + * @return XmlRpcRequest + */ + public function __get($ns) + { + $this->__namespace = $ns; + return $this; + } +} + +?> --- /dev/null +++ b/ext/http/lib/XmlRpcServer.php @@ -0,0 +1,254 @@ + + * registerHandler(new Handler); + * XmlRpcServer::run(); + * } catch (Exception $ex) { + * XmlRpcServer::error($ex->getCode(), $ex->getMessage()); + * } + * + * + * @copyright Michael Wallner, + * @license BSD, revised + * @package pecl/http + * @version $Revision: 227268 $ + */ + +class XmlRpcServer extends HttpResponse +{ + /** + * Server charset + * + * @var string + */ + public static $encoding = "iso-8859-1"; + + /** + * RPC namespace + * + * @var string + */ + public $namespace; + + /** + * RPC handler attached to this server instance + * + * @var XmlRpcRequestHandler + */ + protected $handler; + + private static $xmlreq; + private static $xmlrpc; + private static $refcnt = 0; + private static $handle = array(); + + /** + * Create a new XmlRpcServer instance + * + * @param string $namespace + * @param string $encoding + */ + public function __construct($namespace) + { + $this->namespace = $namespace; + self::initialize(); + } + + /** + * Destructor + */ + public function __destruct() + { + if (self::$refcnt && !--self::$refcnt) { + xmlrpc_server_destroy(self::$xmlrpc); + } + } + + /** + * Static factory + * + * @param string $namespace + * @return XmlRpcServer + */ + public static function factory($namespace) + { + return new XmlRpcServer($namespace); + } + + /** + * Run all servers and send response + * + * @param array $options + */ + public static function run(array $options = null) + { + self::initialize(false, true); + self::setContentType("text/xml; charset=". self::$encoding); + echo xmlrpc_server_call_method(self::$xmlrpc, self::$xmlreq, null, + array("encoding" => self::$encoding) + (array) $options); + } + + /** + * Test hook; call instead of XmlRpcServer::run() + * + * @param string $method + * @param array $params + * @param array $request_options + * @param array $response_options + */ + public static function test($method, array $params, array $request_options = null, array $response_options = null) + { + self::$xmlreq = xmlrpc_encode_request($method, $params, $request_options); + self::run($response_options); + } + + /** + * Optional XMLRPC error handler + * + * @param int $code + * @param string $msg + */ + public static function error($code, $msg, array $options = null) + { + echo xmlrpc_encode(array("faultCode" => $code, "faultString" => $msg), + array("encoding" => self::$encoding) + (array) $options); + } + + /** + * Register a single method + * + * @param string $name + * @param mixed $callback + * @param mixed $dispatch + * @param array $spec + */ + public function registerMethod($name, $callback, $dispatch = null, array $spec = null) + { + if (!is_callable($callback, false, $cb_name)) { + throw new Exception("$cb_name is not a valid callback"); + } + if (isset($dispatch)) { + if (!is_callable($dispatch, false, $cb_name)) { + throw new Exception("$cb_name is not a valid callback"); + } + xmlrpc_server_register_method(self::$xmlrpc, $name, $dispatch); + self::$handle[$name] = $callback; + } else { + xmlrpc_server_register_method(self::$xmlrpc, $name, $callback); + } + + if (isset($spec)) { + xmlrpc_server_add_introspection_data(self::$xmlrpc, $spec); + } + } + + /** + * Register an XmlRpcRequestHandler for this server instance + * + * @param XmlRpcRequestHandler $handler + */ + public function registerHandler(XmlRpcRequestHandler $handler) + { + $this->handler = $handler; + + foreach (get_class_methods($handler) as $method) { + if (!strncmp($method, "xmlrpc", 6)) { + $this->registerMethod( + $this->method($method, $handler->getNamespace()), + array($handler, $method), array($this, "dispatch")); + } + } + + $handler->getIntrospectionData($spec); + if (is_array($spec)) { + xmlrpc_server_add_introspection_data(self::$xmlrpc, $spec); + } + } + + private function method($method, $namespace = null) + { + if (!strlen($namespace)) { + $namespace = strlen($this->namespace) ? $this->namespace : "xmlrpc"; + } + return $namespace .".". strtolower($method[6]) . substr($method, 7); + } + + private function dispatch($method, array $params = null) + { + if (array_key_exists($method, self::$handle)) { + return call_user_func(self::$handle[$method], $params); + } + throw new Exception("Unknown XMLRPC method: $method"); + } + + private static function initialize($server = true, $data = false) + { + if ($data) { + if (!self::$xmlreq && !(self::$xmlreq = http_get_request_body())) { + throw new Exception("Failed to fetch XMLRPC request body"); + } + } + if ($server) { + if (!self::$xmlrpc && !(self::$xmlrpc = xmlrpc_server_create())) { + throw new Exception("Failed to initialize XMLRPC server"); + } + ++self::$refcnt; + } + } +} + +/** + * XmlRpcRequestHandler + * + * Define XMLRPC methods with an "xmlrpc" prefix, eg: + * + * class IntOp implements XmlRpcRequestHandler { + * public function getNamespace() { + * return "int"; + * } + * public function getInstrospectionData(array &$spec = null) { + * } + * // XMLRPC method name: int.sumValues + * public function xmlrpcSumValues(array $values) { + * return array_sum($values); + * } + * } + * + */ +interface XmlRpcRequestHandler +{ + public function getNamespace(); + public function getIntrospectionData(array &$spec = null); +} + +/** + * XmlRpcRequestHandlerStub + */ +abstract class XmlRpcRequestHandlerStub implements XmlRpcRequestHandler +{ + public function getNamespace() + { + } + public function getIntrospectionData(array &$spec = null) + { + } +} + +?> --- /dev/null +++ b/ext/http/missing.c @@ -0,0 +1,74 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: missing.c 292841 2009-12-31 08:48:57Z mike $ */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "php.h" +#include "missing.h" + +#ifdef WONKY +int zend_declare_property_double(zend_class_entry *ce, char *name, int name_length, double value, int access_type TSRMLS_DC) +{ + zval *property = pemalloc(sizeof(zval), ce->type & ZEND_INTERNAL_CLASS); + INIT_PZVAL(property); + ZVAL_DOUBLE(property, value); + return zend_declare_property(ce, name, name_length, property, access_type TSRMLS_CC); +} + +void zend_update_property_double(zend_class_entry *scope, zval *object, char *name, int name_length, double value TSRMLS_DC) +{ + zval *tmp = ecalloc(1, sizeof(zval)); + ZVAL_DOUBLE(tmp, value); + zend_update_property(scope, object, name, name_length, tmp TSRMLS_CC); +} + +int zend_declare_property_bool(zend_class_entry *ce, char *name, int name_length, long value, int access_type TSRMLS_DC) +{ + zval *property = pemalloc(sizeof(zval), ce->type & ZEND_INTERNAL_CLASS); + INIT_PZVAL(property); + ZVAL_BOOL(property, value); + return zend_declare_property(ce, name, name_length, property, access_type TSRMLS_CC); +} + +void zend_update_property_bool(zend_class_entry *scope, zval *object, char *name, int name_length, long value TSRMLS_DC) +{ + zval *tmp = ecalloc(1, sizeof(zval)); + ZVAL_BOOL(tmp, value); + zend_update_property(scope, object, name, name_length, tmp TSRMLS_CC); +} + +void zend_update_property_stringl(zend_class_entry *scope, zval *object, char *name, int name_length, char *value, int value_len TSRMLS_DC) +{ + zval *tmp; + + ALLOC_ZVAL(tmp); + tmp->is_ref = 0; + tmp->refcount = 0; + ZVAL_STRINGL(tmp, value, value_len, 1); + zend_update_property(scope, object, name, name_length, tmp TSRMLS_CC); +} + +#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 + */ + --- /dev/null +++ b/ext/http/missing.h @@ -0,0 +1,180 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: missing.h 298892 2010-05-03 08:29:31Z mike $ */ + +#ifndef PHP_HTTP_MISSING +#define PHP_HTTP_MISSING + +#include "php_version.h" + +#if defined(PHP_VERSION_ID) && (PHP_VERSION_ID >= 50399) +# define ZEND_LITERAL_KEY_DC , const zend_literal *_zend_literal_key +# define ZEND_LITERAL_KEY_CC , _zend_literal_key +# define ZEND_LITERAL_NIL_CC , NULL +# define HTTP_CHECK_OPEN_BASEDIR(file, act) \ + if ((PG(open_basedir) && *PG(open_basedir))) \ + { \ + const char *tmp = file; \ + \ + if (!strncasecmp(tmp, "file:", lenof("file:"))) { \ + tmp += lenof("file:"); \ + while ((tmp - (const char *)file < 7) && (*tmp == '/' || *tmp == '\\')) ++tmp; \ + } \ + \ + if ( (tmp != file || !strstr(file, "://")) && \ + (!*tmp || php_check_open_basedir(tmp TSRMLS_CC))) { \ + act; \ + } \ + } + +#else +# define ZEND_LITERAL_KEY_DC +# define ZEND_LITERAL_KEY_CC +# define ZEND_LITERAL_NIL_CC +# define HTTP_CHECK_OPEN_BASEDIR(file, act) \ + if ((PG(open_basedir) && *PG(open_basedir)) || PG(safe_mode)) \ + { \ + const char *tmp = file; \ + \ + if (!strncasecmp(tmp, "file:", lenof("file:"))) { \ + tmp += lenof("file:"); \ + while ((tmp - (const char *)file < 7) && (*tmp == '/' || *tmp == '\\')) ++tmp; \ + } \ + \ + if ( (tmp != file || !strstr(file, "://")) && \ + (!*tmp || php_check_open_basedir(tmp TSRMLS_CC) || \ + (PG(safe_mode) && !php_checkuid(tmp, "rb+", CHECKUID_CHECK_MODE_PARAM)))) { \ + act; \ + } \ + } + +#endif + +#if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION >= 3) +# define HTTP_ZAPI_HASH_TSRMLS_CC TSRMLS_CC +# define HTTP_ZAPI_HASH_TSRMLS_DC TSRMLS_DC +# define HTTP_ZAPI_CONST_CAST(t) (const t) +# define GLOBAL_ERROR_HANDLING EG(error_handling) +# define GLOBAL_EXCEPTION_CLASS EG(exception_class) +# define IS_CALLABLE(cb_zv, flags, cb_sp) zend_is_callable((cb_zv), (flags), (cb_sp) TSRMLS_CC) +# define HTTP_STATIC_ARG_INFO +#else +# define HTTP_ZAPI_HASH_TSRMLS_CC +# define HTTP_ZAPI_HASH_TSRMLS_DC +# define HTTP_ZAPI_CONST_CAST(t) (t) +# define GLOBAL_ERROR_HANDLING PG(error_handling) +# define GLOBAL_EXCEPTION_CLASS PG(exception_class) +# define IS_CALLABLE(cb_zv, flags, cb_sp) zend_is_callable((cb_zv), (flags), (cb_sp)) +# define HTTP_STATIC_ARG_INFO static +#endif + +#if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION == 0) +# define WONKY +#endif + +#ifndef pemalloc_rel +# define pemalloc_rel(size, persistent) ((persistent)?malloc(size):emalloc_rel(size)) +#endif + +#ifndef ZEND_ACC_DEPRECATED +# define ZEND_ACC_DEPRECATED 0 +#endif + +#if PHP_MAJOR_VERSION == 4 && PHP_MINOR_VERSION == 3 && PHP_RELEASE_VERSION < 10 +# define php_url_parse_ex(u, l) php_url_parse(u) +#endif + +#ifndef TSRMLS_FETCH_FROM_CTX +# ifdef ZTS +# define TSRMLS_FETCH_FROM_CTX(ctx) void ***tsrm_ls = (void ***) ctx +# else +# define TSRMLS_FETCH_FROM_CTX(ctx) +# endif +#endif + +#ifndef TSRMLS_SET_CTX +# ifdef ZTS +# define TSRMLS_SET_CTX(ctx) ctx = (void ***) tsrm_ls +# else +# define TSRMLS_SET_CTX(ctx) +# endif +#endif + +#ifndef ZVAL_ADDREF +# define ZVAL_ADDREF Z_ADDREF_P +#endif + +#ifndef SEPARATE_ARG_IF_REF +#define SEPARATE_ARG_IF_REF(zv) \ + if (PZVAL_IS_REF(zv)) { \ + zval *ov = zv; \ + ALLOC_INIT_ZVAL(zv); \ + Z_TYPE_P(zv) = Z_TYPE_P(ov); \ + zv->value = ov->value; \ + zval_copy_ctor(zv); \ + } else { \ + ZVAL_ADDREF(zv); \ + } +#endif + +#ifndef ZVAL_ZVAL +#define ZVAL_ZVAL(z, zv, copy, dtor) { \ + int is_ref, refcount; \ + is_ref = (z)->is_ref; \ + refcount = (z)->refcount; \ + *(z) = *(zv); \ + if (copy) { \ + zval_copy_ctor(z); \ + } \ + if (dtor) { \ + if (!copy) { \ + ZVAL_NULL(zv); \ + } \ + zval_ptr_dtor(&zv); \ + } \ + (z)->is_ref = is_ref; \ + (z)->refcount = refcount; \ + } +#endif +#ifndef RETVAL_ZVAL +# define RETVAL_ZVAL(zv, copy, dtor) ZVAL_ZVAL(return_value, zv, copy, dtor) +#endif +#ifndef RETURN_ZVAL +# define RETURN_ZVAL(zv, copy, dtor) { RETVAL_ZVAL(zv, copy, dtor); return; } +#endif + +#ifndef ZEND_MN +# define ZEND_MN(name) ZEND_FN(name) +#endif + +#ifdef WONKY +extern int zend_declare_property_double(zend_class_entry *ce, char *name, int name_length, double value, int access_type TSRMLS_DC); +extern void zend_update_property_double(zend_class_entry *scope, zval *object, char *name, int name_length, double value TSRMLS_DC); + +extern int zend_declare_property_bool(zend_class_entry *ce, char *name, int name_length, long value, int access_type TSRMLS_DC); +extern void zend_update_property_bool(zend_class_entry *scope, zval *object, char *name, int name_length, long value TSRMLS_DC); + +extern void zend_update_property_stringl(zend_class_entry *scope, zval *object, char *name, int name_length, char *value, int value_len TSRMLS_DC); +#endif + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/php_http.h @@ -0,0 +1,262 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http.h 310776 2011-05-05 06:35:46Z mike $ */ + +#ifndef PHP_EXT_HTTP_H +#define PHP_EXT_HTTP_H + +#define PHP_HTTP_VERSION "1.7.1" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#else +# ifndef PHP_WIN32 +# include "php_config.h" +# endif +#endif + +#include "php.h" +#include "missing.h" +#include "php_http_std_defs.h" +#include "phpstr/phpstr.h" + +#ifdef HTTP_WANT_SAPI +# if PHP_API_VERSION > 20041225 +# define HTTP_HAVE_SAPI_RTIME +# endif +# include "SAPI.h" +#endif + +#ifdef HTTP_WANT_NETDB +# ifdef PHP_WIN32 +# define HTTP_HAVE_NETDB +# include +# elif defined(HAVE_NETDB_H) +# define HTTP_HAVE_NETDB +# include +# ifdef HAVE_UNISTD_H +# include +# endif +# endif +#endif + +#if defined(HTTP_WANT_CURL) && defined(HTTP_HAVE_CURL) +# ifdef PHP_WIN32 +# include +# define CURL_STATICLIB +# endif +# include +# define HTTP_CURL_VERSION(x, y, z) (LIBCURL_VERSION_NUM >= (((x)<<16) + ((y)<<8) + (z))) +# +# if defined(HTTP_WANT_EVENT) && defined(HTTP_HAVE_EVENT) +# include +# endif +#endif + +#if defined(HTTP_WANT_MAGIC) && defined(HTTP_HAVE_MAGIC) +# if defined(PHP_WIN32) && !defined(USE_MAGIC_DLL) && !defined(USE_MAGIC_STATIC) +# define USE_MAGIC_STATIC +# endif +# include +#endif + +#if defined(HTTP_WANT_ZLIB) && defined(HTTP_HAVE_ZLIB) +# include +#endif + +#include +#define HTTP_IS_CTYPE(type, c) is##type((int) (unsigned char) (c)) +#define HTTP_TO_CTYPE(type, c) to##type((int) (unsigned char) (c)) + +extern zend_module_entry http_module_entry; +#define phpext_http_ptr &http_module_entry + +extern int http_module_number; + +ZEND_BEGIN_MODULE_GLOBALS(http) + + struct _http_globals_etag { + char *mode; + void *ctx; + zend_bool started; + } etag; + + struct _http_globals_log { + char *cache; + char *redirect; + char *not_found; + char *allowed_methods; + char *composite; + } log; + + struct _http_globals_send { + double throttle_delay; + size_t buffer_size; + char *content_type; + char *unquoted_etag; + time_t last_modified; + struct _http_globals_send_deflate { + zend_bool response; + zend_bool start_auto; + long start_flags; + int encoding; + void *stream; + } deflate; + struct _http_globals_send_inflate { + zend_bool start_auto; + long start_flags; + void *stream; + } inflate; + zend_bool not_found_404; + } send; + + struct _http_globals_request { + time_t time; + HashTable *headers; + struct _http_globals_request_methods { + HashTable registered; + char *allowed; + char *custom; + } methods; +#if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL) + struct _http_globals_request_datashare { + zend_llist handles; + zend_bool cookie; + zend_bool dns; + zend_bool ssl; + zend_bool connect; + } datashare; +#endif +#if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_EVENT) + struct _http_globals_request_pool { + struct _http_globals_request_pool_event { + void *base; + } event; + } pool; +#endif + } request; + + struct _http_globals_persistent { + struct _http_globals_persistent_handles { + ulong limit; + struct _http_globals_persistent_handles_ident { + ulong h; + char *s; + size_t l; + } ident; + } handles; + } persistent; + +#ifdef ZEND_ENGINE_2 + zend_bool only_exceptions; +#endif + + zend_bool force_exit; + zend_bool read_post_data; + zval *server_var; + +ZEND_END_MODULE_GLOBALS(http) + +ZEND_EXTERN_MODULE_GLOBALS(http); + +#ifdef ZTS +# include "TSRM.h" +# define HTTP_G ((zend_http_globals *) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(http_globals_id)]) +#else +# define HTTP_G (&http_globals) +#endif + +#if defined(HAVE_ICONV) && (HTTP_SHARED_DEPS || !defined(COMPILE_DL_ICONV)) +# define HTTP_HAVE_ICONV +#endif + +#if defined(HAVE_PHP_SESSION) && (HTTP_SHARED_DEPS || !defined(COMPILE_DL_SESSION)) +# define HTTP_HAVE_SESSION +#endif + +#if defined(HAVE_HASH_EXT) && (HTTP_SHARED_DEPS || !defined(COMPILE_DL_HASH)) && defined(HTTP_HAVE_PHP_HASH_H) +# define HTTP_HAVE_HASH +#endif + +#if defined(HAVE_SPL) +# define HTTP_HAVE_SPL +#endif + +PHP_FUNCTION(http_date); +PHP_FUNCTION(http_build_url); +PHP_FUNCTION(http_build_str); +PHP_FUNCTION(http_negotiate_language); +PHP_FUNCTION(http_negotiate_charset); +PHP_FUNCTION(http_negotiate_content_type); +PHP_FUNCTION(http_negotiate); +PHP_FUNCTION(http_redirect); +PHP_FUNCTION(http_throttle); +PHP_FUNCTION(http_send_status); +PHP_FUNCTION(http_send_last_modified); +PHP_FUNCTION(http_send_content_type); +PHP_FUNCTION(http_send_content_disposition); +PHP_FUNCTION(http_match_modified); +PHP_FUNCTION(http_match_etag); +PHP_FUNCTION(http_cache_last_modified); +PHP_FUNCTION(http_cache_etag); +PHP_FUNCTION(http_send_data); +PHP_FUNCTION(http_send_file); +PHP_FUNCTION(http_send_stream); +PHP_FUNCTION(http_chunked_decode); +PHP_FUNCTION(http_parse_message); +PHP_FUNCTION(http_parse_headers); +PHP_FUNCTION(http_parse_cookie); +PHP_FUNCTION(http_build_cookie); +PHP_FUNCTION(http_parse_params); +PHP_FUNCTION(http_get_request_headers); +PHP_FUNCTION(http_get_request_body); +PHP_FUNCTION(http_get_request_body_stream); +PHP_FUNCTION(http_match_request_header); +PHP_FUNCTION(http_persistent_handles_count); +PHP_FUNCTION(http_persistent_handles_clean); +PHP_FUNCTION(http_persistent_handles_ident); +#ifdef HTTP_HAVE_CURL +PHP_FUNCTION(http_get); +PHP_FUNCTION(http_head); +PHP_FUNCTION(http_post_data); +PHP_FUNCTION(http_post_fields); +PHP_FUNCTION(http_put_data); +PHP_FUNCTION(http_put_file); +PHP_FUNCTION(http_put_stream); +PHP_FUNCTION(http_request); +PHP_FUNCTION(http_request_body_encode); +#endif /* HTTP_HAVE_CURL */ +PHP_FUNCTION(http_request_method_register); +PHP_FUNCTION(http_request_method_unregister); +PHP_FUNCTION(http_request_method_exists); +PHP_FUNCTION(http_request_method_name); +PHP_FUNCTION(ob_etaghandler); +#ifdef HTTP_HAVE_ZLIB +PHP_FUNCTION(http_deflate); +PHP_FUNCTION(http_inflate); +PHP_FUNCTION(ob_deflatehandler); +PHP_FUNCTION(ob_inflatehandler); +#endif +PHP_FUNCTION(http_support); + +#endif /* PHP_HTTP_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 + */ + --- /dev/null +++ b/ext/http/php_http_api.h @@ -0,0 +1,318 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_api.h 298891 2010-05-03 08:26:38Z mike $ */ + +#ifndef PHP_HTTP_API_H +#define PHP_HTTP_API_H + +#define HTTP_SUPPORT 0x01L +#define HTTP_SUPPORT_REQUESTS 0x02L +#define HTTP_SUPPORT_MAGICMIME 0x04L +#define HTTP_SUPPORT_ENCODINGS 0x08L +#define HTTP_SUPPORT_SSLREQUESTS 0x20L +#define HTTP_SUPPORT_PERSISTENCE 0x40L +#define HTTP_SUPPORT_EVENTS 0x80L + +#define HTTP_PARAMS_ALLOW_COMMA 0x01 +#define HTTP_PARAMS_ALLOW_FAILURE 0x02 +#define HTTP_PARAMS_RAISE_ERROR 0x04 +#define HTTP_PARAMS_DEFAULT (HTTP_PARAMS_ALLOW_COMMA|HTTP_PARAMS_ALLOW_FAILURE|HTTP_PARAMS_RAISE_ERROR) +#define HTTP_PARAMS_COLON_SEPARATOR 0x10 + +extern PHP_MINIT_FUNCTION(http_support); + +#define http_support(f) _http_support(f) +PHP_HTTP_API long _http_support(long feature); + +#define pretty_key(key, key_len, uctitle, xhyphen) _http_pretty_key(key, key_len, uctitle, xhyphen) +extern char *_http_pretty_key(char *key, size_t key_len, zend_bool uctitle, zend_bool xhyphen); + +#define http_boundary(b, l) _http_boundary((b), (l) TSRMLS_CC) +extern size_t _http_boundary(char *buf, size_t len TSRMLS_DC); + +#define http_error(type, code, string) _http_error_ex(type, code, "%s", string) +#define http_error_ex _http_error_ex +extern void _http_error_ex(long type TSRMLS_DC, long code, const char *format, ...); + + +#ifdef ZEND_ENGINE_2 +#define http_exception_wrap(o, n, ce) _http_exception_wrap((o), (n), (ce) TSRMLS_CC) +extern zval *_http_exception_wrap(zval *old_exception, zval *new_exception, zend_class_entry *ce TSRMLS_DC); + +#define http_try \ +{ \ + zval *old_exception = EG(exception); \ + EG(exception) = NULL; +#define http_catch(ex_ce) \ + if (EG(exception) && old_exception) { \ + EG(exception) = http_exception_wrap(old_exception, EG(exception), ex_ce); \ + } \ +} +#define http_final(ex_ce) \ + if (EG(exception)) { \ + EG(exception) = http_exception_wrap(EG(exception), NULL, ex_ce); \ + } + +typedef zend_object_value (*http_object_new_t)(zend_class_entry *ce, void *, void ** TSRMLS_DC); + +#define http_object_new(ov, cn, cl, co, ce, i, pp) _http_object_new((ov), (cn), (cl), (http_object_new_t) (co), (ce), (i), (void *) (pp) TSRMLS_CC) +extern STATUS _http_object_new(zend_object_value *ov, const char *cname_str, uint cname_len, http_object_new_t create, zend_class_entry *parent_ce, void *intern_ptr, void **obj_ptr TSRMLS_DC); +#endif /* ZEND_ENGINE_2 */ + + +#define HTTP_CHECK_CURL_INIT(ch, init, action) \ + if ((!(ch)) && (!((ch) = init))) { \ + http_error(HE_WARNING, HTTP_E_REQUEST, "Could not initialize curl"); \ + action; \ + } +#define HTTP_CHECK_CONTENT_TYPE(ct, action) \ + if (!strchr((ct), '/')) { \ + http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, \ + "Content type \"%s\" does not seem to contain a primary and a secondary part", (ct)); \ + action; \ + } +#define HTTP_CHECK_MESSAGE_TYPE_RESPONSE(msg, action) \ + if (!HTTP_MSG_TYPE(RESPONSE, (msg))) { \ + http_error(HE_NOTICE, HTTP_E_MESSAGE_TYPE, "HttpMessage is not of type HTTP_MSG_RESPONSE"); \ + action; \ + } +#define HTTP_CHECK_MESSAGE_TYPE_REQUEST(msg, action) \ + if (!HTTP_MSG_TYPE(REQUEST, (msg))) { \ + http_error(HE_NOTICE, HTTP_E_MESSAGE_TYPE, "HttpMessage is not of type HTTP_MSG_REQUEST"); \ + action; \ + } +#define HTTP_CHECK_GZIP_LEVEL(level, action) \ + if (level < -1 || level > 9) { \ + http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid compression level (-1 to 9): %d", level); \ + action; \ + } + +#define HTTP_CHECK_HEADERS_SENT(action) \ + if (SG(headers_sent) && !SG(request_info).no_headers) { \ + char *output_start_filename = php_get_output_start_filename(TSRMLS_C); \ + int output_start_lineno = php_get_output_start_lineno(TSRMLS_C); \ + \ + if (output_start_filename) { \ + http_error_ex(HE_WARNING, HTTP_E_HEADER, "Cannot modify header information - headers already sent by (output started at %s:%d)", \ + output_start_filename, output_start_lineno); \ + } else { \ + http_error(HE_WARNING, HTTP_E_HEADER, "Cannot modify header information - headers already sent"); \ + } \ + action; \ + } + +#define http_log(f, i, m) _http_log_ex((f), (i), (m) TSRMLS_CC) +extern void _http_log_ex(char *file, const char *ident, const char *message TSRMLS_DC); + +#define http_exit(s, h) http_exit_ex((s), (h), NULL, 1) +#define http_exit_ex(s, h, b, e) _http_exit_ex((s), (h), (b), (e) TSRMLS_CC) +extern STATUS _http_exit_ex(int status, char *header, char *body, zend_bool send_header TSRMLS_DC); + +#define http_check_method(m) http_check_method_ex((m), HTTP_KNOWN_METHODS) +#define http_check_method_ex(m, a) _http_check_method_ex((m), (a)) +extern STATUS _http_check_method_ex(const char *method, const char *methods); + +#define http_got_server_var(v) (NULL != http_get_server_var_ex((v), strlen(v), 1)) +#define http_get_server_var(v, c) http_get_server_var_ex((v), strlen(v), (c)) +#define http_get_server_var_ex(v, l, c) _http_get_server_var_ex((v), (l), (c) TSRMLS_CC) +PHP_HTTP_API zval *_http_get_server_var_ex(const char *key, size_t key_len, zend_bool check TSRMLS_DC); + +#define http_get_request_body(b, l) _http_get_request_body_ex((b), (l), 1 TSRMLS_CC) +#define http_get_request_body_ex(b, l, d) _http_get_request_body_ex((b), (l), (d) TSRMLS_CC) +PHP_HTTP_API STATUS _http_get_request_body_ex(char **body, size_t *length, zend_bool dup TSRMLS_DC); + +#define http_get_request_body_stream() _http_get_request_body_stream(TSRMLS_C) +PHP_HTTP_API php_stream *_http_get_request_body_stream(TSRMLS_D); + + +typedef void (*http_parse_params_callback)(void *cb_arg, const char *key, int keylen, const char *val, int vallen TSRMLS_DC); + +#define http_parse_params_default_callback _http_parse_params_default_callback +PHP_HTTP_API void _http_parse_params_default_callback(void *ht, const char *key, int keylen, const char *val, int vallen TSRMLS_DC); + +#define http_parse_params(s, f, ht) _http_parse_params_ex((s), (f), _http_parse_params_default_callback, (ht) TSRMLS_CC) +#define http_parse_params_ex(s, f, cb, a) _http_parse_params_ex((s), (f), (cb), (a) TSRMLS_CC) +PHP_HTTP_API STATUS _http_parse_params_ex(const char *params, int flags, http_parse_params_callback cb, void *cb_arg TSRMLS_DC); + + +#define http_sleep(s) _http_sleep(s) +static inline void _http_sleep(double s) +{ +#define HTTP_DIFFSEC (0.001) +#define HTTP_MLLISEC (1000) +#define HTTP_MCROSEC (1000 * 1000) +#define HTTP_NANOSEC (1000 * 1000 * 1000) +#define HTTP_MSEC(s) ((long)(s * HTTP_MLLISEC)) +#define HTTP_USEC(s) ((long)(s * HTTP_MCROSEC)) +#define HTTP_NSEC(s) ((long)(s * HTTP_NANOSEC)) + +#if defined(PHP_WIN32) + Sleep((DWORD) HTTP_MSEC(s)); +#elif defined(HAVE_USLEEP) + usleep(HTTP_USEC(s)); +#elif defined(HAVE_NANOSLEEP) + struct timespec req, rem; + + req.tv_sec = (time_t) s; + req.tv_nsec = HTTP_NSEC(s) % HTTP_NANOSEC; + + while (nanosleep(&req, &rem) && (errno == EINTR) && (HTTP_NSEC(rem.tv_sec) + rem.tv_nsec) > HTTP_NSEC(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 = HTTP_USEC(s) % HTTP_MCROSEC; + + select(0, NULL, NULL, NULL, &timeout); +#endif +} + +#define http_locate_str _http_locate_str +static inline const char *_http_locate_str(const char *h, size_t h_len, const char *n, size_t n_len) +{ + const char *p, *e; + + if (n_len && h_len) { + e = h + h_len; + do { + if (*h == *n) { + for (p = n; *p == h[p-n]; ++p) { + if (p == n+n_len-1) { + return h; + } + } + } + } while (h++ != e); + } + + return NULL; +} + +#define http_locate_body _http_locate_body +static inline const char *_http_locate_body(const char *message) +{ + const char *body = NULL, *msg = message; + + while (*msg) { + if (*msg == '\n') { + if (*(msg+1) == '\n') { + body = msg + 2; + break; + } else if (*(msg+1) == '\r' && *(msg+2) == '\n') { + body = msg + 3; + break; + } + } + ++msg; + } + return body; +} + +#define http_locate_eol _http_locate_eol +static inline const char *_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; +} + +#define http_zset(t, z) _http_zset((t), (z)) +static inline zval *_http_zset(int type, zval *z) +{ + if (Z_TYPE_P(z) != type) { + switch (type) { + case IS_NULL: convert_to_null(z); break; + case IS_BOOL: convert_to_boolean(z); break; + case IS_LONG: convert_to_long(z); break; + case IS_DOUBLE: convert_to_double(z); break; + case IS_STRING: convert_to_string(z); break; + case IS_ARRAY: convert_to_array(z); break; + case IS_OBJECT: convert_to_object(z); break; + } + } + return z; +} +#define http_zsep(t, z) _http_zsep_ex((t), (z), NULL) +#define http_zsep_ex(t, z, p) _http_zsep_ex((t), (z), (p)) +static inline zval *_http_zsep_ex(int type, zval *z, zval **p) { + SEPARATE_ARG_IF_REF(z); + if (Z_TYPE_P(z) != type) { + 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; + } + } + if (p) { + *p = z; + } + return z; +} + +typedef struct _HashKey { + char *str; + uint len; + ulong num; + uint dup:1; + uint type:31; +} HashKey; +#define initHashKey(dup) {NULL, 0, 0, (dup), 0} + +#define FOREACH_VAL(pos, array, val) FOREACH_HASH_VAL(pos, Z_ARRVAL_P(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, Z_ARRVAL_P(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, Z_ARRVAL_P(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_JOIN_STRONLY 1 +#define ARRAY_JOIN_PRETTIFY 2 +#define array_join(src, dst, append, flags) zend_hash_apply_with_arguments(src HTTP_ZAPI_HASH_TSRMLS_CC, (append)?apply_array_append_func:apply_array_merge_func, 2, dst, (int)flags) + +extern int apply_array_append_func(void *pDest HTTP_ZAPI_HASH_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key); +extern int apply_array_merge_func(void *pDest HTTP_ZAPI_HASH_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key); + +#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 + */ + --- /dev/null +++ b/ext/http/php_http_cache_api.h @@ -0,0 +1,162 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_cache_api.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_CACHE_API_H +#define PHP_HTTP_CACHE_API_H + +#include "php_http_send_api.h" + +#include "ext/standard/crc32.h" +#include "ext/standard/sha1.h" +#include "ext/standard/md5.h" + +#ifdef HTTP_HAVE_HASH +# include "php_hash.h" +#endif + +#define http_etag_digest(d, l) _http_etag_digest((d), (l)) +static inline char *_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; +} + +#define http_etag_init() _http_etag_init(TSRMLS_C) +static inline void *_http_etag_init(TSRMLS_D) +{ + void *ctx = NULL; + char *mode = HTTP_G->etag.mode; + +#ifdef 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 + if (mode && ((!strcasecmp(mode, "crc32")) || (!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 { + PHP_MD5Init(ctx = emalloc(sizeof(PHP_MD5_CTX))); + } + + return ctx; +} + +#define http_etag_finish(c) _http_etag_finish((c) TSRMLS_CC) +static inline char *_http_etag_finish(void *ctx TSRMLS_DC) +{ + unsigned char digest[128] = {0}; + char *etag = NULL, *mode = HTTP_G->etag.mode; + +#ifdef HTTP_HAVE_HASH + const php_hash_ops *eho = NULL; + + if (mode && (eho = php_hash_fetch_ops(mode, strlen(mode)))) { + eho->hash_final(digest, ctx); + etag = http_etag_digest(digest, eho->digest_size); + } else +#endif + if (mode && ((!strcasecmp(mode, "crc32")) || (!strcasecmp(mode, "crc32b")))) { + *((uint *) ctx) = ~*((uint *) ctx); + etag = http_etag_digest((const unsigned char *) ctx, sizeof(uint)); + } else if (mode && (!strcasecmp(mode, "sha1"))) { + PHP_SHA1Final(digest, ctx); + etag = http_etag_digest(digest, 20); + } else { + PHP_MD5Final(digest, ctx); + etag = http_etag_digest(digest, 16); + } + efree(ctx); + + return etag; +} + +#define http_etag_update(c, d, l) _http_etag_update((c), (d), (l) TSRMLS_CC) +static inline void _http_etag_update(void *ctx, const char *data_ptr, size_t data_len TSRMLS_DC) +{ + char *mode = HTTP_G->etag.mode; +#ifdef HTTP_HAVE_HASH + const php_hash_ops *eho = NULL; + + if (mode && (eho = php_hash_fetch_ops(mode, strlen(mode)))) { + eho->hash_update(ctx, (const unsigned char *) data_ptr, data_len); + } else +#endif + if (mode && ((!strcasecmp(mode, "crc32")) || (!strcasecmp(mode, "crc32b")))) { + uint i, c = *((uint *) ctx); + for (i = 0; i < data_len; ++i) { + CRC32(c, data_ptr[i]); + } + *((uint *)ctx) = c; + } else if (mode && (!strcasecmp(mode, "sha1"))) { + PHP_SHA1Update(ctx, (const unsigned char *) data_ptr, data_len); + } else { + PHP_MD5Update(ctx, (const unsigned char *) data_ptr, data_len); + } +} + +#define http_ob_etaghandler(o, l, ho, hl, m) _http_ob_etaghandler((o), (l), (ho), (hl), (m) TSRMLS_CC) +extern void _http_ob_etaghandler(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC); + +#define http_etag(p, l, m) _http_etag((p), (l), (m) TSRMLS_CC) +PHP_HTTP_API char *_http_etag(const void *data_ptr, size_t data_len, http_send_mode data_mode TSRMLS_DC); + +#define http_last_modified(p, m) _http_last_modified((p), (m) TSRMLS_CC) +PHP_HTTP_API time_t _http_last_modified(const void *data_ptr, http_send_mode data_mode TSRMLS_DC); + +#define http_match_last_modified(entry, modified) _http_match_last_modified_ex((entry), (modified), 1 TSRMLS_CC) +#define http_match_last_modified_ex(entry, modified, ep) _http_match_last_modified_ex((entry), (modified), (ep) TSRMLS_CC) +PHP_HTTP_API zend_bool _http_match_last_modified_ex(const char *entry, time_t t, zend_bool enforce_presence TSRMLS_DC); + +#define http_match_etag(entry, etag) _http_match_etag_ex((entry), (etag), 1 TSRMLS_CC) +#define http_match_etag_ex(entry, etag, ep) _http_match_etag_ex((entry), (etag), (ep) TSRMLS_CC) +PHP_HTTP_API zend_bool _http_match_etag_ex(const char *entry, const char *etag, zend_bool enforce_presence TSRMLS_DC); + +#define http_cache_last_modified(l, s, cc, ccl) _http_cache_last_modified((l), (s), (cc), (ccl) TSRMLS_CC) +PHP_HTTP_API STATUS _http_cache_last_modified(time_t last_modified, time_t send_modified, const char *cache_control, size_t cc_len TSRMLS_DC); + +#define http_cache_etag(e, el, cc, ccl) _http_cache_etag((e), (el), (cc), (ccl) TSRMLS_CC) +PHP_HTTP_API STATUS _http_cache_etag(const char *etag, size_t etag_len, const char *cache_control, size_t cc_len TSRMLS_DC); + +#define http_start_ob_etaghandler() _http_start_ob_etaghandler(TSRMLS_C) +PHP_HTTP_API STATUS _http_start_ob_etaghandler(TSRMLS_D); +#define http_interrupt_ob_etaghandler() _http_interrupt_ob_etaghandler(TSRMLS_C) +PHP_HTTP_API zend_bool _http_interrupt_ob_etaghandler(TSRMLS_D); + +#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 + */ + --- /dev/null +++ b/ext/http/php_http_cookie_api.h @@ -0,0 +1,86 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_cookie_api.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_COOKIE_API_H +#define PHP_HTTP_COOKIE_API_H + +#define HTTP_COOKIE_SECURE 0x10L +#define HTTP_COOKIE_HTTPONLY 0x20L + +#define HTTP_COOKIE_PARSE_RAW 0x01L + +extern PHP_MINIT_FUNCTION(http_cookie); + +/* + 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 _http_cookie_list_t { + HashTable cookies; + HashTable extras; + long flags; + char *path; + char *domain; + time_t expires; +} http_cookie_list; + +#define http_cookie_list_new() _http_cookie_list_init(NULL ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) +#define http_cookie_list_init(l) _http_cookie_list_init((l) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) +PHP_HTTP_API http_cookie_list *_http_cookie_list_init(http_cookie_list *list ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC); + +#define http_cookie_list_dtor(l) _http_cookie_list_dtor((l) TSRMLS_CC) +PHP_HTTP_API void _http_cookie_list_dtor(http_cookie_list *list TSRMLS_DC); + +#define http_cookie_list_free(l) _http_cookie_list_free((l) TSRMLS_CC) +PHP_HTTP_API void _http_cookie_list_free(http_cookie_list **list TSRMLS_DC); + +#define http_cookie_list_has_cookie(list, name, name_len) zend_hash_exists(&(list)->cookies, (name), (name_len)+1) +#define http_cookie_list_has_extra(list, name, name_len) zend_hash_exists(&(list)->extras, (name), (name_len)+1) + +#define http_cookie_list_add_cookie(l, n, nl, v, vl) _http_cookie_list_add_cookie((l), (n), (nl), (v), (vl) TSRMLS_CC) +PHP_HTTP_API void _http_cookie_list_add_cookie(http_cookie_list *list, const char *name, size_t name_len, const char *value, size_t value_len TSRMLS_DC); + +#define http_cookie_list_add_extra(l, n , nl, v, vl) _http_cookie_list_add_extra((l), (n), (nl), (v), (vl) TSRMLS_CC) +PHP_HTTP_API void _http_cookie_list_add_extra(http_cookie_list *list, const char *name, size_t name_len, const char *value, size_t value_len TSRMLS_DC); + +#define http_cookie_list_get_cookie(l, n, nl) _http_cookie_list_get_cookie((l), (n), (nl) TSRMLS_CC) +PHP_HTTP_API const char *_http_cookie_list_get_cookie(http_cookie_list *list, const char *name, size_t name_len TSRMLS_DC); + +#define http_cookie_list_get_extra(l, n, nl) _http_cookie_list_get_extra((l), (n), (nl) TSRMLS_CC) +PHP_HTTP_API const char *_http_cookie_list_get_extra(http_cookie_list *list, const char *name, size_t name_len TSRMLS_DC); + +#define http_parse_cookie(s) _http_parse_cookie_ex(NULL, (s), 0, NULL TSRMLS_CC) +#define http_parse_cookie_ex(l, s, f, a) _http_parse_cookie_ex((l), (s), (f), (a) TSRMLS_CC) +PHP_HTTP_API http_cookie_list *_http_parse_cookie_ex(http_cookie_list * list, const char *string, long flags, char **allowed_extras TSRMLS_DC); + +#define http_cookie_list_tostruct(l, s) _http_cookie_list_tostruct((l), (s) TSRMLS_CC) +PHP_HTTP_API void _http_cookie_list_tostruct(http_cookie_list *list, zval *strct TSRMLS_DC); + +#define http_cookie_list_fromstruct(l, s) _http_cookie_list_fromstruct((l), (s) TSRMLS_CC) +PHP_HTTP_API http_cookie_list *_http_cookie_list_fromstruct(http_cookie_list *list, zval *strct TSRMLS_DC); + +#define http_cookie_list_tostring(l, str, len) _http_cookie_list_tostring((l), (str), (len) TSRMLS_CC) +PHP_HTTP_API void _http_cookie_list_tostring(http_cookie_list *list, char **str, size_t *len 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 + */ --- /dev/null +++ b/ext/http/php_http_date_api.h @@ -0,0 +1,35 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_date_api.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_DATE_API_H +#define PHP_HTTP_DATE_API_H + +#define http_date(t) _http_date((t) TSRMLS_CC) +PHP_HTTP_API char *_http_date(time_t t TSRMLS_DC); + +#define http_parse_date(d) _http_parse_date_ex((d), 0 TSRMLS_CC) +#define http_parse_date_ex(d, s) _http_parse_date_ex((d), (s) TSRMLS_CC) +PHP_HTTP_API time_t _http_parse_date_ex(const char *date, zend_bool silent 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 + */ + --- /dev/null +++ b/ext/http/php_http_deflatestream_object.h @@ -0,0 +1,57 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_deflatestream_object.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_DEFLATESTREAM_OBJECT_H +#define PHP_HTTP_DEFLATESTREAM_OBJECT_H +#ifdef HTTP_HAVE_ZLIB +#ifdef ZEND_ENGINE_2 + +typedef struct _http_deflatestream_object_t { + zend_object zo; + http_encoding_stream *stream; +} http_deflatestream_object; + +extern zend_class_entry *http_deflatestream_object_ce; +extern zend_function_entry http_deflatestream_object_fe[]; + +extern PHP_MINIT_FUNCTION(http_deflatestream_object); + +#define http_deflatestream_object_new(ce) _http_deflatestream_object_new((ce) TSRMLS_CC) +extern zend_object_value _http_deflatestream_object_new(zend_class_entry *ce TSRMLS_DC); +#define http_deflatestream_object_new_ex(ce, s, ptr) _http_deflatestream_object_new_ex((ce), (s), (ptr) TSRMLS_CC) +extern zend_object_value _http_deflatestream_object_new_ex(zend_class_entry *ce, http_encoding_stream *s, http_deflatestream_object **ptr TSRMLS_DC); +#define http_deflatestream_object_clone(zobj) _http_deflatestream_object_clone_obj(zobj TSRMLS_CC) +extern zend_object_value _http_deflatestream_object_clone_obj(zval *object TSRMLS_DC); +#define http_deflatestream_object_free(o) _http_deflatestream_object_free((o) TSRMLS_CC) +extern void _http_deflatestream_object_free(zend_object *object TSRMLS_DC); + +PHP_METHOD(HttpDeflateStream, __construct); +PHP_METHOD(HttpDeflateStream, factory); +PHP_METHOD(HttpDeflateStream, update); +PHP_METHOD(HttpDeflateStream, flush); +PHP_METHOD(HttpDeflateStream, finish); + +#endif +#endif +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/php_http_encoding_api.h @@ -0,0 +1,192 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_encoding_api.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_ENCODING_API_H +#define PHP_HTTP_ENCODING_API_H + +#define http_encoding_dechunk(e, el, d, dl) _http_encoding_dechunk((e), (el), (d), (dl) TSRMLS_CC) +PHP_HTTP_API const char *_http_encoding_dechunk(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC); + +#define http_encoding_response_start(cl, i) _http_encoding_response_start((cl), (i) TSRMLS_CC) +PHP_HTTP_API int _http_encoding_response_start(size_t content_length, zend_bool ignore_http_ohandler TSRMLS_DC); + +#ifdef HTTP_HAVE_ZLIB + +extern PHP_MINIT_FUNCTION(http_encoding); +extern PHP_RINIT_FUNCTION(http_encoding); +extern PHP_RSHUTDOWN_FUNCTION(http_encoding); + +typedef enum _http_encoding_type_t { + HTTP_ENCODING_NONE, + HTTP_ENCODING_GZIP, + HTTP_ENCODING_DEFLATE, +} http_encoding_type; + +#define HTTP_INFLATE_ROUNDS 100 + +#define HTTP_DEFLATE_BUFFER_SIZE_GUESS(S) \ + (((size_t) ((double) S * (double) 1.015)) + 10 + 8 + 4 + 1) +#define HTTP_INFLATE_BUFFER_SIZE_GUESS(S) \ + (((S) + 1) << 3) +#define HTTP_INFLATE_BUFFER_SIZE_ALIGN(S) \ + ((S) += (S) >> (3)) + +#define HTTP_DEFLATE_BUFFER_SIZE 0x8000 +#define HTTP_INFLATE_BUFFER_SIZE 0x1000 + +#define HTTP_DEFLATE_LEVEL_DEF 0x00000000 +#define HTTP_DEFLATE_LEVEL_MIN 0x00000001 +#define HTTP_DEFLATE_LEVEL_MAX 0x00000009 +#define HTTP_DEFLATE_TYPE_ZLIB 0x00000000 +#define HTTP_DEFLATE_TYPE_GZIP 0x00000010 +#define HTTP_DEFLATE_TYPE_RAW 0x00000020 +#define HTTP_DEFLATE_STRATEGY_DEF 0x00000000 +#define HTTP_DEFLATE_STRATEGY_FILT 0x00000100 +#define HTTP_DEFLATE_STRATEGY_HUFF 0x00000200 +#define HTTP_DEFLATE_STRATEGY_RLE 0x00000300 +#define HTTP_DEFLATE_STRATEGY_FIXED 0x00000400 + +#define HTTP_DEFLATE_LEVEL_SET(flags, level) \ + switch (flags & 0xf) \ + { \ + default: \ + if ((flags & 0xf) < 10) { \ + level = flags & 0xf; \ + break; \ + } \ + case HTTP_DEFLATE_LEVEL_DEF: \ + level = Z_DEFAULT_COMPRESSION; \ + break; \ + } + +#define HTTP_DEFLATE_WBITS_SET(flags, wbits) \ + switch (flags & 0xf0) \ + { \ + case HTTP_DEFLATE_TYPE_GZIP: \ + wbits = HTTP_WINDOW_BITS_GZIP; \ + break; \ + case HTTP_DEFLATE_TYPE_RAW: \ + wbits = HTTP_WINDOW_BITS_RAW; \ + break; \ + default: \ + wbits = HTTP_WINDOW_BITS_ZLIB; \ + break; \ + } + +#define HTTP_INFLATE_WBITS_SET(flags, wbits) \ + if (flags & HTTP_INFLATE_TYPE_RAW) { \ + wbits = HTTP_WINDOW_BITS_RAW; \ +} else { \ + wbits = HTTP_WINDOW_BITS_ANY; \ +} + +#define HTTP_DEFLATE_STRATEGY_SET(flags, strategy) \ + switch (flags & 0xf00) \ + { \ + case HTTP_DEFLATE_STRATEGY_FILT: \ + strategy = Z_FILTERED; \ + break; \ + case HTTP_DEFLATE_STRATEGY_HUFF: \ + strategy = Z_HUFFMAN_ONLY; \ + break; \ + case HTTP_DEFLATE_STRATEGY_RLE: \ + strategy = Z_RLE; \ + break; \ + case HTTP_DEFLATE_STRATEGY_FIXED: \ + strategy = Z_FIXED; \ + break; \ + default: \ + strategy = Z_DEFAULT_STRATEGY; \ + break; \ + } + +#define HTTP_WINDOW_BITS_ZLIB 0x0000000f +#define HTTP_WINDOW_BITS_GZIP 0x0000001f +#define HTTP_WINDOW_BITS_ANY 0x0000002f +#define HTTP_WINDOW_BITS_RAW -0x000000f + +#ifndef Z_FIXED +/* Z_FIXED does not exist prior 1.2.2.2 */ +# define Z_FIXED 0 +#endif + +#define HTTP_INFLATE_TYPE_ZLIB 0x00000000 +#define HTTP_INFLATE_TYPE_GZIP 0x00000000 +#define HTTP_INFLATE_TYPE_RAW 0x00000001 + +#define HTTP_ENCODING_STREAM_FLUSH_NONE 0x00000000 +#define HTTP_ENCODING_STREAM_FLUSH_SYNC 0x00100000 +#define HTTP_ENCODING_STREAM_FLUSH_FULL 0x00200000 + +#define HTTP_ENCODING_STREAM_FLUSH_FLAG(f) \ + (((f) & HTTP_ENCODING_STREAM_FLUSH_FULL) ? Z_FULL_FLUSH : \ + (((f) & HTTP_ENCODING_STREAM_FLUSH_SYNC) ? Z_SYNC_FLUSH : Z_NO_FLUSH)) + +#define HTTP_ENCODING_STREAM_PERSISTENT 0x01000000 + +typedef struct _http_encoding_stream_t { + z_stream stream; + int flags; + void *storage; +} http_encoding_stream; + +#define http_encoding_deflate(f, d, dl, r, rl) _http_encoding_deflate((f), (d), (dl), (r), (rl) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) +PHP_HTTP_API STATUS _http_encoding_deflate(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC); +#define http_encoding_inflate(d, dl, r, rl) _http_encoding_inflate((d), (dl), (r), (rl) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) +PHP_HTTP_API STATUS _http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC); + +#define http_encoding_deflate_stream_init(s, f) _http_encoding_deflate_stream_init((s), (f) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) +PHP_HTTP_API http_encoding_stream *_http_encoding_deflate_stream_init(http_encoding_stream *s, int flags ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC); +#define http_encoding_deflate_stream_update(s, d, dl, e, el) _http_encoding_deflate_stream_update((s), (d), (dl), (e), (el) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) +PHP_HTTP_API STATUS _http_encoding_deflate_stream_update(http_encoding_stream *s, const char *data, size_t data_len, char **encoded, size_t *encoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC); +#define http_encoding_deflate_stream_flush(s, e, el) _http_encoding_deflate_stream_flush((s), (e), (el) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) +PHP_HTTP_API STATUS _http_encoding_deflate_stream_flush(http_encoding_stream *s, char **encoded, size_t *encoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC); +#define http_encoding_deflate_stream_finish(s, e, el) _http_encoding_deflate_stream_finish((s), (e), (el) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) +PHP_HTTP_API STATUS _http_encoding_deflate_stream_finish(http_encoding_stream *s, char **encoded, size_t *encoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC); +#define http_encoding_deflate_stream_dtor(s) _http_encoding_deflate_stream_dtor((s) TSRMLS_CC) +PHP_HTTP_API void _http_encoding_deflate_stream_dtor(http_encoding_stream *s TSRMLS_DC); +#define http_encoding_deflate_stream_free(s) _http_encoding_deflate_stream_free((s) TSRMLS_CC) +PHP_HTTP_API void _http_encoding_deflate_stream_free(http_encoding_stream **s TSRMLS_DC); + +#define http_encoding_inflate_stream_init(s, f) _http_encoding_inflate_stream_init((s), (f) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) +PHP_HTTP_API http_encoding_stream *_http_encoding_inflate_stream_init(http_encoding_stream *s, int flags ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC); +#define http_encoding_inflate_stream_update(s, d, dl, e, el) _http_encoding_inflate_stream_update((s), (d), (dl), (e), (el) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) +PHP_HTTP_API STATUS _http_encoding_inflate_stream_update(http_encoding_stream *s, const char *data, size_t data_len, char **decoded, size_t *decoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC); +#define http_encoding_inflate_stream_flush(s, d, dl) _http_encoding_inflate_stream_flush((s), (d), (dl) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) +PHP_HTTP_API STATUS _http_encoding_inflate_stream_flush(http_encoding_stream *s, char **decoded, size_t *decoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC); +#define http_encoding_inflate_stream_finish(s, e, el) _http_encoding_inflate_stream_finish((s), (e), (el) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) +PHP_HTTP_API STATUS _http_encoding_inflate_stream_finish(http_encoding_stream *s, char **decoded, size_t *decoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC); +#define http_encoding_inflate_stream_dtor(s) _http_encoding_inflate_stream_dtor((s) TSRMLS_CC) +PHP_HTTP_API void _http_encoding_inflate_stream_dtor(http_encoding_stream *s TSRMLS_DC); +#define http_encoding_inflate_stream_free(s) _http_encoding_inflate_stream_free((s) TSRMLS_CC) +PHP_HTTP_API void _http_encoding_inflate_stream_free(http_encoding_stream **s TSRMLS_DC); + +#define http_ob_deflatehandler(o, ol, h, hl, m) _http_ob_deflatehandler((o), (ol), (h), (hl), (m) TSRMLS_CC) +extern void _http_ob_deflatehandler(char *, uint, char **, uint *, int TSRMLS_DC); + +#define http_ob_inflatehandler(o, ol, h, hl, m) _http_ob_inflatehandler((o), (ol), (h), (hl), (m) TSRMLS_CC) +extern void _http_ob_inflatehandler(char *, uint, char **, uint *, int TSRMLS_DC); + +#endif /* HTTP_HAVE_ZLIB */ + +#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 + */ --- /dev/null +++ b/ext/http/php_http_exception_object.h @@ -0,0 +1,60 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_exception_object.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_EXCEPTION_OBJECT_H +#define PHP_HTTP_EXCEPTION_OBJECT_H +#ifdef ZEND_ENGINE_2 + +#include "zend_exceptions.h" + +PHP_MINIT_FUNCTION(http_exception_object); + +#define HTTP_EX_DEF_CE http_exception_object_ce +#define HTTP_EX_CE(name) http_ ##name## _exception_object_ce + +extern zend_class_entry *http_exception_object_ce; +extern zend_class_entry *HTTP_EX_CE(runtime); +extern zend_class_entry *HTTP_EX_CE(header); +extern zend_class_entry *HTTP_EX_CE(malformed_headers); +extern zend_class_entry *HTTP_EX_CE(request_method); +extern zend_class_entry *HTTP_EX_CE(message_type); +extern zend_class_entry *HTTP_EX_CE(invalid_param); +extern zend_class_entry *HTTP_EX_CE(encoding); +extern zend_class_entry *HTTP_EX_CE(request); +extern zend_class_entry *HTTP_EX_CE(request_pool); +extern zend_class_entry *HTTP_EX_CE(socket); +extern zend_class_entry *HTTP_EX_CE(response); +extern zend_class_entry *HTTP_EX_CE(url); +extern zend_function_entry http_exception_object_fe[]; + +#define http_exception_get_default _http_exception_get_default +extern zend_class_entry *_http_exception_get_default(); + +#define http_exception_get_for_code(c) _http_exception_get_for_code(c) +extern zend_class_entry *_http_exception_get_for_code(long code); + +PHP_METHOD(HttpException, __toString); + +#endif +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/php_http_filter_api.h @@ -0,0 +1,33 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_filter_api.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_FILTER_API_H +#define PHP_HTTP_FILTER_API_H +#ifdef ZEND_ENGINE_2 + +extern php_stream_filter_factory http_filter_factory; +PHP_MINIT_FUNCTION(http_filter); + +#endif +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/php_http_headers_api.h @@ -0,0 +1,77 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_headers_api.h 300300 2010-06-09 07:29:35Z mike $ */ + +#ifndef PHP_HTTP_HEADERS_API_H +#define PHP_HTTP_HEADERS_API_H + +#include "php_http_info_api.h" + +typedef enum http_range_status_t { + RANGE_OK, + RANGE_NO, + RANGE_ERR +} http_range_status; + +#define http_parse_headers(h, a) _http_parse_headers_ex((h), Z_ARRVAL_P(a), 1, http_info_default_callback, NULL TSRMLS_CC) +#define http_parse_headers_ex(h, ht, p) _http_parse_headers_ex((h), (ht), (p), http_info_default_callback, NULL TSRMLS_CC) +#define http_parse_headers_cb(h, ht, p, f, d) _http_parse_headers_ex((h), (ht), (p), (f), (d) TSRMLS_CC) +PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, HashTable *headers, zend_bool prettify, http_info_callback callback_func, void **callback_data TSRMLS_DC); + +typedef char *(*negotiate_func_t)(const char *test, double *quality, HashTable *supported TSRMLS_DC); + +#define http_negotiate_language_func _http_negotiate_language_func +extern char *_http_negotiate_language_func(const char *test, double *quality, HashTable *supported TSRMLS_DC); +#define http_negotiate_content_type_func _http_negotiate_default_func +#define http_negotiate_encoding_func _http_negotiate_default_func +#define http_negotiate_charset_func _http_negotiate_default_func +#define http_negotiate_default_func _http_negotiate_default_func +extern char *_http_negotiate_default_func(const char *test, double *quality, HashTable *supported TSRMLS_DC); + +#define http_negotiate_language(zsupported) http_negotiate_language_ex(Z_ARRVAL_P(zsupported)) +#define http_negotiate_language_ex(supported) http_negotiate_q("HTTP_ACCEPT_LANGUAGE", (supported), http_negotiate_language_func) +#define http_negotiate_charset(zsupported) http_negotiate_charset_ex(Z_ARRVAL_P(zsupported)) +#define http_negotiate_charset_ex(supported) http_negotiate_q("HTTP_ACCEPT_CHARSET", (supported), http_negotiate_charset_func) +#define http_negotiate_encoding(zsupported) http_negotiate_encoding_ex(Z_ARRVAL_P(zsupported)) +#define http_negotiate_encoding_ex(supported) http_negotiate_q("HTTP_ACCEPT_ENCODING", (supported), http_negotiate_encoding_func) +#define http_negotiate_content_type(zsupported) http_negotiate_content_type_ex(Z_ARRVAL_P(zsupported)) +#define http_negotiate_content_type_ex(supported) http_negotiate_q("HTTP_ACCEPT", (supported), http_negotiate_content_type_func) +#define http_negotiate_q(e, s, n) _http_negotiate_q((e), (s), (n) TSRMLS_CC) +PHP_HTTP_API HashTable *_http_negotiate_q(const char *header, HashTable *supported, negotiate_func_t neg TSRMLS_DC); +#define http_negotiate_z(z, s, n) _http_negotiate_z((z), (s), (n) TSRMLS_CC) +PHP_HTTP_API HashTable *_http_negotiate_z(zval *value, HashTable *supported, negotiate_func_t neg TSRMLS_DC); + +#define http_get_request_ranges(r, l) _http_get_request_ranges((r), (l) TSRMLS_CC) +PHP_HTTP_API http_range_status _http_get_request_ranges(HashTable *ranges, size_t length TSRMLS_DC); + +#define http_get_request_headers(h) _http_get_request_headers((h) TSRMLS_CC) +PHP_HTTP_API void _http_get_request_headers(HashTable *headers TSRMLS_DC); + +#define http_get_response_headers(h) _http_get_response_headers((h) TSRMLS_CC) +PHP_HTTP_API STATUS _http_get_response_headers(HashTable *headers_ht TSRMLS_DC); + +#define http_match_request_header(h, v) http_match_request_header_ex((h), (v), 0) +#define http_match_request_header_ex(h, v, c) _http_match_request_header_ex((h), (v), (c) TSRMLS_CC) +PHP_HTTP_API zend_bool _http_match_request_header_ex(const char *header, const char *value, zend_bool match_case 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 + */ + --- /dev/null +++ b/ext/http/php_http_inflatestream_object.h @@ -0,0 +1,57 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_inflatestream_object.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_INFLATESTREAM_OBJECT_H +#define PHP_HTTP_INFLATESTREAM_OBJECT_H +#ifdef HTTP_HAVE_ZLIB +#ifdef ZEND_ENGINE_2 + +typedef struct _http_inflatestream_object_t { + zend_object zo; + http_encoding_stream *stream; +} http_inflatestream_object; + +extern zend_class_entry *http_inflatestream_object_ce; +extern zend_function_entry http_inflatestream_object_fe[]; + +extern PHP_MINIT_FUNCTION(http_inflatestream_object); + +#define http_inflatestream_object_new(ce) _http_inflatestream_object_new((ce) TSRMLS_CC) +extern zend_object_value _http_inflatestream_object_new(zend_class_entry *ce TSRMLS_DC); +#define http_inflatestream_object_new_ex(ce, s, ptr) _http_inflatestream_object_new_ex((ce), (s), (ptr) TSRMLS_CC) +extern zend_object_value _http_inflatestream_object_new_ex(zend_class_entry *ce, http_encoding_stream *s, http_inflatestream_object **ptr TSRMLS_DC); +#define http_inflatestream_object_clone(zobj) _http_inflatestream_object_clone_obj(zobj TSRMLS_CC) +extern zend_object_value _http_inflatestream_object_clone_obj(zval *object TSRMLS_DC); +#define http_inflatestream_object_free(o) _http_inflatestream_object_free((o) TSRMLS_CC) +extern void _http_inflatestream_object_free(zend_object *object TSRMLS_DC); + +PHP_METHOD(HttpInflateStream, __construct); +PHP_METHOD(HttpInflateStream, factory); +PHP_METHOD(HttpInflateStream, update); +PHP_METHOD(HttpInflateStream, flush); +PHP_METHOD(HttpInflateStream, finish); + +#endif +#endif +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/php_http_info_api.h @@ -0,0 +1,79 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_info_api.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_INFO_API_H +#define PHP_HTTP_INFO_API_H + +#define IS_HTTP_REQUEST 1 +#define IS_HTTP_RESPONSE 2 + +#define HTTP_INFO(ptr) (ptr)->http.info + +#define HTTP_INFO_REQUEST_FMT_ARGS(_http_ptr, _EOL) "%s %s HTTP/%1.1f" _EOL, \ + (_http_ptr)->info.request.method?(_http_ptr)->info.request.method:"UNKNOWN", \ + (_http_ptr)->info.request.url?(_http_ptr)->info.request.url:"/", \ + (_http_ptr)->version>0.0?(_http_ptr)->version:1.1 + +#define HTTP_INFO_RESPONSE_FMT_ARGS(_http_ptr, _EOL) "HTTP/%1.1f %d%s%s" _EOL, \ + (_http_ptr)->version>0.0?(_http_ptr)->version:1.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 _http_request_info_t { + char *method; + char *url; +} http_request_info; + +typedef struct _http_response_info_t { + int code; + char *status; +} http_response_info; + +typedef union _http_info_union_t { + http_request_info request; + http_response_info response; +} http_info_union; + +struct http_info { + http_info_union info; + double version; +}; + +typedef struct _http_info_t { + struct http_info http; + int type; +} http_info; + +typedef void (*http_info_callback)(void **callback_data, HashTable **headers, http_info *info TSRMLS_DC); + +#define http_info_default_callback _http_info_default_callback +PHP_HTTP_API void _http_info_default_callback(void **nothing, HashTable **headers, http_info *info TSRMLS_DC); +#define http_info_dtor _http_info_dtor +PHP_HTTP_API void _http_info_dtor(http_info *info); +#define http_info_parse(p, i) _http_info_parse_ex((p), (i), 1 TSRMLS_CC) +#define http_info_parse_ex(p, i, s) _http_info_parse_ex((p), (i), (s) TSRMLS_CC) +PHP_HTTP_API STATUS _http_info_parse_ex(const char *pre_header, http_info *info , zend_bool silent 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 + */ + --- /dev/null +++ b/ext/http/php_http_message_api.h @@ -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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_message_api.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_MESSAGE_API_H +#define PHP_HTTP_MESSAGE_API_H + +#include "php_http_info_api.h" + +typedef enum _http_message_type_t { + HTTP_MSG_NONE = 0, + HTTP_MSG_REQUEST = IS_HTTP_REQUEST, + HTTP_MSG_RESPONSE = IS_HTTP_RESPONSE, +} http_message_type; + +typedef struct _http_message_t http_message; + +struct _http_message_t { + phpstr body; + HashTable hdrs; + http_message_type type; + struct http_info http; + http_message *parent; +}; + +/* required minimum length of an HTTP message "HTTP/1.1" */ +#define HTTP_MSG_MIN_SIZE 8 + +/* shorthand for type checks */ +#define HTTP_MSG_TYPE(TYPE, msg) ((msg) && ((msg)->type == HTTP_MSG_ ##TYPE)) + +#define http_message_new() http_message_init_ex(NULL, 0) +#define http_message_init(m) http_message_init_ex((m), 0) +#define http_message_init_ex(m, t) _http_message_init_ex((m), (t) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define http_message_init_rel(m, t) _http_message_init_ex((m), (t) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC) +PHP_HTTP_API http_message *_http_message_init_ex(http_message *m, http_message_type t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); +#define http_message_init_env(m, t) _http_message_init_env((m), (t) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +PHP_HTTP_API http_message *_http_message_init_env(http_message *m, http_message_type t TSRMLS_DC ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); + +#define http_message_set_type(m, t) _http_message_set_type((m), (t)) +PHP_HTTP_API void _http_message_set_type(http_message *m, http_message_type t); + +#define http_message_set_info(m, i) _http_message_set_info((m), (i)) +PHP_HTTP_API void _http_message_set_info(http_message *message, http_info *info); + +#define http_message_header(m, h) _http_message_header_ex((m), (h), sizeof(h), 1) +#define http_message_header_ex _http_message_header_ex +static inline zval *_http_message_header_ex(http_message *msg, char *key_str, size_t key_len, int join) +{ + zval **header; + if (SUCCESS == zend_hash_find(&msg->hdrs, key_str, key_len, (void *) &header)) { + if (join && Z_TYPE_PP(header) == IS_ARRAY) { + zval *header_str, **val; + HashPosition pos; + phpstr str; + + phpstr_init(&str); + MAKE_STD_ZVAL(header_str); + FOREACH_VAL(pos, *header, val) { + phpstr_appendf(&str, PHPSTR_LEN(&str) ? ", %s":"%s", Z_STRVAL_PP(val)); + } + phpstr_fix(&str); + ZVAL_STRINGL(header_str, PHPSTR_VAL(&str), PHPSTR_LEN(&str), 0); + return header_str; + } else { + ZVAL_ADDREF(*header); + return *header; + } + } + return NULL; +} + +#define http_message_count(c, m) \ +{ \ + http_message *__tmp_msg = (m); \ + for (c = 0; __tmp_msg; __tmp_msg = __tmp_msg->parent, ++(c)); \ +} + +#define http_message_parse(m, l) http_message_parse_ex(NULL, (m), (l)) +#define http_message_parse_ex(h, m, l) _http_message_parse_ex((h), (m), (l) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) +#define http_message_parse_rel(h, m, l) _http_message_parse_ex((h), (m), (l) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC TSRMLS_CC) +PHP_HTTP_API http_message *_http_message_parse_ex(http_message *msg, const char *message, size_t length ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC); + +#define http_message_tostring(m, s, l) _http_message_tostring((m), (s), (l)) +PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_t *length); + +#define http_message_serialize(m, s, l) _http_message_serialize((m), (s), (l)) +PHP_HTTP_API void _http_message_serialize(http_message *message, char **string, size_t *length); + +#define http_message_reverse(m) _http_message_reverse(m) +PHP_HTTP_API http_message *_http_message_reverse(http_message *msg); + +#define http_message_interconnect(m1, m2) _http_message_interconnect((m1), (m2)) +PHP_HTTP_API http_message *_http_message_interconnect(http_message *m1, http_message *m2); + +#define http_message_tostruct_recursive(m, s) _http_message_tostruct_recursive((m), (s) TSRMLS_CC) +PHP_HTTP_API void _http_message_tostruct_recursive(http_message *msg, zval *strct TSRMLS_DC); + +#define http_message_send(m) _http_message_send((m) TSRMLS_CC) +PHP_HTTP_API STATUS _http_message_send(http_message *message TSRMLS_DC); + +#define http_message_dup(m) _http_message_dup((m) TSRMLS_CC) +PHP_HTTP_API http_message *_http_message_dup(http_message *msg TSRMLS_DC); + +#define http_message_dtor(m) _http_message_dtor((m)) +PHP_HTTP_API void _http_message_dtor(http_message *message); + +#define http_message_free(m) _http_message_free((m)) +PHP_HTTP_API void _http_message_free(http_message **message); + +#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 + */ + --- /dev/null +++ b/ext/http/php_http_message_object.h @@ -0,0 +1,115 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_message_object.h 298590 2010-04-26 11:46:35Z mike $ */ + +#ifndef PHP_HTTP_MESSAGE_OBJECT_H +#define PHP_HTTP_MESSAGE_OBJECT_H +#ifdef ZEND_ENGINE_2 + +typedef struct _http_message_object_t { + zend_object zo; + http_message *message; + zend_object_value parent; + zval *iterator; +} http_message_object; + +extern zend_class_entry *http_message_object_ce; +extern zend_function_entry http_message_object_fe[]; + +extern PHP_MINIT_FUNCTION(http_message_object); +extern PHP_MSHUTDOWN_FUNCTION(http_message_object); + +#define http_message_object_prepend(o, p) http_message_object_prepend_ex((o), (p), 1) +#define http_message_object_prepend_ex(o, p, t) _http_message_object_prepend_ex((o), (p), (t) TSRMLS_CC) +extern void _http_message_object_prepend_ex(zval *this_ptr, zval *prepend, zend_bool top TSRMLS_DC); + +#define http_message_object_reverse(t, r) _http_message_object_reverse((t), (r) TSRMLS_CC) +extern void _http_message_object_reverse(zval *this_ptr, zval *return_value TSRMLS_DC); + +#define http_message_object_new(ce) _http_message_object_new((ce) TSRMLS_CC) +extern zend_object_value _http_message_object_new(zend_class_entry *ce TSRMLS_DC); +#define http_message_object_new_ex(ce, msg, ptr) _http_message_object_new_ex((ce), (msg), (ptr) TSRMLS_CC) +extern zend_object_value _http_message_object_new_ex(zend_class_entry *ce, http_message *msg, http_message_object **ptr TSRMLS_DC); +#define http_message_object_clone(zobj) _http_message_object_clone_obj(zobj TSRMLS_CC) +extern zend_object_value _http_message_object_clone_obj(zval *object TSRMLS_DC); +#define http_message_object_free(o) _http_message_object_free((o) TSRMLS_CC) +extern void _http_message_object_free(zend_object *object TSRMLS_DC); + +#define HTTP_MSG_CHECK_OBJ(obj, dofail) \ + if (!(obj)->message) { \ + http_error(E_WARNING, HTTP_E_MSG, "HttpMessage is empty"); \ + dofail; \ + } +#define HTTP_MSG_CHECK_STD() HTTP_MSG_CHECK_OBJ(obj, RETURN_FALSE) + +#define HTTP_MSG_INIT_OBJ(obj) \ + if (!(obj)->message) { \ + (obj)->message = http_message_new(); \ + } +#define HTTP_MSG_INIT_STD() HTTP_MSG_INIT_OBJ(obj) + +PHP_METHOD(HttpMessage, __construct); +PHP_METHOD(HttpMessage, getBody); +PHP_METHOD(HttpMessage, setBody); +PHP_METHOD(HttpMessage, getHeader); +PHP_METHOD(HttpMessage, getHeaders); +PHP_METHOD(HttpMessage, setHeaders); +PHP_METHOD(HttpMessage, addHeaders); +PHP_METHOD(HttpMessage, getType); +PHP_METHOD(HttpMessage, setType); +PHP_METHOD(HttpMessage, getInfo); +PHP_METHOD(HttpMessage, setInfo); +PHP_METHOD(HttpMessage, getResponseCode); +PHP_METHOD(HttpMessage, setResponseCode); +PHP_METHOD(HttpMessage, getResponseStatus); +PHP_METHOD(HttpMessage, setResponseStatus); +PHP_METHOD(HttpMessage, getRequestMethod); +PHP_METHOD(HttpMessage, setRequestMethod); +PHP_METHOD(HttpMessage, getRequestUrl); +PHP_METHOD(HttpMessage, setRequestUrl); +PHP_METHOD(HttpMessage, getHttpVersion); +PHP_METHOD(HttpMessage, setHttpVersion); +PHP_METHOD(HttpMessage, guessContentType); +PHP_METHOD(HttpMessage, getParentMessage); +PHP_METHOD(HttpMessage, send); +PHP_METHOD(HttpMessage, toString); +PHP_METHOD(HttpMessage, toMessageTypeObject); + +PHP_METHOD(HttpMessage, count); +PHP_METHOD(HttpMessage, serialize); +PHP_METHOD(HttpMessage, unserialize); +PHP_METHOD(HttpMessage, rewind); +PHP_METHOD(HttpMessage, valid); +PHP_METHOD(HttpMessage, current); +PHP_METHOD(HttpMessage, key); +PHP_METHOD(HttpMessage, next); + +PHP_METHOD(HttpMessage, factory); +PHP_METHOD(HttpMessage, fromEnv); + +PHP_METHOD(HttpMessage, detach); +PHP_METHOD(HttpMessage, prepend); +PHP_METHOD(HttpMessage, reverse); + +#endif +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/php_http_persistent_handle_api.h @@ -0,0 +1,58 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_persistent_handle_api.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef HTTP_PERSISTENT_HANDLE_H +#define HTTP_PERSISTENT_HANDLE_H + +typedef void *(*http_persistent_handle_ctor)(void); +typedef void (*http_persistent_handle_dtor)(void *handle); +typedef void *(*http_persistent_handle_copy)(void *handle); + +PHP_MINIT_FUNCTION(http_persistent_handle); +PHP_MSHUTDOWN_FUNCTION(http_persistent_handle); + +#define http_persistent_handle_provide(n, c, d, cc) _http_persistent_handle_provide_ex((n), strlen(n), (c), (d), (cc)) +#define http_persistent_handle_provide_ex(n, l, c, d, cc) _http_persistent_handle_provide_ex((n), (l), (c), (d), (cc)) +PHP_HTTP_API STATUS _http_persistent_handle_provide_ex(const char *name_str, size_t name_len, http_persistent_handle_ctor ctor, http_persistent_handle_dtor dtor, http_persistent_handle_copy copy); + +#define http_persistent_handle_cleanup(n, c) _http_persistent_handle_cleanup_ex((n), strlen(n), (c) TSRMLS_CC) +#define http_persistent_handle_cleanup_ex(n, l,c ) _http_persistent_handle_cleanup_ex((n), (l), (c) TSRMLS_CC) +PHP_HTTP_API void _http_persistent_handle_cleanup_ex(const char *name_str, size_t name_len, int current_ident_only TSRMLS_DC); + +#define http_persistent_handle_statall() _http_persistent_handle_statall_ex(NULL TSRMLS_CC) +#define http_persistent_handle_statall_ex(ht) _http_persistent_handle_statall_ex((ht) TSRMLS_CC) +PHP_HTTP_API HashTable *_http_persistent_handle_statall_ex(HashTable *ht TSRMLS_DC); + +#define http_persistent_handle_acquire(n, h) _http_persistent_handle_acquire_ex((n), strlen(n), (h) TSRMLS_CC) +#define http_persistent_handle_acquire_ex(n, l, h) _http_persistent_handle_acquire_ex((n), (l), (h) TSRMLS_CC) +PHP_HTTP_API STATUS _http_persistent_handle_acquire_ex(const char *name_str, size_t name_len, void **handle TSRMLS_DC); + +#define http_persistent_handle_release(n, h) _http_persistent_handle_release_ex((n), strlen(n), (h) TSRMLS_CC) +#define http_persistent_handle_release_ex(n, l, h) _http_persistent_handle_release_ex((n), (l), (h) TSRMLS_CC) +PHP_HTTP_API STATUS _http_persistent_handle_release_ex(const char *name_str, size_t name_len, void **handle TSRMLS_DC); + +#define http_persistent_handle_accrete(n, oh, nh) _http_persistent_handle_accrete_ex((n), strlen(n), (oh), (nh) TSRMLS_CC) +#define http_persistent_handle_accrete_ex(n, l, oh, nh) _http_persistent_handle_accrete_ex((n), (l), (oh), (nh) TSRMLS_CC) +PHP_HTTP_API STATUS _http_persistent_handle_accrete_ex(const char *name_str, size_t name_len, void *old_handle, void **new_handle TSRMLS_DC); + +#endif /* HTTP_PERSISTENT_HANDLE_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 + */ --- /dev/null +++ b/ext/http/php_http_querystring_api.h @@ -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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_querystring_api.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_QUERYSTRING_API_H +#define PHP_HTTP_QUERYSTRING_API_H + +#ifdef HTTP_HAVE_ICONV +#define http_querystring_xlate(a, p, ie, oe) _http_querystring_xlate((a), (p), (ie), (oe) TSRMLS_CC) +PHP_HTTP_API int _http_querystring_xlate(zval *array, zval *param, const char *ie, const char *oe TSRMLS_DC); +#endif + +#define http_querystring_update(qa, qs) _http_querystring_update((qa), (qs) TSRMLS_CC) +PHP_HTTP_API void _http_querystring_update(zval *qarray, zval *qstring TSRMLS_DC); + +#define http_querystring_modify(q, p) _http_querystring_modify((q), (p) TSRMLS_CC) +PHP_HTTP_API int _http_querystring_modify(zval *qarray, zval *params 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 + */ --- /dev/null +++ b/ext/http/php_http_querystring_object.h @@ -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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_querystring_object.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_QUERYSTRING_OBJECT_H +#define PHP_HTTP_QUERYSTRING_OBJECT_H +#ifdef ZEND_ENGINE_2 + +typedef struct _http_querystring_object_t { + zend_object zo; +} http_querystring_object; + +#define HTTP_QUERYSTRING_TYPE_BOOL IS_BOOL +#define HTTP_QUERYSTRING_TYPE_INT IS_LONG +#define HTTP_QUERYSTRING_TYPE_FLOAT IS_DOUBLE +#define HTTP_QUERYSTRING_TYPE_STRING IS_STRING +#define HTTP_QUERYSTRING_TYPE_ARRAY IS_ARRAY +#define HTTP_QUERYSTRING_TYPE_OBJECT IS_OBJECT + +extern zend_class_entry *http_querystring_object_ce; +extern zend_function_entry http_querystring_object_fe[]; + +extern PHP_MINIT_FUNCTION(http_querystring_object); + +#define http_querystring_object_new(ce) _http_querystring_object_new((ce) TSRMLS_CC) +extern zend_object_value _http_querystring_object_new(zend_class_entry *ce TSRMLS_DC); +#define http_querystring_object_new_ex(ce, n, ptr) _http_querystring_object_new_ex((ce), (n), (ptr) TSRMLS_CC) +extern zend_object_value _http_querystring_object_new_ex(zend_class_entry *ce, void *nothing, http_querystring_object **ptr TSRMLS_DC); +#define http_querystring_object_free(o) _http_querystring_object_free((o) TSRMLS_CC) +extern void _http_querystring_object_free(zend_object *object TSRMLS_DC); + +PHP_METHOD(HttpQueryString, __construct); +PHP_METHOD(HttpQueryString, toString); +PHP_METHOD(HttpQueryString, toArray); +PHP_METHOD(HttpQueryString, get); +PHP_METHOD(HttpQueryString, set); +PHP_METHOD(HttpQueryString, mod); +PHP_METHOD(HttpQueryString, getBool); +PHP_METHOD(HttpQueryString, getInt); +PHP_METHOD(HttpQueryString, getFloat); +PHP_METHOD(HttpQueryString, getString); +PHP_METHOD(HttpQueryString, getArray); +PHP_METHOD(HttpQueryString, getObject); +#ifdef HTTP_HAVE_ICONV +PHP_METHOD(HttpQueryString, xlate); +#endif +PHP_METHOD(HttpQueryString, factory); +#ifndef WONKY +PHP_METHOD(HttpQueryString, singleton); +#endif +PHP_METHOD(HttpQueryString, serialize); +PHP_METHOD(HttpQueryString, unserialize); +PHP_METHOD(HttpQueryString, offsetGet); +PHP_METHOD(HttpQueryString, offsetSet); +PHP_METHOD(HttpQueryString, offsetExists); +PHP_METHOD(HttpQueryString, offsetUnset); +#endif +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/php_http_request_api.h @@ -0,0 +1,144 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_request_api.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_REQUEST_API_H +#define PHP_HTTP_REQUEST_API_H + +#ifdef HTTP_HAVE_CURL + +#include "php_http_request_body_api.h" +#include "php_http_request_method_api.h" + +extern PHP_MINIT_FUNCTION(http_request); +extern PHP_MSHUTDOWN_FUNCTION(http_request); + +typedef struct _http_request_t { + CURL *ch; + char *url; + http_request_method meth; + http_request_body *body; + + struct { + curl_infotype last_type; + phpstr request; + phpstr response; + } conv; + + struct { + phpstr cookies; + HashTable options; + struct curl_slist *headers; + } _cache; + + struct { + uint count; + double delay; + } _retry; + + char _error[CURL_ERROR_SIZE+1]; + zval *_progress_callback; + +#ifdef ZTS + void ***tsrm_ls; +#endif + + uint _in_progress_cb:1; + +} http_request; + +#ifndef pestrndup +# define pestrndup(s,l,p) _pestrndup((s),(l),(p)) +static inline void *_pestrndup(const void *s, size_t l, int p) +{ + void *d = pemalloc(l+1, p); + memcpy(d, s, l); + ((char *) d)[l] = '\0'; + return d; +} +#endif + +/* CURLOPT_PRIVATE storage living as long as a CURL handle */ +typedef struct _http_request_storage_t { + char *url; + char *cookiestore; + char errorbuffer[CURL_ERROR_SIZE]; +} http_request_storage; + +static inline http_request_storage *http_request_storage_get(CURL *ch) +{ + http_request_storage *st = NULL; + curl_easy_getinfo(ch, CURLINFO_PRIVATE, &st); + return st; +} + +#define http_curl_init(r) http_curl_init_ex(NULL, (r)) +#define http_curl_init_ex(c, r) _http_curl_init_ex((c), (r) TSRMLS_CC) +PHP_HTTP_API CURL *_http_curl_init_ex(CURL *ch, http_request *request TSRMLS_DC); + +#define http_curl_free(c) _http_curl_free((c) TSRMLS_CC) +PHP_HTTP_API void _http_curl_free(CURL **ch TSRMLS_DC); + +#define http_curl_copy(c) _http_curl_copy((c) TSRMLS_CC) +PHP_HTTP_API CURL *_http_curl_copy(CURL *ch TSRMLS_DC); + +#define http_request_new() _http_request_init_ex(NULL, NULL, 0, NULL ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) +#define http_request_init(r) _http_request_init_ex((r), NULL, 0, NULL ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) +#define http_request_init_ex(r, c, m, u) _http_request_init_ex((r), (c), (m), (u) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) +PHP_HTTP_API http_request *_http_request_init_ex(http_request *request, CURL *ch, http_request_method meth, const char *url ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC); + +#define http_request_dtor(r) _http_request_dtor((r)) +PHP_HTTP_API void _http_request_dtor(http_request *request); + +#define http_request_free(r) _http_request_free((r)) +PHP_HTTP_API void _http_request_free(http_request **request); + +#define http_request_reset(r) _http_request_reset(r) +PHP_HTTP_API void _http_request_reset(http_request *r); + +#define http_request_enable_cookies(r) _http_request_enable_cookies(r) +PHP_HTTP_API STATUS _http_request_enable_cookies(http_request *request); + +#define http_request_reset_cookies(r, s) _http_request_reset_cookies((r), (s)) +PHP_HTTP_API STATUS _http_request_reset_cookies(http_request *request, int session_only); + +#define http_request_flush_cookies(r) _http_request_flush_cookies(r) +PHP_HTTP_API STATUS _http_request_flush_cookies(http_request *request); + +#define http_request_defaults(r) _http_request_defaults(r) +PHP_HTTP_API void _http_request_defaults(http_request *request); + +#define http_request_prepare(r, o) _http_request_prepare((r), (o)) +PHP_HTTP_API STATUS _http_request_prepare(http_request *request, HashTable *options); + +#define http_request_exec(r) _http_request_exec((r)) +PHP_HTTP_API void _http_request_exec(http_request *request); + +#define http_request_info(r, i) _http_request_info((r), (i)) +PHP_HTTP_API void _http_request_info(http_request *request, HashTable *info); + +#define http_request_set_progress_callback(r, cb) _http_request_set_progress_callback((r), (cb)) +PHP_HTTP_API void _http_request_set_progress_callback(http_request *request, zval *cb); + +#endif +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/php_http_request_body_api.h @@ -0,0 +1,62 @@ +/* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_request_body_api.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_REQUEST_BODY_API_H +#define PHP_HTTP_REQUEST_BODY_API_H + +#ifdef HTTP_HAVE_CURL + +#define HTTP_REQUEST_BODY_EMPTY 0 +#define HTTP_REQUEST_BODY_CSTRING 1 +#define HTTP_REQUEST_BODY_CURLPOST 2 +#define HTTP_REQUEST_BODY_UPLOADFILE 3 +typedef struct _http_request_body_t { + void *data; + size_t size; + uint type:3; + uint free:1; + uint priv:28; +} http_request_body; + + +#define http_request_body_new() http_request_body_init(NULL) +#define http_request_body_init(b) http_request_body_init_ex((b), 0, NULL, 0, 0) +#define http_request_body_init_ex(b, t, d, l, f) _http_request_body_init_ex((b), (t), (d), (l), (f) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) +#define http_request_body_init_rel(b, t, d, l, f) _http_request_body_init_ex((b), (t), (d), (l), (f) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC TSRMLS_CC) +PHP_HTTP_API http_request_body *_http_request_body_init_ex(http_request_body *body, int type, void *data, size_t len, zend_bool free ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC); + +#define http_request_body_fill(b, fields, files) _http_request_body_fill((b), (fields), (files) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) +PHP_HTTP_API http_request_body *_http_request_body_fill(http_request_body *body, HashTable *fields, HashTable *files ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC); + +#define http_request_body_encode(b, s, l) _http_request_body_encode((b), (s), (l) TSRMLS_CC) +PHP_HTTP_API STATUS _http_request_body_encode(http_request_body *body, char **buf, size_t *len TSRMLS_DC); + +#define http_request_body_dtor(b) _http_request_body_dtor((b) TSRMLS_CC) +PHP_HTTP_API void _http_request_body_dtor(http_request_body *body TSRMLS_DC); + +#define http_request_body_free(b) _http_request_body_free((b) TSRMLS_CC) +PHP_HTTP_API void _http_request_body_free(http_request_body **body TSRMLS_DC); + +#endif +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/php_http_request_datashare_api.h @@ -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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_request_datashare_api.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_REQUEST_DATASHARE_API_H +#define PHP_HTTP_REQUEST_DATASHARE_API_H +#ifdef HTTP_HAVE_CURL +#ifdef ZEND_ENGINE_2 + +#ifdef ZTS +typedef struct _http_request_datashare_lock_t { + CURL *ch; + MUTEX_T mx; +} http_request_datashare_lock; + +typedef union _http_request_datashare_handle_t { + zend_llist *list; + http_request_datashare_lock *locks; +} http_request_datashare_handle; +#else +typedef struct _http_request_datashare_handle_t { + zend_llist *list; +} http_request_datashare_handle; +#endif + +typedef struct _http_request_datashare_t { + CURLSH *ch; + zend_bool persistent; + http_request_datashare_handle handle; +} http_request_datashare; + +#define HTTP_RSHARE_HANDLES(s) ((s)->persistent ? &HTTP_G->request.datashare.handles : (s)->handle.list) + +#define http_request_datashare_global_get _http_request_datashare_global_get +extern http_request_datashare *_http_request_datashare_global_get(void); + +extern PHP_MINIT_FUNCTION(http_request_datashare); +extern PHP_MSHUTDOWN_FUNCTION(http_request_datashare); +extern PHP_RINIT_FUNCTION(http_request_datashare); +extern PHP_RSHUTDOWN_FUNCTION(http_request_datashare); + +#define http_request_datashare_new() _http_request_datashare_init_ex(NULL, 0 TSRMLS_CC) +#define http_request_datashare_init(s) _http_request_datashare_init_ex((s), 0 TSRMLS_CC) +#define http_request_datashare_init_ex(s, p) _http_request_datashare_init_ex((s), (p) TSRMLS_CC) +PHP_HTTP_API http_request_datashare *_http_request_datashare_init_ex(http_request_datashare *share, zend_bool persistent TSRMLS_DC); + +#define http_request_datashare_attach(s, r) _http_request_datashare_attach((s), (r) TSRMLS_CC) +PHP_HTTP_API STATUS _http_request_datashare_attach(http_request_datashare *share, zval *request TSRMLS_DC); + +#define http_request_datashare_detach(s, r) _http_request_datashare_detach((s), (r) TSRMLS_CC) +PHP_HTTP_API STATUS _http_request_datashare_detach(http_request_datashare *share, zval *request TSRMLS_DC); + +#define http_request_datashare_detach_all(s) _http_request_datashare_detach_all((s) TSRMLS_CC) +PHP_HTTP_API void _http_request_datashare_detach_all(http_request_datashare *share TSRMLS_DC); + +#define http_request_datashare_dtor(s) _http_request_datashare_dtor((s) TSRMLS_CC) +PHP_HTTP_API void _http_request_datashare_dtor(http_request_datashare *share TSRMLS_DC); + +#define http_request_datashare_free(s) _http_request_datashare_free((s) TSRMLS_CC) +PHP_HTTP_API void _http_request_datashare_free(http_request_datashare **share TSRMLS_DC); + +#define http_request_datashare_set(s, o, l, e) _http_request_datashare_set((s), (o), (l), (e) TSRMLS_CC) +PHP_HTTP_API STATUS _http_request_datashare_set(http_request_datashare *share, const char *option, size_t option_len, zend_bool enable TSRMLS_DC); + + +#endif +#endif +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/php_http_request_int.h @@ -0,0 +1,72 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_request_int.h 292841 2009-12-31 08:48:57Z mike $ */ + +#if defined(ZTS) && defined(HTTP_HAVE_SSL) +# ifdef PHP_WIN32 +# define HTTP_NEED_OPENSSL_TSL +# include +# else /* !PHP_WIN32 */ +# if defined(HTTP_HAVE_OPENSSL) +# define HTTP_NEED_OPENSSL_TSL +# include +# elif defined(HTTP_HAVE_GNUTLS) +# define HTTP_NEED_GNUTLS_TSL +# include +# else +# warning \ + "libcurl was compiled with SSL support, but configure could not determine which" \ + "library was used; thus no SSL crypto locking callbacks will be set, which may " \ + "cause random crashes on SSL requests" +# endif /* HTTP_HAVE_OPENSSL || HTTP_HAVE_GNUTLS */ +# endif /* PHP_WIN32 */ +#endif /* ZTS && HTTP_HAVE_SSL */ + +#define HTTP_CURL_OPT(OPTION, p) curl_easy_setopt((request->ch), OPTION, (p)) + +#define HTTP_CURL_OPT_STRING(OPTION, ldiff, obdc) \ + { \ + char *K = #OPTION; \ + HTTP_CURL_OPT_STRING_EX(K+lenof("CURLOPT_KEY")+ldiff, OPTION, obdc); \ + } +#define HTTP_CURL_OPT_STRING_EX(keyname, optname, obdc) \ + if (!strcasecmp(key.str, keyname)) { \ + zval *copy = http_request_option_cache_ex(request, keyname, strlen(keyname)+1, 0, http_zsep(IS_STRING, *param)); \ + if (obdc) { \ + HTTP_CHECK_OPEN_BASEDIR(Z_STRVAL_P(copy), return FAILURE); \ + } \ + HTTP_CURL_OPT(optname, Z_STRVAL_P(copy)); \ + zval_ptr_dtor(©); \ + continue; \ + } +#define HTTP_CURL_OPT_LONG(OPTION, ldiff) \ + { \ + char *K = #OPTION; \ + HTTP_CURL_OPT_LONG_EX(K+lenof("CURLOPT_KEY")+ldiff, OPTION); \ + } +#define HTTP_CURL_OPT_LONG_EX(keyname, optname) \ + if (!strcasecmp(key.str, keyname)) { \ + zval *copy = http_zsep(IS_LONG, *param); \ + HTTP_CURL_OPT(optname, Z_LVAL_P(copy)); \ + zval_ptr_dtor(©); \ + continue; \ + } + +/* + * 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 + */ --- /dev/null +++ b/ext/http/php_http_request_method_api.h @@ -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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_request_method_api.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_REQUEST_METHOD_API_H +#define PHP_HTTP_REQUEST_METHOD_API_H + +typedef enum _http_request_method_t { + /* force the enum to be signed */ + HTTP_NEG_REQUEST_METHOD =-1, + HTTP_NO_REQUEST_METHOD = 0, + /* HTTP/1.1 */ + HTTP_GET = 1, + HTTP_HEAD = 2, + HTTP_POST = 3, + HTTP_PUT = 4, + HTTP_DELETE = 5, + HTTP_OPTIONS = 6, + HTTP_TRACE = 7, + HTTP_CONNECT = 8, + /* WebDAV - RFC 2518 */ + HTTP_PROPFIND = 9, + HTTP_PROPPATCH = 10, + HTTP_MKCOL = 11, + HTTP_COPY = 12, + HTTP_MOVE = 13, + HTTP_LOCK = 14, + HTTP_UNLOCK = 15, + /* WebDAV Versioning - RFC 3253 */ + HTTP_VERSION_CONTROL = 16, + HTTP_REPORT = 17, + HTTP_CHECKOUT = 18, + HTTP_CHECKIN = 19, + HTTP_UNCHECKOUT = 20, + HTTP_MKWORKSPACE = 21, + HTTP_UPDATE = 22, + HTTP_LABEL = 23, + HTTP_MERGE = 24, + HTTP_BASELINE_CONTROL = 25, + HTTP_MKACTIVITY = 26, + /* WebDAV Access Control - RFC 3744 */ + HTTP_ACL = 27, + HTTP_MAX_REQUEST_METHOD = 28 +} http_request_method; + +#define HTTP_MIN_REQUEST_METHOD (HTTP_NO_REQUEST_METHOD + 1) +#define HTTP_STD_REQUEST_METHOD(m) ((m > HTTP_NO_REQUEST_METHOD) && (m < HTTP_MAX_REQUEST_METHOD)) + +extern PHP_MINIT_FUNCTION(http_request_method); +extern PHP_RINIT_FUNCTION(http_request_method); +extern PHP_RSHUTDOWN_FUNCTION(http_request_method); + +#define http_request_method_name(m) _http_request_method_name((m) TSRMLS_CC) +PHP_HTTP_API const char *_http_request_method_name(http_request_method m TSRMLS_DC); + +#define http_request_method_exists(u, l, c) _http_request_method_exists((u), (l), (c) TSRMLS_CC) +PHP_HTTP_API int _http_request_method_exists(int by_name, http_request_method id, const char *name TSRMLS_DC); + +#define http_request_method_register(m, l) _http_request_method_register((m), (l) TSRMLS_CC) +PHP_HTTP_API int _http_request_method_register(const char *method, int method_name_len TSRMLS_DC); + +#define http_request_method_unregister(mn) _http_request_method_unregister((mn) TSRMLS_CC) +PHP_HTTP_API STATUS _http_request_method_unregister(int method 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 + */ + --- /dev/null +++ b/ext/http/php_http_request_object.h @@ -0,0 +1,118 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_request_object.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_REQUEST_OBJECT_H +#define PHP_HTTP_REQUEST_OBJECT_H +#ifdef HTTP_HAVE_CURL +#ifdef ZEND_ENGINE_2 + +#include "php_http_request_api.h" +#include "php_http_request_pool_api.h" +#include "php_http_request_datashare_api.h" + +typedef struct _http_request_object_t { + zend_object zo; + http_request *request; + http_request_pool *pool; + http_request_datashare *share; +} http_request_object; + +extern zend_class_entry *http_request_object_ce; +extern zend_function_entry http_request_object_fe[]; + +extern PHP_MINIT_FUNCTION(http_request_object); + +#define http_request_object_new(ce) _http_request_object_new((ce) TSRMLS_CC) +extern zend_object_value _http_request_object_new(zend_class_entry *ce TSRMLS_DC); +#define http_request_object_new_ex(ce, ch, ptr) _http_request_object_new_ex((ce), (ch), (ptr) TSRMLS_CC) +extern zend_object_value _http_request_object_new_ex(zend_class_entry *ce, CURL *ch, http_request_object **ptr TSRMLS_DC); +#define http_request_object_clone(zv) _http_request_object_clone_obj((zv) TSRMLS_CC) +extern zend_object_value _http_request_object_clone_obj(zval *zobject TSRMLS_DC); +#define http_request_object_free(o) _http_request_object_free((o) TSRMLS_CC) +extern void _http_request_object_free(zend_object *object TSRMLS_DC); + +#define http_request_object_requesthandler(req, this) _http_request_object_requesthandler((req), (this) TSRMLS_CC) +extern STATUS _http_request_object_requesthandler(http_request_object *obj, zval *this_ptr TSRMLS_DC); +#define http_request_object_responsehandler(req, this) _http_request_object_responsehandler((req), (this) TSRMLS_CC) +extern STATUS _http_request_object_responsehandler(http_request_object *obj, zval *this_ptr TSRMLS_DC); + +PHP_METHOD(HttpRequest, __construct); +PHP_METHOD(HttpRequest, setOptions); +PHP_METHOD(HttpRequest, getOptions); +PHP_METHOD(HttpRequest, addSslOptions); +PHP_METHOD(HttpRequest, setSslOptions); +PHP_METHOD(HttpRequest, getSslOptions); +PHP_METHOD(HttpRequest, addHeaders); +PHP_METHOD(HttpRequest, getHeaders); +PHP_METHOD(HttpRequest, setHeaders); +PHP_METHOD(HttpRequest, addCookies); +PHP_METHOD(HttpRequest, getCookies); +PHP_METHOD(HttpRequest, setCookies); +PHP_METHOD(HttpRequest, enableCookies); +PHP_METHOD(HttpRequest, resetCookies); +PHP_METHOD(HttpRequest, flushCookies); +PHP_METHOD(HttpRequest, setMethod); +PHP_METHOD(HttpRequest, getMethod); +PHP_METHOD(HttpRequest, setUrl); +PHP_METHOD(HttpRequest, getUrl); +PHP_METHOD(HttpRequest, setContentType); +PHP_METHOD(HttpRequest, getContentType); +PHP_METHOD(HttpRequest, setQueryData); +PHP_METHOD(HttpRequest, getQueryData); +PHP_METHOD(HttpRequest, addQueryData); +PHP_METHOD(HttpRequest, setPostFields); +PHP_METHOD(HttpRequest, getPostFields); +PHP_METHOD(HttpRequest, addPostFields); +PHP_METHOD(HttpRequest, getBody); +PHP_METHOD(HttpRequest, setBody); +PHP_METHOD(HttpRequest, addBody); +PHP_METHOD(HttpRequest, addPostFile); +PHP_METHOD(HttpRequest, setPostFiles); +PHP_METHOD(HttpRequest, getPostFiles); +PHP_METHOD(HttpRequest, setPutFile); +PHP_METHOD(HttpRequest, getPutFile); +PHP_METHOD(HttpRequest, getPutData); +PHP_METHOD(HttpRequest, setPutData); +PHP_METHOD(HttpRequest, addPutData); +PHP_METHOD(HttpRequest, send); +PHP_METHOD(HttpRequest, getResponseData); +PHP_METHOD(HttpRequest, getResponseHeader); +PHP_METHOD(HttpRequest, getResponseCookies); +PHP_METHOD(HttpRequest, getResponseCode); +PHP_METHOD(HttpRequest, getResponseStatus); +PHP_METHOD(HttpRequest, getResponseBody); +PHP_METHOD(HttpRequest, getResponseInfo); +PHP_METHOD(HttpRequest, getResponseMessage); +PHP_METHOD(HttpRequest, getRawResponseMessage); +PHP_METHOD(HttpRequest, getRequestMessage); +PHP_METHOD(HttpRequest, getRawRequestMessage); +PHP_METHOD(HttpRequest, getHistory); +PHP_METHOD(HttpRequest, clearHistory); +PHP_METHOD(HttpRequest, getMessageClass); +PHP_METHOD(HttpRequest, setMessageClass); +PHP_METHOD(HttpRequest, factory); + +#endif +#endif +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/php_http_request_pool_api.h @@ -0,0 +1,97 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_request_pool_api.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_REQUEST_POOL_API_H +#define PHP_HTTP_REQUEST_POOL_API_H +#ifdef HTTP_HAVE_CURL +#ifdef ZEND_ENGINE_2 + +typedef struct _http_request_pool_t { + CURLM *ch; + zend_llist finished; + zend_llist handles; + int unfinished; +#ifdef ZTS + void ***tsrm_ls; +#endif +#ifdef HTTP_HAVE_EVENT + struct event *timeout; + unsigned useevents:1; + unsigned runsocket:1; +#endif +} http_request_pool; + +typedef int (*http_request_pool_apply_func)(http_request_pool *pool, zval *request); +typedef int (*http_request_pool_apply_with_arg_func)(http_request_pool *pool, zval *request, void *arg); + +PHP_MINIT_FUNCTION(http_request_pool); +#ifdef HTTP_HAVE_EVENT +PHP_RINIT_FUNCTION(http_request_pool); +#endif + +#define http_request_pool_timeout _http_request_pool_timeout +extern struct timeval *_http_request_pool_timeout(http_request_pool *pool, struct timeval *timeout); + +#define http_request_pool_responsehandler _http_request_pool_responsehandler +extern void _http_request_pool_responsehandler(http_request_pool *pool); + +#define http_request_pool_apply_responsehandler _http_request_pool_responsehandler +extern int _http_request_pool_apply_responsehandler(http_request_pool *pool, zval *req, void *ch); + +#define http_request_pool_init(p) _http_request_pool_init((p) TSRMLS_CC) +PHP_HTTP_API http_request_pool *_http_request_pool_init(http_request_pool *pool TSRMLS_DC); + +#define http_request_pool_attach(p, r) _http_request_pool_attach((p), (r)) +PHP_HTTP_API STATUS _http_request_pool_attach(http_request_pool *pool, zval *request); + +#define http_request_pool_detach(p, r) _http_request_pool_detach((p), (r)) +PHP_HTTP_API STATUS _http_request_pool_detach(http_request_pool *pool, zval *request); + +#define http_request_pool_apply(p, f) _http_request_pool_apply((p), (f)) +PHP_HTTP_API void _http_request_pool_apply(http_request_pool *pool, http_request_pool_apply_func cb); + +#define http_request_pool_apply_with_arg(p, f, a) _http_request_pool_apply_with_arg((p), (f), (a)) +PHP_HTTP_API void _http_request_pool_apply_with_arg(http_request_pool *pool, http_request_pool_apply_with_arg_func cb, void *arg); + +#define http_request_pool_detach_all(p) _http_request_pool_detach_all((p)) +PHP_HTTP_API void _http_request_pool_detach_all(http_request_pool *pool); + +#define http_request_pool_send(p) _http_request_pool_send((p)) +PHP_HTTP_API STATUS _http_request_pool_send(http_request_pool *pool); + +#define http_request_pool_select _http_request_pool_select +PHP_HTTP_API STATUS _http_request_pool_select(http_request_pool *pool); + +#define http_request_pool_select_ex _http_request_pool_select_ex +PHP_HTTP_API STATUS _http_request_pool_select_ex(http_request_pool *pool, struct timeval *custom_timeout); + +#define http_request_pool_perform(p) _http_request_pool_perform((p)) +PHP_HTTP_API int _http_request_pool_perform(http_request_pool *pool); + +#define http_request_pool_dtor(p) _http_request_pool_dtor((p)) +PHP_HTTP_API void _http_request_pool_dtor(http_request_pool *pool); + +#endif +#endif +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/php_http_requestdatashare_object.h @@ -0,0 +1,59 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_requestdatashare_object.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_REQUEST_DATASHARE_OBJECT_H +#define PHP_HTTP_REQUEST_DATASHARE_OBJECT_H +#ifdef HTTP_HAVE_CURL +#ifdef ZEND_ENGINE_2 + +typedef struct _http_requestdatashare_object_t { + zend_object zo; + http_request_datashare *share; +} http_requestdatashare_object; + +extern zend_class_entry *http_requestdatashare_object_ce; +extern zend_function_entry http_requestdatashare_object_fe[]; + +extern PHP_MINIT_FUNCTION(http_requestdatashare_object); + +#define http_requestdatashare_object_new(ce) _http_requestdatashare_object_new((ce) TSRMLS_CC) +extern zend_object_value _http_requestdatashare_object_new(zend_class_entry *ce TSRMLS_DC); +#define http_requestdatashare_object_new_ex(ce, s, ptr) _http_requestdatashare_object_new_ex((ce), (s), (ptr) TSRMLS_CC) +extern zend_object_value _http_requestdatashare_object_new_ex(zend_class_entry *ce, http_request_datashare *share, http_requestdatashare_object **ptr TSRMLS_DC); +#define http_requestdatashare_object_free(o) _http_requestdatashare_object_free((o) TSRMLS_CC) +extern void _http_requestdatashare_object_free(zend_object *object TSRMLS_DC); + +PHP_METHOD(HttpRequestDataShare, __destruct); +PHP_METHOD(HttpRequestDataShare, count); +PHP_METHOD(HttpRequestDataShare, attach); +PHP_METHOD(HttpRequestDataShare, detach); +PHP_METHOD(HttpRequestDataShare, reset); +PHP_METHOD(HttpRequestDataShare, factory); +#ifndef WONKY +PHP_METHOD(HttpRequestDataShare, singleton); +#endif + +#endif +#endif +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/php_http_requestpool_object.h @@ -0,0 +1,69 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_requestpool_object.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_REQUESTPOOL_OBJECT_H +#define PHP_HTTP_REQUESTPOOL_OBJECT_H +#ifdef HTTP_HAVE_CURL +#ifdef ZEND_ENGINE_2 + +typedef struct _http_requestpool_object_t { + zend_object zo; + http_request_pool pool; + struct { + long pos; + } iterator; +} http_requestpool_object; + +extern zend_class_entry *http_requestpool_object_ce; +extern zend_function_entry http_requestpool_object_fe[]; + +extern PHP_MINIT_FUNCTION(http_requestpool_object); + +#define http_requestpool_object_new(ce) _http_requestpool_object_new(ce TSRMLS_CC) +extern zend_object_value _http_requestpool_object_new(zend_class_entry *ce TSRMLS_DC); +#define http_requestpool_object_free(o) _http_requestpool_object_free(o TSRMLS_CC) +extern void _http_requestpool_object_free(zend_object *object TSRMLS_DC); + +PHP_METHOD(HttpRequestPool, __construct); +PHP_METHOD(HttpRequestPool, __destruct); +PHP_METHOD(HttpRequestPool, attach); +PHP_METHOD(HttpRequestPool, detach); +PHP_METHOD(HttpRequestPool, send); +PHP_METHOD(HttpRequestPool, reset); +PHP_METHOD(HttpRequestPool, socketPerform); +PHP_METHOD(HttpRequestPool, socketSelect); +PHP_METHOD(HttpRequestPool, valid); +PHP_METHOD(HttpRequestPool, current); +PHP_METHOD(HttpRequestPool, key); +PHP_METHOD(HttpRequestPool, next); +PHP_METHOD(HttpRequestPool, rewind); +PHP_METHOD(HttpRequestPool, count); +PHP_METHOD(HttpRequestPool, getAttachedRequests); +PHP_METHOD(HttpRequestPool, getFinishedRequests); +PHP_METHOD(HttpRequestPool, enablePipelining); +PHP_METHOD(HttpRequestPool, enableEvents); + +#endif +#endif +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/php_http_response_object.h @@ -0,0 +1,67 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_response_object.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_RESPONSE_OBJECT_H +#define PHP_HTTP_RESPONSE_OBJECT_H +#ifdef ZEND_ENGINE_2 +#ifndef WONKY + +extern zend_class_entry *http_response_object_ce; +extern zend_function_entry http_response_object_fe[]; + +extern PHP_MINIT_FUNCTION(http_response_object); + +PHP_METHOD(HttpResponse, setHeader); +PHP_METHOD(HttpResponse, getHeader); +PHP_METHOD(HttpResponse, setETag); +PHP_METHOD(HttpResponse, getETag); +PHP_METHOD(HttpResponse, setLastModified); +PHP_METHOD(HttpResponse, getLastModified); +PHP_METHOD(HttpResponse, setContentDisposition); +PHP_METHOD(HttpResponse, getContentDisposition); +PHP_METHOD(HttpResponse, setContentType); +PHP_METHOD(HttpResponse, getContentType); +PHP_METHOD(HttpResponse, guessContentType); +PHP_METHOD(HttpResponse, setCache); +PHP_METHOD(HttpResponse, getCache); +PHP_METHOD(HttpResponse, setCacheControl); +PHP_METHOD(HttpResponse, getCacheControl); +PHP_METHOD(HttpResponse, setGzip); +PHP_METHOD(HttpResponse, getGzip); +PHP_METHOD(HttpResponse, setThrottleDelay); +PHP_METHOD(HttpResponse, getThrottleDelay); +PHP_METHOD(HttpResponse, setBufferSize); +PHP_METHOD(HttpResponse, getBufferSize); +PHP_METHOD(HttpResponse, setData); +PHP_METHOD(HttpResponse, getData); +PHP_METHOD(HttpResponse, setFile); +PHP_METHOD(HttpResponse, getFile); +PHP_METHOD(HttpResponse, setStream); +PHP_METHOD(HttpResponse, getStream); +PHP_METHOD(HttpResponse, send); +PHP_METHOD(HttpResponse, capture); + +#endif +#endif +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/php_http_send_api.h @@ -0,0 +1,91 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_send_api.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_SEND_API_H +#define PHP_HTTP_SEND_API_H + +typedef enum _http_send_mode_t { + SEND_DATA, + SEND_RSRC +} http_send_mode; + +#define HTTP_REDIRECT 0L +#define HTTP_REDIRECT_PERM 301L +#define HTTP_REDIRECT_FOUND 302L +#define HTTP_REDIRECT_POST 303L +#define HTTP_REDIRECT_PROXY 305L +#define HTTP_REDIRECT_TEMP 307L + +extern PHP_MINIT_FUNCTION(http_send); + +#define http_send_status(s) sapi_header_op(SAPI_HEADER_SET_STATUS, (void *) (long) (s) TSRMLS_CC) +#define http_send_header(n, v, r) _http_send_header_ex((n), strlen(n), (v), strlen(v), (r), NULL TSRMLS_CC) +#define http_send_header_ex(n, nl, v, vl, r, s) _http_send_header_ex((n), (nl), (v), (vl), (r), (s) TSRMLS_CC) +PHP_HTTP_API STATUS _http_send_header_ex(const char *name, size_t name_len, const char *value, size_t value_len, zend_bool replace, char **sent_header TSRMLS_DC); +#define http_send_header_string(h) _http_send_status_header_ex(0, (h), strlen(h), 1 TSRMLS_CC) +#define http_send_header_string_ex(h, l, r) _http_send_status_header_ex(0, (h), (l), (r) TSRMLS_CC) +#define http_send_status_header(s, h) _http_send_status_header_ex((s), (h), (h)?strlen(h):0, 1 TSRMLS_CC) +#define http_send_status_header_ex(s, h, l, r) _http_send_status_header_ex((s), (h), (l), (r) TSRMLS_CC) +PHP_HTTP_API STATUS _http_send_status_header_ex(int status, const char *header, size_t header_len, zend_bool replace TSRMLS_DC); + +#define http_send_header_zval(n, z, r) http_send_header_zval_ex((n), strlen(n), (z), (r)) +#define http_send_header_zval_ex(n, l, z, r) _http_send_header_zval_ex((n), (l), (z), (r) TSRMLS_CC) +PHP_HTTP_API void _http_send_header_zval_ex(const char *name, size_t name_len, zval **val, zend_bool replace TSRMLS_DC); + +#define http_hide_header(h) http_hide_header_ex((h), strlen(h)) +#define http_hide_header_ex(h, l) _http_hide_header_ex((h), (l) TSRMLS_CC) +PHP_HTTP_API void _http_hide_header_ex(const char *name, size_t name_len TSRMLS_DC); + +#define http_send_last_modified(t) _http_send_last_modified_ex((t), NULL TSRMLS_CC) +#define http_send_last_modified_ex(t, s) _http_send_last_modified_ex((t), (s) TSRMLS_CC) +PHP_HTTP_API STATUS _http_send_last_modified_ex(time_t t, char **sent_header TSRMLS_DC); + +#define http_send_etag(e, l) _http_send_etag_ex((e), (l), NULL TSRMLS_CC) +#define http_send_etag_ex(e, l, s) _http_send_etag_ex((e), (l), (s) TSRMLS_CC) +PHP_HTTP_API STATUS _http_send_etag_ex(const char *etag, size_t etag_len, char **sent_header TSRMLS_DC); + +#define http_send_cache_control(cc, cl) http_send_header_ex("Cache-Control", lenof("Cache-Control"), (cc), (cl), 1, NULL) + +#define http_send_content_type(c, l) _http_send_content_type((c), (l) TSRMLS_CC) +PHP_HTTP_API STATUS _http_send_content_type(const char *content_type, size_t ct_len TSRMLS_DC); + +#define http_send_content_disposition(f, l, i) _http_send_content_disposition((f), (l), (i) TSRMLS_CC) +PHP_HTTP_API STATUS _http_send_content_disposition(const char *filename, size_t f_len, zend_bool send_inline TSRMLS_DC); + +#define http_send_data(d, l) http_send((d), (l), SEND_DATA) +#define http_send_data_ex(d, l, nc) http_send_ex((d), (l), SEND_DATA, (nc)) +#define http_send(d, s, m) _http_send_ex((d), (s), (m), 0 TSRMLS_CC) +#define http_send_ex(d, s, m, nc) _http_send_ex((d), (s), (m), (nc) TSRMLS_CC) +PHP_HTTP_API STATUS _http_send_ex(const void *data, size_t data_size, http_send_mode mode, zend_bool no_cache TSRMLS_DC); + +#define http_send_file(f) http_send_stream_ex(php_stream_open_wrapper_ex(f, "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL, HTTP_DEFAULT_STREAM_CONTEXT), 1, 0) +#define http_send_file_ex(f, nc) http_send_stream_ex(php_stream_open_wrapper_ex(f, "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL, HTTP_DEFAULT_STREAM_CONTEXT), 1, (nc)) +#define http_send_stream(s) http_send_stream_ex((s), 0, 0) +#define http_send_stream_ex(s, c, nc) _http_send_stream_ex((s), (c), (nc) TSRMLS_CC) +PHP_HTTP_API STATUS _http_send_stream_ex(php_stream *s, zend_bool close_stream, zend_bool no_cache TSRMLS_DC); + +#define http_guess_content_type(mf, mm, d, l, m) _http_guess_content_type((mf), (mm), (d), (l), (m) TSRMLS_CC) +PHP_HTTP_API char *_http_guess_content_type(const char *magic_file, long magic_mode, void *data_ptr, size_t data_len, http_send_mode mode 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 + */ + --- /dev/null +++ b/ext/http/php_http_std_defs.h @@ -0,0 +1,406 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_std_defs.h 310777 2011-05-05 06:43:10Z mike $ */ + +#ifndef PHP_HTTP_STD_DEFS_H +#define PHP_HTTP_STD_DEFS_H + +#if defined(PHP_WIN32) +# if defined(HTTP_EXPORTS) +# define PHP_HTTP_API __declspec(dllexport) +# elif defined(COMPILE_DL_HTTP) +# define PHP_HTTP_API __declspec(dllimport) +# else +# define PHP_HTTP_API +# endif +#else +# define PHP_HTTP_API +#endif + +/* make functions that return SUCCESS|FAILURE more obvious */ +typedef int STATUS; + +/* lenof() */ +#define lenof(S) (sizeof(S) - 1) + +#ifndef MIN +# define MIN(a,b) (ab?a:b) +#endif + +/* STR_SET() */ +#ifndef STR_SET +# define STR_SET(STR, SET) \ + { \ + STR_FREE(STR); \ + STR = SET; \ + } +#endif + +#define STR_PTR(s) (s?s:"") + +#define INIT_ZARR(zv, ht) \ + { \ + INIT_PZVAL(&(zv)); \ + Z_TYPE(zv) = IS_ARRAY; \ + Z_ARRVAL(zv) = (ht); \ + } + +/* return bool (v == SUCCESS) */ +#define RETVAL_SUCCESS(v) RETVAL_BOOL(SUCCESS == (v)) +#define RETURN_SUCCESS(v) RETURN_BOOL(SUCCESS == (v)) +/* 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); \ + } +/* return property */ +#define RETVAL_PROP(n) RETVAL_PROP_EX(getThis(), n) +#define RETURN_PROP(n) RETURN_PROP_EX(getThis(), n) +#define RETVAL_PROP_EX(this, n) \ + { \ + zval *__prop = zend_read_property(THIS_CE, this, ZEND_STRS(#n)-1, 0 TSRMLS_CC); \ + RETVAL_ZVAL(__prop, 1, 0); \ + } +#define RETURN_PROP_EX(this, n) \ + { \ + zval *__prop = zend_read_property(THIS_CE, this, ZEND_STRS(#n)-1, 0 TSRMLS_CC); \ + RETURN_ZVAL(__prop, 1, 0); \ + } + +/* function accepts no args */ +#define NO_ARGS zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, ""); + +/* CR LF */ +#define HTTP_CRLF "\r\n" + +/* default cache control */ +#define HTTP_DEFAULT_CACHECONTROL "private, must-revalidate, max-age=0" + +/* max URL length */ +#define HTTP_URL_MAXLEN 4096 + +/* max request method length */ +#define HTTP_REQUEST_METHOD_MAXLEN 31 + +/* def URL arg separator */ +#define HTTP_URL_ARGSEP "&" + +/* send buffer size */ +#define HTTP_SENDBUF_SIZE 40960 + +/* CURL buffer size */ +#define HTTP_CURLBUF_SIZE 16384 + +/* known methods */ +#define HTTP_KNOWN_METHODS \ + /* HTTP 1.1 */ \ + "GET, HEAD, POST, PUT, DELETE, OPTIONS, TRACE, CONNECT, " \ + /* WebDAV - RFC 2518 */ \ + "PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK, " \ + /* WebDAV Versioning - RFC 3253 */ \ + "VERSION-CONTROL, REPORT, CHECKOUT, CHECKIN, UNCHECKOUT, " \ + "MKWORKSPACE, UPDATE, LABEL, MERGE, BASELINE-CONTROL, MKACTIVITY, " \ + /* WebDAV Access Control - RFC 3744 */ \ + "ACL, " \ + /* END */ + +#ifdef ZEND_ENGINE_2 +# include "ext/standard/file.h" +# define HTTP_DEFAULT_STREAM_CONTEXT FG(default_context) +#else +# define HTTP_DEFAULT_STREAM_CONTEXT NULL +#endif + +#define HTTP_PHP_INI_ENTRY(entry, default, scope, updater, global) \ + STD_PHP_INI_ENTRY(entry, default, scope, updater, global, zend_http_globals, http_globals) +#define HTTP_PHP_INI_ENTRY_EX(entry, default, scope, updater, displayer, global) \ + STD_PHP_INI_ENTRY_EX(entry, default, scope, updater, global, zend_http_globals, http_globals, displayer) + + +#define HTTP_LONG_CONSTANT(name, const) REGISTER_LONG_CONSTANT(name, const, CONST_CS | CONST_PERSISTENT) + +/* {{{ objects & properties */ +#ifdef ZEND_ENGINE_2 + +# define HTTP_STATIC_ME_ALIAS(me, al, ai) ZEND_FENTRY(me, ZEND_FN(al), ai, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + +# define HTTP_REGISTER_CLASS_EX(classname, name, parent, flags) \ + { \ + zend_class_entry ce; \ + memset(&ce, 0, sizeof(zend_class_entry)); \ + INIT_CLASS_ENTRY(ce, #classname, name## _fe); \ + ce.create_object = _ ##name## _new; \ + name## _ce = zend_register_internal_class_ex(&ce, parent, NULL TSRMLS_CC); \ + name## _ce->ce_flags |= flags; \ + memcpy(& name## _handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); \ + } + +# define HTTP_REGISTER_CLASS(classname, name, parent, flags) \ + { \ + zend_class_entry ce; \ + memset(&ce, 0, sizeof(zend_class_entry)); \ + INIT_CLASS_ENTRY(ce, #classname, name## _fe); \ + ce.create_object = NULL; \ + name## _ce = zend_register_internal_class_ex(&ce, parent, NULL TSRMLS_CC); \ + name## _ce->ce_flags |= flags; \ + } + +# define HTTP_REGISTER_EXCEPTION(classname, cename, parent) \ + { \ + zend_class_entry ce; \ + memset(&ce, 0, sizeof(zend_class_entry)); \ + INIT_CLASS_ENTRY(ce, #classname, NULL); \ + ce.create_object = NULL; \ + cename = zend_register_internal_class_ex(&ce, parent, NULL TSRMLS_CC); \ + } + +# define getObject(t, o) getObjectEx(t, o, getThis()) +# define getObjectEx(t, o, v) t * o = ((t *) zend_object_store_get_object(v TSRMLS_CC)) +# define putObject(t, o) zend_objects_store_put(o, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) _ ##t## _free, NULL TSRMLS_CC); +# ifndef WONKY +# define freeObject(o) \ + if (OBJ_GUARDS(o)) { \ + zend_hash_destroy(OBJ_GUARDS(o)); \ + FREE_HASHTABLE(OBJ_GUARDS(o)); \ + } \ + if (OBJ_PROP(o)) { \ + zend_hash_destroy(OBJ_PROP(o)); \ + FREE_HASHTABLE(OBJ_PROP(o)); \ + } \ + efree(o); +# else +# define freeObject(o) \ + if (OBJ_PROP(o)) { \ + zend_hash_destroy(OBJ_PROP(o)); \ + FREE_HASHTABLE(OBJ_PROP(o)); \ + } \ + efree(o); +# endif +# define OBJ_PROP(o) (o)->zo.properties +# define OBJ_GUARDS(o) (o)->zo.guards + +# define ACC_PROP_PRIVATE(ce, flags) ((flags & ZEND_ACC_PRIVATE) && (EG(scope) && ce == EG(scope)) +# define ACC_PROP_PROTECTED(ce, flags) ((flags & ZEND_ACC_PROTECTED) && (zend_check_protected(ce, EG(scope)))) +# define ACC_PROP_PUBLIC(flags) (flags & ZEND_ACC_PUBLIC) +# define ACC_PROP(ce, flags) (ACC_PROP_PUBLIC(flags) || ACC_PROP_PRIVATE(ce, flags) || ACC_PROP_PROTECTED(ce, flags)) + +# define SET_EH_THROW_HTTP() SET_EH_THROW_EX(http_exception_get_default()) +# define SET_EH_THROW_EX(ex) php_set_error_handling(EH_THROW, ex TSRMLS_CC) +# define SET_EH_NORMAL() php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC) + +#endif /* ZEND_ENGINE_2 */ +/* }}} */ + +#ifdef ZEND_ENGINE_2 +# define with_error_handling(eh, ec) \ + { \ + error_handling_t __eh = GLOBAL_ERROR_HANDLING; \ + zend_class_entry *__ec= GLOBAL_EXCEPTION_CLASS; \ + php_set_error_handling(eh, ec TSRMLS_CC); +# define end_error_handling() \ + php_set_error_handling(__eh, __ec TSRMLS_CC); \ + } +#else +# define with_error_handling(eh, ec) +# define end_error_handling() +#endif + +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 2) || PHP_MAJOR_VERSION > 5 +# define ZEND_EXCEPTION_GET_DEFAULT() zend_exception_get_default(TSRMLS_C) +#else +# define ZEND_EXCEPTION_GET_DEFAULT() zend_exception_get_default() +#endif + +#ifndef E_THROW +# define E_THROW 0 +#endif +#ifdef ZEND_ENGINE_2 +# define HE_THROW E_THROW TSRMLS_CC +# define HE_NOTICE (HTTP_G->only_exceptions ? E_THROW : E_NOTICE) TSRMLS_CC +# define HE_WARNING (HTTP_G->only_exceptions ? E_THROW : E_WARNING) TSRMLS_CC +# define HE_ERROR (HTTP_G->only_exceptions ? E_THROW : E_ERROR) TSRMLS_CC +#else +# define HE_THROW E_WARNING TSRMLS_CC +# define HE_NOTICE E_NOTICE TSRMLS_CC +# define HE_WARNING E_WARNING TSRMLS_CC +# define HE_ERROR E_ERROR TSRMLS_CC +#endif + +#define HTTP_E_RUNTIME 1L +#define HTTP_E_INVALID_PARAM 2L +#define HTTP_E_HEADER 3L +#define HTTP_E_MALFORMED_HEADERS 4L +#define HTTP_E_REQUEST_METHOD 5L +#define HTTP_E_MESSAGE_TYPE 6L +#define HTTP_E_ENCODING 7L +#define HTTP_E_REQUEST 8L +#define HTTP_E_REQUEST_POOL 9L +#define HTTP_E_SOCKET 10L +#define HTTP_E_RESPONSE 11L +#define HTTP_E_URL 12L +#define HTTP_E_QUERYSTRING 13L + +#ifdef ZEND_ENGINE_2 +# define HTTP_BEGIN_ARGS_EX(class, method, ret_ref, req_args) HTTP_STATIC_ARG_INFO ZEND_BEGIN_ARG_INFO_EX(args_for_ ##class## _ ##method , 0, ret_ref, req_args) +# define HTTP_BEGIN_ARGS_AR(class, method, ret_ref, req_args) HTTP_STATIC_ARG_INFO ZEND_BEGIN_ARG_INFO_EX(args_for_ ##class## _ ##method , 1, ret_ref, req_args) +# define HTTP_END_ARGS } +# define HTTP_EMPTY_ARGS_EX(class, method, ret_ref) HTTP_BEGIN_ARGS_EX(class, method, ret_ref, 0) HTTP_END_ARGS +# define HTTP_ARGS(class, method) args_for_ ##class## _ ##method +# define HTTP_ARG_VAL(name, pass_ref) ZEND_ARG_INFO(pass_ref, name) +# define HTTP_ARG_OBJ(class, name, allow_null) ZEND_ARG_OBJ_INFO(0, name, class, allow_null) +#endif + +#ifdef ZEND_ENGINE_2 +# define EMPTY_FUNCTION_ENTRY {NULL, NULL, NULL, 0, 0} +#else +# define EMPTY_FUNCTION_ENTRY {NULL, NULL, NULL} +#endif + +#ifdef HTTP_HAVE_CURL +# ifdef ZEND_ENGINE_2 +# define HTTP_DECLARE_ARG_PASS_INFO() \ + HTTP_STATIC_ARG_INFO \ + ZEND_BEGIN_ARG_INFO(http_arg_pass_ref_2, 0) \ + ZEND_ARG_PASS_INFO(0) \ + ZEND_ARG_PASS_INFO(1) \ + ZEND_END_ARG_INFO(); \ + \ + HTTP_STATIC_ARG_INFO \ + ZEND_BEGIN_ARG_INFO(http_arg_pass_ref_3, 0) \ + ZEND_ARG_PASS_INFO(0) \ + ZEND_ARG_PASS_INFO(0) \ + ZEND_ARG_PASS_INFO(1) \ + ZEND_END_ARG_INFO(); \ + \ + HTTP_STATIC_ARG_INFO \ + ZEND_BEGIN_ARG_INFO(http_arg_pass_ref_4, 0) \ + ZEND_ARG_PASS_INFO(0) \ + ZEND_ARG_PASS_INFO(0) \ + ZEND_ARG_PASS_INFO(0) \ + ZEND_ARG_PASS_INFO(1) \ + ZEND_END_ARG_INFO(); \ + \ + HTTP_STATIC_ARG_INFO \ + ZEND_BEGIN_ARG_INFO(http_arg_pass_ref_5, 0) \ + ZEND_ARG_PASS_INFO(0) \ + ZEND_ARG_PASS_INFO(0) \ + ZEND_ARG_PASS_INFO(0) \ + ZEND_ARG_PASS_INFO(0) \ + ZEND_ARG_PASS_INFO(1) \ + ZEND_END_ARG_INFO(); + +# else +# define HTTP_DECLARE_ARG_PASS_INFO() \ + static unsigned char http_arg_pass_ref_2[] = {2, BYREF_NONE, BYREF_FORCE}; \ + static unsigned char http_arg_pass_ref_3[] = {3, BYREF_NONE, BYREF_NONE, BYREF_FORCE}; \ + static unsigned char http_arg_pass_ref_4[] = {4, BYREF_NONE, BYREF_NONE, BYREF_NONE, BYREF_FORCE}; \ + static unsigned char http_arg_pass_ref_5[] = {5, BYREF_NONE, BYREF_NONE, BYREF_NONE, BYREF_NONE, BYREF_FORCE}; +# endif /* ZEND_ENGINE_2 */ +#else +# ifdef ZEND_ENGINE_2 +# define HTTP_DECLARE_ARG_PASS_INFO() \ + HTTP_STATIC_ARG_INFO \ + ZEND_BEGIN_ARG_INFO(http_arg_pass_ref_2, 0) \ + ZEND_ARG_PASS_INFO(0) \ + ZEND_ARG_PASS_INFO(1) \ + ZEND_END_ARG_INFO(); \ +\ + HTTP_STATIC_ARG_INFO \ + ZEND_BEGIN_ARG_INFO(http_arg_pass_ref_3, 0) \ + ZEND_ARG_PASS_INFO(0) \ + ZEND_ARG_PASS_INFO(0) \ + ZEND_ARG_PASS_INFO(1) \ + ZEND_END_ARG_INFO(); \ +\ + HTTP_STATIC_ARG_INFO \ + ZEND_BEGIN_ARG_INFO(http_arg_pass_ref_4, 0) \ + ZEND_ARG_PASS_INFO(0) \ + ZEND_ARG_PASS_INFO(0) \ + ZEND_ARG_PASS_INFO(0) \ + ZEND_ARG_PASS_INFO(1) \ + ZEND_END_ARG_INFO(); +# else +# define HTTP_DECLARE_ARG_PASS_INFO() \ + static unsigned char http_arg_pass_ref_2[] = {2, BYREF_NONE, BYREF_FORCE}; \ + static unsigned char http_arg_pass_ref_3[] = {3, BYREF_NONE, BYREF_NONE, BYREF_FORCE}; \ + static unsigned char http_arg_pass_ref_4[] = {4, BYREF_NONE, BYREF_NONE, BYREF_NONE, BYREF_FORCE}; +# endif /* ZEND_ENGINE_2 */ +#endif /* HTTP_HAVE_CURL */ + + +#ifndef HAVE_CURL_SHARE_STRERROR +# define curl_share_strerror(dummy) "unknown error" +#endif +#ifndef HAVE_CURL_EASY_STRERROR +# define curl_easy_strerror(dummy) "unknown error" +#endif +#ifndef HAVE_CURL_MULTI_STRERROR +# define curl_multi_strerror(dummy) "unknown error" +#endif + +#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) + +#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); \ + } + +#endif /* PHP_HTTP_STD_DEFS_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 + */ + --- /dev/null +++ b/ext/http/php_http_url_api.h @@ -0,0 +1,165 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_url_api.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_URL_API_H +#define PHP_HTTP_URL_API_H + +#include "ext/standard/url.h" + +extern PHP_MINIT_FUNCTION(http_url); + +#define http_absolute_url(u) _http_absolute_url_ex((u), HTTP_URL_REPLACE TSRMLS_CC) +#define http_absolute_url_ex(u, f) _http_absolute_url_ex((u), (f) TSRMLS_CC) +PHP_HTTP_API char *_http_absolute_url_ex(const char *url, int flags TSRMLS_DC); + +#define HTTP_URL_REPLACE 0x000 +#define HTTP_URL_JOIN_PATH 0x001 +#define HTTP_URL_JOIN_QUERY 0x002 +#define HTTP_URL_STRIP_USER 0x004 +#define HTTP_URL_STRIP_PASS 0x008 +#define HTTP_URL_STRIP_AUTH (HTTP_URL_STRIP_USER|HTTP_URL_STRIP_PASS) +#define HTTP_URL_STRIP_PORT 0x020 +#define HTTP_URL_STRIP_PATH 0x040 +#define HTTP_URL_STRIP_QUERY 0x080 +#define HTTP_URL_STRIP_FRAGMENT 0x100 +#define HTTP_URL_STRIP_ALL ( \ + HTTP_URL_STRIP_AUTH | \ + HTTP_URL_STRIP_PORT | \ + HTTP_URL_STRIP_PATH | \ + HTTP_URL_STRIP_QUERY | \ + HTTP_URL_STRIP_FRAGMENT \ +) +#define HTTP_URL_FROM_ENV 0x1000 + +#define http_build_url(f, o, n, p, s, l) _http_build_url((f), (o), (n), (p), (s), (l) TSRMLS_CC) +PHP_HTTP_API void _http_build_url(int flags, const php_url *old_url, const php_url *new_url, php_url **url_ptr, char **url_str, size_t *url_len TSRMLS_DC); + +#define http_urlencode_hash(h, q) _http_urlencode_hash_ex((h), 1, NULL, 0, (q), NULL TSRMLS_CC) +#define http_urlencode_hash_ex(h, o, p, pl, q, ql) _http_urlencode_hash_ex((h), (o), (p), (pl), (q), (ql) TSRMLS_CC) +PHP_HTTP_API STATUS _http_urlencode_hash_ex(HashTable *hash, zend_bool override_argsep, char *pre_encoded_data, size_t pre_encoded_len, char **encoded_data, size_t *encoded_len TSRMLS_DC); + +#define http_urlencode_hash_recursive(ht, s, as, al, pr, pl) _http_urlencode_hash_recursive((ht), (s), (as), (al), (pr), (pl) TSRMLS_CC) +PHP_HTTP_API STATUS _http_urlencode_hash_recursive(HashTable *ht, phpstr *str, const char *arg_sep, size_t arg_sep_len, const char *prefix, size_t prefix_len TSRMLS_DC); + +#define http_url_from_struct(u, ht) _http_url_from_struct((u), (ht) TSRMLS_CC) +static inline php_url *_http_url_from_struct(php_url *url, HashTable *ht TSRMLS_DC) +{ + zval **e; + + if (!url) { + url = ecalloc(1, sizeof(php_url)); + } + + if ((SUCCESS == zend_hash_find(ht, "scheme", sizeof("scheme"), (void *) &e)) + && (Z_TYPE_PP(e) == IS_STRING) && Z_STRLEN_PP(e)) { + url->scheme = estrndup(Z_STRVAL_PP(e), Z_STRLEN_PP(e)); + } + if ((SUCCESS == zend_hash_find(ht, "user", sizeof("user"), (void *) &e)) + && (Z_TYPE_PP(e) == IS_STRING) && Z_STRLEN_PP(e)) { + url->user = estrndup(Z_STRVAL_PP(e), Z_STRLEN_PP(e)); + } + if ((SUCCESS == zend_hash_find(ht, "pass", sizeof("pass"), (void *) &e)) + && (Z_TYPE_PP(e) == IS_STRING) && Z_STRLEN_PP(e)) { + url->pass = estrndup(Z_STRVAL_PP(e), Z_STRLEN_PP(e)); + } + if ((SUCCESS == zend_hash_find(ht, "host", sizeof("host"), (void *) &e)) + && (Z_TYPE_PP(e) == IS_STRING) && Z_STRLEN_PP(e)) { + url->host = estrndup(Z_STRVAL_PP(e), Z_STRLEN_PP(e)); + } + if ((SUCCESS == zend_hash_find(ht, "path", sizeof("path"), (void *) &e)) + && (Z_TYPE_PP(e) == IS_STRING) && Z_STRLEN_PP(e)) { + url->path = estrndup(Z_STRVAL_PP(e), Z_STRLEN_PP(e)); + } + if ((SUCCESS == zend_hash_find(ht, "query", sizeof("query"), (void *) &e)) + && (Z_TYPE_PP(e) == IS_STRING) && Z_STRLEN_PP(e)) { + url->query = estrndup(Z_STRVAL_PP(e), Z_STRLEN_PP(e)); + } + if ((SUCCESS == zend_hash_find(ht, "fragment", sizeof("fragment"), (void *) &e)) + && (Z_TYPE_PP(e) == IS_STRING) && Z_STRLEN_PP(e)) { + url->fragment = estrndup(Z_STRVAL_PP(e), Z_STRLEN_PP(e)); + } + if (SUCCESS == zend_hash_find(ht, "port", sizeof("port"), (void *) &e)) { + if (Z_TYPE_PP(e) == IS_LONG) { + url->port = (unsigned short) Z_LVAL_PP(e); + } else { + zval *o = http_zsep(IS_LONG, *e); + + url->port = (unsigned short) Z_LVAL_P(o); + zval_ptr_dtor(&o); + } + } + + return url; +} + +#define http_url_tostruct(u, strct) _http_url_tostruct((u), (strct) TSRMLS_CC) +static inline HashTable *_http_url_tostruct(php_url *url, zval *strct TSRMLS_DC) +{ + zval arr; + + if (strct) { + switch (Z_TYPE_P(strct)) { + default: + zval_dtor(strct); + array_init(strct); + case IS_ARRAY: + case IS_OBJECT: + INIT_ZARR(arr, HASH_OF(strct)); + } + } 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); +} + +#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 + */ + --- /dev/null +++ b/ext/http/php_http_util_object.h @@ -0,0 +1,35 @@ +/* + +--------------------------------------------------------------------+ + | 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-2010, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id: php_http_util_object.h 292841 2009-12-31 08:48:57Z mike $ */ + +#ifndef PHP_HTTP_UTIL_OBJECT_H +#define PHP_HTTP_UTIL_OBJECT_H +#ifdef ZEND_ENGINE_2 + +extern zend_class_entry *http_util_object_ce; +extern zend_function_entry http_util_object_fe[]; + +extern PHP_MINIT_FUNCTION(http_util_object); + +#endif +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/phpstr/phpstr.c @@ -0,0 +1,379 @@ + +/* $Id: phpstr.c 211942 2006-04-24 17:17:09Z mike $ */ + +#include "php.h" +#include "phpstr.h" + +PHPSTR_API phpstr *phpstr_init_ex(phpstr *buf, size_t chunk_size, int flags) +{ + if (!buf) { + buf = pemalloc(sizeof(phpstr), flags & PHPSTR_INIT_PERSISTENT); + } + + if (buf) { + buf->size = (chunk_size) ? chunk_size : PHPSTR_DEFAULT_SIZE; + buf->pmem = (flags & PHPSTR_INIT_PERSISTENT) ? 1 : 0; + buf->data = (flags & PHPSTR_INIT_PREALLOC) ? pemalloc(buf->size, buf->pmem) : NULL; + buf->free = (flags & PHPSTR_INIT_PREALLOC) ? buf->size : 0; + buf->used = 0; + } + + return buf; +} + +PHPSTR_API phpstr *phpstr_from_string_ex(phpstr *buf, const char *string, size_t length) +{ + if ((buf = phpstr_init(buf))) { + if (PHPSTR_NOMEM == phpstr_append(buf, string, length)) { + pefree(buf, buf->pmem); + buf = NULL; + } + } + return buf; +} + +PHPSTR_API size_t phpstr_resize_ex(phpstr *buf, size_t len, size_t override_size, int allow_error) +{ + char *ptr = NULL; +#if 0 + fprintf(stderr, "RESIZE: size=%lu, used=%lu, free=%lu\n", buf->size, buf->used, buf->free); +#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 PHPSTR_NOMEM; + } + + buf->free += size; + return size; + } + return 0; +} + +PHPSTR_API size_t phpstr_shrink(phpstr *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 PHPSTR_NOMEM; + } + buf->free = 1; + } + return buf->used; +} + +PHPSTR_API size_t phpstr_append(phpstr *buf, const char *append, size_t append_len) +{ + if (PHPSTR_NOMEM == phpstr_resize(buf, append_len)) { + return PHPSTR_NOMEM; + } + memcpy(buf->data + buf->used, append, append_len); + buf->used += append_len; + buf->free -= append_len; + return append_len; +} + +PHPSTR_API size_t phpstr_appendf(phpstr *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 = phpstr_append(buf, append, append_len); + efree(append); + + if (PHPSTR_NOMEM == alloc) { + return PHPSTR_NOMEM; + } + return append_len; +} + +PHPSTR_API size_t phpstr_insert(phpstr *buf, const char *insert, size_t insert_len, size_t offset) +{ + if (PHPSTR_NOMEM == phpstr_resize(buf, insert_len)) { + return PHPSTR_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; +} + +PHPSTR_API size_t phpstr_insertf(phpstr *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 = phpstr_insert(buf, insert, insert_len, offset); + efree(insert); + + if (PHPSTR_NOMEM == alloc) { + return PHPSTR_NOMEM; + } + return insert_len; +} + +PHPSTR_API size_t phpstr_prepend(phpstr *buf, const char *prepend, size_t prepend_len) +{ + if (PHPSTR_NOMEM == phpstr_resize(buf, prepend_len)) { + return PHPSTR_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; +} + +PHPSTR_API size_t phpstr_prependf(phpstr *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 = phpstr_prepend(buf, prepend, prepend_len); + efree(prepend); + + if (PHPSTR_NOMEM == alloc) { + return PHPSTR_NOMEM; + } + return prepend_len; +} + +PHPSTR_API char *phpstr_data(const phpstr *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; +} + +PHPSTR_API phpstr *phpstr_dup(const phpstr *buf) +{ + phpstr *dup = phpstr_clone(buf); + if (PHPSTR_NOMEM == phpstr_append(dup, buf->data, buf->used)) { + phpstr_free(&dup); + } + return dup; +} + +PHPSTR_API size_t phpstr_cut(phpstr *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); + buf->used -= length; + buf->free += length; + return length; +} + +PHPSTR_API phpstr *phpstr_sub(const phpstr *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)); + phpstr *sub = phpstr_init_ex(NULL, need, PHPSTR_INIT_PREALLOC | (buf->pmem ? PHPSTR_INIT_PERSISTENT:0)); + if (sub) { + if (PHPSTR_NOMEM == phpstr_append(sub, buf->data + offset, need)) { + phpstr_free(&sub); + } else { + sub->size = buf->size; + } + } + return sub; + } +} + +PHPSTR_API phpstr *phpstr_right(const phpstr *buf, size_t length) +{ + if (length < buf->used) { + return phpstr_sub(buf, buf->used - length, length); + } else { + return phpstr_sub(buf, 0, buf->used); + } +} + + +PHPSTR_API phpstr *phpstr_merge_va(phpstr *buf, unsigned argc, va_list argv) +{ + unsigned i = 0; + buf = phpstr_init(buf); + + if (buf) { + while (argc > i++) { + phpstr_free_t f = va_arg(argv, phpstr_free_t); + phpstr *current = va_arg(argv, phpstr *); + phpstr_append(buf, current->data, current->used); + FREE_PHPSTR(f, current); + } + } + + return buf; +} + +PHPSTR_API phpstr *phpstr_merge_ex(phpstr *buf, unsigned argc, ...) +{ + va_list argv; + phpstr *ret; + + va_start(argv, argc); + ret = phpstr_merge_va(buf, argc, argv); + va_end(argv); + return ret; +} + +PHPSTR_API phpstr *phpstr_merge(unsigned argc, ...) +{ + va_list argv; + phpstr *ret; + + va_start(argv, argc); + ret = phpstr_merge_va(NULL, argc, argv); + va_end(argv); + return ret; +} + +PHPSTR_API phpstr *phpstr_fix(phpstr *buf) +{ + if (PHPSTR_NOMEM == phpstr_resize_ex(buf, 1, 1, 0)) { + return NULL; + } + buf->data[buf->used] = '\0'; + return buf; +} + +PHPSTR_API int phpstr_cmp(phpstr *left, phpstr *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); + } +} + +PHPSTR_API void phpstr_reset(phpstr *buf) +{ + buf->free += buf->used; + buf->used = 0; +} + +PHPSTR_API void phpstr_dtor(phpstr *buf) +{ + if (buf->data) { + pefree(buf->data, buf->pmem); + buf->data = NULL; + } + buf->used = 0; + buf->free = 0; +} + +PHPSTR_API void phpstr_free(phpstr **buf) +{ + if (*buf) { + phpstr_dtor(*buf); + pefree(*buf, (*buf)->pmem); + *buf = NULL; + } +} + +PHPSTR_API size_t phpstr_chunk_buffer(phpstr **s, const char *data, size_t data_len, char **chunk, size_t chunk_size) +{ + phpstr *storage; + + *chunk = NULL; + + if (!*s) { + *s = phpstr_init_ex(NULL, chunk_size << 1, chunk_size ? PHPSTR_INIT_PREALLOC : 0); + } + storage = *s; + + if (data_len) { + phpstr_append(storage, data, data_len); + } + + if (!chunk_size) { + phpstr_data(storage, chunk, &chunk_size); + phpstr_free(s); + return chunk_size; + } + + if (storage->used >= (chunk_size = storage->size >> 1)) { + *chunk = estrndup(storage->data, chunk_size); + phpstr_cut(storage, 0, chunk_size); + return chunk_size; + } + + return 0; +} + +PHPSTR_API void phpstr_chunked_output(phpstr **s, const char *data, size_t data_len, size_t chunk_len, phpstr_passthru_func passthru, void *opaque TSRMLS_DC) +{ + char *chunk = NULL; + size_t got = 0; + + while ((got = phpstr_chunk_buffer(s, data, data_len, &chunk, chunk_len))) { + passthru(opaque, chunk, got TSRMLS_CC); + if (!chunk_len) { + /* we already got the last chunk, + and freed all resources */ + break; + } + data = NULL; + data_len = 0; + STR_SET(chunk, NULL); + } + STR_FREE(chunk); +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ + --- /dev/null +++ b/ext/http/phpstr/phpstr.h @@ -0,0 +1,224 @@ + +/* $Id: phpstr.h 229282 2007-02-07 15:31:50Z mike $ */ + +#ifndef _PHPSTR_H_ +#define _PHPSTR_H_ + +#ifndef PHPSTR_DEFAULT_SIZE +# define PHPSTR_DEFAULT_SIZE 256 +#endif + +#define PHPSTR_NOMEM ((size_t) -1) + +#ifndef STR_FREE +# define STR_FREE(STR) \ + { \ + if (STR) { \ + efree(STR); \ + } \ + } +#endif +#ifndef STR_SET +# define STR_SET(STR, SET) \ + { \ + STR_FREE(STR); \ + STR = SET; \ + } +#endif +#ifndef TSRMLS_D +# define TSRMLS_D +# define TSRMLS_DC +# define TSRMLS_CC +# define TSRMLS_C +#endif +#ifdef PHP_ATTRIBUTE_FORMAT +# define PHPSTR_ATTRIBUTE_FORMAT(f, a, b) PHP_ATTRIBUTE_FORMAT(f, a, b) +#else +# define PHPSTR_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(PHPSTR_EXPORTS) +# define PHPSTR_API __declspec(dllexport) +# elif defined(COMPILE_DL_PHPSTR) +# define PHPSTR_API __declspec(dllimport) +# else +# define PHPSTR_API +# endif +#else +# define PHPSTR_API +#endif + +#define PHPSTR(p) ((phpstr *) (p)) +#define PHPSTR_VAL(p) (PHPSTR(p))->data +#define PHPSTR_LEN(p) (PHPSTR(p))->used + +#define FREE_PHPSTR_PTR(STR) pefree(STR, STR->pmem) +#define FREE_PHPSTR_VAL(STR) phpstr_dtor(STR) +#define FREE_PHPSTR_ALL(STR) phpstr_free(&(STR)) +#define FREE_PHPSTR(free, STR) \ + switch (free) \ + { \ + case PHPSTR_FREE_NOT: break; \ + case PHPSTR_FREE_PTR: pefree(STR, STR->pmem); break; \ + case PHPSTR_FREE_VAL: phpstr_dtor(STR); break; \ + case PHPSTR_FREE_ALL: \ + { \ + phpstr *PTR = (STR); \ + phpstr_free(&PTR); \ + } \ + break; \ + default: break; \ + } + +#define RETURN_PHPSTR_PTR(STR) RETURN_PHPSTR((STR), PHPSTR_FREE_PTR, 0) +#define RETURN_PHPSTR_VAL(STR) RETURN_PHPSTR((STR), PHPSTR_FREE_NOT, 0) +#define RETURN_PHPSTR_DUP(STR) RETURN_PHPSTR((STR), PHPSTR_FREE_NOT, 1) +#define RETVAL_PHPSTR_PTR(STR) RETVAL_PHPSTR((STR), PHPSTR_FREE_PTR, 0) +#define RETVAL_PHPSTR_VAL(STR) RETVAL_PHPSTR((STR), PHPSTR_FREE_NOT, 0) +#define RETVAL_PHPSTR_DUP(STR) RETVAL_PHPSTR((STR), PHPSTR_FREE_NOT, 1) +/* RETURN_PHPSTR(buf, PHPSTR_FREE_PTR, 0) */ +#define RETURN_PHPSTR(STR, free, dup) \ + RETVAL_PHPSTR((STR), (free), (dup)); \ + return; + +#define RETVAL_PHPSTR(STR, free, dup) \ + phpstr_fix(STR); \ + RETVAL_STRINGL((STR)->data, (STR)->used, (dup)); \ + FREE_PHPSTR((free), (STR)); + +typedef struct _phpstr_t { + char *data; + size_t used; + size_t free; + size_t size; + unsigned pmem:1; + unsigned reserved:31; +} phpstr; + +typedef enum _phpstr_free_t { + PHPSTR_FREE_NOT = 0, + PHPSTR_FREE_PTR, /* pefree() */ + PHPSTR_FREE_VAL, /* phpstr_dtor() */ + PHPSTR_FREE_ALL /* phpstr_free() */ +} phpstr_free_t; + +#define PHPSTR_ALL_FREE(STR) PHPSTR_FREE_ALL,(STR) +#define PHPSTR_PTR_FREE(STR) PHPSTR_FREE_PTR,(STR) +#define PHPSTR_VAL_FREE(STR) PHPSTR_FREE_VAL,(STR) +#define PHPSTR_NOT_FREE(STR) PHPSTR_FREE_NOT,(STR) + +#define PHPSTR_INIT_PREALLOC 0x01 +#define PHPSTR_INIT_PERSISTENT 0x02 + +/* create a new phpstr */ +#define phpstr_new() phpstr_init(NULL) +#define phpstr_init(b) phpstr_init_ex(b, PHPSTR_DEFAULT_SIZE, 0) +#define phpstr_clone(phpstr_pointer) phpstr_init_ex(NULL, (phpstr_pointer)->size, (phpstr_pointer)->pmem ? PHPSTR_INIT_PERSISTENT:0) +PHPSTR_API phpstr *phpstr_init_ex(phpstr *buf, size_t chunk_size, int flags); + +/* create a phpstr from a zval or c-string */ +#define phpstr_from_zval(z) phpstr_from_string(Z_STRVAL(z), Z_STRLEN(z)) +#define phpstr_from_zval_ex(b, z) phpstr_from_string_ex(b, Z_STRVAL(z), Z_STRLEN(z)) +#define phpstr_from_string(s, l) phpstr_from_string_ex(NULL, (s), (l)) +PHPSTR_API phpstr *phpstr_from_string_ex(phpstr *buf, const char *string, size_t length); + +/* usually only called from within the internal functions */ +#define phpstr_resize(b, s) phpstr_resize_ex((b), (s), 0, 0) +PHPSTR_API size_t phpstr_resize_ex(phpstr *buf, size_t len, size_t override_size, int allow_error); + +/* shrink memory chunk to actually used size (+1) */ +PHPSTR_API size_t phpstr_shrink(phpstr *buf); + +/* append data to the phpstr */ +#define phpstr_appends(b, a) phpstr_append((b), (a), sizeof(a)-1) +#define phpstr_appendl(b, a) phpstr_append((b), (a), strlen(a)) +PHPSTR_API size_t phpstr_append(phpstr *buf, const char *append, size_t append_len); +PHPSTR_API size_t phpstr_appendf(phpstr *buf, const char *format, ...) PHPSTR_ATTRIBUTE_FORMAT(printf, 2, 3); + +/* insert data at a specific position of the phpstr */ +#define phpstr_inserts(b, i, o) phpstr_insert((b), (i), sizeof(i)-1, (o)) +#define phpstr_insertl(b, i, o) phpstr_insert((b), (i), strlen(i), (o)) +PHPSTR_API size_t phpstr_insert(phpstr *buf, const char *insert, size_t insert_len, size_t offset); +PHPSTR_API size_t phpstr_insertf(phpstr *buf, size_t offset, const char *format, ...) PHPSTR_ATTRIBUTE_FORMAT(printf, 3, 4); + +/* prepend data */ +#define phpstr_prepends(b, p) phpstr_prepend((b), (p), sizeof(p)-1) +#define phpstr_prependl(b, p) phpstr_prepend((b), (p), strlen(p)) +PHPSTR_API size_t phpstr_prepend(phpstr *buf, const char *prepend, size_t prepend_len); +PHPSTR_API size_t phpstr_prependf(phpstr *buf, const char *format, ...) PHPSTR_ATTRIBUTE_FORMAT(printf, 2, 3); + +/* get a zero-terminated string */ +PHPSTR_API char *phpstr_data(const phpstr *buf, char **into, size_t *len); + +/* get a part of the phpstr */ +#define phpstr_mid(b, o, l) phpstr_sub((b), (o), (l)) +#define phpstr_left(b, l) phpstr_sub((b), 0, (l)) +PHPSTR_API phpstr *phpstr_right(const phpstr *buf, size_t length); +PHPSTR_API phpstr *phpstr_sub(const phpstr *buf, size_t offset, size_t len); + +/* remove a substring */ +PHPSTR_API size_t phpstr_cut(phpstr *buf, size_t offset, size_t length); + +/* get a complete phpstr duplicate */ +PHPSTR_API phpstr *phpstr_dup(const phpstr *buf); + +/* merge several phpstr objects + use like: + + phpstr *final = phpstr_merge(3, + PHPSTR_NOT_FREE(&keep), + PHPSTR_ALL_FREE(middle_ptr), + PHPSTR_VAL_FREE(&local); +*/ +PHPSTR_API phpstr *phpstr_merge(unsigned argc, ...); +PHPSTR_API phpstr *phpstr_merge_ex(phpstr *buf, unsigned argc, ...); +PHPSTR_API phpstr *phpstr_merge_va(phpstr *buf, unsigned argc, va_list argv); + +/* sets a trailing NUL byte */ +PHPSTR_API phpstr *phpstr_fix(phpstr *buf); + +/* memcmp for phpstr objects */ +PHPSTR_API int phpstr_cmp(phpstr *left, phpstr *right); + +/* reset phpstr object */ +PHPSTR_API void phpstr_reset(phpstr *buf); + +/* free a phpstr objects contents */ +PHPSTR_API void phpstr_dtor(phpstr *buf); + +/* free a phpstr object completely */ +PHPSTR_API void phpstr_free(phpstr **buf); + +/* stores data in a phpstr until it reaches chunk_size */ +PHPSTR_API size_t phpstr_chunk_buffer(phpstr **s, const char *data, size_t data_len, char **chunk, size_t chunk_size); + +typedef void (*phpstr_passthru_func)(void *opaque, const char *, size_t TSRMLS_DC); + +/* wrapper around phpstr_chunk_buffer, which passes available chunks to passthru() */ +PHPSTR_API void phpstr_chunked_output(phpstr **s, const char *data, size_t data_len, size_t chunk_size, phpstr_passthru_func passthru, void *opaque TSRMLS_DC); + +#endif + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ --- /dev/null +++ b/ext/http/tests/HttpMessage_001.phpt @@ -0,0 +1,70 @@ +--TEST-- +HttpMessage +--SKIPIF-- + +--FILE-- +getResponseStatus()); + +$x = $m->getParentMessage(); +$x = $m->getParentMessage(); +$x = $m->getParentMessage(); + +var_dump($m->getBody()); +var_dump(HttpMessage::fromString($m->toString(true))->toString(true)); +try { + do { + var_dump($m->toString()); + } while ($m = $m->getParentMessage()); +} catch (HttpException $ex) { +} + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +string(15) "Partial content" +string(1) "X" +string(190) "HTTP/1.1 301 +Location: /anywhere +HTTP/1.1 302 +Location: /somewhere +HTTP/1.1 206 Partial content +Content-Range: bytes=2-3 +X-Original-Transfer-Encoding: chunked +Content-Length: 1 + +X +" +string(119) "HTTP/1.1 206 Partial content +Content-Range: bytes=2-3 +X-Original-Transfer-Encoding: chunked +Content-Length: 1 + +X +" +string(36) "HTTP/1.1 302 +Location: /somewhere +" +string(35) "HTTP/1.1 301 +Location: /anywhere +" +Done --- /dev/null +++ b/ext/http/tests/HttpMessage_002.phpt @@ -0,0 +1,65 @@ +--TEST-- +HttpMessage properties +--SKIPIF-- + +--FILE-- +var_property); + var_dump($this->public_property); + var_dump($this->protected_property); + var_dump($this->private_property); + var_dump($this->non_ex_property); + $this->var_property.='_property'; + $this->public_property.='_property'; + $this->protected_property.='_property'; + $this->private_property.='_property'; + $this->non_ex_property = 'non_ex'; + var_dump($this->var_property); + var_dump($this->public_property); + var_dump($this->protected_property); + var_dump($this->private_property); + var_dump($this->non_ex_property); + + print_r($this->headers); + $this->headers['Foo'] = 'Bar'; + } +} + +error_reporting(E_ALL|E_STRICT); + +echo "-TEST\n"; +$m = new Message; +$m->test(); +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +string(3) "var" +string(6) "public" +string(9) "protected" +string(7) "private" + +Notice: Undefined property: Message::$non_ex_property in %s +NULL +string(12) "var_property" +string(15) "public_property" +string(18) "protected_property" +string(16) "private_property" +string(6) "non_ex" +Array +( +) +%aFatal error%sCannot access HttpMessage properties by reference or array key/index in%s --- /dev/null +++ b/ext/http/tests/HttpMessage_003.phpt @@ -0,0 +1,70 @@ +--TEST-- +HttpMessage implements Serializable, Countable +--SKIPIF-- + +--FILE-- +count()); +var_dump($m->serialize()); +$m->unserialize("HTTP/1.1 200 Ok\r\nServer: Funky/1.0"); +var_dump($m); +var_dump($m->count()); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +int(3) +string(148) "HTTP/1.1 301 +Location: /anywhere +HTTP/1.1 302 +Location: /somewhere +HTTP/1.1 200 +X-Original-Transfer-Encoding: chunked +Content-Length: 1 + +X +" +object(HttpMessage)#%d (%d) { + ["type%s]=> + int(2) + ["body%s]=> + string(0) "" + ["requestMethod%s]=> + string(0) "" + ["requestUrl%s]=> + string(0) "" + ["responseStatus%s]=> + string(2) "Ok" + ["responseCode%s]=> + int(200) + ["httpVersion%s]=> + float(1.1) + ["headers%s]=> + array(1) { + ["Server"]=> + string(9) "Funky/1.0" + } + ["parentMessage%s]=> + NULL +} +int(1) +Done --- /dev/null +++ b/ext/http/tests/HttpMessage_004.phpt @@ -0,0 +1,36 @@ +--TEST-- +HttpMessage::detach() +--SKIPIF-- + +--FILE-- +detach(); +$d->addHeaders(array('Server'=>'Funky/2.0')); +var_dump($d->getHeaders() == $m->getHeaders()); +var_dump($d->getBody()); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +bool(false) +string(3) "Hi!" +Done \ No newline at end of file --- /dev/null +++ b/ext/http/tests/HttpMessage_005.phpt @@ -0,0 +1,86 @@ +--TEST-- +HttpMessage::prepend() +--SKIPIF-- + +--FILE-- +prepend($m2); +$m2 = NULL; +echo $m1->toString(true); + +$m1->prepend($m1->detach(), false); +echo $m1->toString(true); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +GET http://example.com/ HTTP/1.0 +HTTP/1.1 200 ok +Server: Funky/2.0 +Content-Type: text/html +Content-Length: 9 + +Hi there! +GET / HTTP/1.1 +Host: example.com +Accept: */* +Connection: close +HTTP/1.1 200 ok +Server: Funky/1.0 +Content-Type: text/plain +Content-Length: 3 + +Hi! +GET http://example.com/ HTTP/1.0 +HTTP/1.1 200 ok +Server: Funky/2.0 +Content-Type: text/html +Content-Length: 9 + +Hi there! +GET / HTTP/1.1 +Host: example.com +Accept: */* +Connection: close +HTTP/1.1 200 ok +Server: Funky/1.0 +Content-Type: text/plain +Content-Length: 3 + +Hi! +HTTP/1.1 200 ok +Server: Funky/1.0 +Content-Type: text/plain +Content-Length: 3 + +Hi! +Done \ No newline at end of file --- /dev/null +++ b/ext/http/tests/HttpMessage_006.phpt @@ -0,0 +1,35 @@ +--TEST-- +HttpMessage iterator +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +== +HTTP/1.1 304 Not Modified +== +GET /foo HTTP/1.1 +== +HTTP/1.1 200 OK +== +GET / HTTP/1.1 +Done \ No newline at end of file --- /dev/null +++ b/ext/http/tests/HttpMessage_007.phpt @@ -0,0 +1,46 @@ +--TEST-- +HttpMessage::reverse() +--SKIPIF-- + +--FILE-- +toString(true); +echo "===\n"; +echo HttpMessage::fromString($s)->reverse()->toString(true); + +$m = new HttpMessage($s); +$r = $m->reverse(); +unset($m); +var_dump($r->count()); +echo $r->toString(true); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +GET /first HTTP/1.1 +HTTP/1.1 200 Ok-first +GET /second HTTP/1.1 +HTTP/1.1 200 Ok-second +GET /third HTTP/1.1 +HTTP/1.1 200 Ok-third +=== +HTTP/1.1 200 Ok-third +GET /third HTTP/1.1 +HTTP/1.1 200 Ok-second +GET /second HTTP/1.1 +HTTP/1.1 200 Ok-first +GET /first HTTP/1.1 +int(6) +HTTP/1.1 200 Ok-third +GET /third HTTP/1.1 +HTTP/1.1 200 Ok-second +GET /second HTTP/1.1 +HTTP/1.1 200 Ok-first +GET /first HTTP/1.1 +Done --- /dev/null +++ b/ext/http/tests/HttpMessage_008.phpt @@ -0,0 +1,41 @@ +--TEST-- +HttpMessage::toMessageTypeObject() +--SKIPIF-- + +--FILE-- +"b",1=>2),null); + +$m = new HttpMessage; +$m->setType(HttpMessage::TYPE_REQUEST); +$m->setRequestMethod('POST'); +$m->setRequestUrl("http://www.example.com"); +$m->setHttpVersion('1.1'); +$m->addHeaders( + array( + "Content-Type" => "application/x-www-form-urlencoded", + "Host" => "www.example.com", + "Content-Length"=> strlen($b), + ) +); +$m->setBody($b); +$r = $m->toMessageTypeObject(); +echo $m,"\n"; +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +POST http://www.example.com HTTP/1.1 +Content-Type: application/x-www-form-urlencoded +Host: www.example.com +Content-Length: 7 + +a=b&1=2 + +Done --- /dev/null +++ b/ext/http/tests/HttpMessage_009_bug16700.phpt @@ -0,0 +1,24 @@ +--TEST-- +Bug #16700 - child classes of HttpMessage cannot not have array properties +--SKIPIF-- + +--FILE-- +properties['foo'] = 'bar'; +echo $child->properties['foo'], "\n"; +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +bar +Done --- /dev/null +++ b/ext/http/tests/HttpQueryString_001.phpt @@ -0,0 +1,116 @@ +--TEST-- +HttpQueryString global +--SKIPIF-- + +--FILE-- +'b','c'=>'3.4','r'=>array(1,2,3)); +$_SERVER['QUERY_STRING'] = 'a=b&c=3.4&r[0]=1&r[1]=2&r[2]=3'; + +var_dump(HttpQueryString::singleton()->get()); +var_dump(HttpQueryString::singleton()->get('n')); +var_dump(HttpQueryString::singleton()->get('a')); +var_dump(HttpQueryString::singleton()->get('a', "i", 0, true)); +var_dump(HttpQueryString::singleton()->get('a', "string", 'hi!')); +var_dump(HttpQueryString::singleton()->get('c')); +var_dump(HttpQueryString::singleton()->get('c', HttpQueryString::TYPE_INT)); +var_dump(HttpQueryString::singleton()->get('c', HttpQueryString::TYPE_FLOAT)); +var_dump(HttpQueryString::singleton()->get('c', HttpQueryString::TYPE_BOOL)); +var_dump(HttpQueryString::singleton()->get('r')); +var_dump(HttpQueryString::singleton()->get('r', HttpQueryString::TYPE_ARRAY)); +var_dump(HttpQueryString::singleton()->get('r', HttpQueryString::TYPE_OBJECT)); + +HttpQueryString::singleton()->set(new HttpQueryString(false, 'z[0]=2')); + +HttpQueryString::singleton()->set(array('a'=>'b', 'c'=> "3.4")); +HttpQueryString::singleton()->set(array('a' => NULL)); + +var_dump(HttpQueryString::singleton()); +var_dump($_GET); +var_dump($_SERVER['QUERY_STRING']); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +string(42) "a=b&c=3.4&r%5B0%5D=1&r%5B1%5D=2&r%5B2%5D=3" +NULL +string(1) "b" +int(0) +string(3) "hi!" +string(3) "3.4" +int(3) +float(3.4) +bool(true) +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} +object(stdClass)#%d (%d) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} +object(HttpQueryString)#1 (2) { + ["queryArray%s]=> + &array(3) { + ["c"]=> + string(3) "3.4" + ["r"]=> + array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + } + ["z"]=> + array(1) { + [0]=> + string(1) "2" + } + } + ["queryString%s]=> + &string(49) "c=3.4&r%5B0%5D=1&r%5B1%5D=2&r%5B2%5D=3&z%5B0%5D=2" +} +array(3) { + ["c"]=> + string(3) "3.4" + ["r"]=> + array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + } + ["z"]=> + array(1) { + [0]=> + string(1) "2" + } +} +string(49) "c=3.4&r%5B0%5D=1&r%5B1%5D=2&r%5B2%5D=3&z%5B0%5D=2" +Done \ No newline at end of file --- /dev/null +++ b/ext/http/tests/HttpQueryString_002.phpt @@ -0,0 +1,108 @@ +--TEST-- +HttpQueryString local +--SKIPIF-- + +--FILE-- +'b','c'=>'3.4','r'=>array(1,2,3))); +var_dump($q->get()); +var_dump($q->get('n')); +var_dump($q->get('a')); +var_dump($q->get('a', "i", 0, true)); +var_dump($q->get('a', "string", 'hi!')); +var_dump($q->get('c')); +var_dump($q->get('c', HttpQueryString::TYPE_INT)); +var_dump($q->get('c', HttpQueryString::TYPE_FLOAT)); +var_dump($q->get('c', HttpQueryString::TYPE_BOOL)); +var_dump($q->get('r')); +var_dump($q->get('r', HttpQueryString::TYPE_ARRAY)); +var_dump($q->get('r', HttpQueryString::TYPE_OBJECT)); + +$q->set('z[0]=2'); +$q->set(array('a'=>'b', 'c'=> "3.4")); +$q->set(array('a' => NULL)); + +var_dump($q); +var_dump($array); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +string(42) "a=b&c=3.4&r%5B0%5D=1&r%5B1%5D=2&r%5B2%5D=3" +NULL +string(1) "b" +int(0) +string(3) "hi!" +string(3) "3.4" +int(3) +float(3.4) +bool(true) +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} +object(stdClass)#%d (%d) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} +object(HttpQueryString)#1 (2) { + ["queryArray%s]=> + array(3) { + ["c"]=> + string(3) "3.4" + ["r"]=> + array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + } + ["z"]=> + array(1) { + [0]=> + string(1) "2" + } + } + ["queryString%s]=> + string(49) "c=3.4&r%5B0%5D=1&r%5B1%5D=2&r%5B2%5D=3&z%5B0%5D=2" +} +array(3) { + ["a"]=> + string(1) "b" + ["c"]=> + string(3) "3.4" + ["r"]=> + array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + } +} +Done \ No newline at end of file --- /dev/null +++ b/ext/http/tests/HttpQueryString_003.phpt @@ -0,0 +1,24 @@ +--TEST-- +HttpQueryString xlate +--SKIPIF-- + +--FILE-- +get()); +$qs->xlate("latin1", "utf8"); +var_dump($qs->get()); +$qs->xlate("utf8", "latin1"); +var_dump($qs->get()); +echo "Done\n"; +--EXPECTF-- +%aTEST +string(29) "%E4%5B0%5D=%FC&%F6%5Ba%5D=%DF" +string(41) "%C3%A4%5B0%5D=%C3%BC&%C3%B6%5Ba%5D=%C3%9F" +string(29) "%E4%5B0%5D=%FC&%F6%5Ba%5D=%DF" +Done --- /dev/null +++ b/ext/http/tests/HttpQueryString_004.phpt @@ -0,0 +1,54 @@ +--TEST-- +HttpQueryString w/ objects +--SKIPIF-- + +--FILE-- +bar = (object) array("baz"=>1); + $this->dont_show = 'xxx'; + $this->dont_show2 = 'zzz'; + } +} +$foo = new test_props; +var_dump($q = new HttpQueryString(false, $foo)); +$foo->bar->baz = 0; +var_dump($q->mod($foo)); +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +object(HttpQueryString)#3 (2) { + ["queryArray%s]=> + array(1) { + ["bar"]=> + array(1) { + ["baz"]=> + int(1) + } + } + ["queryString%s]=> + string(14) "bar%5Bbaz%5D=1" +} +object(HttpQueryString)#4 (2) { + ["queryArray%s]=> + array(1) { + ["bar"]=> + array(1) { + ["baz"]=> + int(0) + } + } + ["queryString%s]=> + string(14) "bar%5Bbaz%5D=0" +} +Done --- /dev/null +++ b/ext/http/tests/HttpRequestDataShare_001.phpt @@ -0,0 +1,38 @@ +--TEST-- +HttpRequestDataShare +--SKIPIF-- + +--FILE-- +dns = true; +$s->cookie = true; + +$r1 = new HttpRequest("http://www.google.com/"); +$r2 = new HttpRequest("http://www.google.com/"); + +$r1->enableCookies(); +$r2->enableCookies(); + +$s->attach($r1); +$s->attach($r2); + +$r1->send(); +$r2->send(); + +$s->reset(); + +var_dump(current($r1->getResponseCookies())->cookies["PREF"] === HttpUtil::parseCookie($r2->getRequestMessage()->getHeader("Cookie"))->cookies["PREF"]); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +bool(true) +Done --- /dev/null +++ b/ext/http/tests/HttpRequestDataShare_002.phpt @@ -0,0 +1,52 @@ +--TEST-- +HttpRequestDataShare global +--SKIPIF-- + +--FILE-- +cookie = true; +var_dump($s); + +$r1 = new HttpRequest("http://www.google.com/"); +$r2 = new HttpRequest("http://www.google.com/"); + +$r1->enableCookies(); +$r2->enableCookies(); + +$s->attach($r1); +$s->attach($r2); + +$r1->send(); +$r2->send(); + +$s->reset(); + +if (current($r1->getResponseCookies())->cookies["PREF"] !== HttpUtil::parseCookie($r2->getRequestMessage()->getHeader("Cookie"))->cookies["PREF"]) { + var_dump( + current($r1->getResponseCookies())->cookies["PREF"], + HttpUtil::parseCookie($r2->getRequestMessage()->getHeader("Cookie"))->cookies["PREF"] + ); +} + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +object(HttpRequestDataShare)#1 (4) { + ["cookie"]=> + bool(true) + ["dns"]=> + bool(true) + ["ssl"]=> + bool(false) + ["connect"]=> + bool(false) +} +Done --- /dev/null +++ b/ext/http/tests/HttpRequestPool_001.phpt @@ -0,0 +1,51 @@ +--TEST-- +HttpRequestPool +--SKIPIF-- + +--FILE-- +addPostFields(array('a'=>1,'b'=>2)) ; + +$pool = new HttpRequestPool( + new HttpRequest('http://www.php.net/', HTTP_METH_HEAD), + $post +); + +$pool->send(); + +foreach ($pool as $req) { + echo $req->getUrl(), '=', + $req->getResponseCode(), ':', + $req->getResponseMessage()->getResponseCode(), "\n"; +} + +foreach ($pool as $req) { + try { + $pool->attach(new HttpRequest('http://foo.bar')); + } catch (HttpRequestPoolException $x) { + echo ".\n"; + } +} + +foreach ($pool as $req) { + $pool->detach($req); +} + +echo "Done\n"; +?> + +--EXPECTF-- +%aTEST +http://www.php.net/=200:200 +http://dev.iworks.at/ext-http/.print_request.php=200:200 +. +. +Done --- /dev/null +++ b/ext/http/tests/HttpRequestPool_002.phpt @@ -0,0 +1,51 @@ +--TEST-- +extending HttpRequestPool +--SKIPIF-- + +--FILE-- +socketPerform()) { + if (!$this->socketSelect()) { + throw new HttpSocketException; + } + } + } + + protected final function socketPerform() + { + $result = parent::socketPerform(); + + echo "."; + foreach ($this->getFinishedRequests() as $r) { + echo "=", $r->getResponseCode(), "="; + $this->detach($r); + } + + return $result; + } +} + +$pool = new MyPool( + new HttpRequest('http://www.php.net/', HTTP_METH_HEAD), + new HttpRequest('http://www.php.net/', HTTP_METH_HEAD), + new HttpRequest('http://www.php.net/', HTTP_METH_HEAD) +); + +$pool->send(); + +echo "\nDone\n"; +?> +--EXPECTREGEX-- +.+TEST +\.*=200=\.*=200=\.*=200= +Done --- /dev/null +++ b/ext/http/tests/HttpRequestPool_003.phpt @@ -0,0 +1,175 @@ +--TEST-- +HttpRequestPool chain +--SKIPIF-- + +--FILE-- +dir = (is_dir($cache_dir) or @mkdir($cache_dir)) ? $cache_dir : null; + + foreach (array_map('trim', file($urls_file)) as $url) { + $this->all[$url] = $this->dir ? $this->dir .'/'. md5($url) : null; + } + + $this->send(); + } + + public final function send() + { + if (RMAX) { + $now = array_slice($this->all, 0, RMAX); + $this->rem = array_slice($this->all, RMAX); + } else { + $now = $urls; + $this->rem = array(); + } + + foreach ($now as $url => $file) { + $this->attach( + new HttpRequest( + $url, + HttpRequest::METH_GET, + array( + 'redirect' => 5, + 'compress' => GZIP, + 'timeout' => TOUT, + 'connecttimeout' => TOUT, + 'lastmodified' => is_file($file)?filemtime($file):0 + ) + ) + ); + } + + while ($this->socketPerform()) { + if (!$this->socketSelect()) { + throw new HttpSocketException; + } + } + } + + protected final function socketPerform() + { + try { + $rc = parent::socketPerform(); + } catch (HttpRequestException $x) { + // a request may have thrown an exception, + // but it is still save to continue + echo $x->getMessage(), "\n"; + } + + foreach ($this->getFinishedRequests() as $r) { + $this->detach($r); + + $u = $r->getUrl(); + $c = $r->getResponseCode(); + $b = $r->getResponseBody(); + + printf("%d %s %d\n", $c, $u, strlen($b)); + + if ($c == 200 && $this->dir) { + file_put_contents($this->all[$u], $b); + } + + if ($a = each($this->rem)) { + list($url, $file) = $a; + $this->attach( + new HttpRequest( + $url, + HttpRequest::METH_GET, + array( + 'redirect' => 5, + 'compress' => GZIP, + 'timeout' => TOUT, + 'connecttimeout' => TOUT, + 'lastmodified' => is_file($file)?filemtime($file):0 + ) + ) + ); + } + } + return $rc; + } +} + +define('GZIP', true); +define('TOUT', 50); +define('RMAX', 10); +chdir(dirname(__FILE__)); + +$time = microtime(true); +$pool = new Pool(); +printf("Elapsed: %0.3fs\n", microtime(true)-$time); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +%d %s %d +Elapsed: %fs +Done --- /dev/null +++ b/ext/http/tests/HttpRequestPool_004.phpt @@ -0,0 +1,19 @@ +--TEST-- +HttpRequestPool::__destruct() invalid curl handle +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +Done --- /dev/null +++ b/ext/http/tests/HttpRequestPool_005.phpt @@ -0,0 +1,46 @@ +--TEST-- +HttpRequestPool exception +--SKIPIF-- + +--FILE-- +send(); +} catch (HttpRequestPoolException $x) { + for ($i=0; $x; ++$i, $x = @$x->innerException) { + printf("%s%s: %s\n", str_repeat("\t", $i), get_class($x), $x->getMessage()); + } + var_dump($i); +} +$p = new HttpRequestPool(new HttpRequest('http://_____'), new HttpRequest('http://_____')); +try { + $p->send(); +} catch (HttpRequestPoolException $x) { + for ($i=0; $x; ++$i, $x = @$x->innerException) { + printf("%s%s: %s\n", str_repeat("\t", $i), get_class($x), $x->getMessage()); + } + var_dump($i); +} +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +HttpRequestPoolException: Exception caused by 2 inner exception(s) + HttpInvalidParamException: Empty or too short HTTP message: '' + HttpRequestException: %souldn't resolve host name; %s (http://_____/) +int(3) +HttpRequestPoolException: Exception caused by 4 inner exception(s) + HttpInvalidParamException: Empty or too short HTTP message: '' + HttpRequestException: %souldn't resolve host name; %s (http://_____/) + HttpInvalidParamException: Empty or too short HTTP message: '' + HttpRequestException: %souldn't resolve host name; %s (http://_____/) +int(5) +Done + --- /dev/null +++ b/ext/http/tests/HttpRequestPool_006.phpt @@ -0,0 +1,50 @@ +--TEST-- +HttpRequestPool detaching in callbacks +--SKIPIF-- + +--FILE-- +getUrl()])) { + $i[$this->getUrl()] = true; + try { + $GLOBALS['p']->detach($this); + } catch (Exception $ex) { + echo $ex, "\n"; + } + } + } + function onFinish() { + $GLOBALS['p']->detach($this); + } +} +$p = new HttpRequestPool(new r("http://at.php.net"), new r("http://de.php.net")); +$p->send(); +var_dump($p->getAttachedRequests()); +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +exception 'HttpRequestPoolException' with message 'HttpRequest object(#%d) cannot be detached from the HttpRequestPool while executing the progress callback' in %aHttpRequestPool_006.php:%d +Stack trace: +#0 %aHttpRequestPool_006.php(%d): HttpRequestPool->detach(Object(r)) +#1 [internal function]: r->onProgress(Array) +#2 %aHttpRequestPool_006.php(%d): HttpRequestPool->send() +#3 {main} +exception 'HttpRequestPoolException' with message 'HttpRequest object(#%d) cannot be detached from the HttpRequestPool while executing the progress callback' in %aHttpRequestPool_006.php:%d +Stack trace: +#0 %aHttpRequestPool_006.php(%d): HttpRequestPool->detach(Object(r)) +#1 [internal function]: r->onProgress(Array) +#2 %aHttpRequestPool_006.php(%d): HttpRequestPool->send() +#3 {main} +array(0) { +} +Done --- /dev/null +++ b/ext/http/tests/HttpRequest_001.phpt @@ -0,0 +1,51 @@ +--TEST-- +HttpRequest options +--SKIPIF-- + +--FILE-- +11, 'headers'=>array('X-Foo'=>'Bar'))); +$r2 = new HttpRequest; +$r2->setOptions(array('redirect'=>99, 'headers'=>array('X-Bar'=>'Foo'))); +$o1 = $r1->getOptions(); +$o2 = $r2->getOptions(); +$r1->setOptions($o2); +$r2->setOptions($o1); +print_r(array($o1, $o2)); +var_dump(serialize($r1->getOptions()) === serialize($r2->getOptions())); +$r1 = null; +$r2 = null; +?> +--EXPECTF-- +%aTEST +Array +( + [0] => Array + ( + [headers] => Array + ( + [X-Foo] => Bar + [X-Bar] => Foo + ) + + [redirect] => 11 + ) + + [1] => Array + ( + [headers] => Array + ( + [X-Bar] => Foo + [X-Foo] => Bar + ) + + [redirect] => 99 + ) + +) +bool(false) --- /dev/null +++ b/ext/http/tests/HttpRequest_002.phpt @@ -0,0 +1,86 @@ +--TEST-- +HttpRequest GET/POST +--SKIPIF-- + +--FILE-- +send(); +print_r($r->getResponseInfo()); + +$r = new HttpRequest('http://dev.iworks.at/ext-http/.print_request.php', HTTP_METH_POST); +$r->addCookies(array('MyCookie' => 'foobar')); +$r->addQueryData(array('gq'=>'foobar','gi'=>10)); +$r->addPostFields(array('pq'=>'foobar','pi'=>10)); +$r->addPostFile('upload', dirname(__FILE__).'/data.txt', 'text/plain'); +$r->send(); +echo $r->getResponseBody(); +var_dump($r->getResponseMessage()->getResponseCode()); + +echo "Done"; +?> +--EXPECTF-- +%aTEST +Array +( + [effective_url] => http://www.google.com/ + [response_code] => 302 + [total_time] => %f + [namelookup_time] => %f + [connect_time] => %f + [pretransfer_time] => %f + [size_upload] => %d + [size_download] => %d + [speed_download] => %d + [speed_upload] => %d + [header_size] => %d + [request_size] => %d + [ssl_verifyresult] => %d + [filetime] => -1 + [content_length_download] => %d + [content_length_upload] => %d + [starttransfer_time] => %f + [content_type] => %s + [redirect_time] => %d + [redirect_count] => %d + [connect_code] => %d + [httpauth_avail] => %d + [proxyauth_avail] => %d + [os_errno] => %d + [num_connects] => %d + [ssl_engines] => Array + %a + [cookies] => Array + %a + [error] => +) +Array +( + [gq] => foobar + [gi] => 10 + [pq] => foobar + [pi] => 10 + [MyCookie] => foobar +) +Array +( + [upload] => Array + ( + [name] => data.txt + [type] => text/plain + [tmp_name] => %a + [error] => 0 + [size] => 1010 + ) + +) +int(200) +Done --- /dev/null +++ b/ext/http/tests/HttpRequest_003.phpt @@ -0,0 +1,54 @@ +--TEST-- +HttpRequest SSL +--SKIPIF-- + +--FILE-- + '3', 'ssl' => array('version' => '3', 'verifyhost' => '1')); +$r = new HttpRequest('https://ssl.irmler.at/iworks/data.txt'); +$r->setOptions($o); +$r->send(); +var_dump($r->getResponseBody()); +var_dump(is_object($r->getResponseMessage())); +var_dump(is_object($r->getResponseMessage())); +var_dump(is_object($r->getResponseMessage())); +var_dump($o); +$r->setOptions($o); +$r->send(); +var_dump($o); +?> +--EXPECTF-- +%aTEST +string(10) "1234567890" +bool(true) +bool(true) +bool(true) +array(2) { + ["redirect"]=> + string(1) "3" + ["ssl"]=> + array(2) { + ["version"]=> + string(1) "3" + ["verifyhost"]=> + string(1) "1" + } +} +array(2) { + ["redirect"]=> + string(1) "3" + ["ssl"]=> + array(2) { + ["version"]=> + string(1) "3" + ["verifyhost"]=> + string(1) "1" + } +} + --- /dev/null +++ b/ext/http/tests/HttpRequest_004.phpt @@ -0,0 +1,162 @@ +--TEST-- +HttpRequest multiple posts +--SKIPIF-- + +--FILE-- + 1, 'dbl' => M_PI), + array('str' => 'something', 'nil' => null) +); + +echo "\nFirst Request\n"; +$r = new HttpRequest('http://dev.iworks.at/ext-http/.print_request.php', HttpRequest::METH_POST); +$r->setPostFields($fields[0]); +$r->addPostFields($fields[1]); +var_dump($r->send()->getBody()); +var_dump($fields); + +echo "\nSecond Request\n"; +$r->setPostFields($fields); +var_dump($r->send()->getBody()); +var_dump($fields); + +echo "\nThird Request\n"; +$r->addPostFields(array('x' => 'X')); +var_dump($r->send()->getBody()); +var_dump($fields); + +echo "\nFourth Request\n"; +$r->setPostFields(array()); +var_dump($r->send()->getBody()); +var_dump($fields); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST + +First Request +string(%d) "Array +( + [int] => 1 + [dbl] => 3.1415926535898 + [str] => something + [nil] => +) +string(44) "int=1&dbl=3.1415926535898&str=something&nil=" +" +array(2) { + [0]=> + array(2) { + ["int"]=> + int(1) + ["dbl"]=> + float(3.1415926535898) + } + [1]=> + array(2) { + ["str"]=> + string(9) "something" + ["nil"]=> + NULL + } +} + +Second Request +string(%d) "Array +( + [0] => Array + ( + [int] => 1 + [dbl] => 3.1415926535898 + ) + + [1] => Array + ( + [str] => something + [nil] => + ) + +) +string(72) "0%5Bint%5D=1&0%5Bdbl%5D=3.1415926535898&1%5Bstr%5D=something&1%5Bnil%5D=" +" +array(2) { + [0]=> + array(2) { + ["int"]=> + int(1) + ["dbl"]=> + float(3.1415926535898) + } + [1]=> + array(2) { + ["str"]=> + string(9) "something" + ["nil"]=> + NULL + } +} + +Third Request +string(%d) "Array +( + [0] => Array + ( + [int] => 1 + [dbl] => 3.1415926535898 + ) + + [1] => Array + ( + [str] => something + [nil] => + ) + + [x] => X +) +string(76) "0%5Bint%5D=1&0%5Bdbl%5D=3.1415926535898&1%5Bstr%5D=something&1%5Bnil%5D=&x=X" +" +array(2) { + [0]=> + array(2) { + ["int"]=> + int(1) + ["dbl"]=> + float(3.1415926535898) + } + [1]=> + array(2) { + ["str"]=> + string(9) "something" + ["nil"]=> + NULL + } +} + +Fourth Request +string(13) "string(0) "" +" +array(2) { + [0]=> + array(2) { + ["int"]=> + int(1) + ["dbl"]=> + float(3.1415926535898) + } + [1]=> + array(2) { + ["str"]=> + string(9) "something" + ["nil"]=> + NULL + } +} +Done + --- /dev/null +++ b/ext/http/tests/HttpRequest_005.phpt @@ -0,0 +1,24 @@ +--TEST-- +HttpRequest accessors +--SKIPIF-- + +--FILE-- + 3 && substr($method, 0, 3) == 'get') + $x = $r->$method(); + } catch (HttpException $e) { + } +} +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +Done --- /dev/null +++ b/ext/http/tests/HttpRequest_006.phpt @@ -0,0 +1,147 @@ +--TEST-- +HttpRequest XMLRPC +--SKIPIF-- + +--FILE-- +setContentType('text/xml'); +$r->setBody(xmlrpc_encode_request('testMethod', array('foo' => 'bar'))); +var_dump($r->send()); +var_dump($r->send()); +var_dump($r->send()); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +object(HttpMessage)#%d (%d) { + ["type:protected"]=> + int(2) + ["body:protected"]=> + string(309) "string(294) " + +testMethod + + + + + + foo + + bar + + + + + + + +" +" + ["requestMethod:protected"]=> + string(0) "" + ["requestUrl:protected"]=> + string(0) "" + ["responseStatus:protected"]=> + string(2) "OK" + ["responseCode:protected"]=> + int(200) + ["httpVersion:protected"]=> + float(1.1) + ["headers:protected"]=> + array(6) { + %a + } + ["parentMessage:protected"]=> + NULL +} +object(HttpMessage)#%d (%d) { + ["type:protected"]=> + int(2) + ["body:protected"]=> + string(309) "string(294) " + +testMethod + + + + + + foo + + bar + + + + + + + +" +" + ["requestMethod:protected"]=> + string(0) "" + ["requestUrl:protected"]=> + string(0) "" + ["responseStatus:protected"]=> + string(2) "OK" + ["responseCode:protected"]=> + int(200) + ["httpVersion:protected"]=> + float(1.1) + ["headers:protected"]=> + array(6) { + %a + } + ["parentMessage:protected"]=> + NULL +} +object(HttpMessage)#%d (%d) { + ["type:protected"]=> + int(2) + ["body:protected"]=> + string(309) "string(294) " + +testMethod + + + + + + foo + + bar + + + + + + + +" +" + ["requestMethod:protected"]=> + string(0) "" + ["requestUrl:protected"]=> + string(0) "" + ["responseStatus:protected"]=> + string(2) "OK" + ["responseCode:protected"]=> + int(200) + ["httpVersion:protected"]=> + float(1.1) + ["headers:protected"]=> + array(6) { + %a + } + ["parentMessage:protected"]=> + NULL +} +Done --- /dev/null +++ b/ext/http/tests/HttpRequest_007.phpt @@ -0,0 +1,64 @@ +--TEST-- +HttpRequest PUT +--SKIPIF-- + +--FILE-- +recordHistory = true; +$r->addHeaders(array('content-type' => 'text/plain')); +$r->setPutFile(__FILE__); +$r->send(); +var_dump($r->getHistory()->toString(true)); +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +string(%d) "PUT /ext-http/.print_put.php5 HTTP/1.1 +User-Agent: PECL::HTTP/%a +Host: dev.iworks.at +Accept: */* +Content-Type: text/plain +Content-Length: %d +Expect: 100-continue + +recordHistory = true; +$r->addHeaders(array('content-type' => 'text/plain')); +$r->setPutFile(__FILE__); +$r->send(); +var_dump($r->getHistory()->toString(true)); +echo "Done\n"; +?> + +HTTP/1.1 100 Continue +HTTP/1.1 200 OK +Date: %a +Server: %a +X-Powered-By: %a +Vary: Accept-Encoding +Content-Length: %d +Content-Type: text/html + +recordHistory = true; +$r->addHeaders(array('content-type' => 'text/plain')); +$r->setPutFile(__FILE__); +$r->send(); +var_dump($r->getHistory()->toString(true)); +echo "Done\n"; +?> + +" +Done --- /dev/null +++ b/ext/http/tests/HttpRequest_008.phpt @@ -0,0 +1,32 @@ +--TEST-- +HttpRequest custom request method +--SKIPIF-- + +--FILE-- +setContentType('text/plain'); +$r->setBody('Yep, this is FOOBAR!'); +var_dump($r->send()->getResponseCode()); +var_dump($r->getRawRequestMessage()); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +int(200) +string(%d) "FOOBAR /ext-http/.print_request.php HTTP/1.1 +User-Agent: %a +Host: dev.iworks.at +Accept: */* +Content-Type: text/plain +Content-Length: 20 + +Yep, this is FOOBAR!" +Done --- /dev/null +++ b/ext/http/tests/HttpRequest_009.phpt @@ -0,0 +1,48 @@ +--TEST-- +HttpRequest callbacks +--SKIPIF-- + +--FILE-- +getResponseCode()); + } +} + +$r = new _R('http://dev.iworks.at/ext-http/.print_request.php', HTTP_METH_POST); +$r->addPostFile('upload', __FILE__, 'text/plain'); +$r->send(); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +Array +( + [dltotal] => %f + [dlnow] => %f + [ultotal] => %f + [ulnow] => %f +) +%array +( + [dltotal] => %f + [dlnow] => %f + [ultotal] => %f + [ulnow] => %f +) +int(200) +Done --- /dev/null +++ b/ext/http/tests/HttpRequest_010.phpt @@ -0,0 +1,48 @@ +--TEST-- +HttpRequest cookie API +--SKIPIF-- + +--FILE-- +send(); +$c[0] = $r->getResponseInfo("cookies"); +if (!empty($c[0])) { + var_dump('$c[0]', $c[0]); +} + +var_dump($r->enableCookies()); +$r->send(); + +$c[1] = $r->getResponseInfo("cookies"); +if (empty($c[1])) { + var_dump('$c[1]', $c[1]); +} + +var_dump($r->resetCookies()); +$r->send(); + +$c[2] = $r->getResponseInfo("cookies"); +if ($c[1] === $c[2]) { + var_dump('$c[1]', $c[1], '$c[2]', $c[2]); +} + +$r->send(); +$c[3] = $r->getResponseInfo("cookies"); +if ($c[2] !== $c[3]) { + var_dump('$c[2]', $c[2], '$c[3]', $c[3]); +} + +echo "Done\n"; +--EXPECTF-- +%aTEST +bool(true) +bool(true) +Done --- /dev/null +++ b/ext/http/tests/HttpResponse_001.phpt @@ -0,0 +1,25 @@ +--TEST-- +HttpResponse - send data with caching headers +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +X-Powered-By: PHP/%a +Cache-Control: public, must-revalidate, max-age=3600 +Last-Modified: %a, %d %a 20%d %d:%d:%d GMT +Content-Type: %a +Accept-Ranges: bytes +ETag: "3858f62230ac3c915f300c664312c63f" +Content-Length: 6 + +foobar --- /dev/null +++ b/ext/http/tests/HttpResponse_002.phpt @@ -0,0 +1,25 @@ +--TEST-- +HttpResponse - send gzipped file +--SKIPIF-- + +--ENV-- +HTTP_ACCEPT_ENCODING=gzip +--FILE-- + +--EXPECTF-- +X-Powered-By: PHP/%a +Content-Type: %a +Accept-Ranges: bytes +Content-Encoding: gzip +Vary: Accept-Encoding + +%a --- /dev/null +++ b/ext/http/tests/HttpResponse_003.phpt @@ -0,0 +1,30 @@ +--TEST-- +HttpResponse - send gzipped file with caching headers +--SKIPIF-- + +--ENV-- +HTTP_ACCEPT_ENCODING=gzip +--FILE-- + +--EXPECTF-- +X-Powered-By: PHP/%a +Cache-Control: public, must-revalidate, max-age=3600 +Last-Modified: %a, %d %a 20%d %d:%d:%d GMT +Content-Type: %a +Accept-Ranges: bytes +ETag: "%a" +Content-Encoding: gzip +Vary: Accept-Encoding + +%a --- /dev/null +++ b/ext/http/tests/HttpResponse_004.phpt @@ -0,0 +1,27 @@ +--TEST-- +HttpResponse - send cached gzipped data +--SKIPIF-- + +--ENV-- +HTTP_IF_NONE_MATCH="900150983cd24fb0d6963f7d28e17f72" +HTTP_ACCEPT_ENCODING=gzip +--FILE-- + +--EXPECTF-- +Status: 304%s +X-Powered-By: PHP/%s +Cache-Control: public, must-revalidate, max-age=3600 +Last-Modified: %s +Accept-Ranges: bytes +ETag: "900150983cd24fb0d6963f7d28e17f72" --- /dev/null +++ b/ext/http/tests/HttpResponse_005.phpt @@ -0,0 +1,24 @@ +--TEST-- +HttpResponse file not found +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Status: 404%s +X-Powered-By: PHP/%s +Content-Type: text/plain + +File not found --- /dev/null +++ b/ext/http/tests/allowed_methods_002.phpt @@ -0,0 +1,21 @@ +--TEST-- +allowed methods +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Status: 405%s +X-Powered-By: PHP/%a +Allow: POST +Content-type: %a + --- /dev/null +++ b/ext/http/tests/allowed_methods_002_logging.phpt @@ -0,0 +1,21 @@ +--TEST-- +logging allowed methods +--SKIPIF-- + +--ENV-- +HTTP_HOST=example.com +--FILE-- + +--EXPECTF-- +%aTEST +%d%d%d%d-%d%d-%d%d %d%d:%d%d:%d%d [405-ALLOWED] Allow: POST <%a> +Done --- /dev/null +++ b/ext/http/tests/bug_15800.phpt @@ -0,0 +1,49 @@ +--TEST-- +Bug #15800 Double free when zval is separated in convert_to_* +--SKIPIF-- + +--FILE-- + array('verifypeer'=>'1')); +debug_zval_dump($o); + +$r = new HttpRequest('http://www.google.com'); +$r->setOptions($o); +$r->send(); +debug_zval_dump($o); + +unset($r); +debug_zval_dump($o); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +array(1) refcount(2){ + ["ssl"]=> + array(1) refcount(1){ + ["verifypeer"]=> + string(1) "1" refcount(1) + } +} +array(1) refcount(2){ + ["ssl"]=> + array(1) refcount(1){ + ["verifypeer"]=> + string(1) "1" refcount(2) + } +} +array(1) refcount(2){ + ["ssl"]=> + array(1) refcount(1){ + ["verifypeer"]=> + string(1) "1" refcount(1) + } +} +Done --- /dev/null +++ b/ext/http/tests/build_str_001.phpt @@ -0,0 +1,30 @@ +--TEST-- +http_build_str +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +a=b +a=b&c%5B0%5D=1 +a=b&c%5B0%5D=1&d%5Be%5D=f +foo%5B0%5D=1&foo%5B1%5D=2&foo%5B2%5D%5B0%5D=3 +Done --- /dev/null +++ b/ext/http/tests/build_url_001.phpt @@ -0,0 +1,18 @@ +--TEST-- +http_build_url() with relative paths +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +http://%a/page +http://%a/with/some/path/ +Done --- /dev/null +++ b/ext/http/tests/build_url_002.phpt @@ -0,0 +1,30 @@ +--TEST-- +http_build_url() with parse_url() +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +https://www.example.com:9999/replaced?q=1#n +https://www.example.com:9999/replaced?q=1#n +Array +( + [scheme] => https + [host] => www.example.com + [port] => 9999 + [path] => /replaced + [query] => q=1 + [fragment] => n +) +Done --- /dev/null +++ b/ext/http/tests/build_url_003.phpt @@ -0,0 +1,26 @@ +--TEST-- +http_build_url() +--SKIPIF-- + +--ENV-- +HTTP_HOST=www.example.com +--FILE-- + 'https'))); +printf("-%s-\n", http_build_url($url, array('scheme' => 'https', 'host' => 'ssl.example.com'))); +printf("-%s-\n", http_build_url($url, array('scheme' => 'ftp', 'host' => 'ftp.example.com', 'port' => 21))); +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +-http://www.example.com/path/?query#anchor- +-https://www.example.com/path/?query#anchor- +-https://ssl.example.com/path/?query#anchor- +-ftp://ftp.example.com/path/?query#anchor- +Done --- /dev/null +++ b/ext/http/tests/build_url_004.phpt @@ -0,0 +1,22 @@ +--TEST-- +http_build_url flags +--SKPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +http://www.example.com/foo/baz +http://www.example.com/foo/baz +http://mike@www.example.com/foo/baz +http://www.example.com/?a%5B0%5D=1&a%5B1%5D=b&b=c +Done --- /dev/null +++ b/ext/http/tests/chunked_decode_001.phpt @@ -0,0 +1,25 @@ +--TEST-- +http_chunked_decode() "\r\n" +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +string(12) "abra +cadabra" + --- /dev/null +++ b/ext/http/tests/chunked_decode_002.phpt @@ -0,0 +1,25 @@ +--TEST-- +http_chunked_decode() "\n" +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +string(12) "abra +cadabra" + --- /dev/null +++ b/ext/http/tests/chunked_decode_003.phpt @@ -0,0 +1,27 @@ +--TEST-- +http_chunked_decode() truncated message +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +%aWarning%ahttp_chunked_decode()%aTruncated message: chunk size 255 exceeds remaining data size 12 at pos 34 of 46 in%a +string(24) "abra +cadabra +all we got +" --- /dev/null +++ b/ext/http/tests/chunked_decode_004.phpt @@ -0,0 +1,26 @@ +--TEST-- +http_chunked_decode() truncated message ending with NUL after a chunk +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +string(24) "abra +cadabra +all we got +" --- /dev/null +++ b/ext/http/tests/cloning_001.phpt @@ -0,0 +1,29 @@ +--TEST-- +cloning +--SKIPIF-- + +--FILE-- +setOptions(array('redirect' => 3)); +var_dump($r1->getOptions() == $r2->getOptions()); +$r1->setUrl('http://www.google.com/'); +var_dump($r1->getUrl() == $r2->getUrl()); +$r1->send(); +var_dump($r1->getResponseInfo() == $r2->getResponseInfo()); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +bool(false) +bool(false) +bool(false) +Done --- /dev/null +++ b/ext/http/tests/data.txt @@ -0,0 +1,10 @@ +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 --- /dev/null +++ b/ext/http/tests/date_001.phpt @@ -0,0 +1,17 @@ +--TEST-- +http_date() with timestamp +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +Thu, 01 Jan 1970 00:00:01 GMT +Fri, 13 Feb 2009 23:31:30 GMT + --- /dev/null +++ b/ext/http/tests/date_002.phpt @@ -0,0 +1,21 @@ +--TEST-- +http_date() without timestamp +--SKIPIF-- + +--FILE-- + 1); +echo "$t\n$d\nDone\n"; +?> +--EXPECTF-- +%aTEST +bool(true) +%d +%a, %d %a %d %d:%d:%d GMT +Done --- /dev/null +++ b/ext/http/tests/encoding_objects_001.phpt @@ -0,0 +1,35 @@ +--TEST-- +encoding stream objects +--SKIPIF-- + +--FILE-- +flush($d->flush("Hi ")); +echo $i->finish($d->finish("there!\n")); +echo $i->finish($d->finish("Yo...\n")); + +$id = $i->update($d->update($pd = file_get_contents(__FILE__))); +foreach (glob('*.phpt') as $f) { + $id .= $i->update($d->update($tmp = file_get_contents($f))); + $pd .= $tmp; +} +$id .= $i->finish($d->finish()); + +var_dump($id == $pd); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +Hi there! +Yo... +bool(true) +Done + --- /dev/null +++ b/ext/http/tests/encodings.phpt @@ -0,0 +1,44 @@ +--TEST-- +encodings +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +bool(true) +bool(true) +bool(true) +Done --- /dev/null +++ b/ext/http/tests/etag_mode_031.phpt @@ -0,0 +1,23 @@ +--TEST-- +crc32 etag (may fail because PHPs crc32 is actually crc32b) +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +X-Powered-By: PHP/%a +Cache-Control: private, must-revalidate, max-age=0 +Accept-Ranges: bytes +ETag: "4e818847" +Content-Length: 4 +Content-type: %a + +abc --- /dev/null +++ b/ext/http/tests/etag_mode_032.phpt @@ -0,0 +1,23 @@ +--TEST-- +sha1 etag +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +X-Powered-By: PHP/%a +Cache-Control: private, must-revalidate, max-age=0 +Accept-Ranges: bytes +ETag: "03cfd743661f07975fa2f1220c5194cbaff48451" +Content-Length: 4 +Content-type: %a + +abc --- /dev/null +++ b/ext/http/tests/etag_mode_033.phpt @@ -0,0 +1,23 @@ +--TEST-- +md5 etag +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +X-Powered-By: PHP/%a +Cache-Control: private, must-revalidate, max-age=0 +Accept-Ranges: bytes +ETag: "0bee89b07a248e27c83fc3d5951213c1" +Content-Length: 4 +Content-type: %a + +abc --- /dev/null +++ b/ext/http/tests/etag_mode_034.phpt @@ -0,0 +1,24 @@ +--TEST-- +ext/hash etag +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +X-Powered-By: PHP/%a +Cache-Control: private, must-revalidate, max-age=0 +Accept-Ranges: bytes +ETag: "edeaaff3f1774ad2888673770c6d64097e391bc362d7d6fb34982ddf0efd18cb" +Content-Length: 4 +Content-type: %a + +abc --- /dev/null +++ b/ext/http/tests/etag_mode_041.phpt @@ -0,0 +1,21 @@ +--TEST-- +ob crc32 etag (may fail because PHPs crc32 is actually crc32b) +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +X-Powered-By: PHP/%a +Cache-Control: private, must-revalidate, max-age=0 +ETag: "4e818847" +Content-type: %a + +abc --- /dev/null +++ b/ext/http/tests/etag_mode_042.phpt @@ -0,0 +1,21 @@ +--TEST-- +ob sha1 etag +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +X-Powered-By: PHP/%a +Cache-Control: private, must-revalidate, max-age=0 +ETag: "03cfd743661f07975fa2f1220c5194cbaff48451" +Content-type: %a + +abc --- /dev/null +++ b/ext/http/tests/etag_mode_043.phpt @@ -0,0 +1,21 @@ +--TEST-- +ob md5 etag +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +X-Powered-By: PHP/%a +Cache-Control: private, must-revalidate, max-age=0 +ETag: "0bee89b07a248e27c83fc3d5951213c1" +Content-type: %a + +abc --- /dev/null +++ b/ext/http/tests/etag_mode_044.phpt @@ -0,0 +1,22 @@ +--TEST-- +ob ext/hash etag +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +X-Powered-By: PHP/%a +Cache-Control: private, must-revalidate, max-age=0 +ETag: "edeaaff3f1774ad2888673770c6d64097e391bc362d7d6fb34982ddf0efd18cb" +Content-type: %a + +abc --- /dev/null +++ b/ext/http/tests/exceptions.phpt @@ -0,0 +1,53 @@ +--TEST-- +exceptions +--SKIPIF-- + +--FILE-- + 'Runtime', + HTTP_E_INVALID_PARAM => 'InvalidParam', + HTTP_E_HEADER => 'Header', + HTTP_E_MALFORMED_HEADERS => 'MalformedHeaders', + HTTP_E_REQUEST_METHOD => 'RequestMethod', + HTTP_E_MESSAGE_TYPE => 'MessageType', + HTTP_E_ENCODING => 'Encoding', + HTTP_E_REQUEST => 'Request', + HTTP_E_REQUEST_POOL => 'RequestPool', + HTTP_E_SOCKET => 'Socket', + HTTP_E_RESPONSE => 'Response', + HTTP_E_URL => 'Url', +); + +foreach ($e as $i => $c) { + try { + $n = "Http{$c}Exception"; + throw new $n; + } catch (HttpException $x) { + printf("%2d: %s\n", $i, get_class($x)); + } +} +echo "Done\n"; +?> +--EXPECTF-- +%aTEST + 1: HttpRuntimeException + 2: HttpInvalidParamException + 3: HttpHeaderException + 4: HttpMalformedHeadersException + 5: HttpRequestMethodException + 6: HttpMessageTypeException + 7: HttpEncodingException + 8: HttpRequestException + 9: HttpRequestPoolException +10: HttpSocketException +11: HttpResponseException +12: HttpUrlException +Done --- /dev/null +++ b/ext/http/tests/get_request_data_001.phpt @@ -0,0 +1,40 @@ +--TEST-- +get request data +--SKIPIF-- + +--POST-- +a=b&c=d +--FILE-- + +--EXPECTF-- +%aTEST +Array +( + [Accept-Charset] => iso-8859-1, * + [Accept-Encoding] => none + [Host] => localhost + [User-Agent] => Mozilla/5.0 +) +string(7) "a=b&c=d" +string(7) "a=b&c=d" +string(7) "a=b&c=d" +string(7) "a=b&c=d" +Done --- /dev/null +++ b/ext/http/tests/log.inc @@ -0,0 +1,23 @@ + --- /dev/null +++ b/ext/http/tests/match_request_header_001.phpt @@ -0,0 +1,23 @@ +--TEST-- +http_match_request_header() +--SKIPIF-- + +--ENV-- +HTTP_FOO=bar +--FILE-- + +--EXPECTF-- +%aTEST +bool(true) +bool(true) +bool(false) +Done --- /dev/null +++ b/ext/http/tests/negotiation_001.phpt @@ -0,0 +1,64 @@ +--TEST-- +negotiation +--SKIPIF-- + +--ENV-- +HTTP_ACCEPT=application/xml, application/xhtml+xml, text/html ; q = .8 +HTTP_ACCEPT_LANGUAGE=de-AT,de-DE;q=0.8,en-GB;q=0.3,en-US;q=0.2 +HTTP_ACCEPT_CHARSET=ISO-8859-1,utf-8;q=0.7,*;q=0.7 +--FILE-- + +--EXPECTF-- +%aTEST +string(2) "de" +string(2) "de" +string(10) "iso-8859-1" +string(10) "iso-8859-1" +string(21) "application/xhtml+xml" +string(21) "application/xhtml+xml" +string(7) "unknown" +string(7) "unknown" +string(7) "unknown" +Array +( + [de] => 900 + [en] => 0.27 +) +Array +( + [iso-8859-1] => 1000 + [utf-8] => 0.7 +) +Array +( + [application/xhtml+xml] => 999 + [text/html] => 0.8 +) +Done --- /dev/null +++ b/ext/http/tests/ob_deflatehandler_001.phpt @@ -0,0 +1,23 @@ +--TEST-- +ob_deflatehandler +--SKIPIF-- + +--ENV-- +HTTP_ACCEPT_ENCODING=gzip +--FILE-- + +--EXPECTF-- +%a +Content-Encoding: gzip +Vary: Accept-Encoding +%a + --- /dev/null +++ b/ext/http/tests/ob_inflatehandler_001.phpt @@ -0,0 +1,16 @@ +--TEST-- +ob_inflatehandler +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST + --- /dev/null +++ b/ext/http/tests/parse_cookie_001.phpt @@ -0,0 +1,41 @@ +--TEST-- +parse cookie +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +object(stdClass)%a { + ["cookies"]=> + array(3) { + ["name"]=> + string(5) "value" + ["foo"]=> + string(7) "bar"baz" + ["hey"]=> + string(6) "got"it" + } + ["extras"]=> + array(1) { + ["comment"]=> + string(0) "" + } + ["flags"]=> + int(32) + ["expires"]=> + int(1) + ["path"]=> + string(1) "/" + ["domain"]=> + string(0) "" +} +Done --- /dev/null +++ b/ext/http/tests/parse_cookie_002.phpt @@ -0,0 +1,80 @@ +--TEST-- +parse cookie +--SKIPIF-- + +--FILE-- +cookies['foo']); +var_dump(http_parse_cookie('foo;')->cookies['foo']); +var_dump(http_parse_cookie('foo ')->cookies['foo']); +var_dump(http_parse_cookie('foo ;')->cookies['foo']); +var_dump(http_parse_cookie('foo ; ')->cookies['foo']); +var_dump(http_parse_cookie('foo=')->cookies['foo']); +var_dump(http_parse_cookie('foo=;')->cookies['foo']); +var_dump(http_parse_cookie('foo =')->cookies['foo']); +var_dump(http_parse_cookie('foo =;')->cookies['foo']); +var_dump(http_parse_cookie('foo= ')->cookies['foo']); +var_dump(http_parse_cookie('foo= ;')->cookies['foo']); + +var_dump(http_parse_cookie('foo=1')->cookies['foo']); +var_dump(http_parse_cookie('foo=1;')->cookies['foo']); +var_dump(http_parse_cookie('foo=1 ;')->cookies['foo']); +var_dump(http_parse_cookie('foo= 1;')->cookies['foo']); +var_dump(http_parse_cookie('foo = 1;')->cookies['foo']); +var_dump(http_parse_cookie('foo = 1 ;')->cookies['foo']); +var_dump(http_parse_cookie('foo=1')->cookies['foo']); +var_dump(http_parse_cookie('foo= 1')->cookies['foo']); + +var_dump(http_parse_cookie('foo="1"')->cookies['foo']); +var_dump(http_parse_cookie('foo="1" ')->cookies['foo']); +var_dump(http_parse_cookie('foo="1";')->cookies['foo']); +var_dump(http_parse_cookie('foo = "1" ;')->cookies['foo']); +var_dump(http_parse_cookie('foo= "1" ')->cookies['foo']); + +var_dump(http_parse_cookie('foo=""')->cookies['foo']); +var_dump(http_parse_cookie('foo="\""')->cookies['foo']); +var_dump(http_parse_cookie('foo=" "')->cookies['foo']); +var_dump(http_parse_cookie('foo= "')->cookies['foo']); +var_dump(http_parse_cookie('foo=" ')->cookies['foo']); +var_dump(http_parse_cookie('foo= " ')->cookies['foo']); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +string(0) "" +string(0) "" +string(0) "" +string(0) "" +string(0) "" +string(0) "" +string(0) "" +string(0) "" +string(0) "" +string(0) "" +string(0) "" +string(1) "1" +string(1) "1" +string(1) "1" +string(1) "1" +string(1) "1" +string(1) "1" +string(1) "1" +string(1) "1" +string(1) "1" +string(1) "1" +string(1) "1" +string(1) "1" +string(1) "1" +string(0) "" +string(1) """ +string(1) " " +string(1) """ +string(1) """ +string(1) """ +Done --- /dev/null +++ b/ext/http/tests/parse_headers_001.phpt @@ -0,0 +1,41 @@ +--TEST-- +http_parse_headers() +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +Array +( + [Host] => Array + ( + [0] => localhost + [1] => ambigious + ) + + [Nospace] => here + [Muchspace] => there + [Empty] => + [Empty2] => + [Folded] => one + two + three +) + --- /dev/null +++ b/ext/http/tests/parse_message_001.phpt @@ -0,0 +1,18 @@ +--TEST-- +http_parse_message() +--SKIPIF-- + +--FILE-- +body; +echo "Done\n"; +--EXPECTF-- +%aTEST +%aThe document has moved%a +Done --- /dev/null +++ b/ext/http/tests/parse_message_002.phpt @@ -0,0 +1,39 @@ +--TEST-- +identity encoding trap +--SKIPIF-- + +--FILE-- + 2 + [httpVersion] => 1.1 + [responseCode] => 200 + [responseStatus] => Ok + [headers] => Array + ( + [Transfer-Encoding] => identity + [Content-Length] => 3 + [Content-Type] => text/plain + ) + + [body] => Hi! + [parentMessage] => +) +Done --- /dev/null +++ b/ext/http/tests/parse_message_003.phpt @@ -0,0 +1,31 @@ +--TEST-- +content range message +--SKIPIF-- + +--FILE-- +body); + +$message = +"HTTP/1.1 200 Ok\n". +"Content-Range: bytes 0-1/1\n\n". +"X\n"; + +$msg = http_parse_message($message); + +echo "Done\n"; +--EXPECTF-- +%aTEST +string(2) "OK" +%a Invalid Content-Range header: bytes 0-1/1 in%a +Done --- /dev/null +++ b/ext/http/tests/parse_message_004.phpt @@ -0,0 +1,115 @@ +--TEST-- +http_parse_message() recursive +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +object(stdClass)%a { + ["type"]=> + int(2) + ["httpVersion"]=> + float(1.1) + ["responseCode"]=> + int(200) + ["responseStatus"]=> + string(2) "Ok" + ["headers"]=> + array(2) { + ["Server"]=> + string(9) "Funky/1.0" + ["Content-Length"]=> + string(2) "10" + } + ["body"]=> + string(10) "1234567890" + ["parentMessage"]=> + object(stdClass)%a { + ["type"]=> + int(1) + ["httpVersion"]=> + float(1.1) + ["requestMethod"]=> + string(3) "GET" + ["requestUrl"]=> + string(1) "/" + ["headers"]=> + array(2) { + ["Host"]=> + string(15) "www.example.com" + ["Accept"]=> + string(3) "*/*" + } + ["body"]=> + string(0) "" + ["parentMessage"]=> + object(stdClass)%a { + ["type"]=> + int(2) + ["httpVersion"]=> + float(1.1) + ["responseCode"]=> + int(200) + ["responseStatus"]=> + string(2) "Ok" + ["headers"]=> + array(2) { + ["Server"]=> + string(9) "Funky/1.0" + ["Content-Length"]=> + string(2) "10" + } + ["body"]=> + string(0) "" + ["parentMessage"]=> + object(stdClass)%a { + ["type"]=> + int(1) + ["httpVersion"]=> + float(1.1) + ["requestMethod"]=> + string(4) "HEAD" + ["requestUrl"]=> + string(1) "/" + ["headers"]=> + array(2) { + ["Host"]=> + string(15) "www.example.com" + ["Accept"]=> + string(3) "*/*" + } + ["body"]=> + string(0) "" + ["parentMessage"]=> + NULL + } + } + } +} +Done --- /dev/null +++ b/ext/http/tests/parse_message_005.phpt @@ -0,0 +1,60 @@ +--TEST-- +http_parse_message() content range header w/(o) = +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +stdClass Object +( + [type] => 2 + [httpVersion] => 1.1 + [responseCode] => 206 + [responseStatus] => + [headers] => Array + ( + [Server] => Funky/1.0 + [Content-Range] => bytes 0-0/100 + ) + + [body] => 1 + [parentMessage] => stdClass Object + ( + [type] => 2 + [httpVersion] => 1.1 + [responseCode] => 206 + [responseStatus] => + [headers] => Array + ( + [Server] => Funky/1.0 + [Content-Range] => bytes: 0-0/100 + ) + + [body] => 1 + [parentMessage] => + ) + +) +Done --- /dev/null +++ b/ext/http/tests/parse_message_006.phpt @@ -0,0 +1,38 @@ +--TEST-- +mixed EOL trap +--SKIPIF-- + +--FILE-- + 2 + [httpVersion] => 1.1 + [responseCode] => 200 + [responseStatus] => Ok + [headers] => Array + ( + [Header] => Value + [Connection] => close + ) + + [body] => Bug! + [parentMessage] => +) +Done --- /dev/null +++ b/ext/http/tests/parse_params_001.phpt @@ -0,0 +1,75 @@ +--TEST-- +http_parse_params +--SKIPIF-- + +--FILE-- +params[0]); +$p = http_parse_params('a=b'); var_dump($p->params[0]); +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +object(stdClass)%a { + ["params"]=> + array(2) { + [0]=> + string(9) "text/html" + [1]=> + array(1) { + ["charset"]=> + string(10) "iso-8859-1" + } + } +} +object(stdClass)%a { + ["params"]=> + array(2) { + [0]=> + string(9) "text/html" + [1]=> + array(1) { + ["charset"]=> + string(10) "iso-8859-1" + } + } +} +object(stdClass)%a { + ["params"]=> + array(2) { + [0]=> + string(10) "attachment" + [1]=> + array(1) { + ["filename"]=> + string(13) "gol;got,a.ext" + } + } +} +object(stdClass)%a { + ["params"]=> + array(3) { + [0]=> + string(6) "public" + [1]=> + string(15) "must-revalidate" + [2]=> + array(1) { + ["max-age"]=> + string(1) "0" + } + } +} +string(1) "a" +array(1) { + ["a"]=> + string(1) "b" +} +Done --- /dev/null +++ b/ext/http/tests/persistent_handles_001.phpt @@ -0,0 +1,91 @@ +--TEST-- +persistent handles +--SKIPIF-- + +--INI-- +http.persistent.handles.limit=-1 +http.persistent.handles.ident=GLOBAL +--FILE-- + $idents) { + foreach ((array)$idents as $ident => $counts) { + if (!empty($counts["free"])) { + printf("%a, %a, %a\n", $provider, $ident, $counts["free"]); + } + } +} + +http_get("http://www.google.com/", null, $info[]); + +echo "One free request handle within GLOBAL: "; +var_dump(http_persistent_handles_count()->http_request["GLOBAL"]["free"]); + +echo "Reusing request handle: "; +http_get("http://www.google.com/", null, $info[]); +var_dump($info[0]["pretransfer_time"] > 10 * $info[1]["pretransfer_time"], $info[0]["pretransfer_time"], $info[1]["pretransfer_time"]); + +echo "Handles' been cleaned up:\n"; +http_persistent_handles_clean(); +print_r(http_persistent_handles_count()); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +No free handles! +One free request handle within GLOBAL: int(1) +Reusing request handle: bool(true) +float(%f) +float(%f) +Handles' been cleaned up: +stdClass Object +( + [http_request] => Array + ( + [GLOBAL] => Array + ( + [used] => 0 + [free] => 0 + ) + + ) + + [http_request_datashare] => Array + ( + [GLOBAL] => Array + ( + [used] => 0 + [free] => 0 + ) + + ) + + [http_request_datashare_lock] => Array + ( + [GLOBAL] => Array + ( + [used] => 0 + [free] => 0 + ) + + ) + + [http_request_pool] => Array + ( + [GLOBAL] => Array + ( + [used] => 0 + [free] => 0 + ) + + ) + +) +Done --- /dev/null +++ b/ext/http/tests/persistent_handles_002.phpt @@ -0,0 +1,83 @@ +--TEST-- +persistent handles +--SKIPIF-- + +--INI-- +http.persistent.handles.limit=-1 +http.persistent.handles.ident=GLOBAL +--FILE-- + $idents) { + foreach ((array)$idents as $ident => $counts) { + if (!empty($counts["free"])) { + printf("%a, %a, %a\n", $provider, $ident, $counts["free"]); + } + } +} + +http_get("http://www.google.com/", null, $info[]); + +echo "One free request handle within GLOBAL: "; +$h = http_persistent_handles_count(); +var_dump($h->http_request["GLOBAL"]["free"]); + +echo "Reusing request handle: "; +http_get("http://www.google.com/", null, $info[]); +var_dump($info[0]["pretransfer_time"] > 10 * $info[1]["pretransfer_time"], $info[0]["pretransfer_time"], $info[1]["pretransfer_time"]); + +echo "Handles' been cleaned up:\n"; +http_persistent_handles_clean(); +print_r(http_persistent_handles_count()); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +No free handles! +One free request handle within GLOBAL: int(1) +Reusing request handle: bool(true) +float(%f) +float(%f) +Handles' been cleaned up: +stdClass Object +( + [http_request] => Array + ( + [GLOBAL] => Array + ( + [used] => 0 + [free] => 0 + ) + + ) + + [http_request_datashare] => Array + ( + [GLOBAL] => Array + ( + [used] => 0 + [free] => 0 + ) + + ) + + [http_request_pool] => Array + ( + [GLOBAL] => Array + ( + [used] => 0 + [free] => 0 + ) + + ) + +) +Done --- /dev/null +++ b/ext/http/tests/persistent_handles_003.phpt @@ -0,0 +1,62 @@ +--TEST-- +persistent handles +--SKIPIF-- + +--INI-- +http.persistent.handles.limit=-1 +http.persistent.handles.ident=GLOBAL +--FILE-- + $idents) { + foreach ((array)$idents as $ident => $counts) { + if (!empty($counts["free"])) { + printf("%a, %a, %a\n", $provider, $ident, $counts["free"]); + } + } +} + +http_get("http://www.google.com/", null, $info[]); + +echo "One free request handle within GLOBAL: "; +$h = http_persistent_handles_count(); +var_dump($h->http_request["GLOBAL"]["free"]); + +echo "Reusing request handle: "; +http_get("http://www.google.com/", null, $info[]); +var_dump($info[0]["pretransfer_time"] > 10 * $info[1]["pretransfer_time"], $info[0]["pretransfer_time"], $info[1]["pretransfer_time"]); + +echo "Handles' been cleaned up:\n"; +http_persistent_handles_clean(); +print_r(http_persistent_handles_count()); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +No free handles! +One free request handle within GLOBAL: int(1) +Reusing request handle: bool(true) +float(%f) +float(%f) +Handles' been cleaned up: +stdClass Object +( + [http_request] => Array + ( + [GLOBAL] => Array + ( + [used] => 0 + [free] => 0 + ) + + ) + +) +Done --- /dev/null +++ b/ext/http/tests/redirect_011.phpt @@ -0,0 +1,24 @@ +--TEST-- +http_redirect() with params +--SKIPIF-- + +--ENV-- +HTTP_HOST=localhost +--FILE-- + 1, 'b' => 2)); +?> +--EXPECTF-- +Status: 302%s +X-Powered-By: PHP/%a +Location: http://localhost/redirect?a=1&b=2 +Content-type: %a + +Redirecting to http://localhost/redirect?a=1&b=2. + --- /dev/null +++ b/ext/http/tests/redirect_011_logging.phpt @@ -0,0 +1,21 @@ +--TEST-- +logging redirects +--SKIPIF-- + +--ENV-- +HTTP_HOST=example.com +--FILE-- + +--EXPECTF-- +%aTEST +%d%d%d%d-%d%d-%d%d %d%d:%d%d:%d%d [302-REDIRECT] Location: http%a <%a> +Done --- /dev/null +++ b/ext/http/tests/redirect_012.phpt @@ -0,0 +1,27 @@ +--TEST-- +http_redirect() with session +--SKIPIF-- + +--ENV-- +HTTP_HOST=localhost +--FILE-- + 1), true); +?> +--EXPECTF-- +Status: 302%s +X-Powered-By: PHP/%a +Set-Cookie: PHPSESSID=%a; path=/ +Expires: %a +Cache-Control: %a +Pragma: %a +Location: http://localhost/redirect?a=1&PHPSESSID=%a +Content-type: %a --- /dev/null +++ b/ext/http/tests/redirect_012_logging.phpt @@ -0,0 +1,22 @@ +--TEST-- +logging redirects +--SKIPIF-- + +--ENV-- +HTTP_HOST=example.com +--FILE-- + +--EXPECTF-- +%aTEST +%d%d%d%d-%d%d-%d%d %d%d:%d%d:%d%d [302-REDIRECT] Location: http%a <%a> +Done --- /dev/null +++ b/ext/http/tests/redirect_013.phpt @@ -0,0 +1,24 @@ +--TEST-- +http_redirect() permanent +--SKIPIF-- + +--ENV-- +HTTP_HOST=localhost +--FILE-- + +--EXPECTF-- +Status: 301%s +X-Powered-By: PHP/%a +Location: http://localhost/redirect +Content-type: %a + +Redirecting to http://localhost/redirect. + --- /dev/null +++ b/ext/http/tests/redirect_013_logging.phpt @@ -0,0 +1,21 @@ +--TEST-- +logging redirects +--SKIPIF-- + +--ENV-- +HTTP_HOST=example.com +--FILE-- + +--EXPECTF-- +%aTEST +%d%d%d%d-%d%d-%d%d %d%d:%d%d:%d%d [301-REDIRECT] Location: http%a <%a> +Done --- /dev/null +++ b/ext/http/tests/request_cookies.phpt @@ -0,0 +1,52 @@ +--TEST-- +urlencoded cookies +--SKIPIF-- + +--FILE-- + "val=ue"); + +$r = new HttpRequest("http://dev.iworks.at/ext-http/.print_request.php", HTTP_METH_GET, array("cookies" => $cookies)); +$r->recordHistory = true; +$r->send(); +$r->setOptions(array('encodecookies' => false)); +$r->send(); +echo $r->getHistory()->toString(true); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +GET /ext-http/.print_request.php HTTP/1.1 +User-Agent: %a +Host: dev.iworks.at +Accept: */* +Cookie: name=val%3Due +HTTP/1.1 200 OK +%a + +Array +( + [name] => val=ue +) + +GET /ext-http/.print_request.php HTTP/1.1 +User-Agent: %a +Host: dev.iworks.at +Accept: */* +Cookie: name=val=ue; +HTTP/1.1 200 OK +%a + +Array +( + [name] => val=ue +) + +Done --- /dev/null +++ b/ext/http/tests/request_etag.phpt @@ -0,0 +1,21 @@ +--TEST-- +request etag +--SKIPIF-- + +--FILE-- + '"26ad3a-5-95eb19c0"'))); +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +string(%d) "HTTP/1.1 304 Not Modified +Date: %a +Server: %a +ETag: "26ad3a-5-95eb19c0" +" +Done \ No newline at end of file --- /dev/null +++ b/ext/http/tests/request_gzip.phpt @@ -0,0 +1,51 @@ +--TEST-- +GZIP request +--SKIPIF-- + +--FILE-- + true)))); + +echo "Done\n"; +--EXPECTF-- +%aTEST +object(stdClass)%a { + ["type"]=> + int(2) + ["httpVersion"]=> + float(1.1) + ["responseCode"]=> + int(200) + ["responseStatus"]=> + string(2) "OK" + ["headers"]=> + array(8) { + %a + ["Vary"]=> + string(15) "Accept-Encoding" + ["Content-Length"]=> + string(2) "26" + ["Content-Type"]=> + string(9) "text/html" + ["X-Original-Content-Encoding"]=> + string(4) "gzip" + ["X-Original-Content-Length"]=> + string(2) "51" + } + ["body"]=> + string(26) "Array +( + [gzip] => 1 +) +" + ["parentMessage"]=> + NULL +} +Done + --- /dev/null +++ b/ext/http/tests/request_methods.phpt @@ -0,0 +1,144 @@ +--TEST-- +request methods +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +int(0) +UNKNOWN +int(0) +int(1) +GET +int(1) +int(2) +HEAD +int(2) +int(3) +POST +int(3) +int(4) +PUT +int(4) +int(5) +DELETE +int(5) +int(6) +OPTIONS +int(6) +int(7) +TRACE +int(7) +int(8) +CONNECT +int(8) +int(9) +PROPFIND +int(9) +int(10) +PROPPATCH +int(10) +int(11) +MKCOL +int(11) +int(12) +COPY +int(12) +int(13) +MOVE +int(13) +int(14) +LOCK +int(14) +int(15) +UNLOCK +int(15) +int(16) +VERSION-CONTROL +int(16) +int(17) +REPORT +int(17) +int(18) +CHECKOUT +int(18) +int(19) +CHECKIN +int(19) +int(20) +UNCHECKOUT +int(20) +int(21) +MKWORKSPACE +int(21) +int(22) +UPDATE +int(22) +int(23) +LABEL +int(23) +int(24) +MERGE +int(24) +int(25) +BASELINE-CONTROL +int(25) +int(26) +MKACTIVITY +int(26) +int(27) +ACL +int(27) +int(0) +UNKNOWN +int(0) +int(28) +int(28) +int(29) +int(29) +int(30) +int(30) +int(31) +int(31) +int(32) +int(32) +bool(true) +int(0) +int(0) +bool(true) +int(0) +int(0) +bool(true) +int(0) +int(0) +bool(true) +int(0) +int(0) +bool(true) +int(0) +int(0) +Done --- /dev/null +++ b/ext/http/tests/request_put_data.phpt @@ -0,0 +1,22 @@ +--TEST-- +http_put_data() +--SKIPIF-- + +--FILE-- + CURLBUF_SIZE */); +$resp = http_put_data("http://dev.iworks.at/ext-http/.print_put.php5", $data); +$mess = http_parse_message($resp); +var_dump($data === $mess->body); + +echo "Done\n"; +?> +--EXPECTF-- +%aTEST +bool(true) +Done --- /dev/null +++ b/ext/http/tests/send_data_001.phpt @@ -0,0 +1,24 @@ +--TEST-- +http_send_data() NIL-NUM range +--SKIPIF-- + +--ENV-- +HTTP_RANGE=bytes=-5 +--FILE-- + +--EXPECTF-- +Status: 206%s +X-Powered-By: PHP/%s +Content-Type: text/plain +Accept-Ranges: bytes +Content-Range: bytes 5995-5999/6000 +Content-Length: 5 + +23abc --- /dev/null +++ b/ext/http/tests/send_data_002.phpt @@ -0,0 +1,24 @@ +--TEST-- +http_send_data() NUM-NUM range +--SKIPIF-- + +--ENV-- +HTTP_RANGE=bytes=5-6 +--FILE-- + +--EXPECTF-- +Status: 206%s +X-Powered-By: PHP/%s +Content-Type: text/plain +Accept-Ranges: bytes +Content-Range: bytes 5-6/6000 +Content-Length: 2 + +c1 --- /dev/null +++ b/ext/http/tests/send_data_003.phpt @@ -0,0 +1,24 @@ +--TEST-- +http_send_data() NUM-NIL range +--SKIPIF-- + +--ENV-- +HTTP_RANGE=bytes=5981- +--FILE-- + +--EXPECTF-- +Status: 206%s +X-Powered-By: PHP/%s +Content-Type: text/plain +Accept-Ranges: bytes +Content-Range: bytes 5981-5999/6000 +Content-Length: 19 + +c123abc123abc123abc --- /dev/null +++ b/ext/http/tests/send_data_004.phpt @@ -0,0 +1,22 @@ +--TEST-- +http_send_data() syntactically invalid range +--SKIPIF-- + +--ENV-- +HTTP_RANGE=bytes=123,-wtf ? +--FILE-- + +--EXPECTF-- +X-Powered-By: PHP/%s +Content-Type: text/plain +Accept-Ranges: bytes +Content-Length: 6000 + +123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc \ No newline at end of file --- /dev/null +++ b/ext/http/tests/send_data_005.phpt @@ -0,0 +1,17 @@ +--TEST-- +http_send_data() oversized range +--SKIPIF-- + +--ENV-- +HTTP_RANGE=bytes=5990-6000 +--FILE-- + +--EXPECTF-- +Status: 416%a \ No newline at end of file --- /dev/null +++ b/ext/http/tests/send_data_006.phpt @@ -0,0 +1,38 @@ +--TEST-- +http_send_data() multiple ranges +--SKIPIF-- + +--ENV-- +HTTP_RANGE=bytes=0-3, 4-5,9-11 +--FILE-- + +--EXPECTF-- +Status: 206%s +X-Powered-By: PHP/%s +Accept-Ranges: bytes +Content-Type: multipart/byteranges; boundary=%d.%d + + +--%d.%d +Content-Type: text/plain +Content-Range: bytes 0-3/6000 + +123a +--%d.%d +Content-Type: text/plain +Content-Range: bytes 4-5/6000 + +bc +--%d.%d +Content-Type: text/plain +Content-Range: bytes 9-11/6000 + +abc +--%d.%d-- --- /dev/null +++ b/ext/http/tests/send_data_010.phpt @@ -0,0 +1,20 @@ +--TEST-- +http_send_data() HTTP_SENDBUF_SIZE long string +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +X-Powered-By: PHP/%s +Accept-Ranges: bytes +Content-Length: 20 +Content-type: %s + +00000000000000000000 --- /dev/null +++ b/ext/http/tests/send_data_011.phpt @@ -0,0 +1,22 @@ +--TEST-- +http_send_data() last modified caching +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +X-Powered-By: PHP/%s +Cache-Control: private, must-revalidate, max-age=0 +Last-Modified: %s, %d %s %d %d:%d:%d GMT +Accept-Ranges: bytes +Content-Length: 4 +Content-type: %s + +abc --- /dev/null +++ b/ext/http/tests/send_failed_precond_001.phpt @@ -0,0 +1,23 @@ +--TEST-- +http_send() failed precondition +--SKIPIF-- + +--ENV-- +HTTP_RANGE=bytes=0-1 +HTTP_IF_UNMODIFIED_SINCE=Thu, 01 Jan 1970 00:16:40 GMT +--FILE-- + +--EXPECTF-- +Status: 412%s +X-Powered-By: %s +Cache-Control: private, must-revalidate, max-age=0 +Last-Modified: %s +Accept-Ranges: bytes +Content-type: text/html --- /dev/null +++ b/ext/http/tests/send_file_005.phpt @@ -0,0 +1,38 @@ +--TEST-- +http_send_file() multiple ranges +--SKIPIF-- + +--ENV-- +HTTP_RANGE=bytes=0-3, 4-5,9-11 +--FILE-- + +--EXPECTF-- +Status: 206%s +X-Powered-By: PHP/%s +Accept-Ranges: bytes +Content-Type: multipart/byteranges; boundary=%d.%d + + +--%d.%d +Content-Type: text/plain +Content-Range: bytes 0-3/1010 + +0123 +--%d.%d +Content-Type: text/plain +Content-Range: bytes 4-5/1010 + +45 +--%d.%d +Content-Type: text/plain +Content-Range: bytes 9-11/1010 + +901 +--%d.%d-- --- /dev/null +++ b/ext/http/tests/send_file_008.phpt @@ -0,0 +1,28 @@ +--TEST-- +http_send_file() +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +X-Powered-By: PHP/%s +Accept-Ranges: bytes +Content-Length: 1010 +Content-type: %s + +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 --- /dev/null +++ b/ext/http/tests/send_file_009.phpt @@ -0,0 +1,23 @@ +--TEST-- +http_send_file() NUM-NUM range +--SKIPIF-- + +--ENV-- +HTTP_RANGE=bytes=5-9 +--FILE-- + +--EXPECTF-- +Status: 206%s +X-Powered-By: PHP/%s +Accept-Ranges: bytes +Content-Range: bytes 5-9/1010 +Content-Length: 5 +Content-type: %s + +56789 --- /dev/null +++ b/ext/http/tests/send_file_010.phpt @@ -0,0 +1,23 @@ +--TEST-- +http_send_file() NIL-NUM range +--SKIPIF-- + +--ENV-- +HTTP_RANGE=bytes=-9 +--FILE-- + +--EXPECTF-- +Status: 206%s +X-Powered-By: PHP/%s +Accept-Ranges: bytes +Content-Range: bytes 1001-1009/1010 +Content-Length: 9 +Content-type: %s + +23456789 --- /dev/null +++ b/ext/http/tests/send_file_011.phpt @@ -0,0 +1,23 @@ +--TEST-- +http_send_file() NUM-NIL range +--SKIPIF-- + +--ENV-- +HTTP_RANGE=bytes=1000- +--FILE-- + +--EXPECTF-- +Status: 206%s +X-Powered-By: PHP/%s +Accept-Ranges: bytes +Content-Range: bytes 1000-1009/1010 +Content-Length: 10 +Content-type: %s + +123456789 --- /dev/null +++ b/ext/http/tests/send_file_012.phpt @@ -0,0 +1,30 @@ +--TEST-- +http_send_file() syntactically invalid range +--SKIPIF-- + +--ENV-- +HTTP_RANGE=bytes=xxx +--FILE-- + +--EXPECTF-- +X-Powered-By: PHP/%s +Accept-Ranges: bytes +Content-Length: 1010 +Content-type: %s + +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 --- /dev/null +++ b/ext/http/tests/send_file_013.phpt @@ -0,0 +1,19 @@ +--TEST-- +http_send_file() oversized range +--SKIPIF-- + +--ENV-- +HTTP_RANGE=bytes=-1111 +--FILE-- + +--EXPECTF-- +Status: 416 +X-Powered-By: PHP/%s +Accept-Ranges: bytes +Content-type: %s --- /dev/null +++ b/ext/http/tests/send_ifrange_001.phpt @@ -0,0 +1,27 @@ +--TEST-- +http_send() If-Range +--SKIPIF-- + +--ENV-- +HTTP_RANGE=bytes=0-1 +HTTP_IF_RANGE="abc" +--FILE-- + +--EXPECTF-- +Status: 206%s +X-Powered-By: %s +Cache-Control: private, must-revalidate, max-age=0 +ETag: "abc" +Accept-Ranges: bytes +Content-Range: bytes 0-1/%d +Content-Length: 2 +Content-type: text/html + + +--ENV-- +HTTP_RANGE=bytes=0-1 +HTTP_IF_RANGE="abcd" +--FILE-- + +--EXPECTF-- +X-Powered-By: %s +Cache-Control: private, must-revalidate, max-age=0 +ETag: "abc" +Accept-Ranges: bytes +Content-Length: %d +Content-type: text/html + +%a --- /dev/null +++ b/ext/http/tests/skip.inc @@ -0,0 +1,15 @@ += v%s",$ver)); } +function checkmax($ver) { skipif(version_compare(PHP_VERSION, $ver) > 0, sprintf("need PHP <= v%s",$ver)); } +function checkurl($url) { skipif(!@fsockopen($url, 80), "$url not responsive"); } +function checkcls($cls) { skipif(!class_exists($cls), "need class $cls"); } +function checkver($ver) { checkmin($ver); } +checkext('http'); +?> --- /dev/null +++ b/ext/http/tests/stream_filters_001.phpt @@ -0,0 +1,45 @@ +--TEST-- +stream filters +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +string(30) "5 +Here +2 +we +5 + go! + +0 +" +string(12) "Here we go! +" +Done --- /dev/null +++ b/ext/http/tests/stream_filters_002.phpt @@ -0,0 +1,50 @@ +--TEST-- +gzip stream filters +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +bool(true) +bool(true) +bool(true) +Done --- /dev/null +++ b/ext/http/tests/stream_filters_003.phpt @@ -0,0 +1,42 @@ +--TEST-- +stream filter fun +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%aTEST +This is some stream filter fun; we'll see if it bails out or not. +The text should come out at the other end of the stream exactly like written to it. +Go figure! --- /dev/null +++ b/ext/http/tests/urls.txt @@ -0,0 +1,49 @@ +http://www.microsoft.com +http://www.opensource.org +http://www.google.com +http://www.yahoo.com +http://www.ibm.com +http://www.mysql.com +http://www.oracle.com +http://www.ripe.net +http://www.iana.org +http://www.amazon.com +http://www.netcraft.com +http://www.heise.de +http://www.chip.de +http://www.ca.com +http://www.cnet.com +http://www.news.com +http://www.cnn.com +http://www.wikipedia.org +http://www.dell.com +http://www.hp.com +http://www.cert.org +http://www.mit.edu +http://www.nist.gov +http://www.ebay.com +http://www.playstation.com +http://www.uefa.com +http://www.ieee.org +http://www.apple.com +http://www.sony.com +http://www.symantec.com +http://www.zdnet.com +http://www.fujitsu.com +http://www.supermicro.com +http://www.hotmail.com +http://www.ecma.com +http://www.bbc.co.uk +http://news.google.com +http://www.foxnews.com +http://www.msn.com +http://www.wired.com +http://www.sky.com +http://www.usatoday.com +http://www.cbs.com +http://www.nbc.com +http://slashdot.org +http://www.bloglines.com +http://www.techweb.com +http://www.newslink.org +http://www.un.org \ No newline at end of file