5
5
#include <php.h>
6
6
#include <SAPI.h>
7
7
#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>
8
11
#include <ext/standard/file.h>
9
12
#include <ext/standard/info.h>
10
13
#include <ext/standard/php_smart_string.h>
@@ -152,6 +155,7 @@ struct _php_brotli_context {
152
155
#if defined(USE_BROTLI_DICTIONARY )
153
156
BrotliEncoderPreparedDictionary * dictionary ;
154
157
#endif
158
+ zend_uchar dict_digest [32 ];
155
159
size_t available_in ;
156
160
const uint8_t * next_in ;
157
161
size_t available_out ;
@@ -167,6 +171,7 @@ static void php_brotli_context_init(php_brotli_context *ctx)
167
171
#if defined(USE_BROTLI_DICTIONARY )
168
172
ctx -> dictionary = NULL ;
169
173
#endif
174
+ memset (ctx -> dict_digest , 0 , sizeof (ctx -> dict_digest ));
170
175
ctx -> available_in = 0 ;
171
176
ctx -> next_in = NULL ;
172
177
ctx -> available_out = 0 ;
@@ -333,6 +338,9 @@ static void php_brotli_context_close(php_brotli_context *ctx)
333
338
334
339
#define PHP_BROTLI_OUTPUT_HANDLER "ob_brotli_handler"
335
340
341
+ #define PHP_BROTLI_ENCODING_BR (1 << 0)
342
+ #define PHP_BROTLI_ENCODING_DCB (1 << 1)
343
+
336
344
static int php_brotli_output_encoding (void )
337
345
{
338
346
#if defined(COMPILE_DL_BROTLI ) && defined(ZTS )
@@ -349,8 +357,13 @@ static int php_brotli_output_encoding(void)
349
357
sizeof ("HTTP_ACCEPT_ENCODING" ) - 1 ))) {
350
358
convert_to_string (enc );
351
359
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 ;
353
365
}
366
+ #endif
354
367
}
355
368
}
356
369
@@ -401,6 +414,50 @@ static zend_string *php_brotli_output_handler_load_dict(php_brotli_context *ctx)
401
414
402
415
php_stream_close (stream );
403
416
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
+
404
461
return dict ;
405
462
#else
406
463
php_error_docref (NULL , E_WARNING ,
@@ -443,8 +500,14 @@ static int php_brotli_output_handler(void **handler_context,
443
500
&& (output_context -> op
444
501
!= (PHP_OUTPUT_HANDLER_START
445
502
|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
+ }
448
511
}
449
512
return FAILURE ;
450
513
}
@@ -502,21 +565,38 @@ static int php_brotli_output_handler(void **handler_context,
502
565
}
503
566
504
567
if (output_context -> op & PHP_OUTPUT_HANDLER_FINAL ) {
568
+ uint8_t * data ;
505
569
size_t size = (size_t )(ctx -> next_out - ctx -> output );
506
570
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
+ }
509
594
510
595
output_context -> out .data = data ;
511
596
output_context -> out .used = size ;
512
597
output_context -> out .free = 1 ;
513
598
514
599
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 );
520
600
}
521
601
} else {
522
602
php_brotli_context_close (ctx );
0 commit comments