Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 17 additions & 18 deletions httplib.h
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down
18 changes: 14 additions & 4 deletions test/test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
});

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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)));
Expand Down