This is an automated email from the ASF dual-hosted git repository.

hgruszecki pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iggy.git


The following commit(s) were added to refs/heads/master by this push:
     new e03355b58 feat(sdk): support hostnames in QUIC and WebSocket clients 
(#2768)
e03355b58 is described below

commit e03355b58deeec63a7526c2dbc3c5cb5ac954534
Author: xin <[email protected]>
AuthorDate: Thu Feb 19 21:44:38 2026 +0900

    feat(sdk): support hostnames in QUIC and WebSocket clients (#2768)
    
    Closes #2729.
    
    ---------
    
    Signed-off-by: shin <[email protected]>
    Signed-off-by: seokjin0414 <[email protected]>
---
 core/bench/src/args/examples.rs            |  6 ++-
 core/common/src/types/args/mod.rs          | 16 ++----
 core/sdk/src/quic/quic_client.rs           | 81 ++++++++++++++++++++++++------
 core/sdk/src/websocket/websocket_client.rs | 28 +++++++++--
 4 files changed, 98 insertions(+), 33 deletions(-)

diff --git a/core/bench/src/args/examples.rs b/core/bench/src/args/examples.rs
index 40c63ced3..ddf888dbc 100644
--- a/core/bench/src/args/examples.rs
+++ b/core/bench/src/args/examples.rs
@@ -118,11 +118,15 @@ const EXAMPLES: &str = r#"EXAMPLES:
 
 5) Remote Server Benchmarking:
 
-    To benchmark a remote server, specify the server address in the transport 
subcommand:
+    To benchmark a remote server, specify the server address in the transport 
subcommand.
+    Both IP addresses and hostnames are supported:
 
     $ cargo r -r --bin iggy-bench -- pinned-producer \
         --streams 5 --producers 5 \
         tcp --server-address 192.168.1.100:8090
+    $ cargo r -r --bin iggy-bench -- pinned-producer \
+        --streams 5 --producers 5 \
+        tcp --server-address localhost:8090
 
     With custom credentials:
 
diff --git a/core/common/src/types/args/mod.rs 
b/core/common/src/types/args/mod.rs
index cc22f9439..1315cba2c 100644
--- a/core/common/src/types/args/mod.rs
+++ b/core/common/src/types/args/mod.rs
@@ -362,18 +362,10 @@ const WEBSOCKET_TRANSPORT: &str = "websocket";
 impl Args {
     pub fn get_server_address(&self) -> Option<String> {
         match self.transport.as_str() {
-            QUIC_TRANSPORT => 
Some(self.quic_server_address.replace("localhost", "127.0.0.1")),
-            HTTP_TRANSPORT => Some(
-                self.http_api_url
-                    .clone()
-                    .replace("http://";, "")
-                    .replace("localhost", "127.0.0.1"),
-            ),
-            TCP_TRANSPORT => Some(self.tcp_server_address.replace("localhost", 
"127.0.0.1")),
-            WEBSOCKET_TRANSPORT => Some(
-                self.websocket_server_address
-                    .replace("localhost", "127.0.0.1"),
-            ),
+            QUIC_TRANSPORT => Some(self.quic_server_address.clone()),
+            HTTP_TRANSPORT => 
Some(self.http_api_url.clone().replace("http://";, "")),
+            TCP_TRANSPORT => Some(self.tcp_server_address.clone()),
+            WEBSOCKET_TRANSPORT => Some(self.websocket_server_address.clone()),
             _ => None,
         }
     }
diff --git a/core/sdk/src/quic/quic_client.rs b/core/sdk/src/quic/quic_client.rs
index e55980d16..9ca335765 100644
--- a/core/sdk/src/quic/quic_client.rs
+++ b/core/sdk/src/quic/quic_client.rs
@@ -34,7 +34,7 @@ use iggy_common::{
 use quinn::crypto::rustls::QuicClientConfig as QuinnQuicClientConfig;
 use quinn::{ClientConfig, Connection, Endpoint, IdleTimeout, RecvStream, 
VarInt};
 use rustls::crypto::CryptoProvider;
-use std::net::SocketAddr;
+use std::net::{SocketAddr, ToSocketAddrs};
 use std::str::FromStr;
 use std::sync::Arc;
 use std::time::Duration;
@@ -165,14 +165,13 @@ impl QuicClient {
 
     /// Create a new QUIC client for the provided configuration.
     pub fn create(config: Arc<QuicClientConfig>) -> Result<Self, IggyError> {
-        let server_address = config
+        let resolved_addr = config
             .server_address
-            .parse::<SocketAddr>()
-            .map_err(|error| {
-                error!("Invalid server address: {error}");
-                IggyError::InvalidServerAddress
-            })?;
-        let client_address = if server_address.is_ipv6()
+            .to_socket_addrs()
+            .ok()
+            .and_then(|mut addrs| addrs.next());
+
+        let client_address = if resolved_addr.is_some_and(|a| a.is_ipv6())
             && config.client_address == 
QuicClientConfig::default().client_address
         {
             "[::1]:0"
@@ -195,6 +194,7 @@ impl QuicClient {
         let mut endpoint = endpoint.unwrap();
         endpoint.set_default_client_config(quic_config);
 
+        let server_address = config.server_address.clone();
         Ok(Self {
             config,
             endpoint,
@@ -203,7 +203,7 @@ impl QuicClient {
             events: broadcast(1000),
             connected_at: Mutex::new(None),
             leader_redirection_state: 
Mutex::new(LeaderRedirectionState::new()),
-            current_server_address: Mutex::new(server_address.to_string()),
+            current_server_address: Mutex::new(server_address),
         })
     }
 
@@ -304,13 +304,20 @@ impl QuicClient {
             let remote_address;
             loop {
                 let server_address_str = 
self.current_server_address.lock().await.clone();
-                let server_address: SocketAddr = 
server_address_str.parse().map_err(|e| {
-                    error!(
-                        "Failed to parse server address '{}': {}",
-                        server_address_str, e
-                    );
-                    IggyError::InvalidServerAddress
-                })?;
+                let server_address = 
tokio::net::lookup_host(&server_address_str)
+                    .await
+                    .map_err(|e| {
+                        error!(
+                            "Failed to resolve server address '{}': {}",
+                            server_address_str, e
+                        );
+                        IggyError::InvalidServerAddress
+                    })?
+                    .next()
+                    .ok_or_else(|| {
+                        error!("No addresses resolved for '{}'", 
server_address_str);
+                        IggyError::InvalidServerAddress
+                    })?;
                 info!(
                     "{NAME} client is connecting to server: {}...",
                     server_address
@@ -912,4 +919,46 @@ mod tests {
             IggyDuration::from_str("5s").unwrap()
         );
     }
+
+    #[tokio::test]
+    async fn should_create_with_hostname_address() {
+        let config = QuicClientConfig {
+            server_address: "localhost:8080".to_string(),
+            ..Default::default()
+        };
+        let client = QuicClient::create(Arc::new(config));
+        assert!(client.is_ok(), "Expected Ok, got: {:?}", client.err());
+    }
+
+    #[tokio::test]
+    async fn should_create_with_fqdn_address() {
+        let config = QuicClientConfig {
+            server_address: "my-server.example.com:8080".to_string(),
+            ..Default::default()
+        };
+        let client = QuicClient::create(Arc::new(config));
+        assert!(client.is_ok(), "Expected Ok, got: {:?}", client.err());
+    }
+
+    #[tokio::test]
+    async fn should_store_raw_hostname_in_current_server_address() {
+        let hostname = "localhost:8080";
+        let config = QuicClientConfig {
+            server_address: hostname.to_string(),
+            ..Default::default()
+        };
+        let client = QuicClient::create(Arc::new(config)).unwrap();
+        let stored = client.current_server_address.lock().await;
+        assert_eq!(*stored, hostname);
+    }
+
+    #[tokio::test]
+    async fn should_succeed_from_connection_string_with_hostname() {
+        let connection_string = "iggy+quic://user:secret@localhost:1234";
+        let client = QuicClient::from_connection_string(connection_string);
+        assert!(client.is_ok());
+
+        let client = client.unwrap();
+        assert_eq!(client.config.server_address, "localhost:1234");
+    }
 }
diff --git a/core/sdk/src/websocket/websocket_client.rs 
b/core/sdk/src/websocket/websocket_client.rs
index ff89d2a30..c64233daf 100644
--- a/core/sdk/src/websocket/websocket_client.rs
+++ b/core/sdk/src/websocket/websocket_client.rs
@@ -217,10 +217,20 @@ impl WebSocketClient {
                     }
                 }
 
-                let server_addr = 
current_address.parse::<SocketAddr>().map_err(|_| {
-                    error!("Invalid server address: {}", current_address);
-                    IggyError::InvalidConfiguration
-                })?;
+                let server_addr = tokio::net::lookup_host(&*current_address)
+                    .await
+                    .map_err(|e| {
+                        error!(
+                            "Failed to resolve server address '{}': {}",
+                            current_address, e
+                        );
+                        IggyError::InvalidConfiguration
+                    })?
+                    .next()
+                    .ok_or_else(|| {
+                        error!("No addresses resolved for '{}'", 
current_address);
+                        IggyError::InvalidConfiguration
+                    })?;
 
                 let connection_stream = if self.config.tls_enabled {
                     match self.connect_tls(server_addr, &mut 
retry_count).await {
@@ -715,4 +725,14 @@ mod tests {
         let client = 
WebSocketClient::from_connection_string(connection_string);
         assert!(client.is_err());
     }
+
+    #[test]
+    fn should_succeed_from_connection_string_with_hostname() {
+        let connection_string = "iggy+ws://user:secret@localhost:8092";
+        let client = 
WebSocketClient::from_connection_string(connection_string);
+        assert!(client.is_ok());
+
+        let client = client.unwrap();
+        assert_eq!(client.config.server_address, "localhost:8092");
+    }
 }

Reply via email to