mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-19 11:19:36 +00:00
Parse chunked trailers when the stream API decompresses
StreamHandle::read() parsed the trailers only on the path without a decompressor, so response->trailers stayed empty for a compressed chunked response. Parse them in one place for both paths, and only after the last chunk: after a failed read it waited another read timeout for a trailer line, and parse_trailers_if_needed() called before the end read body bytes as trailers.
This commit is contained in:
1 parent
07edfb86b4
commit
2e3cb0f985
2 files changed
+19
-29
No files matched your search
@@ -15941,24 +15941,11 @@ ClientImpl::open_stream(const std::string &method, const std::string &path,
|
||||
inline ssize_t ClientImpl::StreamHandle::read(char *buf, size_t len) {
|
||||
if (!is_valid() || !response) { return -1; }
|
||||
|
||||
if (decompressor_) { return read_with_decompression(buf, len); }
|
||||
auto n = detail::read_body_content(stream_, body_reader_, buf, len);
|
||||
|
||||
if (n <= 0 && body_reader_.chunked && !trailers_parsed_ && stream_) {
|
||||
trailers_parsed_ = true;
|
||||
if (body_reader_.chunked_decoder) {
|
||||
if (!body_reader_.chunked_decoder->parse_trailers_into(
|
||||
response->trailers, response->headers)) {
|
||||
return n;
|
||||
}
|
||||
} else {
|
||||
detail::ChunkedDecoder dec(*stream_);
|
||||
if (!dec.parse_trailers_into(response->trailers, response->headers)) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto n = decompressor_
|
||||
? read_with_decompression(buf, len)
|
||||
: detail::read_body_content(stream_, body_reader_, buf, len);
|
||||
|
||||
if (n == 0) { parse_trailers_if_needed(); }
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -16014,22 +16001,14 @@ inline ssize_t ClientImpl::StreamHandle::read_with_decompression(char *buf,
|
||||
}
|
||||
|
||||
inline void ClientImpl::StreamHandle::parse_trailers_if_needed() {
|
||||
if (!response || !stream_ || !body_reader_.chunked || trailers_parsed_) {
|
||||
// The trailers follow the last chunk, so there are none before it
|
||||
auto &decoder = body_reader_.chunked_decoder;
|
||||
if (!response || trailers_parsed_ || !decoder || !decoder->finished) {
|
||||
return;
|
||||
}
|
||||
|
||||
trailers_parsed_ = true;
|
||||
|
||||
const auto bufsiz = 128;
|
||||
char line_buf[bufsiz];
|
||||
detail::stream_line_reader line_reader(*stream_, line_buf, bufsiz);
|
||||
|
||||
if (!line_reader.getline()) { return; }
|
||||
|
||||
if (!detail::parse_trailers(line_reader, response->trailers,
|
||||
response->headers)) {
|
||||
return;
|
||||
}
|
||||
decoder->parse_trailers_into(response->trailers, response->headers);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
@@ -23632,6 +23632,17 @@ TEST_F(OpenStreamTest, Gzip) {
|
||||
EXPECT_EQ("gzip", handle.response->get_header_value("Content-Encoding"));
|
||||
EXPECT_EQ(100u * 1024u, read_all(handle).size());
|
||||
}
|
||||
|
||||
TEST_F(OpenStreamTest, TrailersWithGzip) {
|
||||
Client cli("127.0.0.1", port_);
|
||||
auto handle =
|
||||
cli.open_stream("GET", "/streamed-chunked-with-prohibited-trailer", {},
|
||||
{{"Accept-Encoding", "gzip"}});
|
||||
ASSERT_TRUE(handle.is_valid());
|
||||
EXPECT_EQ("gzip", handle.response->get_header_value("Content-Encoding"));
|
||||
EXPECT_EQ("123456789", read_all(handle));
|
||||
EXPECT_EQ("yes", handle.response->get_trailer_value("X-Allowed"));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
|
||||
|
||||
Reference in new issue
Block a user