Skip to content

Commit a636a09

Browse files
committed
Fix #1656
1 parent cb85e57 commit a636a09

File tree

3 files changed

+158
-0
lines changed

3 files changed

+158
-0
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,18 @@ svr.set_logger([](const auto& req, const auto& res) {
275275
});
276276
```
277277

278+
You can also set a pre-compression logger to capture request/response data before compression is applied. This is useful for debugging and monitoring purposes when you need to see the original, uncompressed response content:
279+
280+
```cpp
281+
svr.set_pre_compression_logger([](const auto& req, const auto& res) {
282+
// Log before compression - res.body contains uncompressed content
283+
// Content-Encoding header is not yet set
284+
your_pre_compression_logger(req, res);
285+
});
286+
```
287+
288+
The pre-compression logger is only called when compression would be applied. For responses without compression, only the regular logger is called.
289+
278290
### Error handler
279291

280292
```cpp

httplib.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,7 @@ class Server {
10591059

10601060
Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
10611061
Server &set_logger(Logger logger);
1062+
Server &set_pre_compression_logger(Logger logger);
10621063

10631064
Server &set_address_family(int family);
10641065
Server &set_tcp_nodelay(bool on);
@@ -1202,6 +1203,7 @@ class Server {
12021203
Expect100ContinueHandler expect_100_continue_handler_;
12031204

12041205
Logger logger_;
1206+
Logger pre_compression_logger_;
12051207

12061208
int address_family_ = AF_UNSPEC;
12071209
bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
@@ -6913,6 +6915,11 @@ inline Server &Server::set_logger(Logger logger) {
69136915
return *this;
69146916
}
69156917

6918+
inline Server &Server::set_pre_compression_logger(Logger logger) {
6919+
pre_compression_logger_ = std::move(logger);
6920+
return *this;
6921+
}
6922+
69166923
inline Server &
69176924
Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
69186925
expect_100_continue_handler_ = std::move(handler);
@@ -7647,6 +7654,8 @@ inline void Server::apply_ranges(const Request &req, Response &res,
76477654
}
76487655

76497656
if (type != detail::EncodingType::None) {
7657+
if (pre_compression_logger_) { pre_compression_logger_(req, res); }
7658+
76507659
std::unique_ptr<detail::compressor> compressor;
76517660
std::string content_encoding;
76527661

test/test.cc

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5901,6 +5901,143 @@ TEST_F(ServerTest, PutWithContentProviderWithZstd) {
59015901
EXPECT_EQ("PUT", res->body);
59025902
}
59035903

5904+
// Pre-compression logging tests
5905+
TEST_F(ServerTest, PreCompressionLogging) {
5906+
// Test data for compression (matches the actual /compress endpoint content)
5907+
const std::string test_content = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
5908+
5909+
// Variables to capture logging data
5910+
std::string pre_compression_body;
5911+
std::string pre_compression_content_type;
5912+
std::string pre_compression_content_encoding;
5913+
5914+
std::string post_compression_body;
5915+
std::string post_compression_content_type;
5916+
std::string post_compression_content_encoding;
5917+
5918+
// Set up pre-compression logger
5919+
svr_.set_pre_compression_logger([&](const Request &req, const Response &res) {
5920+
pre_compression_body = res.body;
5921+
pre_compression_content_type = res.get_header_value("Content-Type");
5922+
pre_compression_content_encoding = res.get_header_value("Content-Encoding");
5923+
});
5924+
5925+
// Set up post-compression logger
5926+
svr_.set_logger([&](const Request &req, const Response &res) {
5927+
post_compression_body = res.body;
5928+
post_compression_content_type = res.get_header_value("Content-Type");
5929+
post_compression_content_encoding = res.get_header_value("Content-Encoding");
5930+
});
5931+
5932+
// Test with gzip compression
5933+
Headers headers;
5934+
headers.emplace("Accept-Encoding", "gzip");
5935+
5936+
auto res = cli_.Get("/compress", headers);
5937+
5938+
// Verify response was compressed
5939+
ASSERT_TRUE(res);
5940+
EXPECT_EQ(StatusCode::OK_200, res->status);
5941+
EXPECT_EQ("gzip", res->get_header_value("Content-Encoding"));
5942+
5943+
// Verify pre-compression logger captured uncompressed content
5944+
EXPECT_EQ(test_content, pre_compression_body);
5945+
EXPECT_EQ("text/plain", pre_compression_content_type);
5946+
EXPECT_TRUE(pre_compression_content_encoding.empty()); // No encoding header before compression
5947+
5948+
// Verify post-compression logger captured compressed content
5949+
EXPECT_NE(test_content, post_compression_body); // Should be different after compression
5950+
EXPECT_EQ("text/plain", post_compression_content_type);
5951+
EXPECT_EQ("gzip", post_compression_content_encoding);
5952+
5953+
// Verify compressed content is smaller
5954+
EXPECT_LT(post_compression_body.size(), pre_compression_body.size());
5955+
}
5956+
5957+
TEST_F(ServerTest, PreCompressionLoggingWithBrotli) {
5958+
const std::string test_content = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
5959+
5960+
std::string pre_compression_body;
5961+
std::string post_compression_body;
5962+
5963+
svr_.set_pre_compression_logger([&](const Request &req, const Response &res) {
5964+
pre_compression_body = res.body;
5965+
});
5966+
5967+
svr_.set_logger([&](const Request &req, const Response &res) {
5968+
post_compression_body = res.body;
5969+
});
5970+
5971+
Headers headers;
5972+
headers.emplace("Accept-Encoding", "br");
5973+
5974+
auto res = cli_.Get("/compress", headers);
5975+
5976+
ASSERT_TRUE(res);
5977+
EXPECT_EQ(StatusCode::OK_200, res->status);
5978+
EXPECT_EQ("br", res->get_header_value("Content-Encoding"));
5979+
5980+
// Verify pre-compression content is uncompressed
5981+
EXPECT_EQ(test_content, pre_compression_body);
5982+
5983+
// Verify post-compression content is compressed
5984+
EXPECT_NE(test_content, post_compression_body);
5985+
EXPECT_LT(post_compression_body.size(), pre_compression_body.size());
5986+
}
5987+
5988+
TEST_F(ServerTest, PreCompressionLoggingWithoutCompression) {
5989+
const std::string test_content = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
5990+
5991+
std::string pre_compression_body;
5992+
std::string post_compression_body;
5993+
5994+
svr_.set_pre_compression_logger([&](const Request &req, const Response &res) {
5995+
pre_compression_body = res.body;
5996+
});
5997+
5998+
svr_.set_logger([&](const Request &req, const Response &res) {
5999+
post_compression_body = res.body;
6000+
});
6001+
6002+
// Request without compression (use /nocompress endpoint)
6003+
Headers headers;
6004+
auto res = cli_.Get("/nocompress", headers);
6005+
6006+
ASSERT_TRUE(res);
6007+
EXPECT_EQ(StatusCode::OK_200, res->status);
6008+
EXPECT_TRUE(res->get_header_value("Content-Encoding").empty());
6009+
6010+
// Pre-compression logger should not be called when no compression is applied
6011+
EXPECT_TRUE(pre_compression_body.empty()); // Pre-compression logger not called
6012+
EXPECT_EQ(test_content, post_compression_body); // Post-compression logger captures final content
6013+
}
6014+
6015+
TEST_F(ServerTest, PreCompressionLoggingOnlyPreLogger) {
6016+
const std::string test_content = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
6017+
6018+
std::string pre_compression_body;
6019+
bool pre_logger_called = false;
6020+
6021+
// Set only pre-compression logger
6022+
svr_.set_pre_compression_logger([&](const Request &req, const Response &res) {
6023+
pre_compression_body = res.body;
6024+
pre_logger_called = true;
6025+
});
6026+
6027+
Headers headers;
6028+
headers.emplace("Accept-Encoding", "gzip");
6029+
6030+
auto res = cli_.Get("/compress", headers);
6031+
6032+
ASSERT_TRUE(res);
6033+
EXPECT_EQ(StatusCode::OK_200, res->status);
6034+
EXPECT_EQ("gzip", res->get_header_value("Content-Encoding"));
6035+
6036+
// Verify pre-compression logger was called
6037+
EXPECT_TRUE(pre_logger_called);
6038+
EXPECT_EQ(test_content, pre_compression_body);
6039+
}
6040+
59046041
TEST(ZstdDecompressor, ChunkedDecompression) {
59056042
std::string data;
59066043
for (size_t i = 0; i < 32 * 1024; ++i) {

0 commit comments

Comments
 (0)