diff --git a/README.md b/README.md index 3decb19..ac93633 100644 --- a/README.md +++ b/README.md @@ -64,11 +64,12 @@ extension=zstd.so ### Output handler option -Name | Default | Changeable -------------------------------- | ------- | ---------- -zstd.output\_compression | 0 | PHP\_INI\_ALL -zstd.output\_compression\_level | 3 | PHP\_INI\_ALL -zstd.output\_compression\_dict | "" | PHP\_INI\_ALL +Name | Default | Changeable +--------------------------------------- | ------- | ---------- +zstd.output\_compression | 0 | PHP\_INI\_ALL +zstd.output\_compression\_level | 3 | PHP\_INI\_ALL +zstd.output\_compression\_exclude\_types | "" | PHP\_INI\_ALL +zstd.output\_compression\_dict | "" | PHP\_INI\_ALL * zstd.output\_compression _boolean_/_integer_ @@ -85,6 +86,14 @@ zstd.output\_compression\_dict | "" | PHP\_INI\_ALL Specify a value between 1 to 22. The default value of `ZSTD_COMPRESS_LEVEL_DEFAULT` (3). +* zstd.output\_compression\_exclude\_types _string_ + + Comma-separated list of MIME types to exclude from transparent output + compression, e.g. `"image/*,application/pdf"`. Both exact types + (`application/pdf`) and wildcard subtypes (`image/*`) are supported. + The comparison ignores any parameters (such as `charset`) present in + the response's `Content-Type` header. + * zstd.output\_compression\_dict _string_ Specifies the path to the compressed dictionary file to be diff --git a/php_zstd.h b/php_zstd.h index c0f23a1..b032e46 100644 --- a/php_zstd.h +++ b/php_zstd.h @@ -52,6 +52,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zstd) zend_long output_compression; zend_long output_compression_default; zend_long output_compression_level; + char *output_compression_exclude_types; char *output_compression_dict; php_zstd_context *ob_handler; bool handler_registered; diff --git a/tests/ob_exclude_001.phpt b/tests/ob_exclude_001.phpt new file mode 100644 index 0000000..c38e5fd --- /dev/null +++ b/tests/ob_exclude_001.phpt @@ -0,0 +1,20 @@ +--TEST-- +zstd.output_compression_exclude_types exact match +--SKIPIF-- + +--INI-- +zstd.output_compression=1 +zstd.output_compression_exclude_types=application/pdf +--ENV-- +HTTP_ACCEPT_ENCODING=zstd +--GET-- +ob=020 +--FILE-- + +--EXPECT-- +hi diff --git a/tests/ob_exclude_002.phpt b/tests/ob_exclude_002.phpt new file mode 100644 index 0000000..8a1a36b --- /dev/null +++ b/tests/ob_exclude_002.phpt @@ -0,0 +1,20 @@ +--TEST-- +zstd.output_compression_exclude_types wildcard match +--SKIPIF-- + +--INI-- +zstd.output_compression=1 +zstd.output_compression_exclude_types=image/* +--ENV-- +HTTP_ACCEPT_ENCODING=zstd +--GET-- +ob=021 +--FILE-- + +--EXPECT-- +hi diff --git a/tests/ob_exclude_003.phpt b/tests/ob_exclude_003.phpt new file mode 100644 index 0000000..fc82db4 --- /dev/null +++ b/tests/ob_exclude_003.phpt @@ -0,0 +1,20 @@ +--TEST-- +zstd.output_compression_exclude_types match with charset parameter +--SKIPIF-- + +--INI-- +zstd.output_compression=1 +zstd.output_compression_exclude_types=text/html +--ENV-- +HTTP_ACCEPT_ENCODING=zstd +--GET-- +ob=022 +--FILE-- + +--EXPECT-- +hi diff --git a/tests/ob_exclude_004.phpt b/tests/ob_exclude_004.phpt new file mode 100644 index 0000000..851b9e7 --- /dev/null +++ b/tests/ob_exclude_004.phpt @@ -0,0 +1,23 @@ +--TEST-- +zstd.output_compression_exclude_types no match still compresses +--SKIPIF-- + +--INI-- +zstd.output_compression=1 +zstd.output_compression_exclude_types=image/*,application/pdf +--ENV-- +HTTP_ACCEPT_ENCODING=zstd +--GET-- +ob=023 +--FILE-- + +--EXPECT_EXTERNAL-- +files/ob_001.zstd +--EXPECTHEADERS-- +Content-Encoding: zstd +Vary: Accept-Encoding diff --git a/zstd.c b/zstd.c index 9eb0541..c2e58c9 100644 --- a/zstd.c +++ b/zstd.c @@ -1295,6 +1295,64 @@ static int php_zstd_output_encoding(void) return PHP_ZSTD_G(compression_coding); } +static int php_zstd_output_mimetype_excluded(void) +{ + const char *mimetype = SG(sapi_headers).mimetype; + const char *exclude = PHP_ZSTD_G(output_compression_exclude_types); + const char *p, *end; + size_t mimetype_len; + + if (!mimetype || !*mimetype || !exclude || !*exclude) { + return 0; + } + + end = mimetype; + while (*end && *end != ';' && *end != ' ' && *end != '\t' + && *end != '\r' && *end != '\n') { + end++; + } + mimetype_len = end - mimetype; + p = exclude; + + while (*p) { + size_t token_len; + + while (*p == ',' || *p == ' ' || *p == '\t' || *p == '\r' + || *p == '\n') { + p++; + } + + end = p; + while (*end && *end != ',' && *end != ' ' && *end != '\t' + && *end != '\r' && *end != '\n') { + end++; + } + + token_len = end - p; + + if (token_len > 0) { + if (token_len >= 2 && p[token_len - 2] == '/' + && p[token_len - 1] == '*') { + size_t prefix_len = token_len - 1; + + if (mimetype_len >= prefix_len + && !strncasecmp(mimetype, p, prefix_len)) { + return 1; + } + } + + if (mimetype_len == token_len + && !strncasecmp(mimetype, p, token_len)) { + return 1; + } + } + + p = end; + } + + return 0; +} + static zend_string* php_zstd_output_handler_load_dict(php_zstd_context *ctx) { @@ -1499,6 +1557,11 @@ php_zstd_output_handler(void **handler_context, { php_zstd_context *ctx = *(php_zstd_context **) handler_context; + if ((output_context->op & PHP_OUTPUT_HANDLER_START) + && php_zstd_output_mimetype_excluded()) { + return FAILURE; + } + if (!php_zstd_output_encoding()) { if ((output_context->op & PHP_OUTPUT_HANDLER_START) && (output_context->op != (PHP_OUTPUT_HANDLER_START @@ -1736,6 +1799,10 @@ PHP_INI_BEGIN() TOSTRING(ZSTD_CLEVEL_DEFAULT), PHP_INI_ALL, OnUpdateLong, output_compression_level, zend_zstd_globals, zstd_globals) + STD_PHP_INI_ENTRY("zstd.output_compression_exclude_types", "", + PHP_INI_ALL, OnUpdateString, + output_compression_exclude_types, + zend_zstd_globals, zstd_globals) STD_PHP_INI_ENTRY("zstd.output_compression_dict", "", PHP_INI_ALL, OnUpdateString, output_compression_dict, zend_zstd_globals, zstd_globals)