diff --git a/httplib.h b/httplib.h index cc9cbf3..a16dd26 100644 --- a/httplib.h +++ b/httplib.h @@ -2549,7 +2549,7 @@ inline bool is_obs_text(char c) { return 128 <= static_cast(c); } inline bool is_field_vchar(char c) { return is_vchar(c) || is_obs_text(c); } inline bool is_field_content(const std::string &s) { - if (s.empty()) { return false; } + if (s.empty()) { return true; } if (s.size() == 1) { return is_field_vchar(s[0]); @@ -4214,22 +4214,21 @@ inline bool parse_header(const char *beg, const char *end, T fn) { if (!key_len) { return false; } auto key = std::string(beg, key_end); - auto val = case_ignore::equal(key, "Location") - ? std::string(p, end) - : decode_url(std::string(p, end), false); + // auto val = (case_ignore::equal(key, "Location") || + // case_ignore::equal(key, "Referer")) + // ? std::string(p, end) + // : decode_url(std::string(p, end), false); + auto val = std::string(p, end); - // NOTE: From RFC 9110: - // Field values containing CR, LF, or NUL characters are - // invalid and dangerous, due to the varying ways that - // implementations might parse and interpret those - // characters; a recipient of CR, LF, or NUL within a field - // value MUST either reject the message or replace each of - // those characters with SP before further processing or - // forwarding of that message. - static const std::string CR_LF_NUL("\r\n\0", 3); - if (val.find_first_of(CR_LF_NUL) != std::string::npos) { return false; } + if (!detail::fields::is_field_value(val)) { return false; } + + if (case_ignore::equal(key, "Location") || + case_ignore::equal(key, "Referer")) { + fn(key, val); + } else { + fn(key, decode_url(val, false)); + } - fn(key, val); return true; } @@ -4265,7 +4264,7 @@ inline bool read_headers(Stream &strm, Headers &headers) { auto end = line_reader.ptr() + line_reader.size() - line_terminator_len; if (!parse_header(line_reader.ptr(), end, - [&](const std::string &key, std::string &val) { + [&](const std::string &key, const std::string &val) { headers.emplace(key, val); })) { return false; diff --git a/test/test.cc b/test/test.cc index bfbf992..76c3223 100644 --- a/test/test.cc +++ b/test/test.cc @@ -1866,7 +1866,7 @@ TEST(PathUrlEncodeTest, PathUrlEncode) { TEST(PathUrlEncodeTest, IncludePercentEncodingLF) { Server svr; - svr.Get("/", [](const Request &req, Response &res) { + svr.Get("/", [](const Request &req, Response &) { EXPECT_EQ("\x0A", req.get_param_value("something")); }); @@ -4936,8 +4936,10 @@ TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) { "Connection: close\r\n" "\r\n"; - ASSERT_TRUE(send_request(5, req)); - EXPECT_EQ(header_value, "\v bar \x1B"); + std::string res; + ASSERT_TRUE(send_request(5, req, &res)); + EXPECT_EQ(header_value, ""); + EXPECT_EQ("HTTP/1.1 400 Bad Request", res.substr(0, 24)); } // Sends a raw request and verifies that there isn't a crash or exception. @@ -5095,6 +5097,14 @@ TEST(ServerRequestParsingTest, InvalidFieldValueContains_CR_LF_NUL) { EXPECT_EQ("HTTP/1.1 400 Bad Request", out.substr(0, 24)); } +TEST(ServerRequestParsingTest, InvalidFieldValueContains_LF) { + std::string out; + std::string request( + "GET /header_field_value_check HTTP/1.1\r\nTest: [\n\n\n]\r\n\r\n", 55); + test_raw_request(request, &out); + EXPECT_EQ("HTTP/1.1 400 Bad Request", out.substr(0, 24)); +} + TEST(ServerRequestParsingTest, EmptyFieldValue) { std::string out; @@ -7984,7 +7994,7 @@ TEST(InvalidHeaderCharsTest, is_field_value) { EXPECT_FALSE(detail::fields::is_field_value(" example_token")); EXPECT_FALSE(detail::fields::is_field_value("example_token ")); EXPECT_TRUE(detail::fields::is_field_value("token@123")); - EXPECT_FALSE(detail::fields::is_field_value("")); + EXPECT_TRUE(detail::fields::is_field_value("")); EXPECT_FALSE(detail::fields::is_field_value("example\rtoken")); EXPECT_FALSE(detail::fields::is_field_value("example\ntoken")); EXPECT_FALSE(detail::fields::is_field_value(std::string("\0", 1)));