mirror of
https://github.com/yhirose/cpp-httplib
synced 2026-06-08 18:30:49 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| da746c6e67 | |||
| 3451da940d | |||
| 38a6b3e69f | |||
| d1037ee9fd | |||
| 2ece5f116b | |||
| c2b6e4ac04 | |||
| 8674555b88 | |||
| 85327e19ae | |||
| ed8efea98b | |||
| 1ccddd1b0b | |||
| 992f3dc690 |
@@ -1,29 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.7.0)
|
||||
project(httplib)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
# Include
|
||||
include(GNUInstallDirs)
|
||||
include(ExternalProject)
|
||||
|
||||
add_library(${PROJECT_NAME} INTERFACE)
|
||||
target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_11)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<INSTALL_INTERFACE:include>)
|
||||
|
||||
install(TARGETS ${PROJECT_NAME} EXPORT httplibConfig
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
install(FILES httplib.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
|
||||
|
||||
install(EXPORT httplibConfig DESTINATION share/httplib/cmake)
|
||||
|
||||
export(TARGETS ${PROJECT_NAME} FILE httplibConfig.cmake)
|
||||
|
||||
#add_subdirectory(example)
|
||||
#add_subdirectory(test)
|
||||
@@ -535,6 +535,8 @@ Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32
|
||||
#include <httplib.h>
|
||||
```
|
||||
|
||||
Note: Cygwin on Windows is not supported.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
|
||||
@@ -312,7 +312,7 @@ struct Response {
|
||||
void set_header(const char *key, const char *val);
|
||||
void set_header(const char *key, const std::string &val);
|
||||
|
||||
void set_redirect(const char *url);
|
||||
void set_redirect(const char *url, int status = 302);
|
||||
void set_content(const char *s, size_t n, const char *content_type);
|
||||
void set_content(std::string s, const char *content_type);
|
||||
|
||||
@@ -462,6 +462,7 @@ public:
|
||||
Server &Patch(const char *pattern, Handler handler);
|
||||
Server &Patch(const char *pattern, HandlerWithContentReader handler);
|
||||
Server &Delete(const char *pattern, Handler handler);
|
||||
Server &Delete(const char *pattern, HandlerWithContentReader handler);
|
||||
Server &Options(const char *pattern, Handler handler);
|
||||
|
||||
[[deprecated]] bool set_base_dir(const char *dir,
|
||||
@@ -551,6 +552,7 @@ private:
|
||||
Handlers patch_handlers_;
|
||||
HandlersForContentReader patch_handlers_for_content_reader_;
|
||||
Handlers delete_handlers_;
|
||||
HandlersForContentReader delete_handlers_for_content_reader_;
|
||||
Handlers options_handlers_;
|
||||
Handler error_handler_;
|
||||
Logger logger_;
|
||||
@@ -846,6 +848,9 @@ public:
|
||||
const char *client_ca_cert_file_path = nullptr,
|
||||
const char *client_ca_cert_dir_path = nullptr);
|
||||
|
||||
SSLServer(X509 *cert, EVP_PKEY *private_key,
|
||||
X509_STORE *client_ca_cert_store = nullptr);
|
||||
|
||||
~SSLServer() override;
|
||||
|
||||
bool is_valid() const override;
|
||||
@@ -863,6 +868,9 @@ public:
|
||||
const std::string &client_cert_path = std::string(),
|
||||
const std::string &client_key_path = std::string());
|
||||
|
||||
SSLClient(const std::string &host, int port, X509 *client_cert,
|
||||
EVP_PKEY *client_key);
|
||||
|
||||
~SSLClient() override;
|
||||
|
||||
bool is_valid() const override;
|
||||
@@ -870,6 +878,8 @@ public:
|
||||
void set_ca_cert_path(const char *ca_ceert_file_path,
|
||||
const char *ca_cert_dir_path = nullptr);
|
||||
|
||||
void set_ca_cert_store(X509_STORE *ca_cert_store);
|
||||
|
||||
void enable_server_certificate_verification(bool enabled);
|
||||
|
||||
long get_openssl_verify_result() const;
|
||||
@@ -895,6 +905,7 @@ private:
|
||||
|
||||
std::string ca_cert_file_path_;
|
||||
std::string ca_cert_dir_path_;
|
||||
X509_STORE *ca_cert_store_ = nullptr;
|
||||
bool server_certificate_verification_ = false;
|
||||
long verify_result_ = 0;
|
||||
};
|
||||
@@ -2046,6 +2057,12 @@ inline bool redirect(T &cli, const Request &req, Response &res,
|
||||
new_req.path = path;
|
||||
new_req.redirect_count -= 1;
|
||||
|
||||
if (res.status == 303 && (req.method != "GET" && req.method != "HEAD")) {
|
||||
new_req.method = "GET";
|
||||
new_req.body.clear();
|
||||
new_req.headers.clear();
|
||||
}
|
||||
|
||||
Response new_res;
|
||||
|
||||
auto ret = cli.send(new_req, new_res);
|
||||
@@ -2515,13 +2532,22 @@ get_range_offset_and_length(const Request &req, const Response &res,
|
||||
|
||||
inline bool expect_content(const Request &req) {
|
||||
if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" ||
|
||||
req.method == "PRI") {
|
||||
req.method == "PRI" || req.method == "DELETE") {
|
||||
return true;
|
||||
}
|
||||
// TODO: check if Content-Length is set
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool has_crlf(const char *s) {
|
||||
auto p = s;
|
||||
while (*p) {
|
||||
if (*p == '\r' || *p == '\n') { return true; }
|
||||
p++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
template <typename CTX, typename Init, typename Update, typename Final>
|
||||
inline std::string message_digest(const std::string &s, Init init,
|
||||
@@ -2708,11 +2734,15 @@ inline size_t Request::get_header_value_count(const char *key) const {
|
||||
}
|
||||
|
||||
inline void Request::set_header(const char *key, const char *val) {
|
||||
headers.emplace(key, val);
|
||||
if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
|
||||
headers.emplace(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
inline void Request::set_header(const char *key, const std::string &val) {
|
||||
headers.emplace(key, val);
|
||||
if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) {
|
||||
headers.emplace(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool Request::has_param(const char *key) const {
|
||||
@@ -2762,16 +2792,26 @@ inline size_t Response::get_header_value_count(const char *key) const {
|
||||
}
|
||||
|
||||
inline void Response::set_header(const char *key, const char *val) {
|
||||
headers.emplace(key, val);
|
||||
if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
|
||||
headers.emplace(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
inline void Response::set_header(const char *key, const std::string &val) {
|
||||
headers.emplace(key, val);
|
||||
if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) {
|
||||
headers.emplace(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
inline void Response::set_redirect(const char *url) {
|
||||
set_header("Location", url);
|
||||
status = 302;
|
||||
inline void Response::set_redirect(const char *url, int status) {
|
||||
if (!detail::has_crlf(url)) {
|
||||
set_header("Location", url);
|
||||
if (300 <= status && status < 400) {
|
||||
this->status = status;
|
||||
} else {
|
||||
this->status = 302;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void Response::set_content(const char *s, size_t n,
|
||||
@@ -2968,6 +3008,13 @@ inline Server &Server::Delete(const char *pattern, Handler 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));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Server &Server::Options(const char *pattern, Handler handler) {
|
||||
options_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
|
||||
return *this;
|
||||
@@ -3480,6 +3527,11 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
|
||||
req, res, 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_)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4032,17 +4084,16 @@ inline bool Client::process_request(Stream &strm, const Request &req,
|
||||
|
||||
// Body
|
||||
if (req.method != "HEAD" && req.method != "CONNECT") {
|
||||
ContentReceiver out = [&](const char *buf, size_t n) {
|
||||
if (res.body.size() + n > res.body.max_size()) { return false; }
|
||||
res.body.append(buf, n);
|
||||
return true;
|
||||
};
|
||||
|
||||
if (req.content_receiver) {
|
||||
out = [&](const char *buf, size_t n) {
|
||||
return req.content_receiver(buf, n);
|
||||
};
|
||||
}
|
||||
auto out =
|
||||
req.content_receiver
|
||||
? static_cast<ContentReceiver>([&](const char *buf, size_t n) {
|
||||
return req.content_receiver(buf, n);
|
||||
})
|
||||
: static_cast<ContentReceiver>([&](const char *buf, size_t n) {
|
||||
if (res.body.size() + n > res.body.max_size()) { return false; }
|
||||
res.body.append(buf, n);
|
||||
return true;
|
||||
});
|
||||
|
||||
int dummy_status;
|
||||
if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
|
||||
@@ -4468,7 +4519,9 @@ inline bool process_and_close_socket_ssl(
|
||||
}
|
||||
}
|
||||
|
||||
SSL_shutdown(ssl);
|
||||
if (ret) {
|
||||
SSL_shutdown(ssl); // shutdown only if not already closed by remote
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(ctx_mutex);
|
||||
SSL_free(ssl);
|
||||
@@ -4495,11 +4548,11 @@ public:
|
||||
private:
|
||||
static void locking_callback(int mode, int type, const char * /*file*/,
|
||||
int /*line*/) {
|
||||
auto &locks = *openSSL_locks_;
|
||||
auto &lk = (*openSSL_locks_)[static_cast<size_t>(type)];
|
||||
if (mode & CRYPTO_LOCK) {
|
||||
locks[type].lock();
|
||||
lk.lock();
|
||||
} else {
|
||||
locks[type].unlock();
|
||||
lk.unlock();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -4607,6 +4660,33 @@ inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
|
||||
}
|
||||
}
|
||||
|
||||
inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
|
||||
X509_STORE *client_ca_cert_store) {
|
||||
ctx_ = SSL_CTX_new(SSLv23_server_method());
|
||||
|
||||
if (ctx_) {
|
||||
SSL_CTX_set_options(ctx_,
|
||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||
SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
|
||||
if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||
|
||||
SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
|
||||
SSL_CTX_free(ctx_);
|
||||
ctx_ = nullptr;
|
||||
} else if (client_ca_cert_store) {
|
||||
|
||||
SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
|
||||
|
||||
SSL_CTX_set_verify(
|
||||
ctx_,
|
||||
SSL_VERIFY_PEER |
|
||||
SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE,
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline SSLServer::~SSLServer() {
|
||||
if (ctx_) { SSL_CTX_free(ctx_); }
|
||||
}
|
||||
@@ -4646,6 +4726,24 @@ inline SSLClient::SSLClient(const std::string &host, int port,
|
||||
}
|
||||
}
|
||||
|
||||
inline SSLClient::SSLClient(const std::string &host, int port,
|
||||
X509 *client_cert, EVP_PKEY *client_key)
|
||||
: Client(host, port) {
|
||||
ctx_ = SSL_CTX_new(SSLv23_client_method());
|
||||
|
||||
detail::split(&host_[0], &host_[host_.size()], '.',
|
||||
[&](const char *b, const char *e) {
|
||||
host_components_.emplace_back(std::string(b, e));
|
||||
});
|
||||
if (client_cert != nullptr && client_key != nullptr) {
|
||||
if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
|
||||
SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
|
||||
SSL_CTX_free(ctx_);
|
||||
ctx_ = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline SSLClient::~SSLClient() {
|
||||
if (ctx_) { SSL_CTX_free(ctx_); }
|
||||
}
|
||||
@@ -4658,6 +4756,10 @@ inline void SSLClient::set_ca_cert_path(const char *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) { ca_cert_store_ = ca_cert_store; }
|
||||
}
|
||||
|
||||
inline void SSLClient::enable_server_certificate_verification(bool enabled) {
|
||||
server_certificate_verification_ = enabled;
|
||||
}
|
||||
@@ -4681,14 +4783,19 @@ inline bool SSLClient::process_and_close_socket(
|
||||
true, sock, request_count, read_timeout_sec_, read_timeout_usec_,
|
||||
ctx_, ctx_mutex_,
|
||||
[&](SSL *ssl) {
|
||||
if (ca_cert_file_path_.empty()) {
|
||||
if (ca_cert_file_path_.empty() && ca_cert_store_ == nullptr) {
|
||||
SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr);
|
||||
} else {
|
||||
} else if (!ca_cert_file_path_.empty()) {
|
||||
if (!SSL_CTX_load_verify_locations(
|
||||
ctx_, ca_cert_file_path_.c_str(), nullptr)) {
|
||||
return false;
|
||||
}
|
||||
SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr);
|
||||
} 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_);
|
||||
}
|
||||
SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr);
|
||||
}
|
||||
|
||||
if (SSL_connect(ssl) != 1) { return false; }
|
||||
|
||||
+154
-2
@@ -697,6 +697,36 @@ protected:
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
})
|
||||
.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_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_header("a", "1\nSet-Cookie: a=1");
|
||||
EXPECT_EQ(0, 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_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_header("a\r\nb", "0");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_header("a\rb", "0");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_header("a\nb", "0");
|
||||
EXPECT_EQ(0, 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_FALSE(res.has_header("Location"));
|
||||
})
|
||||
.Get("/slow",
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
@@ -719,6 +749,13 @@ protected:
|
||||
})
|
||||
.Get("/", [&](const Request & /*req*/,
|
||||
Response &res) { res.set_redirect("/hi"); })
|
||||
.Post("/1", [](const Request & /*req*/,
|
||||
Response &res) { res.set_redirect("/2", 303); })
|
||||
.Get("/2",
|
||||
[](const Request & /*req*/, Response &res) {
|
||||
res.set_content("redirected.", "text/plain");
|
||||
res.status = 200;
|
||||
})
|
||||
.Post("/person",
|
||||
[&](const Request &req, Response &res) {
|
||||
if (req.has_param("name") && req.has_param("note")) {
|
||||
@@ -882,6 +919,11 @@ protected:
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
res.set_content("DELETE", "text/plain");
|
||||
})
|
||||
.Delete("/delete-body",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(req.body, "content");
|
||||
res.set_content(req.body, "text/plain");
|
||||
})
|
||||
.Options(R"(\*)",
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
|
||||
@@ -1081,6 +1123,14 @@ TEST_F(ServerTest, GetMethod302) {
|
||||
EXPECT_EQ("/hi", res->get_header_value("Location"));
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetMethod302Redirect) {
|
||||
cli_.set_follow_location(true);
|
||||
auto res = cli_.Get("/");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("Hello World!", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetMethod404) {
|
||||
auto res = cli_.Get("/invalid");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
@@ -1280,6 +1330,21 @@ TEST_F(ServerTest, GetMethodOutOfBaseDirMount2) {
|
||||
EXPECT_EQ(404, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PostMethod303) {
|
||||
auto res = cli_.Post("/1", "body", "text/plain");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(303, res->status);
|
||||
EXPECT_EQ("/2", res->get_header_value("Location"));
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PostMethod303Redirect) {
|
||||
cli_.set_follow_location(true);
|
||||
auto res = cli_.Post("/1", "body", "text/plain");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("redirected.", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, UserDefinedMIMETypeMapping) {
|
||||
auto res = cli_.Get("/dir/test.abcde");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
@@ -1680,6 +1745,12 @@ TEST_F(ServerTest, GetMethodRemoteAddr) {
|
||||
EXPECT_TRUE(res->body == "::1" || res->body == "127.0.0.1");
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, HTTPResponseSplitting) {
|
||||
auto res = cli_.Get("/http_response_splitting");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, SlowRequest) {
|
||||
request_threads_.push_back(
|
||||
std::thread([=]() { auto res = cli_.Get("/slow"); }));
|
||||
@@ -1765,6 +1836,13 @@ TEST_F(ServerTest, Delete) {
|
||||
EXPECT_EQ("DELETE", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, DeleteContentReceiver) {
|
||||
auto res = cli_.Delete("/delete-body", "content", "text/plain");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("content", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, Options) {
|
||||
auto res = cli_.Options("*");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
@@ -2094,8 +2172,8 @@ TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity) {
|
||||
test_raw_request(
|
||||
"GET /hi HTTP/1.1\r\n"
|
||||
" : "
|
||||
" "
|
||||
);
|
||||
" "
|
||||
" ");
|
||||
}
|
||||
|
||||
TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity2) {
|
||||
@@ -2452,6 +2530,80 @@ TEST(SSLClientServerTest, ClientCertPresent) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST(SSLClientServerTest, MemoryClientCertPresent) {
|
||||
X509 *server_cert;
|
||||
EVP_PKEY *server_private_key;
|
||||
X509_STORE *client_ca_cert_store;
|
||||
X509 *client_cert;
|
||||
EVP_PKEY *client_private_key;
|
||||
|
||||
FILE *f = fopen(SERVER_CERT_FILE, "r+");
|
||||
server_cert = PEM_read_X509(f, nullptr, nullptr, nullptr);
|
||||
fclose(f);
|
||||
|
||||
f = fopen(SERVER_PRIVATE_KEY_FILE, "r+");
|
||||
server_private_key = PEM_read_PrivateKey(f, nullptr, nullptr, nullptr);
|
||||
fclose(f);
|
||||
|
||||
f = fopen(CLIENT_CA_CERT_FILE, "r+");
|
||||
client_cert = PEM_read_X509(f, nullptr, nullptr, nullptr);
|
||||
client_ca_cert_store = X509_STORE_new();
|
||||
X509_STORE_add_cert(client_ca_cert_store, client_cert);
|
||||
X509_free(client_cert);
|
||||
fclose(f);
|
||||
|
||||
f = fopen(CLIENT_CERT_FILE, "r+");
|
||||
client_cert = PEM_read_X509(f, nullptr, nullptr, nullptr);
|
||||
fclose(f);
|
||||
|
||||
f = fopen(CLIENT_PRIVATE_KEY_FILE, "r+");
|
||||
client_private_key = PEM_read_PrivateKey(f, nullptr, nullptr, nullptr);
|
||||
fclose(f);
|
||||
|
||||
SSLServer svr(server_cert, server_private_key, client_ca_cert_store);
|
||||
ASSERT_TRUE(svr.is_valid());
|
||||
|
||||
svr.Get("/test", [&](const Request &req, Response &res) {
|
||||
res.set_content("test", "text/plain");
|
||||
svr.stop();
|
||||
ASSERT_TRUE(true);
|
||||
|
||||
auto peer_cert = SSL_get_peer_certificate(req.ssl);
|
||||
ASSERT_TRUE(peer_cert != nullptr);
|
||||
|
||||
auto subject_name = X509_get_subject_name(peer_cert);
|
||||
ASSERT_TRUE(subject_name != nullptr);
|
||||
|
||||
std::string common_name;
|
||||
{
|
||||
char name[BUFSIZ];
|
||||
auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
|
||||
name, sizeof(name));
|
||||
common_name.assign(name, static_cast<size_t>(name_len));
|
||||
}
|
||||
|
||||
EXPECT_EQ("Common Name", common_name);
|
||||
|
||||
X509_free(peer_cert);
|
||||
});
|
||||
|
||||
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
|
||||
httplib::SSLClient cli(HOST, PORT, client_cert, client_private_key);
|
||||
auto res = cli.Get("/test");
|
||||
cli.set_timeout_sec(30);
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
ASSERT_EQ(200, res->status);
|
||||
|
||||
X509_free(server_cert);
|
||||
EVP_PKEY_free(server_private_key);
|
||||
X509_free(client_cert);
|
||||
EVP_PKEY_free(client_private_key);
|
||||
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST(SSLClientServerTest, ClientCertMissing) {
|
||||
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE,
|
||||
CLIENT_CA_CERT_DIR);
|
||||
|
||||
Reference in New Issue
Block a user