Compare commits

..

9 Commits

Author SHA1 Message Date
Andrew Gasparovic 6d60dc8839 Add cache_control parameter to set_mount_point (#688)
* Add `cache_control` parameter to `set_mount_point`

Specifies the Cache-Control header value to return when specified. For example:

```
svr.set_mount_point("/assets", "public/assets", "public, max-age=604800, immutable");
```

* Add default for cache_control

Default to "no-cache", which is implicitly what is happening today.

* Change set_mount_point to accept Headers

* Don't use C++17 destructuring
2020-10-10 20:46:08 -04:00
Wang Gao b713a3a651 fix MSVC2015 error: std::tolower to ::lower (#689) 2020-10-10 12:02:50 -04:00
yhirose 7e8db1dc68 Comment out -fsanitize=address 2020-10-08 23:14:53 -04:00
yhirose 316add860b Added ReadTimeoutSSL test 2020-10-08 22:55:09 -04:00
yhirose 79ce6f1745 Try to fix Github actions on Ubuntu 2020-10-08 21:49:47 -04:00
yhirose 09fdf4eacd Fix #685 2020-10-08 21:37:42 -04:00
Omkar Jadhav 143b2dd15a Fix memory leak due caused due to X509_STORE (#671)
* Fix memory leak due caused due to X509_STORE

* Add test for repro and address sanitizer to compiler flags

* Add comment

* Sync

* Associate ca_store with ssl context within set_ca_cert_store()

* Split SlowPost test

* Fix #674

Co-authored-by: yhirose <yuji.hirose.bug@gmail.com>
2020-10-02 13:17:37 -04:00
yhirose e2c4e9d95c Fix #674 2020-09-29 19:22:28 -04:00
yhirose d87082f04b Split SlowPost test 2020-09-29 19:17:34 -04:00
4 changed files with 169 additions and 82 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
uses: actions/checkout@v2
- name: install brotli library on ubuntu
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get install -y libbrotli-dev
run: sudo apt update && sudo apt-get install -y libbrotli-dev
- name: install brotli library on macOS
if: matrix.os == 'macOS-latest'
run: brew install brotli
+104 -79
View File
@@ -579,7 +579,8 @@ public:
Server &Options(const char *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_mount_point(const char *mount_point, const char *dir,
Headers headers = Headers());
bool remove_mount_point(const char *mount_point);
void set_file_extension_and_mimetype_mapping(const char *ext,
const char *mime);
@@ -663,9 +664,15 @@ private:
ContentReceiver multipart_receiver);
virtual bool process_and_close_socket(socket_t sock);
struct MountPointEntry {
std::string mount_point;
std::string base_dir;
Headers headers;
};
std::vector<MountPointEntry> base_dirs_;
std::atomic<bool> is_running_;
std::vector<std::pair<std::string, std::string>> base_dirs_;
std::map<std::string, std::string> file_extension_and_mimetype_map_;
Handler file_request_handler_;
Handlers get_handlers_;
@@ -839,6 +846,10 @@ public:
void set_proxy_digest_auth(const char *username, const char *password);
#endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
void enable_server_certificate_verification(bool enabled);
#endif
void set_logger(Logger logger);
protected:
@@ -859,6 +870,8 @@ protected:
Error get_last_error() const;
void copy_settings(const ClientImpl &rhs);
// Error state
mutable Error error_ = Error::Success;
@@ -916,41 +929,11 @@ protected:
std::string proxy_digest_auth_password_;
#endif
Logger logger_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
bool server_certificate_verification_ = true;
#endif
void copy_settings(const ClientImpl &rhs) {
client_cert_path_ = rhs.client_cert_path_;
client_key_path_ = rhs.client_key_path_;
connection_timeout_sec_ = rhs.connection_timeout_sec_;
read_timeout_sec_ = rhs.read_timeout_sec_;
read_timeout_usec_ = rhs.read_timeout_usec_;
write_timeout_sec_ = rhs.write_timeout_sec_;
write_timeout_usec_ = rhs.write_timeout_usec_;
basic_auth_username_ = rhs.basic_auth_username_;
basic_auth_password_ = rhs.basic_auth_password_;
bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
digest_auth_username_ = rhs.digest_auth_username_;
digest_auth_password_ = rhs.digest_auth_password_;
#endif
keep_alive_ = rhs.keep_alive_;
follow_location_ = rhs.follow_location_;
tcp_nodelay_ = rhs.tcp_nodelay_;
socket_options_ = rhs.socket_options_;
compress_ = rhs.compress_;
decompress_ = rhs.decompress_;
interface_ = rhs.interface_;
proxy_host_ = rhs.proxy_host_;
proxy_port_ = rhs.proxy_port_;
proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
#endif
logger_ = rhs.logger_;
}
Logger logger_;
private:
socket_t create_client_socket() const;
@@ -1096,16 +1079,18 @@ public:
void set_proxy_digest_auth(const char *username, const char *password);
#endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
void enable_server_certificate_verification(bool enabled);
#endif
void set_logger(Logger logger);
// SSL
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
Client &set_ca_cert_path(const char *ca_cert_file_path,
const char *ca_cert_dir_path = nullptr);
void set_ca_cert_path(const char *ca_cert_file_path,
const char *ca_cert_dir_path = nullptr);
Client &set_ca_cert_store(X509_STORE *ca_cert_store);
Client &enable_server_certificate_verification(bool enabled);
void set_ca_cert_store(X509_STORE *ca_cert_store);
long get_openssl_verify_result() const;
@@ -1163,8 +1148,6 @@ public:
void set_ca_cert_store(X509_STORE *ca_cert_store);
void enable_server_certificate_verification(bool enabled);
long get_openssl_verify_result() const;
SSL_CTX *ssl_context() const;
@@ -1195,8 +1178,6 @@ private:
std::string ca_cert_file_path_;
std::string ca_cert_dir_path_;
X509_STORE *ca_cert_store_ = nullptr;
bool server_certificate_verification_ = true;
long verify_result_ = 0;
friend class ClientImpl;
@@ -1255,7 +1236,7 @@ inline std::string from_i_to_hex(size_t n) {
inline bool start_with(const std::string &a, const std::string &b) {
if (a.size() < b.size()) { return false; }
for (size_t i = 0; i < b.size(); i++) {
if (std::tolower(a[i]) != std::tolower(b[i])) { return false; }
if (::tolower(a[i]) != ::tolower(b[i])) { return false; }
}
return true;
}
@@ -3841,11 +3822,12 @@ inline bool Server::set_base_dir(const char *dir, const char *mount_point) {
return set_mount_point(mount_point, dir);
}
inline bool Server::set_mount_point(const char *mount_point, const char *dir) {
inline bool Server::set_mount_point(const char *mount_point, const char *dir,
Headers headers) {
if (detail::is_dir(dir)) {
std::string mnt = mount_point ? mount_point : "/";
if (!mnt.empty() && mnt[0] == '/') {
base_dirs_.emplace_back(mnt, dir);
base_dirs_.push_back({mnt, dir, std::move(headers)});
return true;
}
}
@@ -3854,7 +3836,7 @@ inline bool Server::set_mount_point(const char *mount_point, const char *dir) {
inline bool Server::remove_mount_point(const char *mount_point) {
for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
if (it->first == mount_point) {
if (it->mount_point == mount_point) {
base_dirs_.erase(it);
return true;
}
@@ -4276,15 +4258,12 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
inline bool Server::handle_file_request(Request &req, Response &res,
bool head) {
for (const auto &kv : base_dirs_) {
const auto &mount_point = kv.first;
const auto &base_dir = kv.second;
for (const auto &entry : base_dirs_) {
// Prefix match
if (!req.path.compare(0, mount_point.size(), mount_point)) {
std::string sub_path = "/" + req.path.substr(mount_point.size());
if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
std::string sub_path = "/" + req.path.substr(entry.mount_point.size());
if (detail::is_valid_path(sub_path)) {
auto path = base_dir + sub_path;
auto path = entry.base_dir + sub_path;
if (path.back() == '/') { path += "index.html"; }
if (detail::is_file(path)) {
@@ -4292,6 +4271,9 @@ inline bool Server::handle_file_request(Request &req, Response &res,
auto type =
detail::find_content_type(path, file_extension_and_mimetype_map_);
if (type) { res.set_header("Content-Type", type); }
for (const auto& kv : entry.headers) {
res.set_header(kv.first.c_str(), kv.second);
}
res.status = 200;
if (!head && file_request_handler_) {
file_request_handler_(req, res);
@@ -4616,6 +4598,43 @@ inline bool ClientImpl::is_valid() const { return true; }
inline Error ClientImpl::get_last_error() const { return error_; }
inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
client_cert_path_ = rhs.client_cert_path_;
client_key_path_ = rhs.client_key_path_;
connection_timeout_sec_ = rhs.connection_timeout_sec_;
read_timeout_sec_ = rhs.read_timeout_sec_;
read_timeout_usec_ = rhs.read_timeout_usec_;
write_timeout_sec_ = rhs.write_timeout_sec_;
write_timeout_usec_ = rhs.write_timeout_usec_;
basic_auth_username_ = rhs.basic_auth_username_;
basic_auth_password_ = rhs.basic_auth_password_;
bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
digest_auth_username_ = rhs.digest_auth_username_;
digest_auth_password_ = rhs.digest_auth_password_;
#endif
keep_alive_ = rhs.keep_alive_;
follow_location_ = rhs.follow_location_;
tcp_nodelay_ = rhs.tcp_nodelay_;
socket_options_ = rhs.socket_options_;
compress_ = rhs.compress_;
decompress_ = rhs.decompress_;
interface_ = rhs.interface_;
proxy_host_ = rhs.proxy_host_;
proxy_port_ = rhs.proxy_port_;
proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
#endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
server_certificate_verification_ = rhs.server_certificate_verification_;
#endif
logger_ = rhs.logger_;
}
inline socket_t ClientImpl::create_client_socket() const {
if (!proxy_host_.empty() && proxy_port_ != -1) {
return detail::create_client_socket(
@@ -4870,7 +4889,10 @@ inline bool ClientImpl::write_request(Stream &strm, const Request &req,
auto length = std::to_string(req.content_length);
headers.emplace("Content-Length", length);
} else {
headers.emplace("Content-Length", "0");
if (req.method == "POST" || req.method == "PUT" ||
req.method == "PATCH") {
headers.emplace("Content-Length", "0");
}
}
} else {
if (!req.has_header("Content-Type")) {
@@ -5489,6 +5511,12 @@ inline void ClientImpl::set_proxy_digest_auth(const char *username,
}
#endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline void ClientImpl::enable_server_certificate_verification(bool enabled) {
server_certificate_verification_ = enabled;
}
#endif
inline void ClientImpl::set_logger(Logger logger) {
logger_ = std::move(logger);
}
@@ -5826,11 +5854,16 @@ inline void SSLClient::set_ca_cert_path(const char *ca_cert_file_path,
}
inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
if (ca_cert_store) { ca_cert_store_ = ca_cert_store; }
}
inline void SSLClient::enable_server_certificate_verification(bool enabled) {
server_certificate_verification_ = enabled;
if (ca_cert_store) {
if (ctx_) {
if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {
// Free memory allocated for old cert and use new store `ca_cert_store`
SSL_CTX_set_cert_store(ctx_, ca_cert_store);
}
} else {
X509_STORE_free(ca_cert_store);
}
}
}
inline long SSLClient::get_openssl_verify_result() const {
@@ -5908,10 +5941,6 @@ inline bool SSLClient::load_certs() {
ca_cert_dir_path_.c_str())) {
ret = false;
}
} else if (ca_cert_store_ != nullptr) {
if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store_) {
SSL_CTX_set_cert_store(ctx_, ca_cert_store_);
}
} else {
#ifdef _WIN32
detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
@@ -6413,31 +6442,27 @@ inline void Client::set_proxy_digest_auth(const char *username,
}
#endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline void Client::enable_server_certificate_verification(bool enabled) {
cli_->enable_server_certificate_verification(enabled);
}
#endif
inline void Client::set_logger(Logger logger) { cli_->set_logger(logger); }
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline Client &Client::set_ca_cert_path(const char *ca_cert_file_path,
const char *ca_cert_dir_path) {
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);
}
return *this;
}
inline Client &Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
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);
}
return *this;
}
inline Client &Client::enable_server_certificate_verification(bool enabled) {
if (is_ssl_) {
static_cast<SSLClient &>(*cli_).enable_server_certificate_verification(
enabled);
}
return *this;
}
inline long Client::get_openssl_verify_result() const {
+1 -1
View File
@@ -1,6 +1,6 @@
#CXX = clang++
CXXFLAGS = -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion
CXXFLAGS = -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -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
+63 -1
View File
@@ -2070,9 +2070,14 @@ TEST_F(ServerTest, SlowPost) {
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
}
TEST_F(ServerTest, SlowPostFail) {
char buffer[64 * 1024];
memset(buffer, 0x42, sizeof(buffer));
cli_.set_write_timeout(0, 0);
res = cli_.Post(
auto res = cli_.Post(
"/slowpost", 64 * 1024 * 1024,
[&](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
sink.write(buffer, sizeof(buffer));
@@ -2986,6 +2991,50 @@ TEST(KeepAliveTest, ReadTimeout) {
ASSERT_FALSE(svr.is_running());
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
TEST(KeepAliveTest, ReadTimeoutSSL) {
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
ASSERT_TRUE(svr.is_valid());
svr.Get("/a", [&](const Request & /*req*/, Response &res) {
std::this_thread::sleep_for(std::chrono::seconds(2));
res.set_content("a", "text/plain");
});
svr.Get("/b", [&](const Request & /*req*/, Response &res) {
res.set_content("b", "text/plain");
});
auto listen_thread = std::thread([&svr]() {
svr.listen("localhost", PORT);
});
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));
SSLClient cli("localhost", PORT);
cli.enable_server_certificate_verification(false);
cli.set_keep_alive(true);
cli.set_read_timeout(1);
auto resa = cli.Get("/a");
ASSERT_TRUE(!resa);
EXPECT_EQ(Error::Read, resa.error());
auto resb = cli.Get("/b");
ASSERT_TRUE(resb);
EXPECT_EQ(200, resb->status);
EXPECT_EQ("b", resb->body);
svr.stop();
listen_thread.join();
ASSERT_FALSE(svr.is_running());
}
#endif
class ServerTestWithAI_PASSIVE : public ::testing::Test {
protected:
ServerTestWithAI_PASSIVE()
@@ -3120,6 +3169,19 @@ TEST_F(PayloadMaxLengthTest, ExceedLimit) {
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
TEST(SSLClientTest, UpdateCAStore) {
httplib::SSLClient httplib_client("www.google.com");
auto ca_store_1 = X509_STORE_new();
X509_STORE_load_locations(ca_store_1, "/etc/ssl/certs/ca-certificates.crt",
nullptr);
httplib_client.set_ca_cert_store(ca_store_1);
auto ca_store_2 = X509_STORE_new();
X509_STORE_load_locations(ca_store_2, "/etc/ssl/certs/ca-certificates.crt",
nullptr);
httplib_client.set_ca_cert_store(ca_store_2);
}
TEST(SSLClientTest, ServerNameIndication) {
SSLClient cli("httpbin.org", 443);
auto res = cli.Get("/get");