]> granicus.if.org Git - php/commitdiff
BugFix#27619
authorSara Golemon <pollita@php.net>
Wed, 31 Mar 2004 23:48:59 +0000 (23:48 +0000)
committerSara Golemon <pollita@php.net>
Wed, 31 Mar 2004 23:48:59 +0000 (23:48 +0000)
Filters not applied to pre-buffered stream data.
(esp. http:// streams)

NEWS
ext/standard/tests/file/bug27619.phpt [new file with mode: 0644]
main/streams/filter.c
main/streams/php_stream_filter_api.h
main/streams/streams.c

diff --git a/NEWS b/NEWS
index 84c841b08fdcb7fb06e5c08e20da07310433e4aa..7f1b89cd893b19c4bec2f47b538be0d1ebc8eb2d 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,7 @@ PHP                                                                        NEWS
   (Dmitry, Andi)
 - Fixed bug #27628 (Simplify the process of making a POST request via stream
   context). (Ilia)
+- Fixed bug #27619 (filters not applied to pre-buffered stream data). (Sara)
 - Fixed bug #27469 (serialize() objects of incomplete class). (Dmitry)
 - Fixed bug #27457 (handling of numeric indexes in strtr()). (Dmitry)
 
diff --git a/ext/standard/tests/file/bug27619.phpt b/ext/standard/tests/file/bug27619.phpt
new file mode 100644 (file)
index 0000000..095a18c
--- /dev/null
@@ -0,0 +1,18 @@
+--TEST--
+Bug #27619 (filters not applied to pre-buffered data)
+--FILE--
+<?php
+       $fp = tmpfile();
+       fwrite($fp, "this is a lowercase string.\n");
+       rewind($fp);
+
+       /* Echo out the first four bytes 'this' without applying filter
+          Remainder will get sucked into the read buffer though. */
+       echo fread($fp, 4);
+
+       stream_filter_append($fp, "string.toupper");
+
+       fpassthru($fp);
+?>
+--EXPECT--
+this IS A LOWERCASE STRING.
index ff0549972cd824aeb54729fdd2a6eae44742d492..708889f97ce5fed805a3c783c86887bde22bde19 100644 (file)
@@ -283,7 +283,7 @@ PHPAPI void php_stream_filter_free(php_stream_filter *filter TSRMLS_DC)
        pefree(filter, filter->is_persistent);
 }
 
-PHPAPI void php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter)
+PHPAPI void php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
 {
        filter->next = chain->head;
        filter->prev = NULL;
@@ -297,8 +297,10 @@ PHPAPI void php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream
        filter->chain = chain;
 }
 
