@@ -225,6 +225,7 @@ inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) {
225225
226226#ifdef CPPHTTPLIB_BROTLI_SUPPORT
227227#include < brotli/decode.h>
228+ #include < brotli/encode.h>
228229#endif
229230
230231/*
@@ -2157,8 +2158,39 @@ inline EncodingType encoding_type(const Request &req, const Response &res) {
21572158 return EncodingType::None;
21582159}
21592160
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+
21602192#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2161- class gzip_compressor {
2193+ class gzip_compressor : public compressor {
21622194public:
21632195 gzip_compressor () {
21642196 std::memset (&strm_, 0 , sizeof (strm_));
@@ -2172,8 +2204,8 @@ class gzip_compressor {
21722204
21732205 ~gzip_compressor () { deflateEnd (&strm_); }
21742206
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 {
21772209 assert (is_valid_);
21782210
21792211 auto flush = last ? Z_FINISH : Z_NO_FLUSH;
@@ -2206,7 +2238,7 @@ class gzip_compressor {
22062238 z_stream strm_;
22072239};
22082240
2209- class gzip_decompressor {
2241+ class gzip_decompressor : public decompressor {
22102242public:
22112243 gzip_decompressor () {
22122244 std::memset (&strm_, 0 , sizeof (strm_));
@@ -2223,10 +2255,10 @@ class gzip_decompressor {
22232255
22242256 ~gzip_decompressor () { inflateEnd (&strm_); }
22252257
2226- bool is_valid () const { return is_valid_; }
2258+ bool is_valid () const override { return is_valid_; }
22272259
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 {
22302262 assert (is_valid_);
22312263
22322264 int ret = Z_OK;
@@ -2262,7 +2294,52 @@ class gzip_decompressor {
22622294#endif
22632295
22642296#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 {
22662343public:
22672344 brotli_decompressor () {
22682345 decoder_s = BrotliDecoderCreateInstance (0 , 0 , 0 );
@@ -2274,13 +2351,14 @@ class brotli_decompressor {
22742351 if (decoder_s) { BrotliDecoderDestroyInstance (decoder_s); }
22752352 }
22762353
2277- bool is_valid () const { return decoder_s; }
2354+ bool is_valid () const override { return decoder_s; }
22782355
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 {
22812358 if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
2282- decoder_r == BROTLI_DECODER_RESULT_ERROR)
2359+ decoder_r == BROTLI_DECODER_RESULT_ERROR) {
22832360 return 0 ;
2361+ }
22842362
22852363 const uint8_t *next_in = (const uint8_t *)data;
22862364 size_t avail_in = data_length;
@@ -2491,32 +2569,29 @@ bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
24912569 bool decompress, U callback) {
24922570 if (decompress) {
24932571 std::string encoding = x.get_header_value (" Content-Encoding" );
2572+ std::shared_ptr<decompressor> decompressor;
24942573
24952574 if (encoding.find (" gzip" ) != std::string::npos ||
24962575 encoding.find (" deflate" ) != std::string::npos) {
24972576#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>();
25102578#else
25112579 status = 415 ;
25122580 return false ;
25132581#endif
25142582 } else if (encoding.find (" br" ) != std::string::npos) {
25152583#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 ()) {
25182593 ContentReceiver out = [&](const char *buf, size_t n) {
2519- return decompressor. decompress (
2594+ return decompressor-> decompress (
25202595 buf, n,
25212596 [&](const char *buf, size_t n) { return receiver (buf, n); });
25222597 };
@@ -2525,17 +2600,12 @@ bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
25252600 status = 500 ;
25262601 return false ;
25272602 }
2528- #else
2529- status = 415 ;
2530- return false ;
2531- #endif
25322603 }
25332604 }
25342605
25352606 ContentReceiver out = [&](const char *buf, size_t n) {
25362607 return receiver (buf, n);
25372608 };
2538-
25392609 return callback (out);
25402610}
25412611
@@ -2628,44 +2698,31 @@ inline ssize_t write_content(Stream &strm, ContentProvider content_provider,
26282698 return static_cast <ssize_t >(offset - begin_offset);
26292699}
26302700
2631- template <typename T>
2701+ template <typename T, typename U >
26322702inline ssize_t write_content_chunked (Stream &strm,
26332703 ContentProvider content_provider,
2634- T is_shutting_down, EncodingType type ) {
2704+ T is_shutting_down, U &compressor ) {
26352705 size_t offset = 0 ;
26362706 auto data_available = true ;
26372707 ssize_t total_written_length = 0 ;
26382708
26392709 auto ok = true ;
26402710 DataSink data_sink;
26412711
2642- #ifdef CPPHTTPLIB_ZLIB_SUPPORT
2643- detail::gzip_compressor compressor;
2644- #endif
2645-
26462712 data_sink.write = [&](const char *d, size_t l) {
26472713 if (!ok) { return ; }
26482714
26492715 data_available = l > 0 ;
26502716 offset += l;
26512717
26522718 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 ;
26692726 }
26702727
26712728 if (!payload.empty ()) {
@@ -2685,32 +2742,25 @@ inline ssize_t write_content_chunked(Stream &strm,
26852742
26862743 data_available = false ;
26872744
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 {
26962761 ok = false ;
26972762 return ;
26982763 }
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
27142764 }
27152765
27162766 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,
39183968 }
39193969
39203970 if (type != detail::EncodingType::None) {
3921- #ifdef CPPHTTPLIB_ZLIB_SUPPORT
3922- std::string compressed;
3971+ std::shared_ptr<detail::compressor> compressor;
39233972
39243973 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>();
39333976 res.set_header (" Content-Encoding" , " gzip" );
3977+ #endif
39343978 } 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
39363983 }
39373984
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+ }
39403998 }
39413999
39424000 auto length = std::to_string (res.body .size ());
@@ -3999,8 +4057,23 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
39994057 }
40004058 } else {
40014059 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+
40024075 if (detail::write_content_chunked (strm, res.content_provider_ ,
4003- is_shutting_down, type ) < 0 ) {
4076+ is_shutting_down, *compressor ) < 0 ) {
40044077 return false ;
40054078 }
40064079 }
0 commit comments