This commit is contained in:
yhirose 2019-09-03 17:09:36 -04:00
parent 037b4fc789
commit 80f040cf69
3 changed files with 102 additions and 85 deletions

157
httplib.h
View file

@ -17,8 +17,16 @@
#define _CRT_NONSTDC_NO_DEPRECATE #define _CRT_NONSTDC_NO_DEPRECATE
#endif //_CRT_NONSTDC_NO_DEPRECATE #endif //_CRT_NONSTDC_NO_DEPRECATE
#if defined(_MSC_VER) && _MSC_VER < 1900 #if defined(_MSC_VER)
#ifdef _WIN64
typedef int64_t ssize_t;
#else
typedef int ssize_t;
#endif
#if _MSC_VER < 1900
#define snprintf _snprintf_s #define snprintf _snprintf_s
#endif
#endif // _MSC_VER #endif // _MSC_VER
#ifndef S_ISREG #ifndef S_ISREG
@ -130,16 +138,16 @@ typedef std::multimap<std::string, std::string, detail::ci> Headers;
typedef std::multimap<std::string, std::string> Params; typedef std::multimap<std::string, std::string> Params;
typedef std::smatch Match; typedef std::smatch Match;
typedef std::function<void(const char *data, uint64_t len)> Out; typedef std::function<void(const char *data, size_t data_len)> DataSink;
typedef std::function<void()> Done; typedef std::function<void()> Done;
typedef std::function<void(uint64_t offset, uint64_t length, Out out, typedef std::function<void(size_t offset, size_t length, DataSink sink,
Done done)> Done done)>
ContentProvider; ContentProvider;
typedef std::function<bool(const char *data, uint64_t data_length, typedef std::function<bool(const char *data, size_t data_length,
uint64_t offset, uint64_t content_length)> size_t offset, uint64_t content_length)>
ContentReceiver; ContentReceiver;
typedef std::function<bool(uint64_t current, uint64_t total)> Progress; typedef std::function<bool(uint64_t current, uint64_t total)> Progress;
@ -160,7 +168,7 @@ struct MultipartFormData {
}; };
typedef std::vector<MultipartFormData> MultipartFormDataItems; typedef std::vector<MultipartFormData> MultipartFormDataItems;
typedef std::pair<int64_t, int64_t> Range; typedef std::pair<ssize_t, ssize_t> Range;
typedef std::vector<Range> Ranges; typedef std::vector<Range> Ranges;
struct Request { struct Request {
@ -213,12 +221,12 @@ struct Response {
void set_content(const std::string &s, const char *content_type); void set_content(const std::string &s, const char *content_type);
void set_content_provider( void set_content_provider(
uint64_t length, size_t length,
std::function<void(uint64_t offset, uint64_t length, Out out)> provider, std::function<void(size_t offset, size_t length, DataSink sink)> provider,
std::function<void()> resource_releaser = [] {}); std::function<void()> resource_releaser = [] {});
void set_chunked_content_provider( void set_chunked_content_provider(
std::function<void(uint64_t offset, Out out, Done done)> provider, std::function<void(size_t offset, DataSink sink, Done done)> provider,
std::function<void()> resource_releaser = [] {}); std::function<void()> resource_releaser = [] {});
Response() : status(-1), content_provider_resource_length(0) {} Response() : status(-1), content_provider_resource_length(0) {}
@ -229,7 +237,7 @@ struct Response {
} }
} }
uint64_t content_provider_resource_length; size_t content_provider_resource_length;
ContentProvider content_provider; ContentProvider content_provider;
std::function<void()> content_provider_resource_releaser; std::function<void()> content_provider_resource_releaser;
}; };
@ -420,7 +428,7 @@ public:
void set_logger(Logger logger); void set_logger(Logger logger);
void set_keep_alive_max_count(size_t count); void set_keep_alive_max_count(size_t count);
void set_payload_max_length(uint64_t length); void set_payload_max_length(size_t length);
int bind_to_any_port(const char *host, int socket_flags = 0); int bind_to_any_port(const char *host, int socket_flags = 0);
bool listen_after_bind(); bool listen_after_bind();
@ -699,7 +707,7 @@ inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt,
return true; return true;
} }
inline std::string from_i_to_hex(uint64_t n) { inline std::string from_i_to_hex(size_t n) {
const char *charset = "0123456789abcdef"; const char *charset = "0123456789abcdef";
std::string ret; std::string ret;
do { do {
@ -1294,17 +1302,18 @@ inline bool read_headers(Stream &strm, Headers &headers) {
return true; return true;
} }
typedef std::function<bool(const char *data, uint64_t data_length)> typedef std::function<bool(const char *data, size_t data_length)>
ContentReceiverCore; ContentReceiverCore;
inline bool read_content_with_length(Stream &strm, size_t len, inline bool read_content_with_length(Stream &strm, uint64_t len,
Progress progress, Progress progress,
ContentReceiverCore out) { ContentReceiverCore out) {
char buf[CPPHTTPLIB_RECV_BUFSIZ]; char buf[CPPHTTPLIB_RECV_BUFSIZ];
size_t r = 0; uint64_t r = 0;
while (r < len) { while (r < len) {
auto n = strm.read(buf, std::min((len - r), CPPHTTPLIB_RECV_BUFSIZ)); auto read_len = static_cast<size_t>(len - r);
auto n = strm.read(buf, std::min(read_len, CPPHTTPLIB_RECV_BUFSIZ));
if (n <= 0) { return false; } if (n <= 0) { return false; }
if (!out(buf, n)) { return false; } if (!out(buf, n)) { return false; }
@ -1319,11 +1328,12 @@ inline bool read_content_with_length(Stream &strm, size_t len,
return true; return true;
} }
inline void skip_content_with_length(Stream &strm, size_t len) { inline void skip_content_with_length(Stream &strm, uint64_t len) {
char buf[CPPHTTPLIB_RECV_BUFSIZ]; char buf[CPPHTTPLIB_RECV_BUFSIZ];
size_t r = 0; uint64_t r = 0;
while (r < len) { while (r < len) {
auto n = strm.read(buf, std::min((len - r), CPPHTTPLIB_RECV_BUFSIZ)); auto read_len = static_cast<size_t>(len - r);
auto n = strm.read(buf, std::min(read_len, CPPHTTPLIB_RECV_BUFSIZ));
if (n <= 0) { return; } if (n <= 0) { return; }
r += n; r += n;
} }
@ -1382,7 +1392,7 @@ inline bool is_chunked_transfer_encoding(const Headers &headers) {
} }
template <typename T> template <typename T>
bool read_content(Stream &strm, T &x, uint64_t payload_max_length, int &status, bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
Progress progress, ContentReceiverCore receiver) { Progress progress, ContentReceiverCore receiver) {
ContentReceiverCore out = [&](const char *buf, size_t n) { ContentReceiverCore out = [&](const char *buf, size_t n) {
@ -1419,17 +1429,12 @@ bool read_content(Stream &strm, T &x, uint64_t payload_max_length, int &status,
ret = read_content_without_length(strm, out); ret = read_content_without_length(strm, out);
} else { } else {
auto len = get_header_value_uint64(x.headers, "Content-Length", 0); auto len = get_header_value_uint64(x.headers, "Content-Length", 0);
if (len > 0) { if (len > payload_max_length) {
if ((len > payload_max_length) || exceed_payload_max_length = true;
// For 32-bit platform skip_content_with_length(strm, len);
(sizeof(size_t) < sizeof(uint64_t) && ret = false;
len > std::numeric_limits<size_t>::max())) { } else if (len > 0) {
exceed_payload_max_length = true; ret = read_content_with_length(strm, len, progress, out);
skip_content_with_length(strm, len);
ret = false;
} else {
ret = read_content_with_length(strm, len, progress, out);
}
} }
} }
@ -1452,34 +1457,34 @@ template <typename T> inline int write_headers(Stream &strm, const T &info) {
return write_len; return write_len;
} }
inline int write_content(Stream &strm, ContentProvider content_provider, inline ssize_t write_content(Stream &strm, ContentProvider content_provider,
uint64_t offset, uint64_t length) { size_t offset, size_t length) {
uint64_t begin_offset = offset; size_t begin_offset = offset;
uint64_t end_offset = offset + length; size_t end_offset = offset + length;
while (offset < end_offset) { while (offset < end_offset) {
int64_t written_length = 0; ssize_t written_length = 0;
content_provider( content_provider(
offset, end_offset - offset, offset, end_offset - offset,
[&](const char *d, uint64_t l) { [&](const char *d, size_t l) {
offset += l; offset += l;
written_length = strm.write(d, l); written_length = strm.write(d, l);
}, },
[&](void) { written_length = -1; }); [&](void) { written_length = -1; });
if (written_length < 0) { return static_cast<int>(written_length); } if (written_length < 0) { return written_length; }
} }
return static_cast<int>(offset - begin_offset); return static_cast<ssize_t>(offset - begin_offset);
} }
inline int write_content_chunked(Stream &strm, inline ssize_t write_content_chunked(Stream &strm,
ContentProvider content_provider) { ContentProvider content_provider) {
uint64_t offset = 0; size_t offset = 0;
auto data_available = true; auto data_available = true;
uint64_t total_written_length = 0; ssize_t total_written_length = 0;
while (data_available) { while (data_available) {
int64_t written_length = 0; ssize_t written_length = 0;
content_provider( content_provider(
offset, 0, offset, 0,
[&](const char *d, uint64_t l) { [&](const char *d, size_t l) {
data_available = l > 0; data_available = l > 0;
offset += l; offset += l;
@ -1492,10 +1497,10 @@ inline int write_content_chunked(Stream &strm,
written_length = strm.write("0\r\n\r\n"); written_length = strm.write("0\r\n\r\n");
}); });
if (written_length < 0) { return static_cast<int>(written_length); } if (written_length < 0) { written_length; }
total_written_length += written_length; total_written_length += written_length;
} }
return static_cast<int>(total_written_length); return total_written_length;
} }
inline std::string encode_url(const std::string &s) { inline std::string encode_url(const std::string &s) {
@ -1674,13 +1679,13 @@ inline bool parse_range_header(const std::string &s, Ranges &ranges) {
static auto re = std::regex(R"(\s*(\d*)-(\d*))"); static auto re = std::regex(R"(\s*(\d*)-(\d*))");
std::cmatch m; std::cmatch m;
if (std::regex_match(b, e, m, re)) { if (std::regex_match(b, e, m, re)) {
uint64_t first = -1; ssize_t first = -1;
if (!m.str(1).empty()) { first = std::stoll(m.str(1)); } if (!m.str(1).empty()) { first = static_cast<ssize_t>(std::stoll(m.str(1))); }
uint64_t last = -1; ssize_t last = -1;
if (!m.str(2).empty()) { last = std::stoll(m.str(2)); } if (!m.str(2).empty()) { last = static_cast<ssize_t>(std::stoll(m.str(2))); }
if (int64_t(first) != -1 && int64_t(last) != -1 && first > last) { if (first != -1 && last != -1 && first > last) {
throw std::runtime_error("invalid range error"); throw std::runtime_error("invalid range error");
} }
ranges.emplace_back(std::make_pair(first, last)); ranges.emplace_back(std::make_pair(first, last));
@ -1718,8 +1723,8 @@ inline std::string make_multipart_data_boundary() {
return result; return result;
} }
inline std::pair<uint64_t, uint64_t> inline std::pair<size_t, size_t>
get_range_offset_and_length(const Request &req, uint64_t content_length, get_range_offset_and_length(const Request &req, size_t content_length,
size_t index) { size_t index) {
auto r = req.ranges[index]; auto r = req.ranges[index];
@ -1737,9 +1742,9 @@ get_range_offset_and_length(const Request &req, uint64_t content_length,
return std::make_pair(r.first, r.second - r.first + 1); return std::make_pair(r.first, r.second - r.first + 1);
} }
inline std::string make_content_range_header_field(uint64_t offset, inline std::string make_content_range_header_field(size_t offset,
uint64_t length, size_t length,
uint64_t content_length) { size_t content_length) {
std::string field = "bytes "; std::string field = "bytes ";
field += std::to_string(offset); field += std::to_string(offset);
field += "-"; field += "-";
@ -1793,7 +1798,7 @@ inline std::string make_multipart_ranges_data(const Request &req, Response &res,
req, res, boundary, content_type, req, res, boundary, content_type,
[&](const std::string &token) { data += token; }, [&](const std::string &token) { data += token; },
[&](const char *token) { data += token; }, [&](const char *token) { data += token; },
[&](uint64_t offset, uint64_t length) { [&](size_t offset, size_t length) {
data += res.body.substr(offset, length); data += res.body.substr(offset, length);
return true; return true;
}); });
@ -1801,17 +1806,17 @@ inline std::string make_multipart_ranges_data(const Request &req, Response &res,
return data; return data;
} }
inline uint64_t inline size_t
get_multipart_ranges_data_length(const Request &req, Response &res, 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) {
uint64_t data_length = 0; size_t data_length = 0;
process_multipart_ranges_data( process_multipart_ranges_data(
req, res, boundary, content_type, req, res, boundary, content_type,
[&](const std::string &token) { data_length += token.size(); }, [&](const std::string &token) { data_length += token.size(); },
[&](const char *token) { data_length += strlen(token); }, [&](const char *token) { data_length += strlen(token); },
[&](uint64_t /*offset*/, uint64_t length) { [&](size_t /*offset*/, size_t length) {
data_length += length; data_length += length;
return true; return true;
}); });
@ -1827,13 +1832,13 @@ inline bool write_multipart_ranges_data(Stream &strm, const Request &req,
req, res, boundary, content_type, req, res, boundary, content_type,
[&](const std::string &token) { strm.write(token); }, [&](const std::string &token) { strm.write(token); },
[&](const char *token) { strm.write(token); }, [&](const char *token) { strm.write(token); },
[&](uint64_t offset, uint64_t length) { [&](size_t offset, size_t length) {
return detail::write_content(strm, res.content_provider, offset, return detail::write_content(strm, res.content_provider, offset,
length) >= 0; length) >= 0;
}); });
} }
inline std::pair<uint64_t, uint64_t> inline std::pair<size_t, size_t>
get_range_offset_and_length(const Request &req, const Response &res, get_range_offset_and_length(const Request &req, const Response &res,
size_t index) { size_t index) {
auto r = req.ranges[index]; auto r = req.ranges[index];
@ -1969,22 +1974,22 @@ inline void Response::set_content(const std::string &s,
} }
inline void Response::set_content_provider( inline void Response::set_content_provider(
uint64_t length, size_t length,
std::function<void(uint64_t offset, uint64_t length, Out out)> provider, std::function<void(size_t offset, size_t length, DataSink sink)> provider,
std::function<void()> resource_releaser) { std::function<void()> resource_releaser) {
assert(length > 0); assert(length > 0);
content_provider_resource_length = length; content_provider_resource_length = length;
content_provider = [provider](uint64_t offset, uint64_t length, Out out, content_provider = [provider](size_t offset, size_t length, DataSink sink,
Done) { provider(offset, length, out); }; Done) { provider(offset, length, sink); };
content_provider_resource_releaser = resource_releaser; content_provider_resource_releaser = resource_releaser;
} }
inline void Response::set_chunked_content_provider( inline void Response::set_chunked_content_provider(
std::function<void(uint64_t offset, Out out, Done done)> provider, std::function<void(size_t offset, DataSink sink, Done done)> provider,
std::function<void()> resource_releaser) { std::function<void()> resource_releaser) {
content_provider_resource_length = 0; content_provider_resource_length = 0;
content_provider = [provider](uint64_t offset, uint64_t, Out out, Done done) { content_provider = [provider](size_t offset, size_t, DataSink sink, Done done) {
provider(offset, out, done); provider(offset, sink, done);
}; };
content_provider_resource_releaser = resource_releaser; content_provider_resource_releaser = resource_releaser;
} }
@ -2146,7 +2151,7 @@ inline void Server::set_keep_alive_max_count(size_t count) {
keep_alive_max_count_ = count; keep_alive_max_count_ = count;
} }
inline void Server::set_payload_max_length(uint64_t length) { inline void Server::set_payload_max_length(size_t length) {
payload_max_length_ = length; payload_max_length_ = length;
} }
@ -2240,7 +2245,7 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
if (res.body.empty()) { if (res.body.empty()) {
if (res.content_provider_resource_length > 0) { if (res.content_provider_resource_length > 0) {
uint64_t length = 0; size_t length = 0;
if (req.ranges.empty()) { if (req.ranges.empty()) {
length = res.content_provider_resource_length; length = res.content_provider_resource_length;
} else if (req.ranges.size() == 1) { } else if (req.ranges.size() == 1) {
@ -2530,6 +2535,9 @@ Server::process_request(Stream &strm, bool last_connection,
if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH") { if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH") {
if (!detail::read_content(strm, req, payload_max_length_, res.status, if (!detail::read_content(strm, req, payload_max_length_, res.status,
Progress(), [&](const char *buf, size_t n) { Progress(), [&](const char *buf, size_t n) {
if (req.body.size() + n > req.body.max_size()) {
return false;
}
req.body.append(buf, n); req.body.append(buf, n);
return true; return true;
})) { })) {
@ -2741,12 +2749,15 @@ inline bool Client::process_request(Stream &strm, Request &req, Response &res,
// Body // Body
if (req.method != "HEAD") { if (req.method != "HEAD") {
detail::ContentReceiverCore out = [&](const char *buf, size_t n) { detail::ContentReceiverCore out = [&](const char *buf, size_t n) {
if (res.body.size() + n > res.body.max_size()) {
return false;
}
res.body.append(buf, n); res.body.append(buf, n);
return true; return true;
}; };
if (req.content_receiver) { if (req.content_receiver) {
auto offset = std::make_shared<uint64_t>(); auto offset = std::make_shared<size_t>();
auto length = get_header_value_uint64(res.headers, "Content-Length", 0); auto length = get_header_value_uint64(res.headers, "Content-Length", 0);
auto receiver = req.content_receiver; auto receiver = req.content_receiver;
out = [offset, length, receiver](const char *buf, size_t n) { out = [offset, length, receiver](const char *buf, size_t n) {
@ -2757,7 +2768,7 @@ inline bool Client::process_request(Stream &strm, Request &req, Response &res,
} }
int dummy_status; int dummy_status;
if (!detail::read_content(strm, res, std::numeric_limits<uint64_t>::max(), if (!detail::read_content(strm, res, std::numeric_limits<size_t>::max(),
dummy_status, req.progress, out)) { dummy_status, req.progress, out)) {
return false; return false;
} }

View file

@ -54,6 +54,9 @@
#include <limits> #include <limits>
#include <vector> #include <vector>
#pragma warning( push )
#pragma warning( disable : 4996)
// Copyright 2005, Google Inc. // Copyright 2005, Google Inc.
// All rights reserved. // All rights reserved.
// //
@ -19544,4 +19547,6 @@ bool StaticAssertTypeEq() {
} // namespace testing } // namespace testing
#pragma warning( pop )
#endif // GTEST_INCLUDE_GTEST_GTEST_H_ #endif // GTEST_INCLUDE_GTEST_GTEST_H_

View file

@ -229,7 +229,7 @@ TEST(ChunkedEncodingTest, WithContentReceiver) {
std::string body; std::string body;
auto res = auto res =
cli.Get("/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137", cli.Get("/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137",
[&](const char *data, uint64_t data_length, uint64_t, uint64_t) { [&](const char *data, size_t data_length, uint64_t, uint64_t) {
body.append(data, data_length); body.append(data, data_length);
return true; return true;
}); });
@ -501,18 +501,18 @@ protected:
.Get("/streamed-chunked", .Get("/streamed-chunked",
[&](const Request & /*req*/, Response &res) { [&](const Request & /*req*/, Response &res) {
res.set_chunked_content_provider( res.set_chunked_content_provider(
[](uint64_t /*offset*/, Out out, Done done) { [](uint64_t /*offset*/, DataSink sink, Done done) {
out("123", 3); sink("123", 3);
out("456", 3); sink("456", 3);
out("789", 3); sink("789", 3);
done(); done();
}); });
}) })
.Get("/streamed", .Get("/streamed",
[&](const Request & /*req*/, Response &res) { [&](const Request & /*req*/, Response &res) {
res.set_content_provider( res.set_content_provider(
6, [](uint64_t offset, uint64_t /*length*/, Out out) { 6, [](uint64_t offset, uint64_t /*length*/, DataSink sink) {
out(offset < 3 ? "a" : "b", 1); sink(offset < 3 ? "a" : "b", 1);
}); });
}) })
.Get("/streamed-with-range", .Get("/streamed-with-range",
@ -520,10 +520,11 @@ protected:
auto data = new std::string("abcdefg"); auto data = new std::string("abcdefg");
res.set_content_provider( res.set_content_provider(
data->size(), data->size(),
[data](uint64_t offset, uint64_t length, Out out) { [data](uint64_t offset, uint64_t length, DataSink sink) {
const uint64_t DATA_CHUNK_SIZE = 4; size_t DATA_CHUNK_SIZE = 4;
const auto &d = *data; const auto &d = *data;
out(&d[offset], std::min(length, DATA_CHUNK_SIZE)); auto out_len = std::min(static_cast<size_t>(length), DATA_CHUNK_SIZE);
sink(&d[offset], out_len);
}, },
[data] { delete data; }); [data] { delete data; });
}) })
@ -531,9 +532,9 @@ protected:
[&](const Request & /*req*/, Response &res) { [&](const Request & /*req*/, Response &res) {
res.set_content_provider( res.set_content_provider(
size_t(-1), size_t(-1),
[](uint64_t /*offset*/, uint64_t /*length*/, Out out) { [](uint64_t /*offset*/, uint64_t /*length*/, DataSink sink) {
std::string data = "data_chunk"; std::string data = "data_chunk";
out(data.data(), data.size()); sink(data.data(), data.size());
}); });
}) })
.Get("/with-range", .Get("/with-range",