Skip to content

Commit dc77463

Browse files
committed
Refactor ETag handling: separate strong and weak ETag checks for If-Range requests
1 parent ee01189 commit dc77463

File tree

2 files changed

+31
-10
lines changed

2 files changed

+31
-10
lines changed

httplib.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3032,10 +3032,14 @@ inline time_t parse_http_date(const std::string &date_str) {
30323032
#endif
30333033
}
30343034

3035-
// Check if the string is an ETag (starts with '"' or 'W/"')
3036-
inline bool is_etag(const std::string &s) {
3037-
return !s.empty() &&
3038-
(s[0] == '"' || (s.size() > 2 && s[0] == 'W' && s[1] == '/'));
3035+
// Check if the string is a weak ETag (starts with 'W/"')
3036+
inline bool is_weak_etag(const std::string &s) {
3037+
return s.size() > 3 && s[0] == 'W' && s[1] == '/' && s[2] == '"';
3038+
}
3039+
3040+
// Check if the string is a strong ETag (starts with '"' but not 'W/"')
3041+
inline bool is_strong_etag(const std::string &s) {
3042+
return !s.empty() && s[0] == '"';
30393043
}
30403044

30413045
inline size_t to_utf8(int code, char *buff) {
@@ -8399,9 +8403,13 @@ inline bool Server::handle_file_request(Request &req, Response &res) {
83998403
auto if_range = req.get_header_value("If-Range");
84008404
auto valid = false;
84018405

8402-
if (detail::is_etag(if_range)) {
8403-
// ETag comparison (weak comparison for If-Range per RFC 9110)
8406+
if (detail::is_strong_etag(if_range)) {
8407+
// RFC 9110 Section 13.1.5: If-Range requires strong ETag
8408+
// comparison.
84048409
valid = (!etag.empty() && if_range == etag);
8410+
} else if (detail::is_weak_etag(if_range)) {
8411+
// Weak ETags are not valid for If-Range (RFC 9110 Section 13.1.5)
8412+
valid = false;
84058413
} else {
84068414
// HTTP-date comparison
84078415
auto if_range_time = detail::parse_http_date(if_range);

test/test.cc

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12875,13 +12875,16 @@ TEST(ETagTest, IfRangeWithETag) {
1287512875
ASSERT_TRUE(res1->has_header("ETag"));
1287612876
std::string etag = res1->get_header_value("ETag");
1287712877

12878-
// Range request with matching If-Range (ETag): should get 206
12878+
// RFC 9110 Section 13.1.5: If-Range requires strong ETag comparison.
12879+
// Since our server generates weak ETags (W/"..."), If-Range with our
12880+
// ETag should NOT result in partial content - it should return full content.
1287912881
Headers h2 = {{"Range", "bytes=0-4"}, {"If-Range", etag}};
1288012882
auto res2 = cli.Get("/static/if_range_testfile.txt", h2);
1288112883
ASSERT_TRUE(res2);
12882-
EXPECT_EQ(206, res2->status);
12883-
EXPECT_EQ("01234", res2->body);
12884-
EXPECT_TRUE(res2->has_header("Content-Range"));
12884+
// Weak ETag in If-Range -> full content (200), not partial (206)
12885+
EXPECT_EQ(200, res2->status);
12886+
EXPECT_EQ(content, res2->body);
12887+
EXPECT_FALSE(res2->has_header("Content-Range"));
1288512888

1288612889
// Range request with non-matching If-Range (ETag): should get 200 (full
1288712890
// content)
@@ -12892,6 +12895,16 @@ TEST(ETagTest, IfRangeWithETag) {
1289212895
EXPECT_EQ(content, res3->body);
1289312896
EXPECT_FALSE(res3->has_header("Content-Range"));
1289412897

12898+
// Range request with strong ETag (hypothetical - our server doesn't generate
12899+
// strong ETags, but if client sends a strong ETag that doesn't match, it
12900+
// should return full content)
12901+
Headers h4 = {{"Range", "bytes=0-4"}, {"If-Range", "\"strong-etag\""}};
12902+
auto res4 = cli.Get("/static/if_range_testfile.txt", h4);
12903+
ASSERT_TRUE(res4);
12904+
EXPECT_EQ(200, res4->status);
12905+
EXPECT_EQ(content, res4->body);
12906+
EXPECT_FALSE(res4->has_header("Content-Range"));
12907+
1289512908
svr.stop();
1289612909
t.join();
1289712910
std::remove(fname);

0 commit comments

Comments
 (0)