@@ -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,61 @@ 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 () { ZSTD_freeCCtx (ctx_); }
4205+
4206+ inline bool zstd_compressor::compress (const char *data, size_t data_length,
4207+ bool last, Callback callback) {
4208+ std::array<char , CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4209+
4210+ ZSTD_EndDirective mode = last ? ZSTD_e_end : ZSTD_e_continue;
4211+ ZSTD_inBuffer input = {data, data_length, 0 };
4212+
4213+ bool finished;
4214+ do {
4215+ ZSTD_outBuffer output = {buff.data (), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0 };
4216+ size_t const remaining = ZSTD_compressStream2 (ctx_, &output, &input, mode);
4217+
4218+ if (ZSTD_isError (remaining)) { return false ; }
4219+
4220+ if (!callback (buff.data (), output.pos )) { return false ; }
4221+
4222+ finished = last ? (remaining == 0 ) : (input.pos == input.size );
4223+
4224+ } while (!finished);
4225+
4226+ return true ;
4227+ }
4228+
4229+ inline zstd_decompressor::zstd_decompressor () { ctx_ = ZSTD_createDCtx (); }
4230+
4231+ inline zstd_decompressor::~zstd_decompressor () { ZSTD_freeDCtx (ctx_); }
4232+
4233+ inline bool zstd_decompressor::is_valid () const { return ctx_ != nullptr ; }
4234+
4235+ inline bool zstd_decompressor::decompress (const char *data, size_t data_length,
4236+ Callback callback) {
4237+ std::array<char , CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4238+ ZSTD_inBuffer input = {data, data_length, 0 };
4239+
4240+ while (input.pos < input.size ) {
4241+ ZSTD_outBuffer output = {buff.data (), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0 };
4242+ size_t const remaining = ZSTD_decompressStream (ctx_, &output, &input);
4243+
4244+ if (ZSTD_isError (remaining)) { return false ; }
4245+
4246+ if (!callback (buff.data (), output.pos )) { return false ; }
4247+ }
4248+
4249+ return true ;
4250+ }
4251+ #endif
4252+
41604253inline bool has_header (const Headers &headers, const std::string &key) {
41614254 return headers.find (key) != headers.end ();
41624255}
@@ -4397,6 +4490,13 @@ bool prepare_content_receiver(T &x, int &status,
43974490#else
43984491 status = StatusCode::UnsupportedMediaType_415;
43994492 return false ;
4493+ #endif
4494+ } else if (encoding == " zstd" ) {
4495+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
4496+ decompressor = detail::make_unique<zstd_decompressor>();
4497+ #else
4498+ status = StatusCode::UnsupportedMediaType_415;
4499+ return false ;
44004500#endif
44014501 }
44024502
@@ -6634,6 +6734,10 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
66346734 } else if (type == detail::EncodingType::Brotli) {
66356735#ifdef CPPHTTPLIB_BROTLI_SUPPORT
66366736 compressor = detail::make_unique<detail::brotli_compressor>();
6737+ #endif
6738+ } else if (type == detail::EncodingType::Zstd) {
6739+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
6740+ compressor = detail::make_unique<detail::zstd_compressor>();
66376741#endif
66386742 } else {
66396743 compressor = detail::make_unique<detail::nocompressor>();
@@ -7049,6 +7153,8 @@ inline void Server::apply_ranges(const Request &req, Response &res,
70497153 res.set_header (" Content-Encoding" , " gzip" );
70507154 } else if (type == detail::EncodingType::Brotli) {
70517155 res.set_header (" Content-Encoding" , " br" );
7156+ } else if (type == detail::EncodingType::Zstd) {
7157+ res.set_header (" Content-Encoding" , " zstd" );
70527158 }
70537159 }
70547160 }
@@ -7088,6 +7194,11 @@ inline void Server::apply_ranges(const Request &req, Response &res,
70887194#ifdef CPPHTTPLIB_BROTLI_SUPPORT
70897195 compressor = detail::make_unique<detail::brotli_compressor>();
70907196 content_encoding = " br" ;
7197+ #endif
7198+ } else if (type == detail::EncodingType::Zstd) {
7199+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
7200+ compressor = detail::make_unique<detail::zstd_compressor>();
7201+ content_encoding = " zstd" ;
70917202#endif
70927203 }
70937204
@@ -7812,6 +7923,10 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
78127923#ifdef CPPHTTPLIB_ZLIB_SUPPORT
78137924 if (!accept_encoding.empty ()) { accept_encoding += " , " ; }
78147925 accept_encoding += " gzip, deflate" ;
7926+ #endif
7927+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
7928+ if (!accept_encoding.empty ()) { accept_encoding += " , " ; }
7929+ accept_encoding += " zstd" ;
78157930#endif
78167931 req.set_header (" Accept-Encoding" , accept_encoding);
78177932 }
@@ -10377,4 +10492,4 @@ inline SSL_CTX *Client::ssl_context() const {
1037710492
1037810493} // namespace httplib
1037910494
10380- #endif // CPPHTTPLIB_HTTPLIB_H
10495+ #endif // CPPHTTPLIB_HTTPLIB_H
0 commit comments