@@ -313,6 +313,10 @@ using socket_t = int;
313313#include < brotli/encode.h>
314314#endif
315315
316+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
317+ #include < zstd.h>
318+ #endif
319+
316320/*
317321 * Declaration
318322 */
@@ -2444,7 +2448,7 @@ ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
24442448
24452449ssize_t read_socket (socket_t sock, void *ptr, size_t size, int flags);
24462450
2447- enum class EncodingType { None = 0 , Gzip, Brotli };
2451+ enum class EncodingType { None = 0 , Gzip, Brotli, Zstd };
24482452
24492453EncodingType encoding_type (const Request &req, const Response &res);
24502454
@@ -2557,6 +2561,34 @@ class brotli_decompressor final : public decompressor {
25572561};
25582562#endif
25592563
2564+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
2565+ class zstd_compressor : public compressor {
2566+ public:
2567+ zstd_compressor ();
2568+ ~zstd_compressor ();
2569+
2570+ bool compress (const char *data, size_t data_length, bool last,
2571+ Callback callback) override ;
2572+
2573+ private:
2574+ ZSTD_CCtx *ctx_ = nullptr ;
2575+ };
2576+
2577+ class zstd_decompressor : public decompressor {
2578+ public:
2579+ zstd_decompressor ();
2580+ ~zstd_decompressor ();
2581+
2582+ bool is_valid () const override ;
2583+
2584+ bool decompress (const char *data, size_t data_length,
2585+ Callback callback) override ;
2586+
2587+ private:
2588+ ZSTD_DCtx *ctx_ = nullptr ;
2589+ };
2590+ #endif
2591+
25602592// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
25612593// to store data. The call can set memory on stack for performance.
25622594class stream_line_reader {
@@ -3948,6 +3980,12 @@ inline EncodingType encoding_type(const Request &req, const Response &res) {
39483980 if (ret) { return EncodingType::Gzip; }
39493981#endif
39503982
3983+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
3984+ // TODO: 'Accept-Encoding' has zstd, not zstd;q=0
3985+ ret = s.find (" zstd" ) != std::string::npos;
3986+ if (ret) { return EncodingType::Zstd; }
3987+ #endif
3988+
39513989 return EncodingType::None;
39523990}
39533991
@@ -4156,6 +4194,75 @@ inline bool brotli_decompressor::decompress(const char *data,
41564194}
41574195#endif
41584196
4197+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
4198+ inline zstd_compressor::zstd_compressor () {
4199+ ctx_ = ZSTD_createCCtx ();
4200+ ZSTD_CCtx_setParameter (ctx_, ZSTD_c_compressionLevel, ZSTD_fast);
4201+ }
4202+
4203+ inline zstd_compressor::~zstd_compressor () {
4204+ ZSTD_freeCCtx (ctx_);
4205+ }
4206+
4207+ inline bool zstd_compressor::compress (const char *data, size_t data_length,
4208+ bool last, Callback callback) {
4209+ std::array<char , CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4210+
4211+ ZSTD_EndDirective mode = last ? ZSTD_e_end : ZSTD_e_continue;
4212+ ZSTD_inBuffer input = { data, data_length, 0 };
4213+
4214+ bool finished;
4215+ do {
4216+ ZSTD_outBuffer output = { buff.data (), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0 };
4217+ size_t const remaining = ZSTD_compressStream2 (ctx_, &output, &input, mode);
4218+
4219+ if (ZSTD_isError (remaining)) {
4220+ return false ;
4221+ }
4222+
4223+ if (!callback (buff.data (), output.pos )) {
4224+ return false ;
4225+ }
4226+
4227+ finished = last ? (remaining == 0 ) : (input.pos == input.size );
4228+
4229+ } while (!finished);
4230+
4231+ return true ;
4232+ }
4233+
4234+ inline zstd_decompressor::zstd_decompressor () {
4235+ ctx_ = ZSTD_createDCtx ();
4236+ }
4237+
4238+ inline zstd_decompressor::~zstd_decompressor () {
4239+ ZSTD_freeDCtx (ctx_);
4240+ }
4241+
4242+ inline bool zstd_decompressor::is_valid () const { return ctx_ != nullptr ; }
4243+
4244+ inline bool zstd_decompressor::decompress (const char *data, size_t data_length,
4245+ Callback callback) {
4246+ std::array<char , CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4247+ ZSTD_inBuffer input = { data, data_length, 0 };
4248+
4249+ while (input.pos < input.size ) {
4250+ ZSTD_outBuffer output = { buff.data (), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0 };
4251+ size_t const remaining = ZSTD_decompressStream (ctx_, &output , &input);
4252+
4253+ if (ZSTD_isError (remaining)) {
4254+ return false ;
4255+ }
4256+
4257+ if (!callback (buff.data (), output.pos )) {
4258+ return false ;
4259+ }
4260+ }
4261+
4262+ return true ;
4263+ }
4264+ #endif
4265+
41594266inline bool has_header (const Headers &headers, const std::string &key) {
41604267 return headers.find (key) != headers.end ();
41614268}
@@ -4393,6 +4500,13 @@ bool prepare_content_receiver(T &x, int &status,
43934500#else
43944501 status = StatusCode::UnsupportedMediaType_415;
43954502 return false ;
4503+ #endif
4504+ } else if (encoding == " zstd" ) {
4505+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
4506+ decompressor = detail::make_unique<zstd_decompressor>();
4507+ #else
4508+ status = StatusCode::UnsupportedMediaType_415;
4509+ return false ;
43964510#endif
43974511 }
43984512
@@ -6633,6 +6747,10 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
66336747 } else if (type == detail::EncodingType::Brotli) {
66346748#ifdef CPPHTTPLIB_BROTLI_SUPPORT
66356749 compressor = detail::make_unique<detail::brotli_compressor>();
6750+ #endif
6751+ } else if (type == detail::EncodingType::Zstd) {
6752+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
6753+ compressor = detail::make_unique<detail::zstd_compressor>();
66366754#endif
66376755 } else {
66386756 compressor = detail::make_unique<detail::nocompressor>();
@@ -7048,6 +7166,8 @@ inline void Server::apply_ranges(const Request &req, Response &res,
70487166 res.set_header (" Content-Encoding" , " gzip" );
70497167 } else if (type == detail::EncodingType::Brotli) {
70507168 res.set_header (" Content-Encoding" , " br" );
7169+ } else if (type == detail::EncodingType::Zstd) {
7170+ res.set_header (" Content-Encoding" , " zstd" );
70517171 }
70527172 }
70537173 }
@@ -7087,6 +7207,11 @@ inline void Server::apply_ranges(const Request &req, Response &res,
70877207#ifdef CPPHTTPLIB_BROTLI_SUPPORT
70887208 compressor = detail::make_unique<detail::brotli_compressor>();
70897209 content_encoding = " br" ;
7210+ #endif
7211+ } else if (type == detail::EncodingType::Zstd) {
7212+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
7213+ compressor = detail::make_unique<detail::zstd_compressor>();
7214+ content_encoding = " zstd" ;
70907215#endif
70917216 }
70927217
@@ -7811,6 +7936,10 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
78117936#ifdef CPPHTTPLIB_ZLIB_SUPPORT
78127937 if (!accept_encoding.empty ()) { accept_encoding += " , " ; }
78137938 accept_encoding += " gzip, deflate" ;
7939+ #endif
7940+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
7941+ if (!accept_encoding.empty ()) { accept_encoding += " , " ; }
7942+ accept_encoding += " zstd" ;
78147943#endif
78157944 req.set_header (" Accept-Encoding" , accept_encoding);
78167945 }
0 commit comments