mirror of
https://github.com/yhirose/cpp-httplib
synced 2026-06-08 18:30:49 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ccbddd8842 | |||
| 879dd261c2 | |||
| 52f5eb5980 | |||
| ea2f69a0d7 | |||
| 9f2064a8ed | |||
| e3750d9ddf | |||
| c1eee3012e | |||
| 6b08babbd2 | |||
| 215b81342e | |||
| 06bfa7e08b | |||
| 3d83cbb872 | |||
| 8a803b30f6 | |||
| 80be649de7 | |||
| 9648f950f5 | |||
| 6b9ffc8bec | |||
| d903053faf | |||
| 676f1b5a26 | |||
| b8dec12f15 |
@@ -53,6 +53,33 @@ res->body;
|
||||
1. Run server at https://repl.it/@yhirose/cpp-httplib-server
|
||||
2. Run client at https://repl.it/@yhirose/cpp-httplib-client
|
||||
|
||||
SSL Support
|
||||
-----------
|
||||
|
||||
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
|
||||
|
||||
NOTE: cpp-httplib currently supports only version 1.1.1.
|
||||
|
||||
```c++
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#include "path/to/httplib.h"
|
||||
|
||||
// Server
|
||||
httplib::SSLServer svr("./cert.pem", "./key.pem");
|
||||
|
||||
// Client
|
||||
httplib::Client cli("https://localhost:1234"); // scheme + host
|
||||
httplib::SSLClient cli("localhost:1234"); // host
|
||||
|
||||
// Use your CA bundle
|
||||
cli.set_ca_cert_path("./ca-bundle.crt");
|
||||
|
||||
// Disable cert verification
|
||||
cli.enable_server_certificate_verification(false);
|
||||
```
|
||||
|
||||
Note: When using SSL, it seems impossible to avoid SIGPIPE in all cases, since on some operating systems, SIGPIPE can only be suppressed on a per-message basis, but there is no way to make the OpenSSL library do so for its internal communications. If your program needs to avoid being terminated on SIGPIPE, the only fully general way might be to set up a signal handler for SIGPIPE to handle or ignore it yourself.
|
||||
|
||||
Server
|
||||
------
|
||||
|
||||
@@ -719,32 +746,6 @@ res = cli.Get("/resource/foo", {{"Accept-Encoding", "gzip, deflate, br"}});
|
||||
res->body; // Compressed data
|
||||
```
|
||||
|
||||
SSL Support
|
||||
-----------
|
||||
|
||||
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
|
||||
|
||||
NOTE: cpp-httplib currently supports only version 1.1.1.
|
||||
|
||||
```c++
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#include "path/to/httplib.h"
|
||||
|
||||
// Server
|
||||
httplib::SSLServer svr("./cert.pem", "./key.pem");
|
||||
|
||||
// Client
|
||||
httplib::Client cli("https://localhost:1234");
|
||||
|
||||
// Use your CA bundle
|
||||
cli.set_ca_cert_path("./ca-bundle.crt");
|
||||
|
||||
// Disable cert verification
|
||||
cli.enable_server_certificate_verification(false);
|
||||
```
|
||||
|
||||
Note: When using SSL, it seems impossible to avoid SIGPIPE in all cases, since on some operating systems, SIGPIPE can only be suppressed on a per-message basis, but there is no way to make the OpenSSL library do so for its internal communications. If your program needs to avoid being terminated on SIGPIPE, the only fully general way might be to set up a signal handler for SIGPIPE to handle or ignore it yourself.
|
||||
|
||||
Split httplib.h into .h and .cc
|
||||
-------------------------------
|
||||
|
||||
|
||||
@@ -624,35 +624,22 @@ public:
|
||||
|
||||
virtual bool is_valid() const;
|
||||
|
||||
Server &Get(const char *pattern, Handler handler);
|
||||
Server &Get(const char *pattern, size_t pattern_len, Handler handler);
|
||||
Server &Post(const char *pattern, Handler handler);
|
||||
Server &Post(const char *pattern, size_t pattern_len, Handler handler);
|
||||
Server &Post(const char *pattern, HandlerWithContentReader handler);
|
||||
Server &Post(const char *pattern, size_t pattern_len,
|
||||
HandlerWithContentReader handler);
|
||||
Server &Put(const char *pattern, Handler handler);
|
||||
Server &Put(const char *pattern, size_t pattern_len, Handler handler);
|
||||
Server &Put(const char *pattern, HandlerWithContentReader handler);
|
||||
Server &Put(const char *pattern, size_t pattern_len,
|
||||
HandlerWithContentReader handler);
|
||||
Server &Patch(const char *pattern, Handler handler);
|
||||
Server &Patch(const char *pattern, size_t pattern_len, Handler handler);
|
||||
Server &Patch(const char *pattern, HandlerWithContentReader handler);
|
||||
Server &Patch(const char *pattern, size_t pattern_len,
|
||||
HandlerWithContentReader handler);
|
||||
Server &Delete(const char *pattern, Handler handler);
|
||||
Server &Delete(const char *pattern, size_t pattern_len, Handler handler);
|
||||
Server &Delete(const char *pattern, HandlerWithContentReader handler);
|
||||
Server &Delete(const char *pattern, size_t pattern_len,
|
||||
HandlerWithContentReader handler);
|
||||
Server &Options(const char *pattern, Handler handler);
|
||||
Server &Options(const char *pattern, size_t pattern_len, Handler handler);
|
||||
Server &Get(const std::string &pattern, Handler handler);
|
||||
Server &Post(const std::string &pattern, Handler handler);
|
||||
Server &Post(const std::string &pattern, HandlerWithContentReader handler);
|
||||
Server &Put(const std::string &pattern, Handler handler);
|
||||
Server &Put(const std::string &pattern, HandlerWithContentReader handler);
|
||||
Server &Patch(const std::string &pattern, Handler handler);
|
||||
Server &Patch(const std::string &pattern, HandlerWithContentReader handler);
|
||||
Server &Delete(const std::string &pattern, Handler handler);
|
||||
Server &Delete(const std::string &pattern, HandlerWithContentReader handler);
|
||||
Server &Options(const std::string &pattern, Handler handler);
|
||||
|
||||
bool set_base_dir(const char *dir, const char *mount_point = nullptr);
|
||||
bool set_mount_point(const char *mount_point, const char *dir,
|
||||
bool set_base_dir(const std::string &dir,
|
||||
const std::string &mount_point = nullptr);
|
||||
bool set_mount_point(const std::string &mount_point, const std::string &dir,
|
||||
Headers headers = Headers());
|
||||
bool remove_mount_point(const char *mount_point);
|
||||
bool remove_mount_point(const std::string &mount_point);
|
||||
Server &set_file_extension_and_mimetype_mapping(const char *ext,
|
||||
const char *mime);
|
||||
Server &set_file_request_handler(Handler handler);
|
||||
@@ -811,8 +798,31 @@ enum class Error {
|
||||
Compression,
|
||||
};
|
||||
|
||||
inline std::string to_string(const Error error) {
|
||||
switch (error) {
|
||||
case Error::Success: return "Success";
|
||||
case Error::Connection: return "Connection";
|
||||
case Error::BindIPAddress: return "BindIPAddress";
|
||||
case Error::Read: return "Read";
|
||||
case Error::Write: return "Write";
|
||||
case Error::ExceedRedirectCount: return "ExceedRedirectCount";
|
||||
case Error::Canceled: return "Canceled";
|
||||
case Error::SSLConnection: return "SSLConnection";
|
||||
case Error::SSLLoadingCerts: return "SSLLoadingCerts";
|
||||
case Error::SSLServerVerification: return "SSLServerVerification";
|
||||
case Error::UnsupportedMultipartBoundaryChars:
|
||||
return "UnsupportedMultipartBoundaryChars";
|
||||
case Error::Compression: return "Compression";
|
||||
case Error::Unknown: return "Unknown";
|
||||
default: break;
|
||||
}
|
||||
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &os, const Error &obj) {
|
||||
os << static_cast<std::underlying_type<Error>::type>(obj);
|
||||
os << to_string(obj);
|
||||
os << " (" << static_cast<std::underlying_type<Error>::type>(obj) << ')';
|
||||
return os;
|
||||
}
|
||||
|
||||
@@ -1026,6 +1036,12 @@ public:
|
||||
void set_proxy_digest_auth(const char *username, const char *password);
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
void set_ca_cert_path(const char *ca_cert_file_path,
|
||||
const char *ca_cert_dir_path = nullptr);
|
||||
void set_ca_cert_store(X509_STORE *ca_cert_store);
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
void enable_server_certificate_verification(bool enabled);
|
||||
#endif
|
||||
@@ -1127,6 +1143,13 @@ protected:
|
||||
std::string proxy_digest_auth_password_;
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
std::string ca_cert_file_path_;
|
||||
std::string ca_cert_dir_path_;
|
||||
|
||||
X509_STORE *ca_cert_store_ = nullptr;
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
bool server_certificate_verification_ = true;
|
||||
#endif
|
||||
@@ -1153,6 +1176,8 @@ private:
|
||||
ContentProviderWithoutLength content_provider_without_length,
|
||||
const char *content_type);
|
||||
|
||||
std::string adjust_host_string(const std::string &host) const;
|
||||
|
||||
virtual bool process_socket(const Socket &socket,
|
||||
std::function<bool(Stream &strm)> callback);
|
||||
virtual bool is_ssl() const;
|
||||
@@ -1161,9 +1186,9 @@ private:
|
||||
class Client {
|
||||
public:
|
||||
// Universal interface
|
||||
explicit Client(const char *scheme_host_port);
|
||||
explicit Client(const std::string &scheme_host_port);
|
||||
|
||||
explicit Client(const char *scheme_host_port,
|
||||
explicit Client(const std::string &scheme_host_port,
|
||||
const std::string &client_cert_path,
|
||||
const std::string &client_key_path);
|
||||
|
||||
@@ -1403,9 +1428,6 @@ public:
|
||||
|
||||
bool is_valid() const override;
|
||||
|
||||
void set_ca_cert_path(const char *ca_cert_file_path,
|
||||
const char *ca_cert_dir_path = nullptr);
|
||||
|
||||
void set_ca_cert_store(X509_STORE *ca_cert_store);
|
||||
|
||||
long get_openssl_verify_result() const;
|
||||
@@ -1438,8 +1460,6 @@ private:
|
||||
|
||||
std::vector<std::string> host_components_;
|
||||
|
||||
std::string ca_cert_file_path_;
|
||||
std::string ca_cert_dir_path_;
|
||||
long verify_result_ = 0;
|
||||
|
||||
friend class ClientImpl;
|
||||
@@ -2334,7 +2354,7 @@ inline unsigned int str2tag(const std::string &s) {
|
||||
|
||||
namespace udl {
|
||||
|
||||
inline constexpr unsigned int operator"" _(const char *s, size_t l) {
|
||||
inline constexpr unsigned int operator"" _t(const char *s, size_t l) {
|
||||
return str2tag_core(s, l, 0);
|
||||
}
|
||||
|
||||
@@ -2348,59 +2368,59 @@ find_content_type(const std::string &path,
|
||||
auto it = user_data.find(ext);
|
||||
if (it != user_data.end()) { return it->second.c_str(); }
|
||||
|
||||
using udl::operator""_;
|
||||
using udl::operator""_t;
|
||||
|
||||
switch (str2tag(ext)) {
|
||||
default: return nullptr;
|
||||
case "css"_: return "text/css";
|
||||
case "csv"_: return "text/csv";
|
||||
case "txt"_: return "text/plain";
|
||||
case "vtt"_: return "text/vtt";
|
||||
case "htm"_:
|
||||
case "html"_: return "text/html";
|
||||
case "css"_t: return "text/css";
|
||||
case "csv"_t: return "text/csv";
|
||||
case "txt"_t: return "text/plain";
|
||||
case "vtt"_t: return "text/vtt";
|
||||
case "htm"_t:
|
||||
case "html"_t: return "text/html";
|
||||
|
||||
case "apng"_: return "image/apng";
|
||||
case "avif"_: return "image/avif";
|
||||
case "bmp"_: return "image/bmp";
|
||||
case "gif"_: return "image/gif";
|
||||
case "png"_: return "image/png";
|
||||
case "svg"_: return "image/svg+xml";
|
||||
case "webp"_: return "image/webp";
|
||||
case "ico"_: return "image/x-icon";
|
||||
case "tif"_: return "image/tiff";
|
||||
case "tiff"_: return "image/tiff";
|
||||
case "jpg"_:
|
||||
case "jpeg"_: return "image/jpeg";
|
||||
case "apng"_t: return "image/apng";
|
||||
case "avif"_t: return "image/avif";
|
||||
case "bmp"_t: return "image/bmp";
|
||||
case "gif"_t: return "image/gif";
|
||||
case "png"_t: return "image/png";
|
||||
case "svg"_t: return "image/svg+xml";
|
||||
case "webp"_t: return "image/webp";
|
||||
case "ico"_t: return "image/x-icon";
|
||||
case "tif"_t: return "image/tiff";
|
||||
case "tiff"_t: return "image/tiff";
|
||||
case "jpg"_t:
|
||||
case "jpeg"_t: return "image/jpeg";
|
||||
|
||||
case "mp4"_: return "video/mp4";
|
||||
case "mpeg"_: return "video/mpeg";
|
||||
case "webm"_: return "video/webm";
|
||||
case "mp4"_t: return "video/mp4";
|
||||
case "mpeg"_t: return "video/mpeg";
|
||||
case "webm"_t: return "video/webm";
|
||||
|
||||
case "mp3"_: return "audio/mp3";
|
||||
case "mpga"_: return "audio/mpeg";
|
||||
case "weba"_: return "audio/webm";
|
||||
case "wav"_: return "audio/wave";
|
||||
case "mp3"_t: return "audio/mp3";
|
||||
case "mpga"_t: return "audio/mpeg";
|
||||
case "weba"_t: return "audio/webm";
|
||||
case "wav"_t: return "audio/wave";
|
||||
|
||||
case "otf"_: return "font/otf";
|
||||
case "ttf"_: return "font/ttf";
|
||||
case "woff"_: return "font/woff";
|
||||
case "woff2"_: return "font/woff2";
|
||||
case "otf"_t: return "font/otf";
|
||||
case "ttf"_t: return "font/ttf";
|
||||
case "woff"_t: return "font/woff";
|
||||
case "woff2"_t: return "font/woff2";
|
||||
|
||||
case "7z"_: return "application/x-7z-compressed";
|
||||
case "atom"_: return "application/atom+xml";
|
||||
case "pdf"_: return "application/pdf";
|
||||
case "js"_:
|
||||
case "mjs"_: return "application/javascript";
|
||||
case "json"_: return "application/json";
|
||||
case "rss"_: return "application/rss+xml";
|
||||
case "tar"_: return "application/x-tar";
|
||||
case "xht"_:
|
||||
case "xhtml"_: return "application/xhtml+xml";
|
||||
case "xslt"_: return "application/xslt+xml";
|
||||
case "xml"_: return "application/xml";
|
||||
case "gz"_: return "application/gzip";
|
||||
case "zip"_: return "application/zip";
|
||||
case "wasm"_: return "application/wasm";
|
||||
case "7z"_t: return "application/x-7z-compressed";
|
||||
case "atom"_t: return "application/atom+xml";
|
||||
case "pdf"_t: return "application/pdf";
|
||||
case "js"_t:
|
||||
case "mjs"_t: return "application/javascript";
|
||||
case "json"_t: return "application/json";
|
||||
case "rss"_t: return "application/rss+xml";
|
||||
case "tar"_t: return "application/x-tar";
|
||||
case "xht"_t:
|
||||
case "xhtml"_t: return "application/xhtml+xml";
|
||||
case "xslt"_t: return "application/xslt+xml";
|
||||
case "xml"_t: return "application/xml";
|
||||
case "gz"_t: return "application/gzip";
|
||||
case "zip"_t: return "application/zip";
|
||||
case "wasm"_t: return "application/wasm";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2558,28 +2578,40 @@ public:
|
||||
Callback callback) override {
|
||||
assert(is_valid_);
|
||||
|
||||
auto flush = last ? Z_FINISH : Z_NO_FLUSH;
|
||||
|
||||
strm_.avail_in = static_cast<decltype(strm_.avail_in)>(data_length);
|
||||
strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
|
||||
|
||||
int ret = Z_OK;
|
||||
|
||||
std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
|
||||
do {
|
||||
strm_.avail_out = static_cast<uInt>(buff.size());
|
||||
strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
|
||||
constexpr size_t max_avail_in =
|
||||
std::numeric_limits<decltype(strm_.avail_in)>::max();
|
||||
|
||||
ret = deflate(&strm_, flush);
|
||||
if (ret == Z_STREAM_ERROR) { return false; }
|
||||
strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
|
||||
std::min(data_length, max_avail_in));
|
||||
strm_.next_in =
|
||||
const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
|
||||
|
||||
if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
|
||||
return false;
|
||||
}
|
||||
} while (strm_.avail_out == 0);
|
||||
data_length -= strm_.avail_in;
|
||||
data += strm_.avail_in;
|
||||
|
||||
auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;
|
||||
int ret = Z_OK;
|
||||
|
||||
std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
|
||||
do {
|
||||
strm_.avail_out = static_cast<uInt>(buff.size());
|
||||
strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
|
||||
|
||||
ret = deflate(&strm_, flush);
|
||||
if (ret == Z_STREAM_ERROR) { return false; }
|
||||
|
||||
if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
|
||||
return false;
|
||||
}
|
||||
} while (strm_.avail_out == 0);
|
||||
|
||||
assert((flush == Z_FINISH && ret == Z_STREAM_END) ||
|
||||
(flush == Z_NO_FLUSH && ret == Z_OK));
|
||||
assert(strm_.avail_in == 0);
|
||||
|
||||
} while (data_length > 0);
|
||||
|
||||
assert((last && ret == Z_STREAM_END) || (!last && ret == Z_OK));
|
||||
assert(strm_.avail_in == 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2613,28 +2645,41 @@ public:
|
||||
|
||||
int ret = Z_OK;
|
||||
|
||||
strm_.avail_in = static_cast<decltype(strm_.avail_in)>(data_length);
|
||||
strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
|
||||
do {
|
||||
constexpr size_t max_avail_in =
|
||||
std::numeric_limits<decltype(strm_.avail_in)>::max();
|
||||
|
||||
std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
|
||||
while (strm_.avail_in > 0) {
|
||||
strm_.avail_out = static_cast<uInt>(buff.size());
|
||||
strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
|
||||
strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
|
||||
std::min(data_length, max_avail_in));
|
||||
strm_.next_in =
|
||||
const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
|
||||
|
||||
ret = inflate(&strm_, Z_NO_FLUSH);
|
||||
assert(ret != Z_STREAM_ERROR);
|
||||
switch (ret) {
|
||||
case Z_NEED_DICT:
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR: inflateEnd(&strm_); return false;
|
||||
data_length -= strm_.avail_in;
|
||||
data += strm_.avail_in;
|
||||
|
||||
std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
|
||||
while (strm_.avail_in > 0) {
|
||||
strm_.avail_out = static_cast<uInt>(buff.size());
|
||||
strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
|
||||
|
||||
ret = inflate(&strm_, Z_NO_FLUSH);
|
||||
assert(ret != Z_STREAM_ERROR);
|
||||
switch (ret) {
|
||||
case Z_NEED_DICT:
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR: inflateEnd(&strm_); return false;
|
||||
}
|
||||
|
||||
if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (ret != Z_OK && ret != Z_STREAM_END) return false;
|
||||
|
||||
return ret == Z_OK || ret == Z_STREAM_END;
|
||||
} while (data_length > 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -4225,128 +4270,79 @@ inline Server::Server()
|
||||
|
||||
inline Server::~Server() {}
|
||||
|
||||
inline Server &Server::Get(const char *pattern, Handler handler) {
|
||||
return Get(pattern, strlen(pattern), handler);
|
||||
}
|
||||
|
||||
inline Server &Server::Get(const char *pattern, size_t pattern_len,
|
||||
Handler handler) {
|
||||
inline Server &Server::Get(const std::string &pattern, Handler handler) {
|
||||
get_handlers_.push_back(
|
||||
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
|
||||
std::make_pair(std::regex(pattern), std::move(handler)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Server &Server::Post(const char *pattern, Handler handler) {
|
||||
return Post(pattern, strlen(pattern), handler);
|
||||
}
|
||||
|
||||
inline Server &Server::Post(const char *pattern, size_t pattern_len,
|
||||
Handler handler) {
|
||||
inline Server &Server::Post(const std::string &pattern, Handler handler) {
|
||||
post_handlers_.push_back(
|
||||
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
|
||||
std::make_pair(std::regex(pattern), std::move(handler)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Server &Server::Post(const char *pattern,
|
||||
HandlerWithContentReader handler) {
|
||||
return Post(pattern, strlen(pattern), handler);
|
||||
}
|
||||
|
||||
inline Server &Server::Post(const char *pattern, size_t pattern_len,
|
||||
inline Server &Server::Post(const std::string &pattern,
|
||||
HandlerWithContentReader handler) {
|
||||
post_handlers_for_content_reader_.push_back(
|
||||
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
|
||||
std::make_pair(std::regex(pattern), std::move(handler)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Server &Server::Put(const char *pattern, Handler handler) {
|
||||
return Put(pattern, strlen(pattern), handler);
|
||||
}
|
||||
|
||||
inline Server &Server::Put(const char *pattern, size_t pattern_len,
|
||||
Handler handler) {
|
||||
inline Server &Server::Put(const std::string &pattern, Handler handler) {
|
||||
put_handlers_.push_back(
|
||||
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
|
||||
std::make_pair(std::regex(pattern), std::move(handler)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Server &Server::Put(const char *pattern,
|
||||
HandlerWithContentReader handler) {
|
||||
return Put(pattern, strlen(pattern), handler);
|
||||
}
|
||||
|
||||
inline Server &Server::Put(const char *pattern, size_t pattern_len,
|
||||
inline Server &Server::Put(const std::string &pattern,
|
||||
HandlerWithContentReader handler) {
|
||||
put_handlers_for_content_reader_.push_back(
|
||||
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
|
||||
std::make_pair(std::regex(pattern), std::move(handler)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Server &Server::Patch(const char *pattern, Handler handler) {
|
||||
return Patch(pattern, strlen(pattern), handler);
|
||||
}
|
||||
|
||||
inline Server &Server::Patch(const char *pattern, size_t pattern_len,
|
||||
Handler handler) {
|
||||
inline Server &Server::Patch(const std::string &pattern, Handler handler) {
|
||||
patch_handlers_.push_back(
|
||||
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
|
||||
std::make_pair(std::regex(pattern), std::move(handler)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Server &Server::Patch(const char *pattern,
|
||||
HandlerWithContentReader handler) {
|
||||
return Patch(pattern, strlen(pattern), handler);
|
||||
}
|
||||
|
||||
inline Server &Server::Patch(const char *pattern, size_t pattern_len,
|
||||
inline Server &Server::Patch(const std::string &pattern,
|
||||
HandlerWithContentReader handler) {
|
||||
patch_handlers_for_content_reader_.push_back(
|
||||
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
|
||||
std::make_pair(std::regex(pattern), std::move(handler)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Server &Server::Delete(const char *pattern, Handler handler) {
|
||||
return Delete(pattern, strlen(pattern), handler);
|
||||
}
|
||||
|
||||
inline Server &Server::Delete(const char *pattern, size_t pattern_len,
|
||||
Handler handler) {
|
||||
inline Server &Server::Delete(const std::string &pattern, Handler handler) {
|
||||
delete_handlers_.push_back(
|
||||
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
|
||||
std::make_pair(std::regex(pattern), std::move(handler)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Server &Server::Delete(const char *pattern,
|
||||
HandlerWithContentReader handler) {
|
||||
return Delete(pattern, strlen(pattern), handler);
|
||||
}
|
||||
|
||||
inline Server &Server::Delete(const char *pattern, size_t pattern_len,
|
||||
inline Server &Server::Delete(const std::string &pattern,
|
||||
HandlerWithContentReader handler) {
|
||||
delete_handlers_for_content_reader_.push_back(
|
||||
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
|
||||
std::make_pair(std::regex(pattern), std::move(handler)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Server &Server::Options(const char *pattern, Handler handler) {
|
||||
return Options(pattern, strlen(pattern), handler);
|
||||
}
|
||||
|
||||
inline Server &Server::Options(const char *pattern, size_t pattern_len,
|
||||
Handler handler) {
|
||||
inline Server &Server::Options(const std::string &pattern, Handler handler) {
|
||||
options_handlers_.push_back(
|
||||
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
|
||||
std::make_pair(std::regex(pattern), std::move(handler)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool Server::set_base_dir(const char *dir, const char *mount_point) {
|
||||
inline bool Server::set_base_dir(const std::string &dir,
|
||||
const std::string &mount_point) {
|
||||
return set_mount_point(mount_point, dir);
|
||||
}
|
||||
|
||||
inline bool Server::set_mount_point(const char *mount_point, const char *dir,
|
||||
Headers headers) {
|
||||
inline bool Server::set_mount_point(const std::string &mount_point,
|
||||
const std::string &dir, Headers headers) {
|
||||
if (detail::is_dir(dir)) {
|
||||
std::string mnt = mount_point ? mount_point : "/";
|
||||
std::string mnt = !mount_point.empty() ? mount_point : "/";
|
||||
if (!mnt.empty() && mnt[0] == '/') {
|
||||
base_dirs_.push_back({mnt, dir, std::move(headers)});
|
||||
return true;
|
||||
@@ -4355,7 +4351,7 @@ inline bool Server::set_mount_point(const char *mount_point, const char *dir,
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool Server::remove_mount_point(const char *mount_point) {
|
||||
inline bool Server::remove_mount_point(const std::string &mount_point) {
|
||||
for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
|
||||
if (it->mount_point == mount_point) {
|
||||
base_dirs_.erase(it);
|
||||
@@ -4520,25 +4516,58 @@ inline void Server::stop() {
|
||||
}
|
||||
|
||||
inline bool Server::parse_request_line(const char *s, Request &req) {
|
||||
const static std::regex re(
|
||||
"(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) "
|
||||
"(([^? ]+)(?:\\?([^ ]*?))?) (HTTP/1\\.[01])\r\n");
|
||||
auto len = strlen(s);
|
||||
if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') { return false; }
|
||||
len -= 2;
|
||||
|
||||
std::cmatch m;
|
||||
if (std::regex_match(s, m, re)) {
|
||||
req.version = std::string(m[5]);
|
||||
req.method = std::string(m[1]);
|
||||
req.target = std::string(m[2]);
|
||||
req.path = detail::decode_url(m[3], false);
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
// Parse query text
|
||||
auto len = std::distance(m[4].first, m[4].second);
|
||||
if (len > 0) { detail::parse_query_text(m[4], req.params); }
|
||||
detail::split(s, s + len, ' ', [&](const char *b, const char *e) {
|
||||
switch (count) {
|
||||
case 0: req.method = std::string(b, e); break;
|
||||
case 1: req.target = std::string(b, e); break;
|
||||
case 2: req.version = std::string(b, e); break;
|
||||
default: break;
|
||||
}
|
||||
count++;
|
||||
});
|
||||
|
||||
return true;
|
||||
if (count != 3) { return false; }
|
||||
}
|
||||
|
||||
return false;
|
||||
static const std::set<std::string> methods{
|
||||
"GET", "HEAD", "POST", "PUT", "DELETE",
|
||||
"CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"};
|
||||
|
||||
if (methods.find(req.method) == methods.end()) { return false; }
|
||||
|
||||
if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") { return false; }
|
||||
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
detail::split(req.target.data(), req.target.data() + req.target.size(), '?',
|
||||
[&](const char *b, const char *e) {
|
||||
switch (count) {
|
||||
case 0:
|
||||
req.path = detail::decode_url(std::string(b, e), false);
|
||||
break;
|
||||
case 1: {
|
||||
if (e - b > 0) {
|
||||
detail::parse_query_text(std::string(b, e), req.params);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
count++;
|
||||
});
|
||||
|
||||
if (count > 2) { return false; }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool Server::write_response(Stream &strm, bool close_connection,
|
||||
@@ -4615,8 +4644,7 @@ inline bool Server::write_response_core(Stream &strm, bool close_connection,
|
||||
if (!res.body.empty()) {
|
||||
if (!strm.write(res.body)) { ret = false; }
|
||||
} else if (res.content_provider_) {
|
||||
if (write_content_with_provider(strm, req, res, boundary,
|
||||
content_type)) {
|
||||
if (write_content_with_provider(strm, req, res, boundary, content_type)) {
|
||||
res.content_provider_success_ = true;
|
||||
} else {
|
||||
res.content_provider_success_ = false;
|
||||
@@ -5269,9 +5297,8 @@ inline ClientImpl::ClientImpl(const std::string &host, int port)
|
||||
inline ClientImpl::ClientImpl(const std::string &host, int port,
|
||||
const std::string &client_cert_path,
|
||||
const std::string &client_key_path)
|
||||
// : (Error::Success), host_(host), port_(port),
|
||||
: host_(host), port_(port),
|
||||
host_and_port_(host_ + ":" + std::to_string(port_)),
|
||||
host_and_port_(adjust_host_string(host) + ":" + std::to_string(port)),
|
||||
client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
|
||||
|
||||
inline ClientImpl::~ClientImpl() {
|
||||
@@ -5315,6 +5342,11 @@ inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
|
||||
proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
|
||||
proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
|
||||
#endif
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
ca_cert_file_path_ = rhs.ca_cert_file_path_;
|
||||
ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
|
||||
ca_cert_store_ = rhs.ca_cert_store_;
|
||||
#endif
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
server_certificate_verification_ = rhs.server_certificate_verification_;
|
||||
#endif
|
||||
@@ -5551,8 +5583,8 @@ inline bool ClientImpl::handle_request(Stream &strm, Request &req,
|
||||
if (detail::parse_www_authenticate(res, auth, is_proxy)) {
|
||||
Request new_req = req;
|
||||
new_req.authorization_count_ += 1;
|
||||
auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
|
||||
new_req.headers.erase(key);
|
||||
new_req.headers.erase(is_proxy ? "Proxy-Authorization"
|
||||
: "Authorization");
|
||||
new_req.headers.insert(detail::make_digest_authentication_header(
|
||||
req, auth, new_req.authorization_count_, detail::random_string(10),
|
||||
username, password, is_proxy));
|
||||
@@ -5579,7 +5611,7 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
|
||||
if (location.empty()) { return false; }
|
||||
|
||||
const static std::regex re(
|
||||
R"(^(?:(https?):)?(?://([^:/?#]*)(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
|
||||
R"((?:(https?):)?(?://(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
|
||||
|
||||
std::smatch m;
|
||||
if (!std::regex_match(location, m, re)) { return false; }
|
||||
@@ -5588,8 +5620,9 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
|
||||
|
||||
auto next_scheme = m[1].str();
|
||||
auto next_host = m[2].str();
|
||||
auto port_str = m[3].str();
|
||||
auto next_path = m[4].str();
|
||||
if (next_host.empty()) { next_host = m[3].str(); }
|
||||
auto port_str = m[4].str();
|
||||
auto next_path = m[5].str();
|
||||
|
||||
auto next_port = port_;
|
||||
if (!port_str.empty()) {
|
||||
@@ -5609,6 +5642,9 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
SSLClient cli(next_host.c_str(), next_port);
|
||||
cli.copy_settings(*this);
|
||||
if (ca_cert_store_) {
|
||||
cli.set_ca_cert_store(ca_cert_store_);
|
||||
}
|
||||
return detail::redirect(cli, req, res, next_path, location, error);
|
||||
#else
|
||||
return false;
|
||||
@@ -5649,7 +5685,11 @@ inline bool ClientImpl::write_content_with_provider(Stream &strm,
|
||||
inline bool ClientImpl::write_request(Stream &strm, Request &req,
|
||||
bool close_connection, Error &error) {
|
||||
// Prepare additional headers
|
||||
if (close_connection) { req.headers.emplace("Connection", "close"); }
|
||||
if (close_connection) {
|
||||
if (!req.has_header("Connection")) {
|
||||
req.headers.emplace("Connection", "close");
|
||||
}
|
||||
}
|
||||
|
||||
if (!req.has_header("Host")) {
|
||||
if (is_ssl()) {
|
||||
@@ -5670,14 +5710,16 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
|
||||
if (!req.has_header("Accept")) { req.headers.emplace("Accept", "*/*"); }
|
||||
|
||||
if (!req.has_header("User-Agent")) {
|
||||
req.headers.emplace("User-Agent", "cpp-httplib/0.7");
|
||||
req.headers.emplace("User-Agent", "cpp-httplib/0.9");
|
||||
}
|
||||
|
||||
if (req.body.empty()) {
|
||||
if (req.content_provider_) {
|
||||
if (!req.is_chunked_content_provider_) {
|
||||
auto length = std::to_string(req.content_length_);
|
||||
req.headers.emplace("Content-Length", length);
|
||||
if (!req.has_header("Content-Length")) {
|
||||
auto length = std::to_string(req.content_length_);
|
||||
req.headers.emplace("Content-Length", length);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (req.method == "POST" || req.method == "PUT" ||
|
||||
@@ -5697,24 +5739,32 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
|
||||
}
|
||||
|
||||
if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) {
|
||||
req.headers.insert(make_basic_authentication_header(
|
||||
basic_auth_username_, basic_auth_password_, false));
|
||||
if (!req.has_header("Authorization")) {
|
||||
req.headers.insert(make_basic_authentication_header(
|
||||
basic_auth_username_, basic_auth_password_, false));
|
||||
}
|
||||
}
|
||||
|
||||
if (!proxy_basic_auth_username_.empty() &&
|
||||
!proxy_basic_auth_password_.empty()) {
|
||||
req.headers.insert(make_basic_authentication_header(
|
||||
proxy_basic_auth_username_, proxy_basic_auth_password_, true));
|
||||
if (!req.has_header("Proxy-Authorization")) {
|
||||
req.headers.insert(make_basic_authentication_header(
|
||||
proxy_basic_auth_username_, proxy_basic_auth_password_, true));
|
||||
}
|
||||
}
|
||||
|
||||
if (!bearer_token_auth_token_.empty()) {
|
||||
req.headers.insert(make_bearer_token_authentication_header(
|
||||
bearer_token_auth_token_, false));
|
||||
if (!req.has_header("Authorization")) {
|
||||
req.headers.insert(make_bearer_token_authentication_header(
|
||||
bearer_token_auth_token_, false));
|
||||
}
|
||||
}
|
||||
|
||||
if (!proxy_bearer_token_auth_token_.empty()) {
|
||||
req.headers.insert(make_bearer_token_authentication_header(
|
||||
proxy_bearer_token_auth_token_, true));
|
||||
if (!req.has_header("Proxy-Authorization")) {
|
||||
req.headers.insert(make_bearer_token_authentication_header(
|
||||
proxy_bearer_token_auth_token_, true));
|
||||
}
|
||||
}
|
||||
|
||||
// Request line and headers
|
||||
@@ -5737,11 +5787,9 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
|
||||
// Body
|
||||
if (req.body.empty()) {
|
||||
return write_content_with_provider(strm, req, error);
|
||||
} else {
|
||||
return detail::write_data(strm, req.body.data(), req.body.size());
|
||||
}
|
||||
|
||||
return true;
|
||||
return detail::write_data(strm, req.body.data(), req.body.size());
|
||||
}
|
||||
|
||||
inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
|
||||
@@ -5853,6 +5901,12 @@ inline Result ClientImpl::send_with_content_provider(
|
||||
return Result{std::move(res), error, std::move(req.headers)};
|
||||
}
|
||||
|
||||
inline std::string
|
||||
ClientImpl::adjust_host_string(const std::string &host) const {
|
||||
if (host.find(':') != std::string::npos) { return "[" + host + "]"; }
|
||||
return host;
|
||||
}
|
||||
|
||||
inline bool ClientImpl::process_request(Stream &strm, Request &req,
|
||||
Response &res, bool close_connection,
|
||||
Error &error) {
|
||||
@@ -6498,6 +6552,20 @@ inline void ClientImpl::set_proxy_digest_auth(const char *username,
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
inline void ClientImpl::set_ca_cert_path(const char *ca_cert_file_path,
|
||||
const char *ca_cert_dir_path) {
|
||||
if (ca_cert_file_path) { ca_cert_file_path_ = ca_cert_file_path; }
|
||||
if (ca_cert_dir_path) { ca_cert_dir_path_ = ca_cert_dir_path; }
|
||||
}
|
||||
|
||||
inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {
|
||||
if (ca_cert_store && ca_cert_store != ca_cert_store_) {
|
||||
ca_cert_store_ = ca_cert_store;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
inline void ClientImpl::enable_server_certificate_verification(bool enabled) {
|
||||
server_certificate_verification_ = enabled;
|
||||
@@ -6687,15 +6755,18 @@ inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
|
||||
auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
|
||||
if (ret < 0) {
|
||||
auto err = SSL_get_error(ssl_, ret);
|
||||
int n = 1000;
|
||||
#ifdef _WIN32
|
||||
while (err == SSL_ERROR_WANT_READ ||
|
||||
err == SSL_ERROR_SYSCALL && WSAGetLastError() == WSAETIMEDOUT) {
|
||||
while (--n >= 0 &&
|
||||
(err == SSL_ERROR_WANT_READ ||
|
||||
err == SSL_ERROR_SYSCALL && WSAGetLastError() == WSAETIMEDOUT)) {
|
||||
#else
|
||||
while (err == SSL_ERROR_WANT_READ) {
|
||||
while (--n >= 0 && err == SSL_ERROR_WANT_READ) {
|
||||
#endif
|
||||
if (SSL_pending(ssl_) > 0) {
|
||||
return SSL_read(ssl_, ptr, static_cast<int>(size));
|
||||
} else if (is_readable()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
ret = SSL_read(ssl_, ptr, static_cast<int>(size));
|
||||
if (ret >= 0) { return ret; }
|
||||
err = SSL_get_error(ssl_, ret);
|
||||
@@ -6885,12 +6956,6 @@ inline SSLClient::~SSLClient() {
|
||||
|
||||
inline bool SSLClient::is_valid() const { return ctx_; }
|
||||
|
||||
inline void SSLClient::set_ca_cert_path(const char *ca_cert_file_path,
|
||||
const char *ca_cert_dir_path) {
|
||||
if (ca_cert_file_path) { ca_cert_file_path_ = ca_cert_file_path; }
|
||||
if (ca_cert_dir_path) { ca_cert_dir_path_ = ca_cert_dir_path; }
|
||||
}
|
||||
|
||||
inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
|
||||
if (ca_cert_store) {
|
||||
if (ctx_) {
|
||||
@@ -7213,15 +7278,16 @@ inline bool SSLClient::check_host_name(const char *pattern,
|
||||
#endif
|
||||
|
||||
// Universal client implementation
|
||||
inline Client::Client(const char *scheme_host_port)
|
||||
inline Client::Client(const std::string &scheme_host_port)
|
||||
: Client(scheme_host_port, std::string(), std::string()) {}
|
||||
|
||||
inline Client::Client(const char *scheme_host_port,
|
||||
inline Client::Client(const std::string &scheme_host_port,
|
||||
const std::string &client_cert_path,
|
||||
const std::string &client_key_path) {
|
||||
const static std::regex re(R"(^(?:([a-z]+)://)?([^:/?#]+)(?::(\d+))?)");
|
||||
const static std::regex re(
|
||||
R"((?:([a-z]+):\/\/)?(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)");
|
||||
|
||||
std::cmatch m;
|
||||
std::smatch m;
|
||||
if (std::regex_match(scheme_host_port, m, re)) {
|
||||
auto scheme = m[1].str();
|
||||
|
||||
@@ -7238,8 +7304,9 @@ inline Client::Client(const char *scheme_host_port,
|
||||
auto is_ssl = scheme == "https";
|
||||
|
||||
auto host = m[2].str();
|
||||
if (host.empty()) { host = m[3].str(); }
|
||||
|
||||
auto port_str = m[3].str();
|
||||
auto port_str = m[4].str();
|
||||
auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
|
||||
|
||||
if (is_ssl) {
|
||||
@@ -7631,15 +7698,14 @@ inline void Client::set_logger(Logger logger) { cli_->set_logger(logger); }
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
inline void Client::set_ca_cert_path(const char *ca_cert_file_path,
|
||||
const char *ca_cert_dir_path) {
|
||||
if (is_ssl_) {
|
||||
static_cast<SSLClient &>(*cli_).set_ca_cert_path(ca_cert_file_path,
|
||||
ca_cert_dir_path);
|
||||
}
|
||||
cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
|
||||
}
|
||||
|
||||
inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
|
||||
if (is_ssl_) {
|
||||
static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);
|
||||
} else {
|
||||
cli_->set_ca_cert_store(ca_cert_store);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#CXX = clang++
|
||||
CXXFLAGS = -g -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion #-fsanitize=address
|
||||
CXXFLAGS = -g -std=c++11 -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion #-fsanitize=address
|
||||
|
||||
OPENSSL_DIR = /usr/local/opt/openssl@1.1
|
||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||
|
||||
+5790
-2421
File diff suppressed because it is too large
Load Diff
+6845
-14039
File diff suppressed because it is too large
Load Diff
@@ -27,13 +27,28 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <cstdio>
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
GTEST_API_ int main(int argc, char **argv) {
|
||||
std::cout << "Running main() from gtest_main.cc\n";
|
||||
#if GTEST_OS_ESP8266 || GTEST_OS_ESP32
|
||||
#if GTEST_OS_ESP8266
|
||||
extern "C" {
|
||||
#endif
|
||||
void setup() {
|
||||
testing::InitGoogleTest();
|
||||
}
|
||||
|
||||
void loop() { RUN_ALL_TESTS(); }
|
||||
|
||||
#if GTEST_OS_ESP8266
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
GTEST_API_ int main(int argc, char **argv) {
|
||||
printf("Running main() from %s\n", __FILE__);
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
#endif
|
||||
|
||||
+242
-64
@@ -172,7 +172,7 @@ TEST(GetHeaderValueTest, SetContent) {
|
||||
EXPECT_EQ("text/html", res.get_header_value("Content-Type"));
|
||||
|
||||
res.set_content("text", "text/plain");
|
||||
EXPECT_EQ(1, res.get_header_value_count("Content-Type"));
|
||||
EXPECT_EQ(1U, res.get_header_value_count("Content-Type"));
|
||||
EXPECT_EQ("text/plain", res.get_header_value("Content-Type"));
|
||||
}
|
||||
|
||||
@@ -542,7 +542,7 @@ TEST(ConnectionErrorTest, InvalidHostCheckResultErrorToString) {
|
||||
ASSERT_TRUE(!res);
|
||||
stringstream s;
|
||||
s << "error code: " << res.error();
|
||||
EXPECT_EQ("error code: 2", s.str());
|
||||
EXPECT_EQ("error code: Connection (2)", s.str());
|
||||
}
|
||||
|
||||
TEST(ConnectionErrorTest, InvalidPort) {
|
||||
@@ -839,6 +839,18 @@ TEST(HttpsToHttpRedirectTest3, Redirect) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(UrlWithSpace, Redirect) {
|
||||
SSLClient cli("edge.forgecdn.net");
|
||||
cli.set_follow_location(true);
|
||||
|
||||
auto res = cli.Get("/files/2595/310/Neat 1.4-17.jar");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ(18527U, res->get_header_value<uint64_t>("Content-Length"));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
TEST(RedirectToDifferentPort, Redirect) {
|
||||
Server svr8080;
|
||||
Server svr8081;
|
||||
@@ -878,16 +890,6 @@ TEST(RedirectToDifferentPort, Redirect) {
|
||||
ASSERT_FALSE(svr8081.is_running());
|
||||
}
|
||||
|
||||
TEST(UrlWithSpace, Redirect) {
|
||||
SSLClient cli("edge.forgecdn.net");
|
||||
cli.set_follow_location(true);
|
||||
|
||||
auto res = cli.Get("/files/2595/310/Neat 1.4-17.jar");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ(18527, res->get_header_value<uint64_t>("Content-Length"));
|
||||
}
|
||||
|
||||
TEST(RedirectFromPageWithContent, Redirect) {
|
||||
Server svr;
|
||||
|
||||
@@ -943,7 +945,65 @@ TEST(RedirectFromPageWithContent, Redirect) {
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
#endif
|
||||
TEST(RedirectFromPageWithContentIP6, Redirect) {
|
||||
Server svr;
|
||||
|
||||
svr.Get("/1", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_content("___", "text/plain");
|
||||
// res.set_redirect("/2");
|
||||
res.set_redirect("http://[::1]:1234/2");
|
||||
});
|
||||
|
||||
svr.Get("/2", [&](const Request &req, Response &res) {
|
||||
auto host_header = req.headers.find("Host");
|
||||
ASSERT_TRUE(host_header != req.headers.end());
|
||||
EXPECT_EQ("[::1]:1234", host_header->second);
|
||||
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
|
||||
auto th = std::thread([&]() { svr.listen("::1", 1234); });
|
||||
|
||||
while (!svr.is_running()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
// Give GET time to get a few messages.
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
{
|
||||
Client cli("http://[::1]:1234");
|
||||
cli.set_follow_location(true);
|
||||
|
||||
std::string body;
|
||||
auto res = cli.Get("/1", [&](const char *data, size_t data_length) {
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("Hello World!", body);
|
||||
}
|
||||
|
||||
{
|
||||
Client cli("http://[::1]:1234");
|
||||
|
||||
std::string body;
|
||||
auto res = cli.Get("/1", [&](const char *data, size_t data_length) {
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(302, res->status);
|
||||
EXPECT_EQ("___", body);
|
||||
}
|
||||
|
||||
svr.stop();
|
||||
th.join();
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
TEST(PathUrlEncodeTest, PathUrlEncode) {
|
||||
Server svr;
|
||||
@@ -1166,9 +1226,9 @@ TEST(RoutingHandlerTest, PreRoutingHandler) {
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("Routing Handler", res->body);
|
||||
EXPECT_EQ(1, res->get_header_value_count("PRE_ROUTING"));
|
||||
EXPECT_EQ(1U, res->get_header_value_count("PRE_ROUTING"));
|
||||
EXPECT_EQ("on", res->get_header_value("PRE_ROUTING"));
|
||||
EXPECT_EQ(1, res->get_header_value_count("POST_ROUTING"));
|
||||
EXPECT_EQ(1U, res->get_header_value_count("POST_ROUTING"));
|
||||
EXPECT_EQ("on", res->get_header_value("POST_ROUTING"));
|
||||
}
|
||||
|
||||
@@ -1179,8 +1239,8 @@ TEST(RoutingHandlerTest, PreRoutingHandler) {
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("Hello World!\n", res->body);
|
||||
EXPECT_EQ(0, res->get_header_value_count("PRE_ROUTING"));
|
||||
EXPECT_EQ(0, res->get_header_value_count("POST_ROUTING"));
|
||||
EXPECT_EQ(0U, res->get_header_value_count("PRE_ROUTING"));
|
||||
EXPECT_EQ(0U, res->get_header_value_count("POST_ROUTING"));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1190,8 +1250,8 @@ TEST(RoutingHandlerTest, PreRoutingHandler) {
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(404, res->status);
|
||||
EXPECT_EQ("Error", res->body);
|
||||
EXPECT_EQ(0, res->get_header_value_count("PRE_ROUTING"));
|
||||
EXPECT_EQ(0, res->get_header_value_count("POST_ROUTING"));
|
||||
EXPECT_EQ(0U, res->get_header_value_count("PRE_ROUTING"));
|
||||
EXPECT_EQ(0U, res->get_header_value_count("POST_ROUTING"));
|
||||
}
|
||||
|
||||
svr.stop();
|
||||
@@ -1250,31 +1310,31 @@ protected:
|
||||
.Get("/http_response_splitting",
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
res.set_header("a", "1\r\nSet-Cookie: a=1");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_EQ(0U, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_header("a", "1\nSet-Cookie: a=1");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_EQ(0U, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_header("a", "1\rSet-Cookie: a=1");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_EQ(0U, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_header("a\r\nb", "0");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_EQ(0U, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_header("a\rb", "0");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_EQ(0U, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_header("a\nb", "0");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_EQ(0U, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_redirect("1\r\nSet-Cookie: a=1");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_EQ(0U, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("Location"));
|
||||
})
|
||||
.Get("/slow",
|
||||
@@ -1592,7 +1652,7 @@ protected:
|
||||
} else {
|
||||
std::string body;
|
||||
content_reader([&](const char *data, size_t data_length) {
|
||||
EXPECT_EQ(data_length, 7);
|
||||
EXPECT_EQ(7U, data_length);
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
@@ -1640,7 +1700,7 @@ protected:
|
||||
})
|
||||
.Post("/binary",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(4, req.body.size());
|
||||
EXPECT_EQ(4U, req.body.size());
|
||||
EXPECT_EQ("application/octet-stream",
|
||||
req.get_header_value("Content-Type"));
|
||||
EXPECT_EQ("4", req.get_header_value("Content-Length"));
|
||||
@@ -1648,7 +1708,7 @@ protected:
|
||||
})
|
||||
.Put("/binary",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(4, req.body.size());
|
||||
EXPECT_EQ(4U, req.body.size());
|
||||
EXPECT_EQ("application/octet-stream",
|
||||
req.get_header_value("Content-Type"));
|
||||
EXPECT_EQ("4", req.get_header_value("Content-Length"));
|
||||
@@ -1656,7 +1716,7 @@ protected:
|
||||
})
|
||||
.Patch("/binary",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(4, req.body.size());
|
||||
EXPECT_EQ(4U, req.body.size());
|
||||
EXPECT_EQ("application/octet-stream",
|
||||
req.get_header_value("Content-Type"));
|
||||
EXPECT_EQ("4", req.get_header_value("Content-Length"));
|
||||
@@ -1664,7 +1724,7 @@ protected:
|
||||
})
|
||||
.Delete("/binary",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(4, req.body.size());
|
||||
EXPECT_EQ(4U, req.body.size());
|
||||
EXPECT_EQ("application/octet-stream",
|
||||
req.get_header_value("Content-Type"));
|
||||
EXPECT_EQ("4", req.get_header_value("Content-Length"));
|
||||
@@ -1744,7 +1804,7 @@ TEST_F(ServerTest, GetMethod200) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("OK", res->reason);
|
||||
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ(1, res->get_header_value_count("Content-Type"));
|
||||
EXPECT_EQ(1U, res->get_header_value_count("Content-Type"));
|
||||
EXPECT_EQ("Hello World!", res->body);
|
||||
}
|
||||
|
||||
@@ -1754,7 +1814,7 @@ TEST_F(ServerTest, GetMethod200withPercentEncoding) {
|
||||
EXPECT_EQ("HTTP/1.1", res->version);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ(1, res->get_header_value_count("Content-Type"));
|
||||
EXPECT_EQ(1U, res->get_header_value_count("Content-Type"));
|
||||
EXPECT_EQ("Hello World!", res->body);
|
||||
}
|
||||
|
||||
@@ -2032,25 +2092,25 @@ TEST_F(ServerTest, Binary) {
|
||||
"application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
ASSERT_EQ(4U, res->body.size());
|
||||
|
||||
res = cli_.Put("/binary", binary.data(), binary.size(),
|
||||
"application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
ASSERT_EQ(4U, res->body.size());
|
||||
|
||||
res = cli_.Patch("/binary", binary.data(), binary.size(),
|
||||
"application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
ASSERT_EQ(4U, res->body.size());
|
||||
|
||||
res = cli_.Delete("/binary", binary.data(), binary.size(),
|
||||
"application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
ASSERT_EQ(4U, res->body.size());
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, BinaryString) {
|
||||
@@ -2059,22 +2119,22 @@ TEST_F(ServerTest, BinaryString) {
|
||||
auto res = cli_.Post("/binary", binary, "application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
ASSERT_EQ(4U, res->body.size());
|
||||
|
||||
res = cli_.Put("/binary", binary, "application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
ASSERT_EQ(4U, res->body.size());
|
||||
|
||||
res = cli_.Patch("/binary", binary, "application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
ASSERT_EQ(4U, res->body.size());
|
||||
|
||||
res = cli_.Delete("/binary", binary, "application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
ASSERT_EQ(4U, res->body.size());
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, EmptyRequest) {
|
||||
@@ -2390,7 +2450,7 @@ TEST_F(ServerTest, GetStreamedWithRangeMultipart) {
|
||||
EXPECT_EQ(206, res->status);
|
||||
EXPECT_EQ("269", res->get_header_value("Content-Length"));
|
||||
EXPECT_EQ(false, res->has_header("Content-Range"));
|
||||
EXPECT_EQ(269, res->body.size());
|
||||
EXPECT_EQ(269U, res->body.size());
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetStreamedEndless) {
|
||||
@@ -2477,7 +2537,7 @@ TEST_F(ServerTest, GetWithRangeMultipart) {
|
||||
EXPECT_EQ(206, res->status);
|
||||
EXPECT_EQ("269", res->get_header_value("Content-Length"));
|
||||
EXPECT_EQ(false, res->has_header("Content-Range"));
|
||||
EXPECT_EQ(269, res->body.size());
|
||||
EXPECT_EQ(269U, res->body.size());
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetWithRangeMultipartOffsetGreaterThanContent) {
|
||||
@@ -2717,10 +2777,12 @@ TEST_F(ServerTest, PutLargeFileWithGzip) {
|
||||
|
||||
TEST_F(ServerTest, PutLargeFileWithGzip2) {
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
Client cli("https://localhost:1234");
|
||||
std::string s = std::string("https://") + HOST + ":" + std::to_string(PORT);
|
||||
Client cli(s.c_str());
|
||||
cli.enable_server_certificate_verification(false);
|
||||
#else
|
||||
Client cli("http://localhost:1234");
|
||||
std::string s = std::string("http://") + HOST + ":" + std::to_string(PORT);
|
||||
Client cli(s.c_str());
|
||||
#endif
|
||||
cli.set_compress(true);
|
||||
|
||||
@@ -2806,6 +2868,54 @@ TEST(GzipDecompressor, ChunkedDecompression) {
|
||||
}
|
||||
ASSERT_EQ(data, decompressed_data);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
TEST(GzipDecompressor, LargeRandomData) {
|
||||
|
||||
// prepare large random data that is difficult to be compressed and is
|
||||
// expected to have large size even when compressed
|
||||
std::random_device seed_gen;
|
||||
std::mt19937 random(seed_gen());
|
||||
constexpr auto large_size_byte = 4294967296UL; // 4GiB
|
||||
constexpr auto data_size = large_size_byte + 134217728UL; // + 128MiB
|
||||
std::vector<std::uint32_t> data(data_size / sizeof(std::uint32_t));
|
||||
std::generate(data.begin(), data.end(), [&]() { return random(); });
|
||||
|
||||
// compress data over 4GiB
|
||||
std::string compressed_data;
|
||||
compressed_data.reserve(large_size_byte + 536870912UL); // + 512MiB reserved
|
||||
httplib::detail::gzip_compressor compressor;
|
||||
auto result = compressor.compress(reinterpret_cast<const char *>(data.data()),
|
||||
data.size() * sizeof(std::uint32_t), true,
|
||||
[&](const char *data, size_t size) {
|
||||
compressed_data.insert(
|
||||
compressed_data.size(), data, size);
|
||||
return true;
|
||||
});
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
// FIXME: compressed data size is expected to be greater than 4GiB,
|
||||
// but there is no guarantee
|
||||
// ASSERT_TRUE(compressed_data.size() >= large_size_byte);
|
||||
|
||||
// decompress data over 4GiB
|
||||
std::string decompressed_data;
|
||||
decompressed_data.reserve(data_size);
|
||||
httplib::detail::gzip_decompressor decompressor;
|
||||
result = decompressor.decompress(
|
||||
compressed_data.data(), compressed_data.size(),
|
||||
[&](const char *data, size_t size) {
|
||||
decompressed_data.insert(decompressed_data.size(), data, size);
|
||||
return true;
|
||||
});
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
// compare
|
||||
ASSERT_EQ(data_size, decompressed_data.size());
|
||||
ASSERT_TRUE(std::memcmp(data.data(), decompressed_data.data(), data_size) ==
|
||||
0);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
|
||||
@@ -3066,7 +3176,7 @@ TEST_F(ServerTest, GzipWithContentReceiver) {
|
||||
std::string body;
|
||||
auto res = cli_.Get("/compress", headers,
|
||||
[&](const char *data, uint64_t data_length) {
|
||||
EXPECT_EQ(data_length, 100);
|
||||
EXPECT_EQ(100U, data_length);
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
@@ -3092,14 +3202,14 @@ TEST_F(ServerTest, GzipWithoutDecompressing) {
|
||||
EXPECT_EQ("gzip", res->get_header_value("Content-Encoding"));
|
||||
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ("33", res->get_header_value("Content-Length"));
|
||||
EXPECT_EQ(33, res->body.size());
|
||||
EXPECT_EQ(33U, res->body.size());
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GzipWithContentReceiverWithoutAcceptEncoding) {
|
||||
std::string body;
|
||||
auto res = cli_.Get("/compress", [&](const char *data, uint64_t data_length) {
|
||||
EXPECT_EQ(data_length, 100);
|
||||
EXPECT_EQ(100U, data_length);
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
@@ -3135,7 +3245,7 @@ TEST_F(ServerTest, NoGzipWithContentReceiver) {
|
||||
std::string body;
|
||||
auto res = cli_.Get("/nocompress", headers,
|
||||
[&](const char *data, uint64_t data_length) {
|
||||
EXPECT_EQ(data_length, 100);
|
||||
EXPECT_EQ(100U, data_length);
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
@@ -3231,14 +3341,14 @@ TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) {
|
||||
// Only space and horizontal tab are whitespace. Make sure other whitespace-
|
||||
// like characters are not treated the same - use vertical tab and escape.
|
||||
const std::string req = "GET /validate-ws-in-headers HTTP/1.1\r\n"
|
||||
"foo: \t \v bar \e\t \r\n"
|
||||
"foo: \t \v bar \x1B\t \r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n";
|
||||
|
||||
ASSERT_TRUE(send_request(5, req));
|
||||
svr.stop();
|
||||
t.join();
|
||||
EXPECT_EQ(header_value, "\v bar \e");
|
||||
EXPECT_EQ(header_value, "\v bar \x1B");
|
||||
}
|
||||
|
||||
// Sends a raw request and verifies that there isn't a crash or exception.
|
||||
@@ -3390,7 +3500,7 @@ TEST(ServerStopTest, StopServerWithChunkedTransmission) {
|
||||
res.set_chunked_content_provider("text/event-stream", [](size_t offset,
|
||||
DataSink &sink) {
|
||||
char buffer[27];
|
||||
auto size = static_cast<size_t>(sprintf(buffer, "data:%ld\n\n", offset));
|
||||
auto size = static_cast<size_t>(sprintf(buffer, "data:%zd\n\n", offset));
|
||||
auto ret = sink.write(buffer, size);
|
||||
EXPECT_TRUE(ret);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
@@ -3647,26 +3757,60 @@ TEST(ErrorHandlerWithContentProviderTest, ErrorHandler) {
|
||||
TEST(GetWithParametersTest, GetWithParameters) {
|
||||
Server svr;
|
||||
|
||||
svr.Get("/", [&](const Request &req, Response &res) {
|
||||
auto text = req.get_param_value("hello");
|
||||
res.set_content(text, "text/plain");
|
||||
svr.Get("/", [&](const Request &req, Response &) {
|
||||
EXPECT_EQ("world", req.get_param_value("hello"));
|
||||
EXPECT_EQ("world2", req.get_param_value("hello2"));
|
||||
EXPECT_EQ("world3", req.get_param_value("hello3"));
|
||||
});
|
||||
|
||||
auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); });
|
||||
svr.Get("/params", [&](const Request &req, Response &) {
|
||||
EXPECT_EQ("world", req.get_param_value("hello"));
|
||||
EXPECT_EQ("world2", req.get_param_value("hello2"));
|
||||
EXPECT_EQ("world3", req.get_param_value("hello3"));
|
||||
});
|
||||
|
||||
svr.Get(R"(/resources/([a-z0-9\\-]+))", [&](const Request& req, Response&) {
|
||||
EXPECT_EQ("resource-id", req.matches[1]);
|
||||
EXPECT_EQ("foo", req.get_param_value("param1"));
|
||||
EXPECT_EQ("bar", req.get_param_value("param2"));
|
||||
});
|
||||
|
||||
auto listen_thread = std::thread([&svr]() { svr.listen(HOST, PORT); });
|
||||
while (!svr.is_running()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
Client cli("localhost", PORT);
|
||||
{
|
||||
Client cli(HOST, PORT);
|
||||
|
||||
Params params;
|
||||
params.emplace("hello", "world");
|
||||
auto res = cli.Get("/", params, Headers{});
|
||||
Params params;
|
||||
params.emplace("hello", "world");
|
||||
params.emplace("hello2", "world2");
|
||||
params.emplace("hello3", "world3");
|
||||
auto res = cli.Get("/", params, Headers{});
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("world", res->body);
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
Client cli(HOST, PORT);
|
||||
|
||||
auto res = cli.Get("/params?hello=world&hello2=world2&hello3=world3");
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
Client cli(HOST, PORT);
|
||||
|
||||
auto res = cli.Get("/resources/resource-id?param1=foo¶m2=bar");
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
svr.stop();
|
||||
listen_thread.join();
|
||||
@@ -4319,7 +4463,7 @@ TEST(DecodeWithChunkedEncoding, BrotliEncoding) {
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ(287630, res->body.size());
|
||||
EXPECT_EQ(287630U, res->body.size());
|
||||
EXPECT_EQ("application/javascript; charset=utf-8",
|
||||
res->get_header_value("Content-Type"));
|
||||
}
|
||||
@@ -4360,4 +4504,38 @@ TEST(HttpsToHttpRedirectTest3, SimpleInterface) {
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(HttpToHttpsRedirectTest, CertFile) {
|
||||
Server svr;
|
||||
ASSERT_TRUE(svr.is_valid());
|
||||
svr.Get("/index", [&](const Request &, Response &res) {
|
||||
res.set_redirect("https://127.0.0.1:1235/index");
|
||||
svr.stop();
|
||||
});
|
||||
|
||||
SSLServer ssl_svr(SERVER_CERT2_FILE, SERVER_PRIVATE_KEY_FILE);
|
||||
ASSERT_TRUE(ssl_svr.is_valid());
|
||||
ssl_svr.Get("/index", [&](const Request &, Response &res) {
|
||||
res.set_content("test", "text/plain");
|
||||
ssl_svr.stop();
|
||||
});
|
||||
|
||||
|
||||
thread t = thread([&]() { ASSERT_TRUE(svr.listen("127.0.0.1", PORT)); });
|
||||
thread t2 = thread([&]() { ASSERT_TRUE(ssl_svr.listen("127.0.0.1", 1235)); });
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
|
||||
Client cli("127.0.0.1", PORT);
|
||||
cli.set_ca_cert_path(SERVER_CERT2_FILE);
|
||||
cli.enable_server_certificate_verification(true);
|
||||
cli.set_follow_location(true);
|
||||
cli.set_connection_timeout(30);
|
||||
|
||||
auto res = cli.Get("/index");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
|
||||
t.join();
|
||||
t2.join();
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user