Compare commits

...

6 Commits

Author SHA1 Message Date
yhirose e155ba44bb Fix #706 2020-10-19 15:23:35 -04:00
Muchamad Arifin Dwi P a4a9637738 Fix #700 null pointer exception (#702) 2020-10-16 20:44:14 -04:00
Omkar Jadhav 5292142046 Add cpp-httplib to oss-fuzz (#684)
* *Add server fuzzer target  and seed corpus
* Add fuzz_test option to Makefile

* Fix #685

* Try to fix Github actions on Ubuntu

* Added ReadTimeoutSSL test

* Comment out `-fsanitize=address`

* Rebase upstream changes

* remove address sanitizer temporarily

* Add separate Makefile for fuzzing

* 1. Remove special char from dictionary
2. Clean fuzzing/Makefile

* Use specific path to avoid accidently linking openssl version brought in by oss-fuzz

* remove addition of flags

* Refactor Makefile

* Add missing newline

* Add fuzztest to github workflow

* Fix

Co-authored-by: yhirose <yuji.hirose.bug@gmail.com>
2020-10-15 08:11:40 -04:00
Snape3058 cc5147ad72 Replace shared_ptr with unique_ptr for better performance (#695)
* Backport std::make_unique from C++14.

* Replace shared_ptr with unique_ptr for better performance.

Co-authored-by: Ella <maxutong16@otcaix.iscas.ac.cn>
2020-10-15 08:09:11 -04:00
Andrew Gasparovic fffbf1a669 Use move semantics instead of copy for functions (#692)
* Use move semantics instead of copy for functions

In some cases, a few more copies could be prevented by changing function definitions to accept parameters by const-ref, rather than by value, but I didn't want to change public signatures.

* Fix two use-after-move errors
2020-10-11 19:00:36 -04:00
lightvector d37bc0fb4d Allow client to specify boundary and use more entropy by default (#691) (#694) 2020-10-11 15:34:54 -04:00
10 changed files with 605 additions and 108 deletions
+3
View File
@@ -27,6 +27,9 @@ jobs:
- name: make
if: matrix.os != 'windows-latest'
run: cd test && make
- name: check fuzz test target
if: matrix.os == 'ubuntu-latest'
run: cd test && make -f Makefile.fuzz_test
- name: setup msbuild on windows
if: matrix.os == 'windows-latest'
uses: warrenbuckley/Setup-MSBuild@v1
+187 -107
View File
@@ -237,6 +237,27 @@ namespace httplib {
namespace detail {
/*
* Backport std::make_unique from C++14.
*
* NOTE: This code came up with the following stackoverflow post:
* https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique
*
*/
template <class T, class... Args>
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(Args &&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
template <class T>
typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(std::size_t n) {
typedef typename std::remove_extent<T>::type RT;
return std::unique_ptr<T>(new RT[n]);
}
struct ci {
bool operator()(const std::string &s1, const std::string &s2) const {
return std::lexicographical_compare(
@@ -317,14 +338,17 @@ public:
ContentReceiver receiver)>;
ContentReader(Reader reader, MultipartReader multipart_reader)
: reader_(reader), multipart_reader_(multipart_reader) {}
: reader_(std::move(reader)),
multipart_reader_(std::move(multipart_reader)) {}
bool operator()(MultipartContentHeader header,
ContentReceiver receiver) const {
return multipart_reader_(header, receiver);
return multipart_reader_(std::move(header), std::move(receiver));
}
bool operator()(ContentReceiver receiver) const { return reader_(receiver); }
bool operator()(ContentReceiver receiver) const {
return reader_(std::move(receiver));
}
Reader reader_;
MultipartReader multipart_reader_;
@@ -475,7 +499,7 @@ public:
void enqueue(std::function<void()> fn) override {
std::unique_lock<std::mutex> lock(mutex_);
jobs_.push_back(fn);
jobs_.push_back(std::move(fn));
cond_.notify_one();
}
@@ -704,13 +728,14 @@ enum Error {
Canceled,
SSLConnection,
SSLLoadingCerts,
SSLServerVerification
SSLServerVerification,
UnsupportedMultipartBoundaryChars
};
class Result {
public:
Result(const std::shared_ptr<Response> &res, Error err)
: res_(res), err_(err) {}
Result(std::unique_ptr<Response> res, Error err)
: res_(std::move(res)), err_(err) {}
operator bool() const { return res_ != nullptr; }
bool operator==(std::nullptr_t) const { return res_ == nullptr; }
bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
@@ -720,7 +745,7 @@ public:
Error error() const { return err_; }
private:
std::shared_ptr<Response> res_;
std::unique_ptr<Response> res_;
Error err_;
};
@@ -777,6 +802,9 @@ public:
Result Post(const char *path, const MultipartFormDataItems &items);
Result Post(const char *path, const Headers &headers,
const MultipartFormDataItems &items);
Result Post(const char *path, const Headers &headers,
const MultipartFormDataItems &items,
const std::string& boundary);
Result Put(const char *path);
Result Put(const char *path, const std::string &body,
@@ -943,7 +971,7 @@ private:
bool handle_request(Stream &strm, const Request &req, Response &res,
bool close_connection);
void stop_core();
std::shared_ptr<Response> send_with_content_provider(
std::unique_ptr<Response> send_with_content_provider(
const char *method, const char *path, const Headers &headers,
const std::string &body, size_t content_length,
ContentProvider content_provider, const char *content_type);
@@ -1012,6 +1040,9 @@ public:
Result Post(const char *path, const MultipartFormDataItems &items);
Result Post(const char *path, const Headers &headers,
const MultipartFormDataItems &items);
Result Post(const char *path, const Headers &headers,
const MultipartFormDataItems &items,
const std::string& boundary);
Result Put(const char *path);
Result Put(const char *path, const std::string &body,
const char *content_type);
@@ -1098,7 +1129,7 @@ public:
#endif
private:
std::shared_ptr<ClientImpl> cli_;
std::unique_ptr<ClientImpl> cli_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
bool is_ssl_ = false;
@@ -1944,7 +1975,7 @@ inline socket_t create_client_socket(const char *host, int port,
time_t timeout_sec, time_t timeout_usec,
const std::string &intf, Error &error) {
auto sock = create_socket(
host, port, 0, tcp_nodelay, socket_options,
host, port, 0, tcp_nodelay, std::move(socket_options),
[&](socket_t sock, struct addrinfo &ai) -> bool {
if (!intf.empty()) {
#ifdef USE_IF2IP
@@ -2574,19 +2605,19 @@ bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
bool decompress, U callback) {
if (decompress) {
std::string encoding = x.get_header_value("Content-Encoding");
std::shared_ptr<decompressor> decompressor;
std::unique_ptr<decompressor> decompressor;
if (encoding.find("gzip") != std::string::npos ||
encoding.find("deflate") != std::string::npos) {
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
decompressor = std::make_shared<gzip_decompressor>();
decompressor = detail::make_unique<gzip_decompressor>();
#else
status = 415;
return false;
#endif
} else if (encoding.find("br") != std::string::npos) {
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
decompressor = std::make_shared<brotli_decompressor>();
decompressor = detail::make_unique<brotli_decompressor>();
#else
status = 415;
return false;
@@ -2600,7 +2631,7 @@ bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
buf, n,
[&](const char *buf, size_t n) { return receiver(buf, n); });
};
return callback(out);
return callback(std::move(out));
} else {
status = 500;
return false;
@@ -2611,7 +2642,7 @@ bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
ContentReceiver out = [&](const char *buf, size_t n) {
return receiver(buf, n);
};
return callback(out);
return callback(std::move(out));
}
template <typename T>
@@ -2619,7 +2650,7 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
Progress progress, ContentReceiver receiver,
bool decompress) {
return prepare_content_receiver(
x, status, receiver, decompress, [&](const ContentReceiver &out) {
x, status, std::move(receiver), decompress, [&](const ContentReceiver &out) {
auto ret = true;
auto exceed_payload_max_length = false;
@@ -2634,7 +2665,7 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
skip_content_with_length(strm, len);
ret = false;
} else if (len > 0) {
ret = read_content_with_length(strm, len, progress, out);
ret = read_content_with_length(strm, len, std::move(progress), out);
}
}
@@ -2918,7 +2949,7 @@ public:
bool is_valid() const { return is_valid_; }
template <typename T, typename U>
bool parse(const char *buf, size_t n, T content_callback, U header_callback) {
bool parse(const char *buf, size_t n, const T &content_callback, const U &header_callback) {
static const std::regex re_content_disposition(
"^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename="
@@ -3090,8 +3121,13 @@ inline std::string make_multipart_data_boundary() {
static const char data[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
// std::random_device might actually be deterministic on some
// platforms, but due to lack of support in the c++ standard library,
// doing better requires either some ugly hacks or breaking portability.
std::random_device seed_gen;
std::mt19937 engine(seed_gen());
// Request 128 bits of entropy for initialization
std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
std::mt19937 engine(seed_sequence);
std::string result = "--cpp-httplib-multipart-data-";
@@ -3451,7 +3487,7 @@ inline std::pair<std::string, std::string> make_range_header(Ranges ranges) {
if (r.second != -1) { field += std::to_string(r.second); }
i++;
}
return std::make_pair("Range", field);
return std::make_pair("Range", std::move(field));
}
inline std::pair<std::string, std::string>
@@ -3460,7 +3496,7 @@ make_basic_authentication_header(const std::string &username,
bool is_proxy = false) {
auto field = "Basic " + detail::base64_encode(username + ":" + password);
auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
return std::make_pair(key, field);
return std::make_pair(key, std::move(field));
}
inline std::pair<std::string, std::string>
@@ -3468,7 +3504,7 @@ make_bearer_token_authentication_header(const std::string &token,
bool is_proxy = false) {
auto field = "Bearer " + token;
auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
return std::make_pair(key, field);
return std::make_pair(key, std::move(field));
}
// Request implementation
@@ -3761,60 +3797,66 @@ inline Server::Server()
inline Server::~Server() {}
inline Server &Server::Get(const char *pattern, Handler handler) {
get_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
get_handlers_.push_back(std::make_pair(std::regex(pattern),
std::move(handler)));
return *this;
}
inline Server &Server::Post(const char *pattern, Handler handler) {
post_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
post_handlers_.push_back(std::make_pair(std::regex(pattern),
std::move(handler)));
return *this;
}
inline Server &Server::Post(const char *pattern,
HandlerWithContentReader handler) {
post_handlers_for_content_reader_.push_back(
std::make_pair(std::regex(pattern), handler));
std::make_pair(std::regex(pattern), std::move(handler)));
return *this;
}
inline Server &Server::Put(const char *pattern, Handler handler) {
put_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
put_handlers_.push_back(std::make_pair(std::regex(pattern),
std::move(handler)));
return *this;
}
inline Server &Server::Put(const char *pattern,
HandlerWithContentReader handler) {
put_handlers_for_content_reader_.push_back(
std::make_pair(std::regex(pattern), handler));
std::make_pair(std::regex(pattern), std::move(handler)));
return *this;
}
inline Server &Server::Patch(const char *pattern, Handler handler) {
patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
patch_handlers_.push_back(std::make_pair(std::regex(pattern),
std::move(handler)));
return *this;
}
inline Server &Server::Patch(const char *pattern,
HandlerWithContentReader handler) {
patch_handlers_for_content_reader_.push_back(
std::make_pair(std::regex(pattern), handler));
std::make_pair(std::regex(pattern), std::move(handler)));
return *this;
}
inline Server &Server::Delete(const char *pattern, Handler handler) {
delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
delete_handlers_.push_back(std::make_pair(std::regex(pattern),
std::move(handler)));
return *this;
}
inline Server &Server::Delete(const char *pattern,
HandlerWithContentReader handler) {
delete_handlers_for_content_reader_.push_back(
std::make_pair(std::regex(pattern), handler));
std::make_pair(std::regex(pattern), std::move(handler)));
return *this;
}
inline Server &Server::Options(const char *pattern, Handler handler) {
options_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
options_handlers_.push_back(std::make_pair(std::regex(pattern),
std::move(handler)));
return *this;
}
@@ -3860,7 +3902,7 @@ inline void Server::set_error_handler(Handler handler) {
inline void Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
inline void Server::set_socket_options(SocketOptions socket_options) {
socket_options_ = socket_options;
socket_options_ = std::move(socket_options);
}
inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); }
@@ -4045,16 +4087,16 @@ inline bool Server::write_response(Stream &strm, bool close_connection,
}
if (type != detail::EncodingType::None) {
std::shared_ptr<detail::compressor> compressor;
std::unique_ptr<detail::compressor> compressor;
if (type == detail::EncodingType::Gzip) {
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
compressor = std::make_shared<detail::gzip_compressor>();
compressor = detail::make_unique<detail::gzip_compressor>();
res.set_header("Content-Encoding", "gzip");
#endif
} else if (type == detail::EncodingType::Brotli) {
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
compressor = std::make_shared<detail::brotli_compressor>();
compressor = detail::make_unique<detail::brotli_compressor>();
res.set_header("Content-Encoding", "brotli");
#endif
}
@@ -4136,17 +4178,17 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
if (res.is_chunked_content_provider) {
auto type = detail::encoding_type(req, res);
std::shared_ptr<detail::compressor> compressor;
std::unique_ptr<detail::compressor> compressor;
if (type == detail::EncodingType::Gzip) {
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
compressor = std::make_shared<detail::gzip_compressor>();
compressor = detail::make_unique<detail::gzip_compressor>();
#endif
} else if (type == detail::EncodingType::Brotli) {
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
compressor = std::make_shared<detail::brotli_compressor>();
compressor = detail::make_unique<detail::brotli_compressor>();
#endif
} else {
compressor = std::make_shared<detail::nocompressor>();
compressor = detail::make_unique<detail::nocompressor>();
}
assert(compressor != nullptr);
@@ -4198,8 +4240,9 @@ inline bool Server::read_content_with_content_receiver(
Stream &strm, Request &req, Response &res, ContentReceiver receiver,
MultipartContentHeader multipart_header,
ContentReceiver multipart_receiver) {
return read_content_core(strm, req, res, receiver, multipart_header,
multipart_receiver);
return read_content_core(strm, req, res, std::move(receiver),
std::move(multipart_header),
std::move(multipart_receiver));
}
inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
@@ -4234,7 +4277,7 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
mulitpart_header);
};
} else {
out = receiver;
out = std::move(receiver);
}
if (req.method == "DELETE" && !req.has_header("Content-Length")) {
@@ -4290,7 +4333,7 @@ inline socket_t
Server::create_server_socket(const char *host, int port, int socket_flags,
SocketOptions socket_options) const {
return detail::create_socket(
host, port, socket_flags, tcp_nodelay_, socket_options,
host, port, socket_flags, tcp_nodelay_, std::move(socket_options),
[](socket_t sock, struct addrinfo &ai) -> bool {
if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
return false;
@@ -4392,32 +4435,38 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
{
ContentReader reader(
[&](ContentReceiver receiver) {
return read_content_with_content_receiver(strm, req, res, receiver,
return read_content_with_content_receiver(strm, req, res,
std::move(receiver),
nullptr, nullptr);
},
[&](MultipartContentHeader header, ContentReceiver receiver) {
return read_content_with_content_receiver(strm, req, res, nullptr,
header, receiver);
std::move(header),
std::move(receiver));
});
if (req.method == "POST") {
if (dispatch_request_for_content_reader(
req, res, reader, post_handlers_for_content_reader_)) {
req, res, std::move(reader),
post_handlers_for_content_reader_)) {
return true;
}
} else if (req.method == "PUT") {
if (dispatch_request_for_content_reader(
req, res, reader, put_handlers_for_content_reader_)) {
req, res, std::move(reader),
put_handlers_for_content_reader_)) {
return true;
}
} else if (req.method == "PATCH") {
if (dispatch_request_for_content_reader(
req, res, reader, patch_handlers_for_content_reader_)) {
req, res, std::move(reader),
patch_handlers_for_content_reader_)) {
return true;
}
} else if (req.method == "DELETE") {
if (dispatch_request_for_content_reader(
req, res, reader, delete_handlers_for_content_reader_)) {
req, res, std::move(reader),
delete_handlers_for_content_reader_)) {
return true;
}
}
@@ -4973,7 +5022,7 @@ inline bool ClientImpl::write_request(Stream &strm, const Request &req,
return true;
}
inline std::shared_ptr<Response> ClientImpl::send_with_content_provider(
inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
const char *method, const char *path, const Headers &headers,
const std::string &body, size_t content_length,
ContentProvider content_provider, const char *content_type) {
@@ -5036,15 +5085,15 @@ inline std::shared_ptr<Response> ClientImpl::send_with_content_provider(
{
if (content_provider) {
req.content_length = content_length;
req.content_provider = content_provider;
req.content_provider = std::move(content_provider);
} else {
req.body = body;
}
}
auto res = std::make_shared<Response>();
auto res = detail::make_unique<Response>();
return send(req, *res) ? res : nullptr;
return send(req, *res) ? std::move(res) : nullptr;
}
inline bool ClientImpl::process_request(Stream &strm, const Request &req,
@@ -5090,7 +5139,8 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
int dummy_status;
if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
dummy_status, progress, out, decompress_)) {
dummy_status, std::move(progress), std::move(out),
decompress_)) {
if (error_ != Error::Canceled) { error_ = Error::Read; }
return false;
}
@@ -5112,7 +5162,8 @@ ClientImpl::process_socket(Socket &socket,
std::function<bool(Stream &strm)> callback) {
return detail::process_client_socket(socket.sock, read_timeout_sec_,
read_timeout_usec_, write_timeout_sec_,
write_timeout_usec_, callback);
write_timeout_usec_,
std::move(callback));
}
inline bool ClientImpl::is_ssl() const { return false; }
@@ -5138,9 +5189,9 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
req.headers.insert(headers.begin(), headers.end());
req.progress = std::move(progress);
auto res = std::make_shared<Response>();
auto res = detail::make_unique<Response>();
auto ret = send(req, *res);
return Result{ret ? res : nullptr, get_last_error()};
return Result{ret ? std::move(res) : nullptr, get_last_error()};
}
inline Result ClientImpl::Get(const char *path,
@@ -5170,23 +5221,23 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
inline Result ClientImpl::Get(const char *path,
ResponseHandler response_handler,
ContentReceiver content_receiver) {
return Get(path, Headers(), std::move(response_handler), content_receiver,
nullptr);
return Get(path, Headers(), std::move(response_handler),
std::move(content_receiver), nullptr);
}
inline Result ClientImpl::Get(const char *path, const Headers &headers,
ResponseHandler response_handler,
ContentReceiver content_receiver) {
return Get(path, headers, std::move(response_handler), content_receiver,
nullptr);
return Get(path, headers, std::move(response_handler),
std::move(content_receiver), nullptr);
}
inline Result ClientImpl::Get(const char *path,
ResponseHandler response_handler,
ContentReceiver content_receiver,
Progress progress) {
return Get(path, Headers(), std::move(response_handler), content_receiver,
progress);
return Get(path, Headers(), std::move(response_handler),
std::move(content_receiver), std::move(progress));
}
inline Result ClientImpl::Get(const char *path, const Headers &headers,
@@ -5202,9 +5253,9 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
req.content_receiver = std::move(content_receiver);
req.progress = std::move(progress);
auto res = std::make_shared<Response>();
auto res = detail::make_unique<Response>();
auto ret = send(req, *res);
return Result{ret ? res : nullptr, get_last_error()};
return Result{ret ? std::move(res) : nullptr, get_last_error()};
}
inline Result ClientImpl::Head(const char *path) {
@@ -5218,9 +5269,9 @@ inline Result ClientImpl::Head(const char *path, const Headers &headers) {
req.headers.insert(headers.begin(), headers.end());
req.path = path;
auto res = std::make_shared<Response>();
auto res = detail::make_unique<Response>();
auto ret = send(req, *res);
return Result{ret ? res : nullptr, get_last_error()};
return Result{ret ? std::move(res) : nullptr, get_last_error()};
}
inline Result ClientImpl::Post(const char *path) {
@@ -5237,7 +5288,7 @@ inline Result ClientImpl::Post(const char *path, const Headers &headers,
const char *content_type) {
auto ret = send_with_content_provider("POST", path, headers, body, 0, nullptr,
content_type);
return Result{ret, get_last_error()};
return Result{std::move(ret), get_last_error()};
}
inline Result ClientImpl::Post(const char *path, const Params &params) {
@@ -5247,7 +5298,8 @@ inline Result ClientImpl::Post(const char *path, const Params &params) {
inline Result ClientImpl::Post(const char *path, size_t content_length,
ContentProvider content_provider,
const char *content_type) {
return Post(path, Headers(), content_length, content_provider, content_type);
return Post(path, Headers(), content_length, std::move(content_provider),
content_type);
}
inline Result ClientImpl::Post(const char *path, const Headers &headers,
@@ -5255,9 +5307,10 @@ inline Result ClientImpl::Post(const char *path, const Headers &headers,
ContentProvider content_provider,
const char *content_type) {
auto ret = send_with_content_provider("POST", path, headers, std::string(),
content_length, content_provider,
content_length,
std::move(content_provider),
content_type);
return Result{ret, get_last_error()};
return Result{std::move(ret), get_last_error()};
}
inline Result ClientImpl::Post(const char *path, const Headers &headers,
@@ -5273,7 +5326,18 @@ inline Result ClientImpl::Post(const char *path,
inline Result ClientImpl::Post(const char *path, const Headers &headers,
const MultipartFormDataItems &items) {
auto boundary = detail::make_multipart_data_boundary();
return Post(path, headers, items, detail::make_multipart_data_boundary());
}
inline Result ClientImpl::Post(const char *path, const Headers &headers,
const MultipartFormDataItems &items,
const std::string& boundary) {
for (size_t i = 0; i < boundary.size(); i++) {
char c = boundary[i];
if (!std::isalnum(c) && c != '-' && c != '_') {
error_ = Error::UnsupportedMultipartBoundaryChars;
return Result{nullptr, error_};
}
}
std::string body;
@@ -5311,13 +5375,14 @@ inline Result ClientImpl::Put(const char *path, const Headers &headers,
const char *content_type) {
auto ret = send_with_content_provider("PUT", path, headers, body, 0, nullptr,
content_type);
return Result{ret, get_last_error()};
return Result{std::move(ret), get_last_error()};
}
inline Result ClientImpl::Put(const char *path, size_t content_length,
ContentProvider content_provider,
const char *content_type) {
return Put(path, Headers(), content_length, content_provider, content_type);
return Put(path, Headers(), content_length, std::move(content_provider),
content_type);
}
inline Result ClientImpl::Put(const char *path, const Headers &headers,
@@ -5325,9 +5390,10 @@ inline Result ClientImpl::Put(const char *path, const Headers &headers,
ContentProvider content_provider,
const char *content_type) {
auto ret = send_with_content_provider("PUT", path, headers, std::string(),
content_length, content_provider,
content_length,
std::move(content_provider),
content_type);
return Result{ret, get_last_error()};
return Result{std::move(ret), get_last_error()};
}
inline Result ClientImpl::Put(const char *path, const Params &params) {
@@ -5350,13 +5416,14 @@ inline Result ClientImpl::Patch(const char *path, const Headers &headers,
const char *content_type) {
auto ret = send_with_content_provider("PATCH", path, headers, body, 0,
nullptr, content_type);
return Result{ret, get_last_error()};
return Result{std::move(ret), get_last_error()};
}
inline Result ClientImpl::Patch(const char *path, size_t content_length,
ContentProvider content_provider,
const char *content_type) {
return Patch(path, Headers(), content_length, content_provider, content_type);
return Patch(path, Headers(), content_length, std::move(content_provider),
content_type);
}
inline Result ClientImpl::Patch(const char *path, const Headers &headers,
@@ -5364,9 +5431,10 @@ inline Result ClientImpl::Patch(const char *path, const Headers &headers,
ContentProvider content_provider,
const char *content_type) {
auto ret = send_with_content_provider("PATCH", path, headers, std::string(),
content_length, content_provider,
content_length,
std::move(content_provider),
content_type);
return Result{ret, get_last_error()};
return Result{std::move(ret), get_last_error()};
}
inline Result ClientImpl::Delete(const char *path) {
@@ -5394,9 +5462,9 @@ inline Result ClientImpl::Delete(const char *path, const Headers &headers,
if (content_type) { req.headers.emplace("Content-Type", content_type); }
req.body = body;
auto res = std::make_shared<Response>();
auto res = detail::make_unique<Response>();
auto ret = send(req, *res);
return Result{ret ? res : nullptr, get_last_error()};
return Result{ret ? std::move(res) : nullptr, get_last_error()};
}
inline Result ClientImpl::Options(const char *path) {
@@ -5410,9 +5478,9 @@ inline Result ClientImpl::Options(const char *path, const Headers &headers) {
req.headers.insert(headers.begin(), headers.end());
req.path = path;
auto res = std::make_shared<Response>();
auto res = detail::make_unique<Response>();
auto ret = send(req, *res);
return Result{ret ? res : nullptr, get_last_error()};
return Result{ret ? std::move(res) : nullptr, get_last_error()};
}
inline size_t ClientImpl::is_socket_open() const {
@@ -5479,7 +5547,7 @@ inline void ClientImpl::set_default_headers(Headers headers) {
inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
socket_options_ = socket_options;
socket_options_ = std::move(socket_options);
}
inline void ClientImpl::set_compress(bool on) { compress_ = on; }
@@ -5788,9 +5856,12 @@ inline bool SSLServer::process_and_close_socket(socket_t sock) {
});
detail::ssl_delete(ctx_mutex_, ssl, ret);
detail::shutdown_socket(sock);
detail::close_socket(sock);
return ret;
}
detail::shutdown_socket(sock);
detail::close_socket(sock);
return false;
}
@@ -6024,7 +6095,7 @@ SSLClient::process_socket(Socket &socket,
assert(socket.ssl);
return detail::process_client_socket_ssl(
socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
write_timeout_sec_, write_timeout_usec_, callback);
write_timeout_sec_, write_timeout_usec_, std::move(callback));
}
inline bool SSLClient::is_ssl() const { return true; }
@@ -6187,28 +6258,28 @@ inline Client::Client(const char *scheme_host_port,
if (is_ssl) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
cli_ = std::make_shared<SSLClient>(host.c_str(), port, client_cert_path,
client_key_path);
cli_ = detail::make_unique<SSLClient>(host.c_str(), port,
client_cert_path, client_key_path);
is_ssl_ = is_ssl;
#endif
} else {
cli_ = std::make_shared<ClientImpl>(host.c_str(), port, client_cert_path,
client_key_path);
cli_ = detail::make_unique<ClientImpl>(host.c_str(), port,
client_cert_path, client_key_path);
}
} else {
cli_ = std::make_shared<ClientImpl>(scheme_host_port, 80, client_cert_path,
client_key_path);
cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
client_cert_path, client_key_path);
}
}
inline Client::Client(const std::string &host, int port)
: cli_(std::make_shared<ClientImpl>(host, port)) {}
: cli_(detail::make_unique<ClientImpl>(host, port)) {}
inline Client::Client(const std::string &host, int port,
const std::string &client_cert_path,
const std::string &client_key_path)
: cli_(std::make_shared<ClientImpl>(host, port, client_cert_path,
client_key_path)) {}
: cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
client_key_path)) {}
inline Client::~Client() {}
@@ -6221,11 +6292,11 @@ inline Result Client::Get(const char *path, const Headers &headers) {
return cli_->Get(path, headers);
}
inline Result Client::Get(const char *path, Progress progress) {
return cli_->Get(path, progress);
return cli_->Get(path, std::move(progress));
}
inline Result Client::Get(const char *path, const Headers &headers,
Progress progress) {
return cli_->Get(path, headers, progress);
return cli_->Get(path, headers, std::move(progress));
}
inline Result Client::Get(const char *path, ContentReceiver content_receiver) {
return cli_->Get(path, std::move(content_receiver));
@@ -6262,7 +6333,8 @@ inline Result Client::Get(const char *path, ResponseHandler response_handler,
inline Result Client::Get(const char *path, const Headers &headers,
ResponseHandler response_handler,
ContentReceiver content_receiver, Progress progress) {
return cli_->Get(path, headers, response_handler, content_receiver, progress);
return cli_->Get(path, headers, std::move(response_handler),
std::move(content_receiver), std::move(progress));
}
inline Result Client::Head(const char *path) { return cli_->Head(path); }
@@ -6282,13 +6354,14 @@ inline Result Client::Post(const char *path, const Headers &headers,
inline Result Client::Post(const char *path, size_t content_length,
ContentProvider content_provider,
const char *content_type) {
return cli_->Post(path, content_length, content_provider, content_type);
return cli_->Post(path, content_length, std::move(content_provider),
content_type);
}
inline Result Client::Post(const char *path, const Headers &headers,
size_t content_length,
ContentProvider content_provider,
const char *content_type) {
return cli_->Post(path, headers, content_length, content_provider,
return cli_->Post(path, headers, content_length, std::move(content_provider),
content_type);
}
inline Result Client::Post(const char *path, const Params &params) {
@@ -6306,6 +6379,11 @@ inline Result Client::Post(const char *path, const Headers &headers,
const MultipartFormDataItems &items) {
return cli_->Post(path, headers, items);
}
inline Result Client::Post(const char *path, const Headers &headers,
const MultipartFormDataItems &items,
const std::string& boundary) {
return cli_->Post(path, headers, items, boundary);
}
inline Result Client::Put(const char *path) { return cli_->Put(path); }
inline Result Client::Put(const char *path, const std::string &body,
const char *content_type) {
@@ -6318,13 +6396,14 @@ inline Result Client::Put(const char *path, const Headers &headers,
inline Result Client::Put(const char *path, size_t content_length,
ContentProvider content_provider,
const char *content_type) {
return cli_->Put(path, content_length, content_provider, content_type);
return cli_->Put(path, content_length, std::move(content_provider),
content_type);
}
inline Result Client::Put(const char *path, const Headers &headers,
size_t content_length,
ContentProvider content_provider,
const char *content_type) {
return cli_->Put(path, headers, content_length, content_provider,
return cli_->Put(path, headers, content_length, std::move(content_provider),
content_type);
}
inline Result Client::Put(const char *path, const Params &params) {
@@ -6345,13 +6424,14 @@ inline Result Client::Patch(const char *path, const Headers &headers,
inline Result Client::Patch(const char *path, size_t content_length,
ContentProvider content_provider,
const char *content_type) {
return cli_->Patch(path, content_length, content_provider, content_type);
return cli_->Patch(path, content_length, std::move(content_provider),
content_type);
}
inline Result Client::Patch(const char *path, const Headers &headers,
size_t content_length,
ContentProvider content_provider,
const char *content_type) {
return cli_->Patch(path, headers, content_length, content_provider,
return cli_->Patch(path, headers, content_length, std::move(content_provider),
content_type);
}
inline Result Client::Delete(const char *path) { return cli_->Delete(path); }
@@ -6386,7 +6466,7 @@ inline void Client::set_default_headers(Headers headers) {
inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
inline void Client::set_socket_options(SocketOptions socket_options) {
cli_->set_socket_options(socket_options);
cli_->set_socket_options(std::move(socket_options));
}
inline void Client::set_connection_timeout(time_t sec, time_t usec) {
-1
View File
@@ -1,4 +1,3 @@
#CXX = clang++
CXXFLAGS = -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion #-fsanitize=address
+36
View File
@@ -0,0 +1,36 @@
#CXX = clang++
CXXFLAGS += -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion
OPENSSL_DIR = /usr/local/opt/openssl@1.1
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
BROTLI_DIR = /usr/local/opt/brotli
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
# By default, use standalone_fuzz_target_runner.
# This runner does no fuzzing, but simply executes the inputs
# provided via parameters.
# Run e.g. "make all LIB_FUZZING_ENGINE=/path/to/libFuzzer.a"
# to link the fuzzer(s) against a real fuzzing engine.
# OSS-Fuzz will define its own value for LIB_FUZZING_ENGINE.
LIB_FUZZING_ENGINE ?= standalone_fuzz_target_runner.o
# Runs server_fuzzer.cc based on value of $(LIB_FUZZING_ENGINE).
# Usage: make fuzz_test LIB_FUZZING_ENGINE=/path/to/libFuzzer
all fuzz_test: server_fuzzer
./server_fuzzer fuzzing/corpus/*
# Fuzz target, so that you can choose which $(LIB_FUZZING_ENGINE) to use.
server_fuzzer : fuzzing/server_fuzzer.cc ../httplib.h standalone_fuzz_target_runner.o
$(CXX) $(CXXFLAGS) -o $@ $< $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread
# Standalone fuzz runner, which just reads inputs from fuzzing/corpus/ dir and
# feeds it to server_fuzzer.
standalone_fuzz_target_runner.o : fuzzing/standalone_fuzz_target_runner.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
rm -f server_fuzzer pem *.0 *.o *.1 *.srl *.zip
+26
View File
@@ -0,0 +1,26 @@
#CXX = clang++
# Do not add default sanitizer flags here as OSS-fuzz adds its own sanitizer flags.
CXXFLAGS += -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I../.. -I. -Wall -Wextra -Wtype-limits -Wconversion
OPENSSL_DIR = /usr/local/opt/openssl@1.1
# Using full path to libssl and libcrypto to avoid accidentally picking openssl libs brought in by msan.
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -I$(OPENSSL_DIR)/lib /usr/local/lib/libssl.a /usr/local/lib/libcrypto.a
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
BROTLI_DIR = /usr/local/opt/brotli
# BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
# Runs all the tests and also fuzz tests against seed corpus.
all : server_fuzzer
./server_fuzzer corpus/*
# Fuzz target, so that you can choose which $(LIB_FUZZING_ENGINE) to use.
server_fuzzer : server_fuzzer.cc ../../httplib.h
$(CXX) $(CXXFLAGS) -o $@ $< -Wl,-Bstatic $(OPENSSL_SUPPORT) -Wl,-Bdynamic -ldl $(ZLIB_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread
zip -q -r server_fuzzer_seed_corpus.zip corpus
clean:
rm -f server_fuzzer pem *.0 *.o *.1 *.srl *.zip
+1
View File
@@ -0,0 +1 @@
PUT /search/sample?a=12 HTTP/1.1
+5
View File
@@ -0,0 +1,5 @@
GET /hello.htm HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
+88
View File
@@ -0,0 +1,88 @@
#include <memory>
#include <httplib.h>
class FuzzedStream : public httplib::Stream {
public:
FuzzedStream(const uint8_t* data, size_t size)
: data_(data), size_(size), read_pos_(0) {}
ssize_t read(char* ptr, size_t size) override {
if (size + read_pos_ > size_) {
size = size_ - read_pos_;
}
memcpy(ptr, data_ + read_pos_, size);
read_pos_ += size;
return size;
}
ssize_t write(const char* ptr, size_t size) override {
response_.append(ptr, size);
return static_cast<int>(size);
}
int write(const char* ptr) { return write(ptr, strlen(ptr)); }
int write(const std::string& s) { return write(s.data(), s.size()); }
std::string get_remote_addr() const { return ""; }
bool is_readable() const override { return true; }
bool is_writable() const override { return true; }
void get_remote_ip_and_port(std::string &ip, int &port) const override {
ip = "127.0.0.1";
port = 8080;
}
private:
const uint8_t* data_;
size_t size_;
size_t read_pos_;
std::string response_;
};
class FuzzableServer : public httplib::Server {
public:
void ProcessFuzzedRequest(FuzzedStream& stream) {
bool connection_close = false;
process_request(stream, /*last_connection=*/false, connection_close,
nullptr);
}
};
static FuzzableServer g_server;
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
g_server.Get(R"(.*)",
[&](const httplib::Request& req, httplib::Response& res) {
res.set_content("response content", "text/plain");
});
g_server.Post(R"(.*)",
[&](const httplib::Request& req, httplib::Response& res) {
res.set_content("response content", "text/plain");
});
g_server.Put(R"(.*)",
[&](const httplib::Request& req, httplib::Response& res) {
res.set_content("response content", "text/plain");
});
g_server.Patch(R"(.*)",
[&](const httplib::Request& req, httplib::Response& res) {
res.set_content("response content", "text/plain");
});
g_server.Delete(R"(.*)",
[&](const httplib::Request& req, httplib::Response& res) {
res.set_content("response content", "text/plain");
});
g_server.Options(R"(.*)",
[&](const httplib::Request& req, httplib::Response& res) {
res.set_content("response content", "text/plain");
});
return 0;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzedStream stream{data, size};
g_server.ProcessFuzzedRequest(stream);
return 0;
}
+224
View File
@@ -0,0 +1,224 @@
# Sources: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
# misc
"HTTP/1.1"
# verbs
"CONNECT"
"DELETE"
"GET"
"HEAD"
"OPTIONS"
"PATCH"
"POST"
"PUT"
"TRACE"
# Webdav/caldav verbs
"ACL"
"BASELINE-CONTROL"
"BIND"
"CHECKIN"
"CHECKOUT"
"COPY"
"LABEL"
"LINK"
"LOCK"
"MERGE"
"MKACTIVITY"
"MKCALENDAR"
"MKCOL"
"MKREDIRECTREF"
"MKWORKSPACE"
"MOVE"
"ORDERPATCH"
"PRI"
"PROPFIND"
"PROPPATCH"
"REBIND"
"REPORT"
"SEARCH"
"UNBIND"
"UNCHECKOUT"
"UNLINK"
"UNLOCK"
"UPDATE"
"UPDATEREDIRECTREF"
"VERSION-CONTROL"
# Fields
"A-IM"
"Accept"
"Accept-Charset"
"Accept-Datetime"
"Accept-Encoding"
"Accept-Language"
"Accept-Patch"
"Accept-Ranges"
"Access-Control-Allow-Credentials"
"Access-Control-Allow-Headers"
"Access-Control-Allow-Methods"
"Access-Control-Allow-Origin"
"Access-Control-Expose-Headers"
"Access-Control-Max-Age"
"Access-Control-Request-Headers"
"Access-Control-Request-Method"
"Age"
"Allow"
"Alt-Svc"
"Authorization"
"Cache-Control"
"Connection"
"Connection:"
"Content-Disposition"
"Content-Encoding"
"Content-Language"
"Content-Length"
"Content-Location"
"Content-MD5"
"Content-Range"
"Content-Security-Policy"
"Content-Type"
"Cookie"
"DNT"
"Date"
"Delta-Base"
"ETag"
"Expect"
"Expires"
"Forwarded"
"From"
"Front-End-Https"
"HTTP2-Settings"
"Host"
"IM"
"If-Match"
"If-Modified-Since"
"If-None-Match"
"If-Range"
"If-Unmodified-Since"
"Last-Modified"
"Link"
"Location"
"Max-Forwards"
"Origin"
"P3P"
"Pragma"
"Proxy-Authenticate"
"Proxy-Authorization"
"Proxy-Connection"
"Public-Key-Pins"
"Range"
"Referer"
"Refresh"
"Retry-After"
"Save-Data"
"Server"
"Set-Cookie"
"Status"
"Strict-Transport-Security"
"TE"
"Timing-Allow-Origin"
"Tk"
"Trailer"
"Transfer-Encoding"
"Upgrade"
"Upgrade-Insecure-Requests"
"User-Agent"
"Vary"
"Via"
"WWW-Authenticate"
"Warning"
"X-ATT-DeviceId"
"X-Content-Duration"
"X-Content-Security-Policy"
"X-Content-Type-Options"
"X-Correlation-ID"
"X-Csrf-Token"
"X-Forwarded-For"
"X-Forwarded-Host"
"X-Forwarded-Proto"
"X-Frame-Options"
"X-Http-Method-Override"
"X-Powered-By"
"X-Request-ID"
"X-Requested-With"
"X-UA-Compatible"
"X-UIDH"
"X-Wap-Profile"
"X-WebKit-CSP"
"X-XSS-Protection"
# Source: string and character literals in httplib.h
" "
"&"
", "
"-"
"--"
"."
".."
":"
"="
" = = "
"0123456789abcdef"
"%02X"
"%0A"
"\\x0a\\x0d"
"%0D"
"%20"
"%27"
"%2B"
"%2C"
"%3A"
"%3B"
"application/javascript"
"application/json"
"application/pdf"
"application/xhtml+xml"
"application/xml"
"application/x-www-form-urlencoded"
"Bad Request"
"boundary="
"bytes="
"chunked"
"close"
"CONNECT"
"css"
"Forbidden"
"Found"
"gif"
"gzip"
"html"
"ico"
"image/gif"
"image/jpg"
"image/png"
"image/svg+xml"
"image/x-icon"
"index.html"
"Internal Server Error"
"jpeg"
"js"
"json"
"Location"
"Moved Permanently"
"multipart/form-data"
"Not Found"
"Not Modified"
"OK"
"pdf"
"png"
"Range"
"REMOTE_ADDR"
"See Other"
"svg"
"text/"
"text/css"
"text/html"
"text/plain"
"txt"
"Unsupported Media Type"
"xhtml"
"xml"
@@ -0,0 +1,35 @@
// Copyright 2017 Google Inc. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// This runner does not do any fuzzing, but allows us to run the fuzz target
// on the test corpus or on a single file,
// e.g. the one that comes from a bug report.
#include <cassert>
#include <iostream>
#include <fstream>
#include <vector>
// Forward declare the "fuzz target" interface.
// We deliberately keep this inteface simple and header-free.
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
// It reads all files passed as parameters and feeds their contents
// one by one into the fuzz target (LLVMFuzzerTestOneInput).
int main(int argc, char **argv) {
for (int i = 1; i < argc; i++) {
std::ifstream in(argv[i]);
in.seekg(0, in.end);
size_t length = in.tellg();
in.seekg (0, in.beg);
std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl;
// Allocate exactly length bytes so that we reliably catch buffer overflows.
std::vector<char> bytes(length);
in.read(bytes.data(), bytes.size());
LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t *>(bytes.data()),
bytes.size());
std::cout << "Execution successful" << std::endl;
}
std::cout << "Execution finished" << std::endl;
return 0;
}