From 34644fd4c700fb55c3a2c2ff9966d0f5e7572509 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Fri, 11 Nov 2005 17:06:37 +0000 Subject: [PATCH] - poor stream filter for chunked encoding --- KnownIssues.txt | 7 +- config.m4 | 3 +- config.w32 | 3 +- http.c | 2 + http_filter_api.c | 267 ++++++++++++++++++++++++++++++++++ http_request_api.c | 16 +- package2.xml | 3 + php_http_filter_api.h | 30 ++++ tests/stream_filters_001.phpt | 40 +++++ 9 files changed, 357 insertions(+), 14 deletions(-) create mode 100644 http_filter_api.c create mode 100644 php_http_filter_api.h create mode 100644 tests/stream_filters_001.phpt diff --git a/KnownIssues.txt b/KnownIssues.txt index d0dc686..b754e2d 100644 --- a/KnownIssues.txt +++ b/KnownIssues.txt @@ -13,8 +13,11 @@ See also http://bugs.php.net/bug.php?id=34429 If you keep getting "SSL connect error" when trying to issue requests on Windows, try another (newer) libeay32.dll/ssleay32.dll pair. +Inflating compressed HttpRequest responses happens twice if libcurl +was built with zlib support. Internals: - the request bodies created in http_request_pool_attach() are not - destroyed in http_request_pool_detach(); may be a memory problem - in long running scripts + destroyed in http_request_pool_detach() but on reset; + may be a memory problem in long running scripts + diff --git a/config.m4 b/config.m4 index 1952cd9..9b6176f 100644 --- a/config.m4 +++ b/config.m4 @@ -191,7 +191,8 @@ dnl ---- http_response_object.c http_exception_object.c http_requestpool_object.c \ http_api.c http_cache_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_info_api.c http_request_method_api.c http_encoding_api.c \ + http_filter_api.c" PHP_NEW_EXTENSION([http], $PHP_HTTP_SOURCES, $ext_shared) PHP_ADD_BUILD_DIR($ext_builddir/phpstr, 1) PHP_SUBST([HTTP_SHARED_LIBADD]) diff --git a/config.w32 b/config.w32 index 1cc13f5..8120ff2 100644 --- a/config.w32 +++ b/config.w32 @@ -12,7 +12,8 @@ if (PHP_HTTP != "no") { "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_info_api.c http_request_method_api.c http_encoding_api.c "+ + "http_filter_api.c", null, "/I\"" + configure_module_dirname + "/phpstr\""); ADD_SOURCES(configure_module_dirname + "/phpstr", "phpstr.c", "http"); diff --git a/http.c b/http.c index b81c8cb..244c423 100644 --- a/http.c +++ b/http.c @@ -30,6 +30,7 @@ #include "php_http_send_api.h" #include "php_http_cache_api.h" #include "php_http_headers_api.h" +#include "php_http_filter_api.h" #include "php_http_request_method_api.h" #ifdef HTTP_HAVE_CURL # include "php_http_request_api.h" @@ -292,6 +293,7 @@ PHP_MINIT_FUNCTION(http) if ( (SUCCESS != PHP_MINIT_CALL(http_support)) || (SUCCESS != PHP_MINIT_CALL(http_headers)) || (SUCCESS != PHP_MINIT_CALL(http_cache)) || + (SUCCESS != PHP_MINIT_CALL(http_filter)) || #ifdef HTTP_HAVE_CURL (SUCCESS != PHP_MINIT_CALL(http_request)) || #endif /* HTTP_HAVE_CURL */ diff --git a/http_filter_api.c b/http_filter_api.c new file mode 100644 index 0000000..ccec761 --- /dev/null +++ b/http_filter_api.c @@ -0,0 +1,267 @@ +/* + +----------------------------------------------------------------------+ + | PECL :: http | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, that | + | is bundled with this package in the file LICENSE, and is available | + | through the world-wide-web at http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Copyright (c) 2004-2005 Michael Wallner | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "php.h" + +#include "php_http_std_defs.h" +#include "php_http_api.h" +#include "php_http_filter_api.h" + +#include "phpstr/phpstr.h" + +#include "php_streams.h" + +#ifndef HTTP_DEBUG_FILTERS +# define HTTP_DEBUG_FILTERS 0 +#endif + +/* + * TODO: phpstr is not persistent aware + */ + +typedef enum { + HFS_HEX = 0, + HFS_DATA, +} http_filter_status; + +typedef struct { + phpstr buffer; + size_t wanted; + int eollen; + int passon; + http_filter_status status; +} http_filter_buffer; + +#define PHP_STREAM_FILTER_OP_FILTER_PARAMS \ + php_stream *stream, \ + php_stream_filter *this, \ + php_stream_bucket_brigade *buckets_in, \ + php_stream_bucket_brigade *buckets_out, \ + size_t *bytes_consumed, int flags \ + TSRMLS_DC +#define PHP_STREAM_FILTER_OP_FILTER(function) \ + static php_stream_filter_status_t function(PHP_STREAM_FILTER_OP_FILTER_PARAMS) + +#define NEW_BUCKET(data, length) \ + php_stream_bucket_append(buckets_out, php_stream_bucket_new(stream, pestrndup(data, length, this->is_persistent), (length), 1, this->is_persistent TSRMLS_CC) TSRMLS_CC); + +inline void *pestrndup(const char *s, size_t l, int p) +{ + void *d = pemalloc(l + 1, p); + if (d) { + memcpy(d, s, l); + ((char *) d)[l] = 0; + } + return d; +} + +PHP_STREAM_FILTER_OP_FILTER(http_filter_chunked_decode) +{ + php_stream_bucket *ptr, *nxt; + http_filter_buffer *buffer = (http_filter_buffer *) (this->abstract); + + if (bytes_consumed) { + *bytes_consumed = 0; + } + + if (!buckets_in->head) { + return PSFS_FEED_ME; + } + +#if HTTP_DEBUG_FILTERS + fprintf(stderr, "Reading in bucket buffers "); +#endif + + /* fetch available bucket data */ + for (ptr = buckets_in->head; ptr; ptr = nxt) { + nxt = ptr->next; + phpstr_append(PHPSTR(buffer), ptr->buf, ptr->buflen); + php_stream_bucket_unlink(ptr TSRMLS_CC); + php_stream_bucket_delref(ptr TSRMLS_CC); + +#if HTTP_DEBUG_FILTERS + fprintf(stderr, "."); +#endif + } + if (bytes_consumed) { + *bytes_consumed = PHPSTR_LEN(buffer); + } + phpstr_fix(PHPSTR(buffer)); + +#if HTTP_DEBUG_FILTERS + fprintf(stderr, " done\nCurrent buffer length: %lu bytes\n", PHPSTR_LEN(buffer)); +#endif + + buffer->passon = 0; + while (1) { + if (buffer->status == HFS_HEX) { + const char *eol; + char *stop; + ulong clen; + +#if HTTP_DEBUG_FILTERS + fprintf(stderr, "Status HFS_HEX: "); +#endif + + if (!(eol = http_locate_eol(PHPSTR_VAL(buffer), &buffer->eollen))) { +#if HTTP_DEBUG_FILTERS + fprintf(stderr, "return PFSF_FEED_ME (no eol)\n"); +#endif + return buffer->passon ? PSFS_PASS_ON : PSFS_FEED_ME; + } + if (!(clen = strtoul(PHPSTR_VAL(buffer), &stop, 16))) { +#if HTTP_DEBUG_FILTERS + fprintf(stderr, "return PFSF_FEED_ME (no len)\n"); +#endif + phpstr_dtor(PHPSTR(buffer)); + return buffer->passon ? PSFS_PASS_ON : PSFS_FEED_ME; + } + + buffer->status = HFS_DATA; + buffer->wanted = clen; + phpstr_cut(PHPSTR(buffer), 0, eol + buffer->eollen - PHPSTR_VAL(buffer)); + +#if HTTP_DEBUG_FILTERS + fprintf(stderr, "read %lu bytes chunk size\n", buffer->wanted); +#endif + } + +#if HTTP_DEBUG_FILTERS + fprintf(stderr, "Current status: %s\n", buffer->status == HFS_DATA?"HFS_DATA":"HFS_HEX"); + fprintf(stderr, "Current buffer length: %lu bytes\n", PHPSTR_LEN(buffer)); +#endif + + if (buffer->status == HFS_DATA && buffer->wanted > 0 && buffer->wanted <= PHPSTR_LEN(buffer)) { + +#if HTTP_DEBUG_FILTERS + fprintf(stderr, "Passing on %lu(%lu) bytes\n", buffer->wanted, PHPSTR_LEN(buffer)); +#endif + + NEW_BUCKET(PHPSTR_VAL(buffer), buffer->wanted); + phpstr_cut(PHPSTR(buffer), 0, buffer->wanted + buffer->eollen); + buffer->wanted = 0; + buffer->eollen = 0; + buffer->passon = 1; + buffer->status = HFS_HEX; + continue; + } + return buffer->passon ? PSFS_PASS_ON : PSFS_FEED_ME; + } + + return PSFS_FEED_ME; +} + +static void http_filter_chunked_decode_dtor(php_stream_filter *this TSRMLS_DC) +{ + http_filter_buffer *b = (http_filter_buffer *) (this->abstract); + + phpstr_dtor(PHPSTR(b)); + pefree(b, this->is_persistent); +} + +PHP_STREAM_FILTER_OP_FILTER(http_filter_chunked_encode) +{ + phpstr buf; + php_stream_bucket *ptr, *nxt; + + if (bytes_consumed) { + *bytes_consumed = 0; + } + + if (!buckets_in->head) { + return PSFS_FEED_ME; + } + + phpstr_init(&buf); + for (ptr = buckets_in->head; ptr; ptr = nxt) { + if (bytes_consumed) { + *bytes_consumed += ptr->buflen; + } + nxt = ptr->next; + + phpstr_appendf(&buf, "%x" HTTP_CRLF, ptr->buflen); + phpstr_append(&buf, ptr->buf, ptr->buflen); + phpstr_appends(&buf, HTTP_CRLF); + NEW_BUCKET(PHPSTR_VAL(&buf), PHPSTR_LEN(&buf)); + PHPSTR_LEN(&buf) = 0; + + php_stream_bucket_unlink(ptr TSRMLS_CC); + php_stream_bucket_delref(ptr TSRMLS_CC); + } + phpstr_dtor(&buf); + + if (flags & PSFS_FLAG_FLUSH_CLOSE) { + NEW_BUCKET("0"HTTP_CRLF, lenof("0"HTTP_CRLF)); + } + + return PSFS_PASS_ON; +} + +static php_stream_filter_ops http_filter_ops_chunked_decode = { + http_filter_chunked_decode, + http_filter_chunked_decode_dtor, + "http.chunked_decode" +}; + +static php_stream_filter_ops http_filter_ops_chunked_encode = { + http_filter_chunked_encode, + NULL, + "http.chunked_encode" +}; + +static php_stream_filter *http_filter_create(const char *name, zval *params, int p TSRMLS_DC) +{ + php_stream_filter *f = NULL; + void *b = NULL; + + if (!strcasecmp(name, "http.chunked_decode")) { + if (b = pecalloc(1, sizeof(http_filter_buffer), p)) { + phpstr_init(PHPSTR(b)); + if (!(f = php_stream_filter_alloc(&http_filter_ops_chunked_decode, b, p))) { + pefree(b, p); + } + } + } else + if (!strcasecmp(name, "http.chunked_encode")) { + f = php_stream_filter_alloc(&http_filter_ops_chunked_encode, NULL, p); + } + + return f; +} + +php_stream_filter_factory http_filter_factory = { + http_filter_create +}; + +PHP_MINIT_FUNCTION(http_filter) +{ + php_stream_filter_register_factory("http.*", &http_filter_factory TSRMLS_CC); + return SUCCESS; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + diff --git a/http_request_api.c b/http_request_api.c index 5d6b8f2..d94e558 100644 --- a/http_request_api.c +++ b/http_request_api.c @@ -721,16 +721,12 @@ PHP_HTTP_API STATUS _http_request_exec(CURL *ch, HashTable *info, phpstr *respon http_request_conv(ch, response, request); /* perform request */ - switch (result = curl_easy_perform(ch)) - { - default: - http_error(HE_WARNING, HTTP_E_REQUEST, curl_easy_strerror(result)); - case CURLE_OK: - /* get curl info */ - if (info) { - http_request_info(ch, info); - } - break; + if (CURLE_OK != (result = curl_easy_perform(ch))) { + http_error(HE_WARNING, HTTP_E_REQUEST, curl_easy_strerror(result)); + } + /* get curl info */ + if (info) { + http_request_info(ch, info); } /* always succeeds */ return SUCCESS; diff --git a/package2.xml b/package2.xml index 94bf053..669a9fd 100644 --- a/package2.xml +++ b/package2.xml @@ -78,6 +78,7 @@ + @@ -99,6 +100,7 @@ + @@ -190,6 +192,7 @@ + diff --git a/php_http_filter_api.h b/php_http_filter_api.h new file mode 100644 index 0000000..d77e97b --- /dev/null +++ b/php_http_filter_api.h @@ -0,0 +1,30 @@ +/* + +--------------------------------------------------------------------+ + | 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-2005, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_HTTP_FILTER_API_H +#define PHP_HTTP_FILTER_API_H + +PHP_MINIT_FUNCTION(http_filter); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + diff --git a/tests/stream_filters_001.phpt b/tests/stream_filters_001.phpt new file mode 100644 index 0000000..fe28774 --- /dev/null +++ b/tests/stream_filters_001.phpt @@ -0,0 +1,40 @@ +--TEST-- +stream filters +--SKIPIF-- + +--FILE-- +