Skip to content

Commit 337fbb0

Browse files
committed
Fix #2279
Enhance request handling: add support for requests without Content-Length or Transfer-Encoding headers
1 parent 9e7861b commit 337fbb0

File tree

2 files changed

+78
-4
lines changed

2 files changed

+78
-4
lines changed

httplib.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7932,7 +7932,11 @@ inline bool Server::read_content_core(
79327932
size_t /*len*/) { return receiver(buf, n); };
79337933
}
79347934

7935-
if (req.method == "DELETE" && !req.has_header("Content-Length")) {
7935+
// RFC 7230 Section 3.3.3: If this is a request message and none of the above
7936+
// are true (no Transfer-Encoding and no Content-Length), then the message
7937+
// body length is zero (no message body is present).
7938+
if (!req.has_header("Content-Length") &&
7939+
!detail::is_chunked_transfer_encoding(req.headers)) {
79367940
return true;
79377941
}
79387942

test/test.cc

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3424,7 +3424,7 @@ class ServerTest : public ::testing::Test {
34243424
res.set_content(req.body, "text/plain");
34253425
})
34263426
.Post("/post-loopback",
3427-
[&](const Request &req, Response &res,
3427+
[&](const Request &, Response &res,
34283428
ContentReader const &content_reader) {
34293429
std::string body;
34303430
content_reader([&](const char *data, size_t data_length) {
@@ -3435,7 +3435,7 @@ class ServerTest : public ::testing::Test {
34353435
res.set_content(body, "text/plain");
34363436
})
34373437
.Put("/put-loopback",
3438-
[&](const Request &req, Response &res,
3438+
[&](const Request &, Response &res,
34393439
ContentReader const &content_reader) {
34403440
std::string body;
34413441
content_reader([&](const char *data, size_t data_length) {
@@ -3446,7 +3446,7 @@ class ServerTest : public ::testing::Test {
34463446
res.set_content(body, "text/plain");
34473447
})
34483448
.Patch("/patch-loopback",
3449-
[&](const Request &req, Response &res,
3449+
[&](const Request &, Response &res,
34503450
ContentReader const &content_reader) {
34513451
std::string body;
34523452
content_reader([&](const char *data, size_t data_length) {
@@ -11609,3 +11609,73 @@ TEST(ForwardedHeadersTest, HandlesWhitespaceAroundIPs) {
1160911609
EXPECT_EQ(observed_xff, "198.51.100.23 , 203.0.113.66 , 192.0.2.45");
1161011610
EXPECT_EQ(observed_remote_addr, "203.0.113.66");
1161111611
}
11612+
11613+
TEST(ServerRequestParsingTest, RequestWithoutContentLengthOrTransferEncoding) {
11614+
Server svr;
11615+
11616+
svr.Post("/post", [&](const Request &req, Response &res) {
11617+
res.set_content(req.body, "text/plain");
11618+
});
11619+
11620+
svr.Put("/put", [&](const Request &req, Response &res) {
11621+
res.set_content(req.body, "text/plain");
11622+
});
11623+
11624+
svr.Patch("/patch", [&](const Request &req, Response &res) {
11625+
res.set_content(req.body, "text/plain");
11626+
});
11627+
11628+
svr.Delete("/delete", [&](const Request &req, Response &res) {
11629+
res.set_content(req.body, "text/plain");
11630+
});
11631+
11632+
thread t = thread([&]() { svr.listen(HOST, PORT); });
11633+
auto se = detail::scope_exit([&] {
11634+
svr.stop();
11635+
t.join();
11636+
ASSERT_FALSE(svr.is_running());
11637+
});
11638+
11639+
svr.wait_until_ready();
11640+
11641+
std::string resp;
11642+
11643+
// POST without Content-Length
11644+
ASSERT_TRUE(send_request(5,
11645+
"POST /post HTTP/1.1\r\n"
11646+
"Host: localhost\r\n"
11647+
"Connection: close\r\n"
11648+
"\r\n",
11649+
&resp));
11650+
EXPECT_TRUE(resp.find("HTTP/1.1 200 OK") == 0);
11651+
11652+
// PUT without Content-Length
11653+
resp.clear();
11654+
ASSERT_TRUE(send_request(5,
11655+
"PUT /put HTTP/1.1\r\n"
11656+
"Host: localhost\r\n"
11657+
"Connection: close\r\n"
11658+
"\r\n",
11659+
&resp));
11660+
EXPECT_TRUE(resp.find("HTTP/1.1 200 OK") == 0);
11661+
11662+
// PATCH without Content-Length
11663+
resp.clear();
11664+
ASSERT_TRUE(send_request(5,
11665+
"PATCH /patch HTTP/1.1\r\n"
11666+
"Host: localhost\r\n"
11667+
"Connection: close\r\n"
11668+
"\r\n",
11669+
&resp));
11670+
EXPECT_TRUE(resp.find("HTTP/1.1 200 OK") == 0);
11671+
11672+
// DELETE without Content-Length
11673+
resp.clear();
11674+
ASSERT_TRUE(send_request(5,
11675+
"DELETE /delete HTTP/1.1\r\n"
11676+
"Host: localhost\r\n"
11677+
"Connection: close\r\n"
11678+
"\r\n",
11679+
&resp));
11680+
EXPECT_TRUE(resp.find("HTTP/1.1 200 OK") == 0);
11681+
}

0 commit comments

Comments
 (0)