@@ -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 */
@@ -2441,7 +2445,7 @@ ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
24412445
24422446ssize_t read_socket (socket_t sock, void *ptr, size_t size, int flags);
24432447
2444- enum class EncodingType { None = 0 , Gzip, Brotli };
2448+ enum class EncodingType { None = 0 , Gzip, Brotli, Zstd };
24452449
24462450EncodingType encoding_type (const Request &req, const Response &res);
24472451
@@ -2554,6 +2558,34 @@ class brotli_decompressor final : public decompressor {
25542558};
25552559#endif
25562560
2561+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
2562+ class zstd_compressor : public compressor {
2563+ public:
2564+ zstd_compressor ();
2565+ ~zstd_compressor ();
2566+
2567+ bool compress (const char *data, size_t data_length, bool last,
2568+ Callback callback) override ;
2569+
2570+ private:
2571+ ZSTD_CCtx *ctx_ = nullptr ;
2572+ };
2573+
2574+ class zstd_decompressor : public decompressor {
2575+ public:
2576+ zstd_decompressor ();
2577+ ~zstd_decompressor ();
2578+
2579+ bool is_valid () const override ;
2580+
2581+ bool decompress (const char *data, size_t data_length,
2582+ Callback callback) override ;
2583+
2584+ private:
2585+ ZSTD_DCtx *ctx_ = nullptr ;
2586+ };
2587+ #endif
2588+
25572589// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
25582590// to store data. The call can set memory on stack for performance.
25592591class stream_line_reader {
@@ -3936,6 +3968,12 @@ inline EncodingType encoding_type(const Request &req, const Response &res) {
39363968 if (ret) { return EncodingType::Gzip; }
39373969#endif
39383970
3971+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
3972+ // TODO: 'Accept-Encoding' has zstd, not zstd;q=0
3973+ ret = s.find (" zstd" ) != std::string::npos;
3974+ if (ret) { return EncodingType::Zstd; }
3975+ #endif
3976+
39393977 return EncodingType::None;
39403978}
39413979
@@ -4144,6 +4182,75 @@ inline bool brotli_decompressor::decompress(const char *data,
41444182}
41454183#endif
41464184
4185+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
4186+ inline zstd_compressor::zstd_compressor () {
4187+ ctx_ = ZSTD_createCCtx ();
4188+ ZSTD_CCtx_setParameter (ctx_, ZSTD_c_compressionLevel, ZSTD_fast);
4189+ }
4190+
4191+ inline zstd_compressor::~zstd_compressor () {
4192+ ZSTD_freeCCtx (ctx_);
4193+ }
4194+
4195+ inline bool zstd_compressor::compress (const char *data, size_t data_length,
4196+ bool last, Callback callback) {
4197+ std::array<char , CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4198+
4199+ ZSTD_EndDirective mode = last ? ZSTD_e_end : ZSTD_e_continue;
4200+ ZSTD_inBuffer input = { data, data_length, 0 };
4201+
4202+ bool finished;
4203+ do {
4204+ ZSTD_outBuffer output = { buff.data (), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0 };
4205+ size_t const remaining = ZSTD_compressStream2 (ctx_, &output, &input, mode);
4206+
4207+ if (ZSTD_isError (remaining)) {
4208+ return false ;
4209+ }
4210+
4211+ if (!callback (buff.data (), output.pos )) {
4212+ return false ;
4213+ }
4214+
4215+ finished = last ? (remaining == 0 ) : (input.pos == input.size );
4216+
4217+ } while (!finished);
4218+
4219+ return true ;
4220+ }
4221+
4222+ inline zstd_decompressor::zstd_decompressor () {
4223+ ctx_ = ZSTD_createDCtx ();
4224+ }
4225+
4226+ inline zstd_decompressor::~zstd_decompressor () {
4227+ ZSTD_freeDCtx (ctx_);
4228+ }
4229+
4230+ inline bool zstd_decompressor::is_valid () const { return ctx_ != nullptr ; }
4231+
4232+ inline bool zstd_decompressor::decompress (const char *data, size_t data_length,
4233+ Callback callback) {
4234+ std::array<char , CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4235+ ZSTD_inBuffer input = { data, data_length, 0 };
4236+
4237+ while (input.pos < input.size ) {
4238+ ZSTD_outBuffer output = { buff.data (), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0 };
4239+ size_t const remaining = ZSTD_decompressStream (ctx_, &output , &input);
4240+
4241+ if (ZSTD_isError (remaining)) {
4242+ return false ;
4243+ }
4244+
4245+ if (!callback (buff.data (), output.pos )) {
4246+ return false ;
4247+ }
4248+ }
4249+
4250+ return true ;
4251+ }
4252+ #endif
4253+
41474254inline bool has_header (const Headers &headers, const std::string &key) {
41484255 return headers.find (key) != headers.end ();
41494256}
@@ -4381,6 +4488,13 @@ bool prepare_content_receiver(T &x, int &status,
43814488#else
43824489 status = StatusCode::UnsupportedMediaType_415;
43834490 return false ;
4491+ #endif
4492+ } else if (encoding == " zstd" ) {
4493+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
4494+ decompressor = detail::make_unique<zstd_decompressor>();
4495+ #else
4496+ status = StatusCode::UnsupportedMediaType_415;
4497+ return false ;
43844498#endif
43854499 }
43864500
@@ -6621,6 +6735,10 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
66216735 } else if (type == detail::EncodingType::Brotli) {
66226736#ifdef CPPHTTPLIB_BROTLI_SUPPORT
66236737 compressor = detail::make_unique<detail::brotli_compressor>();
6738+ #endif
6739+ } else if (type == detail::EncodingType::Zstd) {
6740+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
6741+ compressor = detail::make_unique<detail::zstd_compressor>();
66246742#endif
66256743 } else {
66266744 compressor = detail::make_unique<detail::nocompressor>();
@@ -7036,6 +7154,8 @@ inline void Server::apply_ranges(const Request &req, Response &res,
70367154 res.set_header (" Content-Encoding" , " gzip" );
70377155 } else if (type == detail::EncodingType::Brotli) {
70387156 res.set_header (" Content-Encoding" , " br" );
7157+ } else if (type == detail::EncodingType::Zstd) {
7158+ res.set_header (" Content-Encoding" , " zstd" );
70397159 }
70407160 }
70417161 }
@@ -7075,6 +7195,11 @@ inline void Server::apply_ranges(const Request &req, Response &res,
70757195#ifdef CPPHTTPLIB_BROTLI_SUPPORT
70767196 compressor = detail::make_unique<detail::brotli_compressor>();
70777197 content_encoding = " br" ;
7198+ #endif
7199+ } else if (type == detail::EncodingType::Zstd) {
7200+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
7201+ compressor = detail::make_unique<detail::zstd_compressor>();
7202+ content_encoding = " zstd" ;
70787203#endif
70797204 }
70807205
@@ -7799,6 +7924,10 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
77997924#ifdef CPPHTTPLIB_ZLIB_SUPPORT
78007925 if (!accept_encoding.empty ()) { accept_encoding += " , " ; }
78017926 accept_encoding += " gzip, deflate" ;
7927+ #endif
7928+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
7929+ if (!accept_encoding.empty ()) { accept_encoding += " , " ; }
7930+ accept_encoding += " zstd" ;
78027931#endif
78037932 req.set_header (" Accept-Encoding" , accept_encoding);
78047933 }
0 commit comments