mirror of
https://github.com/yhirose/cpp-httplib
synced 2026-06-08 18:30:49 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 80c0cc445e | |||
| 762024b890 | |||
| 82a90a2325 | |||
| b7cac4f4b8 | |||
| e323374d2a | |||
| ffc294d37e | |||
| fceada9ef4 | |||
| 5f0f73fad9 |
@@ -866,7 +866,7 @@ NOTE: Windows 8 or lower, Visual Studio 2013 or lower, and Cygwin and MSYS2 incl
|
||||
License
|
||||
-------
|
||||
|
||||
MIT license (© 2023 Yuji Hirose)
|
||||
MIT license (© 2024 Yuji Hirose)
|
||||
|
||||
Special Thanks To
|
||||
-----------------
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
//
|
||||
// httplib.h
|
||||
//
|
||||
// Copyright (c) 2023 Yuji Hirose. All rights reserved.
|
||||
// Copyright (c) 2024 Yuji Hirose. All rights reserved.
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#ifndef CPPHTTPLIB_HTTPLIB_H
|
||||
#define CPPHTTPLIB_HTTPLIB_H
|
||||
|
||||
#define CPPHTTPLIB_VERSION "0.15.0"
|
||||
#define CPPHTTPLIB_VERSION "0.15.2"
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
@@ -82,6 +82,10 @@
|
||||
#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
|
||||
#endif
|
||||
|
||||
#ifndef CPPHTTPLIB_RANGE_MAX_COUNT
|
||||
#define CPPHTTPLIB_RANGE_MAX_COUNT 1024
|
||||
#endif
|
||||
|
||||
#ifndef CPPHTTPLIB_TCP_NODELAY
|
||||
#define CPPHTTPLIB_TCP_NODELAY false
|
||||
#endif
|
||||
@@ -2065,7 +2069,7 @@ void hosted_at(const std::string &hostname, std::vector<std::string> &addrs);
|
||||
|
||||
std::string append_query_params(const std::string &path, const Params ¶ms);
|
||||
|
||||
std::pair<std::string, std::string> make_range_header(Ranges ranges);
|
||||
std::pair<std::string, std::string> make_range_header(const Ranges &ranges);
|
||||
|
||||
std::pair<std::string, std::string>
|
||||
make_basic_authentication_header(const std::string &username,
|
||||
@@ -2413,7 +2417,11 @@ inline bool is_valid_path(const std::string &path) {
|
||||
// Read component
|
||||
auto beg = i;
|
||||
while (i < path.size() && path[i] != '/') {
|
||||
if (path[i] == '\0') { return false; }
|
||||
if (path[i] == '\0') {
|
||||
return false;
|
||||
} else if (path[i] == '\\') {
|
||||
return false;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -2571,7 +2579,7 @@ inline std::string trim_double_quotes_copy(const std::string &s) {
|
||||
|
||||
inline void split(const char *b, const char *e, char d,
|
||||
std::function<void(const char *, const char *)> fn) {
|
||||
return split(b, e, d, (std::numeric_limits<size_t>::max)(), fn);
|
||||
return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));
|
||||
}
|
||||
|
||||
inline void split(const char *b, const char *e, char d, size_t m,
|
||||
@@ -4720,37 +4728,75 @@ serialize_multipart_formdata(const MultipartFormDataItems &items,
|
||||
return body;
|
||||
}
|
||||
|
||||
inline std::pair<size_t, size_t>
|
||||
get_range_offset_and_length(Range range, size_t content_length) {
|
||||
if (range.first == -1 && range.second == -1) {
|
||||
return std::make_pair(0, content_length);
|
||||
inline bool normalize_ranges(Request &req, Response &res) {
|
||||
ssize_t contant_len = static_cast<ssize_t>(
|
||||
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()) {
|
||||
// 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) {
|
||||
auto &first_pos = r.first;
|
||||
auto &last_pos = r.second;
|
||||
|
||||
if (first_pos == -1 && last_pos == -1) {
|
||||
first_pos = 0;
|
||||
last_pos = contant_len;
|
||||
}
|
||||
|
||||
if (first_pos == -1) {
|
||||
first_pos = contant_len - last_pos;
|
||||
last_pos = contant_len - 1;
|
||||
}
|
||||
|
||||
if (last_pos == -1) { last_pos = contant_len - 1; }
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
auto slen = static_cast<ssize_t>(content_length);
|
||||
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline std::pair<size_t, size_t>
|
||||
get_range_offset_and_length(const Request &req, size_t content_length,
|
||||
size_t index) {
|
||||
return get_range_offset_and_length(req.ranges[index], content_length);
|
||||
get_range_offset_and_length(Range r, size_t content_length) {
|
||||
assert(r.first != -1 && r.second != -1);
|
||||
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));
|
||||
|
||||
return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);
|
||||
}
|
||||
|
||||
inline std::string
|
||||
make_content_range_header_field(const std::pair<ssize_t, ssize_t> &range,
|
||||
size_t content_length) {
|
||||
|
||||
auto ret = get_range_offset_and_length(range, content_length);
|
||||
auto st = ret.first;
|
||||
auto ed = (std::min)(st + ret.second - 1, content_length - 1);
|
||||
inline std::string make_content_range_header_field(
|
||||
const std::pair<size_t, size_t> &offset_and_length, size_t content_length) {
|
||||
auto st = offset_and_length.first;
|
||||
auto ed = st + offset_and_length.second - 1;
|
||||
|
||||
std::string field = "bytes ";
|
||||
field += std::to_string(st);
|
||||
@@ -4762,11 +4808,11 @@ make_content_range_header_field(const std::pair<ssize_t, ssize_t> &range,
|
||||
}
|
||||
|
||||
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 &content_type,
|
||||
SToken stoken, CToken ctoken,
|
||||
Content content) {
|
||||
size_t content_length, SToken stoken,
|
||||
CToken ctoken, Content content) {
|
||||
for (size_t i = 0; i < req.ranges.size(); i++) {
|
||||
ctoken("--");
|
||||
stoken(boundary);
|
||||
@@ -4777,16 +4823,17 @@ bool process_multipart_ranges_data(const Request &req, Response &res,
|
||||
ctoken("\r\n");
|
||||
}
|
||||
|
||||
auto offset_and_length =
|
||||
get_range_offset_and_length(req.ranges[i], content_length);
|
||||
|
||||
ctoken("Content-Range: ");
|
||||
const auto &range = req.ranges[i];
|
||||
stoken(make_content_range_header_field(range, res.content_length_));
|
||||
stoken(make_content_range_header_field(offset_and_length, content_length));
|
||||
ctoken("\r\n");
|
||||
ctoken("\r\n");
|
||||
|
||||
auto ret = get_range_offset_and_length(req, res.content_length_, i);
|
||||
auto offset = ret.first;
|
||||
auto length = ret.second;
|
||||
if (!content(offset, length)) { return false; }
|
||||
if (!content(offset_and_length.first, offset_and_length.second)) {
|
||||
return false;
|
||||
}
|
||||
ctoken("\r\n");
|
||||
}
|
||||
|
||||
@@ -4797,31 +4844,30 @@ bool process_multipart_ranges_data(const Request &req, Response &res,
|
||||
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 &content_type,
|
||||
size_t content_length,
|
||||
std::string &data) {
|
||||
return process_multipart_ranges_data(
|
||||
req, res, boundary, content_type,
|
||||
process_multipart_ranges_data(
|
||||
req, boundary, content_type, content_length,
|
||||
[&](const std::string &token) { data += token; },
|
||||
[&](const std::string &token) { data += token; },
|
||||
[&](size_t offset, size_t length) {
|
||||
if (offset < res.body.size()) {
|
||||
data += res.body.substr(offset, length);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
assert(offset + length <= content_length);
|
||||
data += res.body.substr(offset, length);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
inline size_t
|
||||
get_multipart_ranges_data_length(const Request &req, Response &res,
|
||||
const std::string &boundary,
|
||||
const std::string &content_type) {
|
||||
inline size_t get_multipart_ranges_data_length(const Request &req,
|
||||
const std::string &boundary,
|
||||
const std::string &content_type,
|
||||
size_t content_length) {
|
||||
size_t data_length = 0;
|
||||
|
||||
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(); },
|
||||
[&](size_t /*offset*/, size_t length) {
|
||||
@@ -4833,13 +4879,13 @@ get_multipart_ranges_data_length(const Request &req, Response &res,
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool write_multipart_ranges_data(Stream &strm, const Request &req,
|
||||
Response &res,
|
||||
const std::string &boundary,
|
||||
const std::string &content_type,
|
||||
const T &is_shutting_down) {
|
||||
inline bool
|
||||
write_multipart_ranges_data(Stream &strm, const Request &req, Response &res,
|
||||
const std::string &boundary,
|
||||
const std::string &content_type,
|
||||
size_t content_length, const T &is_shutting_down) {
|
||||
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); },
|
||||
[&](size_t offset, size_t length) {
|
||||
@@ -5198,10 +5244,11 @@ inline std::string append_query_params(const std::string &path,
|
||||
}
|
||||
|
||||
// Header utilities
|
||||
inline std::pair<std::string, std::string> make_range_header(Ranges ranges) {
|
||||
inline std::pair<std::string, std::string>
|
||||
make_range_header(const Ranges &ranges) {
|
||||
std::string field = "bytes=";
|
||||
auto i = 0;
|
||||
for (auto r : ranges) {
|
||||
for (const auto &r : ranges) {
|
||||
if (i != 0) { field += ", "; }
|
||||
if (r.first != -1) { field += std::to_string(r.first); }
|
||||
field += '-';
|
||||
@@ -5354,7 +5401,7 @@ inline void Response::set_content_provider(
|
||||
set_header("Content-Type", content_type);
|
||||
content_length_ = in_length;
|
||||
if (in_length > 0) { content_provider_ = std::move(provider); }
|
||||
content_provider_resource_releaser_ = resource_releaser;
|
||||
content_provider_resource_releaser_ = std::move(resource_releaser);
|
||||
is_chunked_content_provider_ = false;
|
||||
}
|
||||
|
||||
@@ -5364,7 +5411,7 @@ inline void Response::set_content_provider(
|
||||
set_header("Content-Type", content_type);
|
||||
content_length_ = 0;
|
||||
content_provider_ = detail::ContentProviderAdapter(std::move(provider));
|
||||
content_provider_resource_releaser_ = resource_releaser;
|
||||
content_provider_resource_releaser_ = std::move(resource_releaser);
|
||||
is_chunked_content_provider_ = false;
|
||||
}
|
||||
|
||||
@@ -5374,7 +5421,7 @@ inline void Response::set_chunked_content_provider(
|
||||
set_header("Content-Type", content_type);
|
||||
content_length_ = 0;
|
||||
content_provider_ = detail::ContentProviderAdapter(std::move(provider));
|
||||
content_provider_resource_releaser_ = resource_releaser;
|
||||
content_provider_resource_releaser_ = std::move(resource_releaser);
|
||||
is_chunked_content_provider_ = true;
|
||||
}
|
||||
|
||||
@@ -6018,7 +6065,6 @@ inline bool Server::write_response_core(Stream &strm, bool close_connection,
|
||||
if (write_content_with_provider(strm, req, res, boundary, content_type)) {
|
||||
res.content_provider_success_ = true;
|
||||
} else {
|
||||
res.content_provider_success_ = false;
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
@@ -6043,15 +6089,16 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
|
||||
return detail::write_content(strm, res.content_provider_, 0,
|
||||
res.content_length_, is_shutting_down);
|
||||
} else if (req.ranges.size() == 1) {
|
||||
auto ret =
|
||||
detail::get_range_offset_and_length(req, res.content_length_, 0);
|
||||
auto offset = ret.first;
|
||||
auto length = ret.second;
|
||||
return detail::write_content(strm, res.content_provider_, offset, length,
|
||||
is_shutting_down);
|
||||
auto offset_and_length = detail::get_range_offset_and_length(
|
||||
req.ranges[0], res.content_length_);
|
||||
|
||||
return detail::write_content(strm, res.content_provider_,
|
||||
offset_and_length.first,
|
||||
offset_and_length.second, is_shutting_down);
|
||||
} else {
|
||||
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 {
|
||||
if (res.is_chunked_content_provider_) {
|
||||
@@ -6443,14 +6490,14 @@ inline void Server::apply_ranges(const Request &req, Response &res,
|
||||
std::string &content_type,
|
||||
std::string &boundary) const {
|
||||
if (req.ranges.size() > 1) {
|
||||
boundary = detail::make_multipart_data_boundary();
|
||||
|
||||
auto it = res.headers.find("Content-Type");
|
||||
if (it != res.headers.end()) {
|
||||
content_type = it->second;
|
||||
res.headers.erase(it);
|
||||
}
|
||||
|
||||
boundary = detail::make_multipart_data_boundary();
|
||||
|
||||
res.set_header("Content-Type",
|
||||
"multipart/byteranges; boundary=" + boundary);
|
||||
}
|
||||
@@ -6463,16 +6510,17 @@ inline void Server::apply_ranges(const Request &req, Response &res,
|
||||
if (req.ranges.empty()) {
|
||||
length = res.content_length_;
|
||||
} else if (req.ranges.size() == 1) {
|
||||
auto ret =
|
||||
detail::get_range_offset_and_length(req, res.content_length_, 0);
|
||||
length = ret.second;
|
||||
auto offset_and_length = detail::get_range_offset_and_length(
|
||||
req.ranges[0], res.content_length_);
|
||||
|
||||
length = offset_and_length.second;
|
||||
|
||||
auto content_range = detail::make_content_range_header_field(
|
||||
req.ranges[0], res.content_length_);
|
||||
offset_and_length, res.content_length_);
|
||||
res.set_header("Content-Range", content_range);
|
||||
} else {
|
||||
length = detail::get_multipart_ranges_data_length(req, res, boundary,
|
||||
content_type);
|
||||
length = detail::get_multipart_ranges_data_length(
|
||||
req, boundary, content_type, res.content_length_);
|
||||
}
|
||||
res.set_header("Content-Length", std::to_string(length));
|
||||
} else {
|
||||
@@ -6491,29 +6539,22 @@ inline void Server::apply_ranges(const Request &req, Response &res,
|
||||
if (req.ranges.empty()) {
|
||||
;
|
||||
} else if (req.ranges.size() == 1) {
|
||||
auto offset_and_length =
|
||||
detail::get_range_offset_and_length(req.ranges[0], res.body.size());
|
||||
auto offset = offset_and_length.first;
|
||||
auto length = offset_and_length.second;
|
||||
|
||||
auto content_range = detail::make_content_range_header_field(
|
||||
req.ranges[0], res.body.size());
|
||||
offset_and_length, res.body.size());
|
||||
res.set_header("Content-Range", content_range);
|
||||
|
||||
auto ret = detail::get_range_offset_and_length(req, res.body.size(), 0);
|
||||
auto offset = ret.first;
|
||||
auto length = ret.second;
|
||||
|
||||
if (offset < res.body.size()) {
|
||||
res.body = res.body.substr(offset, length);
|
||||
} else {
|
||||
res.body.clear();
|
||||
res.status = StatusCode::RangeNotSatisfiable_416;
|
||||
}
|
||||
assert(offset + length <= res.body.size());
|
||||
res.body = res.body.substr(offset, length);
|
||||
} else {
|
||||
std::string data;
|
||||
if (detail::make_multipart_ranges_data(req, res, boundary, content_type,
|
||||
data)) {
|
||||
res.body.swap(data);
|
||||
} else {
|
||||
res.body.clear();
|
||||
res.status = StatusCode::RangeNotSatisfiable_416;
|
||||
}
|
||||
detail::make_multipart_ranges_data(req, res, boundary, content_type,
|
||||
res.body.size(), data);
|
||||
res.body.swap(data);
|
||||
}
|
||||
|
||||
if (type != detail::EncodingType::None) {
|
||||
@@ -6689,13 +6730,20 @@ Server::process_request(Stream &strm, bool close_connection,
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (routed) {
|
||||
if (res.status == -1) {
|
||||
res.status = req.ranges.empty() ? StatusCode::OK_200
|
||||
: StatusCode::PartialContent_206;
|
||||
if (detail::normalize_ranges(req, res)) {
|
||||
if (res.status == -1) {
|
||||
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 {
|
||||
if (res.status == -1) { res.status = StatusCode::NotFound_404; }
|
||||
return write_response(strm, close_connection, req, res);
|
||||
@@ -7601,14 +7649,15 @@ inline Result ClientImpl::Get(const std::string &path, const Params ¶ms,
|
||||
if (params.empty()) { return Get(path, headers); }
|
||||
|
||||
std::string path_with_query = append_query_params(path, params);
|
||||
return Get(path_with_query, headers, progress);
|
||||
return Get(path_with_query, headers, std::move(progress));
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Get(const std::string &path, const Params ¶ms,
|
||||
const Headers &headers,
|
||||
ContentReceiver content_receiver,
|
||||
Progress progress) {
|
||||
return Get(path, params, headers, nullptr, content_receiver, progress);
|
||||
return Get(path, params, headers, nullptr, std::move(content_receiver),
|
||||
std::move(progress));
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Get(const std::string &path, const Params ¶ms,
|
||||
@@ -7617,12 +7666,13 @@ inline Result ClientImpl::Get(const std::string &path, const Params ¶ms,
|
||||
ContentReceiver content_receiver,
|
||||
Progress progress) {
|
||||
if (params.empty()) {
|
||||
return Get(path, headers, response_handler, content_receiver, progress);
|
||||
return Get(path, headers, std::move(response_handler),
|
||||
std::move(content_receiver), std::move(progress));
|
||||
}
|
||||
|
||||
std::string path_with_query = append_query_params(path, params);
|
||||
return Get(path_with_query, headers, response_handler, content_receiver,
|
||||
progress);
|
||||
return Get(path_with_query, headers, std::move(response_handler),
|
||||
std::move(content_receiver), std::move(progress));
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Head(const std::string &path) {
|
||||
@@ -8997,19 +9047,20 @@ inline Result Client::Get(const std::string &path, const Headers &headers,
|
||||
}
|
||||
inline Result Client::Get(const std::string &path, const Params ¶ms,
|
||||
const Headers &headers, Progress progress) {
|
||||
return cli_->Get(path, params, headers, progress);
|
||||
return cli_->Get(path, params, headers, std::move(progress));
|
||||
}
|
||||
inline Result Client::Get(const std::string &path, const Params ¶ms,
|
||||
const Headers &headers,
|
||||
ContentReceiver content_receiver, Progress progress) {
|
||||
return cli_->Get(path, params, headers, content_receiver, progress);
|
||||
return cli_->Get(path, params, headers, std::move(content_receiver),
|
||||
std::move(progress));
|
||||
}
|
||||
inline Result Client::Get(const std::string &path, const Params ¶ms,
|
||||
const Headers &headers,
|
||||
ResponseHandler response_handler,
|
||||
ContentReceiver content_receiver, Progress progress) {
|
||||
return cli_->Get(path, params, headers, response_handler, content_receiver,
|
||||
progress);
|
||||
return cli_->Get(path, params, headers, std::move(response_handler),
|
||||
std::move(content_receiver), std::move(progress));
|
||||
}
|
||||
|
||||
inline Result Client::Head(const std::string &path) { return cli_->Head(path); }
|
||||
|
||||
+56
-14
@@ -1831,7 +1831,7 @@ protected:
|
||||
});
|
||||
})
|
||||
.Get("/streamed-with-range",
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
[&](const Request &req, Response &res) {
|
||||
auto data = new std::string("abcdefg");
|
||||
res.set_content_provider(
|
||||
data->size(), "text/plain",
|
||||
@@ -1845,8 +1845,8 @@ protected:
|
||||
EXPECT_TRUE(ret);
|
||||
return true;
|
||||
},
|
||||
[data](bool success) {
|
||||
EXPECT_TRUE(success);
|
||||
[data, &req](bool success) {
|
||||
EXPECT_EQ(success, !req.has_param("error"));
|
||||
delete data;
|
||||
});
|
||||
})
|
||||
@@ -2509,6 +2509,12 @@ TEST_F(ServerTest, GetMethodOutOfBaseDirMount2) {
|
||||
EXPECT_EQ(StatusCode::NotFound_404, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetMethodOutOfBaseDirMountWithBackslash) {
|
||||
auto res = cli_.Get("/mount/%2e%2e%5c/www2/dir/test.html");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(StatusCode::NotFound_404, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PostMethod303) {
|
||||
auto res = cli_.Post("/1", "body", "text/plain");
|
||||
ASSERT_TRUE(res);
|
||||
@@ -2957,13 +2963,12 @@ TEST_F(ServerTest, GetStreamedWithRangeSuffix1) {
|
||||
}
|
||||
|
||||
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);
|
||||
EXPECT_EQ(StatusCode::PartialContent_206, res->status);
|
||||
EXPECT_EQ("7", res->get_header_value("Content-Length"));
|
||||
EXPECT_EQ(true, res->has_header("Content-Range"));
|
||||
EXPECT_EQ("bytes 0-6/7", res->get_header_value("Content-Range"));
|
||||
EXPECT_EQ(std::string("abcdefg"), res->body);
|
||||
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, GetStreamedWithRangeError) {
|
||||
@@ -2972,16 +2977,18 @@ TEST_F(ServerTest, GetStreamedWithRangeError) {
|
||||
"92233720368547758079223372036854775807"}});
|
||||
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, GetRangeWithMaxLongLength) {
|
||||
auto res =
|
||||
cli_.Get("/with-range", {{"Range", "bytes=0-9223372036854775807"}});
|
||||
EXPECT_EQ(StatusCode::PartialContent_206, res->status);
|
||||
EXPECT_EQ("7", res->get_header_value("Content-Length"));
|
||||
EXPECT_EQ("bytes 0-6/7", res->get_header_value("Content-Range"));
|
||||
EXPECT_EQ(true, res->has_header("Content-Range"));
|
||||
EXPECT_EQ(std::string("abcdefg"), res->body);
|
||||
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, GetStreamedWithRangeMultipart) {
|
||||
@@ -2994,6 +3001,41 @@ TEST_F(ServerTest, GetStreamedWithRangeMultipart) {
|
||||
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) {
|
||||
uint64_t offset = 0;
|
||||
auto res = cli_.Get("/streamed-cancel",
|
||||
|
||||
Reference in New Issue
Block a user