Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

Commit 5e0c8d6

Browse files
committed
chore: merge wasip3 in wasmtime_wasi
Signed-off-by: Roman Volosatovs <[email protected]>
1 parent 1d8ed12 commit 5e0c8d6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+4884
-66
lines changed

ci/vendor-wit.sh

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,7 @@ make_vendor "wasi-config" "config@f4d699b"
6363

6464
make_vendor "wasi-keyvalue" "keyvalue@219ea36"
6565

66-
make_vendor "wasi-clocks/src/p3" "clocks@[email protected]"
67-
68-
make_vendor "wasi-random/src/p3" "random@[email protected]"
69-
70-
make_vendor "wasi-sockets/src/p3" "
71-
72-
73-
"
74-
75-
make_vendor "wasi-cli/src/p3" "
66+
make_vendor "wasi/src/p3" "
7667
7768
7869
filesystem@[email protected]
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
use futures::{stream, try_join, SinkExt as _, StreamExt as _};
2+
use test_programs::p3::wasi::sockets::types::{
3+
ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, TcpSocket,
4+
};
5+
use test_programs::p3::{sockets::attempt_random_port, wit_stream};
6+
use wit_bindgen_rt::async_support::StreamReader;
7+
8+
struct Component;
9+
10+
test_programs::p3::export!(Component);
11+
12+
/// Bind a socket and let the system determine a port.
13+
fn test_tcp_bind_ephemeral_port(ip: IpAddress) {
14+
let bind_addr = IpSocketAddress::new(ip, 0);
15+
16+
let sock = TcpSocket::new(ip.family());
17+
sock.bind(bind_addr).unwrap();
18+
19+
let bound_addr = sock.local_address().unwrap();
20+
21+
assert_eq!(bind_addr.ip(), bound_addr.ip());
22+
assert_ne!(bind_addr.port(), bound_addr.port());
23+
}
24+
25+
/// Bind a socket on a specified port.
26+
fn test_tcp_bind_specific_port(ip: IpAddress) {
27+
let sock = TcpSocket::new(ip.family());
28+
29+
let bind_addr = attempt_random_port(ip, |bind_addr| sock.bind(bind_addr)).unwrap();
30+
31+
let bound_addr = sock.local_address().unwrap();
32+
33+
assert_eq!(bind_addr.ip(), bound_addr.ip());
34+
assert_eq!(bind_addr.port(), bound_addr.port());
35+
}
36+
37+
/// Two sockets may not be actively bound to the same address at the same time.
38+
fn test_tcp_bind_addrinuse(ip: IpAddress) {
39+
let bind_addr = IpSocketAddress::new(ip, 0);
40+
41+
let sock1 = TcpSocket::new(ip.family());
42+
sock1.bind(bind_addr).unwrap();
43+
sock1.listen().unwrap();
44+
45+
let bound_addr = sock1.local_address().unwrap();
46+
47+
let sock2 = TcpSocket::new(ip.family());
48+
assert_eq!(sock2.bind(bound_addr), Err(ErrorCode::AddressInUse));
49+
}
50+
51+
// The WASI runtime should set SO_REUSEADDR for us
52+
async fn test_tcp_bind_reuseaddr(ip: IpAddress) {
53+
let client = TcpSocket::new(ip.family());
54+
55+
let bind_addr = {
56+
let listener1 = TcpSocket::new(ip.family());
57+
58+
let bind_addr = attempt_random_port(ip, |bind_addr| listener1.bind(bind_addr)).unwrap();
59+
60+
let mut accept = listener1.listen().unwrap();
61+
62+
let connect_addr =
63+
IpSocketAddress::new(IpAddress::new_loopback(ip.family()), bind_addr.port());
64+
client.connect(connect_addr).unwrap();
65+
66+
let mut sock = accept.next().await.unwrap();
67+
assert_eq!(sock.len(), 1);
68+
let sock = sock.pop().unwrap();
69+
let (mut data_tx, data_rx) = wit_stream::new();
70+
sock.send(data_rx).unwrap();
71+
data_tx.send(vec![0; 10]).await.unwrap();
72+
73+
bind_addr
74+
};
75+
76+
{
77+
let listener2 = TcpSocket::new(ip.family());
78+
79+
// If SO_REUSEADDR was configured correctly, the following lines shouldn't be
80+
// affected by the TIME_WAIT state of the just closed `listener1` socket:
81+
listener2.bind(bind_addr).unwrap();
82+
listener2.listen().unwrap();
83+
}
84+
85+
drop(client);
86+
}
87+
88+
// Try binding to an address that is not configured on the system.
89+
fn test_tcp_bind_addrnotavail(ip: IpAddress) {
90+
let bind_addr = IpSocketAddress::new(ip, 0);
91+
92+
let sock = TcpSocket::new(ip.family());
93+
94+
assert_eq!(sock.bind(bind_addr), Err(ErrorCode::AddressNotBindable));
95+
}
96+
97+
/// Bind should validate the address family.
98+
fn test_tcp_bind_wrong_family(family: IpAddressFamily) {
99+
let wrong_ip = match family {
100+
IpAddressFamily::Ipv4 => IpAddress::IPV6_LOOPBACK,
101+
IpAddressFamily::Ipv6 => IpAddress::IPV4_LOOPBACK,
102+
};
103+
104+
let sock = TcpSocket::new(family);
105+
let result = sock.bind(IpSocketAddress::new(wrong_ip, 0));
106+
107+
assert!(matches!(result, Err(ErrorCode::InvalidArgument)));
108+
}
109+
110+
/// Bind only works on unicast addresses.
111+
fn test_tcp_bind_non_unicast() {
112+
let ipv4_broadcast = IpSocketAddress::new(IpAddress::IPV4_BROADCAST, 0);
113+
let ipv4_multicast = IpSocketAddress::new(IpAddress::Ipv4((224, 254, 0, 0)), 0);
114+
let ipv6_multicast = IpSocketAddress::new(IpAddress::Ipv6((0xff00, 0, 0, 0, 0, 0, 0, 0)), 0);
115+
116+
let sock_v4 = TcpSocket::new(IpAddressFamily::Ipv4);
117+
let sock_v6 = TcpSocket::new(IpAddressFamily::Ipv6);
118+
119+
assert!(matches!(
120+
sock_v4.bind(ipv4_broadcast),
121+
Err(ErrorCode::InvalidArgument)
122+
));
123+
assert!(matches!(
124+
sock_v4.bind(ipv4_multicast),
125+
Err(ErrorCode::InvalidArgument)
126+
));
127+
assert!(matches!(
128+
sock_v6.bind(ipv6_multicast),
129+
Err(ErrorCode::InvalidArgument)
130+
));
131+
}
132+
133+
fn test_tcp_bind_dual_stack() {
134+
let sock = TcpSocket::new(IpAddressFamily::Ipv6);
135+
let addr = IpSocketAddress::new(IpAddress::IPV4_MAPPED_LOOPBACK, 0);
136+
137+
// Binding an IPv4-mapped-IPv6 address on a ipv6-only socket should fail:
138+
assert!(matches!(sock.bind(addr), Err(ErrorCode::InvalidArgument)));
139+
}
140+
141+
impl test_programs::p3::exports::wasi::cli::run::Guest for Component {
142+
async fn run() -> Result<(), ()> {
143+
const RESERVED_IPV4_ADDRESS: IpAddress = IpAddress::Ipv4((192, 0, 2, 0)); // Reserved for documentation and examples.
144+
const RESERVED_IPV6_ADDRESS: IpAddress =
145+
IpAddress::Ipv6((0x2001, 0x0db8, 0, 0, 0, 0, 0, 0)); // Reserved for documentation and examples.
146+
147+
test_tcp_bind_ephemeral_port(IpAddress::IPV4_LOOPBACK);
148+
test_tcp_bind_ephemeral_port(IpAddress::IPV6_LOOPBACK);
149+
test_tcp_bind_ephemeral_port(IpAddress::IPV4_UNSPECIFIED);
150+
test_tcp_bind_ephemeral_port(IpAddress::IPV6_UNSPECIFIED);
151+
152+
test_tcp_bind_specific_port(IpAddress::IPV4_LOOPBACK);
153+
test_tcp_bind_specific_port(IpAddress::IPV6_LOOPBACK);
154+
test_tcp_bind_specific_port(IpAddress::IPV4_UNSPECIFIED);
155+
test_tcp_bind_specific_port(IpAddress::IPV6_UNSPECIFIED);
156+
157+
test_tcp_bind_reuseaddr(IpAddress::IPV4_LOOPBACK).await;
158+
test_tcp_bind_reuseaddr(IpAddress::IPV6_LOOPBACK).await;
159+
160+
test_tcp_bind_addrinuse(IpAddress::IPV4_LOOPBACK);
161+
test_tcp_bind_addrinuse(IpAddress::IPV6_LOOPBACK);
162+
test_tcp_bind_addrinuse(IpAddress::IPV4_UNSPECIFIED);
163+
test_tcp_bind_addrinuse(IpAddress::IPV6_UNSPECIFIED);
164+
165+
test_tcp_bind_addrnotavail(RESERVED_IPV4_ADDRESS);
166+
test_tcp_bind_addrnotavail(RESERVED_IPV6_ADDRESS);
167+
168+
test_tcp_bind_wrong_family(IpAddressFamily::Ipv4);
169+
test_tcp_bind_wrong_family(IpAddressFamily::Ipv6);
170+
171+
test_tcp_bind_non_unicast();
172+
173+
test_tcp_bind_dual_stack();
174+
175+
Ok(())
176+
}
177+
}
178+
179+
fn main() {}

