Skip to content

Commit 0411229

Browse files
committed
Fix problem with invalid range
1 parent 401de60 commit 0411229

File tree

2 files changed

+171
-146
lines changed

2 files changed

+171
-146
lines changed

httplib.h

Lines changed: 152 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -676,8 +676,14 @@ class Server {
676676
const HandlersForContentReader &handlers);
677677

678678
bool parse_request_line(const char *s, Request &req);
679+
void apply_ranges(const Request &req, Response &res,
680+
std::string &content_type, std::string &boundary);
679681
bool write_response(Stream &strm, bool close_connection, const Request &req,
680682
Response &res);
683+
bool write_response_with_content(Stream &strm, bool close_connection,
684+
const Request &req, Response &res,
685+
std::string &content_type,
686+
std::string &boundary);
681687
bool write_content_with_provider(Stream &strm, const Request &req,
682688
Response &res, const std::string &boundary,
683689
const std::string &content_type);
@@ -3171,9 +3177,7 @@ get_range_offset_and_length(const Request &req, size_t content_length,
31713177
r.second = slen - 1;
31723178
}
31733179

3174-
if (r.second == -1) {
3175-
r.second = slen - 1;
3176-
}
3180+
if (r.second == -1) { r.second = slen - 1; }
31773181
return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);
31783182
}
31793183

@@ -3223,21 +3227,21 @@ bool process_multipart_ranges_data(const Request &req, Response &res,
32233227
return true;
32243228
}
32253229

