- poor stream filter for chunked encoding
authorMichael Wallner <mike@php.net>
Fri, 11 Nov 2005 17:06:37 +0000 (17:06 +0000)
committerMichael Wallner <mike@php.net>
Fri, 11 Nov 2005 17:06:37 +0000 (17:06 +0000)
KnownIssues.txt
config.m4
config.w32
http.c
http_filter_api.c [new file with mode: 0644]
http_request_api.c
package2.xml
php_http_filter_api.h [new file with mode: 0644]
tests/stream_filters_001.phpt [new file with mode: 0644]

index d0dc686..b754e2d 100644 (file)
@@ -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
+
index 1952cd9..9b6176f 100644 (file)
--- 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])
index 1cc13f5..8120ff2 100644 (file)
@@ -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 (file)
--- 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 (file)
index 0000000..ccec761
--- /dev/null
@@ -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 <mike@php.net>               |
+   +----------------------------------------------------------------------+
+*/
+
+/* $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
+ */
+
index 5d6b8f2..d94e558 100644 (file)
@@ -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;
index 94bf053..669a9fd 100644 (file)
@@ -78,6 +78,7 @@
    <file role="src" name="php_http_cache_api.h"/>
    <file role="src" name="php_http_date_api.h"/>
    <file role="src" name="php_http_encoding_api.h"/>
+   <file role="src" name="php_http_filter_api.h"/>
    <file role="src" name="php_http_headers_api.h"/>
    <file role="src" name="php_http_info_api.h"/>
    <file role="src" name="php_http_message_api.h"/>
    <file role="src" name="http_cache_api.c"/>
    <file role="src" name="http_date_api.c"/>
    <file role="src" name="http_encoding_api.c"/>
+   <file role="src" name="http_filter_api.c"/>
    <file role="src" name="http_headers_api.c"/>
    <file role="src" name="http_info_api.c"/>
    <file role="src" name="http_message_api.c"/>
     <file role="test" name="redirect_013_logging.phpt"/>
     <file role="test" name="request_gzip.phpt"/>
     <file role="test" name="request_methods.phpt"/>
+    <file role="text" name="stream_filters.phpt"/>
     <file role="test" name="send_data_001.phpt"/>
     <file role="test" name="send_data_002.phpt"/>
     <file role="test" name="send_data_003.phpt"/>
diff --git a/php_http_filter_api.h b/php_http_filter_api.h
new file mode 100644 (file)
index 0000000..d77e97b
--- /dev/null
@@ -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 <mike@php.net>            |
+    +--------------------------------------------------------------------+
+*/
+
+/* $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 (file)
index 0000000..fe28774
--- /dev/null
@@ -0,0 +1,40 @@
+--TEST--
+stream filters
+--SKIPIF--
+<?php
+include 'skip.inc';
+?>
+--FILE--
+<?php
+echo "-TEST\n";
+
+define('F', 'http.test_stream_filters');
+
+$f = fopen(F, 'w');
+stream_filter_append($f, 'http.chunked_encode');
+
+fwrite($f, "Here ");
+fwrite($f, "we");
+fwrite($f, " go!\n");
+fclose($f);
+
+var_dump(file_get_contents(F));
+
+$f = fopen(F, 'r');
+stream_filter_append($f, 'http.chunked_decode');
+var_dump(fread($f, 256));
+
+echo "Done\n";
+--EXPECTF--
+%sTEST
+string(27) "5
+Here 
+2
+we
+5
+ go!
+
+"
+string(12) "Here we go!
+"
+Done