diff --git a/httplib.h b/httplib.h index c1a9366..3a36096 100644 --- a/httplib.h +++ b/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); diff --git a/test/test.cc b/test/test.cc index 317461c..c2f9b0b 100644 --- a/test/test.cc +++ b/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{"/"};