From 1fcc889e54183548e3b45145b98e5463bdaae73b Mon Sep 17 00:00:00 2001 From: Changbin Park Date: Thu, 28 Jul 2022 16:37:18 +0900 Subject: [PATCH 1/4] Add support UNIX domain socket * `set_address_family(AF_UNIX)` is required --- httplib.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/httplib.h b/httplib.h index 2a3742b0eb..acb4a7d14d 100644 --- a/httplib.h +++ b/httplib.h @@ -183,6 +183,7 @@ using socket_t = SOCKET; #include #include #include +#include #include using socket_t = int; @@ -2570,6 +2571,30 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port, hints.ai_flags = socket_flags; } +#ifndef _WIN32 + if (hints.ai_family == AF_UNIX) { + const auto addrlen = host.length(); + if (addrlen > sizeof(sockaddr_un::sun_path)) return INVALID_SOCKET; + + auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol); + if (sock != INVALID_SOCKET) { + sockaddr_un addr; + addr.sun_family = AF_UNIX; + std::copy(host.begin(), host.end(), addr.sun_path); + + hints.ai_addr = reinterpret_cast(&addr); + hints.ai_addrlen = static_cast( + sizeof(addr) - sizeof(addr.sun_path) + addrlen); + + if (!bind_or_connect(sock, hints)) { + close_socket(sock); + sock = INVALID_SOCKET; + } + } + return sock; + } +#endif + auto service = std::to_string(port); if (getaddrinfo(node, service.c_str(), &hints, &result)) { From 5606bb06df5a0f88d6f80e3075dec177749e3148 Mon Sep 17 00:00:00 2001 From: Changbin Park Date: Thu, 28 Jul 2022 16:52:31 +0900 Subject: [PATCH 2/4] add unittest for UNIX domain socket --- test/test.cc | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/test.cc b/test/test.cc index 204ae4328c..bec15bed05 100644 --- a/test/test.cc +++ b/test/test.cc @@ -5064,3 +5064,43 @@ TEST(MultipartFormDataTest, WithPreamble) { #endif +#ifndef _WIN32 +class UnixSocketTest : public ::testing::Test { +protected: + void TearDown() override { + std::remove(pathname_.c_str()); + } + + const std::string pathname_ {"./httplib-server.sock"}; + const std::string pattern_ {"/hi"}; + const std::string content_ {"Hello World!"}; +}; + +TEST_F(UnixSocketTest, pathname) { + httplib::Server svr; + svr.Get(pattern_, [&](const httplib::Request &, httplib::Response &res) { + res.set_content(content_, "text/plain"); + }); + + std::thread t {[&] { + ASSERT_TRUE(svr.set_address_family(AF_UNIX).listen(pathname_, 80)); }}; + while (!svr.is_running()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + ASSERT_TRUE(svr.is_running()); + + httplib::Client cli{pathname_}; + cli.set_address_family(AF_UNIX); + ASSERT_TRUE(cli.is_valid()); + + const auto &result = cli.Get(pattern_); + ASSERT_TRUE(result) << "error: " << result.error(); + + const auto &resp = result.value(); + EXPECT_EQ(resp.status, 200); + EXPECT_EQ(resp.body, content_); + + svr.stop(); + t.join(); +} +#endif From 380f6a23495bfc2e3794ead58b2c6c15d969299f Mon Sep 17 00:00:00 2001 From: Changbin Park Date: Mon, 1 Aug 2022 10:12:20 +0900 Subject: [PATCH 3/4] add support UNIX domain socket with abstract address Abstract address of AF_UNIX begins with null(0x00) which can't be delivered via .c_str() method. --- httplib.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/httplib.h b/httplib.h index acb4a7d14d..45b315279d 100644 --- a/httplib.h +++ b/httplib.h @@ -7883,12 +7883,12 @@ inline Client::Client(const std::string &scheme_host_port, if (is_ssl) { #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - cli_ = detail::make_unique(host.c_str(), port, + cli_ = detail::make_unique(host, port, client_cert_path, client_key_path); is_ssl_ = is_ssl; #endif } else { - cli_ = detail::make_unique(host.c_str(), port, + cli_ = detail::make_unique(host, port, client_cert_path, client_key_path); } } else { From 81bc57c66d5a766fde7dd2bd011bcb874f478afb Mon Sep 17 00:00:00 2001 From: Changbin Park Date: Mon, 1 Aug 2022 10:11:55 +0900 Subject: [PATCH 4/4] add unittest for UNIX domain socket with abstract address --- test/test.cc | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/test/test.cc b/test/test.cc index bec15bed05..fc0fa0953d 100644 --- a/test/test.cc +++ b/test/test.cc @@ -5071,6 +5071,19 @@ class UnixSocketTest : public ::testing::Test { std::remove(pathname_.c_str()); } + void client_GET(const std::string &addr) { + httplib::Client cli{addr}; + cli.set_address_family(AF_UNIX); + ASSERT_TRUE(cli.is_valid()); + + const auto &result = cli.Get(pattern_); + ASSERT_TRUE(result) << "error: " << result.error(); + + const auto &resp = result.value(); + EXPECT_EQ(resp.status, 200); + EXPECT_EQ(resp.body, content_); + } + const std::string pathname_ {"./httplib-server.sock"}; const std::string pattern_ {"/hi"}; const std::string content_ {"Hello World!"}; @@ -5089,18 +5102,33 @@ TEST_F(UnixSocketTest, pathname) { } ASSERT_TRUE(svr.is_running()); - httplib::Client cli{pathname_}; - cli.set_address_family(AF_UNIX); - ASSERT_TRUE(cli.is_valid()); + client_GET(pathname_); + + svr.stop(); + t.join(); +} + +#ifdef __linux__ +TEST_F(UnixSocketTest, abstract) { + constexpr char svr_path[] {"\x00httplib-server.sock"}; + const std::string abstract_addr {svr_path, sizeof(svr_path) - 1}; - const auto &result = cli.Get(pattern_); - ASSERT_TRUE(result) << "error: " << result.error(); + httplib::Server svr; + svr.Get(pattern_, [&](const httplib::Request &, httplib::Response &res) { + res.set_content(content_, "text/plain"); + }); + + std::thread t {[&] { + ASSERT_TRUE(svr.set_address_family(AF_UNIX).listen(abstract_addr, 80)); }}; + while (!svr.is_running()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + ASSERT_TRUE(svr.is_running()); - const auto &resp = result.value(); - EXPECT_EQ(resp.status, 200); - EXPECT_EQ(resp.body, content_); + client_GET(abstract_addr); svr.stop(); t.join(); } #endif +#endif // #ifndef _WIN32