mirror of
https://github.com/yhirose/cpp-httplib
synced 2024-11-21 14:29:10 -07:00
Fix #1766
This commit is contained in:
parent
ffc294d37e
commit
e323374d2a
2 changed files with 82 additions and 15 deletions
56
httplib.h
56
httplib.h
|
@ -82,6 +82,10 @@
|
||||||
#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
|
#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef CPPHTTPLIB_RANGE_MAX_COUNT
|
||||||
|
#define CPPHTTPLIB_RANGE_MAX_COUNT 1024
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef CPPHTTPLIB_TCP_NODELAY
|
#ifndef CPPHTTPLIB_TCP_NODELAY
|
||||||
#define CPPHTTPLIB_TCP_NODELAY false
|
#define CPPHTTPLIB_TCP_NODELAY false
|
||||||
#endif
|
#endif
|
||||||
|
@ -4721,29 +4725,57 @@ serialize_multipart_formdata(const MultipartFormDataItems &items,
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool normalize_ranges(Request &req, Response &res) {
|
inline bool normalize_ranges(Request &req, Response &res) {
|
||||||
ssize_t len = static_cast<ssize_t>(res.content_length_ ? res.content_length_
|
ssize_t contant_len = static_cast<ssize_t>(
|
||||||
: res.body.size());
|
res.content_length_ ? res.content_length_ : res.body.size());
|
||||||
|
|
||||||
|
ssize_t prev_first_pos = -1;
|
||||||
|
ssize_t prev_last_pos = -1;
|
||||||
|
size_t overwrapping_count = 0;
|
||||||
|
|
||||||
if (!req.ranges.empty()) {
|
if (!req.ranges.empty()) {
|
||||||
|
// NOTE: The following Range check is based on '14.2. Range' in RFC 9110
|
||||||
|
// 'HTTP Semantics' to avoid potential denial-of-service attacks.
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc9110#section-14.2
|
||||||
|
|
||||||
|
// Too many ranges
|
||||||
|
if (req.ranges.size() > CPPHTTPLIB_RANGE_MAX_COUNT) { return false; }
|
||||||
|
|
||||||
for (auto &r : req.ranges) {
|
for (auto &r : req.ranges) {
|
||||||
auto &st = r.first;
|
auto &first_pos = r.first;
|
||||||
auto &ed = r.second;
|
auto &last_pos = r.second;
|
||||||
|
|
||||||
if (st == -1 && ed == -1) {
|
if (first_pos == -1 && last_pos == -1) {
|
||||||
st = 0;
|
first_pos = 0;
|
||||||
ed = len;
|
last_pos = contant_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (st == -1) {
|
if (first_pos == -1) {
|
||||||
st = len - ed;
|
first_pos = contant_len - last_pos;
|
||||||
ed = len - 1;
|
last_pos = contant_len - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ed == -1) { ed = len - 1; }
|
if (last_pos == -1) { last_pos = contant_len - 1; }
|
||||||
|
|
||||||
if (!(0 <= st && st <= ed && ed <= len - 1)) { return false; }
|
// Range must be within content length
|
||||||
|
if (!(0 <= first_pos && first_pos <= last_pos &&
|
||||||
|
last_pos <= contant_len - 1)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ranges must be in ascending order
|
||||||
|
if (first_pos <= prev_first_pos) { return false; }
|
||||||
|
|
||||||
|
// Request must not have more than two overlapping ranges
|
||||||
|
if (first_pos <= prev_last_pos) {
|
||||||
|
overwrapping_count++;
|
||||||
|
if (overwrapping_count > 2) { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_first_pos = (std::max)(prev_first_pos, first_pos);
|
||||||
|
prev_last_pos = (std::max)(prev_last_pos, last_pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
41
test/test.cc
41
test/test.cc
|
@ -2962,7 +2962,7 @@ TEST_F(ServerTest, GetStreamedWithRangeSuffix2) {
|
||||||
EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status);
|
EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status);
|
||||||
EXPECT_EQ("0", res->get_header_value("Content-Length"));
|
EXPECT_EQ("0", res->get_header_value("Content-Length"));
|
||||||
EXPECT_EQ(false, res->has_header("Content-Range"));
|
EXPECT_EQ(false, res->has_header("Content-Range"));
|
||||||
EXPECT_EQ(0, res->body.size());
|
EXPECT_EQ(0U, res->body.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, GetStreamedWithRangeError) {
|
TEST_F(ServerTest, GetStreamedWithRangeError) {
|
||||||
|
@ -2973,7 +2973,7 @@ TEST_F(ServerTest, GetStreamedWithRangeError) {
|
||||||
EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status);
|
EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status);
|
||||||
EXPECT_EQ("0", res->get_header_value("Content-Length"));
|
EXPECT_EQ("0", res->get_header_value("Content-Length"));
|
||||||
EXPECT_EQ(false, res->has_header("Content-Range"));
|
EXPECT_EQ(false, res->has_header("Content-Range"));
|
||||||
EXPECT_EQ(0, res->body.size());
|
EXPECT_EQ(0U, res->body.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, GetRangeWithMaxLongLength) {
|
TEST_F(ServerTest, GetRangeWithMaxLongLength) {
|
||||||
|
@ -2982,7 +2982,7 @@ TEST_F(ServerTest, GetRangeWithMaxLongLength) {
|
||||||
EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status);
|
EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status);
|
||||||
EXPECT_EQ("0", res->get_header_value("Content-Length"));
|
EXPECT_EQ("0", res->get_header_value("Content-Length"));
|
||||||
EXPECT_EQ(false, res->has_header("Content-Range"));
|
EXPECT_EQ(false, res->has_header("Content-Range"));
|
||||||
EXPECT_EQ(0, res->body.size());
|
EXPECT_EQ(0U, res->body.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, GetStreamedWithRangeMultipart) {
|
TEST_F(ServerTest, GetStreamedWithRangeMultipart) {
|
||||||
|
@ -2995,6 +2995,41 @@ TEST_F(ServerTest, GetStreamedWithRangeMultipart) {
|
||||||
EXPECT_EQ(267U, res->body.size());
|
EXPECT_EQ(267U, res->body.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ServerTest, GetStreamedWithTooManyRanges) {
|
||||||
|
Ranges ranges;
|
||||||
|
for (size_t i = 0; i < CPPHTTPLIB_RANGE_MAX_COUNT + 1; i++) {
|
||||||
|
ranges.emplace_back(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto res =
|
||||||
|
cli_.Get("/streamed-with-range?error", {{make_range_header(ranges)}});
|
||||||
|
ASSERT_TRUE(res);
|
||||||
|
EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status);
|
||||||
|
EXPECT_EQ("0", res->get_header_value("Content-Length"));
|
||||||
|
EXPECT_EQ(false, res->has_header("Content-Range"));
|
||||||
|
EXPECT_EQ(0U, res->body.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ServerTest, GetStreamedWithNonAscendingRanges) {
|
||||||
|
auto res = cli_.Get("/streamed-with-range?error",
|
||||||
|
{{make_range_header({{0, -1}, {0, -1}})}});
|
||||||
|
ASSERT_TRUE(res);
|
||||||
|
EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status);
|
||||||
|
EXPECT_EQ("0", res->get_header_value("Content-Length"));
|
||||||
|
EXPECT_EQ(false, res->has_header("Content-Range"));
|
||||||
|
EXPECT_EQ(0U, res->body.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ServerTest, GetStreamedWithRangesMoreThanTwoOverwrapping) {
|
||||||
|
auto res = cli_.Get("/streamed-with-range?error",
|
||||||
|
{{make_range_header({{0, 1}, {1, 2}, {2, 3}, {3, 4}})}});
|
||||||
|
ASSERT_TRUE(res);
|
||||||
|
EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status);
|
||||||
|
EXPECT_EQ("0", res->get_header_value("Content-Length"));
|
||||||
|
EXPECT_EQ(false, res->has_header("Content-Range"));
|
||||||
|
EXPECT_EQ(0U, res->body.size());
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, GetStreamedEndless) {
|
TEST_F(ServerTest, GetStreamedEndless) {
|
||||||
uint64_t offset = 0;
|
uint64_t offset = 0;
|
||||||
auto res = cli_.Get("/streamed-cancel",
|
auto res = cli_.Get("/streamed-cancel",
|
||||||
|
|
Loading…
Reference in a new issue