Skip to content

Commit 362d064

Browse files
UNIX domain socket support (#1346)
* Add support UNIX domain socket * `set_address_family(AF_UNIX)` is required * add unittest for UNIX domain socket * 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. * add unittest for UNIX domain socket with abstract address Co-authored-by: Changbin Park <[email protected]>
1 parent 1bd88de commit 362d064

File tree

2 files changed

+95
-2
lines changed

2 files changed

+95
-2
lines changed

httplib.h

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ using socket_t = SOCKET;
183183
#include <pthread.h>
184184
#include <sys/select.h>
185185
#include <sys/socket.h>
186+
#include <sys/un.h>
186187
#include <unistd.h>
187188

188189
using socket_t = int;
@@ -2570,6 +2571,30 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port,
25702571
hints.ai_flags = socket_flags;
25712572
}
25722573

2574+
#ifndef _WIN32
2575+
if (hints.ai_family == AF_UNIX) {
2576+
const auto addrlen = host.length();
2577+
if (addrlen > sizeof(sockaddr_un::sun_path)) return INVALID_SOCKET;
2578+
2579+
auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
2580+
if (sock != INVALID_SOCKET) {
2581+
sockaddr_un addr;
2582+
addr.sun_family = AF_UNIX;
2583+
std::copy(host.begin(), host.end(), addr.sun_path);
2584+
2585+
hints.ai_addr = reinterpret_cast<sockaddr*>(&addr);
2586+
hints.ai_addrlen = static_cast<socklen_t>(
2587+
sizeof(addr) - sizeof(addr.sun_path) + addrlen);
2588+
2589+
if (!bind_or_connect(sock, hints)) {
2590+
close_socket(sock);
2591+
sock = INVALID_SOCKET;
2592+
}
2593+
}
2594+
return sock;
2595+
}
2596+
#endif
2597+
25732598
auto service = std::to_string(port);
25742599

25752600
if (getaddrinfo(node, service.c_str(), &hints, &result)) {
@@ -7858,12 +7883,12 @@ inline Client::Client(const std::string &scheme_host_port,
78587883

78597884
if (is_ssl) {
78607885
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7861-
cli_ = detail::make_unique<SSLClient>(host.c_str(), port,
7886+
cli_ = detail::make_unique<SSLClient>(host, port,
78627887
client_cert_path, client_key_path);
78637888
is_ssl_ = is_ssl;
78647889
#endif
78657890
} else {
7866-
cli_ = detail::make_unique<ClientImpl>(host.c_str(), port,
7891+
cli_ = detail::make_unique<ClientImpl>(host, port,
78677892
client_cert_path, client_key_path);
78687893
}
78697894
} else {

test/test.cc

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5064,3 +5064,71 @@ TEST(MultipartFormDataTest, WithPreamble) {
50645064

50655065
#endif
50665066

5067+
#ifndef _WIN32
5068+
class UnixSocketTest : public ::testing::Test {
5069+
protected:
5070+
void TearDown() override {
5071+
std::remove(pathname_.c_str());
5072+
}
5073+
5074+
void client_GET(const std::string &addr) {
5075+
httplib::Client cli{addr};
5076+
cli.set_address_family(AF_UNIX);
5077+
ASSERT_TRUE(cli.is_valid());
5078+
5079+
const auto &result = cli.Get(pattern_);
5080+
ASSERT_TRUE(result) << "error: " << result.error();
5081+
5082+
const auto &resp = result.value();
5083+
EXPECT_EQ(resp.status, 200);
5084+
EXPECT_EQ(resp.body, content_);
5085+
}
5086+
5087+
const std::string pathname_ {"./httplib-server.sock"};
5088+
const std::string pattern_ {"/hi"};
5089+
const std::string content_ {"Hello World!"};
5090+
};
5091+
5092+
TEST_F(UnixSocketTest, pathname) {
5093+
httplib::Server svr;
5094+
svr.Get(pattern_, [&](const httplib::Request &, httplib::Response &res) {
5095+
res.set_content(content_, "text/plain");
5096+
});
5097+
5098+
std::thread t {[&] {
5099+
ASSERT_TRUE(svr.set_address_family(AF_UNIX).listen(pathname_, 80)); }};
5100+
while (!svr.is_running()) {
5101+
std::this_thread::sleep_for(std::chrono::milliseconds(1));
5102+
}
5103+
ASSERT_TRUE(svr.is_running());
5104+
5105+
client_GET(pathname_);
5106+
5107+
svr.stop();
5108+
t.join();
5109+
}
5110+
5111+
#ifdef __linux__
5112+
TEST_F(UnixSocketTest, abstract) {
5113+
constexpr char svr_path[] {"\x00httplib-server.sock"};
5114+
const std::string abstract_addr {svr_path, sizeof(svr_path) - 1};
5115+
5116+
httplib::Server svr;
5117+
svr.Get(pattern_, [&](const httplib::Request &, httplib::Response &res) {
5118+
res.set_content(content_, "text/plain");
5119+
});
5120+
5121+
std::thread t {[&] {
5122+
ASSERT_TRUE(svr.set_address_family(AF_UNIX).listen(abstract_addr, 80)); }};
5123+
while (!svr.is_running()) {
5124+
std::this_thread::sleep_for(std::chrono::milliseconds(1));
5125+
}
5126+
ASSERT_TRUE(svr.is_running());
5127+
5128+
client_GET(abstract_addr);
5129+
5130+
svr.stop();
5131+
t.join();
5132+
}
5133+
#endif
5134+
#endif // #ifndef _WIN32

0 commit comments

Comments
 (0)