crates/wasi-sockets/src/p3/host/types/tcp.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,13 @@ where
137137
|| !socket_addr_check(remote_address, SocketAddrUse::TcpConnect).await =>
138138
{
139139
if let Some((sock, bound, ..)) = sock {
140-
Ok(Ok((sock, bound, Err(ErrorCode::AccessDenied))))
140+
Ok(Ok(Err((sock, bound, ErrorCode::AccessDenied))))
141141
} else {
142142
Ok(Err(ErrorCode::AccessDenied))
143143
}
144144
}
145-
Ok(Some((sock, bound, family))) => {
146-
let res = connect(&sock, remote_address, family).await;
147-
Ok(Ok((sock, bound, res)))
145+
Ok(Some((sock, .., family))) => {
146+
Ok(Ok(Ok(connect(sock, remote_address, family).await)))
148147
}
149148
Ok(None) => Ok(Err(ErrorCode::InvalidState)),
150149
Err(err) => Err(err),
@@ -156,24 +155,28 @@ where
156155
.table()
157156
.get_mut(&mut socket)
158157
.context("failed to get socket resource from table")?;
159-
let (sock, bound, res) = match sock {
158+
let sock = match sock {
160159
Ok(sock) => sock,
161160
Err(err) => return Ok(Err(err)),
162161
};
163162
ensure!(
164163
matches!(socket.tcp_state, TcpState::Connecting),
165164
"corrupted socket state"
166165
);
167-
match res {
168-
Ok(stream) => {
166+
match sock {
167+
Ok(Ok(stream)) => {
169168
socket.tcp_state = TcpState::Connected(stream);
170169
Ok(Ok(()))
171170
}
172-
Err(err) if bound => {
171+
Ok(Err(err)) => {
172+
socket.tcp_state = TcpState::Closed;
173+
Ok(Err(err))
174+
}
175+
Err((sock, bound, err)) if bound => {
173176
socket.tcp_state = TcpState::Bound(sock);
174177
Ok(Err(err))
175178
}
176-
Err(err) => {
179+
Err((sock, bound, err)) => {
177180
socket.tcp_state = TcpState::Default(sock);
178181
Ok(Err(err))
179182
}

crates/wasi-sockets/tests/p3.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,8 @@ foreach_sockets_0_3!(assert_test_exists);
108108
async fn sockets_0_3_ip_name_lookup() -> anyhow::Result<()> {
109109
run(SOCKETS_0_3_IP_NAME_LOOKUP_COMPONENT).await
110110
}
111+
112+
#[test_log::test(tokio::test(flavor = "multi_thread"))]
113+
async fn sockets_0_3_tcp_bind() -> anyhow::Result<()> {
114+
run(SOCKETS_0_3_TCP_BIND_COMPONENT).await
115+
}

crates/wasi/Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,13 @@ windows-sys = { workspace = true }
5454
rustix = { workspace = true, features = ["event", "net"] }
5555

5656
[features]
57-
default = [ "preview1"]
57+
default = [ "preview1", "p3"]
5858
preview1 = [
5959
"dep:wiggle",
6060
]
61+
p3 = [
62+
"wasmtime/component-model-async",
63+
]
6164

6265
[[test]]
6366
name = "process_stdin"

crates/wasi/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@ mod filesystem;
242242
mod host;
243243
mod ip_name_lookup;
244244
mod network;
245+
#[cfg(feature = "p3")]
246+
pub mod p3;
245247
pub mod pipe;
246248
mod poll;
247249
#[cfg(feature = "preview1")]

0 commit comments

Comments
 (0)