|
|
|
@@ -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.3"
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
@@ -957,7 +961,7 @@ private:
|
|
|
|
|
bool parse_request_line(const char *s, Request &req) const;
|
|
|
|
|
void apply_ranges(const Request &req, Response &res,
|
|
|
|
|
std::string &content_type, std::string &boundary) const;
|
|
|
|
|
bool write_response(Stream &strm, bool close_connection, const Request &req,
|
|
|
|
|
bool write_response(Stream &strm, bool close_connection, Request &req,
|
|
|
|
|
Response &res);
|
|
|
|
|
bool write_response_with_content(Stream &strm, bool close_connection,
|
|
|
|
|
const Request &req, Response &res);
|
|
|
|
@@ -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 range_error(Request &req, Response &res) {
|
|
|
|
|
if (!req.ranges.empty() && 200 <= res.status && res.status < 300) {
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
// 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 true; }
|
|
|
|
|
|
|
|
|
|
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 true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ranges must be in ascending order
|
|
|
|
|
if (first_pos <= prev_first_pos) { return true; }
|
|
|
|
|
|
|
|
|
|
// Request must not have more than two overlapping ranges
|
|
|
|
|
if (first_pos <= prev_last_pos) {
|
|
|
|
|
overwrapping_count++;
|
|
|
|
|
if (overwrapping_count > 2) { return true; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -5940,7 +5987,10 @@ inline bool Server::parse_request_line(const char *s, Request &req) const {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool Server::write_response(Stream &strm, bool close_connection,
|
|
|
|
|
const Request &req, Response &res) {
|
|
|
|
|
Request &req, Response &res) {
|
|
|
|
|
// NOTE: `req.ranges` should be empty, otherwise it will be applied
|
|
|
|
|
// incorrectly to the error content.
|
|
|
|
|
req.ranges.clear();
|
|
|
|
|
return write_response_core(strm, close_connection, req, res, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -6018,7 +6068,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 +6092,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 +6493,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 +6513,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 +6542,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) {
|
|
|
|
@@ -6653,7 +6697,7 @@ Server::process_request(Stream &strm, bool close_connection,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rounting
|
|
|
|
|
// Routing
|
|
|
|
|
auto routed = false;
|
|
|
|
|
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
|
|
|
|
|
routed = routing(req, res, strm);
|
|
|
|
@@ -6689,15 +6733,24 @@ 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::range_error(req, res)) {
|
|
|
|
|
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 +7654,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 +7671,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 +9052,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); }
|
|
|
|
|