@@ -225,6 +225,7 @@ inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) {
225
225
226
226
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
227
227
#include < brotli/decode.h>
228
+ #include < brotli/encode.h>
228
229
#endif
229
230
230
231
/*
@@ -2157,8 +2158,39 @@ inline EncodingType encoding_type(const Request &req, const Response &res) {
2157
2158
return EncodingType::None;
2158
2159
}
2159
2160
2161
+ class compressor {
2162
+ public:
2163
+ virtual ~compressor (){};
2164
+
2165
+ typedef std::function<bool (const char *data, size_t data_len)> Callback;
2166
+ virtual bool compress (const char *data, size_t data_length, bool last,
2167
+ Callback callback) = 0;
2168
+ };
2169
+
2170
+ class decompressor {
2171
+ public:
2172
+ virtual ~decompressor () {}
2173
+
2174
+ virtual bool is_valid () const = 0;
2175
+
2176
+ typedef std::function<bool (const char *data, size_t data_len)> Callback;
2177
+ virtual bool decompress (const char *data, size_t data_length,
2178
+ Callback callback) = 0;
2179
+ };
2180
+
2181
+ class nocompressor : public compressor {
2182
+ public:
2183
+ ~nocompressor (){};
2184
+
2185
+ bool compress (const char *data, size_t data_length, bool /* last*/ ,
2186
+ Callback callback) override {
2187
+ if (!data_length) { return true ; }
2188
+ return callback (data, data_length);
2189
+ }
2190
+ };
2191
+
2160
2192
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2161
- class gzip_compressor {
2193
+ class gzip_compressor : public compressor {
2162
2194
public:
2163
2195
gzip_compressor () {
2164
2196
std::memset (&strm_, 0 , sizeof (strm_));
@@ -2172,8 +2204,8 @@ class gzip_compressor {
2172
2204
2173
2205
~gzip_compressor () { deflateEnd (&strm_); }
2174
2206
2175
- template < typename T>
2176
- bool compress ( const char *data, size_t data_length, bool last, T callback) {
2207
+ bool compress ( const char *data, size_t data_length, bool last,
2208
+ Callback callback) override {
2177
2209
assert (is_valid_);
2178
2210
2179
2211
auto flush = last ? Z_FINISH : Z_NO_FLUSH;
@@ -2206,7 +2238,7 @@ class gzip_compressor {
2206
2238
z_stream strm_;
2207
2239
};
2208
2240
2209
- class gzip_decompressor {
2241
+ class gzip_decompressor : public decompressor {
2210
2242
public:
2211
2243
gzip_decompressor () {
2212
2244
std::memset (&strm_, 0 , sizeof (strm_));
@@ -2223,10 +2255,10 @@ class gzip_decompressor {
2223
2255
2224
2256
~gzip_decompressor () { inflateEnd (&strm_); }
2225
2257
2226
- bool is_valid () const { return is_valid_; }
2258
+ bool is_valid () const override { return is_valid_; }
2227
2259
2228
- template < typename T>
2229
- bool decompress ( const char *data, size_t data_length, T callback) {
2260
+ bool decompress ( const char *data, size_t data_length,
2261
+ Callback callback) override {
2230
2262
assert (is_valid_);
2231
2263
2232
2264
int ret = Z_OK;
@@ -2262,7 +2294,52 @@ class gzip_decompressor {
2262
2294
#endif
2263
2295
2264
2296
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2265
- class brotli_decompressor {
2297
+ class brotli_compressor : public compressor {
2298
+ public:
2299
+ brotli_compressor () {
2300
+ state_ = BrotliEncoderCreateInstance (nullptr , nullptr , nullptr );
2301
+ }
2302
+
2303
+ ~brotli_compressor () { BrotliEncoderDestroyInstance (state_); }
2304
+
2305
+ bool compress (const char *data, size_t data_length, bool last,
2306
+ Callback callback) override {
2307
+ std::array<uint8_t , 16384 > buff{};
2308
+
2309
+ auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
2310
+ auto available_in = data_length;
2311
+ auto next_in = reinterpret_cast <const uint8_t *>(data);
2312
+
2313
+ for (;;) {
2314
+ if (last) {
2315
+ if (BrotliEncoderIsFinished (state_)) { break ; }
2316
+ } else {
2317
+ if (!available_in) { break ; }
2318
+ }
2319
+
2320
+ auto available_out = buff.size ();
2321
+ auto next_out = buff.data ();
2322
+
2323
+ if (!BrotliEncoderCompressStream (state_, operation, &available_in,
2324
+ &next_in, &available_out, &next_out,
2325
+ nullptr )) {
2326
+ return false ;
2327
+ }
2328
+
2329
+ auto output_bytes = buff.size () - available_out;
2330
+ if (output_bytes) {
2331
+ callback (reinterpret_cast <const char *>(buff.data ()), output_bytes);
2332
+ }
2333
+ }
2334
+
2335
+ return true ;
2336
+ }
2337
+
2338
+ private:
2339
+ BrotliEncoderState *state_ = nullptr ;
2340
+ };
2341
+
2342
+ class brotli_decompressor : public decompressor {
2266
2343
public:
2267
2344
brotli_decompressor () {
2268
2345
decoder_s = BrotliDecoderCreateInstance (0 , 0 , 0 );
@@ -2274,13 +2351,14 @@ class brotli_decompressor {
2274
2351
if (decoder_s) { BrotliDecoderDestroyInstance (decoder_s); }
2275
2352
}
2276
2353
2277
- bool is_valid () const { return decoder_s; }
2354
+ bool is_valid () const override { return decoder_s; }
2278
2355
2279
- template < typename T>
2280
- bool decompress ( const char *data, size_t data_length, T callback) {
2356
+ bool decompress ( const char *data, size_t data_length,
2357
+ Callback callback) override {
2281
2358
if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
2282
- decoder_r == BROTLI_DECODER_RESULT_ERROR)
2359
+ decoder_r == BROTLI_DECODER_RESULT_ERROR) {
2283
2360
return 0 ;
2361
+ }
2284
2362
2285
2363
const uint8_t *next_in = (const uint8_t *)data;
2286
2364
size_t avail_in = data_length;
@@ -2491,32 +2569,29 @@ bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
2491
2569
bool decompress, U callback) {
2492
2570
if (decompress) {
2493
2571
std::string encoding = x.get_header_value (" Content-Encoding" );
2572
+ std::shared_ptr<decompressor> decompressor;
2494
2573
2495
2574
if (encoding.find (" gzip" ) != std::string::npos ||
2496
2575
encoding.find (" deflate" ) != std::string::npos) {
2497
2576
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2498
- gzip_decompressor decompressor;
2499
- if (decompressor.is_valid ()) {
2500
- ContentReceiver out = [&](const char *buf, size_t n) {
2501
- return decompressor.decompress (
2502
- buf, n,
2503
- [&](const char *buf, size_t n) { return receiver (buf, n); });
2504
- };
2505
- return callback (out);
2506
- } else {
2507
- status = 500 ;
2508
- return false ;
2509
- }
2577
+ decompressor = std::make_shared<gzip_decompressor>();
2510
2578
#else
2511
2579
status = 415 ;
2512
2580
return false ;
2513
2581
#endif
2514
2582
} else if (encoding.find (" br" ) != std::string::npos) {
2515
2583
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2516
- brotli_decompressor decompressor;
2517
- if (decompressor.is_valid ()) {
2584
+ decompressor = std::make_shared<brotli_decompressor>();
2585
+ #else
2586
+ status = 415 ;
2587
+ return false ;
2588
+ #endif
2589
+ }
2590
+
2591
+ if (decompressor) {
2592
+ if (decompressor->is_valid ()) {
2518
2593
ContentReceiver out = [&](const char *buf, size_t n) {
2519
- return decompressor. decompress (
2594
+ return decompressor-> decompress (
2520
2595
buf, n,
2521
2596
[&](const char *buf, size_t n) { return receiver (buf, n); });
2522
2597
};
@@ -2525,17 +2600,12 @@ bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
2525
2600
status = 500 ;
2526
2601
return false ;
2527
2602
}
2528
- #else
2529
- status = 415 ;
2530
- return false ;
2531
- #endif
2532
2603
}
2533
2604
}
2534
2605
2535
2606
ContentReceiver out = [&](const char *buf, size_t n) {
2536
2607
return receiver (buf, n);
2537
2608
};
2538
-
2539
2609
return callback (out);
2540
2610
}
2541
2611
@@ -2628,44 +2698,31 @@ inline ssize_t write_content(Stream &strm, ContentProvider content_provider,
2628
2698
return static_cast <ssize_t >(offset - begin_offset);
2629
2699
}
2630
2700
2631
- template <typename T>
2701
+ template <typename T, typename U >
2632
2702
inline ssize_t write_content_chunked (Stream &strm,
2633
2703
ContentProvider content_provider,
2634
- T is_shutting_down, EncodingType type ) {
2704
+ T is_shutting_down, U &compressor ) {
2635
2705
size_t offset = 0 ;
2636
2706
auto data_available = true ;
2637
2707
ssize_t total_written_length = 0 ;
2638
2708
2639
2709
auto ok = true ;
2640
2710
DataSink data_sink;
2641
2711
2642
- #ifdef CPPHTTPLIB_ZLIB_SUPPORT
2643
- detail::gzip_compressor compressor;
2644
- #endif
2645
-
2646
2712
data_sink.write = [&](const char *d, size_t l) {
2647
2713
if (!ok) { return ; }
2648
2714
2649
2715
data_available = l > 0 ;
2650
2716
offset += l;
2651
2717
2652
2718
std::string payload;
2653
- if (type == EncodingType::Gzip) {
2654
- #ifdef CPPHTTPLIB_ZLIB_SUPPORT
2655
- if (!compressor.compress (d, l, false ,
2656
- [&](const char *data, size_t data_len) {
2657
- payload.append (data, data_len);
2658
- return true ;
2659
- })) {
2660
- ok = false ;
2661
- return ;
2662
- }
2663
- #endif
2664
- } else if (type == EncodingType::Brotli) {
2665
- #ifdef CPPHTTPLIB_BROTLI_SUPPORT
2666
- #endif
2667
- } else {
2668
- payload = std::string (d, l);
2719
+ if (!compressor.compress (d, l, false ,
2720
+ [&](const char *data, size_t data_len) {
2721
+ payload.append (data, data_len);
2722
+ return true ;
2723
+ })) {
2724
+ ok = false ;
2725
+ return ;
2669
2726
}
2670
2727
2671
2728
if (!payload.empty ()) {
@@ -2685,32 +2742,25 @@ inline ssize_t write_content_chunked(Stream &strm,
2685
2742
2686
2743
data_available = false ;
2687
2744
2688
- if (type == EncodingType::Gzip) {
2689
- #ifdef CPPHTTPLIB_ZLIB_SUPPORT
2690
- std::string payload;
2691
- if (!compressor.compress (nullptr , 0 , true ,
2692
- [&](const char *data, size_t data_len) {
2693
- payload.append (data, data_len);
2694
- return true ;
2695
- })) {
2745
+ std::string payload;
2746
+ if (!compressor.compress (nullptr , 0 , true ,
2747
+ [&](const char *data, size_t data_len) {
2748
+ payload.append (data, data_len);
2749
+ return true ;
2750
+ })) {
2751
+ ok = false ;
2752
+ return ;
2753
+ }
2754
+
2755
+ if (!payload.empty ()) {
2756
+ // Emit chunked response header and footer for each chunk
2757
+ auto chunk = from_i_to_hex (payload.size ()) + " \r\n " + payload + " \r\n " ;
2758
+ if (write_data (strm, chunk.data (), chunk.size ())) {
2759
+ total_written_length += chunk.size ();
2760
+ } else {
2696
2761
ok = false ;
2697
2762
return ;
2698
2763
}
2699
-
2700
- if (!payload.empty ()) {
2701
- // Emit chunked response header and footer for each chunk
2702
- auto chunk = from_i_to_hex (payload.size ()) + " \r\n " + payload + " \r\n " ;
2703
- if (write_data (strm, chunk.data (), chunk.size ())) {
2704
- total_written_length += chunk.size ();
2705
- } else {
2706
- ok = false ;
2707
- return ;
2708
- }
2709
- }
2710
- #endif
2711
- } else if (type == EncodingType::Brotli) {
2712
- #ifdef CPPHTTPLIB_BROTLI_SUPPORT
2713
- #endif
2714
2764
}
2715
2765
2716
2766
static const std::string done_marker (" 0\r\n\r\n " );
@@ -3918,25 +3968,33 @@ inline bool Server::write_response(Stream &strm, bool close_connection,
3918
3968
}
3919
3969
3920
3970
if (type != detail::EncodingType::None) {
3921
- #ifdef CPPHTTPLIB_ZLIB_SUPPORT
3922
- std::string compressed;
3971
+ std::shared_ptr<detail::compressor> compressor;
3923
3972
3924
3973
if (type == detail::EncodingType::Gzip) {
3925
- detail::gzip_compressor compressor;
3926
- if (!compressor.compress (res.body .data (), res.body .size (), true ,
3927
- [&](const char *data, size_t data_len) {
3928
- compressed.append (data, data_len);
3929
- return true ;
3930
- })) {
3931
- return false ;
3932
- }
3974
+ #ifdef CPPHTTPLIB_ZLIB_SUPPORT
3975
+ compressor = std::make_shared<detail::gzip_compressor>();
3933
3976
res.set_header (" Content-Encoding" , " gzip" );
3977
+ #endif
3934
3978
} else if (type == detail::EncodingType::Brotli) {
3935
- // TODO:
3979
+ #ifdef CPPHTTPLIB_BROTLI_SUPPORT
3980
+ compressor = std::make_shared<detail::brotli_compressor>();
3981
+ res.set_header (" Content-Encoding" , " brotli" );
3982
+ #endif
3936
3983
}
3937
3984
3938
- res.body .swap (compressed);
3939
- #endif
3985
+ if (compressor) {
3986
+ std::string compressed;
3987
+
3988
+ if (!compressor->compress (res.body .data (), res.body .size (), true ,
3989
+ [&](const char *data, size_t data_len) {
3990
+ compressed.append (data, data_len);
3991
+ return true ;
3992
+ })) {
3993
+ return false ;
3994
+ }
3995
+
3996
+ res.body .swap (compressed);
3997
+ }
3940
3998
}
3941
3999
3942
4000
auto length = std::to_string (res.body .size ());
@@ -3999,8 +4057,23 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
3999
4057
}
4000
4058
} else {
4001
4059
auto type = detail::encoding_type (req, res);
4060
+
4061
+ std::shared_ptr<detail::compressor> compressor;
4062
+ if (type == detail::EncodingType::Gzip) {
4063
+ #ifdef CPPHTTPLIB_ZLIB_SUPPORT
4064
+ compressor = std::make_shared<detail::gzip_compressor>();
4065
+ #endif
4066
+ } else if (type == detail::EncodingType::Brotli) {
4067
+ #ifdef CPPHTTPLIB_BROTLI_SUPPORT
4068
+ compressor = std::make_shared<detail::brotli_compressor>();
4069
+ #endif
4070
+ } else {
4071
+ compressor = std::make_shared<detail::nocompressor>();
4072
+ }
4073
+ assert (compressor != nullptr );
4074
+
4002
4075
if (detail::write_content_chunked (strm, res.content_provider_ ,
4003
- is_shutting_down, type ) < 0 ) {
4076
+ is_shutting_down, *compressor ) < 0 ) {
4004
4077
return false ;
4005
4078
}
4006
4079
}
0 commit comments