1
0
mirror of synced 2025-04-19 00:24:02 +03:00

Changed to return 416 for a request with an invalid range

This commit is contained in:
yhirose 2024-01-28 08:13:19 -05:00
parent 5f0f73fad9
commit fceada9ef4
2 changed files with 95 additions and 79 deletions

145
httplib.h
View File

@ -4720,29 +4720,47 @@ serialize_multipart_formdata(const MultipartFormDataItems &items,
return body; return body;
} }
inline bool normalize_ranges(Request &req, Response &res) {
ssize_t len = static_cast<ssize_t>(res.content_length_ ? res.content_length_
: res.body.size());
if (!req.ranges.empty()) {
for (auto &r : req.ranges) {
auto &st = r.first;
auto &ed = r.second;
if (st == -1 && ed == -1) {
st = 0;
ed = len;
}
if (st == -1) {
st = len - ed;
ed = len - 1;
}
if (ed == -1) { ed = len - 1; }
if (!(0 <= st && st <= ed && ed <= len - 1)) { return false; }
}
}
return true;
}
inline std::pair<size_t, size_t> inline std::pair<size_t, size_t>
get_range_offset_and_length(Range range, size_t content_length) { get_range_offset_and_length(Range r, size_t content_length) {
if (range.first == -1 && range.second == -1) { assert(r.first != -1 && r.second != -1);
return std::make_pair(0, content_length); assert(0 <= r.first && r.first < static_cast<ssize_t>(content_length));
} assert(r.first <= r.second &&
r.second < static_cast<ssize_t>(content_length));
auto slen = static_cast<ssize_t>(content_length); return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);
if (range.first == -1) {
range.first = (std::max)(static_cast<ssize_t>(0), slen - range.second);
range.second = slen - 1;
}
if (range.second == -1) { range.second = slen - 1; }
return std::make_pair(range.first,
static_cast<size_t>(range.second - range.first) + 1);
} }
inline std::string make_content_range_header_field( inline std::string make_content_range_header_field(
const std::pair<size_t, size_t> &offset_and_length, size_t content_length) { const std::pair<size_t, size_t> &offset_and_length, size_t content_length) {
auto st = offset_and_length.first; auto st = offset_and_length.first;
auto ed = (std::min)(st + offset_and_length.second - 1, content_length - 1); auto ed = st + offset_and_length.second - 1;
std::string field = "bytes "; std::string field = "bytes ";
field += std::to_string(st); field += std::to_string(st);
@ -4754,11 +4772,11 @@ inline std::string make_content_range_header_field(
} }
template <typename SToken, typename CToken, typename Content> template <typename SToken, typename CToken, typename Content>
bool process_multipart_ranges_data(const Request &req, Response &res, bool process_multipart_ranges_data(const Request &req,
const std::string &boundary, const std::string &boundary,
const std::string &content_type, const std::string &content_type,
SToken stoken, CToken ctoken, size_t content_length, SToken stoken,
Content content) { CToken ctoken, Content content) {
for (size_t i = 0; i < req.ranges.size(); i++) { for (size_t i = 0; i < req.ranges.size(); i++) {
ctoken("--"); ctoken("--");
stoken(boundary); stoken(boundary);
@ -4770,11 +4788,10 @@ bool process_multipart_ranges_data(const Request &req, Response &res,
} }
auto offset_and_length = auto offset_and_length =
get_range_offset_and_length(req.ranges[i], res.content_length_); get_range_offset_and_length(req.ranges[i], content_length);
ctoken("Content-Range: "); ctoken("Content-Range: ");
stoken(make_content_range_header_field(offset_and_length, stoken(make_content_range_header_field(offset_and_length, content_length));
res.content_length_));
ctoken("\r\n"); ctoken("\r\n");
ctoken("\r\n"); ctoken("\r\n");
@ -4791,31 +4808,30 @@ bool process_multipart_ranges_data(const Request &req, Response &res,
return true; return true;
} }
inline bool make_multipart_ranges_data(const Request &req, Response &res, inline void make_multipart_ranges_data(const Request &req, Response &res,
const std::string &boundary, const std::string &boundary,
const std::string &content_type, const std::string &content_type,
size_t content_length,
std::string &data) { std::string &data) {
return process_multipart_ranges_data( process_multipart_ranges_data(
req, res, boundary, content_type, req, boundary, content_type, content_length,
[&](const std::string &token) { data += token; }, [&](const std::string &token) { data += token; },
[&](const std::string &token) { data += token; }, [&](const std::string &token) { data += token; },
[&](size_t offset, size_t length) { [&](size_t offset, size_t length) {
if (offset < res.body.size()) { assert(offset + length <= content_length);
data += res.body.substr(offset, length); data += res.body.substr(offset, length);
return true; return true;
}
return false;
}); });
} }
inline size_t inline size_t get_multipart_ranges_data_length(const Request &req,
get_multipart_ranges_data_length(const Request &req, Response &res, const std::string &boundary,
const std::string &boundary, const std::string &content_type,
const std::string &content_type) { size_t content_length) {
size_t data_length = 0; size_t data_length = 0;
process_multipart_ranges_data( process_multipart_ranges_data(
req, res, boundary, content_type, req, boundary, content_type, content_length,
[&](const std::string &token) { data_length += token.size(); }, [&](const std::string &token) { data_length += token.size(); },
[&](const std::string &token) { data_length += token.size(); }, [&](const std::string &token) { data_length += token.size(); },
[&](size_t /*offset*/, size_t length) { [&](size_t /*offset*/, size_t length) {
@ -4827,13 +4843,13 @@ get_multipart_ranges_data_length(const Request &req, Response &res,
} }
template <typename T> template <typename T>
inline bool write_multipart_ranges_data(Stream &strm, const Request &req, inline bool
Response &res, write_multipart_ranges_data(Stream &strm, const Request &req, Response &res,
const std::string &boundary, const std::string &boundary,
const std::string &content_type, const std::string &content_type,
const T &is_shutting_down) { size_t content_length, const T &is_shutting_down) {
return process_multipart_ranges_data( return process_multipart_ranges_data(
req, res, boundary, content_type, req, boundary, content_type, content_length,
[&](const std::string &token) { strm.write(token); }, [&](const std::string &token) { strm.write(token); },
[&](const std::string &token) { strm.write(token); }, [&](const std::string &token) { strm.write(token); },
[&](size_t offset, size_t length) { [&](size_t offset, size_t length) {
@ -6012,7 +6028,6 @@ inline bool Server::write_response_core(Stream &strm, bool close_connection,
if (write_content_with_provider(strm, req, res, boundary, content_type)) { if (write_content_with_provider(strm, req, res, boundary, content_type)) {
res.content_provider_success_ = true; res.content_provider_success_ = true;
} else { } else {
res.content_provider_success_ = false;
ret = false; ret = false;
} }
} }
@ -6045,7 +6060,8 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
offset_and_length.second, is_shutting_down); offset_and_length.second, is_shutting_down);
} else { } else {
return detail::write_multipart_ranges_data( return detail::write_multipart_ranges_data(
strm, req, res, boundary, content_type, is_shutting_down); strm, req, res, boundary, content_type, res.content_length_,
is_shutting_down);
} }
} else { } else {
if (res.is_chunked_content_provider_) { if (res.is_chunked_content_provider_) {
@ -6437,14 +6453,14 @@ inline void Server::apply_ranges(const Request &req, Response &res,
std::string &content_type, std::string &content_type,
std::string &boundary) const { std::string &boundary) const {
if (req.ranges.size() > 1) { if (req.ranges.size() > 1) {
boundary = detail::make_multipart_data_boundary();
auto it = res.headers.find("Content-Type"); auto it = res.headers.find("Content-Type");
if (it != res.headers.end()) { if (it != res.headers.end()) {
content_type = it->second; content_type = it->second;
res.headers.erase(it); res.headers.erase(it);
} }
boundary = detail::make_multipart_data_boundary();
res.set_header("Content-Type", res.set_header("Content-Type",
"multipart/byteranges; boundary=" + boundary); "multipart/byteranges; boundary=" + boundary);
} }
@ -6466,8 +6482,8 @@ inline void Server::apply_ranges(const Request &req, Response &res,
offset_and_length, res.content_length_); offset_and_length, res.content_length_);
res.set_header("Content-Range", content_range); res.set_header("Content-Range", content_range);
} else { } else {
length = detail::get_multipart_ranges_data_length(req, res, boundary, length = detail::get_multipart_ranges_data_length(
content_type); req, boundary, content_type, res.content_length_);
} }
res.set_header("Content-Length", std::to_string(length)); res.set_header("Content-Length", std::to_string(length));
} else { } else {
@ -6495,21 +6511,13 @@ inline void Server::apply_ranges(const Request &req, Response &res,
offset_and_length, res.body.size()); offset_and_length, res.body.size());
res.set_header("Content-Range", content_range); res.set_header("Content-Range", content_range);
if (offset < res.body.size()) { assert(offset + length <= res.body.size());
res.body = res.body.substr(offset, length); res.body = res.body.substr(offset, length);
} else {
res.body.clear();
res.status = StatusCode::RangeNotSatisfiable_416;
}
} else { } else {
std::string data; std::string data;
if (detail::make_multipart_ranges_data(req, res, boundary, content_type, detail::make_multipart_ranges_data(req, res, boundary, content_type,
data)) { res.body.size(), data);
res.body.swap(data); res.body.swap(data);
} else {
res.body.clear();
res.status = StatusCode::RangeNotSatisfiable_416;
}
} }
if (type != detail::EncodingType::None) { if (type != detail::EncodingType::None) {
@ -6685,13 +6693,20 @@ Server::process_request(Stream &strm, bool close_connection,
} }
} }
#endif #endif
if (routed) { if (routed) {
if (res.status == -1) { if (detail::normalize_ranges(req, res)) {
res.status = req.ranges.empty() ? StatusCode::OK_200 if (res.status == -1) {
: StatusCode::PartialContent_206; res.status = req.ranges.empty() ? StatusCode::OK_200
: StatusCode::PartialContent_206;
}
return write_response_with_content(strm, close_connection, req, res);
} else {
res.body.clear();
res.content_length_ = 0;
res.content_provider_ = nullptr;
res.status = StatusCode::RangeNotSatisfiable_416;
return write_response(strm, close_connection, req, res);
} }
return write_response_with_content(strm, close_connection, req, res);
} else { } else {
if (res.status == -1) { res.status = StatusCode::NotFound_404; } if (res.status == -1) { res.status = StatusCode::NotFound_404; }
return write_response(strm, close_connection, req, res); return write_response(strm, close_connection, req, res);

View File

@ -1831,7 +1831,7 @@ protected:
}); });
}) })
.Get("/streamed-with-range", .Get("/streamed-with-range",
[&](const Request & /*req*/, Response &res) { [&](const Request &req, Response &res) {
auto data = new std::string("abcdefg"); auto data = new std::string("abcdefg");
res.set_content_provider( res.set_content_provider(
data->size(), "text/plain", data->size(), "text/plain",
@ -1845,8 +1845,8 @@ protected:
EXPECT_TRUE(ret); EXPECT_TRUE(ret);
return true; return true;
}, },
[data](bool success) { [data, &req](bool success) {
EXPECT_TRUE(success); EXPECT_EQ(success, !req.has_param("error"));
delete data; delete data;
}); });
}) })
@ -2957,13 +2957,12 @@ TEST_F(ServerTest, GetStreamedWithRangeSuffix1) {
} }
TEST_F(ServerTest, GetStreamedWithRangeSuffix2) { TEST_F(ServerTest, GetStreamedWithRangeSuffix2) {
auto res = cli_.Get("/streamed-with-range", {{"Range", "bytes=-9999"}}); auto res = cli_.Get("/streamed-with-range?error", {{"Range", "bytes=-9999"}});
ASSERT_TRUE(res); ASSERT_TRUE(res);
EXPECT_EQ(StatusCode::PartialContent_206, res->status); EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status);
EXPECT_EQ("7", res->get_header_value("Content-Length")); EXPECT_EQ("0", res->get_header_value("Content-Length"));
EXPECT_EQ(true, res->has_header("Content-Range")); EXPECT_EQ(false, res->has_header("Content-Range"));
EXPECT_EQ("bytes 0-6/7", res->get_header_value("Content-Range")); EXPECT_EQ(0, res->body.size());
EXPECT_EQ(std::string("abcdefg"), res->body);
} }
TEST_F(ServerTest, GetStreamedWithRangeError) { TEST_F(ServerTest, GetStreamedWithRangeError) {
@ -2972,16 +2971,18 @@ TEST_F(ServerTest, GetStreamedWithRangeError) {
"92233720368547758079223372036854775807"}}); "92233720368547758079223372036854775807"}});
ASSERT_TRUE(res); ASSERT_TRUE(res);
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(false, res->has_header("Content-Range"));
EXPECT_EQ(0, res->body.size());
} }
TEST_F(ServerTest, GetRangeWithMaxLongLength) { TEST_F(ServerTest, GetRangeWithMaxLongLength) {
auto res = auto res =
cli_.Get("/with-range", {{"Range", "bytes=0-9223372036854775807"}}); cli_.Get("/with-range", {{"Range", "bytes=0-9223372036854775807"}});
EXPECT_EQ(StatusCode::PartialContent_206, res->status); EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status);
EXPECT_EQ("7", res->get_header_value("Content-Length")); EXPECT_EQ("0", res->get_header_value("Content-Length"));
EXPECT_EQ("bytes 0-6/7", res->get_header_value("Content-Range")); EXPECT_EQ(false, res->has_header("Content-Range"));
EXPECT_EQ(true, res->has_header("Content-Range")); EXPECT_EQ(0, res->body.size());
EXPECT_EQ(std::string("abcdefg"), res->body);
} }
TEST_F(ServerTest, GetStreamedWithRangeMultipart) { TEST_F(ServerTest, GetStreamedWithRangeMultipart) {