X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=blobdiff_plain;f=php_http_filter.c;h=f145f02b68a70dbd555483cff72fd4d356552b81;hp=cdbc4088b4a5edbf8655144376a3eaece71c79c8;hb=e438aa9ce944f8c8f175554d9aa66d152a47f780;hpb=8d05291f42b3b42159b3fe91492aa4862f3d4405 diff --git a/php_http_filter.c b/php_http_filter.c index cdbc408..f145f02 100644 --- a/php_http_filter.c +++ b/php_http_filter.c @@ -6,15 +6,19 @@ | 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) { - php_stream_filter_register_factory("http.*", &php_http_filter_factory TSRMLS_CC); + php_stream_filter_register_factory("http.*", &php_http_filter_factory); return SUCCESS; } @@ -23,8 +27,8 @@ PHP_MINIT_FUNCTION(http_filter) php_stream_filter *this, \ php_stream_bucket_brigade *buckets_in, \ php_stream_bucket_brigade *buckets_out, \ - size_t *bytes_consumed, int flags \ - TSRMLS_DC + size_t *bytes_consumed, \ + int flags #define PHP_HTTP_FILTER_OP(filter) \ http_filter_op_ ##filter #define PHP_HTTP_FILTER_OPS(filter) \ @@ -32,7 +36,7 @@ PHP_MINIT_FUNCTION(http_filter) #define PHP_HTTP_FILTER_DTOR(filter) \ http_filter_ ##filter## _dtor #define PHP_HTTP_FILTER_DESTRUCTOR(filter) \ - void PHP_HTTP_FILTER_DTOR(filter)(php_stream_filter *this TSRMLS_DC) + void PHP_HTTP_FILTER_DTOR(filter)(php_stream_filter *this) #define PHP_HTTP_FILTER_FUNC(filter) \ http_filter_ ##filter #define PHP_HTTP_FILTER_FUNCTION(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; \ @@ -51,13 +61,13 @@ PHP_MINIT_FUNCTION(http_filter) } \ memcpy(__data, data, length); \ \ - __buck = php_stream_bucket_new(stream, __data, length, 1, this->is_persistent TSRMLS_CC); \ + __buck = php_stream_bucket_new(stream, __data, length, 1, this->is_persistent); \ if (!__buck) { \ pefree(__data, this->is_persistent); \ return PSFS_ERR_FATAL; \ } \ \ - php_stream_bucket_append(buckets_out, __buck TSRMLS_CC); \ + php_stream_bucket_append(buckets_out, __buck); \ } typedef struct _http_chunked_decode_filter_buffer_t { @@ -71,52 +81,49 @@ static PHP_HTTP_FILTER_FUNCTION(chunked_decode) { int out_avail = 0; php_stream_bucket *ptr, *nxt; - PHP_HTTP_FILTER_BUFFER(chunked_decode) *buffer = (PHP_HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract); + PHP_HTTP_FILTER_BUFFER(chunked_decode) *buffer = Z_PTR(this->abstract); if (bytes_consumed) { *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); + php_stream_bucket_delref(ptr); } + 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,29 +163,29 @@ 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); + php_stream_notify_info(PHP_STREAM_CONTEXT(stream), PHP_STREAM_NOTIFY_COMPLETED, NULL, 0); break; } /* continue */ @@ -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; } @@ -204,7 +211,7 @@ static PHP_HTTP_FILTER_FUNCTION(chunked_decode) static PHP_HTTP_FILTER_DESTRUCTOR(chunked_decode) { - PHP_HTTP_FILTER_BUFFER(chunked_decode) *b = (PHP_HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract); + PHP_HTTP_FILTER_BUFFER(chunked_decode) *b = Z_PTR(this->abstract); php_http_buffer_dtor(PHP_HTTP_BUFFER(b)); pefree(b, this->is_persistent); @@ -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); + 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); } + + /* 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,77 +280,85 @@ 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; + PHP_HTTP_FILTER_BUFFER(zlib) *buffer = Z_PTR(this->abstract); if (bytes_consumed) { *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); + 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); } - + /* 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) { - PHP_HTTP_FILTER_BUFFER(zlib) *buffer = (PHP_HTTP_FILTER_BUFFER(zlib) *) this->abstract; + PHP_HTTP_FILTER_BUFFER(zlib) *buffer = Z_PTR(this->abstract); php_http_encoding_stream_free(&buffer); } @@ -359,11 +374,26 @@ static PHP_HTTP_FILTER_OPS(inflate) = { "http.inflate" }; -static php_stream_filter *http_filter_create(const char *name, zval *params, int p TSRMLS_DC) +static php_stream_filter *http_filter_create(const char *name, zval *params, int p) { - zval **tmp = ¶ms; + zval *tmp = params; 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 (!(tmp = zend_hash_str_find_ind(HASH_OF(params), ZEND_STRL("flags")))) { + break; + } + /* no break */ + default: + flags |= zval_get_long(tmp) & 0x0fffffff; + break; + } + } + if (!strcasecmp(name, "http.chunked_decode")) { PHP_HTTP_FILTER_BUFFER(chunked_decode) *b = NULL; @@ -380,10 +410,9 @@ 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))) { + if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), flags))) { if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(inflate), b, p))) { php_http_encoding_stream_free(&b); } @@ -391,26 +420,9 @@ 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 ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), flags))) { if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(deflate), b, p))) { php_http_encoding_stream_free(&b); }