mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2025-05-14 16:58:30 +00:00
Add zstd support (#2088)
* Add zstd support * Add zstd to CI tests * Use use zstd cmake target instead of ZSTD. Use cmake variable for found packages * Add missing comment for HTTPLIB_REQUIRE_ZSTD * Fix test.yaml rebase error * Use zstd::libzstd target * Add include and library paths to ZSTD args * Run clang-format * Add zstd to httplibConfig.cmake.in
This commit is contained in:
parent
0bda3a7d1a
commit
c765584e6b
6 changed files with 393 additions and 8 deletions
119
httplib.h
119
httplib.h
|
@ -312,6 +312,10 @@ using socket_t = int;
|
|||
#include <brotli/encode.h>
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
|
||||
#include <zstd.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Declaration
|
||||
*/
|
||||
|
@ -2445,7 +2449,7 @@ ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
|
|||
|
||||
ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags);
|
||||
|
||||
enum class EncodingType { None = 0, Gzip, Brotli };
|
||||
enum class EncodingType { None = 0, Gzip, Brotli, Zstd };
|
||||
|
||||
EncodingType encoding_type(const Request &req, const Response &res);
|
||||
|
||||
|
@ -2558,6 +2562,34 @@ private:
|
|||
};
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
|
||||
class zstd_compressor : public compressor {
|
||||
public:
|
||||
zstd_compressor();
|
||||
~zstd_compressor();
|
||||
|
||||
bool compress(const char *data, size_t data_length, bool last,
|
||||
Callback callback) override;
|
||||
|
||||
private:
|
||||
ZSTD_CCtx *ctx_ = nullptr;
|
||||
};
|
||||
|
||||
class zstd_decompressor : public decompressor {
|
||||
public:
|
||||
zstd_decompressor();
|
||||
~zstd_decompressor();
|
||||
|
||||
bool is_valid() const override;
|
||||
|
||||
bool decompress(const char *data, size_t data_length,
|
||||
Callback callback) override;
|
||||
|
||||
private:
|
||||
ZSTD_DCtx *ctx_ = nullptr;
|
||||
};
|
||||
#endif
|
||||
|
||||
// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
|
||||
// to store data. The call can set memory on stack for performance.
|
||||
class stream_line_reader {
|
||||
|
@ -3949,6 +3981,12 @@ inline EncodingType encoding_type(const Request &req, const Response &res) {
|
|||
if (ret) { return EncodingType::Gzip; }
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
|
||||
// TODO: 'Accept-Encoding' has zstd, not zstd;q=0
|
||||
ret = s.find("zstd") != std::string::npos;
|
||||
if (ret) { return EncodingType::Zstd; }
|
||||
#endif
|
||||
|
||||
return EncodingType::None;
|
||||
}
|
||||
|
||||
|
@ -4157,6 +4195,61 @@ inline bool brotli_decompressor::decompress(const char *data,
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
|
||||
inline zstd_compressor::zstd_compressor() {
|
||||
ctx_ = ZSTD_createCCtx();
|
||||
ZSTD_CCtx_setParameter(ctx_, ZSTD_c_compressionLevel, ZSTD_fast);
|
||||
}
|
||||
|
||||
inline zstd_compressor::~zstd_compressor() { ZSTD_freeCCtx(ctx_); }
|
||||
|
||||
inline bool zstd_compressor::compress(const char *data, size_t data_length,
|
||||
bool last, Callback callback) {
|
||||
std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
|
||||
|
||||
ZSTD_EndDirective mode = last ? ZSTD_e_end : ZSTD_e_continue;
|
||||
ZSTD_inBuffer input = {data, data_length, 0};
|
||||
|
||||
bool finished;
|
||||
do {
|
||||
ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
|
||||
size_t const remaining = ZSTD_compressStream2(ctx_, &output, &input, mode);
|
||||
|
||||
if (ZSTD_isError(remaining)) { return false; }
|
||||
|
||||
if (!callback(buff.data(), output.pos)) { return false; }
|
||||
|
||||
finished = last ? (remaining == 0) : (input.pos == input.size);
|
||||
|
||||
} while (!finished);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline zstd_decompressor::zstd_decompressor() { ctx_ = ZSTD_createDCtx(); }
|
||||
|
||||
inline zstd_decompressor::~zstd_decompressor() { ZSTD_freeDCtx(ctx_); }
|
||||
|
||||
inline bool zstd_decompressor::is_valid() const { return ctx_ != nullptr; }
|
||||
|
||||
inline bool zstd_decompressor::decompress(const char *data, size_t data_length,
|
||||
Callback callback) {
|
||||
std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
|
||||
ZSTD_inBuffer input = {data, data_length, 0};
|
||||
|
||||
while (input.pos < input.size) {
|
||||
ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
|
||||
size_t const remaining = ZSTD_decompressStream(ctx_, &output, &input);
|
||||
|
||||
if (ZSTD_isError(remaining)) { return false; }
|
||||
|
||||
if (!callback(buff.data(), output.pos)) { return false; }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline bool has_header(const Headers &headers, const std::string &key) {
|
||||
return headers.find(key) != headers.end();
|
||||
}
|
||||
|
@ -4397,6 +4490,13 @@ bool prepare_content_receiver(T &x, int &status,
|
|||
#else
|
||||
status = StatusCode::UnsupportedMediaType_415;
|
||||
return false;
|
||||
#endif
|
||||
} else if (encoding == "zstd") {
|
||||
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
|
||||
decompressor = detail::make_unique<zstd_decompressor>();
|
||||
#else
|
||||
status = StatusCode::UnsupportedMediaType_415;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -6634,6 +6734,10 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
|
|||
} else if (type == detail::EncodingType::Brotli) {
|
||||
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
|
||||
compressor = detail::make_unique<detail::brotli_compressor>();
|
||||
#endif
|
||||
} else if (type == detail::EncodingType::Zstd) {
|
||||
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
|
||||
compressor = detail::make_unique<detail::zstd_compressor>();
|
||||
#endif
|
||||
} else {
|
||||
compressor = detail::make_unique<detail::nocompressor>();
|
||||
|
@ -7049,6 +7153,8 @@ inline void Server::apply_ranges(const Request &req, Response &res,
|
|||
res.set_header("Content-Encoding", "gzip");
|
||||
} else if (type == detail::EncodingType::Brotli) {
|
||||
res.set_header("Content-Encoding", "br");
|
||||
} else if (type == detail::EncodingType::Zstd) {
|
||||
res.set_header("Content-Encoding", "zstd");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7088,6 +7194,11 @@ inline void Server::apply_ranges(const Request &req, Response &res,
|
|||
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
|
||||
compressor = detail::make_unique<detail::brotli_compressor>();
|
||||
content_encoding = "br";
|
||||
#endif
|
||||
} else if (type == detail::EncodingType::Zstd) {
|
||||
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
|
||||
compressor = detail::make_unique<detail::zstd_compressor>();
|
||||
content_encoding = "zstd";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -7812,6 +7923,10 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
|
|||
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
||||
if (!accept_encoding.empty()) { accept_encoding += ", "; }
|
||||
accept_encoding += "gzip, deflate";
|
||||
#endif
|
||||
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
|
||||
if (!accept_encoding.empty()) { accept_encoding += ", "; }
|
||||
accept_encoding += "zstd";
|
||||
#endif
|
||||
req.set_header("Accept-Encoding", accept_encoding);
|
||||
}
|
||||
|
@ -10377,4 +10492,4 @@ inline SSL_CTX *Client::ssl_context() const {
|
|||
|
||||
} // namespace httplib
|
||||
|
||||
#endif // CPPHTTPLIB_HTTPLIB_H
|
||||
#endif // CPPHTTPLIB_HTTPLIB_H
|
Loading…
Add table
Add a link
Reference in a new issue