dmitry          Thu Apr 16 10:16:27 2009 UTC

  Added files:                 (Branch: PHP_5_3)
    /php-src/ext/standard/tests/filters chunked_001.phpt 

  Modified files:              
    /php-src    NEWS 
    /php-src/ext/standard       filters.c http_fopen_wrapper.c 
  Log:
  - Added "dechunk" filter which can decode HTTP responces with chunked 
transfer-encoding. HTTP streams use this filter automatically in case 
"Transfer-Encoding: chunked" header presents in responce. It's possible to 
disable this behaviour using "http"=>array("auto_decode"=>0) in stream context
  - Fixed bug #47021 (SoapClient stumbles over WSDL delivered with 
"Transfer-Encoding: chunked")
  
  
http://cvs.php.net/viewvc.cgi/php-src/NEWS?r1=1.2027.2.547.2.965.2.559&r2=1.2027.2.547.2.965.2.560&diff_format=u
Index: php-src/NEWS
diff -u php-src/NEWS:1.2027.2.547.2.965.2.559 
php-src/NEWS:1.2027.2.547.2.965.2.560
--- php-src/NEWS:1.2027.2.547.2.965.2.559       Thu Apr 16 06:47:36 2009
+++ php-src/NEWS        Thu Apr 16 10:16:26 2009
@@ -6,6 +6,11 @@
 - Upgraded bundled PCRE to version 7.9. (Nuno)
 - Added 'n' flag to fopen to allow passing O_NONBLOCK to the underlying
   open(2) system call. (Mikko)
+- Added "dechunk" filter which can decode HTTP responces with chunked
+  transfer-encoding. HTTP streams use this filter automatically in case
+  "Transfer-Encoding: chunked" header presents in responce. It's possible to
+  disable this behaviour using "http"=>array("auto_decode"=>0) in stream
+  context. (Dmitry)
 
 - Fixed bug #47880 (crashes in call_user_func_array()). (Dmitry)
 - Fixed bug #47856 (stristr() converts needle to lower-case). (Ilia)
@@ -28,6 +33,8 @@
 - Fixed bug #47516 (nowdoc can not be embed in heredoc but can be embed in
   double quote). (Dmitry)
 - Fixed bug #47038 (Memory leak in include). (Dmitry)
+- Fixed bug #47021 (SoapClient stumbles over WSDL delivered with
+  "Transfer-Encoding: chunked"). (Dmitry)
 - Fixed bug #46108 (DateTime - Memory leak when unserializing). (Felipe)
 - Fixed bug #44861 (scrollable cursor don't work with pgsql). (Matteo)
 - Fixed bug #44409 (PDO::FETCH_SERIALIZE calls __construct()). (Matteo)
http://cvs.php.net/viewvc.cgi/php-src/ext/standard/filters.c?r1=1.44.2.6.2.4.2.3&r2=1.44.2.6.2.4.2.4&diff_format=u
Index: php-src/ext/standard/filters.c
diff -u php-src/ext/standard/filters.c:1.44.2.6.2.4.2.3 
php-src/ext/standard/filters.c:1.44.2.6.2.4.2.4
--- php-src/ext/standard/filters.c:1.44.2.6.2.4.2.3     Wed Dec 31 11:15:45 2008
+++ php-src/ext/standard/filters.c      Thu Apr 16 10:16:27 2009
@@ -20,7 +20,7 @@
    +----------------------------------------------------------------------+
 */
 
-/* $Id: filters.c,v 1.44.2.6.2.4.2.3 2008/12/31 11:15:45 sebastian Exp $ */
+/* $Id: filters.c,v 1.44.2.6.2.4.2.4 2009/04/16 10:16:27 dmitry Exp $ */
 
 #include "php.h"
 #include "php_globals.h"
@@ -1897,6 +1897,220 @@
 
 /* }}} */
 
