dmitry          Thu Apr 16 10:34:15 2009 UTC

  Modified files:              
    /php-src/ext/standard       filters.c http_fopen_wrapper.c 
    /php-src/ext/standard/tests/filters chunked_001.phpt 
  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/ext/standard/filters.c?r1=1.63&r2=1.64&diff_format=u
Index: php-src/ext/standard/filters.c
diff -u php-src/ext/standard/filters.c:1.63 php-src/ext/standard/filters.c:1.64
--- php-src/ext/standard/filters.c:1.63 Tue Mar 10 23:39:39 2009
+++ php-src/ext/standard/filters.c      Thu Apr 16 10:34:15 2009
@@ -20,7 +20,7 @@
    +----------------------------------------------------------------------+
 */
 
-/* $Id: filters.c,v 1.63 2009/03/10 23:39:39 helly Exp $ */
+/* $Id: filters.c,v 1.64 2009/04/16 10:34:15 dmitry Exp $ */
 
 #include "php.h"
 #include "php_globals.h"
@@ -1978,6 +1978,225 @@
 
 /* }}} */
 
+/* {{{ 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) {
+               if (buckets_in->head->buf_type == IS_UNICODE) {
+                       /* dechuk not allowed for unicode data */
+                       return PSFS_ERR_FATAL;
+               }
+               bucket = php_stream_bucket_make_writeable(buckets_in->head 
TSRMLS_CC);
+               consumed += bucket->buflen;
+               bucket->buflen = php_dechunk(bucket->buf.s, 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",
+       PSFO_FLAG_ACCEPTS_STRING | PSFO_FLAG_OUTPUTS_STRING
+};
+
+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;
@@ -1988,6 +2207,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.140&r2=1.141&diff_format=u
Index: php-src/ext/standard/http_fopen_wrapper.c
diff -u php-src/ext/standard/http_fopen_wrapper.c:1.140 
php-src/ext/standard/http_fopen_wrapper.c:1.141
--- php-src/ext/standard/http_fopen_wrapper.c:1.140     Thu Mar 26 20:02:28 2009
+++ php-src/ext/standard/http_fopen_wrapper.c   Thu Apr 16 10:34:15 2009
@@ -19,7 +19,7 @@
    |          Sara Golemon <poll...@php.net>                              |
    +----------------------------------------------------------------------+
  */
-/* $Id: http_fopen_wrapper.c,v 1.140 2009/03/26 20:02:28 felipe Exp $ */ 
+/* $Id: http_fopen_wrapper.c,v 1.141 2009/04/16 10:34:15 dmitry Exp $ */ 
 
 #include "php.h"
 #include "php_globals.h"
@@ -154,6 +154,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';
 
@@ -645,6 +646,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') {
@@ -793,6 +813,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);
        }
 
        if (charset) {
http://cvs.php.net/viewvc.cgi/php-src/ext/standard/tests/filters/chunked_001.phpt?r1=1.1&r2=1.2&diff_format=u
Index: php-src/ext/standard/tests/filters/chunked_001.phpt
diff -u /dev/null php-src/ext/standard/tests/filters/chunked_001.phpt:1.2
--- /dev/null   Thu Apr 16 10:34:15 2009
+++ php-src/ext/standard/tests/filters/chunked_001.phpt Thu Apr 16 10:34:15 2009
@@ -0,0 +1,33 @@
+--TEST--
+Chunked encoding
+--SKIPIF--
+<?php
+$filters = stream_get_filters();
+if(! in_array( "dechunk", $filters )) die( "chunked filter not available." );
+?>
+--FILE--
+<?php
+$streams = array(
+       b"data://text/plain,0\r\n",
+       b"data://text/plain,2\r\nte\r\n2\r\nst\r\n0\r\n",
+       b"data://text/plain,2\nte\n2\nst\n0\n",
+       b"data://text/plain,2;a=1\nte\n2;a=2;b=3\r\nst\n0\n",
+       b"data://text/plain,2\nte\n2\nst\n0\na=b\r\nc=d\n\r\n",
+       b"data://text/plain,1f\n0123456789abcdef0123456789abcde\n1\nf\n0\n",
+       b"data://text/plain,1E\n0123456789abcdef0123456789abcd\n2\nef\n0\n",
+);
+foreach ($streams as $name) {
+       $fp = fopen($name, "rb");
+       stream_filter_append($fp, "dechunk", STREAM_FILTER_READ);
+       var_dump(stream_get_contents($fp));
+       fclose($fp);
+}
+?>
+--EXPECT--
+string(0) ""
+string(4) "test"
+string(4) "test"
+string(4) "test"
+string(4) "test"
+string(32) "0123456789abcdef0123456789abcdef"
+string(32) "0123456789abcdef0123456789abcdef"

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

Reply via email to