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

erickguan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opendal.git


The following commit(s) were added to refs/heads/main by this push:
     new 931baacfb fix(services/memcached): TCP address resolution (#7701)
931baacfb is described below

commit 931baacfbb73d50e16961ff8d97c0b688c8d054d
Author: Philipp Krones <[email protected]>
AuthorDate: Mon Jun 22 18:28:44 2026 +0200

    fix(services/memcached): TCP address resolution (#7701)
    
    * [memcached] Fix TCP address resolution
    
    PR #7112 added support for Unix sockets to the memcached backend. As a
    side effect, it switched the TCP address parsing from being done inside
    of `TcpStream::connect` to `SocketAddr::parse`. The major difference is
    that `SocketAddr::parse` cannot resolve addresses like `localhost:1234`,
    as it errors out if it sees non-octal numbers in the address.
    
    This seemed like an unintended breaking change to me. I also can't see
    the benefit of doing that, as `TcpStream::connect` already calls
    `to_socket_addrs` internally.
    
    * Add regression tests
    
    * Update test comments
---
 core/services/memcached/src/core.rs | 64 ++++++++++++++++++++++++++++++++++---
 1 file changed, 59 insertions(+), 5 deletions(-)

diff --git a/core/services/memcached/src/core.rs 
b/core/services/memcached/src/core.rs
index 1221bb08e..a272a2b51 100644
--- a/core/services/memcached/src/core.rs
+++ b/core/services/memcached/src/core.rs
@@ -23,7 +23,6 @@ use fastpool::bounded;
 use opendal_core::raw::*;
 use opendal_core::*;
 use std::io;
-use std::net::SocketAddr;
 use std::pin::Pin;
 use std::task::{Context, Poll};
 use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
@@ -42,10 +41,7 @@ pub enum SocketStream {
 
 impl SocketStream {
     pub async fn connect_tcp(addr_str: &str) -> io::Result<Self> {
-        let socket_addr: SocketAddr = addr_str
-            .parse()
-            .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
-        let stream = TcpStream::connect(socket_addr).await?;
+        let stream = TcpStream::connect(addr_str).await?;
         Ok(SocketStream::Tcp(stream))
     }
 
@@ -223,3 +219,61 @@ impl MemcachedCore {
         conn.delete(&percent_encode_path(key)).await
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use tokio::net::TcpListener;
+
+    // regression test for connecting to a webdav server.
+    // Because setting up a dedicated behavior test is expensive, we choose to 
test `SocketStream::connect_tcp` instead.
+    // In the future, we could set up a webdav server to test TCP connection 
properly.
+    #[tokio::test]
+    async fn connect_tcp_socket() -> std::io::Result<()> {
+        for addr in &["127.0.0.1:11211", "localhost:11211", "[::1]:11211"] {
+            let listener = TcpListener::bind(addr).await?;
+            let addr = listener.local_addr()?.to_string();
+
+            let accepted = tokio::spawn(async move {
+                let _ = listener.accept().await?;
+                Ok::<_, std::io::Error>(())
+            });
+
+            let _stream = SocketStream::connect_tcp(&addr).await?;
+            accepted.await.unwrap()?;
+        }
+
+        Ok(())
+    }
+
+    // regression test for connecting to a webdav server.
+    // Because setting up a dedicated behavior test is expensive, we choose to 
test `SocketStream::connect_unix` instead.
+    // In the future, we could set up a webdav server to test UNIX socket 
connection properly.
+    #[cfg(unix)]
+    #[tokio::test]
+    async fn connect_unix_socket() -> std::io::Result<()> {
+        use std::time::{SystemTime, UNIX_EPOCH};
+        use tokio::net::UnixListener;
+
+        let path = std::env::temp_dir().join(format!(
+            "opendal-memcached-{}.sock",
+            SystemTime::now()
+                .duration_since(UNIX_EPOCH)
+                .unwrap()
+                .as_nanos()
+        ));
+
+        let listener = UnixListener::bind(&path)?;
+
+        let accepted = tokio::spawn(async move {
+            let _ = listener.accept().await?;
+            Ok::<_, std::io::Error>(())
+        });
+
+        let _stream = 
SocketStream::connect_unix(path.to_str().unwrap()).await?;
+        accepted.await.unwrap()?;
+
+        let _ = std::fs::remove_file(path);
+        Ok(())
+    }
+}

Reply via email to