Skip to content

Commit d757244

Browse files
committed
experimental: dcb support in output handler
1 parent eaf5c66 commit d757244

File tree

1 file changed

+90
-10
lines changed

1 file changed

+90
-10
lines changed

brotli.c

Lines changed: 90 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
#include <php.h>
66
#include <SAPI.h>
77
#include <php_ini.h>
8+
#include <ext/hash/php_hash.h>
9+
#include <ext/hash/php_hash_sha.h>
10+
#include <ext/standard/base64.h>
811
#include <ext/standard/file.h>
912
#include <ext/standard/info.h>
1013
#include <ext/standard/php_smart_string.h>
@@ -152,6 +155,7 @@ struct _php_brotli_context {
152155
#if defined(USE_BROTLI_DICTIONARY)
153156
BrotliEncoderPreparedDictionary *dictionary;
154157
#endif
158+
zend_uchar dict_digest[32];
155159
size_t available_in;
156160
const uint8_t *next_in;
157161
size_t available_out;
@@ -167,6 +171,7 @@ static void php_brotli_context_init(php_brotli_context *ctx)
167171
#if defined(USE_BROTLI_DICTIONARY)
168172
ctx->dictionary = NULL;
169173
#endif
174+
memset(ctx->dict_digest, 0, sizeof(ctx->dict_digest));
170175
ctx->available_in = 0;
171176
ctx->next_in = NULL;
172177
ctx->available_out = 0;
@@ -333,6 +338,9 @@ static void php_brotli_context_close(php_brotli_context *ctx)
333338

334339
#define PHP_BROTLI_OUTPUT_HANDLER "ob_brotli_handler"
335340

341+
#define PHP_BROTLI_ENCODING_BR (1 << 0)
342+
#define PHP_BROTLI_ENCODING_DCB (1 << 1)
343+
336344
static int php_brotli_output_encoding(void)
337345
{
338346
#if defined(COMPILE_DL_BROTLI) && defined(ZTS)
@@ -349,8 +357,13 @@ static int php_brotli_output_encoding(void)
349357
sizeof("HTTP_ACCEPT_ENCODING") - 1))) {
350358
convert_to_string(enc);
351359
if (strstr(Z_STRVAL_P(enc), "br")) {
352-
BROTLI_G(compression_coding) = 1;
360+
BROTLI_G(compression_coding) = PHP_BROTLI_ENCODING_BR;
361+
}
362+
#if defined(USE_BROTLI_DICTIONARY)
363+
if (strstr(Z_STRVAL_P(enc), "dcb")) {
364+
BROTLI_G(compression_coding) |= PHP_BROTLI_ENCODING_DCB;
353365
}
366+
#endif
354367
}
355368
}
356369

@@ -401,6 +414,50 @@ static zend_string *php_brotli_output_handler_load_dict(php_brotli_context *ctx)
401414

402415
php_stream_close(stream);
403416

417+
if (!dict) {
418+
return NULL;
419+
}
420+
421+
if (BROTLI_G(compression_coding) & PHP_BROTLI_ENCODING_DCB) {
422+
zval *available;
423+
if ((Z_TYPE(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY
424+
|| zend_is_auto_global_str(ZEND_STRL("_SERVER")))
425+
&& (available = zend_hash_str_find(
426+
Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]),
427+
"HTTP_AVAILABLE_DICTIONARY",
428+
sizeof("HTTP_AVAILABLE_DICTIONARY") - 1))) {
429+
convert_to_string(available);
430+
431+
PHP_SHA256_CTX context;
432+
PHP_SHA256Init(&context);
433+
PHP_SHA256Update(&context, ZSTR_VAL(dict), ZSTR_LEN(dict));
434+
PHP_SHA256Final(ctx->dict_digest, &context);
435+
436+
zend_string *b64;
437+
b64 = php_base64_encode(ctx->dict_digest, sizeof(ctx->dict_digest));
438+
if (b64) {
439+
if (Z_STRLEN_P(available) <= ZSTR_LEN(b64)
440+
|| memcmp(ZSTR_VAL(b64),
441+
Z_STRVAL_P(available) + 1, ZSTR_LEN(b64))) {
442+
php_error_docref(NULL, E_WARNING,
443+
"brotli: invalid available-dictionary: "
444+
"request(%s) != actual(%s)",
445+
Z_STRVAL_P(available), ZSTR_VAL(b64));
446+
BROTLI_G(compression_coding) &= ~PHP_BROTLI_ENCODING_DCB;
447+
zend_string_free(dict);
448+
dict = NULL;
449+
}
450+
zend_string_free(b64);
451+
}
452+
} else {
453+
php_error_docref(NULL, E_WARNING,
454+
"brotli: not found available-dictionary");
455+
BROTLI_G(compression_coding) &= ~PHP_BROTLI_ENCODING_DCB;
456+
zend_string_free(dict);
457+
dict = NULL;
458+
}
459+
}
460+
404461
return dict;
405462
#else
406463
php_error_docref(NULL, E_WARNING,
@@ -443,8 +500,14 @@ static int php_brotli_output_handler(void **handler_context,
443500
&& (output_context->op
444501
!= (PHP_OUTPUT_HANDLER_START
445502
|PHP_OUTPUT_HANDLER_CLEAN|PHP_OUTPUT_HANDLER_FINAL))) {
446-
sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"),
447-
1, 0);
503+
if (BROTLI_G(compression_coding) & PHP_BROTLI_ENCODING_DCB) {
504+
sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding, "
505+
"Available-Dictionary"),
506+
1, 0);
507+
} else {
508+
sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"),
509+
1, 0);
510+
}
448511
}
449512
return FAILURE;
450513
}
@@ -502,21 +565,38 @@ static int php_brotli_output_handler(void **handler_context,
502565
}
503566

504567
if (output_context->op & PHP_OUTPUT_HANDLER_FINAL) {
568+
uint8_t *data;
505569
size_t size = (size_t)(ctx->next_out - ctx->output);
506570

507-
uint8_t *data = (uint8_t *)emalloc(size);
508-
memcpy(data, ctx->output, size);
571+
if (BROTLI_G(compression_coding) & PHP_BROTLI_ENCODING_DCB) {
572+
// \xFF + DCB + dictionary-sha256 + compress-data
573+
data = (uint8_t *)emalloc(size + 36);
574+
memcpy(data, "\xff", 1);
575+
memcpy(data + 1, "DCB", 3);
576+
memcpy(data + 4, ctx->dict_digest, 32);
577+
memcpy(data + 36, ctx->output, size);
578+
size += 36;
579+
580+
sapi_add_header_ex(ZEND_STRL("Content-Encoding: dcb"),
581+
1, 1);
582+
sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding, "
583+
"Available-Dictionary"),
584+
1, 0);
585+
} else {
586+
data = (uint8_t *)emalloc(size);
587+
memcpy(data, ctx->output, size);
588+
589+
sapi_add_header_ex(ZEND_STRL("Content-Encoding: br"),
590+
1, 1);
591+
sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"),
592+
1, 0);
593+
}
509594

510595
output_context->out.data = data;
511596
output_context->out.used = size;
512597
output_context->out.free = 1;
513598

514599
php_brotli_context_close(ctx);
515-
516-
sapi_add_header_ex(ZEND_STRL("Content-Encoding: br"),
517-
1, 1);
518-
sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"),
519-
1, 0);
520600
}
521601
} else {
522602
php_brotli_context_close(ctx);

0 commit comments

Comments
 (0)