X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=blobdiff_plain;f=php_http_filter.c;h=b6d967b6f756eb27f62e57920086fabfa5655315;hp=2c5335848910682695489a3a8576d52e3dad9016;hb=refs%2Fheads%2Fv2.3.x;hpb=02b671786e119a62ebe1a8183d682a39b4958a7b diff --git a/php_http_filter.c b/php_http_filter.c index 2c53358..b6d967b 100644 --- a/php_http_filter.c +++ b/php_http_filter.c @@ -6,12 +6,16 @@ | 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_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); @@ -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) {