|
|
|
@@ -7,6 +7,7 @@
|
|
|
|
|
#include <thread>
|
|
|
|
|
|
|
|
|
|
#define SERVER_CERT_FILE "./cert.pem"
|
|
|
|
|
#define SERVER_CERT2_FILE "./cert2.pem"
|
|
|
|
|
#define SERVER_PRIVATE_KEY_FILE "./key.pem"
|
|
|
|
|
#define CA_CERT_FILE "./ca-bundle.crt"
|
|
|
|
|
#define CLIENT_CA_CERT_FILE "./rootCA.cert.pem"
|
|
|
|
@@ -134,6 +135,17 @@ TEST(GetHeaderValueTest, RegularValue) {
|
|
|
|
|
EXPECT_STREQ("text/html", val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(GetHeaderValueTest, SetContent) {
|
|
|
|
|
Response res;
|
|
|
|
|
|
|
|
|
|
res.set_content("html", "text/html");
|
|
|
|
|
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("text/plain", res.get_header_value("Content-Type"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(GetHeaderValueTest, RegularValueInt) {
|
|
|
|
|
Headers headers = {{"Content-Length", "100"}, {"Dummy", "Dummy"}};
|
|
|
|
|
auto val =
|
|
|
|
@@ -829,7 +841,7 @@ TEST(UrlWithSpace, Redirect) {
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
TEST(Server, BindDualStack) {
|
|
|
|
|
TEST(BindServerTest, BindDualStack) {
|
|
|
|
|
Server svr;
|
|
|
|
|
|
|
|
|
|
svr.Get("/1", [&](const Request & /*req*/, Response &res) {
|
|
|
|
@@ -862,7 +874,7 @@ TEST(Server, BindDualStack) {
|
|
|
|
|
ASSERT_FALSE(svr.is_running());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Server, BindAndListenSeparately) {
|
|
|
|
|
TEST(BindServerTest, BindAndListenSeparately) {
|
|
|
|
|
Server svr;
|
|
|
|
|
int port = svr.bind_to_any_port("0.0.0.0");
|
|
|
|
|
ASSERT_TRUE(svr.is_valid());
|
|
|
|
@@ -871,7 +883,7 @@ TEST(Server, BindAndListenSeparately) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
TEST(SSLServer, BindAndListenSeparately) {
|
|
|
|
|
TEST(BindServerTest, BindAndListenSeparatelySSL) {
|
|
|
|
|
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE,
|
|
|
|
|
CLIENT_CA_CERT_DIR);
|
|
|
|
|
int port = svr.bind_to_any_port("0.0.0.0");
|
|
|
|
@@ -881,6 +893,41 @@ TEST(SSLServer, BindAndListenSeparately) {
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
TEST(ErrorHandlerTest, ContentLength) {
|
|
|
|
|
Server svr;
|
|
|
|
|
|
|
|
|
|
svr.set_error_handler([](const Request & /*req*/, Response &res) {
|
|
|
|
|
res.status = 200;
|
|
|
|
|
res.set_content("abcdefghijklmnopqrstuvwxyz",
|
|
|
|
|
"text/html"); // <= Content-Length still 13
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
svr.Get("/hi", [](const Request & /*req*/, Response &res) {
|
|
|
|
|
res.set_content("Hello World!\n", "text/plain");
|
|
|
|
|
res.status = 524;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
auto thread = std::thread([&]() { svr.listen(HOST, PORT); });
|
|
|
|
|
|
|
|
|
|
// Give GET time to get a few messages.
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
Client cli(HOST, PORT);
|
|
|
|
|
|
|
|
|
|
auto res = cli.Get("/hi");
|
|
|
|
|
ASSERT_TRUE(res);
|
|
|
|
|
EXPECT_EQ(200, res->status);
|
|
|
|
|
EXPECT_EQ("text/html", res->get_header_value("Content-Type"));
|
|
|
|
|
EXPECT_EQ("26", res->get_header_value("Content-Length"));
|
|
|
|
|
EXPECT_EQ("abcdefghijklmnopqrstuvwxyz", res->body);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
svr.stop();
|
|
|
|
|
thread.join();
|
|
|
|
|
ASSERT_FALSE(svr.is_running());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class ServerTest : public ::testing::Test {
|
|
|
|
|
protected:
|
|
|
|
|
ServerTest()
|
|
|
|
@@ -1897,6 +1944,41 @@ TEST_F(ServerTest, GetStreamedWithRange2) {
|
|
|
|
|
EXPECT_EQ(std::string("bcdefg"), res->body);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ServerTest, GetStreamedWithRangeSuffix1) {
|
|
|
|
|
auto res = cli_.Get("/streamed-with-range", {{"Range", "bytes=-3"}});
|
|
|
|
|
ASSERT_TRUE(res);
|
|
|
|
|
EXPECT_EQ(206, res->status);
|
|
|
|
|
EXPECT_EQ("3", res->get_header_value("Content-Length"));
|
|
|
|
|
EXPECT_EQ(true, res->has_header("Content-Range"));
|
|
|
|
|
EXPECT_EQ(std::string("efg"), res->body);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ServerTest, GetStreamedWithRangeSuffix2) {
|
|
|
|
|
auto res = cli_.Get("/streamed-with-range", {{"Range", "bytes=-9999"}});
|
|
|
|
|
ASSERT_TRUE(res);
|
|
|
|
|
EXPECT_EQ(206, res->status);
|
|
|
|
|
EXPECT_EQ("7", res->get_header_value("Content-Length"));
|
|
|
|
|
EXPECT_EQ(true, res->has_header("Content-Range"));
|
|
|
|
|
EXPECT_EQ(std::string("abcdefg"), res->body);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ServerTest, GetStreamedWithRangeError) {
|
|
|
|
|
auto res = cli_.Get("/streamed-with-range",
|
|
|
|
|
{{"Range", "bytes=92233720368547758079223372036854775806-"
|
|
|
|
|
"92233720368547758079223372036854775807"}});
|
|
|
|
|
ASSERT_TRUE(res);
|
|
|
|
|
EXPECT_EQ(416, res->status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ServerTest, GetRangeWithMaxLongLength) {
|
|
|
|
|
auto res =
|
|
|
|
|
cli_.Get("/with-range", {{"Range", "bytes=0-9223372036854775807"}});
|
|
|
|
|
EXPECT_EQ(206, res->status);
|
|
|
|
|
EXPECT_EQ("7", res->get_header_value("Content-Length"));
|
|
|
|
|
EXPECT_EQ(true, res->has_header("Content-Range"));
|
|
|
|
|
EXPECT_EQ(std::string("abcdefg"), res->body);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ServerTest, GetStreamedWithRangeMultipart) {
|
|
|
|
|
auto res =
|
|
|
|
|
cli_.Get("/streamed-with-range", {{make_range_header({{1, 2}, {4, 5}})}});
|
|
|
|
@@ -1932,8 +2014,7 @@ TEST_F(ServerTest, ClientStop) {
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(2));
|
|
|
|
|
while (cli_.is_socket_open()) {
|
|
|
|
|
cli_.stop();
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
|
|
@@ -1979,6 +2060,12 @@ TEST_F(ServerTest, GetWithRange4) {
|
|
|
|
|
EXPECT_EQ(std::string("fg"), res->body);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ServerTest, GetWithRangeOffsetGreaterThanContent) {
|
|
|
|
|
auto res = cli_.Get("/with-range", {{make_range_header({{10000, 20000}})}});
|
|
|
|
|
ASSERT_TRUE(res);
|
|
|
|
|
EXPECT_EQ(416, res->status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ServerTest, GetWithRangeMultipart) {
|
|
|
|
|
auto res = cli_.Get("/with-range", {{make_range_header({{1, 2}, {4, 5}})}});
|
|
|
|
|
ASSERT_TRUE(res);
|
|
|
|
@@ -1988,6 +2075,13 @@ TEST_F(ServerTest, GetWithRangeMultipart) {
|
|
|
|
|
EXPECT_EQ(269, res->body.size());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ServerTest, GetWithRangeMultipartOffsetGreaterThanContent) {
|
|
|
|
|
auto res =
|
|
|
|
|
cli_.Get("/with-range", {{make_range_header({{-1, 2}, {10000, 30000}})}});
|
|
|
|
|
ASSERT_TRUE(res);
|
|
|
|
|
EXPECT_EQ(416, res->status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ServerTest, GetStreamedChunked) {
|
|
|
|
|
auto res = cli_.Get("/streamed-chunked");
|
|
|
|
|
ASSERT_TRUE(res);
|
|
|
|
@@ -3005,9 +3099,7 @@ TEST(KeepAliveTest, ReadTimeoutSSL) {
|
|
|
|
|
res.set_content("b", "text/plain");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
auto listen_thread = std::thread([&svr]() {
|
|
|
|
|
svr.listen("localhost", PORT);
|
|
|
|
|
});
|
|
|
|
|
auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); });
|
|
|
|
|
while (!svr.is_running()) {
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
|
|
|
}
|
|
|
|
@@ -3213,6 +3305,31 @@ TEST(SSLClientTest, ServerCertificateVerification3) {
|
|
|
|
|
ASSERT_EQ(301, res->status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(SSLClientTest, ServerCertificateVerification4) {
|
|
|
|
|
SSLServer svr(SERVER_CERT2_FILE, SERVER_PRIVATE_KEY_FILE);
|
|
|
|
|
ASSERT_TRUE(svr.is_valid());
|
|
|
|
|
|
|
|
|
|
svr.Get("/test", [&](const Request &, Response &res) {
|
|
|
|
|
res.set_content("test", "text/plain");
|
|
|
|
|
svr.stop();
|
|
|
|
|
ASSERT_TRUE(true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
thread t = thread([&]() { ASSERT_TRUE(svr.listen("127.0.0.1", PORT)); });
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
|
|
|
|
|
|
|
|
SSLClient cli("127.0.0.1", PORT);
|
|
|
|
|
cli.set_ca_cert_path(SERVER_CERT2_FILE);
|
|
|
|
|
cli.enable_server_certificate_verification(true);
|
|
|
|
|
cli.set_connection_timeout(30);
|
|
|
|
|
|
|
|
|
|
auto res = cli.Get("/test");
|
|
|
|
|
ASSERT_TRUE(res);
|
|
|
|
|
ASSERT_EQ(200, res->status);
|
|
|
|
|
|
|
|
|
|
t.join();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(SSLClientTest, WildcardHostNameMatch) {
|
|
|
|
|
SSLClient cli("www.youtube.com");
|
|
|
|
|
|
|
|
|
@@ -3267,6 +3384,7 @@ TEST(SSLClientServerTest, ClientCertPresent) {
|
|
|
|
|
t.join();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if !defined(_WIN32) || defined(OPENSSL_USE_APPLINK)
|
|
|
|
|
TEST(SSLClientServerTest, MemoryClientCertPresent) {
|
|
|
|
|
X509 *server_cert;
|
|
|
|
|
EVP_PKEY *server_private_key;
|
|
|
|
@@ -3342,6 +3460,7 @@ TEST(SSLClientServerTest, MemoryClientCertPresent) {
|
|
|
|
|
|
|
|
|
|
t.join();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
TEST(SSLClientServerTest, ClientCertMissing) {
|
|
|
|
|
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE,
|
|
|
|
@@ -3386,6 +3505,51 @@ TEST(SSLClientServerTest, TrustDirOptional) {
|
|
|
|
|
|
|
|
|
|
t.join();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(SSLClientServerTest, SSLConnectTimeout) {
|
|
|
|
|
class NoListenSSLServer : public SSLServer {
|
|
|
|
|
public:
|
|
|
|
|
NoListenSSLServer(const char *cert_path, const char *private_key_path,
|
|
|
|
|
const char *client_ca_cert_file_path,
|
|
|
|
|
const char *client_ca_cert_dir_path = nullptr)
|
|
|
|
|
: SSLServer(cert_path, private_key_path, client_ca_cert_file_path,
|
|
|
|
|
client_ca_cert_dir_path),
|
|
|
|
|
stop_(false) {}
|
|
|
|
|
|
|
|
|
|
bool stop_;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
bool process_and_close_socket(socket_t /*sock*/) override {
|
|
|
|
|
// Don't create SSL context
|
|
|
|
|
while (!stop_) {
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
NoListenSSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE,
|
|
|
|
|
CLIENT_CA_CERT_FILE);
|
|
|
|
|
ASSERT_TRUE(svr.is_valid());
|
|
|
|
|
|
|
|
|
|
svr.Get("/test", [&](const Request &, Response &res) {
|
|
|
|
|
res.set_content("test", "text/plain");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
|
|
|
|
|
|
|
|
SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
|
|
|
|
|
cli.enable_server_certificate_verification(false);
|
|
|
|
|
cli.set_connection_timeout(1);
|
|
|
|
|
|
|
|
|
|
auto res = cli.Get("/test");
|
|
|
|
|
ASSERT_TRUE(!res);
|
|
|
|
|
EXPECT_EQ(Error::SSLConnection, res.error());
|
|
|
|
|
|
|
|
|
|
svr.stop_ = true;
|
|
|
|
|
svr.stop();
|
|
|
|
|
t.join();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
@@ -3395,17 +3559,15 @@ TEST(CleanupTest, WSACleanup) {
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// #ifndef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
// TEST(NoSSLSupport, SimpleInterface) {
|
|
|
|
|
// Client cli("https://yahoo.com");
|
|
|
|
|
// ASSERT_FALSE(cli.is_valid());
|
|
|
|
|
// }
|
|
|
|
|
// #endif
|
|
|
|
|
#ifndef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
TEST(NoSSLSupport, SimpleInterface) {
|
|
|
|
|
ASSERT_ANY_THROW(Client cli("https://yahoo.com"));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
TEST(InvalidScheme, SimpleInterface) {
|
|
|
|
|
Client cli("scheme://yahoo.com");
|
|
|
|
|
ASSERT_FALSE(cli.is_valid());
|
|
|
|
|
ASSERT_ANY_THROW(Client cli("scheme://yahoo.com"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(NoScheme, SimpleInterface) {
|
|
|
|
|