Browse Source

Ignore Range on requests other than GET and HEAD

RFC 9110 14.2 defines range handling only for GET, and a server must
ignore a Range header on any other method. The server parsed it on
every method, so a POST with a Range got a 206 slice of the handler's
response, or a 416 when the Range was malformed. Range is now parsed
only for GET and HEAD; other methods get the whole response, and a
handler can still read the header itself.
yhirose 1 day ago
parent
commit
32a0ca8b35
2 changed files with 28 additions and 1 deletions
  1. 3 1
      httplib.h
  2. 25 0
      test/test.cc

+ 3 - 1
httplib.h

@@ -15202,7 +15202,9 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
     }
   }
 
-  if (req.has_header("Range")) {
+  // RFC 9110 14.2: range handling is defined only for GET (and so for HEAD)
+  if ((req.method == "GET" || req.method == "HEAD") &&
+      req.has_header("Range")) {
     const auto &range_header_value = req.get_header_value("Range");
     if (!detail::parse_range_header(range_header_value, req.ranges)) {
       connection_closed = true;

+ 25 - 0
test/test.cc

@@ -2742,6 +2742,31 @@ TEST(RangeTest, FromHTTPBin_Online) {
   }
 }
 
+TEST(RangeTest, IgnoredForPost) {
+  Server svr;
+  svr.Post("/", [](const Request &, Response &res) {
+    res.set_content("abcdefg", "text/plain");
+  });
+
+  auto port = svr.bind_to_any_port(HOST);
+  ASSERT_GT(port, 0);
+  thread t = thread([&] { svr.listen_after_bind(); });
+  auto se = detail::scope_exit([&] {
+    svr.stop();
+    t.join();
+    ASSERT_FALSE(svr.is_running());
+  });
+  svr.wait_until_ready();
+
+  Client cli(HOST, port);
+  for (auto range : {"bytes=1-2", "bytes=x"}) {
+    auto res = cli.Post("/", Headers{{"Range", range}});
+    ASSERT_TRUE(res) << range << ": " << to_string(res.error());
+    EXPECT_EQ(StatusCode::OK_200, res->status) << range;
+    EXPECT_EQ("abcdefg", res->body) << range;
+  }
+}
+
 TEST(GetAddrInfoDanglingRefTest, LongTimeout) {
   auto host = "unresolvableaddress.local";
   auto path = std::string{"/"};