@@ -312,6 +312,10 @@ using socket_t = int;
312312#include < brotli/encode.h>
313313#endif
314314
315+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
316+ #include < zstd.h>
317+ #endif
318+
315319/*
316320 * Declaration
317321 */
@@ -2445,7 +2449,7 @@ ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
24452449
24462450ssize_t read_socket (socket_t sock, void *ptr, size_t size, int flags);
24472451
2448- enum class EncodingType { None = 0 , Gzip, Brotli };
2452+ enum class EncodingType { None = 0 , Gzip, Brotli, Zstd };
24492453
24502454EncodingType encoding_type (const Request &req, const Response &res);
24512455
@@ -2558,6 +2562,34 @@ class brotli_decompressor final : public decompressor {
25582562};
25592563#endif
25602564
2565+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
2566+ class zstd_compressor : public compressor {
2567+ public:
2568+ zstd_compressor ();
2569+ ~zstd_compressor ();
2570+
2571+ bool compress (const char *data, size_t data_length, bool last,
2572+ Callback callback) override ;
2573+
2574+ private:
2575+ ZSTD_CCtx *ctx_ = nullptr ;
2576+ };
2577+
2578+ class zstd_decompressor : public decompressor {
2579+ public:
2580+ zstd_decompressor ();
2581+ ~zstd_decompressor ();
2582+
2583+ bool is_valid () const override ;
2584+
2585+ bool decompress (const char *data, size_t data_length,
2586+ Callback callback) override ;
2587+
2588+ private:
2589+ ZSTD_DCtx *ctx_ = nullptr ;
2590+ };
2591+ #endif
2592+
25612593// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
25622594// to store data. The call can set memory on stack for performance.
25632595class stream_line_reader {
@@ -3949,6 +3981,12 @@ inline EncodingType encoding_type(const Request &req, const Response &res) {
39493981 if (ret) { return EncodingType::Gzip; }
39503982#endif
39513983
3984+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
3985+ // TODO: 'Accept-Encoding' has zstd, not zstd;q=0
3986+ ret = s.find (" zstd" ) != std::string::npos;
3987+ if (ret) { return EncodingType::Zstd; }
3988+ #endif
3989+
39523990 return EncodingType::None;
39533991}
39543992
@@ -4157,6 +4195,75 @@ inline bool brotli_decompressor::decompress(const char *data,
41574195}
41584196#endif
41594197
4198+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
4199+ inline zstd_compressor::zstd_compressor () {
4200+ ctx_ = ZSTD_createCCtx ();
4201+ ZSTD_CCtx_setParameter (ctx_, ZSTD_c_compressionLevel, ZSTD_fast);
4202+ }
4203+
4204+ inline zstd_compressor::~zstd_compressor () {
4205+ ZSTD_freeCCtx (ctx_);
4206+ }
4207+
4208+ inline bool zstd_compressor::compress (const char *data, size_t data_length,
4209+ bool last, Callback callback) {
4210+ std::array<char , CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4211+
4212+ ZSTD_EndDirective mode = last ? ZSTD_e_end : ZSTD_e_continue;
4213+ ZSTD_inBuffer input = { data, data_length, 0 };
4214+
4215+ bool finished;
4216+ do {
4217+ ZSTD_outBuffer output = { buff.data (), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0 };
4218+ size_t const remaining = ZSTD_compressStream2 (ctx_, &output, &input, mode);
4219+
4220+ if (ZSTD_isError (remaining)) {
4221+ return false ;
4222+ }
4223+
4224+ if (!callback (buff.data (), output.pos )) {
4225+ return false ;
4226+ }
4227+
4228+ finished = last ? (remaining == 0 ) : (input.pos == input.size );
4229+
4230+ } while (!finished);
4231+
4232+ return true ;
4233+ }
4234+
4235+ inline zstd_decompressor::zstd_decompressor () {
4236+ ctx_ = ZSTD_createDCtx ();
4237+ }
4238+
4239+ inline zstd_decompressor::~zstd_decompressor () {
4240+ ZSTD_freeDCtx (ctx_);
4241+ }
4242+
4243+ inline bool zstd_decompressor::is_valid () const { return ctx_ != nullptr ; }
4244+
4245+ inline bool zstd_decompressor::decompress (const char *data, size_t data_length,
4246+ Callback callback) {
4247+ std::array<char , CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4248+ ZSTD_inBuffer input = { data, data_length, 0 };
4249+
4250+ while (input.pos < input.size ) {
4251+ ZSTD_outBuffer output = { buff.data (), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0 };
4252+ size_t const remaining = ZSTD_decompressStream (ctx_, &output , &input);
4253+
4254+ if (ZSTD_isError (remaining)) {
4255+ return false ;
4256+ }
4257+
4258+ if (!callback (buff.data (), output.pos )) {
4259+ return false ;
4260+ }
4261+ }
4262+
4263+ return true ;
4264+ }
4265+ #endif
4266+
41604267inline bool has_header (const Headers &headers, const std::string &key) {
41614268 return headers.find (key) != headers.end ();
41624269}
@@ -4397,6 +4504,13 @@ bool prepare_content_receiver(T &x, int &status,
43974504#else
43984505 status = StatusCode::UnsupportedMediaType_415;
43994506 return false ;
4507+ #endif
4508+ } else if (encoding == " zstd" ) {
4509+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
4510+ decompressor = detail::make_unique<zstd_decompressor>();
4511+ #else
4512+ status = StatusCode::UnsupportedMediaType_415;
4513+ return false ;
44004514#endif
44014515 }
44024516
@@ -6634,6 +6748,10 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
66346748 } else if (type == detail::EncodingType::Brotli) {
66356749#ifdef CPPHTTPLIB_BROTLI_SUPPORT
66366750 compressor = detail::make_unique<detail::brotli_compressor>();
6751+ #endif
6752+ } else if (type == detail::EncodingType::Zstd) {
6753+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
6754+ compressor = detail::make_unique<detail::zstd_compressor>();
66376755#endif
66386756 } else {
66396757 compressor = detail::make_unique<detail::nocompressor>();
@@ -7049,6 +7167,8 @@ inline void Server::apply_ranges(const Request &req, Response &res,
70497167 res.set_header (" Content-Encoding" , " gzip" );
70507168 } else if (type == detail::EncodingType::Brotli) {
70517169 res.set_header (" Content-Encoding" , " br" );
7170+ } else if (type == detail::EncodingType::Zstd) {
7171+ res.set_header (" Content-Encoding" , " zstd" );
70527172 }
70537173 }
70547174 }
@@ -7088,6 +7208,11 @@ inline void Server::apply_ranges(const Request &req, Response &res,
70887208#ifdef CPPHTTPLIB_BROTLI_SUPPORT
70897209 compressor = detail::make_unique<detail::brotli_compressor>();
70907210 content_encoding = " br" ;
7211+ #endif
7212+ } else if (type == detail::EncodingType::Zstd) {
7213+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
7214+ compressor = detail::make_unique<detail::zstd_compressor>();
7215+ content_encoding = " zstd" ;
70917216#endif
70927217 }
70937218
@@ -7812,6 +7937,10 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
78127937#ifdef CPPHTTPLIB_ZLIB_SUPPORT
78137938 if (!accept_encoding.empty ()) { accept_encoding += " , " ; }
78147939 accept_encoding += " gzip, deflate" ;
7940+ #endif
7941+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
7942+ if (!accept_encoding.empty ()) { accept_encoding += " , " ; }
7943+ accept_encoding += " zstd" ;
78157944#endif
78167945 req.set_header (" Accept-Encoding" , accept_encoding);
78177946 }
0 commit comments