+/* {{{ chunked filter implementation */
+typedef enum _php_chunked_filter_state {
+       CHUNK_SIZE_START,
+       CHUNK_SIZE,
+       CHUNK_SIZE_EXT_START,
+       CHUNK_SIZE_EXT,
+       CHUNK_SIZE_CR,
+       CHUNK_SIZE_LF,
+       CHUNK_BODY,
+       CHUNK_BODY_CR,
+       CHUNK_BODY_LF,
+       CHUNK_TRAILER,
+       CHUNK_ERROR
+} php_chunked_filter_state;
+
+typedef struct _php_chunked_filter_data {
+       php_chunked_filter_state state;
+       int chunk_size;
+       int persistent;
+} php_chunked_filter_data;
+
+static int php_dechunk(char *buf, int len, php_chunked_filter_data *data)
+{
+       char *p = buf;
+       char *end = p + len;
+       char *out = buf;
+       int out_len = 0;
+
+       while (p < end) {
+               switch (data->state) {
+                       case CHUNK_SIZE_START:
+                               data->chunk_size = 0;
+                       case CHUNK_SIZE:
+                               while (p < end) {
+                                       if (*p >= '0' && *p <= '9') {
+                                               data->chunk_size = 
(data->chunk_size * 16) + (*p - '0');
+                                       } else if (*p >= 'A' && *p <= 'F') {
+                                               data->chunk_size = 
(data->chunk_size * 16) + (*p - 'A' + 10);
+                                       } else if (*p >= 'a' && *p <= 'f') {
+                                               data->chunk_size = 
(data->chunk_size * 16) + (*p - 'a' + 10);
+                                       } else if (data->state == 
CHUNK_SIZE_START) {
+                                               data->state = CHUNK_ERROR;
+                                               break;
+                                       } else {
+                                               data->state = 
CHUNK_SIZE_EXT_START;
+                                               break;
+                                       }
+                                       data->state = CHUNK_SIZE;
+                                       p++;
+                               }
+                               if (data->state == CHUNK_ERROR) {
+                                       continue;
+                               } else if (p == end) {
+                                       return out_len;
+                               }
+                       case CHUNK_SIZE_EXT_START:
+                               if (*p == ';'|| *p == '\r' || *p == '\n') {
+                                       data->state = CHUNK_SIZE_EXT;
+                               } else {
+                                       data->state = CHUNK_ERROR;
+                                       continue;
+                               }
+                       case CHUNK_SIZE_EXT:
+                               /* skip extension */
+                               while (p < end && *p != '\r' && *p != '\n') {
+                                       p++;
+                               }
+                               if (p == end) {
+                                       return out_len;
+                               }
+                       case CHUNK_SIZE_CR:
+                               if (*p == '\r') {
+                                       p++;
+                                       if (p == end) {
+                                               data->state = CHUNK_SIZE_LF;
+                                               return out_len;
+                                       }
+                               }
+                       case CHUNK_SIZE_LF:
+                               if (*p == '\n') {
+                                       p++;
+                                       if (data->chunk_size == 0) {
+                                               /* last chunk */
+                                               data->state = CHUNK_TRAILER;
+                                               continue;
+                                       } else if (p == end) {
+                                               data->state = CHUNK_BODY;
+                                               return out_len;
+                                       }
+                               } else {
+                                       data->state = CHUNK_ERROR;
+                                       continue;
+                               }
+                       case CHUNK_BODY:
+                               if (end - p >= data->chunk_size) {
+                                       if (p != out) {
+                                               memmove(out, p, 
data->chunk_size);
+                                       }
+                                       out += data->chunk_size;
+                                       out_len += data->chunk_size;
+                                       p += data->chunk_size;
+                                       if (p == end) {
+                                               data->state = CHUNK_BODY_CR;
+                                               return out_len;
+                                       }
+                               } else {
+                                       if (p != out) {
+                                               memmove(out, p, end - p);
+                                       }
+                                       data->chunk_size -= end - p;
+                                       out_len += end - p;
+                                       return out_len;
+                               }
+                       case CHUNK_BODY_CR:
+                               if (*p == '\r') {
+                                       p++;
+                                       if (p == end) {
+                                               data->state = CHUNK_BODY_LF;
+                                               return out_len;
+                                       }
+                               }
+                       case CHUNK_BODY_LF:
+                               if (*p == '\n') {
+                                       p++;
+                                       data->state = CHUNK_SIZE_START;
+                                       continue;
+                               } else {
+                                       data->state = CHUNK_ERROR;
+                                       continue;
+                               }
+                       case CHUNK_TRAILER:
+                               /* ignore trailer */
+                               p = end;
+                               continue;
+                       case CHUNK_ERROR:
+                               if (p != out) {
+                                       memmove(out, p, end - p);
+                               }
+                               out_len += end - p;
+                               return out_len; 
+               }
+       }
+       return out_len;
+}
+
+static php_stream_filter_status_t php_chunked_filter(
+       php_stream *stream,
+       php_stream_filter *thisfilter,
+       php_stream_bucket_brigade *buckets_in,
+       php_stream_bucket_brigade *buckets_out,
+       size_t *bytes_consumed,
+       int flags
+       TSRMLS_DC)
+{
+       php_stream_bucket *bucket;
+       size_t consumed = 0;
+       php_chunked_filter_data *data = (php_chunked_filter_data *) 
thisfilter->abstract;
+
+       while (buckets_in->head) {
+               bucket = php_stream_bucket_make_writeable(buckets_in->head 
TSRMLS_CC);
+               consumed += bucket->buflen;
+               bucket->buflen = php_dechunk(bucket->buf, bucket->buflen, 
data);        
+               php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
+       }
+
+       if (bytes_consumed) {
+               *bytes_consumed = consumed;
+       }
+       
+       return PSFS_PASS_ON;
+}
+
+static void php_chunked_dtor(php_stream_filter *thisfilter TSRMLS_DC)
+{
+       if (thisfilter && thisfilter->abstract) {
+               php_chunked_filter_data *data = (php_chunked_filter_data *) 
thisfilter->abstract;
+               pefree(data, data->persistent);
+       }
+}
+
+static php_stream_filter_ops chunked_filter_ops = {
+       php_chunked_filter,
+       php_chunked_dtor,
+       "dechunk"
+};
+
+static php_stream_filter *chunked_filter_create(const char *filtername, zval 
*filterparams, int persistent TSRMLS_DC)
+{
+       php_stream_filter_ops *fops = NULL;
+       php_chunked_filter_data *data;
+
+       if (strcasecmp(filtername, "dechunk")) {
+               return NULL;
+       }
+
+       /* Create this filter */
+       data = (php_chunked_filter_data *)pecalloc(1, 
sizeof(php_chunked_filter_data), persistent);
+       if (!data) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating 
%zd bytes", sizeof(php_chunked_filter_data));
+               return NULL;
+       }
+       data->state = CHUNK_SIZE_START;
+       data->chunk_size = 0;
+       data->persistent = persistent;
+       fops = &chunked_filter_ops;
+
+       return php_stream_filter_alloc(fops, data, persistent);
+}
+
+static php_stream_filter_factory chunked_filter_factory = {
+       chunked_filter_create
+};
+/* }}} */
+
 static const struct {
        php_stream_filter_ops *ops;
        php_stream_filter_factory *factory;
@@ -1907,6 +2121,7 @@
        { &strfilter_strip_tags_ops, &strfilter_strip_tags_factory },
        { &strfilter_convert_ops, &strfilter_convert_factory },
        { &consumed_filter_ops, &consumed_filter_factory },
+       { &chunked_filter_ops, &chunked_filter_factory },
        /* additional filters to go here */
        { NULL, NULL }
 };