-PHPAPI void php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter)
+PHPAPI void php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
 {
+       php_stream *stream = chain->stream;
+
        filter->prev = chain->tail;
        filter->next = NULL;
        if (chain->tail) {
@@ -308,6 +310,75 @@ PHPAPI void php_stream_filter_append(php_stream_filter_chain *chain, php_stream_
        }
        chain->tail = filter;
        filter->chain = chain;
+
+       if ((stream->writepos - stream->readpos) > 0) {
+               /* Let's going ahead and wind anything in the buffer through this filter */
+               php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL };
+               php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out;
+               php_stream_filter_status_t status;
+               php_stream_bucket *bucket;
+               size_t consumed = 0;
+
+               bucket = php_stream_bucket_new(stream, stream->readbuf + stream->readpos, stream->writepos - stream->readpos, 0, 0 TSRMLS_CC);
+               php_stream_bucket_append(brig_inp, bucket TSRMLS_CC);
+               status = filter->fops->filter(stream, filter, brig_inp, brig_outp, &consumed, PSFS_FLAG_NORMAL TSRMLS_CC);
+
+               if (stream->readpos + consumed > stream->writepos || consumed < 0) {
+                       /* No behaving filter should cause this. */
+                       status = PSFS_ERR_FATAL;
+               }
+
+               switch (status) {
+                       case PSFS_ERR_FATAL:
+                               /* If this first cycle simply fails then there's something wrong with the filter.
+                                  Pull the filter off the chain and leave the read buffer alone. */
+                               if (chain->head == filter) {
+                                       chain->head = NULL;
+                                       chain->tail = NULL;
+                               } else {
+                                       filter->prev->next = NULL;
+                                       chain->tail = filter->prev;
+                               }
+                               php_stream_bucket_unlink(bucket TSRMLS_CC);
+                               php_stream_bucket_delref(bucket TSRMLS_CC);
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filter failed to process pre-buffered data.  Not adding to filterchain.");
+                               break;
+                       case PSFS_FEED_ME:
+                               /* We don't actually need data yet,
+                                  leave this filter in a feed me state until data is needed. 
+                                  Reset stream's internal read buffer since the filter is "holding" it. */
+                               stream->readpos = 0;
+                               stream->writepos = 0;
+                               break;
+                       case PSFS_PASS_ON:
+                               /* Put any filtered data onto the readbuffer stack.
+                                  Previously read data has been at least partially consumed. */
+                               stream->readpos += consumed;
+
+                               if (stream->writepos == stream->readpos) {
+                                       /* Entirely consumed */
+                                       stream->writepos = 0;
+                                       stream->readpos = 0;
+                               }
+
+                               while (brig_outp->head) {
+                                       bucket = brig_outp->head;
+                                       /* Grow buffer to hold this bucket if need be.
+                                          TODO: See warning in main/stream/streams.c::php_stream_fill_read_buffer */
+                                       if (stream->readbuflen - stream->writepos < bucket->buflen) {
+                                               stream->readbuflen += bucket->buflen;
+                                               stream->readbuf = perealloc(stream->readbuf, stream->readbuflen, stream->is_persistent);
+                                       }
+                                       memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen);
+                                       stream->writepos += bucket->buflen;
+
+                                       php_stream_bucket_unlink(bucket TSRMLS_CC);
+                                       php_stream_bucket_delref(bucket TSRMLS_CC);
+                               }
+                               break;
+               }
+       }
+
 }
 
 PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, int call_dtor TSRMLS_DC)
index dcab09efd6c578452f321a4538f538ea504e9ac9..2cbe6a775a535e7eca992af9d437e6ff431d4368 100644 (file)
@@ -100,6 +100,9 @@ typedef struct _php_stream_filter_ops {
 
 typedef struct _php_stream_filter_chain {
        php_stream_filter *head, *tail;
+
+       /* Owning stream */
+       php_stream *stream;
 } php_stream_filter_chain;
 
 struct _php_stream_filter {
@@ -118,14 +121,16 @@ struct _php_stream_filter {
 
 /* stack filter onto a stream */
 BEGIN_EXTERN_C()
-PHPAPI void php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter);
-PHPAPI void php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter);
+PHPAPI void _php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC);
+PHPAPI void _php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC);
 PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, int call_dtor TSRMLS_DC);
 PHPAPI void php_stream_filter_free(php_stream_filter *filter TSRMLS_DC);
 PHPAPI php_stream_filter *_php_stream_filter_alloc(php_stream_filter_ops *fops, void *abstract, int persistent STREAMS_DC TSRMLS_DC);
 END_EXTERN_C()
 #define php_stream_filter_alloc(fops, thisptr, persistent) _php_stream_filter_alloc((fops), (thisptr), (persistent) STREAMS_CC TSRMLS_CC)
 #define php_stream_filter_alloc_rel(fops, thisptr, persistent) _php_stream_filter_alloc((fops), (thisptr), (persistent) STREAMS_REL_CC TSRMLS_CC)
+#define php_stream_fitler_prepend(chain, filter) _php_stream_filter_prepend((chain), (filter) TSRMLS_CC)
+#define php_stream_fitler_append(chain, filter) _php_stream_filter_append((chain), (filter) TSRMLS_CC)
 
 #define php_stream_is_filtered(stream) ((stream)->readfilters.head || (stream)->writefilters.head)
 
index af5caa93a781367d6351e51ce49c89a1f82903e1..fd67eea27d24896874196ef3c018fe67a052ed86 100755 (executable)
@@ -216,6 +216,9 @@ PHPAPI php_stream *_php_stream_alloc(php_stream_ops *ops, void *abstract, const
 
        memset(ret, 0, sizeof(php_stream));
 
+       ret->readfilters.stream = ret;
+       ret->writefilters.stream = ret;
+
 #if STREAM_DEBUG
 fprintf(stderr, "stream_alloc: %s:%p persistent=%s\n", ops->label, ret, persistent_id);
 #endif