Skip to content

Commit 5c6d757

Browse files
committed
experimental: dcz support in output handler
1 parent 4cc370f commit 5c6d757

File tree

1 file changed

+87
-6
lines changed

1 file changed

+87
-6
lines changed

zstd.c

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
#include <php.h>
2929
#include <SAPI.h>
3030
#include <php_ini.h>
31+
#include <ext/hash/php_hash.h>
32+
#include <ext/hash/php_hash_sha.h>
33+
#include <ext/standard/base64.h>
3134
#include <ext/standard/file.h>
3235
#include <ext/standard/info.h>
3336
#if PHP_VERSION_ID < 70200
@@ -80,6 +83,7 @@ struct _php_zstd_context {
8083
ZSTD_DDict *ddict;
8184
ZSTD_inBuffer input;
8285
ZSTD_outBuffer output;
86+
zend_uchar dict_digest[32];
8387
zend_object std;
8488
};
8589

@@ -107,6 +111,7 @@ static void php_zstd_context_init(php_zstd_context *ctx)
107111
ctx->output.dst = NULL;
108112
ctx->output.size = 0;
109113
ctx->output.pos = 0;
114+
memset(ctx->dict_digest, 0, sizeof(ctx->dict_digest));
110115
}
111116

112117
static void php_zstd_context_free(php_zstd_context *ctx)
@@ -1253,6 +1258,9 @@ static int APC_UNSERIALIZER_NAME(zstd)(APC_UNSERIALIZER_ARGS)
12531258
#if PHP_VERSION_ID >= 80000
12541259
#define PHP_ZSTD_OUTPUT_HANDLER_NAME "zstd output compression"
12551260

1261+
#define PHP_ZSTD_ENCODING_ZSTD (1 << 0)
1262+
#define PHP_ZSTD_ENCODING_DCZ (1 << 1)
1263+
12561264
static int php_zstd_output_encoding(void)
12571265
{
12581266
zval *enc;
@@ -1270,7 +1278,10 @@ static int php_zstd_output_encoding(void)
12701278
sizeof("HTTP_ACCEPT_ENCODING") - 1))) {
12711279
convert_to_string(enc);
12721280
if (strstr(Z_STRVAL_P(enc), "zstd")) {
1273-
PHP_ZSTD_G(compression_coding) = 1;
1281+
PHP_ZSTD_G(compression_coding) = PHP_ZSTD_ENCODING_ZSTD;
1282+
}
1283+
if (strstr(Z_STRVAL_P(enc), "dcz")) {
1284+
PHP_ZSTD_G(compression_coding) |= PHP_ZSTD_ENCODING_DCZ;
12741285
}
12751286
}
12761287
}
@@ -1308,6 +1319,50 @@ php_zstd_output_handler_load_dict(php_zstd_context *ctx)
13081319

13091320
php_stream_close(stream);
13101321

1322+
if (!data) {
1323+
return NULL;
1324+
}
1325+
1326+
if (PHP_ZSTD_G(compression_coding) & PHP_ZSTD_ENCODING_DCZ) {
1327+
zval *available;
1328+
if ((Z_TYPE(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY
1329+
|| zend_is_auto_global_str(ZEND_STRL("_SERVER")))
1330+
&& (available = zend_hash_str_find(
1331+
Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]),
1332+
"HTTP_AVAILABLE_DICTIONARY",
1333+
sizeof("HTTP_AVAILABLE_DICTIONARY") - 1))) {
1334+
convert_to_string(available);
1335+
1336+
PHP_SHA256_CTX context;
1337+
PHP_SHA256Init(&context);
1338+
PHP_SHA256Update(&context, ZSTR_VAL(data), ZSTR_LEN(data));
1339+
PHP_SHA256Final(ctx->dict_digest, &context);
1340+
1341+
zend_string *b64;
1342+
b64 = php_base64_encode(ctx->dict_digest, sizeof(ctx->dict_digest));
1343+
if (b64) {
1344+
if (Z_STRLEN_P(available) <= ZSTR_LEN(b64)
1345+
|| memcmp(ZSTR_VAL(b64),
1346+
Z_STRVAL_P(available) + 1, ZSTR_LEN(b64))) {
1347+
php_error_docref(NULL, E_WARNING,
1348+
"zstd: invalid available-dictionary: "
1349+
"request(%s) != actual(%s)",
1350+
Z_STRVAL_P(available), ZSTR_VAL(b64));
1351+
PHP_ZSTD_G(compression_coding) &= ~PHP_ZSTD_ENCODING_DCZ;
1352+
zend_string_release(data);
1353+
data = NULL;
1354+
}
1355+
zend_string_free(b64);
1356+
}
1357+
} else {
1358+
php_error_docref(NULL, E_WARNING,
1359+
"zstd: not found available-dictionary");
1360+
PHP_ZSTD_G(compression_coding) &= ~PHP_ZSTD_ENCODING_DCZ;
1361+
zend_string_release(data);
1362+
data = NULL;
1363+
}
1364+
}
1365+
13111366
return data;
13121367
}
13131368

