Forráskód Böngészése

Ignore a Range header with a unit other than bytes (#2657)

RFC 9110 14.2 says an origin server MUST ignore a Range header field
with a range unit it does not understand. parse_range_header() accepts
only bytes=, so a request such as "Range: items=1-2" got a 416 instead
of the full response. The unit is now checked first, and only a bytes
range is parsed and answered with a 416 when it is invalid.
Eminy 3 napja
szülő
commit
39c98d4671
2 módosított fájl, 33 hozzáadás és 1 törlés
  1. 4 1
      httplib.h
  2. 29 0
      test/test.cc

+ 4 - 1
httplib.h

@@ -15292,7 +15292,10 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
   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)) {
+    // RFC 9110 14.2: a range unit other than bytes must be ignored
+    auto unit = range_header_value.substr(0, range_header_value.find('='));
+    if (detail::case_ignore::equal(unit, "bytes") &&
+        !detail::parse_range_header(range_header_value, req.ranges)) {
       connection_closed = true;
       res.status = StatusCode::RangeNotSatisfiable_416;
       output_error_log(Error::InvalidRangeHeader, &req);

+ 29 - 0
test/test.cc

@@ -2818,6 +2818,35 @@ TEST(RangeTest, IgnoredForPost) {
   }
 }
 
+TEST(RangeTest, IgnoredForUnknownUnit) {
+  Server svr;
+  svr.Get("/", [](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 : {"items=1-2", "none=x"}) {
+    auto res = cli.Get("/", 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;
+  }
+
+  auto res = cli.Get("/", Headers{{"Range", "bytes=x"}});
+  ASSERT_TRUE(res) << to_string(res.error());
+  EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status);
+}
+
 TEST(GetAddrInfoDanglingRefTest, LongTimeout) {
   auto host = "unresolvableaddress.local";
   auto path = std::string{"/"};