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
112117static  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+ 
12561264static  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