From 6c1356cbe7363bf41f770b1dce59abef93dcc021 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Mon, 26 Feb 2018 08:49:03 +0100 Subject: [PATCH 1/1] add brotli encoding --- .gitignore | 2 + autoconf/pecl/libbrotli.m4 | 33 ++ config9.m4 | 7 + package.xml | 4 + src/php_http.c | 4 + src/php_http_api.h | 2 + src/php_http_encoding.c | 603 ++---------------------------- src/php_http_encoding.h | 115 +----- src/php_http_encoding_brotli.c | 461 +++++++++++++++++++++++ src/php_http_encoding_brotli.h | 54 +++ src/php_http_encoding_zlib.c | 650 +++++++++++++++++++++++++++++++++ src/php_http_encoding_zlib.h | 62 ++++ 12 files changed, 1318 insertions(+), 679 deletions(-) create mode 100644 autoconf/pecl/libbrotli.m4 create mode 100644 src/php_http_encoding_brotli.c create mode 100644 src/php_http_encoding_brotli.h create mode 100644 src/php_http_encoding_zlib.c create mode 100644 src/php_http_encoding_zlib.h diff --git a/.gitignore b/.gitignore index 15cb267..aeed865 100644 --- a/.gitignore +++ b/.gitignore @@ -77,3 +77,5 @@ vendor/ /php_http_version.h /tests/helper/server.log *.gcov +*gcov +*lcov diff --git a/autoconf/pecl/libbrotli.m4 b/autoconf/pecl/libbrotli.m4 new file mode 100644 index 0000000..8ea8b1f --- /dev/null +++ b/autoconf/pecl/libbrotli.m4 @@ -0,0 +1,33 @@ + +AC_DEFUN([PECL_CHECK_LIBBROTLI], [ + PECL_CHECK_LIBBROTLI_DEC([$1], [$2]) + PECL_CHECK_DONE(libbrotlidec, [$PECL_VAR([HAVE_LIBBROTLI_DEC])]) + PECL_CHECK_LIBBROTLI_ENC([$1], [$2]) + PECL_CHECK_DONE(libbrotlienc, [$PECL_VAR([HAVE_LIBBROTLI_ENC])]) + + if $PECL_VAR([HAVE_LIBBROTLI_DEC]) && $PECL_VAR([HAVE_LIBBROTLI_ENC]); then + PECL_VAR([HAVE_LIBBROTLI])=true + fi +]) + +AC_DEFUN([PECL_CHECK_LIBBROTLI_DEC], [ + PECL_CHECK_PKGCONFIG(libbrotlidec, [$1]) + + PECL_HAVE_VERSION(libbrotlidec, ifelse($2,,1.0,$2), [ + PECL_VAR([HAVE_LIBBROTLI_DEC])=true + ], [ + AC_MSG_WARN([skipping libbrotli]) + PECL_VAR([HAVE_LIBBROTLI_DEC])=false + ]) +]) + +AC_DEFUN([PECL_CHECK_LIBBROTLI_ENC], [ + PECL_CHECK_PKGCONFIG(libbrotlienc, [$1]) + + PECL_HAVE_VERSION(libbrotlienc, ifelse($2,,1.0,$2), [ + PECL_VAR([HAVE_LIBBROTLI_ENC])=true + ], [ + AC_MSG_WARN([skipping libbrotli]) + PECL_VAR([HAVE_LIBBROTLI_ENC])=false + ]) +]) diff --git a/config9.m4 b/config9.m4 index dda5b01..897b92f 100644 --- a/config9.m4 +++ b/config9.m4 @@ -2,6 +2,7 @@ m4_foreach(dir, [., ext/http], [ sinclude(dir/autoconf/pecl/pecl.m4) sinclude(dir/autoconf/pecl/zlib.m4) + sinclude(dir/autoconf/pecl/libbrotli.m4) sinclude(dir/autoconf/pecl/libcurl.m4) sinclude(dir/autoconf/pecl/libevent.m4) ]) @@ -29,6 +30,12 @@ if test "$PHP_HTTP" != "no"; then [ --with-http-zlib-dir[=DIR] HTTP: where to find zlib], $PHP_HTTP, no) PECL_CHECK_ZLIB([$PHP_HTTP_ZLIB_DIR], [1.2.0.4]) PECL_CHECK_DONE(zlib, $PECL_VAR([HAVE_ZLIB])) + + dnl BROTLI + PHP_ARG_WITH([http-libbrotli-dir], [whether/where to check for libbrotli], + [ --with-http-libbrotli-dir[=DIR] HTTP: where to find libbrotli], $PHP_HTTP, no) + PECL_CHECK_LIBBROTLI([$PHP_HTTP_LIBBROTLI_DIR], [1.0]) + PECL_CHECK_DONE(libbrotli, $PECL_VAR([HAVE_LIBBROTLI])) dnl CURL PHP_ARG_WITH([http-libcurl-dir], [whether/where to check for libcurl], diff --git a/package.xml b/package.xml index 0f10907..5233895 100644 --- a/package.xml +++ b/package.xml @@ -92,6 +92,10 @@ https://mdref.m6w6.name/http + + + + diff --git a/src/php_http.c b/src/php_http.c index bc9166a..498a224 100644 --- a/src/php_http.c +++ b/src/php_http.c @@ -137,6 +137,10 @@ PHP_MINIT_FUNCTION(http) || SUCCESS != PHP_MINIT_CALL(http_exception) || SUCCESS != PHP_MINIT_CALL(http_cookie) || SUCCESS != PHP_MINIT_CALL(http_encoding) + || SUCCESS != PHP_MINIT_CALL(http_encoding_zlib) +#if PHP_HTTP_HAVE_LIBBROTLI + || SUCCESS != PHP_MINIT_CALL(http_encoding_brotli) +#endif || SUCCESS != PHP_MINIT_CALL(http_filter) || SUCCESS != PHP_MINIT_CALL(http_header) || SUCCESS != PHP_MINIT_CALL(http_header_parser) diff --git a/src/php_http_api.h b/src/php_http_api.h index 4071f0f..91b2eb6 100644 --- a/src/php_http_api.h +++ b/src/php_http_api.h @@ -80,6 +80,8 @@ #include "php_http.h" #include "php_http_cookie.h" #include "php_http_encoding.h" +#include "php_http_encoding_zlib.h" +#include "php_http_encoding_brotli.h" #include "php_http_info.h" #include "php_http_message.h" #include "php_http_env.h" diff --git a/src/php_http_encoding.c b/src/php_http_encoding.c index f5d634a..ad43853 100644 --- a/src/php_http_encoding.c +++ b/src/php_http_encoding.c @@ -12,8 +12,6 @@ #include "php_http_api.h" -#include - static inline int eol_match(char **line, int *eol_len) { char *ptr = *line; @@ -109,129 +107,6 @@ const char *php_http_encoding_dechunk(const char *encoded, size_t encoded_len, c return e_ptr; } -static inline int php_http_inflate_rounds(z_stream *Z, int flush, char **buf, size_t *len) -{ - int status = 0, round = 0; - php_http_buffer_t buffer; - - *buf = NULL; - *len = 0; - - php_http_buffer_init_ex(&buffer, Z->avail_in, PHP_HTTP_BUFFER_INIT_PREALLOC); - - do { - if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize_ex(&buffer, buffer.size, 0, 1)) { - status = Z_MEM_ERROR; - } else { - Z->avail_out = buffer.free; - Z->next_out = (Bytef *) buffer.data + buffer.used; -#if 0 - fprintf(stderr, "\n%3d: %3d PRIOR: size=%7lu,\tfree=%7lu,\tused=%7lu,\tavail_in=%7lu,\tavail_out=%7lu\n", round, status, buffer.size, buffer.free, buffer.used, Z->avail_in, Z->avail_out); -#endif - status = inflate(Z, flush); - php_http_buffer_account(&buffer, buffer.free - Z->avail_out); -#if 0 - fprintf(stderr, "%3d: %3d AFTER: size=%7lu,\tfree=%7lu,\tused=%7lu,\tavail_in=%7lu,\tavail_out=%7lu\n", round, status, buffer.size, buffer.free, buffer.used, Z->avail_in, Z->avail_out); -#endif - PHP_HTTP_INFLATE_BUFFER_SIZE_ALIGN(buffer.size); - } - } while ((Z_BUF_ERROR == status || (Z_OK == status && Z->avail_in)) && ++round < PHP_HTTP_INFLATE_ROUNDS); - - if (status == Z_OK || status == Z_STREAM_END) { - php_http_buffer_shrink(&buffer); - php_http_buffer_fix(&buffer); - *buf = buffer.data; - *len = buffer.used; - } else { - php_http_buffer_dtor(&buffer); - } - - return status; -} - -ZEND_RESULT_CODE php_http_encoding_deflate(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len) -{ - int status, level, wbits, strategy; - z_stream Z; - - PHP_HTTP_DEFLATE_LEVEL_SET(flags, level); - PHP_HTTP_DEFLATE_WBITS_SET(flags, wbits); - PHP_HTTP_DEFLATE_STRATEGY_SET(flags, strategy); - - memset(&Z, 0, sizeof(z_stream)); - *encoded = NULL; - *encoded_len = 0; - - status = deflateInit2(&Z, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy); - if (Z_OK == status) { - *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len); - *encoded = emalloc(*encoded_len); - - Z.next_in = (Bytef *) data; - Z.next_out = (Bytef *) *encoded; - Z.avail_in = data_len; - Z.avail_out = *encoded_len; - - status = deflate(&Z, Z_FINISH); - deflateEnd(&Z); - - if (Z_STREAM_END == status) { - /* size buffer down to actual length */ - *encoded = erealloc(*encoded, Z.total_out + 1); - (*encoded)[*encoded_len = Z.total_out] = '\0'; - return SUCCESS; - } else { - PTR_SET(*encoded, NULL); - *encoded_len = 0; - } - } - - php_error_docref(NULL, E_WARNING, "Could not deflate data: %s", zError(status)); - return FAILURE; -} - -ZEND_RESULT_CODE php_http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len) -{ - z_stream Z; - int status, wbits = PHP_HTTP_WINDOW_BITS_ANY; - - memset(&Z, 0, sizeof(z_stream)); - -retry_raw_inflate: - status = inflateInit2(&Z, wbits); - if (Z_OK == status) { - Z.next_in = (Bytef *) data; - Z.avail_in = data_len + 1; /* include the terminating NULL, see #61287 */ - - switch (status = php_http_inflate_rounds(&Z, Z_NO_FLUSH, decoded, decoded_len)) { - case Z_STREAM_END: - inflateEnd(&Z); - return SUCCESS; - - case Z_OK: - status = Z_DATA_ERROR; - break; - - case Z_DATA_ERROR: - /* raw deflated data? */ - if (PHP_HTTP_WINDOW_BITS_ANY == wbits) { - inflateEnd(&Z); - wbits = PHP_HTTP_WINDOW_BITS_RAW; - goto retry_raw_inflate; - } - break; - } - inflateEnd(&Z); - - if (*decoded_len && *decoded) { - efree(*decoded); - } - } - - php_error_docref(NULL, E_WARNING, "Could not inflate data: %s", zError(status)); - return FAILURE; -} - php_http_encoding_stream_t *php_http_encoding_stream_init(php_http_encoding_stream_t *s, php_http_encoding_stream_ops_t *ops, unsigned flags) { int freeme; @@ -243,10 +118,10 @@ php_http_encoding_stream_t *php_http_encoding_stream_init(php_http_encoding_stre s->flags = flags; - if ((s->ops = ops)) { + if (EXPECTED(s->ops = ops)) { php_http_encoding_stream_t *ss = s->ops->init(s); - if (ss) { + if (EXPECTED(ss)) { return ss; } } else { @@ -286,10 +161,10 @@ ZEND_RESULT_CODE php_http_encoding_stream_reset(php_http_encoding_stream_t **s) { php_http_encoding_stream_t *ss; - if ((*s)->ops->dtor) { + if (EXPECTED((*s)->ops->dtor)) { (*s)->ops->dtor(*s); } - if ((ss = (*s)->ops->init(*s))) { + if (EXPECTED(ss = (*s)->ops->init(*s))) { *s = ss; return SUCCESS; } @@ -298,7 +173,7 @@ ZEND_RESULT_CODE php_http_encoding_stream_reset(php_http_encoding_stream_t **s) ZEND_RESULT_CODE php_http_encoding_stream_update(php_http_encoding_stream_t *s, const char *in_str, size_t in_len, char **out_str, size_t *out_len) { - if (!s->ops->update) { + if (UNEXPECTED(!s->ops->update)) { return FAILURE; } return s->ops->update(s, in_str, in_len, out_str, out_len); @@ -334,15 +209,15 @@ ZEND_RESULT_CODE php_http_encoding_stream_finish(php_http_encoding_stream_t *s, void php_http_encoding_stream_dtor(php_http_encoding_stream_t *s) { - if (s->ops->dtor) { + if (EXPECTED(s->ops->dtor)) { s->ops->dtor(s); } } void php_http_encoding_stream_free(php_http_encoding_stream_t **s) { - if (*s) { - if ((*s)->ops->dtor) { + if (EXPECTED(*s)) { + if (EXPECTED((*s)->ops->dtor)) { (*s)->ops->dtor(*s); } pefree(*s, ((*s)->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT)); @@ -356,48 +231,6 @@ struct dechunk_ctx { unsigned zeroed:1; }; -static php_http_encoding_stream_t *deflate_init(php_http_encoding_stream_t *s) -{ - int status, level, wbits, strategy, p = (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT); - z_streamp ctx = pecalloc(1, sizeof(z_stream), p); - - PHP_HTTP_DEFLATE_LEVEL_SET(s->flags, level); - PHP_HTTP_DEFLATE_WBITS_SET(s->flags, wbits); - PHP_HTTP_DEFLATE_STRATEGY_SET(s->flags, strategy); - - if (Z_OK == (status = deflateInit2(ctx, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy))) { - if ((ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) { - s->ctx = ctx; - return s; - } - deflateEnd(ctx); - status = Z_MEM_ERROR; - } - pefree(ctx, p); - php_error_docref(NULL, E_WARNING, "Failed to initialize deflate encoding stream: %s", zError(status)); - return NULL; -} - -static php_http_encoding_stream_t *inflate_init(php_http_encoding_stream_t *s) -{ - int status, wbits, p = (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT); - z_streamp ctx = pecalloc(1, sizeof(z_stream), p); - - PHP_HTTP_INFLATE_WBITS_SET(s->flags, wbits); - - if (Z_OK == (status = inflateInit2(ctx, wbits))) { - if ((ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) { - s->ctx = ctx; - return s; - } - inflateEnd(ctx); - status = Z_MEM_ERROR; - } - pefree(ctx, p); - php_error_docref(NULL, E_WARNING, "Failed to initialize inflate stream: %s", zError(status)); - return NULL; -} - static php_http_encoding_stream_t *dechunk_init(php_http_encoding_stream_t *s) { struct dechunk_ctx *ctx = pecalloc(1, sizeof(*ctx), (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT)); @@ -413,42 +246,6 @@ static php_http_encoding_stream_t *dechunk_init(php_http_encoding_stream_t *s) return s; } -static php_http_encoding_stream_t *deflate_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to) -{ - int status, p = to->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT; - z_streamp from_ctx = from->ctx, to_ctx = pecalloc(1, sizeof(*to_ctx), p); - - if (Z_OK == (status = deflateCopy(to_ctx, from_ctx))) { - if ((to_ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) { - php_http_buffer_append(to_ctx->opaque, PHP_HTTP_BUFFER(from_ctx->opaque)->data, PHP_HTTP_BUFFER(from_ctx->opaque)->used); - to->ctx = to_ctx; - return to; - } - deflateEnd(to_ctx); - status = Z_MEM_ERROR; - } - php_error_docref(NULL, E_WARNING, "Failed to copy deflate encoding stream: %s", zError(status)); - return NULL; -} - -static php_http_encoding_stream_t *inflate_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to) -{ - int status, p = from->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT; - z_streamp from_ctx = from->ctx, to_ctx = pecalloc(1, sizeof(*to_ctx), p); - - if (Z_OK == (status = inflateCopy(to_ctx, from_ctx))) { - if ((to_ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) { - php_http_buffer_append(to_ctx->opaque, PHP_HTTP_BUFFER(from_ctx->opaque)->data, PHP_HTTP_BUFFER(from_ctx->opaque)->used); - to->ctx = to_ctx; - return to; - } - inflateEnd(to_ctx); - status = Z_MEM_ERROR; - } - php_error_docref(NULL, E_WARNING, "Failed to copy inflate encoding stream: %s", zError(status)); - return NULL; -} - static php_http_encoding_stream_t *dechunk_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to) { int p = from->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT; @@ -466,84 +263,6 @@ static php_http_encoding_stream_t *dechunk_copy(php_http_encoding_stream_t *from return NULL; } -static ZEND_RESULT_CODE deflate_update(php_http_encoding_stream_t *s, const char *data, size_t data_len, char **encoded, size_t *encoded_len) -{ - int status; - z_streamp ctx = s->ctx; - - /* append input to our buffer */ - php_http_buffer_append(PHP_HTTP_BUFFER(ctx->opaque), data, data_len); - - ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data; - ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used; - - /* deflate */ - *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len); - *encoded = emalloc(*encoded_len); - ctx->avail_out = *encoded_len; - ctx->next_out = (Bytef *) *encoded; - - switch (status = deflate(ctx, PHP_HTTP_ENCODING_STREAM_FLUSH_FLAG(s->flags))) { - case Z_OK: - case Z_STREAM_END: - /* cut processed chunk off the buffer */ - if (ctx->avail_in) { - php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in); - } else { - php_http_buffer_reset(PHP_HTTP_BUFFER(ctx->opaque)); - } - - /* size buffer down to actual size */ - *encoded_len -= ctx->avail_out; - *encoded = erealloc(*encoded, *encoded_len + 1); - (*encoded)[*encoded_len] = '\0'; - return SUCCESS; - } - - PTR_SET(*encoded, NULL); - *encoded_len = 0; - php_error_docref(NULL, E_WARNING, "Failed to update deflate stream: %s", zError(status)); - return FAILURE; -} - -static ZEND_RESULT_CODE inflate_update(php_http_encoding_stream_t *s, const char *data, size_t data_len, char **decoded, size_t *decoded_len) -{ - int status; - z_streamp ctx = s->ctx; - - /* append input to buffer */ - php_http_buffer_append(PHP_HTTP_BUFFER(ctx->opaque), data, data_len); - -retry_raw_inflate: - ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data; - ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used; - - switch (status = php_http_inflate_rounds(ctx, PHP_HTTP_ENCODING_STREAM_FLUSH_FLAG(s->flags), decoded, decoded_len)) { - case Z_OK: - case Z_STREAM_END: - /* cut off */ - if (ctx->avail_in) { - php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in); - } else { - php_http_buffer_reset(PHP_HTTP_BUFFER(ctx->opaque)); - } - return SUCCESS; - - case Z_DATA_ERROR: - /* raw deflated data ? */ - if (!(s->flags & PHP_HTTP_INFLATE_TYPE_RAW) && !ctx->total_out) { - inflateEnd(ctx); - s->flags |= PHP_HTTP_INFLATE_TYPE_RAW; - inflateInit2(ctx, PHP_HTTP_WINDOW_BITS_RAW); - goto retry_raw_inflate; - } - break; - } - - php_error_docref(NULL, E_WARNING, "Failed to update inflate stream: %s", zError(status)); - return FAILURE; -} - static ZEND_RESULT_CODE dechunk_update(php_http_encoding_stream_t *s, const char *data, size_t data_len, char **decoded, size_t *decoded_len) { php_http_buffer_t tmp; @@ -671,34 +390,6 @@ static ZEND_RESULT_CODE dechunk_update(php_http_encoding_stream_t *s, const char return SUCCESS; } -static ZEND_RESULT_CODE deflate_flush(php_http_encoding_stream_t *s, char **encoded, size_t *encoded_len) -{ - int status; - z_streamp ctx = s->ctx; - - *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE; - *encoded = emalloc(*encoded_len); - - ctx->avail_in = 0; - ctx->next_in = NULL; - ctx->avail_out = *encoded_len; - ctx->next_out = (Bytef *) *encoded; - - switch (status = deflate(ctx, Z_FULL_FLUSH)) { - case Z_OK: - case Z_STREAM_END: - *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE - ctx->avail_out; - *encoded = erealloc(*encoded, *encoded_len + 1); - (*encoded)[*encoded_len] = '\0'; - return SUCCESS; - } - - PTR_SET(*encoded, NULL); - *encoded_len = 0; - php_error_docref(NULL, E_WARNING, "Failed to flush deflate stream: %s", zError(status)); - return FAILURE; -} - static ZEND_RESULT_CODE dechunk_flush(php_http_encoding_stream_t *s, char **decoded, size_t *decoded_len) { struct dechunk_ctx *ctx = s->ctx; @@ -719,125 +410,13 @@ static ZEND_RESULT_CODE dechunk_flush(php_http_encoding_stream_t *s, char **deco return SUCCESS; } -static ZEND_RESULT_CODE deflate_finish(php_http_encoding_stream_t *s, char **encoded, size_t *encoded_len) -{ - int status; - z_streamp ctx = s->ctx; - - *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE; - *encoded = emalloc(*encoded_len); - - /* deflate remaining input */ - ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data; - ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used; - - ctx->avail_out = *encoded_len; - ctx->next_out = (Bytef *) *encoded; - - do { - status = deflate(ctx, Z_FINISH); - } while (Z_OK == status); - - if (Z_STREAM_END == status) { - /* cut processed input off */ - php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in); - - /* size down */ - *encoded_len -= ctx->avail_out; - *encoded = erealloc(*encoded, *encoded_len + 1); - (*encoded)[*encoded_len] = '\0'; - return SUCCESS; - } - - PTR_SET(*encoded, NULL); - *encoded_len = 0; - php_error_docref(NULL, E_WARNING, "Failed to finish deflate stream: %s", zError(status)); - return FAILURE; -} - -static ZEND_RESULT_CODE inflate_finish(php_http_encoding_stream_t *s, char **decoded, size_t *decoded_len) -{ - int status; - z_streamp ctx = s->ctx; - - if (!PHP_HTTP_BUFFER(ctx->opaque)->used) { - *decoded = NULL; - *decoded_len = 0; - return SUCCESS; - } - - *decoded_len = (PHP_HTTP_BUFFER(ctx->opaque)->used + 1) * PHP_HTTP_INFLATE_ROUNDS; - *decoded = emalloc(*decoded_len); - - /* inflate remaining input */ - ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data; - ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used; - - ctx->avail_out = *decoded_len; - ctx->next_out = (Bytef *) *decoded; - - if (Z_STREAM_END == (status = inflate(ctx, Z_FINISH))) { - /* cut processed input off */ - php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in); - - /* size down */ - *decoded_len -= ctx->avail_out; - *decoded = erealloc(*decoded, *decoded_len + 1); - (*decoded)[*decoded_len] = '\0'; - return SUCCESS; - } - - PTR_SET(*decoded, NULL); - *decoded_len = 0; - php_error_docref(NULL, E_WARNING, "Failed to finish inflate stream: %s", zError(status)); - return FAILURE; -} -static zend_bool deflate_done(php_http_encoding_stream_t *s) -{ - z_streamp ctx = s->ctx; - return !ctx->avail_in && !PHP_HTTP_BUFFER(ctx->opaque)->used; -} - -static zend_bool inflate_done(php_http_encoding_stream_t *s) -{ - z_streamp ctx = s->ctx; - return !ctx->avail_in && !PHP_HTTP_BUFFER(ctx->opaque)->used; -} static zend_bool dechunk_done(php_http_encoding_stream_t *s) { return ((struct dechunk_ctx *) s->ctx)->zeroed; } -static void deflate_dtor(php_http_encoding_stream_t *s) -{ - if (s->ctx) { - z_streamp ctx = s->ctx; - - if (ctx->opaque) { - php_http_buffer_free((php_http_buffer_t **) &ctx->opaque); - } - deflateEnd(ctx); - pefree(ctx, (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT)); - s->ctx = NULL; - } -} - -static void inflate_dtor(php_http_encoding_stream_t *s) -{ - if (s->ctx) { - z_streamp ctx = s->ctx; - - if (ctx->opaque) { - php_http_buffer_free((php_http_buffer_t **) &ctx->opaque); - } - inflateEnd(ctx); - pefree(ctx, (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT)); - s->ctx = NULL; - } -} - static void dechunk_dtor(php_http_encoding_stream_t *s) { if (s->ctx) { @@ -849,36 +428,6 @@ static void dechunk_dtor(php_http_encoding_stream_t *s) } } -static php_http_encoding_stream_ops_t php_http_encoding_deflate_ops = { - deflate_init, - deflate_copy, - deflate_update, - deflate_flush, - deflate_done, - deflate_finish, - deflate_dtor -}; - -php_http_encoding_stream_ops_t *php_http_encoding_stream_get_deflate_ops(void) -{ - return &php_http_encoding_deflate_ops; -} - -static php_http_encoding_stream_ops_t php_http_encoding_inflate_ops = { - inflate_init, - inflate_copy, - inflate_update, - NULL, - inflate_done, - inflate_finish, - inflate_dtor -}; - -php_http_encoding_stream_ops_t *php_http_encoding_stream_get_inflate_ops(void) -{ - return &php_http_encoding_inflate_ops; -} - static php_http_encoding_stream_ops_t php_http_encoding_dechunk_ops = { dechunk_init, dechunk_copy, @@ -944,16 +493,7 @@ zend_class_entry *php_http_get_encoding_stream_class_entry(void) { return php_http_encoding_stream_class_entry; } -static zend_class_entry *php_http_deflate_stream_class_entry; -zend_class_entry *php_http_get_deflate_stream_class_entry(void) -{ - return php_http_deflate_stream_class_entry; -} -static zend_class_entry *php_http_inflate_stream_class_entry; -zend_class_entry *php_http_get_inflate_stream_class_entry(void) -{ - return php_http_inflate_stream_class_entry; -} + static zend_class_entry *php_http_dechunk_stream_class_entry; zend_class_entry *php_http_get_dechunk_stream_class_entry(void) { @@ -973,17 +513,21 @@ static PHP_METHOD(HttpEncodingStream, __construct) obj = PHP_HTTP_OBJ(NULL, getThis()); - if (obj->stream) { + if (UNEXPECTED(obj->stream)) { php_http_throw(bad_method_call, "http\\Encoding\\Stream cannot be initialized twice", NULL); return; } - if (instanceof_function(obj->zo.ce, php_http_deflate_stream_class_entry)) { - ops = &php_http_encoding_deflate_ops; - } else if (instanceof_function(obj->zo.ce, php_http_inflate_stream_class_entry)) { - ops = &php_http_encoding_inflate_ops; + if (instanceof_function(obj->zo.ce, php_http_get_deflate_stream_class_entry())) { + ops = php_http_encoding_stream_get_deflate_ops(); + } else if (instanceof_function(obj->zo.ce, php_http_get_inflate_stream_class_entry())) { + ops = php_http_encoding_stream_get_inflate_ops(); } else if (instanceof_function(obj->zo.ce, php_http_dechunk_stream_class_entry)) { ops = &php_http_encoding_dechunk_ops; + } else if (instanceof_function(obj->zo.ce, php_http_get_enbrotli_stream_class_entry())) { + ops = php_http_encoding_stream_get_enbrotli_ops(); + } else if (instanceof_function(obj->zo.ce, php_http_get_debrotli_stream_class_entry())) { + ops = php_http_encoding_stream_get_debrotli_ops(); } else { php_http_throw(runtime, "Unknown http\\Encoding\\Stream class '%s'", obj->zo.ce->name->val); return; @@ -1000,15 +544,15 @@ static PHP_METHOD(HttpEncodingStream, update) size_t data_len; char *data_str; - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data_str, &data_len)) { + if (EXPECTED(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data_str, &data_len))) { php_http_encoding_stream_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); - if (obj->stream) { + if (EXPECTED(obj->stream)) { char *encoded_str = NULL; size_t encoded_len; - if (SUCCESS == php_http_encoding_stream_update(obj->stream, data_str, data_len, &encoded_str, &encoded_len)) { - if (encoded_str) { + if (EXPECTED(SUCCESS == php_http_encoding_stream_update(obj->stream, data_str, data_len, &encoded_str, &encoded_len))) { + if (EXPECTED(encoded_str)) { RETURN_STR(php_http_cs2zs(encoded_str, encoded_len)); } else { RETURN_EMPTY_STRING(); @@ -1022,14 +566,14 @@ ZEND_BEGIN_ARG_INFO_EX(ai_HttpEncodingStream_flush, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(HttpEncodingStream, flush) { - if (SUCCESS == zend_parse_parameters_none()) { + if (EXPECTED(SUCCESS == zend_parse_parameters_none())) { php_http_encoding_stream_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); - if (obj->stream) { + if (EXPECTED(obj->stream)) { char *encoded_str = NULL; size_t encoded_len; - if (SUCCESS == php_http_encoding_stream_flush(obj->stream, &encoded_str, &encoded_len)) { + if (EXPECTED(SUCCESS == php_http_encoding_stream_flush(obj->stream, &encoded_str, &encoded_len))) { if (encoded_str) { RETURN_STR(php_http_cs2zs(encoded_str, encoded_len)); } else { @@ -1044,10 +588,10 @@ ZEND_BEGIN_ARG_INFO_EX(ai_HttpEncodingStream_done, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(HttpEncodingStream, done) { - if (SUCCESS == zend_parse_parameters_none()) { + if (EXPECTED(SUCCESS == zend_parse_parameters_none())) { php_http_encoding_stream_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); - if (obj->stream) { + if (EXPECTED(obj->stream)) { RETURN_BOOL(php_http_encoding_stream_done(obj->stream)); } } @@ -1057,15 +601,15 @@ ZEND_BEGIN_ARG_INFO_EX(ai_HttpEncodingStream_finish, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(HttpEncodingStream, finish) { - if (SUCCESS == zend_parse_parameters_none()) { + if (EXPECTED(SUCCESS == zend_parse_parameters_none())) { php_http_encoding_stream_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); - if (obj->stream) { + if (EXPECTED(obj->stream)) { char *encoded_str = NULL; size_t encoded_len; - if (SUCCESS == php_http_encoding_stream_finish(obj->stream, &encoded_str, &encoded_len)) { - if (SUCCESS == php_http_encoding_stream_reset(&obj->stream)) { + if (EXPECTED(SUCCESS == php_http_encoding_stream_finish(obj->stream, &encoded_str, &encoded_len))) { + if (EXPECTED(SUCCESS == php_http_encoding_stream_reset(&obj->stream))) { if (encoded_str) { RETURN_STR(php_http_cs2zs(encoded_str, encoded_len)); } else { @@ -1088,64 +632,6 @@ static zend_function_entry php_http_encoding_stream_methods[] = { EMPTY_FUNCTION_ENTRY }; -ZEND_BEGIN_ARG_INFO_EX(ai_HttpDeflateStream_encode, 0, 0, 1) - ZEND_ARG_INFO(0, data) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO(); -static PHP_METHOD(HttpDeflateStream, encode) -{ - char *str; - size_t len; - zend_long flags = 0; - - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &str, &len, &flags)) { - char *enc_str = NULL; - size_t enc_len; - - if (SUCCESS == php_http_encoding_deflate(flags, str, len, &enc_str, &enc_len)) { - if (enc_str) { - RETURN_STR(php_http_cs2zs(enc_str, enc_len)); - } else { - RETURN_EMPTY_STRING(); - } - } - } - RETURN_FALSE; -} - -static zend_function_entry php_http_deflate_stream_methods[] = { - PHP_ME(HttpDeflateStream, encode, ai_HttpDeflateStream_encode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - EMPTY_FUNCTION_ENTRY -}; - -ZEND_BEGIN_ARG_INFO_EX(ai_HttpInflateStream_decode, 0, 0, 1) - ZEND_ARG_INFO(0, data) -ZEND_END_ARG_INFO(); -static PHP_METHOD(HttpInflateStream, decode) -{ - char *str; - size_t len; - - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &len)) { - char *enc_str = NULL; - size_t enc_len; - - if (SUCCESS == php_http_encoding_inflate(str, len, &enc_str, &enc_len)) { - if (enc_str) { - RETURN_STR(php_http_cs2zs(enc_str, enc_len)); - } else { - RETURN_EMPTY_STRING(); - } - } - } - RETURN_FALSE; -} - -static zend_function_entry php_http_inflate_stream_methods[] = { - PHP_ME(HttpInflateStream, decode, ai_HttpInflateStream_decode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - EMPTY_FUNCTION_ENTRY -}; - ZEND_BEGIN_ARG_INFO_EX(ai_HttpDechunkStream_decode, 0, 0, 1) ZEND_ARG_INFO(0, data) ZEND_ARG_INFO(1, decoded_len) @@ -1156,12 +642,12 @@ static PHP_METHOD(HttpDechunkStream, decode) size_t len; zval *zlen = NULL; - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|z!", &str, &len, &zlen)) { + if (EXPECTED(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|z!", &str, &len, &zlen))) { const char *end_ptr; char *enc_str = NULL; size_t enc_len; - if ((end_ptr = php_http_encoding_dechunk(str, len, &enc_str, &enc_len))) { + if (EXPECTED(end_ptr = php_http_encoding_dechunk(str, len, &enc_str, &enc_len))) { if (zlen) { ZVAL_DEREF(zlen); zval_dtor(zlen); @@ -1199,28 +685,6 @@ PHP_MINIT_FUNCTION(http_encoding) zend_declare_class_constant_long(php_http_encoding_stream_class_entry, ZEND_STRL("FLUSH_SYNC"), PHP_HTTP_ENCODING_STREAM_FLUSH_SYNC); zend_declare_class_constant_long(php_http_encoding_stream_class_entry, ZEND_STRL("FLUSH_FULL"), PHP_HTTP_ENCODING_STREAM_FLUSH_FULL); - memset(&ce, 0, sizeof(ce)); - INIT_NS_CLASS_ENTRY(ce, "http\\Encoding\\Stream", "Deflate", php_http_deflate_stream_methods); - php_http_deflate_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_encoding_stream_class_entry); - php_http_deflate_stream_class_entry->create_object = php_http_encoding_stream_object_new; - - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_GZIP"), PHP_HTTP_DEFLATE_TYPE_GZIP); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_ZLIB"), PHP_HTTP_DEFLATE_TYPE_ZLIB); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_RAW"), PHP_HTTP_DEFLATE_TYPE_RAW); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_DEF"), PHP_HTTP_DEFLATE_LEVEL_DEF); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_MIN"), PHP_HTTP_DEFLATE_LEVEL_MIN); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_MAX"), PHP_HTTP_DEFLATE_LEVEL_MAX); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_DEF"), PHP_HTTP_DEFLATE_STRATEGY_DEF); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_FILT"), PHP_HTTP_DEFLATE_STRATEGY_FILT); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_HUFF"), PHP_HTTP_DEFLATE_STRATEGY_HUFF); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_RLE"), PHP_HTTP_DEFLATE_STRATEGY_RLE); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_FIXED"), PHP_HTTP_DEFLATE_STRATEGY_FIXED); - - memset(&ce, 0, sizeof(ce)); - INIT_NS_CLASS_ENTRY(ce, "http\\Encoding\\Stream", "Inflate", php_http_inflate_stream_methods); - php_http_inflate_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_encoding_stream_class_entry); - php_http_inflate_stream_class_entry->create_object = php_http_encoding_stream_object_new; - memset(&ce, 0, sizeof(ce)); INIT_NS_CLASS_ENTRY(ce, "http\\Encoding\\Stream", "Dechunk", php_http_dechunk_stream_methods); php_http_dechunk_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_encoding_stream_class_entry); @@ -1229,7 +693,6 @@ PHP_MINIT_FUNCTION(http_encoding) return SUCCESS; } - /* * Local variables: * tab-width: 4 diff --git a/src/php_http_encoding.h b/src/php_http_encoding.h index a924eaf..0995021 100644 --- a/src/php_http_encoding.h +++ b/src/php_http_encoding.h @@ -13,113 +13,17 @@ #ifndef PHP_HTTP_ENCODING_H #define PHP_HTTP_ENCODING_H -#include - extern PHP_MINIT_FUNCTION(http_encoding); -extern PHP_RINIT_FUNCTION(http_encoding); -extern PHP_RSHUTDOWN_FUNCTION(http_encoding); - -#define PHP_HTTP_INFLATE_ROUNDS 100 - -#define PHP_HTTP_DEFLATE_BUFFER_SIZE_GUESS(S) \ - (((size_t) ((double) S * (double) 1.015)) + 10 + 8 + 4 + 1) -#define PHP_HTTP_INFLATE_BUFFER_SIZE_GUESS(S) \ - (((S) + 1) << 3) -#define PHP_HTTP_INFLATE_BUFFER_SIZE_ALIGN(S) \ - ((S) += (S) >> (3)) - -#define PHP_HTTP_DEFLATE_BUFFER_SIZE 0x8000 -#define PHP_HTTP_INFLATE_BUFFER_SIZE 0x1000 - -#define PHP_HTTP_DEFLATE_LEVEL_DEF 0x00000000 -#define PHP_HTTP_DEFLATE_LEVEL_MIN 0x00000001 -#define PHP_HTTP_DEFLATE_LEVEL_MAX 0x00000009 -#define PHP_HTTP_DEFLATE_TYPE_ZLIB 0x00000000 -#define PHP_HTTP_DEFLATE_TYPE_GZIP 0x00000010 -#define PHP_HTTP_DEFLATE_TYPE_RAW 0x00000020 -#define PHP_HTTP_DEFLATE_STRATEGY_DEF 0x00000000 -#define PHP_HTTP_DEFLATE_STRATEGY_FILT 0x00000100 -#define PHP_HTTP_DEFLATE_STRATEGY_HUFF 0x00000200 -#define PHP_HTTP_DEFLATE_STRATEGY_RLE 0x00000300 -#define PHP_HTTP_DEFLATE_STRATEGY_FIXED 0x00000400 - -#define PHP_HTTP_DEFLATE_LEVEL_SET(flags, level) \ - switch (flags & 0xf) \ - { \ - default: \ - if ((flags & 0xf) < 10) { \ - level = flags & 0xf; \ - break; \ - } \ - case PHP_HTTP_DEFLATE_LEVEL_DEF: \ - level = Z_DEFAULT_COMPRESSION; \ - break; \ - } - -#define PHP_HTTP_DEFLATE_WBITS_SET(flags, wbits) \ - switch (flags & 0xf0) \ - { \ - case PHP_HTTP_DEFLATE_TYPE_GZIP: \ - wbits = PHP_HTTP_WINDOW_BITS_GZIP; \ - break; \ - case PHP_HTTP_DEFLATE_TYPE_RAW: \ - wbits = PHP_HTTP_WINDOW_BITS_RAW; \ - break; \ - default: \ - wbits = PHP_HTTP_WINDOW_BITS_ZLIB; \ - break; \ - } - -#define PHP_HTTP_INFLATE_WBITS_SET(flags, wbits) \ - if (flags & PHP_HTTP_INFLATE_TYPE_RAW) { \ - wbits = PHP_HTTP_WINDOW_BITS_RAW; \ -} else { \ - wbits = PHP_HTTP_WINDOW_BITS_ANY; \ -} - -#define PHP_HTTP_DEFLATE_STRATEGY_SET(flags, strategy) \ - switch (flags & 0xf00) \ - { \ - case PHP_HTTP_DEFLATE_STRATEGY_FILT: \ - strategy = Z_FILTERED; \ - break; \ - case PHP_HTTP_DEFLATE_STRATEGY_HUFF: \ - strategy = Z_HUFFMAN_ONLY; \ - break; \ - case PHP_HTTP_DEFLATE_STRATEGY_RLE: \ - strategy = Z_RLE; \ - break; \ - case PHP_HTTP_DEFLATE_STRATEGY_FIXED: \ - strategy = Z_FIXED; \ - break; \ - default: \ - strategy = Z_DEFAULT_STRATEGY; \ - break; \ - } - -#define PHP_HTTP_WINDOW_BITS_ZLIB 0x0000000f -#define PHP_HTTP_WINDOW_BITS_GZIP 0x0000001f -#define PHP_HTTP_WINDOW_BITS_ANY 0x0000002f -#define PHP_HTTP_WINDOW_BITS_RAW -0x000000f - -#ifndef Z_FIXED -/* Z_FIXED does not exist prior 1.2.2.2 */ -# define Z_FIXED 0 -#endif -#define PHP_HTTP_INFLATE_TYPE_ZLIB 0x00000000 -#define PHP_HTTP_INFLATE_TYPE_GZIP 0x00000000 -#define PHP_HTTP_INFLATE_TYPE_RAW 0x00000001 +#define PHP_HTTP_ENCODING_STREAM_PERSISTENT 0x01000000 #define PHP_HTTP_ENCODING_STREAM_FLUSH_NONE 0x00000000 #define PHP_HTTP_ENCODING_STREAM_FLUSH_SYNC 0x00100000 #define PHP_HTTP_ENCODING_STREAM_FLUSH_FULL 0x00200000 -#define PHP_HTTP_ENCODING_STREAM_FLUSH_FLAG(f) \ - (((f) & PHP_HTTP_ENCODING_STREAM_FLUSH_FULL) ? Z_FULL_FLUSH : \ - (((f) & PHP_HTTP_ENCODING_STREAM_FLUSH_SYNC) ? Z_SYNC_FLUSH : Z_NO_FLUSH)) - -#define PHP_HTTP_ENCODING_STREAM_PERSISTENT 0x01000000 +#define PHP_HTTP_ENCODING_STREAM_FLUSH_FLAG(flags, full, sync, none) \ + (((flags) & PHP_HTTP_ENCODING_STREAM_FLUSH_FULL) ? (full) : \ + (((flags) & PHP_HTTP_ENCODING_STREAM_FLUSH_SYNC) ? (sync) : (none))) typedef struct php_http_encoding_stream php_http_encoding_stream_t; @@ -147,9 +51,6 @@ struct php_http_encoding_stream { php_http_encoding_stream_ops_t *ops; }; -PHP_HTTP_API php_http_encoding_stream_ops_t *php_http_encoding_stream_get_deflate_ops(void); -PHP_HTTP_API php_http_encoding_stream_ops_t *php_http_encoding_stream_get_inflate_ops(void); -PHP_HTTP_API php_http_encoding_stream_ops_t *php_http_encoding_stream_get_dechunk_ops(void); PHP_HTTP_API php_http_encoding_stream_t *php_http_encoding_stream_init(php_http_encoding_stream_t *s, php_http_encoding_stream_ops_t *ops, unsigned flags); PHP_HTTP_API php_http_encoding_stream_t *php_http_encoding_stream_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to); @@ -161,9 +62,9 @@ PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_stream_finish(php_http_encoding_ PHP_HTTP_API void php_http_encoding_stream_dtor(php_http_encoding_stream_t *s); PHP_HTTP_API void php_http_encoding_stream_free(php_http_encoding_stream_t **s); +PHP_HTTP_API php_http_encoding_stream_ops_t *php_http_encoding_stream_get_dechunk_ops(void); PHP_HTTP_API const char *php_http_encoding_dechunk(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len); -PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_deflate(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len); -PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len); +PHP_HTTP_API zend_class_entry *php_http_get_dechunk_stream_class_entry(void); typedef struct php_http_encoding_stream_object { php_http_encoding_stream_t *stream; @@ -177,10 +78,6 @@ php_http_encoding_stream_object_t *php_http_encoding_stream_object_new_ex(zend_c zend_object *php_http_encoding_stream_object_clone(zval *object); void php_http_encoding_stream_object_free(zend_object *object); -PHP_HTTP_API zend_class_entry *php_http_get_deflate_stream_class_entry(void); -PHP_HTTP_API zend_class_entry *php_http_get_inflate_stream_class_entry(void); -PHP_HTTP_API zend_class_entry *php_http_get_dechunk_stream_class_entry(void); - #endif /* diff --git a/src/php_http_encoding_brotli.c b/src/php_http_encoding_brotli.c new file mode 100644 index 0000000..a02c66e --- /dev/null +++ b/src/php_http_encoding_brotli.c @@ -0,0 +1,461 @@ +/* + +--------------------------------------------------------------------+ + | 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-2014, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +#include "php_http_api.h" + +#if PHP_HTTP_HAVE_LIBBROTLI + +#define PHP_HTTP_DEBROTLI_ROUNDS 100 +#define PHP_HTTP_ENBROTLI_ROUNDS 100 + +#define PHP_HTTP_ENBROTLI_BUFFER_SIZE_GUESS(S) \ + BrotliEncoderMaxCompressedSize(S) +#define PHP_HTTP_DEBROTLI_BUFFER_SIZE_ALIGN(S) \ + ((S) += (S) >> 3) + +#define PHP_HTTP_ENBROTLI_FLUSH_FLAG(flags) \ + PHP_HTTP_ENCODING_STREAM_FLUSH_FLAG((flags), BROTLI_OPERATION_FLUSH, BROTLI_OPERATION_FLUSH, BROTLI_OPERATION_PROCESS) + +#define PHP_HTTP_ENBROTLI_BUFFER_SIZE 0x8000 +#define PHP_HTTP_DEBROTLI_BUFFER_SIZE 0x1000 + +#define PHP_HTTP_ENBROTLI_LEVEL_SET(flags, level) \ + level = (((flags) & 0xf) ?: PHP_HTTP_ENBROTLI_LEVEL_DEF) +#define PHP_HTTP_ENBROTLI_WBITS_SET(flags, wbits) \ + wbits = ((((flags) >> 4) & 0xff) ?: (PHP_HTTP_ENBROTLI_WBITS_DEF >> 4)) +#define PHP_HTTP_ENBROTLI_MODE_SET(flags, mode) \ + mode = (((flags) >> 12) & 0xf) + + +ZEND_RESULT_CODE php_http_encoding_enbrotli(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len) +{ + BROTLI_BOOL rc; + int q, win, mode; + + *encoded_len = PHP_HTTP_ENBROTLI_BUFFER_SIZE_GUESS(data_len); + *encoded = emalloc(*encoded_len + 1); + + PHP_HTTP_ENBROTLI_LEVEL_SET(flags, q); + PHP_HTTP_ENBROTLI_WBITS_SET(flags, win); + PHP_HTTP_ENBROTLI_MODE_SET(flags, mode); + + rc = BrotliEncoderCompress(q, win, mode, data_len, (const unsigned char *) data, encoded_len, (unsigned char *) *encoded); + if (rc) { + return SUCCESS; + } + + PTR_SET(*encoded, NULL); + *encoded_len = 0; + + php_error_docref(NULL, E_WARNING, "Could not brotli encode data"); + return FAILURE; +} + +ZEND_RESULT_CODE php_http_encoding_debrotli(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len) +{ + BrotliDecoderState *br; + BrotliDecoderResult rc; + php_http_buffer_t buffer; + unsigned char *ptr; + size_t len; + int round = 0; + + *decoded = NULL; + *decoded_len = 0; + + br = BrotliDecoderCreateInstance(NULL, NULL, NULL); + if (!br) { + return FAILURE; + } + + php_http_buffer_init_ex(&buffer, encoded_len, PHP_HTTP_BUFFER_INIT_PREALLOC); + + do { + if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize_ex(&buffer, buffer.size, 0, 1)) { + break; + } else { + len = buffer.free; + ptr = (unsigned char *) &buffer.data[buffer.used]; + + rc = BrotliDecoderDecompressStream(br, &encoded_len, (const unsigned char **) &encoded, &len, &ptr, NULL); + + php_http_buffer_account(&buffer, buffer.free - len); + PHP_HTTP_DEBROTLI_BUFFER_SIZE_ALIGN(buffer.size); + } + } while ((BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT == rc) && ++round < PHP_HTTP_DEBROTLI_ROUNDS); + + BrotliDecoderDestroyInstance(br); + + if (rc == BROTLI_DECODER_RESULT_SUCCESS) { + php_http_buffer_shrink(&buffer); + php_http_buffer_fix(&buffer); + + *decoded = buffer.data; + *decoded_len = buffer.used; + + return SUCCESS; + } + + php_http_buffer_dtor(&buffer); + php_error_docref(NULL, E_WARNING, "Could not brotli decode data: %s", BrotliDecoderErrorString(rc)); + + return FAILURE; +} + +struct enbrotli_ctx { + BrotliEncoderState *br; + php_http_buffer_t buffer; +}; + +static php_http_encoding_stream_t *enbrotli_init(php_http_encoding_stream_t *s) +{ + BrotliEncoderState *br; + int q, win, mode, p = (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT); + struct enbrotli_ctx *ctx = pemalloc(sizeof(*ctx), p); + + PHP_HTTP_ENBROTLI_LEVEL_SET(s->flags, q); + PHP_HTTP_ENBROTLI_WBITS_SET(s->flags, win); + PHP_HTTP_ENBROTLI_MODE_SET(s->flags, mode); + + br = BrotliEncoderCreateInstance(NULL, NULL, NULL); + if (br) { + BrotliEncoderSetParameter(br, BROTLI_PARAM_QUALITY, q); + BrotliEncoderSetParameter(br, BROTLI_PARAM_LGWIN, win); + BrotliEncoderSetParameter(br, BROTLI_PARAM_MODE, mode); + + ctx->br = br; + php_http_buffer_init_ex(&ctx->buffer, PHP_HTTP_ENBROTLI_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0); + s->ctx = ctx; + return s; + } + + pefree(ctx, p); + php_error_docref(NULL, E_WARNING, "Failed to initialize brotli encoding stream"); + return NULL; +} + +static ZEND_RESULT_CODE enbrotli_update(php_http_encoding_stream_t *s, const char *data, size_t data_len, char **encoded, size_t *encoded_len) +{ + struct enbrotli_ctx *ctx = s->ctx; + const unsigned char *in_ptr; + unsigned char *out_ptr; + size_t in_len, out_len; + BROTLI_BOOL rc; + + php_http_buffer_append(&ctx->buffer, data, data_len); + + in_len = ctx->buffer.used; + in_ptr = (unsigned char *) ctx->buffer.data; + out_len = PHP_HTTP_ENBROTLI_BUFFER_SIZE_GUESS(in_len); + out_ptr = emalloc(out_len + 1); + + *encoded_len = out_len; + *encoded = (char *) out_ptr; + + rc = BrotliEncoderCompressStream(ctx->br, PHP_HTTP_ENBROTLI_FLUSH_FLAG(s->flags), &in_len, &in_ptr, &out_len, &out_ptr, NULL); + if (rc) { + if (in_len) { + php_http_buffer_cut(&ctx->buffer, 0, ctx->buffer.used - in_len); + } else { + php_http_buffer_reset(&ctx->buffer); + } + + *encoded_len -= out_len; + *encoded = erealloc(*encoded, *encoded_len + 1); + (*encoded)[*encoded_len] = '\0'; + return SUCCESS; + } + + PTR_SET(*encoded, NULL); + *encoded_len = 0; + + php_error_docref(NULL, E_WARNING, "Failed to update brotli encoding stream"); + return FAILURE; +} + +static inline ZEND_RESULT_CODE enbrotli_flush_ex(php_http_encoding_stream_t *s, BrotliEncoderOperation op, char **encoded, size_t *encoded_len) +{ + struct enbrotli_ctx *ctx = s->ctx; + size_t out_len, len; + ptrdiff_t off; + unsigned char *out_ptr, *ptr; + BROTLI_BOOL rc; + int round = 0; + + out_len = len = 32; + out_ptr = ptr = emalloc(len); + + do { + const unsigned char *empty = NULL; + size_t unused = 0; + + rc = BrotliEncoderCompressStream(ctx->br, op, &unused, &empty, &out_len, &out_ptr, NULL); + if (!rc) { + break; + } + if (!BrotliEncoderHasMoreOutput(ctx->br)) { + *encoded_len = len - out_len; + *encoded = erealloc(ptr, *encoded_len + 1); + (*encoded)[*encoded_len] = '\0'; + return SUCCESS; + } + + /* again */ + off = out_ptr - ptr; + len += 32; + ptr = erealloc(ptr, len); + out_len += 32; + out_ptr = ptr + off; + } while (++round < PHP_HTTP_ENBROTLI_ROUNDS); + + efree(ptr); + *encoded = NULL; + *encoded_len = 0; + + php_error_docref(NULL, E_WARNING, "Failed to flush brotli encoding stream"); + return FAILURE; +} + +static ZEND_RESULT_CODE enbrotli_flush(php_http_encoding_stream_t *s, char **encoded, size_t *encoded_len) +{ + return enbrotli_flush_ex(s, BROTLI_OPERATION_FLUSH, encoded, encoded_len); +} + +static ZEND_RESULT_CODE enbrotli_finish(php_http_encoding_stream_t *s, char **encoded, size_t *encoded_len) +{ + return enbrotli_flush_ex(s, BROTLI_OPERATION_FINISH, encoded, encoded_len); +} + +static zend_bool enbrotli_done(php_http_encoding_stream_t *s) +{ + struct enbrotli_ctx *ctx = s->ctx; + + return !ctx->buffer.used && BrotliEncoderIsFinished(ctx->br); +} + +static void enbrotli_dtor(php_http_encoding_stream_t *s) +{ + if (s->ctx) { + struct enbrotli_ctx *ctx = s->ctx; + + php_http_buffer_dtor(&ctx->buffer); + BrotliEncoderDestroyInstance(ctx->br); + pefree(ctx, (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT)); + s->ctx = NULL; + } +} + +static php_http_encoding_stream_t *debrotli_init(php_http_encoding_stream_t *s) +{ + BrotliDecoderState *br; + + br = BrotliDecoderCreateInstance(NULL, NULL, NULL); + if (br) { + s->ctx = br; + return s; + } + + php_error_docref(NULL, E_WARNING, "Failed to initialize brotli decoding stream"); + return NULL; +} + +static ZEND_RESULT_CODE debrotli_update(php_http_encoding_stream_t *s, const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len) +{ + BrotliDecoderState *br = s->ctx; + php_http_buffer_t buffer; + BrotliDecoderResult rc; + unsigned char *ptr; + size_t len; + int round = 0; + + php_http_buffer_init_ex(&buffer, encoded_len, PHP_HTTP_BUFFER_INIT_PREALLOC); + + do { + if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize_ex(&buffer, buffer.size, 0, 1)) { + break; + } else { + len = buffer.free; + ptr = (unsigned char *) &buffer.data[buffer.used]; + + rc = BrotliDecoderDecompressStream(br, &encoded_len, (const unsigned char **) &encoded, &len, &ptr, NULL); + + php_http_buffer_account(&buffer, buffer.free - len); + PHP_HTTP_DEBROTLI_BUFFER_SIZE_ALIGN(buffer.size); + } + } while ((BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT == rc) && ++round < PHP_HTTP_DEBROTLI_ROUNDS); + + if (rc != BROTLI_DECODER_RESULT_ERROR) { + php_http_buffer_shrink(&buffer); + php_http_buffer_fix(&buffer); + + *decoded = buffer.data; + *decoded_len = buffer.used; + + return SUCCESS; + } + + php_http_buffer_dtor(&buffer); + php_error_docref(NULL, E_WARNING, "Could not brotli decode data: %s", BrotliDecoderErrorString(rc)); + + return FAILURE; +} + +static zend_bool debrotli_done(php_http_encoding_stream_t *s) +{ + return BrotliDecoderIsFinished(s->ctx); +} + +static void debrotli_dtor(php_http_encoding_stream_t *s) +{ + if (s->ctx) { + BrotliDecoderDestroyInstance(s->ctx); + s->ctx = NULL; + } +} + +static php_http_encoding_stream_ops_t php_http_encoding_enbrotli_ops = { + enbrotli_init, + NULL, + enbrotli_update, + enbrotli_flush, + enbrotli_done, + enbrotli_finish, + enbrotli_dtor +}; + +php_http_encoding_stream_ops_t *php_http_encoding_stream_get_enbrotli_ops(void) +{ + return &php_http_encoding_enbrotli_ops; +} + +static php_http_encoding_stream_ops_t php_http_encoding_debrotli_ops = { + debrotli_init, + NULL, + debrotli_update, + NULL, + debrotli_done, + NULL, + debrotli_dtor +}; + +php_http_encoding_stream_ops_t *php_http_encoding_stream_get_debrotli_ops(void) +{ + return &php_http_encoding_debrotli_ops; +} + +static zend_class_entry *php_http_enbrotli_stream_class_entry; +zend_class_entry *php_http_get_enbrotli_stream_class_entry(void) +{ + return php_http_enbrotli_stream_class_entry; +} + +static zend_class_entry *php_http_debrotli_stream_class_entry; +zend_class_entry *php_http_get_debrotli_stream_class_entry(void) +{ + return php_http_debrotli_stream_class_entry; +} + +ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnbrotliStream_encode, 0, 0, 1) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO(); +static PHP_METHOD(HttpEnbrotliStream, encode) +{ + char *str; + size_t len; + zend_long flags = PHP_HTTP_ENBROTLI_MODE_GENERIC | PHP_HTTP_ENBROTLI_WBITS_DEF | PHP_HTTP_ENBROTLI_LEVEL_DEF; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &len, &flags)) { + char *enc_str = NULL; + size_t enc_len; + + if (SUCCESS == php_http_encoding_enbrotli(flags, str, len, &enc_str, &enc_len)) { + if (enc_str) { + RETURN_STR(php_http_cs2zs(enc_str, enc_len)); + } else { + RETURN_EMPTY_STRING(); + } + } + } + RETURN_FALSE; +} +static zend_function_entry php_http_enbrotli_stream_methods[] = { + PHP_ME(HttpEnbrotliStream, encode, ai_HttpEnbrotliStream_encode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + EMPTY_FUNCTION_ENTRY +}; + +ZEND_BEGIN_ARG_INFO_EX(ai_HttpDebrotliStream_decode, 0, 0, 1) + ZEND_ARG_INFO(0, data) +ZEND_END_ARG_INFO(); +static PHP_METHOD(HttpDebrotliStream, decode) +{ + char *str; + size_t len; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &len)) { + char *enc_str = NULL; + size_t enc_len; + + if (SUCCESS == php_http_encoding_debrotli(str, len, &enc_str, &enc_len)) { + if (enc_str) { + RETURN_STR(php_http_cs2zs(enc_str, enc_len)); + } else { + RETURN_EMPTY_STRING(); + } + } + } + RETURN_FALSE; +} + +static zend_function_entry php_http_debrotli_stream_methods[] = { + PHP_ME(HttpDebrotliStream, decode, ai_HttpDebrotliStream_decode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + EMPTY_FUNCTION_ENTRY +}; + +PHP_MINIT_FUNCTION(http_encoding_brotli) +{ + zend_class_entry ce; + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "http\\Encoding\\Stream", "Enbrotli", php_http_enbrotli_stream_methods); + php_http_enbrotli_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_get_encoding_stream_class_entry()); + php_http_enbrotli_stream_class_entry->create_object = php_http_encoding_stream_object_new; + + zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("LEVEL_MIN"), PHP_HTTP_ENBROTLI_LEVEL_MIN); + zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("LEVEL_DEF"), PHP_HTTP_ENBROTLI_LEVEL_DEF); + zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("LEVEL_MAX"), PHP_HTTP_ENBROTLI_LEVEL_MAX); + zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("WBITS_MIN"), PHP_HTTP_ENBROTLI_WBITS_MIN); + zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("WBITS_DEF"), PHP_HTTP_ENBROTLI_WBITS_DEF); + zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("WBITS_MAX"), PHP_HTTP_ENBROTLI_WBITS_MAX); + zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("MODE_GENERIC"), PHP_HTTP_ENBROTLI_MODE_GENERIC); + zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("MODE_TEXT"), PHP_HTTP_ENBROTLI_MODE_TEXT); + zend_declare_class_constant_long(php_http_enbrotli_stream_class_entry, ZEND_STRL("MODE_FONT"), PHP_HTTP_ENBROTLI_MODE_FONT); + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "http\\Encoding\\Stream", "Debrotli", php_http_debrotli_stream_methods); + php_http_debrotli_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_get_encoding_stream_class_entry()); + php_http_debrotli_stream_class_entry->create_object = php_http_encoding_stream_object_new; + + return SUCCESS; +} + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + diff --git a/src/php_http_encoding_brotli.h b/src/php_http_encoding_brotli.h new file mode 100644 index 0000000..923ac54 --- /dev/null +++ b/src/php_http_encoding_brotli.h @@ -0,0 +1,54 @@ +/* + +--------------------------------------------------------------------+ + | 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-2014, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +#ifndef PHP_HTTP_ENCODING_BROTLI_H +#define PHP_HTTP_ENCODING_BROTLI_H +#if PHP_HTTP_HAVE_LIBBROTLI + +#include +#include + +extern PHP_MINIT_FUNCTION(http_encoding_brotli); + +PHP_HTTP_API zend_class_entry *php_http_get_enbrotli_stream_class_entry(void); +PHP_HTTP_API zend_class_entry *php_http_get_debrotli_stream_class_entry(void); + +PHP_HTTP_API php_http_encoding_stream_ops_t *php_http_encoding_stream_get_enbrotli_ops(void); +PHP_HTTP_API php_http_encoding_stream_ops_t *php_http_encoding_stream_get_debrotli_ops(void); + +PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_enbrotli(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len); +PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_debrotli(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len); + +#define PHP_HTTP_ENBROTLI_LEVEL_MIN 0x00000001 +#define PHP_HTTP_ENBROTLI_LEVEL_DEF 0x0000000a +#define PHP_HTTP_ENBROTLI_LEVEL_MAX 0x0000000b + +#define PHP_HTTP_ENBROTLI_WBITS_MIN 0x000000a0 +#define PHP_HTTP_ENBROTLI_WBITS_DEF 0x00000160 +#define PHP_HTTP_ENBROTLI_WBITS_MAX 0x00000180 + +#define PHP_HTTP_ENBROTLI_MODE_GENERIC 0x00000000 +#define PHP_HTTP_ENBROTLI_MODE_TEXT 0x00001000 +#define PHP_HTTP_ENBROTLI_MODE_FONT 0x00002000 + +#endif +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + diff --git a/src/php_http_encoding_zlib.c b/src/php_http_encoding_zlib.c new file mode 100644 index 0000000..5e19cec --- /dev/null +++ b/src/php_http_encoding_zlib.c @@ -0,0 +1,650 @@ +/* + +--------------------------------------------------------------------+ + | 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-2014, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +#include "php_http_api.h" + +#define PHP_HTTP_INFLATE_ROUNDS 100 +#define PHP_HTTP_INFLATE_BUFFER_SIZE 0x1000 + +#define PHP_HTTP_DEFLATE_BUFFER_SIZE 0x8000 + +#define PHP_HTTP_DEFLATE_BUFFER_SIZE_GUESS(S) \ + (((size_t) ((double) S * (double) 1.015)) + 10 + 8 + 4 + 1) + +#define PHP_HTTP_INFLATE_BUFFER_SIZE_GUESS(S) \ + (((S) + 1) << 3) +#define PHP_HTTP_INFLATE_BUFFER_SIZE_ALIGN(S) \ + ((S) += (S) >> (3)) + +#define PHP_HTTP_INFLATE_FLUSH_FLAG(flags) \ + PHP_HTTP_ENCODING_STREAM_FLUSH_FLAG((flags), Z_FULL_FLUSH, Z_SYNC_FLUSH, Z_NO_FLUSH) +#define PHP_HTTP_DEFLATE_FLUSH_FLAG(flags) \ + PHP_HTTP_ENCODING_STREAM_FLUSH_FLAG((flags), Z_FULL_FLUSH, Z_SYNC_FLUSH, Z_NO_FLUSH) + +#define PHP_HTTP_WINDOW_BITS_ZLIB 0x0000000f +#define PHP_HTTP_WINDOW_BITS_GZIP 0x0000001f +#define PHP_HTTP_WINDOW_BITS_ANY 0x0000002f +#define PHP_HTTP_WINDOW_BITS_RAW -0x000000f + +#define PHP_HTTP_DEFLATE_LEVEL_SET(flags, level) \ + switch (flags & 0xf) \ + { \ + default: \ + if ((flags & 0xf) < 10) { \ + level = flags & 0xf; \ + break; \ + } \ + case PHP_HTTP_DEFLATE_LEVEL_DEF: \ + level = Z_DEFAULT_COMPRESSION; \ + break; \ + } + +#define PHP_HTTP_DEFLATE_WBITS_SET(flags, wbits) \ + switch (flags & 0xf0) \ + { \ + case PHP_HTTP_DEFLATE_TYPE_GZIP: \ + wbits = PHP_HTTP_WINDOW_BITS_GZIP; \ + break; \ + case PHP_HTTP_DEFLATE_TYPE_RAW: \ + wbits = PHP_HTTP_WINDOW_BITS_RAW; \ + break; \ + default: \ + wbits = PHP_HTTP_WINDOW_BITS_ZLIB; \ + break; \ + } + +#define PHP_HTTP_INFLATE_WBITS_SET(flags, wbits) \ + if (flags & PHP_HTTP_INFLATE_TYPE_RAW) { \ + wbits = PHP_HTTP_WINDOW_BITS_RAW; \ + } else { \ + wbits = PHP_HTTP_WINDOW_BITS_ANY; \ + } + +#define PHP_HTTP_DEFLATE_STRATEGY_SET(flags, strategy) \ + switch (flags & 0xf00) \ + { \ + case PHP_HTTP_DEFLATE_STRATEGY_FILT: \ + strategy = Z_FILTERED; \ + break; \ + case PHP_HTTP_DEFLATE_STRATEGY_HUFF: \ + strategy = Z_HUFFMAN_ONLY; \ + break; \ + case PHP_HTTP_DEFLATE_STRATEGY_RLE: \ + strategy = Z_RLE; \ + break; \ + case PHP_HTTP_DEFLATE_STRATEGY_FIXED: \ + strategy = Z_FIXED; \ + break; \ + default: \ + strategy = Z_DEFAULT_STRATEGY; \ + break; \ + } + +ZEND_RESULT_CODE php_http_encoding_deflate(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len) +{ + int status, level, wbits, strategy; + z_stream Z; + + PHP_HTTP_DEFLATE_LEVEL_SET(flags, level); + PHP_HTTP_DEFLATE_WBITS_SET(flags, wbits); + PHP_HTTP_DEFLATE_STRATEGY_SET(flags, strategy); + + memset(&Z, 0, sizeof(z_stream)); + *encoded = NULL; + *encoded_len = 0; + + status = deflateInit2(&Z, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy); + if (EXPECTED(Z_OK == status)) { + *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len); + *encoded = emalloc(*encoded_len); + + Z.next_in = (Bytef *) data; + Z.next_out = (Bytef *) *encoded; + Z.avail_in = data_len; + Z.avail_out = *encoded_len; + + status = deflate(&Z, Z_FINISH); + deflateEnd(&Z); + + if (EXPECTED(Z_STREAM_END == status)) { + /* size buffer down to actual length */ + *encoded = erealloc(*encoded, Z.total_out + 1); + (*encoded)[*encoded_len = Z.total_out] = '\0'; + return SUCCESS; + } else { + PTR_SET(*encoded, NULL); + *encoded_len = 0; + } + } + + php_error_docref(NULL, E_WARNING, "Could not deflate data: %s", zError(status)); + return FAILURE; +} + +static php_http_encoding_stream_t *deflate_init(php_http_encoding_stream_t *s) +{ + int status, level, wbits, strategy, p = (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT); + z_streamp ctx = pecalloc(1, sizeof(z_stream), p); + + PHP_HTTP_DEFLATE_LEVEL_SET(s->flags, level); + PHP_HTTP_DEFLATE_WBITS_SET(s->flags, wbits); + PHP_HTTP_DEFLATE_STRATEGY_SET(s->flags, strategy); + + if (EXPECTED(Z_OK == (status = deflateInit2(ctx, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy)))) { + if (EXPECTED(ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) { + s->ctx = ctx; + return s; + } + deflateEnd(ctx); + status = Z_MEM_ERROR; + } + pefree(ctx, p); + php_error_docref(NULL, E_WARNING, "Failed to initialize deflate encoding stream: %s", zError(status)); + return NULL; +} + +static php_http_encoding_stream_t *deflate_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to) +{ + int status, p = to->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT; + z_streamp from_ctx = from->ctx, to_ctx = pecalloc(1, sizeof(*to_ctx), p); + + if (Z_OK == (status = deflateCopy(to_ctx, from_ctx))) { + if ((to_ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) { + php_http_buffer_append(to_ctx->opaque, PHP_HTTP_BUFFER(from_ctx->opaque)->data, PHP_HTTP_BUFFER(from_ctx->opaque)->used); + to->ctx = to_ctx; + return to; + } + deflateEnd(to_ctx); + status = Z_MEM_ERROR; + } + php_error_docref(NULL, E_WARNING, "Failed to copy deflate encoding stream: %s", zError(status)); + return NULL; +} + +static ZEND_RESULT_CODE deflate_update(php_http_encoding_stream_t *s, const char *data, size_t data_len, char **encoded, size_t *encoded_len) +{ + int status; + z_streamp ctx = s->ctx; + + /* append input to our buffer */ + php_http_buffer_append(PHP_HTTP_BUFFER(ctx->opaque), data, data_len); + + ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data; + ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used; + + /* deflate */ + *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len); + *encoded = emalloc(*encoded_len); + ctx->avail_out = *encoded_len; + ctx->next_out = (Bytef *) *encoded; + + switch (status = deflate(ctx, PHP_HTTP_DEFLATE_FLUSH_FLAG(s->flags))) { + case Z_OK: + case Z_STREAM_END: + /* cut processed chunk off the buffer */ + if (ctx->avail_in) { + php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in); + } else { + php_http_buffer_reset(PHP_HTTP_BUFFER(ctx->opaque)); + } + + /* size buffer down to actual size */ + *encoded_len -= ctx->avail_out; + *encoded = erealloc(*encoded, *encoded_len + 1); + (*encoded)[*encoded_len] = '\0'; + return SUCCESS; + } + + PTR_SET(*encoded, NULL); + *encoded_len = 0; + php_error_docref(NULL, E_WARNING, "Failed to update deflate stream: %s", zError(status)); + return FAILURE; +} + +static ZEND_RESULT_CODE deflate_flush(php_http_encoding_stream_t *s, char **encoded, size_t *encoded_len) +{ + int status; + z_streamp ctx = s->ctx; + + *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE; + *encoded = emalloc(*encoded_len); + + ctx->avail_in = 0; + ctx->next_in = NULL; + ctx->avail_out = *encoded_len; + ctx->next_out = (Bytef *) *encoded; + + status = deflate(ctx, Z_FULL_FLUSH); + if (EXPECTED(Z_OK == status || Z_STREAM_END == status)) { + *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE - ctx->avail_out; + *encoded = erealloc(*encoded, *encoded_len + 1); + (*encoded)[*encoded_len] = '\0'; + return SUCCESS; + } + + PTR_SET(*encoded, NULL); + *encoded_len = 0; + php_error_docref(NULL, E_WARNING, "Failed to flush deflate stream: %s", zError(status)); + return FAILURE; +} + +static ZEND_RESULT_CODE deflate_finish(php_http_encoding_stream_t *s, char **encoded, size_t *encoded_len) +{ + int status; + z_streamp ctx = s->ctx; + + *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE; + *encoded = emalloc(*encoded_len); + + /* deflate remaining input */ + ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data; + ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used; + + ctx->avail_out = *encoded_len; + ctx->next_out = (Bytef *) *encoded; + + do { + status = deflate(ctx, Z_FINISH); + } while (Z_OK == status); + + if (EXPECTED(Z_STREAM_END == status)) { + /* cut processed input off */ + php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in); + + /* size down */ + *encoded_len -= ctx->avail_out; + *encoded = erealloc(*encoded, *encoded_len + 1); + (*encoded)[*encoded_len] = '\0'; + return SUCCESS; + } + + PTR_SET(*encoded, NULL); + *encoded_len = 0; + php_error_docref(NULL, E_WARNING, "Failed to finish deflate stream: %s", zError(status)); + return FAILURE; +} + +static zend_bool deflate_done(php_http_encoding_stream_t *s) +{ + z_streamp ctx = s->ctx; + return !ctx->avail_in && !PHP_HTTP_BUFFER(ctx->opaque)->used; +} + +static void deflate_dtor(php_http_encoding_stream_t *s) +{ + if (EXPECTED(s->ctx)) { + z_streamp ctx = s->ctx; + + if (EXPECTED(ctx->opaque)) { + php_http_buffer_free((php_http_buffer_t **) &ctx->opaque); + } + deflateEnd(ctx); + pefree(ctx, (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT)); + s->ctx = NULL; + } +} + +static inline int php_http_inflate_rounds(z_stream *Z, int flush, char **buf, size_t *len) +{ + int status = 0, round = 0; + php_http_buffer_t buffer; + + *buf = NULL; + *len = 0; + + php_http_buffer_init_ex(&buffer, Z->avail_in, PHP_HTTP_BUFFER_INIT_PREALLOC); + + do { + if (UNEXPECTED(PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize_ex(&buffer, buffer.size, 0, 1))) { + status = Z_MEM_ERROR; + } else { + Z->avail_out = buffer.free; + Z->next_out = (Bytef *) buffer.data + buffer.used; +#if 0 + fprintf(stderr, "\n%3d: %3d PRIOR: size=%7lu,\tfree=%7lu,\tused=%7lu,\tavail_in=%7lu,\tavail_out=%7lu\n", round, status, buffer.size, buffer.free, buffer.used, Z->avail_in, Z->avail_out); +#endif + status = inflate(Z, flush); + php_http_buffer_account(&buffer, buffer.free - Z->avail_out); +#if 0 + fprintf(stderr, "%3d: %3d AFTER: size=%7lu,\tfree=%7lu,\tused=%7lu,\tavail_in=%7lu,\tavail_out=%7lu\n", round, status, buffer.size, buffer.free, buffer.used, Z->avail_in, Z->avail_out); +#endif + PHP_HTTP_INFLATE_BUFFER_SIZE_ALIGN(buffer.size); + } + } while ((Z_BUF_ERROR == status || (Z_OK == status && Z->avail_in)) && ++round < PHP_HTTP_INFLATE_ROUNDS); + + if (EXPECTED(status == Z_OK || status == Z_STREAM_END)) { + php_http_buffer_shrink(&buffer); + php_http_buffer_fix(&buffer); + *buf = buffer.data; + *len = buffer.used; + } else { + php_http_buffer_dtor(&buffer); + } + + return status; +} + +ZEND_RESULT_CODE php_http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len) +{ + z_stream Z; + int status, wbits = PHP_HTTP_WINDOW_BITS_ANY; + + memset(&Z, 0, sizeof(z_stream)); + +retry_raw_inflate: + status = inflateInit2(&Z, wbits); + if (EXPECTED(Z_OK == status)) { + Z.next_in = (Bytef *) data; + Z.avail_in = data_len + 1; /* include the terminating NULL, see #61287 */ + + switch (status = php_http_inflate_rounds(&Z, Z_NO_FLUSH, decoded, decoded_len)) { + case Z_STREAM_END: + inflateEnd(&Z); + return SUCCESS; + + case Z_OK: + status = Z_DATA_ERROR; + break; + + case Z_DATA_ERROR: + /* raw deflated data? */ + if (PHP_HTTP_WINDOW_BITS_ANY == wbits) { + inflateEnd(&Z); + wbits = PHP_HTTP_WINDOW_BITS_RAW; + goto retry_raw_inflate; + } + break; + } + inflateEnd(&Z); + + if (*decoded_len && *decoded) { + efree(*decoded); + } + } + + php_error_docref(NULL, E_WARNING, "Could not inflate data: %s", zError(status)); + return FAILURE; +} + +static php_http_encoding_stream_t *inflate_init(php_http_encoding_stream_t *s) +{ + int status, wbits, p = (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT); + z_streamp ctx = pecalloc(1, sizeof(z_stream), p); + + PHP_HTTP_INFLATE_WBITS_SET(s->flags, wbits); + + if (EXPECTED(Z_OK == (status = inflateInit2(ctx, wbits)))) { + if ((ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) { + s->ctx = ctx; + return s; + } + inflateEnd(ctx); + status = Z_MEM_ERROR; + } + pefree(ctx, p); + php_error_docref(NULL, E_WARNING, "Failed to initialize inflate stream: %s", zError(status)); + return NULL; +} + +static php_http_encoding_stream_t *inflate_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to) +{ + int status, p = from->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT; + z_streamp from_ctx = from->ctx, to_ctx = pecalloc(1, sizeof(*to_ctx), p); + + if (Z_OK == (status = inflateCopy(to_ctx, from_ctx))) { + if ((to_ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) { + php_http_buffer_append(to_ctx->opaque, PHP_HTTP_BUFFER(from_ctx->opaque)->data, PHP_HTTP_BUFFER(from_ctx->opaque)->used); + to->ctx = to_ctx; + return to; + } + inflateEnd(to_ctx); + status = Z_MEM_ERROR; + } + php_error_docref(NULL, E_WARNING, "Failed to copy inflate encoding stream: %s", zError(status)); + return NULL; +} + +static ZEND_RESULT_CODE inflate_update(php_http_encoding_stream_t *s, const char *data, size_t data_len, char **decoded, size_t *decoded_len) +{ + int status; + z_streamp ctx = s->ctx; + + /* append input to buffer */ + php_http_buffer_append(PHP_HTTP_BUFFER(ctx->opaque), data, data_len); + +retry_raw_inflate: + ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data; + ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used; + + switch (status = php_http_inflate_rounds(ctx, PHP_HTTP_INFLATE_FLUSH_FLAG(s->flags), decoded, decoded_len)) { + case Z_OK: + case Z_STREAM_END: + /* cut off */ + if (ctx->avail_in) { + php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in); + } else { + php_http_buffer_reset(PHP_HTTP_BUFFER(ctx->opaque)); + } + return SUCCESS; + + case Z_DATA_ERROR: + /* raw deflated data ? */ + if (!(s->flags & PHP_HTTP_INFLATE_TYPE_RAW) && !ctx->total_out) { + inflateEnd(ctx); + s->flags |= PHP_HTTP_INFLATE_TYPE_RAW; + inflateInit2(ctx, PHP_HTTP_WINDOW_BITS_RAW); + goto retry_raw_inflate; + } + break; + } + + php_error_docref(NULL, E_WARNING, "Failed to update inflate stream: %s", zError(status)); + return FAILURE; +} + +static ZEND_RESULT_CODE inflate_finish(php_http_encoding_stream_t *s, char **decoded, size_t *decoded_len) +{ + int status; + z_streamp ctx = s->ctx; + + if (!PHP_HTTP_BUFFER(ctx->opaque)->used) { + *decoded = NULL; + *decoded_len = 0; + return SUCCESS; + } + + *decoded_len = (PHP_HTTP_BUFFER(ctx->opaque)->used + 1) * PHP_HTTP_INFLATE_ROUNDS; + *decoded = emalloc(*decoded_len); + + /* inflate remaining input */ + ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data; + ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used; + + ctx->avail_out = *decoded_len; + ctx->next_out = (Bytef *) *decoded; + + if (Z_STREAM_END == (status = inflate(ctx, Z_FINISH))) { + /* cut processed input off */ + php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in); + + /* size down */ + *decoded_len -= ctx->avail_out; + *decoded = erealloc(*decoded, *decoded_len + 1); + (*decoded)[*decoded_len] = '\0'; + return SUCCESS; + } + + PTR_SET(*decoded, NULL); + *decoded_len = 0; + php_error_docref(NULL, E_WARNING, "Failed to finish inflate stream: %s", zError(status)); + return FAILURE; +} + +static zend_bool inflate_done(php_http_encoding_stream_t *s) +{ + z_streamp ctx = s->ctx; + return !ctx->avail_in && !PHP_HTTP_BUFFER(ctx->opaque)->used; +} + +static void inflate_dtor(php_http_encoding_stream_t *s) +{ + if (EXPECTED(s->ctx)) { + z_streamp ctx = s->ctx; + + if (EXPECTED(ctx->opaque)) { + php_http_buffer_free((php_http_buffer_t **) &ctx->opaque); + } + inflateEnd(ctx); + pefree(ctx, (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT)); + s->ctx = NULL; + } +} + +static php_http_encoding_stream_ops_t php_http_encoding_deflate_ops = { + deflate_init, + deflate_copy, + deflate_update, + deflate_flush, + deflate_done, + deflate_finish, + deflate_dtor +}; + +php_http_encoding_stream_ops_t *php_http_encoding_stream_get_deflate_ops(void) +{ + return &php_http_encoding_deflate_ops; +} + +static php_http_encoding_stream_ops_t php_http_encoding_inflate_ops = { + inflate_init, + inflate_copy, + inflate_update, + NULL, + inflate_done, + inflate_finish, + inflate_dtor +}; + +php_http_encoding_stream_ops_t *php_http_encoding_stream_get_inflate_ops(void) +{ + return &php_http_encoding_inflate_ops; +} + +static zend_class_entry *php_http_deflate_stream_class_entry; +zend_class_entry *php_http_get_deflate_stream_class_entry(void) +{ + return php_http_deflate_stream_class_entry; +} + +static zend_class_entry *php_http_inflate_stream_class_entry; +zend_class_entry *php_http_get_inflate_stream_class_entry(void) +{ + return php_http_inflate_stream_class_entry; +} + + +ZEND_BEGIN_ARG_INFO_EX(ai_HttpDeflateStream_encode, 0, 0, 1) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO(); +static PHP_METHOD(HttpDeflateStream, encode) +{ + char *str; + size_t len; + zend_long flags = 0; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &str, &len, &flags)) { + char *enc_str = NULL; + size_t enc_len; + + if (SUCCESS == php_http_encoding_deflate(flags, str, len, &enc_str, &enc_len)) { + if (enc_str) { + RETURN_STR(php_http_cs2zs(enc_str, enc_len)); + } else { + RETURN_EMPTY_STRING(); + } + } + } + RETURN_FALSE; +} + +static zend_function_entry php_http_deflate_stream_methods[] = { + PHP_ME(HttpDeflateStream, encode, ai_HttpDeflateStream_encode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + EMPTY_FUNCTION_ENTRY +}; + +ZEND_BEGIN_ARG_INFO_EX(ai_HttpInflateStream_decode, 0, 0, 1) + ZEND_ARG_INFO(0, data) +ZEND_END_ARG_INFO(); +static PHP_METHOD(HttpInflateStream, decode) +{ + char *str; + size_t len; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &len)) { + char *enc_str = NULL; + size_t enc_len; + + if (SUCCESS == php_http_encoding_inflate(str, len, &enc_str, &enc_len)) { + if (enc_str) { + RETURN_STR(php_http_cs2zs(enc_str, enc_len)); + } else { + RETURN_EMPTY_STRING(); + } + } + } + RETURN_FALSE; +} + +static zend_function_entry php_http_inflate_stream_methods[] = { + PHP_ME(HttpInflateStream, decode, ai_HttpInflateStream_decode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + EMPTY_FUNCTION_ENTRY +}; + +PHP_MINIT_FUNCTION(http_encoding_zlib) +{ + zend_class_entry ce; + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "http\\Encoding\\Stream", "Deflate", php_http_deflate_stream_methods); + php_http_deflate_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_get_encoding_stream_class_entry()); + php_http_deflate_stream_class_entry->create_object = php_http_encoding_stream_object_new; + + zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_GZIP"), PHP_HTTP_DEFLATE_TYPE_GZIP); + zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_ZLIB"), PHP_HTTP_DEFLATE_TYPE_ZLIB); + zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_RAW"), PHP_HTTP_DEFLATE_TYPE_RAW); + zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_DEF"), PHP_HTTP_DEFLATE_LEVEL_DEF); + zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_MIN"), PHP_HTTP_DEFLATE_LEVEL_MIN); + zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_MAX"), PHP_HTTP_DEFLATE_LEVEL_MAX); + zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_DEF"), PHP_HTTP_DEFLATE_STRATEGY_DEF); + zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_FILT"), PHP_HTTP_DEFLATE_STRATEGY_FILT); + zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_HUFF"), PHP_HTTP_DEFLATE_STRATEGY_HUFF); + zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_RLE"), PHP_HTTP_DEFLATE_STRATEGY_RLE); + zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_FIXED"), PHP_HTTP_DEFLATE_STRATEGY_FIXED); + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "http\\Encoding\\Stream", "Inflate", php_http_inflate_stream_methods); + php_http_inflate_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_get_encoding_stream_class_entry()); + php_http_inflate_stream_class_entry->create_object = php_http_encoding_stream_object_new; + + return SUCCESS; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + diff --git a/src/php_http_encoding_zlib.h b/src/php_http_encoding_zlib.h new file mode 100644 index 0000000..9d17560 --- /dev/null +++ b/src/php_http_encoding_zlib.h @@ -0,0 +1,62 @@ +/* + +--------------------------------------------------------------------+ + | 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-2014, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +#ifndef PHP_HTTP_ENCODING_ZLIB_H +#define PHP_HTTP_ENCODING_ZLIB_H + +#include "php_http_encoding.h" + +#include + +#ifndef Z_FIXED +/* Z_FIXED does not exist prior 1.2.2.2 */ +# define Z_FIXED 0 +#endif + +extern PHP_MINIT_FUNCTION(http_encoding_zlib); + +zend_class_entry *php_http_get_deflate_stream_class_entry(void); +zend_class_entry *php_http_get_inflate_stream_class_entry(void); + +PHP_HTTP_API php_http_encoding_stream_ops_t *php_http_encoding_stream_get_deflate_ops(void); +PHP_HTTP_API php_http_encoding_stream_ops_t *php_http_encoding_stream_get_inflate_ops(void); + +PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_deflate(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len); +PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len); + +#define PHP_HTTP_DEFLATE_LEVEL_DEF 0x00000000 +#define PHP_HTTP_DEFLATE_LEVEL_MIN 0x00000001 +#define PHP_HTTP_DEFLATE_LEVEL_MAX 0x00000009 +#define PHP_HTTP_DEFLATE_TYPE_ZLIB 0x00000000 +#define PHP_HTTP_DEFLATE_TYPE_GZIP 0x00000010 +#define PHP_HTTP_DEFLATE_TYPE_RAW 0x00000020 +#define PHP_HTTP_DEFLATE_STRATEGY_DEF 0x00000000 +#define PHP_HTTP_DEFLATE_STRATEGY_FILT 0x00000100 +#define PHP_HTTP_DEFLATE_STRATEGY_HUFF 0x00000200 +#define PHP_HTTP_DEFLATE_STRATEGY_RLE 0x00000300 +#define PHP_HTTP_DEFLATE_STRATEGY_FIXED 0x00000400 + +#define PHP_HTTP_INFLATE_TYPE_ZLIB 0x00000000 +#define PHP_HTTP_INFLATE_TYPE_GZIP 0x00000000 +#define PHP_HTTP_INFLATE_TYPE_RAW 0x00000001 + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + -- 2.30.2