Skip to content

Commit ff24832

Browse files
feat: add TcpStream bind_and_connect (#409)
1 parent 3c16bb8 commit ff24832

3 files changed

Lines changed: 57 additions & 3 deletions

File tree

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
/Cargo.lock
33
/.vscode
44
/.cargo
5-
/.idea
5+
.idea
66
/.direnv
7-
.envrc
7+
.envrc

compio-net/src/tcp.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,22 @@ impl TcpStream {
176176
.await
177177
}
178178

179+
/// Bind to `bind_addr` then opens a TCP connection to a remote host.
180+
pub async fn bind_and_connect(
181+
bind_addr: SocketAddr,
182+
addr: impl ToSocketAddrsAsync,
183+
) -> io::Result<Self> {
184+
super::each_addr(addr, |addr| async move {
185+
let addr = SockAddr::from(addr);
186+
let bind_addr = SockAddr::from(bind_addr);
187+
188+
let socket = Socket::bind(&bind_addr, Type::STREAM, Some(Protocol::TCP)).await?;
189+
socket.connect_async(&addr).await?;
190+
Ok(Self { inner: socket })
191+
})
192+
.await
193+
}
194+
179195
/// Creates new TcpStream from a [`std::net::TcpStream`].
180196
pub fn from_std(stream: std::net::TcpStream) -> io::Result<Self> {
181197
Ok(Self {

compio-net/tests/tcp_connect.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::{
2-
net::{IpAddr, SocketAddr},
2+
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
33
panic::resume_unwind,
44
};
55

@@ -42,6 +42,44 @@ test_connect_ip! {
4242
(connect_v6, "[::1]:0", SocketAddr::is_ipv6),
4343
}
4444

45+
async fn test_bind_and_connect_ip_impl(
46+
bind_addr: SocketAddr,
47+
target: impl ToSocketAddrsAsync,
48+
assert_fn: impl FnOnce(&SocketAddr) -> bool,
49+
) {
50+
let listener = TcpListener::bind(target).await.unwrap();
51+
let addr = listener.local_addr().unwrap();
52+
assert!(assert_fn(&addr));
53+
54+
let task = compio_runtime::spawn(async move {
55+
let (socket, addr) = listener.accept().await.unwrap();
56+
assert_eq!(addr, socket.peer_addr().unwrap());
57+
socket
58+
});
59+
60+
let mine = TcpStream::bind_and_connect(bind_addr, &addr).await.unwrap();
61+
let theirs = task.await.unwrap_or_else(|e| resume_unwind(e));
62+
63+
assert_eq!(mine.local_addr().unwrap(), theirs.peer_addr().unwrap());
64+
assert_eq!(theirs.local_addr().unwrap(), mine.peer_addr().unwrap());
65+
}
66+
67+
macro_rules! test_bind_and_connect_ip {
68+
($(($ident:ident, $bind_addr:expr, $target:expr, $addr_f:path),)*) => {
69+
$(
70+
#[compio_macros::test]
71+
async fn $ident() {
72+
test_bind_and_connect_ip_impl($bind_addr, $target, $addr_f).await;
73+
}
74+
)*
75+
}
76+
}
77+
78+
test_bind_and_connect_ip! {
79+
(bind_and_connect_v4, SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 0), "127.0.0.1:0", SocketAddr::is_ipv4),
80+
(bind_and_connect_v6, SocketAddr::new(Ipv6Addr::LOCALHOST.into(), 0), "[::1]:0", SocketAddr::is_ipv6),
81+
}
82+
4583
async fn test_connect_impl<A: ToSocketAddrsAsync>(mapping: impl FnOnce(&TcpListener) -> A) {
4684
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
4785
let addr = mapping(&listener);

0 commit comments

Comments
 (0)