@@ -12716,7 +12716,7 @@ TEST(ETagTest, StaticFileETagAndIfNoneMatch) {
1271612716 EXPECT_FALSE (etag.empty ());
1271712717
1271812718 // Verify ETag format: W/"hex-hex"
12719- ASSERT_GE (etag.length (), 5u ); // Minimum: W/""
12719+ ASSERT_GE (etag.length (), 5u ); // Minimum: W/""
1272012720 EXPECT_EQ (' W' , etag[0 ]);
1272112721 EXPECT_EQ (' /' , etag[1 ]);
1272212722 EXPECT_EQ (' "' , etag[2 ]);
@@ -12967,3 +12967,134 @@ TEST(ETagTest, IfRangeWithDate) {
1296712967 t.join ();
1296812968 std::remove (fname);
1296912969}
12970+ TEST (ETagTest, MalformedIfNoneMatchAndWhitespace) {
12971+ using namespace httplib ;
12972+
12973+ const char *fname = " etag_malformed.txt" ;
12974+ const char *content = " malformed-etag" ;
12975+ {
12976+ std::ofstream ofs (fname);
12977+ ofs << content;
12978+ ASSERT_TRUE (ofs.good ());
12979+ }
12980+
12981+ Server svr;
12982+ svr.set_mount_point (" /static" , " ." );
12983+ auto t = std::thread ([&]() { svr.listen (" localhost" , 8092 ); });
12984+ svr.wait_until_ready ();
12985+
12986+ Client cli (" localhost" , 8092 );
12987+
12988+ // baseline: should get 200 and an ETag
12989+ auto res1 = cli.Get (" /static/etag_malformed.txt" );
12990+ ASSERT_TRUE (res1);
12991+ ASSERT_EQ (200 , res1->status );
12992+ ASSERT_TRUE (res1->has_header (" ETag" ));
12993+
12994+ // Malformed ETag value (missing quotes) should be treated as non-matching
12995+ Headers h_bad = {{" If-None-Match" , " W/noquotes" }};
12996+ auto res_bad = cli.Get (" /static/etag_malformed.txt" , h_bad);
12997+ ASSERT_TRUE (res_bad);
12998+ EXPECT_EQ (200 , res_bad->status );
12999+
13000+ // Whitespace-only header value should be considered invalid / non-matching
13001+ Headers h_space = {{" If-None-Match" , " " }};
13002+ auto res_space = cli.Get (" /static/etag_malformed.txt" , h_space);
13003+ ASSERT_TRUE (res_space);
13004+ EXPECT_EQ (200 , res_space->status );
13005+
13006+ svr.stop ();
13007+ t.join ();
13008+ std::remove (fname);
13009+ }
13010+
13011+ TEST (ETagTest, InvalidIfModifiedSinceAndIfRangeDate) {
13012+ using namespace httplib ;
13013+
13014+ const char *fname = " ims_invalid_format.txt" ;
13015+ const char *content = " ims-bad-format" ;
13016+ {
13017+ std::ofstream ofs (fname);
13018+ ofs << content;
13019+ ASSERT_TRUE (ofs.good ());
13020+ }
13021+
13022+ Server svr;
13023+ svr.set_mount_point (" /static" , " ." );
13024+ auto t = std::thread ([&]() { svr.listen (" localhost" , 8093 ); });
13025+ svr.wait_until_ready ();
13026+
13027+ Client cli (" localhost" , 8093 );
13028+
13029+ auto res1 = cli.Get (" /static/ims_invalid_format.txt" );
13030+ ASSERT_TRUE (res1);
13031+ ASSERT_EQ (200 , res1->status );
13032+ ASSERT_TRUE (res1->has_header (" Last-Modified" ));
13033+
13034+ // If-Modified-Since with invalid format should not result in 304
13035+ Headers h_bad_date = {{" If-Modified-Since" , " not-a-valid-date" }};
13036+ auto res_bad = cli.Get (" /static/ims_invalid_format.txt" , h_bad_date);
13037+ ASSERT_TRUE (res_bad);
13038+ EXPECT_EQ (200 , res_bad->status );
13039+
13040+ // If-Range with invalid date format should be treated as mismatch -> full
13041+ // content (200)
13042+ Headers h_ifrange_bad = {{" Range" , " bytes=0-3" },
13043+ {" If-Range" , " invalid-date" }};
13044+ auto res_ifrange = cli.Get (" /static/ims_invalid_format.txt" , h_ifrange_bad);
13045+ ASSERT_TRUE (res_ifrange);
13046+ EXPECT_EQ (200 , res_ifrange->status );
13047+
13048+ svr.stop ();
13049+ t.join ();
13050+ std::remove (fname);
13051+ }
13052+
13053+ TEST (ETagTest, IfRangeWithMalformedETag) {
13054+ using namespace httplib ;
13055+
13056+ const char *fname = " ifrange_malformed.txt" ;
13057+ const std::string content = " 0123456789" ;
13058+ {
13059+ std::ofstream ofs (fname);
13060+ ofs << content;
13061+ ASSERT_TRUE (ofs.good ());
13062+ }
13063+
13064+ Server svr;
13065+ svr.set_mount_point (" /static" , " ." );
13066+ auto t = std::thread ([&]() { svr.listen (" localhost" , 8094 ); });
13067+ svr.wait_until_ready ();
13068+
13069+ Client cli (" localhost" , 8094 );
13070+
13071+ // First request: get ETag
13072+ auto res1 = cli.Get (" /static/ifrange_malformed.txt" );
13073+ ASSERT_TRUE (res1);
13074+ ASSERT_EQ (200 , res1->status );
13075+ ASSERT_TRUE (res1->has_header (" ETag" ));
13076+
13077+ // If-Range with malformed ETag (no quotes) should be treated as mismatch ->
13078+ // full content (200)
13079+ Headers h_malformed = {{" Range" , " bytes=0-4" }, {" If-Range" , " W/noquotes" }};
13080+ auto res2 = cli.Get (" /static/ifrange_malformed.txt" , h_malformed);
13081+ ASSERT_TRUE (res2);
13082+ EXPECT_EQ (200 , res2->status );
13083+ EXPECT_EQ (content, res2->body );
13084+
13085+ svr.stop ();
13086+ t.join ();
13087+ std::remove (fname);
13088+ }
13089+
13090+ TEST (ETagTest, DateParsingAndMtimeNegative) {
13091+ using namespace httplib ;
13092+
13093+ // parse_http_date should return -1 for invalid format
13094+ time_t parsed = detail::parse_http_date (" this is not a date" );
13095+ EXPECT_EQ (static_cast <time_t >(-1 ), parsed);
13096+
13097+ // file_mtime_to_http_date returns empty string for negative mtime
13098+ std::string s = detail::file_mtime_to_http_date (static_cast <time_t >(-1 ));
13099+ EXPECT_TRUE (s.empty ());
13100+ }
0 commit comments