From 907257f51de5213b6f1febe983e7d39ad6e1118c Mon Sep 17 00:00:00 2001 From: "Kim, Hyuk" Date: Sat, 6 Jun 2026 05:34:20 +0900 Subject: [PATCH] add set_hostname_addr_map to WebSocketClient (#2463) * add set_hostname_addr_map to WebSocketClient * add WebSocketTest unit test cases * SpecifyServerIPAddress_AnotherHostname * SpecifyServerIPAddress_RealHostname * Change wrong_ip from 0.0.0.0 to 192.0.2.1 Use 192.0.2.1 (RFC 5737 documentation address) to ensure it acts as a non-routable address and does not alias to loopback. * Fix style check * set short timeout in WebSocketTest.SpecifyServerIPAddress_RealHostname cannot reach wrong_ip --- httplib.h | 16 +++++++++++++++- test/test.cc | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/httplib.h b/httplib.h index c04b2ffa..b36cbc08 100644 --- a/httplib.h +++ b/httplib.h @@ -3848,6 +3848,7 @@ public: void set_socket_options(SocketOptions socket_options); void set_connection_timeout(time_t sec, time_t usec = 0); void set_interface(const std::string &intf); + void set_hostname_addr_map(std::map addr_map); #ifdef CPPHTTPLIB_SSL_ENABLED void set_ca_cert_path(const std::string &path); @@ -3882,6 +3883,9 @@ private: time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND; std::string interface_; + // Hostname-IP map + std::map addr_map_; + #ifdef CPPHTTPLIB_SSL_ENABLED bool is_ssl_ = false; tls::ctx_t tls_ctx_ = nullptr; @@ -20326,9 +20330,14 @@ inline bool WebSocketClient::connect() { if (!is_valid_) { return false; } shutdown_and_close(); + // Check is custom IP specified for host_ + std::string ip; + auto it = addr_map_.find(host_); + if (it != addr_map_.end()) { ip = it->second; } + Error error; sock_ = detail::create_client_socket( - host_, std::string(), port_, address_family_, tcp_nodelay_, ipv6_v6only_, + host_, ip, port_, address_family_, tcp_nodelay_, ipv6_v6only_, socket_options_, connection_timeout_sec_, connection_timeout_usec_, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, interface_, error); @@ -20423,6 +20432,11 @@ inline void WebSocketClient::set_interface(const std::string &intf) { interface_ = intf; } +inline void WebSocketClient::set_hostname_addr_map( + std::map addr_map) { + addr_map_ = std::move(addr_map); +} + #ifdef CPPHTTPLIB_SSL_ENABLED inline void WebSocketClient::set_ca_cert_path(const std::string &path) { diff --git a/test/test.cc b/test/test.cc index fa4b0e04..223c6a19 100644 --- a/test/test.cc +++ b/test/test.cc @@ -17706,6 +17706,57 @@ TEST(WebSocketTest, ComplexPath) { EXPECT_TRUE(ws2.is_valid()); } +TEST(WebSocketTest, SpecifyServerIPAddress_AnotherHostname) { + Server svr; + svr.WebSocket("/ws", [](const Request &, ws::WebSocket &ws) { + std::string msg; + while (ws.read(msg)) {} + }); + + auto port = svr.bind_to_any_port(HOST); + std::thread t([&]() { svr.listen_after_bind(); }); + svr.wait_until_ready(); + + auto another_host = "example.com"; + auto wrong_ip = "192.0.2.1"; + + ws::WebSocketClient client("ws://localhost:" + std::to_string(port) + "/ws"); + client.set_hostname_addr_map({{another_host, wrong_ip}}); + + ASSERT_TRUE(client.connect()); + EXPECT_TRUE(client.is_open()); + client.close(); + + svr.stop(); + t.join(); +} + +TEST(WebSocketTest, SpecifyServerIPAddress_RealHostname) { + Server svr; + svr.WebSocket("/ws", [](const Request &, ws::WebSocket &ws) { + std::string msg; + while (ws.read(msg)) {} + }); + + auto port = svr.bind_to_any_port(HOST); + std::thread t([&]() { svr.listen_after_bind(); }); + svr.wait_until_ready(); + + auto wrong_ip = "192.0.2.1"; + + ws::WebSocketClient client("ws://localhost:" + std::to_string(port) + "/ws"); + client.set_hostname_addr_map({{"localhost", wrong_ip}}); + client.set_connection_timeout(1); + client.set_read_timeout(1); + client.set_write_timeout(1); + + EXPECT_FALSE(client.connect()); + EXPECT_FALSE(client.is_open()); + + svr.stop(); + t.join(); +} + class WebSocketIntegrationTest : public ::testing::Test { protected: void SetUp() override {