X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=blobdiff_plain;f=php_http_filter.c;h=b6d967b6f756eb27f62e57920086fabfa5655315;hp=cdbc4088b4a5edbf8655144376a3eaece71c79c8;hb=refs%2Fheads%2Fv2.4.x;hpb=8d05291f42b3b42159b3fe91492aa4862f3d4405 diff --git a/php_http_filter.c b/php_http_filter.c index cdbc408..b6d967b 100644 --- a/php_http_filter.c +++ b/php_http_filter.c @@ -6,11 +6,15 @@ | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ - | Copyright (c) 2004-2011, Michael Wallner | + | Copyright (c) 2004-2014, Michael Wallner | +--------------------------------------------------------------------+ */ -#include "php_http.h" +#include "php_http_api.h" + +#ifndef DBG_FILTER +# define DBG_FILTER 0 +#endif PHP_MINIT_FUNCTION(http_filter) { @@ -40,6 +44,12 @@ PHP_MINIT_FUNCTION(http_filter) #define PHP_HTTP_FILTER_BUFFER(filter) \ http_filter_ ##filter## _buffer +#define PHP_HTTP_FILTER_IS_CLOSING(stream, flags) \ + ( (flags & PSFS_FLAG_FLUSH_CLOSE) \ + || php_stream_eof(stream) \ + || ((stream->ops == &php_stream_temp_ops || stream->ops == &php_stream_memory_ops) && stream->eof) \ + ) + #define NEW_BUCKET(data, length) \ { \ char *__data; \ @@ -77,46 +87,43 @@ static PHP_HTTP_FILTER_FUNCTION(chunked_decode) *bytes_consumed = 0; } - /* new data available? */ - if (buckets_in->head) { - - /* fetch available bucket data */ - for (ptr = buckets_in->head; ptr; ptr = nxt) { - nxt = ptr->next; - if (bytes_consumed) { - *bytes_consumed += ptr->buflen; - } - - if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(PHP_HTTP_BUFFER(buffer), ptr->buf, ptr->buflen)) { - return PSFS_ERR_FATAL; - } - - php_stream_bucket_unlink(ptr TSRMLS_CC); - php_stream_bucket_delref(ptr TSRMLS_CC); + /* fetch available bucket data */ + for (ptr = buckets_in->head; ptr; ptr = nxt) { + if (bytes_consumed) { + *bytes_consumed += ptr->buflen; } + + if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(PHP_HTTP_BUFFER(buffer), ptr->buf, ptr->buflen)) { + return PSFS_ERR_FATAL; + } + + nxt = ptr->next; + php_stream_bucket_unlink(ptr TSRMLS_CC); + php_stream_bucket_delref(ptr TSRMLS_CC); } + if (!php_http_buffer_fix(PHP_HTTP_BUFFER(buffer))) { return PSFS_ERR_FATAL; } /* we have data in our buffer */ - while (PHP_HTTP_BUFFER_LEN(buffer)) { + while (PHP_HTTP_BUFFER(buffer)->used) { /* we already know the size of the chunk and are waiting for data */ if (buffer->hexlen) { /* not enough data buffered */ - if (PHP_HTTP_BUFFER_LEN(buffer) < buffer->hexlen) { + if (PHP_HTTP_BUFFER(buffer)->used < buffer->hexlen) { /* flush anyway? */ if (flags & PSFS_FLAG_FLUSH_INC) { /* flush all data (should only be chunk data) */ out_avail = 1; - NEW_BUCKET(PHP_HTTP_BUFFER_VAL(buffer), PHP_HTTP_BUFFER_LEN(buffer)); + NEW_BUCKET(PHP_HTTP_BUFFER(buffer)->data, PHP_HTTP_BUFFER(buffer)->used); /* waiting for less data now */ - buffer->hexlen -= PHP_HTTP_BUFFER_LEN(buffer); + buffer->hexlen -= PHP_HTTP_BUFFER(buffer)->used; /* no more buffered data */ php_http_buffer_reset(PHP_HTTP_BUFFER(buffer)); /* break */ @@ -131,7 +138,7 @@ static PHP_HTTP_FILTER_FUNCTION(chunked_decode) /* we seem to have all data of the chunk */ else { out_avail = 1; - NEW_BUCKET(PHP_HTTP_BUFFER_VAL(buffer), buffer->hexlen); + NEW_BUCKET(PHP_HTTP_BUFFER(buffer)->data, buffer->hexlen); /* remove outgoing data from the buffer */ php_http_buffer_cut(PHP_HTTP_BUFFER(buffer), 0, buffer->hexlen); @@ -146,9 +153,9 @@ static PHP_HTTP_FILTER_FUNCTION(chunked_decode) size_t off = 0; /* ignore preceeding CRLFs (too loose?) */ - while (off < PHP_HTTP_BUFFER_LEN(buffer) && ( - PHP_HTTP_BUFFER_VAL(buffer)[off] == '\n' || - PHP_HTTP_BUFFER_VAL(buffer)[off] == '\r')) { + while (off < PHP_HTTP_BUFFER(buffer)->used && ( + PHP_HTTP_BUFFER(buffer)->data[off] == '\n' || + PHP_HTTP_BUFFER(buffer)->data[off] == '\r')) { ++off; } if (off) { @@ -156,26 +163,26 @@ static PHP_HTTP_FILTER_FUNCTION(chunked_decode) } /* still data there? */ - if (PHP_HTTP_BUFFER_LEN(buffer)) { + if (PHP_HTTP_BUFFER(buffer)->used) { int eollen; const char *eolstr; /* we need eol, so we can be sure we have all hex digits */ php_http_buffer_fix(PHP_HTTP_BUFFER(buffer)); - if ((eolstr = php_http_locate_bin_eol(PHP_HTTP_BUFFER_VAL(buffer), PHP_HTTP_BUFFER_LEN(buffer), &eollen))) { + if ((eolstr = php_http_locate_bin_eol(PHP_HTTP_BUFFER(buffer)->data, PHP_HTTP_BUFFER(buffer)->used, &eollen))) { char *stop = NULL; /* read in chunk size */ - buffer->hexlen = strtoul(PHP_HTTP_BUFFER_VAL(buffer), &stop, 16); + buffer->hexlen = strtoul(PHP_HTTP_BUFFER(buffer)->data, &stop, 16); /* if strtoul() stops at the beginning of the buffered data - there's domething oddly wrong, i.e. bad input */ - if (stop == PHP_HTTP_BUFFER_VAL(buffer)) { + there's something oddly wrong, i.e. bad input */ + if (stop == PHP_HTTP_BUFFER(buffer)->data) { return PSFS_ERR_FATAL; } /* cut out */ - php_http_buffer_cut(PHP_HTTP_BUFFER(buffer), 0, eolstr + eollen - PHP_HTTP_BUFFER_VAL(buffer)); + php_http_buffer_cut(PHP_HTTP_BUFFER(buffer), 0, eolstr + eollen - PHP_HTTP_BUFFER(buffer)->data); /* buffer->hexlen is 0 now or contains the size of the next chunk */ if (!buffer->hexlen) { php_stream_notify_info(stream->context, PHP_STREAM_NOTIFY_COMPLETED, NULL, 0); @@ -192,9 +199,9 @@ static PHP_HTTP_FILTER_FUNCTION(chunked_decode) } /* flush before close, but only if we are already waiting for more data */ - if ((flags & PSFS_FLAG_FLUSH_CLOSE) && buffer->hexlen && PHP_HTTP_BUFFER_LEN(buffer)) { + if (PHP_HTTP_FILTER_IS_CLOSING(stream, flags) && buffer->hexlen && PHP_HTTP_BUFFER(buffer)->used) { out_avail = 1; - NEW_BUCKET(PHP_HTTP_BUFFER_VAL(buffer), PHP_HTTP_BUFFER_LEN(buffer)); + NEW_BUCKET(PHP_HTTP_BUFFER(buffer)->data, PHP_HTTP_BUFFER(buffer)->used); php_http_buffer_reset(PHP_HTTP_BUFFER(buffer)); buffer->hexlen = 0; } @@ -212,7 +219,7 @@ static PHP_HTTP_FILTER_DESTRUCTOR(chunked_decode) static PHP_HTTP_FILTER_FUNCTION(chunked_encode) { - int out_avail = 0; + php_http_buffer_t buf; php_stream_bucket *ptr, *nxt; if (bytes_consumed) { @@ -220,43 +227,43 @@ static PHP_HTTP_FILTER_FUNCTION(chunked_encode) } /* new data available? */ - if (buckets_in->head) { - php_http_buffer_t buf; - out_avail = 1; - - php_http_buffer_init(&buf); - - /* fetch available bucket data */ - for (ptr = buckets_in->head; ptr; ptr = nxt) { - nxt = ptr->next; - if (bytes_consumed) { - *bytes_consumed += ptr->buflen; - } - - php_http_buffer_appendf(&buf, "%lx" PHP_HTTP_CRLF, (long unsigned int) ptr->buflen); - php_http_buffer_append(&buf, ptr->buf, ptr->buflen); - php_http_buffer_appends(&buf, PHP_HTTP_CRLF); - - /* pass through */ - NEW_BUCKET(PHP_HTTP_BUFFER_VAL(&buf), PHP_HTTP_BUFFER_LEN(&buf)); - /* reset */ - php_http_buffer_reset(&buf); - - php_stream_bucket_unlink(ptr TSRMLS_CC); - php_stream_bucket_delref(ptr TSRMLS_CC); + php_http_buffer_init(&buf); + + /* fetch available bucket data */ + for (ptr = buckets_in->head; ptr; ptr = nxt) { + if (bytes_consumed) { + *bytes_consumed += ptr->buflen; } +#if DBG_FILTER + fprintf(stderr, "update: chunked (-> %zu) (w: %zu, r: %zu)\n", ptr->buflen, stream->writepos, stream->readpos); +#endif - /* free buffer */ - php_http_buffer_dtor(&buf); + nxt = ptr->next; + php_stream_bucket_unlink(ptr TSRMLS_CC); + php_http_buffer_appendf(&buf, "%lx" PHP_HTTP_CRLF, (long unsigned int) ptr->buflen); + php_http_buffer_append(&buf, ptr->buf, ptr->buflen); + php_http_buffer_appends(&buf, PHP_HTTP_CRLF); + + /* pass through */ + NEW_BUCKET(buf.data, buf.used); + /* reset */ + php_http_buffer_reset(&buf); + php_stream_bucket_delref(ptr TSRMLS_CC); } + + /* free buffer */ + php_http_buffer_dtor(&buf); /* terminate with "0" */ - if (flags & PSFS_FLAG_FLUSH_CLOSE) { - out_avail = 1; - NEW_BUCKET("0" PHP_HTTP_CRLF, lenof("0" PHP_HTTP_CRLF)); + if (PHP_HTTP_FILTER_IS_CLOSING(stream, flags)) { +#if DBG_FILTER + fprintf(stderr, "finish: chunked\n"); +#endif + + NEW_BUCKET("0" PHP_HTTP_CRLF PHP_HTTP_CRLF, lenof("0" PHP_HTTP_CRLF PHP_HTTP_CRLF)); } - return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME; + return PSFS_PASS_ON; } static PHP_HTTP_FILTER_OPS(chunked_decode) = { @@ -273,7 +280,6 @@ static PHP_HTTP_FILTER_OPS(chunked_encode) = { static PHP_HTTP_FILTER_FUNCTION(zlib) { - int out_avail = 0; php_stream_bucket *ptr, *nxt; PHP_HTTP_FILTER_BUFFER(zlib) *buffer = (PHP_HTTP_FILTER_BUFFER(zlib) *) this->abstract; @@ -281,65 +287,74 @@ static PHP_HTTP_FILTER_FUNCTION(zlib) *bytes_consumed = 0; } - /* new data available? */ - if (buckets_in->head) { + /* fetch available bucket data */ + for (ptr = buckets_in->head; ptr; ptr = nxt) { + char *encoded = NULL; + size_t encoded_len = 0; + + if (bytes_consumed) { + *bytes_consumed += ptr->buflen; + } + +#if DBG_FILTER + fprintf(stderr, "bucket: b=%p p=%p p=%p\n", ptr->brigade, ptr->prev, ptr->next); +#endif - /* fetch available bucket data */ - for (ptr = buckets_in->head; ptr; ptr = nxt) { - char *encoded = NULL; - size_t encoded_len = 0; - - nxt = ptr->next; - if (bytes_consumed) { - *bytes_consumed += ptr->buflen; - } - - if (ptr->buflen) { - php_http_encoding_stream_update(buffer, ptr->buf, ptr->buflen, &encoded, &encoded_len); - if (encoded) { - if (encoded_len) { - out_avail = 1; - NEW_BUCKET(encoded, encoded_len); - } - efree(encoded); - } + nxt = ptr->next; + php_stream_bucket_unlink(ptr TSRMLS_CC); + php_http_encoding_stream_update(buffer, ptr->buf, ptr->buflen, &encoded, &encoded_len); + +#if DBG_FILTER + fprintf(stderr, "update: deflate (-> %zu) (w: %zu, r: %zu)\n", encoded_len, stream->writepos, stream->readpos); +#endif + + if (encoded) { + if (encoded_len) { + NEW_BUCKET(encoded, encoded_len); } - - php_stream_bucket_unlink(ptr TSRMLS_CC); - php_stream_bucket_delref(ptr TSRMLS_CC); + efree(encoded); } + php_stream_bucket_delref(ptr TSRMLS_CC); } - + /* flush & close */ if (flags & PSFS_FLAG_FLUSH_INC) { char *encoded = NULL; size_t encoded_len = 0; php_http_encoding_stream_flush(buffer, &encoded, &encoded_len); + +#if DBG_FILTER + fprintf(stderr, "flush: deflate (-> %zu)\n", encoded_len); +#endif + if (encoded) { if (encoded_len) { - out_avail = 1; NEW_BUCKET(encoded, encoded_len); } efree(encoded); } } - if (flags & PSFS_FLAG_FLUSH_CLOSE) { + if (PHP_HTTP_FILTER_IS_CLOSING(stream, flags)) { char *encoded = NULL; size_t encoded_len = 0; php_http_encoding_stream_finish(buffer, &encoded, &encoded_len); + +#if DBG_FILTER + fprintf(stderr, "finish: deflate (-> %zu)\n", encoded_len); +#endif + if (encoded) { if (encoded_len) { - out_avail = 1; NEW_BUCKET(encoded, encoded_len); } efree(encoded); } } - return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME; + return PSFS_PASS_ON; } static PHP_HTTP_FILTER_DESTRUCTOR(zlib) { @@ -363,7 +378,28 @@ static php_stream_filter *http_filter_create(const char *name, zval *params, int { zval **tmp = ¶ms; php_stream_filter *f = NULL; + int flags = p ? PHP_HTTP_ENCODING_STREAM_PERSISTENT : 0; + if (params) { + switch (Z_TYPE_P(params)) { + case IS_ARRAY: + case IS_OBJECT: + if (SUCCESS != zend_hash_find(HASH_OF(params), "flags", sizeof("flags"), (void *) &tmp)) { + break; + } + /* no break */ + default: + { + zval *num = php_http_ztyp(IS_LONG, *tmp); + + flags |= (Z_LVAL_P(num) & 0x0fffffff); + zval_ptr_dtor(&num); + + } + break; + } + } + if (!strcasecmp(name, "http.chunked_decode")) { PHP_HTTP_FILTER_BUFFER(chunked_decode) *b = NULL; @@ -380,7 +416,6 @@ static php_stream_filter *http_filter_create(const char *name, zval *params, int } else if (!strcasecmp(name, "http.inflate")) { - int flags = p ? PHP_HTTP_ENCODING_STREAM_PERSISTENT : 0; PHP_HTTP_FILTER_BUFFER(zlib) *b = NULL; if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), flags TSRMLS_CC))) { @@ -391,25 +426,8 @@ static php_stream_filter *http_filter_create(const char *name, zval *params, int } else if (!strcasecmp(name, "http.deflate")) { - int flags = p ? PHP_HTTP_ENCODING_STREAM_PERSISTENT : 0; PHP_HTTP_FILTER_BUFFER(zlib) *b = NULL; - if (params) { - switch (Z_TYPE_P(params)) { - case IS_ARRAY: - case IS_OBJECT: - if (SUCCESS != zend_hash_find(HASH_OF(params), "flags", sizeof("flags"), (void *) &tmp)) { - break; - } - default: - { - zval *num = php_http_ztyp(IS_LONG, *tmp); - - flags |= (Z_LVAL_P(num) & 0x0fffffff); - zval_ptr_dtor(&num); - } - } - } if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), flags TSRMLS_CC))) { if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(deflate), b, p))) { php_http_encoding_stream_free(&b);