Skip to content

Commit 975cf0d

Browse files
committed
Fix #1908
1 parent 4854a69 commit 975cf0d

File tree

2 files changed

+24
-27
lines changed

2 files changed

+24
-27
lines changed

httplib.h

+18-19
Original file line numberDiff line numberDiff line change
@@ -4021,6 +4021,18 @@ inline bool parse_header(const char *beg, const char *end, T fn) {
40214021
auto val = compare_case_ignore(key, "Location")
40224022
? std::string(p, end)
40234023
: decode_url(std::string(p, end), false);
4024+
4025+
// NOTE: From RFC 9110:
4026+
// Field values containing CR, LF, or NUL characters are
4027+
// invalid and dangerous, due to the varying ways that
4028+
// implementations might parse and interpret those
4029+
// characters; a recipient of CR, LF, or NUL within a field
4030+
// value MUST either reject the message or replace each of
4031+
// those characters with SP before further processing or
4032+
// forwarding of that message.
4033+
static const std::string CR_LF_NUL("\r\n\0", 3);
4034+
if (val.find_first_of(CR_LF_NUL) != std::string::npos) { return false; }
4035+
40244036
fn(key, val);
40254037
return true;
40264038
}
@@ -4058,25 +4070,12 @@ inline bool read_headers(Stream &strm, Headers &headers) {
40584070
// Exclude line terminator
40594071
auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
40604072

4061-
parse_header(line_reader.ptr(), end,
4062-
[&](const std::string &key, std::string &val) {
4063-
// NOTE: From RFC 9110:
4064-
// Field values containing CR, LF, or NUL characters are
4065-
// invalid and dangerous, due to the varying ways that
4066-
// implementations might parse and interpret those
4067-
// characters; a recipient of CR, LF, or NUL within a field
4068-
// value MUST either reject the message or replace each of
4069-
// those characters with SP before further processing or
4070-
// forwarding of that message.
4071-
for (auto &c : val) {
4072-
switch (c) {
4073-
case '\0':
4074-
case '\n':
4075-
case '\r': c = ' '; break;
4076-
}
4077-
}
4078-
headers.emplace(key, val);
4079-
});
4073+
if (!parse_header(line_reader.ptr(), end,
4074+
[&](const std::string &key, std::string &val) {
4075+
headers.emplace(key, val);
4076+
})) {
4077+
return false;
4078+
}
40804079
}
40814080

40824081
return true;

test/test.cc

+6-8
Original file line numberDiff line numberDiff line change
@@ -4718,9 +4718,7 @@ static void test_raw_request(const std::string &req,
47184718
svr.Put("/put_hi", [&](const Request & /*req*/, Response &res) {
47194719
res.set_content("ok", "text/plain");
47204720
});
4721-
svr.Get("/header_field_value_check", [&](const Request &req, Response &res) {
4722-
auto val = req.get_header_value("Test");
4723-
EXPECT_EQ("[ ]", val);
4721+
svr.Get("/header_field_value_check", [&](const Request &/*req*/, Response &res) {
47244722
res.set_content("ok", "text/plain");
47254723
});
47264724

@@ -4857,9 +4855,11 @@ TEST(ServerRequestParsingTest, InvalidSpaceInURL) {
48574855
}
48584856

48594857
TEST(ServerRequestParsingTest, InvalidFieldValueContains_CR_LF_NUL) {
4858+
std::string out;
48604859
std::string request(
48614860
"GET /header_field_value_check HTTP/1.1\r\nTest: [\r\x00\n]\r\n\r\n", 55);
4862-
test_raw_request(request);
4861+
test_raw_request(request, &out);
4862+
EXPECT_EQ("HTTP/1.1 400 Bad Request", out.substr(0, 24));
48634863
}
48644864

48654865
TEST(ServerStopTest, StopServerWithChunkedTransmission) {
@@ -7587,10 +7587,8 @@ TEST(FileSystemTest, FileAndDirExistenceCheck) {
75877587
TEST(DirtyDataRequestTest, HeadFieldValueContains_CR_LF_NUL) {
75887588
Server svr;
75897589

7590-
svr.Get("/test", [&](const Request &req, Response &) {
7591-
auto val = req.get_header_value("Test");
7592-
EXPECT_EQ(val.size(), 7u);
7593-
EXPECT_EQ(val, "_ _ _");
7590+
svr.Get("/test", [&](const Request &/*req*/, Response &res) {
7591+
EXPECT_EQ(res.status, 400);
75947592
});
75957593

75967594
auto thread = std::thread([&]() { svr.listen(HOST, PORT); });

0 commit comments

Comments
 (0)