http://cvs.php.net/viewvc.cgi/php-src/ext/standard/http_fopen_wrapper.c?r1=1.99.2.12.2.9.2.12&r2=1.99.2.12.2.9.2.13&diff_format=u
Index: php-src/ext/standard/http_fopen_wrapper.c
diff -u php-src/ext/standard/http_fopen_wrapper.c:1.99.2.12.2.9.2.12 
php-src/ext/standard/http_fopen_wrapper.c:1.99.2.12.2.9.2.13
--- php-src/ext/standard/http_fopen_wrapper.c:1.99.2.12.2.9.2.12        Wed Dec 
31 11:15:45 2008
+++ php-src/ext/standard/http_fopen_wrapper.c   Thu Apr 16 10:16:27 2009
@@ -19,7 +19,7 @@
    |          Sara Golemon <[email protected]>                              |
    +----------------------------------------------------------------------+
  */
-/* $Id: http_fopen_wrapper.c,v 1.99.2.12.2.9.2.12 2008/12/31 11:15:45 
sebastian Exp $ */ 
+/* $Id: http_fopen_wrapper.c,v 1.99.2.12.2.9.2.13 2009/04/16 10:16:27 dmitry 
Exp $ */ 
 
 #include "php.h"
 #include "php_globals.h"
@@ -111,6 +111,7 @@
        char *user_headers = NULL;
        int header_init = ((flags & HTTP_WRAPPER_HEADER_INIT) != 0);
        int redirected = ((flags & HTTP_WRAPPER_REDIRECTED) != 0);
+       php_stream_filter *transfer_encoding = NULL;
 
        tmp_line[0] = '\0';
 
@@ -597,6 +598,25 @@
                        } else if (!strncasecmp(http_header_line, 
"Content-Length: ", 16)) {
                                file_size = atoi(http_header_line + 16);
                                php_stream_notify_file_size(context, file_size, 
http_header_line, 0);
+                       } else if (!strncasecmp(http_header_line, 
"Transfer-Encoding: chunked", sizeof("Transfer-Encoding: chunked"))) {
+
+                               /* create filter to decode response body */
+                               if (!(options & STREAM_ONLY_GET_HEADERS)) {
+                                       long decode = 1;
+
+                                       if (context && 
php_stream_context_get_option(context, "http", "auto_decode", &tmpzval) == 
SUCCESS) {
+                                               SEPARATE_ZVAL(tmpzval);
+                                               convert_to_boolean(*tmpzval);
+                                               decode = Z_LVAL_PP(tmpzval);
+                                       }
+                                       if (decode) {
+                                               transfer_encoding = 
php_stream_filter_create("dechunk", NULL, php_stream_is_persistent(stream) 
TSRMLS_CC);
+                                               if (transfer_encoding) {
+                                                       /* don't store 
transfer-encodeing header */
+                                                       continue;
+                                               }
+                                       }
+                               }
                        }
 
                        if (http_header_line[0] == '\0') {
@@ -740,6 +760,11 @@
                 * the stream */
                stream->position = 0;
 
+               if (transfer_encoding) {
+                       php_stream_filter_append(&stream->readfilters, 
transfer_encoding);
+               }
+       } else if (transfer_encoding) {
+               php_stream_filter_free(transfer_encoding TSRMLS_CC);
        }
 
        return stream;

http://cvs.php.net/viewvc.cgi/php-src/ext/standard/tests/filters/chunked_001.phpt?view=markup&rev=1.1
Index: php-src/ext/standard/tests/filters/chunked_001.phpt
+++ php-src/ext/standard/tests/filters/chunked_001.phpt

-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to