@@ -312,6 +312,10 @@ using socket_t = int;
312
312
#include < brotli/encode.h>
313
313
#endif
314
314
315
+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
316
+ #include < zstd.h>
317
+ #endif
318
+
315
319
/*
316
320
* Declaration
317
321
*/
@@ -2441,7 +2445,7 @@ ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
2441
2445
2442
2446
ssize_t read_socket (socket_t sock, void *ptr, size_t size, int flags);
2443
2447
2444
- enum class EncodingType { None = 0 , Gzip, Brotli };
2448
+ enum class EncodingType { None = 0 , Gzip, Brotli, Zstd };
2445
2449
2446
2450
EncodingType encoding_type (const Request &req, const Response &res);
2447
2451
@@ -2554,6 +2558,34 @@ class brotli_decompressor final : public decompressor {
2554
2558
};
2555
2559
#endif
2556
2560
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
+
2557
2589
// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
2558
2590
// to store data. The call can set memory on stack for performance.
2559
2591
class stream_line_reader {
@@ -3936,6 +3968,12 @@ inline EncodingType encoding_type(const Request &req, const Response &res) {
3936
3968
if (ret) { return EncodingType::Gzip; }
3937
3969
#endif
3938
3970
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
+
3939
3977
return EncodingType::None;
3940
3978
}
3941
3979
@@ -4144,6 +4182,75 @@ inline bool brotli_decompressor::decompress(const char *data,
4144
4182
}
4145
4183
#endif
4146
4184
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
+
4147
4254
inline bool has_header (const Headers &headers, const std::string &key) {
4148
4255
return headers.find (key) != headers.end ();
4149
4256
}
@@ -4381,6 +4488,13 @@ bool prepare_content_receiver(T &x, int &status,
4381
4488
#else
4382
4489
status = StatusCode::UnsupportedMediaType_415;
4383
4490
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 ;
4384
4498
#endif
4385
4499
}
4386
4500
@@ -6621,6 +6735,10 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
6621
6735
} else if (type == detail::EncodingType::Brotli) {
6622
6736
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6623
6737
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>();
6624
6742
#endif
6625
6743
} else {
6626
6744
compressor = detail::make_unique<detail::nocompressor>();
@@ -7036,6 +7154,8 @@ inline void Server::apply_ranges(const Request &req, Response &res,
7036
7154
res.set_header (" Content-Encoding" , " gzip" );
7037
7155
} else if (type == detail::EncodingType::Brotli) {
7038
7156
res.set_header (" Content-Encoding" , " br" );
7157
+ } else if (type == detail::EncodingType::Zstd) {
7158
+ res.set_header (" Content-Encoding" , " zstd" );
7039
7159
}
7040
7160
}
7041
7161
}
@@ -7075,6 +7195,11 @@ inline void Server::apply_ranges(const Request &req, Response &res,
7075
7195
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
7076
7196
compressor = detail::make_unique<detail::brotli_compressor>();
7077
7197
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" ;
7078
7203
#endif
7079
7204
}
7080
7205
@@ -7799,6 +7924,10 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
7799
7924
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7800
7925
if (!accept_encoding.empty ()) { accept_encoding += " , " ; }
7801
7926
accept_encoding += " gzip, deflate" ;
7927
+ #endif
7928
+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
7929
+ if (!accept_encoding.empty ()) { accept_encoding += " , " ; }
7930
+ accept_encoding += " zstd" ;
7802
7931
#endif
7803
7932
req.set_header (" Accept-Encoding" , accept_encoding);
7804
7933
}
0 commit comments