@@ -313,6 +313,10 @@ using socket_t = int;
313
313
#include < brotli/encode.h>
314
314
#endif
315
315
316
+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
317
+ #include < zstd.h>
318
+ #endif
319
+
316
320
/*
317
321
* Declaration
318
322
*/
@@ -2444,7 +2448,7 @@ ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
2444
2448
2445
2449
ssize_t read_socket (socket_t sock, void *ptr, size_t size, int flags);
2446
2450
2447
- enum class EncodingType { None = 0 , Gzip, Brotli };
2451
+ enum class EncodingType { None = 0 , Gzip, Brotli, Zstd };
2448
2452
2449
2453
EncodingType encoding_type (const Request &req, const Response &res);
2450
2454
@@ -2557,6 +2561,34 @@ class brotli_decompressor final : public decompressor {
2557
2561
};
2558
2562
#endif
2559
2563
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
+
2560
2592
// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
2561
2593
// to store data. The call can set memory on stack for performance.
2562
2594
class stream_line_reader {
@@ -3948,6 +3980,12 @@ inline EncodingType encoding_type(const Request &req, const Response &res) {
3948
3980
if (ret) { return EncodingType::Gzip; }
3949
3981
#endif
3950
3982
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
+
3951
3989
return EncodingType::None;
3952
3990
}
3953
3991
@@ -4156,6 +4194,75 @@ inline bool brotli_decompressor::decompress(const char *data,
4156
4194
}
4157
4195
#endif
4158
4196
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
+
4159
4266
inline bool has_header (const Headers &headers, const std::string &key) {
4160
4267
return headers.find (key) != headers.end ();
4161
4268
}
@@ -4393,6 +4500,13 @@ bool prepare_content_receiver(T &x, int &status,
4393
4500
#else
4394
4501
status = StatusCode::UnsupportedMediaType_415;
4395
4502
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 ;
4396
4510
#endif
4397
4511
}
4398
4512
@@ -6633,6 +6747,10 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
6633
6747
} else if (type == detail::EncodingType::Brotli) {
6634
6748
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6635
6749
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>();
6636
6754
#endif
6637
6755
} else {
6638
6756
compressor = detail::make_unique<detail::nocompressor>();
@@ -7048,6 +7166,8 @@ inline void Server::apply_ranges(const Request &req, Response &res,
7048
7166
res.set_header (" Content-Encoding" , " gzip" );
7049
7167
} else if (type == detail::EncodingType::Brotli) {
7050
7168
res.set_header (" Content-Encoding" , " br" );
7169
+ } else if (type == detail::EncodingType::Zstd) {
7170
+ res.set_header (" Content-Encoding" , " zstd" );
7051
7171
}
7052
7172
}
7053
7173
}
@@ -7087,6 +7207,11 @@ inline void Server::apply_ranges(const Request &req, Response &res,
7087
7207
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
7088
7208
compressor = detail::make_unique<detail::brotli_compressor>();
7089
7209
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" ;
7090
7215
#endif
7091
7216
}
7092
7217
@@ -7811,6 +7936,10 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
7811
7936
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7812
7937
if (!accept_encoding.empty ()) { accept_encoding += " , " ; }
7813
7938
accept_encoding += " gzip, deflate" ;
7939
+ #endif
7940
+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
7941
+ if (!accept_encoding.empty ()) { accept_encoding += " , " ; }
7942
+ accept_encoding += " zstd" ;
7814
7943
#endif
7815
7944
req.set_header (" Accept-Encoding" , accept_encoding);
7816
7945
}
0 commit comments