@@ -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