Read no body from an unframed request in builds without TLS too

A request with neither Content-Length nor chunked Transfer-Encoding
has no body (RFC 9112 6.3), and builds with TLS support read none.
Builds without it still read whatever followed such a request on a
connection about to close, up to EOF, as its body. Every build now
reads no body. Tests that exercised only that read, or passed whatever
the server did, are removed, and a new one checks in every build that
the bytes after such a request are not taken as its body.
This commit is contained in:
yhirose committed 2026-10-22 08:50:17 -04:00
1 parent fbd3e2cc9b
commit 1c91d54b7c
2 files changed
+30 -239

No files matched your search

-37
View File
@@ -10398,15 +10398,6 @@ inline bool has_framed_body(const Request &req) {
req.get_header_value_u64("Content-Length") > 0;
}
inline bool is_connection_persistent(const Request &req) {
if (has_header_token(req.headers, "Connection", "close")) { return false; }
if (req.version == "HTTP/1.0" &&
!has_header_token(req.headers, "Connection", "keep-alive")) {
return false;
}
return true;
}
inline bool expect_content(const Request &req) {
if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" ||
req.method == "DELETE") {
@@ -14319,38 +14310,10 @@ inline bool Server::read_content_core(
}
// RFC 9112 §6: no Transfer-Encoding and no Content-Length means no body.
// For non-SSL builds we still scan non-persistent connections for stray
// body bytes so the payload limit is enforced (413). On keep-alive,
// pending bytes may be the next request (issue #2450), so skip.
#if !defined(CPPHTTPLIB_SSL_ENABLED)
if (!req.has_header("Content-Length") &&
!detail::is_chunked_transfer_encoding(req.headers)) {
if (!detail::is_connection_persistent(req) && payload_max_length_ > 0 &&
payload_max_length_ < (std::numeric_limits<size_t>::max)()) {
auto has_data = strm.is_readable();
if (!has_data) {
auto s = strm.socket();
if (s != INVALID_SOCKET) {
has_data = detail::select_read(s, 0, 0) > 0;
}
}
if (has_data) {
// Route through the same decompressing reader used by the
// length-framed and chunked paths below, so payload_max_length_ is
// enforced on the decompressed size here too instead of only on the
// compressed wire bytes.
return detail::read_content(strm, req, payload_max_length_, res.status,
nullptr, out, true);
}
}
return true;
}
#else
if (!req.has_header("Content-Length") &&
!detail::is_chunked_transfer_encoding(req.headers)) {
return true;
}
#endif
// A second read would start where the first one stopped, or in the next
// request.