yhirose 10 months ago
parent
commit
4941d5b56b
2 changed files with 31 additions and 22 deletions
  1. 17 18
      httplib.h
  2. 14 4
      test/test.cc

+ 17 - 18
httplib.h

@@ -2549,7 +2549,7 @@ inline bool is_obs_text(char c) { return 128 <= static_cast<unsigned char>(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);
-
-    // 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; }
-
-    fn(key, val);
+    // 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);
+
+    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));
+    }
+
     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;

+ 14 - 4
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)));