X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=blobdiff_plain;f=http_filter_api.c;h=23b71190acebb730a3d5002b734d4f108299e9ed;hp=ccec761cb6593611cf97191207c5d5c29c1ddc7c;hb=ad5f896b03adaa073134a00108a9cdf00720673a;hpb=34644fd4c700fb55c3a2c2ff9966d0f5e7572509 diff --git a/http_filter_api.c b/http_filter_api.c index ccec761..23b7119 100644 --- a/http_filter_api.c +++ b/http_filter_api.c @@ -1,246 +1,518 @@ /* - +----------------------------------------------------------------------+ - | PECL :: http | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.0 of the PHP license, that | - | is bundled with this package in the file LICENSE, and is available | - | through the world-wide-web at http://www.php.net/license/3_0.txt. | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Copyright (c) 2004-2005 Michael Wallner | - +----------------------------------------------------------------------+ + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2010, Michael Wallner | + +--------------------------------------------------------------------+ */ /* $Id$ */ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif -#include "php.h" +#define HTTP_WANT_ZLIB +#include "php_http.h" -#include "php_http_std_defs.h" -#include "php_http_api.h" -#include "php_http_filter_api.h" - -#include "phpstr/phpstr.h" +#ifdef ZEND_ENGINE_2 #include "php_streams.h" +#include "php_http_api.h" +#include "php_http_encoding_api.h" +#include "php_http_filter_api.h" -#ifndef HTTP_DEBUG_FILTERS -# define HTTP_DEBUG_FILTERS 0 -#endif +PHP_MINIT_FUNCTION(http_filter) +{ + php_stream_filter_register_factory("http.*", &http_filter_factory TSRMLS_CC); + return SUCCESS; +} /* - * TODO: phpstr is not persistent aware - */ - -typedef enum { - HFS_HEX = 0, - HFS_DATA, -} http_filter_status; - -typedef struct { - phpstr buffer; - size_t wanted; - int eollen; - int passon; - http_filter_status status; -} http_filter_buffer; + - +*/ -#define PHP_STREAM_FILTER_OP_FILTER_PARAMS \ +#define HTTP_FILTER_PARAMS \ php_stream *stream, \ php_stream_filter *this, \ php_stream_bucket_brigade *buckets_in, \ php_stream_bucket_brigade *buckets_out, \ size_t *bytes_consumed, int flags \ TSRMLS_DC -#define PHP_STREAM_FILTER_OP_FILTER(function) \ - static php_stream_filter_status_t function(PHP_STREAM_FILTER_OP_FILTER_PARAMS) +#define HTTP_FILTER_OP(filter) \ + http_filter_op_ ##filter +#define HTTP_FILTER_OPS(filter) \ + php_stream_filter_ops HTTP_FILTER_OP(filter) +#define HTTP_FILTER_DTOR(filter) \ + http_filter_ ##filter## _dtor +#define HTTP_FILTER_DESTRUCTOR(filter) \ + void HTTP_FILTER_DTOR(filter)(php_stream_filter *this TSRMLS_DC) +#define HTTP_FILTER_FUNC(filter) \ + http_filter_ ##filter +#define HTTP_FILTER_FUNCTION(filter) \ + php_stream_filter_status_t HTTP_FILTER_FUNC(filter)(HTTP_FILTER_PARAMS) +#define HTTP_FILTER_BUFFER(filter) \ + http_filter_ ##filter## _buffer #define NEW_BUCKET(data, length) \ - php_stream_bucket_append(buckets_out, php_stream_bucket_new(stream, pestrndup(data, length, this->is_persistent), (length), 1, this->is_persistent TSRMLS_CC) TSRMLS_CC); - -inline void *pestrndup(const char *s, size_t l, int p) -{ - void *d = pemalloc(l + 1, p); - if (d) { - memcpy(d, s, l); - ((char *) d)[l] = 0; + { \ + char *__data; \ + php_stream_bucket *__buck; \ + \ + __data = pemalloc(length, this->is_persistent); \ + if (!__data) { \ + return PSFS_ERR_FATAL; \ + } \ + memcpy(__data, data, length); \ + \ + __buck = php_stream_bucket_new(stream, __data, length, 1, this->is_persistent TSRMLS_CC); \ + if (!__buck) { \ + pefree(__data, this->is_persistent); \ + return PSFS_ERR_FATAL; \ + } \ + \ + php_stream_bucket_append(buckets_out, __buck TSRMLS_CC); \ } - return d; -} -PHP_STREAM_FILTER_OP_FILTER(http_filter_chunked_decode) +typedef struct _http_chunked_decode_filter_buffer_t { + phpstr buffer; + ulong hexlen; +} HTTP_FILTER_BUFFER(chunked_decode); + +#ifdef HTTP_HAVE_ZLIB +typedef http_encoding_stream HTTP_FILTER_BUFFER(deflate); +typedef http_encoding_stream HTTP_FILTER_BUFFER(inflate); +#endif /* HTTP_HAVE_ZLIB */ + + +static HTTP_FILTER_FUNCTION(chunked_decode) { + int out_avail = 0; php_stream_bucket *ptr, *nxt; - http_filter_buffer *buffer = (http_filter_buffer *) (this->abstract); + HTTP_FILTER_BUFFER(chunked_decode) *buffer = (HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract); if (bytes_consumed) { *bytes_consumed = 0; } - if (!buckets_in->head) { - return PSFS_FEED_ME; - } - -#if HTTP_DEBUG_FILTERS - fprintf(stderr, "Reading in bucket buffers "); -#endif - - /* fetch available bucket data */ - for (ptr = buckets_in->head; ptr; ptr = nxt) { - nxt = ptr->next; - phpstr_append(PHPSTR(buffer), ptr->buf, ptr->buflen); - php_stream_bucket_unlink(ptr TSRMLS_CC); - php_stream_bucket_delref(ptr TSRMLS_CC); - -#if HTTP_DEBUG_FILTERS - fprintf(stderr, "."); -#endif + /* 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 (PHPSTR_NOMEM == phpstr_append(PHPSTR(buffer), ptr->buf, ptr->buflen)) { + return PSFS_ERR_FATAL; + } + + php_stream_bucket_unlink(ptr TSRMLS_CC); + php_stream_bucket_delref(ptr TSRMLS_CC); + } } - if (bytes_consumed) { - *bytes_consumed = PHPSTR_LEN(buffer); + if (!phpstr_fix(PHPSTR(buffer))) { + return PSFS_ERR_FATAL; } - phpstr_fix(PHPSTR(buffer)); -#if HTTP_DEBUG_FILTERS - fprintf(stderr, " done\nCurrent buffer length: %lu bytes\n", PHPSTR_LEN(buffer)); -#endif + /* we have data in our buffer */ + while (PHPSTR_LEN(buffer)) { - buffer->passon = 0; - while (1) { - if (buffer->status == HFS_HEX) { - const char *eol; - char *stop; - ulong clen; + /* we already know the size of the chunk and are waiting for data */ + if (buffer->hexlen) { + + /* not enough data buffered */ + if (PHPSTR_LEN(buffer) < buffer->hexlen) { -#if HTTP_DEBUG_FILTERS - fprintf(stderr, "Status HFS_HEX: "); -#endif + /* flush anyway? */ + if (flags & PSFS_FLAG_FLUSH_INC) { + + /* flush all data (should only be chunk data) */ + out_avail = 1; + NEW_BUCKET(PHPSTR_VAL(buffer), PHPSTR_LEN(buffer)); + + /* waiting for less data now */ + buffer->hexlen -= PHPSTR_LEN(buffer); + /* no more buffered data */ + phpstr_reset(PHPSTR(buffer)); + /* break */ + } + + /* we have too less data and don't need to flush */ + else { + break; + } + } - if (!(eol = http_locate_eol(PHPSTR_VAL(buffer), &buffer->eollen))) { -#if HTTP_DEBUG_FILTERS - fprintf(stderr, "return PFSF_FEED_ME (no eol)\n"); -#endif - return buffer->passon ? PSFS_PASS_ON : PSFS_FEED_ME; - } - if (!(clen = strtoul(PHPSTR_VAL(buffer), &stop, 16))) { -#if HTTP_DEBUG_FILTERS - fprintf(stderr, "return PFSF_FEED_ME (no len)\n"); -#endif - phpstr_dtor(PHPSTR(buffer)); - return buffer->passon ? PSFS_PASS_ON : PSFS_FEED_ME; + /* we seem to have all data of the chunk */ + else { + out_avail = 1; + NEW_BUCKET(PHPSTR_VAL(buffer), buffer->hexlen); + + /* remove outgoing data from the buffer */ + phpstr_cut(PHPSTR(buffer), 0, buffer->hexlen); + /* reset hexlen */ + buffer->hexlen = 0; + /* continue */ } + } + + /* we don't know the length of the chunk yet */ + else { + size_t off = 0; - buffer->status = HFS_DATA; - buffer->wanted = clen; - phpstr_cut(PHPSTR(buffer), 0, eol + buffer->eollen - PHPSTR_VAL(buffer)); + /* ignore preceeding CRLFs (too loose?) */ + while (off < PHPSTR_LEN(buffer) && ( + PHPSTR_VAL(buffer)[off] == '\n' || + PHPSTR_VAL(buffer)[off] == '\r')) { + ++off; + } + if (off) { + phpstr_cut(PHPSTR(buffer), 0, off); + } -#if HTTP_DEBUG_FILTERS - fprintf(stderr, "read %lu bytes chunk size\n", buffer->wanted); -#endif - } - -#if HTTP_DEBUG_FILTERS - fprintf(stderr, "Current status: %s\n", buffer->status == HFS_DATA?"HFS_DATA":"HFS_HEX"); - fprintf(stderr, "Current buffer length: %lu bytes\n", PHPSTR_LEN(buffer)); -#endif - - if (buffer->status == HFS_DATA && buffer->wanted > 0 && buffer->wanted <= PHPSTR_LEN(buffer)) { - -#if HTTP_DEBUG_FILTERS - fprintf(stderr, "Passing on %lu(%lu) bytes\n", buffer->wanted, PHPSTR_LEN(buffer)); -#endif - - NEW_BUCKET(PHPSTR_VAL(buffer), buffer->wanted); - phpstr_cut(PHPSTR(buffer), 0, buffer->wanted + buffer->eollen); - buffer->wanted = 0; - buffer->eollen = 0; - buffer->passon = 1; - buffer->status = HFS_HEX; - continue; + /* still data there? */ + if (PHPSTR_LEN(buffer)) { + int eollen; + const char *eolstr; + + /* we need eol, so we can be sure we have all hex digits */ + phpstr_fix(PHPSTR(buffer)); + if ((eolstr = http_locate_eol(PHPSTR_VAL(buffer), &eollen))) { + char *stop = NULL; + + /* read in chunk size */ + buffer->hexlen = strtoul(PHPSTR_VAL(buffer), &stop, 16); + + /* if strtoul() stops at the beginning of the buffered data + there's domething oddly wrong, i.e. bad input */ + if (stop == PHPSTR_VAL(buffer)) { + return PSFS_ERR_FATAL; + } + + /* cut out */ + phpstr_cut(PHPSTR(buffer), 0, eolstr + eollen - PHPSTR_VAL(buffer)); + /* buffer->hexlen is 0 now or contains the size of the next chunk */ + /* continue */ + } else { + /* we have not enough data buffered to read in chunk size */ + break; + } + } + /* break */ } - return buffer->passon ? PSFS_PASS_ON : PSFS_FEED_ME; } - return PSFS_FEED_ME; + /* flush before close, but only if we are already waiting for more data */ + if ((flags & PSFS_FLAG_FLUSH_CLOSE) && buffer->hexlen && PHPSTR_LEN(buffer)) { + out_avail = 1; + NEW_BUCKET(PHPSTR_VAL(buffer), PHPSTR_LEN(buffer)); + phpstr_reset(PHPSTR(buffer)); + buffer->hexlen = 0; + } + + return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME; } -static void http_filter_chunked_decode_dtor(php_stream_filter *this TSRMLS_DC) +static HTTP_FILTER_DESTRUCTOR(chunked_decode) { - http_filter_buffer *b = (http_filter_buffer *) (this->abstract); + HTTP_FILTER_BUFFER(chunked_decode) *b = (HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract); phpstr_dtor(PHPSTR(b)); pefree(b, this->is_persistent); } -PHP_STREAM_FILTER_OP_FILTER(http_filter_chunked_encode) +static HTTP_FILTER_FUNCTION(chunked_encode) { - phpstr buf; + int out_avail = 0; php_stream_bucket *ptr, *nxt; if (bytes_consumed) { *bytes_consumed = 0; } - if (!buckets_in->head) { - return PSFS_FEED_ME; - } - - phpstr_init(&buf); - for (ptr = buckets_in->head; ptr; ptr = nxt) { - if (bytes_consumed) { - *bytes_consumed += ptr->buflen; - } - nxt = ptr->next; + /* new data available? */ + if (buckets_in->head) { + phpstr buf; + out_avail = 1; - phpstr_appendf(&buf, "%x" HTTP_CRLF, ptr->buflen); - phpstr_append(&buf, ptr->buf, ptr->buflen); - phpstr_appends(&buf, HTTP_CRLF); - NEW_BUCKET(PHPSTR_VAL(&buf), PHPSTR_LEN(&buf)); - PHPSTR_LEN(&buf) = 0; + phpstr_init(&buf); - 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) { + nxt = ptr->next; + if (bytes_consumed) { + *bytes_consumed += ptr->buflen; + } + + phpstr_appendf(&buf, "%x" HTTP_CRLF, ptr->buflen); + phpstr_append(&buf, ptr->buf, ptr->buflen); + phpstr_appends(&buf, HTTP_CRLF); + + /* pass through */ + NEW_BUCKET(PHPSTR_VAL(&buf), PHPSTR_LEN(&buf)); + /* reset */ + phpstr_reset(&buf); + + php_stream_bucket_unlink(ptr TSRMLS_CC); + php_stream_bucket_delref(ptr TSRMLS_CC); + } + + /* free buffer */ + phpstr_dtor(&buf); } - phpstr_dtor(&buf); + /* terminate with "0" */ if (flags & PSFS_FLAG_FLUSH_CLOSE) { - NEW_BUCKET("0"HTTP_CRLF, lenof("0"HTTP_CRLF)); + out_avail = 1; + NEW_BUCKET("0" HTTP_CRLF, lenof("0" HTTP_CRLF)); } - return PSFS_PASS_ON; + return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME; } -static php_stream_filter_ops http_filter_ops_chunked_decode = { - http_filter_chunked_decode, - http_filter_chunked_decode_dtor, +static HTTP_FILTER_OPS(chunked_decode) = { + HTTP_FILTER_FUNC(chunked_decode), + HTTP_FILTER_DTOR(chunked_decode), "http.chunked_decode" }; -static php_stream_filter_ops http_filter_ops_chunked_encode = { - http_filter_chunked_encode, +static HTTP_FILTER_OPS(chunked_encode) = { + HTTP_FILTER_FUNC(chunked_encode), NULL, "http.chunked_encode" }; +#ifdef HTTP_HAVE_ZLIB + +static HTTP_FILTER_FUNCTION(deflate) +{ + int out_avail = 0; + php_stream_bucket *ptr, *nxt; + HTTP_FILTER_BUFFER(inflate) *buffer = (HTTP_FILTER_BUFFER(deflate) *) 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; + + nxt = ptr->next; + if (bytes_consumed) { + *bytes_consumed += ptr->buflen; + } + + if (ptr->buflen) { + http_encoding_deflate_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); + } + } + + php_stream_bucket_unlink(ptr TSRMLS_CC); + php_stream_bucket_delref(ptr TSRMLS_CC); + } + } + + /* flush & close */ + if (flags & PSFS_FLAG_FLUSH_INC) { + char *encoded = NULL; + size_t encoded_len = 0; + + http_encoding_deflate_stream_flush(buffer, &encoded, &encoded_len); + if (encoded) { + if (encoded_len) { + out_avail = 1; + NEW_BUCKET(encoded, encoded_len); + } + efree(encoded); + } + } + + if (flags & PSFS_FLAG_FLUSH_CLOSE) { + char *encoded = NULL; + size_t encoded_len = 0; + + http_encoding_deflate_stream_finish(buffer, &encoded, &encoded_len); + if (encoded) { + if (encoded_len) { + out_avail = 1; + NEW_BUCKET(encoded, encoded_len); + } + efree(encoded); + } + } + + return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME; +} + +static HTTP_FILTER_FUNCTION(inflate) +{ + int out_avail = 0; + php_stream_bucket *ptr, *nxt; + HTTP_FILTER_BUFFER(inflate) *buffer = (HTTP_FILTER_BUFFER(inflate) *) 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 *decoded = NULL; + size_t decoded_len = 0; + + nxt = ptr->next; + if (bytes_consumed) { + *bytes_consumed += ptr->buflen; + } + + if (ptr->buflen) { + http_encoding_inflate_stream_update(buffer, ptr->buf, ptr->buflen, &decoded, &decoded_len); + if (decoded) { + if (decoded_len) { + out_avail = 1; + NEW_BUCKET(decoded, decoded_len); + } + efree(decoded); + } + } + + php_stream_bucket_unlink(ptr TSRMLS_CC); + php_stream_bucket_delref(ptr TSRMLS_CC); + } + } + + /* flush & close */ + if (flags & PSFS_FLAG_FLUSH_INC) { + char *decoded = NULL; + size_t decoded_len = 0; + + http_encoding_inflate_stream_flush(buffer, &decoded, &decoded_len); + if (decoded) { + if (decoded_len) { + out_avail = 1; + NEW_BUCKET(decoded, decoded_len); + } + efree(decoded); + } + } + + if (flags & PSFS_FLAG_FLUSH_CLOSE) { + char *decoded = NULL; + size_t decoded_len = 0; + + http_encoding_inflate_stream_finish(buffer, &decoded, &decoded_len); + if (decoded) { + if (decoded_len) { + out_avail = 1; + NEW_BUCKET(decoded, decoded_len); + } + efree(decoded); + } + } + + return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME; +} + +static HTTP_FILTER_DESTRUCTOR(deflate) +{ + HTTP_FILTER_BUFFER(deflate) *buffer = (HTTP_FILTER_BUFFER(deflate) *) this->abstract; + http_encoding_deflate_stream_free(&buffer); +} + +static HTTP_FILTER_DESTRUCTOR(inflate) +{ + HTTP_FILTER_BUFFER(inflate) *buffer = (HTTP_FILTER_BUFFER(inflate) *) this->abstract; + http_encoding_inflate_stream_free(&buffer); +} + +static HTTP_FILTER_OPS(deflate) = { + HTTP_FILTER_FUNC(deflate), + HTTP_FILTER_DTOR(deflate), + "http.deflate" +}; + +static HTTP_FILTER_OPS(inflate) = { + HTTP_FILTER_FUNC(inflate), + HTTP_FILTER_DTOR(inflate), + "http.inflate" +}; + +#endif /* HTTP_HAVE_ZLIB */ + static php_stream_filter *http_filter_create(const char *name, zval *params, int p TSRMLS_DC) { + zval **tmp = ¶ms; php_stream_filter *f = NULL; - void *b = NULL; if (!strcasecmp(name, "http.chunked_decode")) { - if (b = pecalloc(1, sizeof(http_filter_buffer), p)) { - phpstr_init(PHPSTR(b)); - if (!(f = php_stream_filter_alloc(&http_filter_ops_chunked_decode, b, p))) { + HTTP_FILTER_BUFFER(chunked_decode) *b = NULL; + + if ((b = pecalloc(1, sizeof(HTTP_FILTER_BUFFER(chunked_decode)), p))) { + phpstr_init_ex(PHPSTR(b), 4096, p ? PHPSTR_INIT_PERSISTENT : 0); + if (!(f = php_stream_filter_alloc(&HTTP_FILTER_OP(chunked_decode), b, p))) { pefree(b, p); } } } else + if (!strcasecmp(name, "http.chunked_encode")) { - f = php_stream_filter_alloc(&http_filter_ops_chunked_encode, NULL, p); + f = php_stream_filter_alloc(&HTTP_FILTER_OP(chunked_encode), NULL, p); +#ifdef HTTP_HAVE_ZLIB + } else + + if (!strcasecmp(name, "http.inflate")) { + int flags = p ? HTTP_ENCODING_STREAM_PERSISTENT : 0; + HTTP_FILTER_BUFFER(inflate) *b = NULL; + + if ((b = http_encoding_inflate_stream_init(NULL, flags))) { + if (!(f = php_stream_filter_alloc(&HTTP_FILTER_OP(inflate), b, p))) { + http_encoding_inflate_stream_free(&b); + } + } + } else + + if (!strcasecmp(name, "http.deflate")) { + int flags = p ? HTTP_ENCODING_STREAM_PERSISTENT : 0; + HTTP_FILTER_BUFFER(deflate) *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 = http_zsep(IS_LONG, *tmp); + + flags |= (Z_LVAL_P(num) & 0x0fffffff); + zval_ptr_dtor(&num); + } + } + } + if ((b = http_encoding_deflate_stream_init(NULL, flags))) { + if (!(f = php_stream_filter_alloc(&HTTP_FILTER_OP(deflate), b, p))) { + http_encoding_deflate_stream_free(&b); + } + } +#endif /* HTTP_HAVE_ZLIB */ } return f; @@ -250,11 +522,7 @@ php_stream_filter_factory http_filter_factory = { http_filter_create }; -PHP_MINIT_FUNCTION(http_filter) -{ - php_stream_filter_register_factory("http.*", &http_filter_factory TSRMLS_CC); - return SUCCESS; -} +#endif /* ZEND_ENGINE_2 */ /* * Local variables: @@ -264,4 +532,3 @@ PHP_MINIT_FUNCTION(http_filter) * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ -