1
0
mirror of synced 2025-12-18 16:34:09 +03:00

Refactor ETag handling: separate strong and weak ETag checks for If-Range requests

This commit is contained in:
yhirose
2025-12-05 00:09:01 -05:00
parent ee01189189
commit dc77463896
2 changed files with 31 additions and 10 deletions

View File

@@ -3032,10 +3032,14 @@ inline time_t parse_http_date(const std::string &date_str) {
#endif #endif
} }
// Check if the string is an ETag (starts with '"' or 'W/"') // Check if the string is a weak ETag (starts with 'W/"')
inline bool is_etag(const std::string &s) { inline bool is_weak_etag(const std::string &s) {
return !s.empty() && return s.size() > 3 && s[0] == 'W' && s[1] == '/' && s[2] == '"';
(s[0] == '"' || (s.size() > 2 && s[0] == 'W' && s[1] == '/')); }
// Check if the string is a strong ETag (starts with '"' but not 'W/"')
inline bool is_strong_etag(const std::string &s) {
return !s.empty() && s[0] == '"';
} }
inline size_t to_utf8(int code, char *buff) { inline size_t to_utf8(int code, char *buff) {
@@ -8399,9 +8403,13 @@ inline bool Server::handle_file_request(Request &req, Response &res) {
auto if_range = req.get_header_value("If-Range"); auto if_range = req.get_header_value("If-Range");
auto valid = false; auto valid = false;
if (detail::is_etag(if_range)) { if (detail::is_strong_etag(if_range)) {
// ETag comparison (weak comparison for If-Range per RFC 9110) // RFC 9110 Section 13.1.5: If-Range requires strong ETag
// comparison.
valid = (!etag.empty() && if_range == etag); valid = (!etag.empty() && if_range == etag);
} else if (detail::is_weak_etag(if_range)) {
// Weak ETags are not valid for If-Range (RFC 9110 Section 13.1.5)
valid = false;
} else { } else {
// HTTP-date comparison // HTTP-date comparison
auto if_range_time = detail::parse_http_date(if_range); auto if_range_time = detail::parse_http_date(if_range);

View File

@@ -12875,13 +12875,16 @@ TEST(ETagTest, IfRangeWithETag) {
ASSERT_TRUE(res1->has_header("ETag")); ASSERT_TRUE(res1->has_header("ETag"));
std::string etag = res1->get_header_value("ETag"); std::string etag = res1->get_header_value("ETag");
// Range request with matching If-Range (ETag): should get 206 // RFC 9110 Section 13.1.5: If-Range requires strong ETag comparison.
// Since our server generates weak ETags (W/"..."), If-Range with our
// ETag should NOT result in partial content - it should return full content.
Headers h2 = {{"Range", "bytes=0-4"}, {"If-Range", etag}}; Headers h2 = {{"Range", "bytes=0-4"}, {"If-Range", etag}};
auto res2 = cli.Get("/static/if_range_testfile.txt", h2); auto res2 = cli.Get("/static/if_range_testfile.txt", h2);
ASSERT_TRUE(res2); ASSERT_TRUE(res2);
EXPECT_EQ(206, res2->status); // Weak ETag in If-Range -> full content (200), not partial (206)
EXPECT_EQ("01234", res2->body); EXPECT_EQ(200, res2->status);
EXPECT_TRUE(res2->has_header("Content-Range")); EXPECT_EQ(content, res2->body);
EXPECT_FALSE(res2->has_header("Content-Range"));
// Range request with non-matching If-Range (ETag): should get 200 (full // Range request with non-matching If-Range (ETag): should get 200 (full
// content) // content)
@@ -12892,6 +12895,16 @@ TEST(ETagTest, IfRangeWithETag) {
EXPECT_EQ(content, res3->body); EXPECT_EQ(content, res3->body);
EXPECT_FALSE(res3->has_header("Content-Range")); EXPECT_FALSE(res3->has_header("Content-Range"));
// Range request with strong ETag (hypothetical - our server doesn't generate
// strong ETags, but if client sends a strong ETag that doesn't match, it
// should return full content)
Headers h4 = {{"Range", "bytes=0-4"}, {"If-Range", "\"strong-etag\""}};
auto res4 = cli.Get("/static/if_range_testfile.txt", h4);
ASSERT_TRUE(res4);
EXPECT_EQ(200, res4->status);
EXPECT_EQ(content, res4->body);
EXPECT_FALSE(res4->has_header("Content-Range"));
svr.stop(); svr.stop();
t.join(); t.join();
std::remove(fname); std::remove(fname);