Skip to content

Commit fceada9

Browse files
committed
Changed to return 416 for a request with an invalid range
1 parent 5f0f73f commit fceada9

File tree

2 files changed

+93
-77
lines changed

2 files changed

+93
-77
lines changed

httplib.h

Lines changed: 78 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -4720,29 +4720,47 @@ serialize_multipart_formdata(const MultipartFormDataItems &items,
47204720
return body;
47214721
}
47224722

4723-
inline std::pair<size_t, size_t>
4724-
get_range_offset_and_length(Range range, size_t content_length) {
4725-
if (range.first == -1 && range.second == -1) {
4726-
return std::make_pair(0, content_length);
4727-
}
4723+
inline bool normalize_ranges(Request &req, Response &res) {
4724+
ssize_t len = static_cast<ssize_t>(res.content_length_ ? res.content_length_
4725+
: res.body.size());
4726+
4727+
if (!req.ranges.empty()) {
4728+
for (auto &r : req.ranges) {
4729+
auto &st = r.first;
4730+
auto &ed = r.second;
4731+
4732+
if (st == -1 && ed == -1) {
4733+
st = 0;
4734+
ed = len;
4735+
}
4736+
4737+
if (st == -1) {
4738+
st = len - ed;
4739+
ed = len - 1;
4740+
}
47284741

4729-
auto slen = static_cast<ssize_t>(content_length);
4742+
if (ed == -1) { ed = len - 1; }
47304743

4731-
if (range.first == -1) {
4732-
range.first = (std::max)(static_cast<ssize_t>(0), slen - range.second);
4733-
range.second = slen - 1;
4744+
if (!(0 <= st && st <= ed && ed <= len - 1)) { return false; }
4745+
}
47344746
}
4747+
return true;
4748+
}
47354749

4736-
if (range.second == -1) { range.second = slen - 1; }
4737-
return std::make_pair(range.first,
4738-
static_cast<size_t>(range.second - range.first) + 1);
4750+
inline std::pair<size_t, size_t>
4751+
get_range_offset_and_length(Range r, size_t content_length) {
4752+
assert(r.first != -1 && r.second != -1);
4753+
assert(0 <= r.first && r.first < static_cast<ssize_t>(content_length));
4754+
assert(r.first <= r.second &&
4755+
r.second < static_cast<ssize_t>(content_length));
4756+
4757+
return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);
47394758
}
47404759

47414760
inline std::string make_content_range_header_field(
47424761
const std::pair<size_t, size_t> &offset_and_length, size_t content_length) {
4743-
47444762
auto st = offset_and_length.first;
4745-
auto ed = (std::min)(st + offset_and_length.second - 1, content_length - 1);
4763+
auto ed = st + offset_and_length.second - 1;
47464764

47474765
std::string field = "bytes ";
47484766
field += std::to_string(st);
@@ -4754,11 +4772,11 @@ inline std::string make_content_range_header_field(
47544772
}
47554773

47564774
template <typename SToken, typename CToken, typename Content>
4757-
bool process_multipart_ranges_data(const Request &req, Response &res,
4775+
bool process_multipart_ranges_data(const Request &req,
47584776
const std::string &boundary,
47594777
const std::string &content_type,
4760-
SToken stoken, CToken ctoken,
4761-
Content content) {
4778+
size_t content_length, SToken stoken,
4779+
CToken ctoken, Content content) {
47624780
for (size_t i = 0; i < req.ranges.size(); i++) {
47634781
ctoken("--");
47644782
stoken(boundary);
@@ -4770,11 +4788,10 @@ bool process_multipart_ranges_data(const Request &req, Response &res,
47704788
}
47714789

47724790
auto offset_and_length =
4773-
get_range_offset_and_length(req.ranges[i], res.content_length_);
4791+
get_range_offset_and_length(req.ranges[i], content_length);
47744792

47754793
ctoken("Content-Range: ");
4776-
stoken(make_content_range_header_field(offset_and_length,
4777-
res.content_length_));
4794+
stoken(make_content_range_header_field(offset_and_length, content_length));
47784795
ctoken("\r\n");
47794796
ctoken("\r\n");
47804797

@@ -4791,31 +4808,30 @@ bool process_multipart_ranges_data(const Request &req, Response &res,
47914808
return true;
47924809
}
47934810

4794-
inline bool make_multipart_ranges_data(const Request &req, Response &res,
4811+
inline void make_multipart_ranges_data(const Request &req, Response &res,
47954812
const std::string &boundary,
47964813
const std::string &content_type,
4814+
size_t content_length,
47974815
std::string &data) {
4798-
return process_multipart_ranges_data(
4799-
req, res, boundary, content_type,
4816+
process_multipart_ranges_data(
4817+
req, boundary, content_type, content_length,
48004818
[&](const std::string &token) { data += token; },
48014819
[&](const std::string &token) { data += token; },
48024820
[&](size_t offset, size_t length) {
4803-
if (offset < res.body.size()) {
4804-
data += res.body.substr(offset, length);
4805-
return true;
4806-
}
4807-
return false;
4821+
assert(offset + length <= content_length);
4822+
data += res.body.substr(offset, length);
4823+
return true;
48084824
});
48094825
}
48104826

4811-
inline size_t
4812-
get_multipart_ranges_data_length(const Request &req, Response &res,
4813-
const std::string &boundary,
4814-
const std::string &content_type) {
4827+
inline size_t get_multipart_ranges_data_length(const Request &req,
4828+
const std::string &boundary,
4829+
const std::string &content_type,
4830+
size_t content_length) {
48154831
size_t data_length = 0;
48164832

48174833
process_multipart_ranges_data(
4818-
req, res, boundary, content_type,
4834+
req, boundary, content_type, content_length,
48194835
[&](const std::string &token) { data_length += token.size(); },
48204836
[&](const std::string &token) { data_length += token.size(); },
48214837
[&](size_t /*offset*/, size_t length) {
@@ -4827,13 +4843,13 @@ get_multipart_ranges_data_length(const Request &req, Response &res,
48274843
}
48284844

48294845
template <typename T>
4830-
inline bool write_multipart_ranges_data(Stream &strm, const Request &req,
4831-
Response &res,
4832-
const std::string &boundary,
4833-
const std::string &content_type,
4834-
const T &is_shutting_down) {
4846+
inline bool
4847+
write_multipart_ranges_data(Stream &strm, const Request &req, Response &res,
4848+
const std::string &boundary,
4849+
const std::string &content_type,
4850+
size_t content_length, const T &is_shutting_down) {
48354851
return process_multipart_ranges_data(
4836-
req, res, boundary, content_type,
4852+
req, boundary, content_type, content_length,
48374853
[&](const std::string &token) { strm.write(token); },
48384854
[&](const std::string &token) { strm.write(token); },
48394855
[&](size_t offset, size_t length) {
@@ -6012,7 +6028,6 @@ inline bool Server::write_response_core(Stream &strm, bool close_connection,
60126028
if (write_content_with_provider(strm, req, res, boundary, content_type)) {
60136029
res.content_provider_success_ = true;
60146030
} else {
6015-
res.content_provider_success_ = false;
60166031
ret = false;
60176032
}
60186033
}
@@ -6045,7 +6060,8 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
60456060
offset_and_length.second, is_shutting_down);
60466061
} else {
60476062
return detail::write_multipart_ranges_data(
6048-
strm, req, res, boundary, content_type, is_shutting_down);
6063+
strm, req, res, boundary, content_type, res.content_length_,
6064+
is_shutting_down);
60496065
}
60506066
} else {
60516067
if (res.is_chunked_content_provider_) {
@@ -6437,14 +6453,14 @@ inline void Server::apply_ranges(const Request &req, Response &res,
64376453
std::string &content_type,
64386454
std::string &boundary) const {
64396455
if (req.ranges.size() > 1) {
6440-
boundary = detail::make_multipart_data_boundary();
6441-
64426456
auto it = res.headers.find("Content-Type");
64436457
if (it != res.headers.end()) {
64446458
content_type = it->second;
64456459
res.headers.erase(it);
64466460
}
64476461

6462+
boundary = detail::make_multipart_data_boundary();
6463+
64486464
res.set_header("Content-Type",
64496465
"multipart/byteranges; boundary=" + boundary);
64506466
}
@@ -6466,8 +6482,8 @@ inline void Server::apply_ranges(const Request &req, Response &res,
64666482
offset_and_length, res.content_length_);
64676483
res.set_header("Content-Range", content_range);
64686484
} else {
6469-
length = detail::get_multipart_ranges_data_length(req, res, boundary,
6470-
content_type);
6485+
length = detail::get_multipart_ranges_data_length(
6486+
req, boundary, content_type, res.content_length_);
64716487
}
64726488
res.set_header("Content-Length", std::to_string(length));
64736489
} else {
@@ -6495,21 +6511,13 @@ inline void Server::apply_ranges(const Request &req, Response &res,
64956511
offset_and_length, res.body.size());
64966512
res.set_header("Content-Range", content_range);
64976513

6498-
if (offset < res.body.size()) {
6499-
res.body = res.body.substr(offset, length);
6500-
} else {
6501-
res.body.clear();
6502-
res.status = StatusCode::RangeNotSatisfiable_416;
6503-
}
6514+
assert(offset + length <= res.body.size());
6515+
res.body = res.body.substr(offset, length);
65046516
} else {
65056517
std::string data;
6506-
if (detail::make_multipart_ranges_data(req, res, boundary, content_type,
6507-
data)) {
6508-
res.body.swap(data);
6509-
} else {
6510-
res.body.clear();
6511-
res.status = StatusCode::RangeNotSatisfiable_416;
6512-
}
6518+
detail::make_multipart_ranges_data(req, res, boundary, content_type,
6519+
res.body.size(), data);
6520+
res.body.swap(data);
65136521
}
65146522

65156523
if (type != detail::EncodingType::None) {
@@ -6685,13 +6693,20 @@ Server::process_request(Stream &strm, bool close_connection,
66856693
}
66866694
}
66876695
#endif
6688-
66896696
if (routed) {
6690-
if (res.status == -1) {
6691-
res.status = req.ranges.empty() ? StatusCode::OK_200
6692-
: StatusCode::PartialContent_206;
6697+
if (detail::normalize_ranges(req, res)) {
6698+
if (res.status == -1) {
6699+
res.status = req.ranges.empty() ? StatusCode::OK_200
6700+
: StatusCode::PartialContent_206;
6701+
}
6702+
return write_response_with_content(strm, close_connection, req, res);
6703+
} else {
6704+
res.body.clear();
6705+
res.content_length_ = 0;
6706+
res.content_provider_ = nullptr;
6707+
res.status = StatusCode::RangeNotSatisfiable_416;
6708+
return write_response(strm, close_connection, req, res);
66936709
}
6694-
return write_response_with_content(strm, close_connection, req, res);
66956710
} else {
66966711
if (res.status == -1) { res.status = StatusCode::NotFound_404; }
66976712
return write_response(strm, close_connection, req, res);

test/test.cc

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1831,7 +1831,7 @@ class ServerTest : public ::testing::Test {
18311831
});
18321832
})
18331833
.Get("/streamed-with-range",
1834-
[&](const Request & /*req*/, Response &res) {
1834+
[&](const Request &req, Response &res) {
18351835
auto data = new std::string("abcdefg");
18361836
res.set_content_provider(
18371837
data->size(), "text/plain",
@@ -1845,8 +1845,8 @@ class ServerTest : public ::testing::Test {
18451845
EXPECT_TRUE(ret);
18461846
return true;
18471847
},
1848-
[data](bool success) {
1849-
EXPECT_TRUE(success);
1848+
[data, &req](bool success) {
1849+
EXPECT_EQ(success, !req.has_param("error"));
18501850
delete data;
18511851
});
18521852
})
@@ -2957,13 +2957,12 @@ TEST_F(ServerTest, GetStreamedWithRangeSuffix1) {
29572957
}
29582958

29592959
TEST_F(ServerTest, GetStreamedWithRangeSuffix2) {
2960-
auto res = cli_.Get("/streamed-with-range", {{"Range", "bytes=-9999"}});
2960+
auto res = cli_.Get("/streamed-with-range?error", {{"Range", "bytes=-9999"}});
29612961
ASSERT_TRUE(res);
2962-
EXPECT_EQ(StatusCode::PartialContent_206, res->status);
2963-
EXPECT_EQ("7", res->get_header_value("Content-Length"));
2964-
EXPECT_EQ(true, res->has_header("Content-Range"));
2965-
EXPECT_EQ("bytes 0-6/7", res->get_header_value("Content-Range"));
2966-
EXPECT_EQ(std::string("abcdefg"), res->body);
2962+
EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status);
2963+
EXPECT_EQ("0", res->get_header_value("Content-Length"));
2964+
EXPECT_EQ(false, res->has_header("Content-Range"));
2965+
EXPECT_EQ(0, res->body.size());
29672966
}
29682967

29692968
TEST_F(ServerTest, GetStreamedWithRangeError) {
@@ -2972,16 +2971,18 @@ TEST_F(ServerTest, GetStreamedWithRangeError) {
29722971
"92233720368547758079223372036854775807"}});
29732972
ASSERT_TRUE(res);
29742973
EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status);
2974+
EXPECT_EQ("0", res->get_header_value("Content-Length"));
2975+
EXPECT_EQ(false, res->has_header("Content-Range"));
2976+
EXPECT_EQ(0, res->body.size());
29752977
}
29762978

29772979
TEST_F(ServerTest, GetRangeWithMaxLongLength) {
29782980
auto res =
29792981
cli_.Get("/with-range", {{"Range", "bytes=0-9223372036854775807"}});
2980-
EXPECT_EQ(StatusCode::PartialContent_206, res->status);
2981-
EXPECT_EQ("7", res->get_header_value("Content-Length"));
2982-
EXPECT_EQ("bytes 0-6/7", res->get_header_value("Content-Range"));
2983-
EXPECT_EQ(true, res->has_header("Content-Range"));
2984-
EXPECT_EQ(std::string("abcdefg"), res->body);
2982+
EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status);
2983+
EXPECT_EQ("0", res->get_header_value("Content-Length"));
2984+
EXPECT_EQ(false, res->has_header("Content-Range"));
2985+
EXPECT_EQ(0, res->body.size());
29852986
}
29862987

29872988
TEST_F(ServerTest, GetStreamedWithRangeMultipart) {

0 commit comments

Comments
 (0)