3226-
inline std::string make_multipart_ranges_data(const Request &req, Response &res,
3227-
const std::string &boundary,
3228-
const std::string &content_type) {
3229-
std::string data;
3230-
3231-
process_multipart_ranges_data(
3230+
inline bool make_multipart_ranges_data(const Request &req, Response &res,
3231+
const std::string &boundary,
3232+
const std::string &content_type,
3233+
std::string &data) {
3234+
return process_multipart_ranges_data(
32323235
req, res, boundary, content_type,
32333236
[&](const std::string &token) { data += token; },
32343237
[&](const char *token) { data += token; },
32353238
[&](size_t offset, size_t length) {
3236-
data += res.body.substr(offset, length);
3237-
return true;
3239+
if (offset < res.body.size()) {
3240+
data += res.body.substr(offset, length);
3241+
return true;
3242+
}
3243+
return false;
32383244
});
3239-
3240-
return data;
32413245
}
32423246

32433247
inline size_t
@@ -4006,18 +4010,19 @@ inline bool Server::parse_request_line(const char *s, Request &req) {
40064010

40074011
inline bool Server::write_response(Stream &strm, bool close_connection,
40084012
const Request &req, Response &res) {
4013+
std::string content_type;
4014+
std::string boundary;
4015+
return write_response_with_content(strm, close_connection, req, res,
4016+
content_type, boundary);
4017+
}
4018+
4019+
inline bool Server::write_response_with_content(
4020+
Stream &strm, bool close_connection, const Request &req, Response &res,
4021+
std::string &content_type, std::string &boundary) {
40094022
assert(res.status != -1);
40104023

40114024
if (400 <= res.status && error_handler_) { error_handler_(req, res); }
40124025

4013-
detail::BufferStream bstrm;
4014-
4015-
// Response line
4016-
if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status,
4017-
detail::status_message(res.status))) {
4018-
return false;
4019-
}
4020-
40214026
// Headers
40224027
if (close_connection || req.get_header_value("Connection") == "close") {
40234028
res.set_header("Connection", "close");
@@ -4033,109 +4038,21 @@ inline bool Server::write_response(Stream &strm, bool close_connection,
40334038
res.set_header("Content-Type", "text/plain");
40344039
}
40354040

4036-
if (!res.has_header("Accept-Ranges") && req.method == "HEAD") {
4037-
res.set_header("Accept-Ranges", "bytes");
4041+
if (!res.has_header("Content-Length") && res.body.empty() &&
4042+
!res.content_length_ && !res.content_provider_) {
4043+
res.set_header("Content-Length", "0");
40384044
}
40394045

4040-
std::string content_type;
4041-
std::string boundary;
4042-
4043-
if (req.ranges.size() > 1) {
4044-
boundary = detail::make_multipart_data_boundary();
4045-
4046-
auto it = res.headers.find("Content-Type");
4047-
if (it != res.headers.end()) {
4048-
content_type = it->second;
4049-
res.headers.erase(it);
4050-
}
4051-
4052-
res.headers.emplace("Content-Type",
4053-
"multipart/byteranges; boundary=" + boundary);
4046+
if (!res.has_header("Accept-Ranges") && req.method == "HEAD") {
4047+
res.set_header("Accept-Ranges", "bytes");
40544048
}
40554049

4056-
auto type = detail::encoding_type(req, res);
4057-
4058-
if (res.body.empty()) {
4059-
if (res.content_length_ > 0) {
4060-
size_t length = 0;
4061-
if (req.ranges.empty()) {
4062-
length = res.content_length_;
4063-
} else if (req.ranges.size() == 1) {
4064-
auto offsets =
4065-
detail::get_range_offset_and_length(req, res.content_length_, 0);
4066-
auto offset = offsets.first;
4067-
length = offsets.second;
4068-
auto content_range = detail::make_content_range_header_field(
4069-
offset, length, res.content_length_);
4070-
res.set_header("Content-Range", content_range);
4071-
} else {
4072-
length = detail::get_multipart_ranges_data_length(req, res, boundary,
4073-
content_type);
4074-
}
4075-
res.set_header("Content-Length", std::to_string(length));
4076-
} else {
4077-
if (res.content_provider_) {
4078-
if (res.is_chunked_content_provider) {
4079-
res.set_header("Transfer-Encoding", "chunked");
4080-
if (type == detail::EncodingType::Gzip) {
4081-
res.set_header("Content-Encoding", "gzip");
4082-
} else if (type == detail::EncodingType::Brotli) {
4083-
res.set_header("Content-Encoding", "br");
4084-
}
4085-
}
4086-
} else {
4087-
res.set_header("Content-Length", "0");
4088-
}
4089-
}
4090-
} else {
4091-
if (req.ranges.empty()) {
4092-
;
4093-
} else if (req.ranges.size() == 1) {
4094-
auto offsets =
4095-
detail::get_range_offset_and_length(req, res.body.size(), 0);
4096-
auto offset = offsets.first;
4097-
auto length = offsets.second;
4098-
auto content_range = detail::make_content_range_header_field(
4099-
offset, length, res.body.size());
4100-
res.set_header("Content-Range", content_range);
4101-
res.body = res.body.substr(offset, length);
4102-
} else {
4103-
res.body =
4104-
detail::make_multipart_ranges_data(req, res, boundary, content_type);
4105-
}
4106-
4107-
if (type != detail::EncodingType::None) {
4108-
std::unique_ptr<detail::compressor> compressor;
4109-
4110-
if (type == detail::EncodingType::Gzip) {
4111-
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4112-
compressor = detail::make_unique<detail::gzip_compressor>();
4113-
res.set_header("Content-Encoding", "gzip");
4114-
#endif
4115-
} else if (type == detail::EncodingType::Brotli) {
4116-
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4117-
compressor = detail::make_unique<detail::brotli_compressor>();
4118-
res.set_header("Content-Encoding", "brotli");
4119-
#endif
4120-
}
4121-
4122-
if (compressor) {
4123-
std::string compressed;
4124-
4125-
if (!compressor->compress(res.body.data(), res.body.size(), true,
4126-
[&](const char *data, size_t data_len) {
4127-
compressed.append(data, data_len);
4128-
return true;
4129-
})) {
4130-
return false;
4131-
}
4132-
4133-
res.body.swap(compressed);
4134-
}
4135-
}
4050+
detail::BufferStream bstrm;
41364051

4137-
auto length = std::to_string(res.body.size());
4138-
res.set_header("Content-Length", length);
4052+
// Response line
4053+
if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status,
4054+
detail::status_message(res.status))) {
4055+
return false;
41394056
}
41404057

41414058
if (!detail::write_headers(bstrm, res, Headers())) { return false; }
@@ -4535,6 +4452,116 @@ inline bool Server::dispatch_request(Request &req, Response &res,
45354452
return false;
45364453
}
45374454

4455+
inline void Server::apply_ranges(const Request &req, Response &res,
4456+
std::string &content_type,
4457+
std::string &boundary) {
4458+
if (req.ranges.size() > 1) {
4459+
boundary = detail::make_multipart_data_boundary();
4460+
4461+
auto it = res.headers.find("Content-Type");
4462+
if (it != res.headers.end()) {
4463+
content_type = it->second;
4464+
res.headers.erase(it);
4465+
}
4466+
4467+
res.headers.emplace("Content-Type",
4468+
"multipart/byteranges; boundary=" + boundary);
4469+
}
4470+
4471+
auto type = detail::encoding_type(req, res);
4472+
4473+
if (res.body.empty()) {
4474+
if (res.content_length_ > 0) {
4475+
size_t length = 0;
4476+
if (req.ranges.empty()) {
4477+
length = res.content_length_;
4478+
} else if (req.ranges.size() == 1) {
4479+
auto offsets =
4480+
detail::get_range_offset_and_length(req, res.content_length_, 0);
4481+
auto offset = offsets.first;
4482+
length = offsets.second;
4483+
auto content_range = detail::make_content_range_header_field(
4484+
offset, length, res.content_length_);
4485+
res.set_header("Content-Range", content_range);
4486+
} else {
4487+
length = detail::get_multipart_ranges_data_length(req, res, boundary,
4488+
content_type);
4489+
}
4490+
res.set_header("Content-Length", std::to_string(length));
4491+
} else {
4492+
if (res.content_provider_) {
4493+
if (res.is_chunked_content_provider) {
4494+
res.set_header("Transfer-Encoding", "chunked");
4495+
if (type == detail::EncodingType::Gzip) {
4496+
res.set_header("Content-Encoding", "gzip");
4497+
} else if (type == detail::EncodingType::Brotli) {
4498+
res.set_header("Content-Encoding", "br");
4499+
}
4500+
}
4501+
}
4502+
}
4503+
} else {
4504+
if (req.ranges.empty()) {
4505+
;
4506+
} else if (req.ranges.size() == 1) {
4507+
auto offsets =
4508+
detail::get_range_offset_and_length(req, res.body.size(), 0);
4509+
auto offset = offsets.first;
4510+
auto length = offsets.second;
4511+
auto content_range = detail::make_content_range_header_field(
4512+
offset, length, res.body.size());
4513+
res.set_header("Content-Range", content_range);
4514+
if (offset < res.body.size()) {
4515+
res.body = res.body.substr(offset, length);
4516+
} else {
4517+
res.body.clear();
4518+
res.status = 416;
4519+
}
4520+
} else {
4521+
std::string data;
4522+
if (detail::make_multipart_ranges_data(req, res, boundary, content_type,
4523+
data)) {
4524+
res.body.swap(data);
4525+
} else {
4526+
res.body.clear();
4527+
res.status = 416;
4528+
}
4529+
}
4530+
4531+
if (type != detail::EncodingType::None) {
4532+
std::unique_ptr<detail::compressor> compressor;
4533+
std::string content_encoding;
4534+
4535+
if (type == detail::EncodingType::Gzip) {
4536+
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4537+
compressor = detail::make_unique<detail::gzip_compressor>();
4538+
content_encoding = "gzip";
4539+
#endif
4540+
} else if (type == detail::EncodingType::Brotli) {
4541+
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4542+
compressor = detail::make_unique<detail::brotli_compressor>();
4543+
content_encoding = "brotli";
4544+
#endif
4545+
}
4546+
4547+
if (compressor) {
4548+
std::string compressed;
4549+
if (compressor->compress(res.body.data(), res.body.size(), true,
4550+
[&](const char *data, size_t data_len) {
4551+
compressed.append(data, data_len);
4552+
return true;
4553+
})) {
4554+
res.body.swap(compressed);
4555+
res.set_header("Content-Encoding", content_encoding);
4556+
}
4557+
}
4558+
}
4559+
4560+
auto length = std::to_string(res.body.size());
4561+
res.set_header("Content-Length", length);
4562+
}
4563+
}
4564+
45384565
inline bool Server::dispatch_request_for_content_reader(
45394566
Request &req, Response &res, ContentReader content_reader,
45404567
const HandlersForContentReader &handlers) {
@@ -4626,7 +4653,12 @@ Server::process_request(Stream &strm, bool close_connection,
46264653
if (res.status == -1) { res.status = 404; }
46274654
}
46284655

4629-
return write_response(strm, close_connection, req, res);
4656+
std::string content_type;
4657+
std::string boundary;
4658+
apply_ranges(req, res, content_type, boundary);
4659+
4660+
return write_response_with_content(strm, close_connection, req, res,
4661+
content_type, boundary);
46304662
}
46314663

46324664
inline bool Server::is_valid() const { return true; }

0 commit comments

Comments
 (0)