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
This commit is contained in:
Kim, Hyuk
2026-06-05 16:34:20 -04:00
committed by GitHub
parent 0c2f535b74
commit 907257f51d
2 changed files with 66 additions and 1 deletions
+15 -1
View File
@@ -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<std::string, std::string> 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<std::string, std::string> 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<std::string, std::string> addr_map) {
addr_map_ = std::move(addr_map);
}
#ifdef CPPHTTPLIB_SSL_ENABLED
inline void WebSocketClient::set_ca_cert_path(const std::string &path) {
+51
View File
@@ -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 {