@@ -1347,9 +1402,19 @@ php_zstd_output_handler_write(php_zstd_context *ctx,
13471402
if (output_context->out.size < ctx->output.size) {
13481403
output_context->out.size = ctx->output.size;
13491404
}
1350-
output_context->out.data = emalloc(output_context->out.size);
1405+
1406+
if ((output_context->op & PHP_OUTPUT_HANDLER_START)
1407+
&& (PHP_ZSTD_G(compression_coding) & PHP_ZSTD_ENCODING_DCZ)) {
1408+
output_context->out.size += 40;
1409+
output_context->out.data = emalloc(output_context->out.size);
1410+
memcpy(output_context->out.data, "\x5e\x2a\x4d\x18\x20\x00\x00\x00", 8);
1411+
memcpy(output_context->out.data + 8, ctx->dict_digest, 32);
1412+
output_context->out.used = 40;
1413+
} else {
1414+
output_context->out.data = emalloc(output_context->out.size);
1415+
output_context->out.used = 0;
1416+
}
13511417
output_context->out.free = 1;
1352-
output_context->out.used = 0;
13531418

13541419
do {
13551420
ctx->output.pos = 0;
@@ -1430,7 +1495,13 @@ php_zstd_output_handler(void **handler_context,
14301495
&& (output_context->op != (PHP_OUTPUT_HANDLER_START
14311496
|PHP_OUTPUT_HANDLER_CLEAN
14321497
|PHP_OUTPUT_HANDLER_FINAL))) {
1433-
sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"), 1, 0);
1498+
if (PHP_ZSTD_G(compression_coding) & PHP_ZSTD_ENCODING_DCZ) {
1499+
sapi_add_header_ex(
1500+
ZEND_STRL("Vary: Accept-Encoding, Available-Dictionary"),
1501+
1, 0);
1502+
} else {
1503+
sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"), 1, 0);
1504+
}
14341505
}
14351506
return FAILURE;
14361507
}
@@ -1449,8 +1520,18 @@ php_zstd_output_handler(void **handler_context,
14491520
if (SG(headers_sent) || !PHP_ZSTD_G(output_compression)) {
14501521
return FAILURE;
14511522
}
1452-
sapi_add_header_ex(ZEND_STRL("Content-Encoding: zstd"), 1, 1);
1453-
sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"), 1, 0);
1523+
if (PHP_ZSTD_G(compression_coding) & PHP_ZSTD_ENCODING_DCZ) {
1524+
sapi_add_header_ex(ZEND_STRL("Content-Encoding: dcz"),
1525+
1, 1);
1526+
sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding, "
1527+
"Available-Dictionary"),
1528+
1, 0);
1529+
} else {
1530+
sapi_add_header_ex(ZEND_STRL("Content-Encoding: zstd"),
1531+
1, 1);
1532+
sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"),
1533+
1, 0);
1534+
}
14541535
php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE,
14551536
NULL);
14561537
}

0 commit comments

Comments
 (0)