diff --git a/ci/vendor-wit.sh b/ci/vendor-wit.sh index 150b4ee149bf..886401f5924f 100755 --- a/ci/vendor-wit.sh +++ b/ci/vendor-wit.sh @@ -34,7 +34,7 @@ get_github() { } p2=0.2.6 -p3=0.3.0-rc-2026-02-09 +p3=0.3.0-rc-2026-03-15 rm -rf crates/wasi-io/wit/deps mkdir -p crates/wasi-io/wit/deps diff --git a/crates/test-programs/src/bin/p3_cli_many_tasks.rs b/crates/test-programs/src/bin/p3_cli_many_tasks.rs index c2784146cb32..f7b161ac6bb1 100644 --- a/crates/test-programs/src/bin/p3_cli_many_tasks.rs +++ b/crates/test-programs/src/bin/p3_cli_many_tasks.rs @@ -1,6 +1,6 @@ use test_programs::p3::wasi as wasip3; -#[link(wasm_import_module = "wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09")] +#[link(wasm_import_module = "wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15")] unsafe extern "C" { #[link_name = "[async-lower]wait-for"] fn wait_for(dur: u64) -> u32; diff --git a/crates/test-programs/src/bin/p3_http_middleware.rs b/crates/test-programs/src/bin/p3_http_middleware.rs index 1381bc9a8334..54fb11cb9b9f 100644 --- a/crates/test-programs/src/bin/p3_http_middleware.rs +++ b/crates/test-programs/src/bin/p3_http_middleware.rs @@ -18,19 +18,19 @@ wit_bindgen::generate!({ path: "../wasi-http/src/p3/wit", world: "wasi:http/middleware", with: { - "wasi:http/handler@0.3.0-rc-2026-02-09": test_programs::p3::wasi::http::handler, - "wasi:http/types@0.3.0-rc-2026-02-09": test_programs::p3::wasi::http::types, - "wasi:http/client@0.3.0-rc-2026-02-09": test_programs::p3::wasi::http::client, - "wasi:random/random@0.3.0-rc-2026-02-09": test_programs::p3::wasi::random::random, - "wasi:random/insecure@0.3.0-rc-2026-02-09": test_programs::p3::wasi::random::insecure, - "wasi:random/insecure-seed@0.3.0-rc-2026-02-09": test_programs::p3::wasi::random::insecure_seed, - "wasi:cli/stdout@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::stdout, - "wasi:cli/stderr@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::stderr, - "wasi:cli/stdin@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::stdin, - "wasi:cli/types@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::types, - "wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09": test_programs::p3::wasi::clocks::monotonic_clock, - "wasi:clocks/system-clock@0.3.0-rc-2026-02-09": test_programs::p3::wasi::clocks::system_clock, - "wasi:clocks/types@0.3.0-rc-2026-02-09": test_programs::p3::wasi::clocks::types, + "wasi:http/handler@0.3.0-rc-2026-03-15": test_programs::p3::wasi::http::handler, + "wasi:http/types@0.3.0-rc-2026-03-15": test_programs::p3::wasi::http::types, + "wasi:http/client@0.3.0-rc-2026-03-15": test_programs::p3::wasi::http::client, + "wasi:random/random@0.3.0-rc-2026-03-15": test_programs::p3::wasi::random::random, + "wasi:random/insecure@0.3.0-rc-2026-03-15": test_programs::p3::wasi::random::insecure, + "wasi:random/insecure-seed@0.3.0-rc-2026-03-15": test_programs::p3::wasi::random::insecure_seed, + "wasi:cli/stdout@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::stdout, + "wasi:cli/stderr@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::stderr, + "wasi:cli/stdin@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::stdin, + "wasi:cli/types@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::types, + "wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15": test_programs::p3::wasi::clocks::monotonic_clock, + "wasi:clocks/system-clock@0.3.0-rc-2026-03-15": test_programs::p3::wasi::clocks::system_clock, + "wasi:clocks/types@0.3.0-rc-2026-03-15": test_programs::p3::wasi::clocks::types, }, }); diff --git a/crates/test-programs/src/bin/p3_http_middleware_with_chain.rs b/crates/test-programs/src/bin/p3_http_middleware_with_chain.rs index 7b66ac714dc8..cf14e3532b99 100644 --- a/crates/test-programs/src/bin/p3_http_middleware_with_chain.rs +++ b/crates/test-programs/src/bin/p3_http_middleware_with_chain.rs @@ -6,13 +6,13 @@ mod bindings { package local:local; world middleware-with-chain { - include wasi:http/service@0.3.0-rc-2026-02-09; + include wasi:http/service@0.3.0-rc-2026-03-15; import chain-http; } interface chain-http { - use wasi:http/types@0.3.0-rc-2026-02-09.{request, response, error-code}; + use wasi:http/types@0.3.0-rc-2026-03-15.{request, response, error-code}; handle: async func(request: request) -> result; } @@ -20,18 +20,18 @@ interface chain-http { // workaround https://github.com/bytecodealliance/wit-bindgen/issues/1544 // generate_all with: { - "wasi:http/types@0.3.0-rc-2026-02-09": test_programs::p3::wasi::http::types, - "wasi:http/client@0.3.0-rc-2026-02-09": test_programs::p3::wasi::http::client, - "wasi:random/random@0.3.0-rc-2026-02-09": test_programs::p3::wasi::random::random, - "wasi:random/insecure@0.3.0-rc-2026-02-09": test_programs::p3::wasi::random::insecure, - "wasi:random/insecure-seed@0.3.0-rc-2026-02-09": test_programs::p3::wasi::random::insecure_seed, - "wasi:cli/stdout@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::stdout, - "wasi:cli/stderr@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::stderr, - "wasi:cli/stdin@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::stdin, - "wasi:cli/types@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::types, - "wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09": test_programs::p3::wasi::clocks::monotonic_clock, - "wasi:clocks/system-clock@0.3.0-rc-2026-02-09": test_programs::p3::wasi::clocks::system_clock, - "wasi:clocks/types@0.3.0-rc-2026-02-09": test_programs::p3::wasi::clocks::types, + "wasi:http/types@0.3.0-rc-2026-03-15": test_programs::p3::wasi::http::types, + "wasi:http/client@0.3.0-rc-2026-03-15": test_programs::p3::wasi::http::client, + "wasi:random/random@0.3.0-rc-2026-03-15": test_programs::p3::wasi::random::random, + "wasi:random/insecure@0.3.0-rc-2026-03-15": test_programs::p3::wasi::random::insecure, + "wasi:random/insecure-seed@0.3.0-rc-2026-03-15": test_programs::p3::wasi::random::insecure_seed, + "wasi:cli/stdout@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::stdout, + "wasi:cli/stderr@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::stderr, + "wasi:cli/stdin@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::stdin, + "wasi:cli/types@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::types, + "wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15": test_programs::p3::wasi::clocks::monotonic_clock, + "wasi:clocks/system-clock@0.3.0-rc-2026-03-15": test_programs::p3::wasi::clocks::system_clock, + "wasi:clocks/types@0.3.0-rc-2026-03-15": test_programs::p3::wasi::clocks::types, }, }); diff --git a/crates/test-programs/src/bin/p3_readdir.rs b/crates/test-programs/src/bin/p3_readdir.rs index dcb9e759a8d7..44b4ffbd2943 100644 --- a/crates/test-programs/src/bin/p3_readdir.rs +++ b/crates/test-programs/src/bin/p3_readdir.rs @@ -62,9 +62,9 @@ async fn test_readdir(dir: &Descriptor) { let entries = read_dir(dir).await; assert_eq!(entries.len(), 2); assert_eq!(entries[0].name, "file"); - assert_eq!(entries[0].type_, DescriptorType::RegularFile); + assert!(matches!(entries[0].type_, DescriptorType::RegularFile)); assert_eq!(entries[1].name, "nested"); - assert_eq!(entries[1].type_, DescriptorType::Directory); + assert!(matches!(entries[1].type_, DescriptorType::Directory)); assert_empty_dir(&nested).await; drop(nested); diff --git a/crates/test-programs/src/bin/p3_sockets_ip_name_lookup.rs b/crates/test-programs/src/bin/p3_sockets_ip_name_lookup.rs index 94548c2191f1..b8b72a2a434d 100644 --- a/crates/test-programs/src/bin/p3_sockets_ip_name_lookup.rs +++ b/crates/test-programs/src/bin/p3_sockets_ip_name_lookup.rs @@ -69,32 +69,32 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { ); // Invalid inputs - assert_eq!( + assert!(matches!( resolve_addresses("".into()).await.unwrap_err(), ErrorCode::InvalidArgument - ); - assert_eq!( + )); + assert!(matches!( resolve_addresses(" ".into()).await.unwrap_err(), ErrorCode::InvalidArgument - ); - assert_eq!( + )); + assert!(matches!( resolve_addresses("a.b<&>".into()).await.unwrap_err(), ErrorCode::InvalidArgument - ); - assert_eq!( + )); + assert!(matches!( resolve_addresses("127.0.0.1:80".into()).await.unwrap_err(), ErrorCode::InvalidArgument - ); - assert_eq!( + )); + assert!(matches!( resolve_addresses("[::]:80".into()).await.unwrap_err(), ErrorCode::InvalidArgument - ); - assert_eq!( + )); + assert!(matches!( resolve_addresses("http://example.com/".into()) .await .unwrap_err(), ErrorCode::InvalidArgument - ); + )); Ok(()) } } diff --git a/crates/test-programs/src/bin/p3_sockets_tcp_bind.rs b/crates/test-programs/src/bin/p3_sockets_tcp_bind.rs index b7b4cf05f098..276064797a2b 100644 --- a/crates/test-programs/src/bin/p3_sockets_tcp_bind.rs +++ b/crates/test-programs/src/bin/p3_sockets_tcp_bind.rs @@ -46,7 +46,10 @@ fn test_tcp_bind_addrinuse(ip: IpAddress) { let bound_addr = sock1.get_local_address().unwrap(); let sock2 = TcpSocket::create(ip.family()).unwrap(); - assert_eq!(sock2.bind(bound_addr), Err(ErrorCode::AddressInUse)); + assert!(matches!( + sock2.bind(bound_addr), + Err(ErrorCode::AddressInUse) + )); } // The WASI runtime should set SO_REUSEADDR for us @@ -111,7 +114,10 @@ fn test_tcp_bind_addrnotavail(ip: IpAddress) { let sock = TcpSocket::create(ip.family()).unwrap(); - assert_eq!(sock.bind(bind_addr), Err(ErrorCode::AddressNotBindable)); + assert!(matches!( + sock.bind(bind_addr), + Err(ErrorCode::AddressNotBindable) + )); } /// Bind should validate the address family. diff --git a/crates/test-programs/src/bin/p3_sockets_tcp_connect.rs b/crates/test-programs/src/bin/p3_sockets_tcp_connect.rs index cb8d0306e886..960de022d3c4 100644 --- a/crates/test-programs/src/bin/p3_sockets_tcp_connect.rs +++ b/crates/test-programs/src/bin/p3_sockets_tcp_connect.rs @@ -15,7 +15,10 @@ async fn test_tcp_connect_unspec(family: IpAddressFamily) { let addr = IpSocketAddress::new(IpAddress::new_unspecified(family), SOME_PORT); let sock = TcpSocket::create(family).unwrap(); - assert_eq!(sock.connect(addr).await, Err(ErrorCode::InvalidArgument)); + assert!(matches!( + sock.connect(addr).await, + Err(ErrorCode::InvalidArgument) + )); } /// 0 is not a valid remote port. @@ -23,7 +26,10 @@ async fn test_tcp_connect_port_0(family: IpAddressFamily) { let addr = IpSocketAddress::new(IpAddress::new_loopback(family), 0); let sock = TcpSocket::create(family).unwrap(); - assert_eq!(sock.connect(addr).await, Err(ErrorCode::InvalidArgument)); + assert!(matches!( + sock.connect(addr).await, + Err(ErrorCode::InvalidArgument) + )); } /// Connect should validate the address family. @@ -36,10 +42,10 @@ async fn test_tcp_connect_wrong_family(family: IpAddressFamily) { let sock = TcpSocket::create(family).unwrap(); - assert_eq!( + assert!(matches!( sock.connect(remote_addr).await, Err(ErrorCode::InvalidArgument) - ); + )); } /// Can only connect to unicast addresses. @@ -52,18 +58,18 @@ async fn test_tcp_connect_non_unicast() { let sock_v4 = TcpSocket::create(IpAddressFamily::Ipv4).unwrap(); let sock_v6 = TcpSocket::create(IpAddressFamily::Ipv6).unwrap(); - assert_eq!( + assert!(matches!( sock_v4.connect(ipv4_broadcast).await, Err(ErrorCode::InvalidArgument) - ); - assert_eq!( + )); + assert!(matches!( sock_v4.connect(ipv4_multicast).await, Err(ErrorCode::InvalidArgument) - ); - assert_eq!( + )); + assert!(matches!( sock_v6.connect(ipv6_multicast).await, Err(ErrorCode::InvalidArgument) - ); + )); } async fn test_tcp_connect_dual_stack() { @@ -83,15 +89,15 @@ async fn test_tcp_connect_dual_stack() { // Tests: // Connecting to an IPv4 address on an IPv6 socket should fail: - assert_eq!( + assert!(matches!( v6_client.connect(v4_listener_addr).await, Err(ErrorCode::InvalidArgument) - ); + )); // Connecting to an IPv4-mapped-IPv6 address on an IPv6 socket should fail: - assert_eq!( + assert!(matches!( v6_client.connect(v6_listener_addr).await, Err(ErrorCode::InvalidArgument) - ); + )); } /// Client sockets can be explicitly bound. diff --git a/crates/test-programs/src/bin/p3_sockets_tcp_listen.rs b/crates/test-programs/src/bin/p3_sockets_tcp_listen.rs index 3cd22f3ad6bb..a2f61f56098c 100644 --- a/crates/test-programs/src/bin/p3_sockets_tcp_listen.rs +++ b/crates/test-programs/src/bin/p3_sockets_tcp_listen.rs @@ -25,7 +25,7 @@ fn test_tcp_listen_with_bind(family: IpAddressFamily) { let local_addr = sock.get_local_address().unwrap(); assert!(matches!(sock.listen(), Ok(_))); - assert_eq!(sock.get_local_address(), Ok(local_addr)); + assert_eq!(sock.get_local_address().unwrap(), local_addr); } impl test_programs::p3::exports::wasi::cli::run::Guest for Component { diff --git a/crates/test-programs/src/bin/p3_sockets_tcp_states.rs b/crates/test-programs/src/bin/p3_sockets_tcp_states.rs index 97d3d3b10fa5..c99d112b4d9d 100644 --- a/crates/test-programs/src/bin/p3_sockets_tcp_states.rs +++ b/crates/test-programs/src/bin/p3_sockets_tcp_states.rs @@ -16,35 +16,41 @@ fn test_tcp_unbound_state_invariants(family: IpAddressFamily) { // sock.shutdown(ShutdownType::Both), // Err(ErrorCode::InvalidState) //)); - assert_eq!(sock.get_local_address(), Err(ErrorCode::InvalidState)); - assert_eq!(sock.get_remote_address(), Err(ErrorCode::InvalidState)); + assert!(matches!( + sock.get_local_address(), + Err(ErrorCode::InvalidState) + )); + assert!(matches!( + sock.get_remote_address(), + Err(ErrorCode::InvalidState) + )); assert!(!sock.get_is_listening()); assert_eq!(sock.get_address_family(), family); - assert_eq!(sock.set_listen_backlog_size(32), Ok(())); + sock.set_listen_backlog_size(32).unwrap(); assert!(sock.get_keep_alive_enabled().is_ok()); - assert_eq!(sock.set_keep_alive_enabled(false), Ok(())); - assert_eq!(sock.get_keep_alive_enabled(), Ok(false)); + sock.set_keep_alive_enabled(false).unwrap(); + assert_eq!(sock.get_keep_alive_enabled().unwrap(), false); assert!(sock.get_keep_alive_idle_time().is_ok()); - assert_eq!(sock.set_keep_alive_idle_time(1), Ok(())); + sock.set_keep_alive_idle_time(1).unwrap(); assert!(sock.get_keep_alive_interval().is_ok()); - assert_eq!(sock.set_keep_alive_interval(1), Ok(())); + sock.set_keep_alive_interval(1).unwrap(); assert!(sock.get_keep_alive_count().is_ok()); - assert_eq!(sock.set_keep_alive_count(1), Ok(())); + sock.set_keep_alive_count(1).unwrap(); assert!(sock.get_hop_limit().is_ok()); - assert_eq!(sock.set_hop_limit(255), Ok(())); - assert_eq!(sock.get_hop_limit(), Ok(255)); + sock.set_hop_limit(255).unwrap(); + assert_eq!(sock.get_hop_limit().unwrap(), 255); assert!(sock.get_receive_buffer_size().is_ok()); - assert_eq!(sock.set_receive_buffer_size(16000), Ok(())); + sock.set_receive_buffer_size(16000).unwrap(); assert!(sock.get_send_buffer_size().is_ok()); - assert_eq!(sock.set_send_buffer_size(16000), Ok(())); + sock.set_send_buffer_size(16000).unwrap(); } fn test_tcp_bound_state_invariants(family: IpAddressFamily) { @@ -52,7 +58,10 @@ fn test_tcp_bound_state_invariants(family: IpAddressFamily) { let sock = TcpSocket::create(family).unwrap(); sock.bind(bind_address).unwrap(); - assert_eq!(sock.bind(bind_address), Err(ErrorCode::InvalidState)); + assert!(matches!( + sock.bind(bind_address), + Err(ErrorCode::InvalidState) + )); // TODO: Test send and receive //assert!(matches!( // sock.shutdown(ShutdownType::Both), @@ -60,34 +69,37 @@ fn test_tcp_bound_state_invariants(family: IpAddressFamily) { //)); assert!(sock.get_local_address().is_ok()); - assert_eq!(sock.get_remote_address(), Err(ErrorCode::InvalidState)); + assert!(matches!( + sock.get_remote_address(), + Err(ErrorCode::InvalidState) + )); assert!(!sock.get_is_listening()); assert_eq!(sock.get_address_family(), family); - assert_eq!(sock.set_listen_backlog_size(32), Ok(())); + sock.set_listen_backlog_size(32).unwrap(); assert!(sock.get_keep_alive_enabled().is_ok()); - assert_eq!(sock.set_keep_alive_enabled(false), Ok(())); - assert_eq!(sock.get_keep_alive_enabled(), Ok(false)); + sock.set_keep_alive_enabled(false).unwrap(); + assert_eq!(sock.get_keep_alive_enabled().unwrap(), false); assert!(sock.get_keep_alive_idle_time().is_ok()); - assert_eq!(sock.set_keep_alive_idle_time(1), Ok(())); + sock.set_keep_alive_idle_time(1).unwrap(); assert!(sock.get_keep_alive_interval().is_ok()); - assert_eq!(sock.set_keep_alive_interval(1), Ok(())); + sock.set_keep_alive_interval(1).unwrap(); assert!(sock.get_keep_alive_count().is_ok()); - assert_eq!(sock.set_keep_alive_count(1), Ok(())); + sock.set_keep_alive_count(1).unwrap(); assert!(sock.get_hop_limit().is_ok()); - assert_eq!(sock.set_hop_limit(255), Ok(())); - assert_eq!(sock.get_hop_limit(), Ok(255)); + sock.set_hop_limit(255).unwrap(); + assert_eq!(sock.get_hop_limit().unwrap(), 255); assert!(sock.get_receive_buffer_size().is_ok()); - assert_eq!(sock.set_receive_buffer_size(16000), Ok(())); + sock.set_receive_buffer_size(16000).unwrap(); assert!(sock.get_send_buffer_size().is_ok()); - assert_eq!(sock.set_send_buffer_size(16000), Ok(())); + sock.set_send_buffer_size(16000).unwrap(); } async fn test_tcp_listening_state_invariants(family: IpAddressFamily) { @@ -96,12 +108,15 @@ async fn test_tcp_listening_state_invariants(family: IpAddressFamily) { sock.bind(bind_address).unwrap(); sock.listen().unwrap(); - assert_eq!(sock.bind(bind_address), Err(ErrorCode::InvalidState)); - assert_eq!( + assert!(matches!( + sock.bind(bind_address), + Err(ErrorCode::InvalidState) + )); + assert!(matches!( sock.connect(IpSocketAddress::new(IpAddress::new_loopback(family), 1)) .await, Err(ErrorCode::InvalidState) - ); + )); assert!(matches!(sock.listen(), Err(ErrorCode::InvalidState))); // Skipping: tcp::accept // TODO: Test send and receive @@ -111,7 +126,10 @@ async fn test_tcp_listening_state_invariants(family: IpAddressFamily) { //)); assert!(sock.get_local_address().is_ok()); - assert_eq!(sock.get_remote_address(), Err(ErrorCode::InvalidState)); + assert!(matches!( + sock.get_remote_address(), + Err(ErrorCode::InvalidState) + )); assert!(sock.get_is_listening()); assert_eq!(sock.get_address_family(), family); @@ -121,27 +139,27 @@ async fn test_tcp_listening_state_invariants(family: IpAddressFamily) { )); assert!(sock.get_keep_alive_enabled().is_ok()); - assert_eq!(sock.set_keep_alive_enabled(false), Ok(())); - assert_eq!(sock.get_keep_alive_enabled(), Ok(false)); + sock.set_keep_alive_enabled(false).unwrap(); + assert_eq!(sock.get_keep_alive_enabled().unwrap(), false); assert!(sock.get_keep_alive_idle_time().is_ok()); - assert_eq!(sock.set_keep_alive_idle_time(1), Ok(())); + sock.set_keep_alive_idle_time(1).unwrap(); assert!(sock.get_keep_alive_interval().is_ok()); - assert_eq!(sock.set_keep_alive_interval(1), Ok(())); + sock.set_keep_alive_interval(1).unwrap(); assert!(sock.get_keep_alive_count().is_ok()); - assert_eq!(sock.set_keep_alive_count(1), Ok(())); + sock.set_keep_alive_count(1).unwrap(); assert!(sock.get_hop_limit().is_ok()); - assert_eq!(sock.set_hop_limit(255), Ok(())); - assert_eq!(sock.get_hop_limit(), Ok(255)); + sock.set_hop_limit(255).unwrap(); + assert_eq!(sock.get_hop_limit().unwrap(), 255); assert!(sock.get_receive_buffer_size().is_ok()); - assert_eq!(sock.set_receive_buffer_size(16000), Ok(())); + sock.set_receive_buffer_size(16000).unwrap(); assert!(sock.get_send_buffer_size().is_ok()); - assert_eq!(sock.set_send_buffer_size(16000), Ok(())); + sock.set_send_buffer_size(16000).unwrap(); } async fn test_tcp_connected_state_invariants(family: IpAddressFamily) { @@ -160,11 +178,14 @@ async fn test_tcp_connected_state_invariants(family: IpAddressFamily) { } ); - assert_eq!(sock.bind(bind_address), Err(ErrorCode::InvalidState)); - assert_eq!( + assert!(matches!( + sock.bind(bind_address), + Err(ErrorCode::InvalidState) + )); + assert!(matches!( sock.connect(addr_listener).await, Err(ErrorCode::InvalidState) - ); + )); assert!(matches!(sock.listen(), Err(ErrorCode::InvalidState))); // Skipping: tcp::shutdown @@ -174,27 +195,27 @@ async fn test_tcp_connected_state_invariants(family: IpAddressFamily) { assert_eq!(sock.get_address_family(), family); assert!(sock.get_keep_alive_enabled().is_ok()); - assert_eq!(sock.set_keep_alive_enabled(false), Ok(())); - assert_eq!(sock.get_keep_alive_enabled(), Ok(false)); + sock.set_keep_alive_enabled(false).unwrap(); + assert_eq!(sock.get_keep_alive_enabled().unwrap(), false); assert!(sock.get_keep_alive_idle_time().is_ok()); - assert_eq!(sock.set_keep_alive_idle_time(1), Ok(())); + sock.set_keep_alive_idle_time(1).unwrap(); assert!(sock.get_keep_alive_interval().is_ok()); - assert_eq!(sock.set_keep_alive_interval(1), Ok(())); + sock.set_keep_alive_interval(1).unwrap(); assert!(sock.get_keep_alive_count().is_ok()); - assert_eq!(sock.set_keep_alive_count(1), Ok(())); + sock.set_keep_alive_count(1).unwrap(); assert!(sock.get_hop_limit().is_ok()); - assert_eq!(sock.set_hop_limit(255), Ok(())); - assert_eq!(sock.get_hop_limit(), Ok(255)); + sock.set_hop_limit(255).unwrap(); + assert_eq!(sock.get_hop_limit().unwrap(), 255); assert!(sock.get_receive_buffer_size().is_ok()); - assert_eq!(sock.set_receive_buffer_size(16000), Ok(())); + sock.set_receive_buffer_size(16000).unwrap(); assert!(sock.get_send_buffer_size().is_ok()); - assert_eq!(sock.set_send_buffer_size(16000), Ok(())); + sock.set_send_buffer_size(16000).unwrap(); } impl test_programs::p3::exports::wasi::cli::run::Guest for Component { diff --git a/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs b/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs index 9b6b40157572..dc9f30c9de54 100644 --- a/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs +++ b/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs @@ -47,7 +47,7 @@ async fn test_tcp_receive_stream_should_be_dropped_by_remote_shutdown(family: Ip let (stream_result, data) = client.receive_stream.read(Vec::with_capacity(1)).await; assert_eq!(data.len(), 0); assert_eq!(stream_result, StreamResult::Dropped); - assert_eq!(client.receive_result.await, Ok(())); + client.receive_result.await.unwrap(); }) .await; } @@ -69,7 +69,7 @@ async fn test_tcp_receive_future_should_resolve_when_stream_dropped(family: IpAd // Dropping the stream should've caused the future to resolve even // though there was still data pending: - assert_eq!(receive_result.await, Ok(())); + receive_result.await.unwrap(); } }).await; } @@ -83,7 +83,7 @@ async fn test_tcp_send_future_should_resolve_when_stream_dropped(family: IpAddre .. } = client; drop(send_stream); - assert_eq!(send_result.await, Ok(())); + send_result.await.unwrap(); }) .await; } @@ -123,7 +123,7 @@ async fn test_tcp_receive_once(family: IpAddressFamily) { let (stream_result, data) = reader.read(Vec::with_capacity(10)).await; assert_eq!(data.len(), 0); assert_eq!(stream_result, StreamResult::Dropped); - assert_eq!(future.await, Err(ErrorCode::InvalidState)); + assert!(matches!(future.await, Err(ErrorCode::InvalidState))); } }) .await; @@ -142,7 +142,7 @@ async fn test_tcp_send_once(family: IpAddressFamily) { let (stream_result, rest) = writer.write(DATA.into()).await; assert_eq!(rest.into_vec(), DATA); assert_eq!(stream_result, StreamResult::Dropped); - assert_eq!(future.await, Err(ErrorCode::InvalidState)); + assert!(matches!(future.await, Err(ErrorCode::InvalidState))); } }) .await; @@ -175,23 +175,23 @@ async fn test_tcp_stream_lifetimes(family: IpAddressFamily) { let rest = server_send_stream.write_all(b"ping".into()).await; assert!(rest.is_empty()); drop(server_send_stream); - assert_eq!(server_send_result.await, Ok(())); + server_send_result.await.unwrap(); } { let data = client_receive_stream.collect().await; assert_eq!(data, b"ping"); - assert_eq!(client_receive_result.await, Ok(())); + client_receive_result.await.unwrap(); } { let rest = client_send_stream.write_all(b"pong".into()).await; assert!(rest.is_empty()); drop(client_send_stream); - assert_eq!(client_send_result.await, Ok(())); + client_send_result.await.unwrap(); } { let data = server_receive_stream.collect().await; assert_eq!(data, b"pong"); - assert_eq!(server_receive_result.await, Ok(())); + server_receive_result.await.unwrap(); } }) .await; diff --git a/crates/test-programs/src/bin/p3_sockets_udp_connect.rs b/crates/test-programs/src/bin/p3_sockets_udp_connect.rs index 723068cb1c19..11273c230af5 100644 --- a/crates/test-programs/src/bin/p3_sockets_udp_connect.rs +++ b/crates/test-programs/src/bin/p3_sockets_udp_connect.rs @@ -20,26 +20,35 @@ fn test_udp_connect_disconnect_reconnect(family: IpAddressFamily) { let client = UdpSocket::create(family).unwrap(); - assert_eq!(client.disconnect(), Err(ErrorCode::InvalidState)); - assert_eq!(client.get_remote_address(), Err(ErrorCode::InvalidState)); + assert!(matches!(client.disconnect(), Err(ErrorCode::InvalidState))); + assert!(matches!( + client.get_remote_address(), + Err(ErrorCode::InvalidState) + )); - assert_eq!(client.disconnect(), Err(ErrorCode::InvalidState)); - assert_eq!(client.get_remote_address(), Err(ErrorCode::InvalidState)); + assert!(matches!(client.disconnect(), Err(ErrorCode::InvalidState))); + assert!(matches!( + client.get_remote_address(), + Err(ErrorCode::InvalidState) + )); _ = client.connect(remote1).unwrap(); - assert_eq!(client.get_remote_address(), Ok(remote1)); + assert_eq!(client.get_remote_address().unwrap(), remote1); _ = client.connect(remote1).unwrap(); - assert_eq!(client.get_remote_address(), Ok(remote1)); + assert_eq!(client.get_remote_address().unwrap(), remote1); _ = client.connect(remote2).unwrap(); - assert_eq!(client.get_remote_address(), Ok(remote2)); + assert_eq!(client.get_remote_address().unwrap(), remote2); _ = client.disconnect().unwrap(); - assert_eq!(client.get_remote_address(), Err(ErrorCode::InvalidState)); + assert!(matches!( + client.get_remote_address(), + Err(ErrorCode::InvalidState) + )); _ = client.connect(remote1).unwrap(); - assert_eq!(client.get_remote_address(), Ok(remote1)); + assert_eq!(client.get_remote_address().unwrap(), remote1); } /// `0.0.0.0` / `::` is not a valid remote address in WASI. @@ -175,9 +184,9 @@ async fn test_udp_connect_and_send(family: IpAddressFamily) { client.bind(unspecified_port).unwrap(); client.connect(remote).unwrap(); - assert_eq!(client.get_remote_address(), Ok(remote)); + assert_eq!(client.get_remote_address().unwrap(), remote); - assert_eq!(client.send(b"hello".into(), None).await, Ok(())); + client.send(b"hello".into(), None).await.unwrap(); } impl test_programs::p3::exports::wasi::cli::run::Guest for Component { diff --git a/crates/test-programs/src/bin/p3_sockets_udp_states.rs b/crates/test-programs/src/bin/p3_sockets_udp_states.rs index 370acda8d738..13afa925cb87 100644 --- a/crates/test-programs/src/bin/p3_sockets_udp_states.rs +++ b/crates/test-programs/src/bin/p3_sockets_udp_states.rs @@ -12,13 +12,19 @@ async fn test_udp_unbound_state_invariants(family: IpAddressFamily) { // Skipping: udp::bind - assert_eq!( + assert!(matches!( sock.send(b"test".into(), None).await, Err(ErrorCode::InvalidArgument) - ); - assert_eq!(sock.disconnect(), Err(ErrorCode::InvalidState)); - assert_eq!(sock.get_local_address(), Err(ErrorCode::InvalidState)); - assert_eq!(sock.get_remote_address(), Err(ErrorCode::InvalidState)); + )); + assert!(matches!(sock.disconnect(), Err(ErrorCode::InvalidState))); + assert!(matches!( + sock.get_local_address(), + Err(ErrorCode::InvalidState) + )); + assert!(matches!( + sock.get_remote_address(), + Err(ErrorCode::InvalidState) + )); assert_eq!(sock.get_address_family(), family); assert!(matches!(sock.get_unicast_hop_limit(), Ok(_))); diff --git a/crates/test-programs/src/p3/mod.rs b/crates/test-programs/src/p3/mod.rs index d5dea4f18439..edb65c425acf 100644 --- a/crates/test-programs/src/p3/mod.rs +++ b/crates/test-programs/src/p3/mod.rs @@ -6,12 +6,12 @@ wit_bindgen::generate!({ package wasmtime:test; world testp3 { - include wasi:cli/imports@0.3.0-rc-2026-02-09; - import wasi:http/types@0.3.0-rc-2026-02-09; - import wasi:http/client@0.3.0-rc-2026-02-09; - import wasi:http/handler@0.3.0-rc-2026-02-09; + include wasi:cli/imports@0.3.0-rc-2026-03-15; + import wasi:http/types@0.3.0-rc-2026-03-15; + import wasi:http/client@0.3.0-rc-2026-03-15; + import wasi:http/handler@0.3.0-rc-2026-03-15; - export wasi:cli/run@0.3.0-rc-2026-02-09; + export wasi:cli/run@0.3.0-rc-2026-03-15; } ", path: "../wasi-http/src/p3/wit", @@ -28,19 +28,19 @@ pub mod service { default_bindings_module: "test_programs::p3::service", pub_export_macro: true, with: { - "wasi:http/handler@0.3.0-rc-2026-02-09": crate::p3::wasi::http::handler, - "wasi:http/types@0.3.0-rc-2026-02-09": crate::p3::wasi::http::types, - "wasi:http/client@0.3.0-rc-2026-02-09": crate::p3::wasi::http::client, - "wasi:random/random@0.3.0-rc-2026-02-09": crate::p3::wasi::random::random, - "wasi:random/insecure@0.3.0-rc-2026-02-09": crate::p3::wasi::random::insecure, - "wasi:random/insecure-seed@0.3.0-rc-2026-02-09": crate::p3::wasi::random::insecure_seed, - "wasi:cli/stdout@0.3.0-rc-2026-02-09": crate::p3::wasi::cli::stdout, - "wasi:cli/stderr@0.3.0-rc-2026-02-09": crate::p3::wasi::cli::stderr, - "wasi:cli/stdin@0.3.0-rc-2026-02-09": crate::p3::wasi::cli::stdin, - "wasi:cli/types@0.3.0-rc-2026-02-09": crate::p3::wasi::cli::types, - "wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09": crate::p3::wasi::clocks::monotonic_clock, - "wasi:clocks/system-clock@0.3.0-rc-2026-02-09": crate::p3::wasi::clocks::system_clock, - "wasi:clocks/types@0.3.0-rc-2026-02-09": crate::p3::wasi::clocks::types, + "wasi:http/handler@0.3.0-rc-2026-03-15": crate::p3::wasi::http::handler, + "wasi:http/types@0.3.0-rc-2026-03-15": crate::p3::wasi::http::types, + "wasi:http/client@0.3.0-rc-2026-03-15": crate::p3::wasi::http::client, + "wasi:random/random@0.3.0-rc-2026-03-15": crate::p3::wasi::random::random, + "wasi:random/insecure@0.3.0-rc-2026-03-15": crate::p3::wasi::random::insecure, + "wasi:random/insecure-seed@0.3.0-rc-2026-03-15": crate::p3::wasi::random::insecure_seed, + "wasi:cli/stdout@0.3.0-rc-2026-03-15": crate::p3::wasi::cli::stdout, + "wasi:cli/stderr@0.3.0-rc-2026-03-15": crate::p3::wasi::cli::stderr, + "wasi:cli/stdin@0.3.0-rc-2026-03-15": crate::p3::wasi::cli::stdin, + "wasi:cli/types@0.3.0-rc-2026-03-15": crate::p3::wasi::cli::types, + "wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15": crate::p3::wasi::clocks::monotonic_clock, + "wasi:clocks/system-clock@0.3.0-rc-2026-03-15": crate::p3::wasi::clocks::system_clock, + "wasi:clocks/types@0.3.0-rc-2026-03-15": crate::p3::wasi::clocks::types, }, }); } diff --git a/crates/wasi-http/src/p3/wit/deps/cli.wit b/crates/wasi-http/src/p3/wit/deps/cli.wit index 4f8bbf8a24a5..8ba52c5cd594 100644 --- a/crates/wasi-http/src/p3/wit/deps/cli.wit +++ b/crates/wasi-http/src/p3/wit/deps/cli.wit @@ -1,6 +1,6 @@ -package wasi:cli@0.3.0-rc-2026-02-09; +package wasi:cli@0.3.0-rc-2026-03-15; -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface environment { /// Get the POSIX-style environment variables. /// @@ -10,23 +10,23 @@ interface environment { /// Morally, these are a value import, but until value imports are available /// in the component model, this import function should return the same /// values each time it is called. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-environment: func() -> list>; /// Get the POSIX-style arguments to the program. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-arguments: func() -> list; /// Return a path that programs should use as their initial current working /// directory, interpreting `.` as shorthand for this. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-initial-cwd: func() -> option; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface exit { /// Exit the current instance and any linked instances. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) exit: func(status: result); /// Exit the current instance and any linked instances, reporting the @@ -41,16 +41,16 @@ interface exit { exit-with-code: func(status-code: u8); } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface run { /// Run the program. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) run: async func() -> result; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface types { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) enum error-code { /// Input/output error io, @@ -61,7 +61,7 @@ interface types { } } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface stdin { use types.{error-code}; @@ -78,11 +78,11 @@ interface stdin { /// /// Multiple streams may be active at the same time. The behavior of concurrent /// reads is implementation-specific. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) read-via-stream: func() -> tuple, future>>; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface stdout { use types.{error-code}; @@ -94,11 +94,11 @@ interface stdout { /// /// Otherwise if there is an error the readable end of the stream will be /// dropped and this function will return an error-code. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) write-via-stream: func(data: stream) -> future>; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface stderr { use types.{error-code}; @@ -110,7 +110,7 @@ interface stderr { /// /// Otherwise if there is an error the readable end of the stream will be /// dropped and this function will return an error-code. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) write-via-stream: func(data: stream) -> future>; } @@ -119,10 +119,10 @@ interface stderr { /// In the future, this may include functions for disabling echoing, /// disabling input buffering so that keyboard events are sent through /// immediately, querying supported features, and so on. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-input { /// The input side of a terminal. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource terminal-input; } @@ -131,126 +131,126 @@ interface terminal-input { /// In the future, this may include functions for querying the terminal /// size, being notified of terminal size changes, querying supported /// features, and so on. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-output { /// The output side of a terminal. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource terminal-output; } /// An interface providing an optional `terminal-input` for stdin as a /// link-time authority. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-stdin { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use terminal-input.{terminal-input}; /// If stdin is connected to a terminal, return a `terminal-input` handle /// allowing further interaction with it. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-terminal-stdin: func() -> option; } /// An interface providing an optional `terminal-output` for stdout as a /// link-time authority. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-stdout { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use terminal-output.{terminal-output}; /// If stdout is connected to a terminal, return a `terminal-output` handle /// allowing further interaction with it. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-terminal-stdout: func() -> option; } /// An interface providing an optional `terminal-output` for stderr as a /// link-time authority. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-stderr { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use terminal-output.{terminal-output}; /// If stderr is connected to a terminal, return a `terminal-output` handle /// allowing further interaction with it. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-terminal-stderr: func() -> option; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import environment; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import exit; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stdin; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stdout; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stderr; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-input; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-output; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stdin; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stdout; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stderr; - import wasi:clocks/types@0.3.0-rc-2026-02-09; - import wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09; - import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; + import wasi:clocks/types@0.3.0-rc-2026-03-15; + import wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15; + import wasi:clocks/system-clock@0.3.0-rc-2026-03-15; @unstable(feature = clocks-timezone) - import wasi:clocks/timezone@0.3.0-rc-2026-02-09; - import wasi:filesystem/types@0.3.0-rc-2026-02-09; - import wasi:filesystem/preopens@0.3.0-rc-2026-02-09; - import wasi:sockets/types@0.3.0-rc-2026-02-09; - import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-02-09; - import wasi:random/random@0.3.0-rc-2026-02-09; - import wasi:random/insecure@0.3.0-rc-2026-02-09; - import wasi:random/insecure-seed@0.3.0-rc-2026-02-09; + import wasi:clocks/timezone@0.3.0-rc-2026-03-15; + import wasi:filesystem/types@0.3.0-rc-2026-03-15; + import wasi:filesystem/preopens@0.3.0-rc-2026-03-15; + import wasi:sockets/types@0.3.0-rc-2026-03-15; + import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-03-15; + import wasi:random/random@0.3.0-rc-2026-03-15; + import wasi:random/insecure@0.3.0-rc-2026-03-15; + import wasi:random/insecure-seed@0.3.0-rc-2026-03-15; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world command { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import environment; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import exit; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stdin; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stdout; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stderr; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-input; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-output; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stdin; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stdout; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stderr; - import wasi:clocks/types@0.3.0-rc-2026-02-09; - import wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09; - import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; + import wasi:clocks/types@0.3.0-rc-2026-03-15; + import wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15; + import wasi:clocks/system-clock@0.3.0-rc-2026-03-15; @unstable(feature = clocks-timezone) - import wasi:clocks/timezone@0.3.0-rc-2026-02-09; - import wasi:filesystem/types@0.3.0-rc-2026-02-09; - import wasi:filesystem/preopens@0.3.0-rc-2026-02-09; - import wasi:sockets/types@0.3.0-rc-2026-02-09; - import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-02-09; - import wasi:random/random@0.3.0-rc-2026-02-09; - import wasi:random/insecure@0.3.0-rc-2026-02-09; - import wasi:random/insecure-seed@0.3.0-rc-2026-02-09; + import wasi:clocks/timezone@0.3.0-rc-2026-03-15; + import wasi:filesystem/types@0.3.0-rc-2026-03-15; + import wasi:filesystem/preopens@0.3.0-rc-2026-03-15; + import wasi:sockets/types@0.3.0-rc-2026-03-15; + import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-03-15; + import wasi:random/random@0.3.0-rc-2026-03-15; + import wasi:random/insecure@0.3.0-rc-2026-03-15; + import wasi:random/insecure-seed@0.3.0-rc-2026-03-15; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) export run; } diff --git a/crates/wasi-http/src/p3/wit/deps/clocks.wit b/crates/wasi-http/src/p3/wit/deps/clocks.wit index 71986bab2bcc..19fc4bcd59e9 100644 --- a/crates/wasi-http/src/p3/wit/deps/clocks.wit +++ b/crates/wasi-http/src/p3/wit/deps/clocks.wit @@ -1,10 +1,10 @@ -package wasi:clocks@0.3.0-rc-2026-02-09; +package wasi:clocks@0.3.0-rc-2026-03-15; /// This interface common types used throughout wasi:clocks. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface types { /// A duration of time, in nanoseconds. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type duration = u64; } @@ -16,14 +16,14 @@ interface types { /// /// A monotonic clock is a clock which has an unspecified initial value, and /// successive reads of the clock will produce non-decreasing values. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface monotonic-clock { use types.{duration}; /// A mark on a monotonic clock is a number of nanoseconds since an /// unspecified initial value, and can only be compared to instances from /// the same monotonic-clock. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type mark = u64; /// Read the current value of the clock. @@ -35,20 +35,20 @@ interface monotonic-clock { /// the value of the clock in a `mark`. Consequently, implementations /// should ensure that the starting time is low enough to avoid the /// possibility of overflow in practice. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) now: func() -> mark; /// Query the resolution of the clock. Returns the duration of time /// corresponding to a clock tick. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-resolution: func() -> duration; /// Wait until the specified mark has occurred. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) wait-until: async func(when: mark); /// Wait for the specified duration to elapse. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) wait-for: async func(how-long: duration); } @@ -62,7 +62,7 @@ interface monotonic-clock { /// monotonic, making it unsuitable for measuring elapsed time. /// /// It is intended for reporting the current date and time for humans. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface system-clock { use types.{duration}; @@ -82,7 +82,7 @@ interface system-clock { /// /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record instant { seconds: s64, nanoseconds: u32, @@ -94,12 +94,12 @@ interface system-clock { /// will not necessarily produce a sequence of non-decreasing values. /// /// The nanoseconds field of the output is always less than 1000000000. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) now: func() -> instant; /// Query the resolution of the clock. Returns the smallest duration of time /// that the implementation permits distinguishing. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-resolution: func() -> duration; } @@ -148,13 +148,13 @@ interface timezone { to-debug-string: func() -> string; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import monotonic-clock; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import system-clock; @unstable(feature = clocks-timezone) import timezone; diff --git a/crates/wasi-http/src/p3/wit/deps/filesystem.wit b/crates/wasi-http/src/p3/wit/deps/filesystem.wit index 8fb83041a103..697681f30c5e 100644 --- a/crates/wasi-http/src/p3/wit/deps/filesystem.wit +++ b/crates/wasi-http/src/p3/wit/deps/filesystem.wit @@ -1,4 +1,4 @@ -package wasi:filesystem@0.3.0-rc-2026-02-09; +package wasi:filesystem@0.3.0-rc-2026-03-15; /// WASI filesystem is a filesystem API primarily intended to let users run WASI /// programs that access their files on their existing filesystems, without @@ -35,23 +35,20 @@ package wasi:filesystem@0.3.0-rc-2026-02-09; /// store or a database instead. /// /// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface types { - @since(version = 0.3.0-rc-2026-02-09) - use wasi:clocks/system-clock@0.3.0-rc-2026-02-09.{instant}; + @since(version = 0.3.0-rc-2026-03-15) + use wasi:clocks/system-clock@0.3.0-rc-2026-03-15.{instant}; /// File size or length of a region within a file. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type filesize = u64; /// The type of a filesystem object referenced by a descriptor. /// /// Note: This was called `filetype` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) - enum descriptor-type { - /// The type of the descriptor or file is unknown or is different from - /// any of the other types specified. - unknown, + @since(version = 0.3.0-rc-2026-03-15) + variant descriptor-type { /// The descriptor refers to a block device inode. block-device, /// The descriptor refers to a character device inode. @@ -66,12 +63,15 @@ interface types { regular-file, /// The descriptor refers to a socket. socket, + /// The type of the descriptor or file is different from any of the + /// other types specified. + other(option), } /// Descriptor flags. /// /// Note: This was called `fdflags` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) flags descriptor-flags { /// Read mode: Data can be read. read, @@ -113,7 +113,7 @@ interface types { } /// Flags determining the method of how paths are resolved. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) flags path-flags { /// As long as the resolved path corresponds to a symbolic link, it is /// expanded. @@ -121,7 +121,7 @@ interface types { } /// Open flags used by `open-at`. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) flags open-flags { /// Create file if it does not exist, similar to `O_CREAT` in POSIX. create, @@ -134,13 +134,13 @@ interface types { } /// Number of hard links to an inode. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type link-count = u64; /// File attributes. /// /// Note: This was called `filestat` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record descriptor-stat { /// File type. %type: descriptor-type, @@ -167,7 +167,7 @@ interface types { } /// When setting a timestamp, this gives the value to set it to. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant new-timestamp { /// Leave the timestamp set to its previous value. no-change, @@ -179,7 +179,7 @@ interface types { } /// A directory entry. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record directory-entry { /// The type of the file referred to by this directory entry. %type: descriptor-type, @@ -191,8 +191,8 @@ interface types { /// Not all of these error codes are returned by the functions provided by this /// API; some are used in higher-level library layers, and others are provided /// merely for alignment with POSIX. - @since(version = 0.3.0-rc-2026-02-09) - enum error-code { + @since(version = 0.3.0-rc-2026-03-15) + variant error-code { /// Permission denied, similar to `EACCES` in POSIX. access, /// Connection already in progress, similar to `EALREADY` in POSIX. @@ -265,10 +265,14 @@ interface types { text-file-busy, /// Cross-device link, similar to `EXDEV` in POSIX. cross-device, + /// A catch-all for errors not captured by the existing variants. + /// Implementations can use this to extend the error type without + /// breaking existing code. + other(option), } /// File or memory access pattern advisory information. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) enum advice { /// The application has no advice to give on its behavior with respect /// to the specified data. @@ -292,7 +296,7 @@ interface types { /// A 128-bit hash value, split into parts because wasm doesn't have a /// 128-bit integer type. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record metadata-hash-value { /// 64 bits of a 128-bit hash value. lower: u64, @@ -303,7 +307,7 @@ interface types { /// A descriptor is a reference to a filesystem object, which may be a file, /// directory, named pipe, special file, or other object on which filesystem /// calls may be made. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource descriptor { /// Return a stream for reading from a file. /// @@ -321,7 +325,7 @@ interface types { /// resolves to `err` with an `error-code`. /// /// Note: This is similar to `pread` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) read-via-stream: func(offset: filesize) -> tuple, future>>; /// Return a stream for writing to a file, if available. /// @@ -335,7 +339,7 @@ interface types { /// written or an error is encountered. /// /// Note: This is similar to `pwrite` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) write-via-stream: func(data: stream, offset: filesize) -> future>; /// Return a stream for appending to a file, if available. /// @@ -345,12 +349,12 @@ interface types { /// written or an error is encountered. /// /// Note: This is similar to `write` with `O_APPEND` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) append-via-stream: func(data: stream) -> future>; /// Provide file advisory information on a descriptor. /// /// This is similar to `posix_fadvise` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) advise: async func(offset: filesize, length: filesize, advice: advice) -> result<_, error-code>; /// Synchronize the data of a file to disk. /// @@ -358,7 +362,7 @@ interface types { /// opened for writing. /// /// Note: This is similar to `fdatasync` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) sync-data: async func() -> result<_, error-code>; /// Get flags associated with a descriptor. /// @@ -366,7 +370,7 @@ interface types { /// /// Note: This returns the value that was the `fs_flags` value returned /// from `fdstat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-flags: async func() -> result; /// Get the dynamic type of a descriptor. /// @@ -378,20 +382,20 @@ interface types { /// /// Note: This returns the value that was the `fs_filetype` value returned /// from `fdstat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-type: async func() -> result; /// Adjust the size of an open file. If this increases the file's size, the /// extra bytes are filled with zeros. /// /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-size: async func(size: filesize) -> result<_, error-code>; /// Adjust the timestamps of an open file or directory. /// /// Note: This is similar to `futimens` in POSIX. /// /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-times: async func(data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; /// Read directory entries from a directory. /// @@ -405,7 +409,7 @@ interface types { /// /// This function returns a future, which will resolve to an error code if /// reading full contents of the directory fails. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) read-directory: func() -> tuple, future>>; /// Synchronize the data and metadata of a file to disk. /// @@ -413,12 +417,12 @@ interface types { /// opened for writing. /// /// Note: This is similar to `fsync` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) sync: async func() -> result<_, error-code>; /// Create a directory. /// /// Note: This is similar to `mkdirat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) create-directory-at: async func(path: string) -> result<_, error-code>; /// Return the attributes of an open file or directory. /// @@ -429,7 +433,7 @@ interface types { /// modified, use `metadata-hash`. /// /// Note: This was called `fd_filestat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) stat: async func() -> result; /// Return the attributes of a file or directory. /// @@ -438,7 +442,7 @@ interface types { /// discussion of alternatives. /// /// Note: This was called `path_filestat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) stat-at: async func(path-flags: path-flags, path: string) -> result; /// Adjust the timestamps of a file or directory. /// @@ -446,7 +450,7 @@ interface types { /// /// Note: This was called `path_filestat_set_times` in earlier versions of /// WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-times-at: async func(path-flags: path-flags, path: string, data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; /// Create a hard link. /// @@ -455,7 +459,7 @@ interface types { /// `error-code::not-permitted` if the old path is not a file. /// /// Note: This is similar to `linkat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) link-at: async func(old-path-flags: path-flags, old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; /// Open a file or directory. /// @@ -469,7 +473,7 @@ interface types { /// `error-code::read-only`. /// /// Note: This is similar to `openat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) open-at: async func(path-flags: path-flags, path: string, open-flags: open-flags, %flags: descriptor-flags) -> result; /// Read the contents of a symbolic link. /// @@ -477,19 +481,19 @@ interface types { /// filesystem, this function fails with `error-code::not-permitted`. /// /// Note: This is similar to `readlinkat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) readlink-at: async func(path: string) -> result; /// Remove a directory. /// /// Return `error-code::not-empty` if the directory is not empty. /// /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) remove-directory-at: async func(path: string) -> result<_, error-code>; /// Rename a filesystem object. /// /// Note: This is similar to `renameat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) rename-at: async func(old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; /// Create a symbolic link (also known as a "symlink"). /// @@ -497,13 +501,18 @@ interface types { /// `error-code::not-permitted`. /// /// Note: This is similar to `symlinkat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) symlink-at: async func(old-path: string, new-path: string) -> result<_, error-code>; /// Unlink a filesystem object that is not a directory. /// - /// Return `error-code::is-directory` if the path refers to a directory. - /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + /// This is similar to `unlinkat(fd, path, 0)` in POSIX. + /// + /// Error returns are as specified by POSIX. + /// + /// If the filesystem object is a directory, `error-code::access` or + /// `error-code::is-directory` may be returned instead of the + /// POSIX-specified `error-code::not-permitted`. + @since(version = 0.3.0-rc-2026-03-15) unlink-file-at: async func(path: string) -> result<_, error-code>; /// Test whether two descriptors refer to the same filesystem object. /// @@ -511,7 +520,7 @@ interface types { /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. /// wasi-filesystem does not expose device and inode numbers, so this function /// may be used instead. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) is-same-object: async func(other: borrow) -> bool; /// Return a hash of the metadata associated with a filesystem object referred /// to by a descriptor. @@ -532,35 +541,35 @@ interface types { /// computed hash. /// /// However, none of these is required. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) metadata-hash: async func() -> result; /// Return a hash of the metadata associated with a filesystem object referred /// to by a directory descriptor and a relative path. /// /// This performs the same hash computation as `metadata-hash`. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) metadata-hash-at: async func(path-flags: path-flags, path: string) -> result; } } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface preopens { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use types.{descriptor}; /// Return the set of preopened directories, and their paths. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-directories: func() -> list>; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) - import wasi:clocks/types@0.3.0-rc-2026-02-09; - @since(version = 0.3.0-rc-2026-02-09) - import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) + import wasi:clocks/types@0.3.0-rc-2026-03-15; + @since(version = 0.3.0-rc-2026-03-15) + import wasi:clocks/system-clock@0.3.0-rc-2026-03-15; + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import preopens; } diff --git a/crates/wasi-http/src/p3/wit/deps/http.wit b/crates/wasi-http/src/p3/wit/deps/http.wit index 442b18ea129f..c1c1e68e75ba 100644 --- a/crates/wasi-http/src/p3/wit/deps/http.wit +++ b/crates/wasi-http/src/p3/wit/deps/http.wit @@ -1,13 +1,13 @@ -package wasi:http@0.3.0-rc-2026-02-09; +package wasi:http@0.3.0-rc-2026-03-15; /// This interface defines all of the types and methods for implementing HTTP /// Requests and Responses, as well as their headers, trailers, and bodies. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface types { - use wasi:clocks/types@0.3.0-rc-2026-02-09.{duration}; + use wasi:clocks/types@0.3.0-rc-2026-03-15.{duration}; /// This type corresponds to HTTP standard Methods. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant method { get, head, @@ -22,7 +22,7 @@ interface types { } /// This type corresponds to HTTP standard Related Schemes. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant scheme { HTTP, HTTPS, @@ -30,21 +30,21 @@ interface types { } /// Defines the case payload type for `DNS-error` above: - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record DNS-error-payload { rcode: option, info-code: option, } /// Defines the case payload type for `TLS-alert-received` above: - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record TLS-alert-received-payload { alert-id: option, alert-message: option, } /// Defines the case payload type for `HTTP-response-{header,trailer}-size` above: - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record field-size-payload { field-name: option, field-size: option, @@ -52,7 +52,7 @@ interface types { /// These cases are inspired by the IANA HTTP Proxy Error Types: /// - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant error-code { DNS-timeout, DNS-error(DNS-error-payload), @@ -102,7 +102,7 @@ interface types { /// This type enumerates the different kinds of errors that may occur when /// setting or appending to a `fields` resource. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant header-error { /// This error indicates that a `field-name` or `field-value` was /// syntactically invalid when used with an operation that sets headers in a @@ -114,30 +114,49 @@ interface types { /// This error indicates that the operation on the `fields` was not /// permitted because the fields are immutable. immutable, + /// This error indicates that the operation would exceed an + /// implementation-defined limit on field sizes. This may apply to + /// an individual `field-value`, a single `field-name` plus all its + /// values, or the total aggregate size of all fields. + size-exceeded, + /// This is a catch-all error for anything that doesn't fit cleanly into a + /// more specific case. Implementations can use this to extend the error + /// type without breaking existing code. It also includes an optional + /// string for an unstructured description of the error. Users should not + /// depend on the string for diagnosing errors, as it's not required to be + /// consistent between implementations. + other(option), } /// This type enumerates the different kinds of errors that may occur when /// setting fields of a `request-options` resource. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant request-options-error { /// Indicates the specified field is not supported by this implementation. not-supported, /// Indicates that the operation on the `request-options` was not permitted /// because it is immutable. immutable, + /// This is a catch-all error for anything that doesn't fit cleanly into a + /// more specific case. Implementations can use this to extend the error + /// type without breaking existing code. It also includes an optional + /// string for an unstructured description of the error. Users should not + /// depend on the string for diagnosing errors, as it's not required to be + /// consistent between implementations. + other(option), } /// Field names are always strings. /// /// Field names should always be treated as case insensitive by the `fields` /// resource for the purposes of equality checking. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type field-name = string; /// Field values should always be ASCII strings. However, in /// reality, HTTP implementations often have to interpret malformed values, /// so they are provided as a list of bytes. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type field-value = list; /// This following block defines the `fields` resource which corresponds to @@ -155,7 +174,11 @@ interface types { /// original casing used to construct or mutate the `fields` resource. The `fields` /// resource should use that original casing when serializing the fields for /// transport or when returning them from a method. - @since(version = 0.3.0-rc-2026-02-09) + /// + /// Implementations may impose limits on individual field values and on total + /// aggregate field section size. Operations that would exceed these limits + /// fail with `header-error.size-exceeded` + @since(version = 0.3.0-rc-2026-03-15) resource fields { /// Construct an empty HTTP Fields. /// @@ -175,7 +198,8 @@ interface types { /// well-formed, so they are represented as a raw list of bytes. /// /// An error result will be returned if any header or value was - /// syntactically invalid, or if a header was forbidden. + /// syntactically invalid, if a header was forbidden, or if the + /// entries would exceed an implementation size limit. from-list: static func(entries: list>) -> result; /// Get all of the values corresponding to a name. If the name is not present /// in this `fields`, an empty list is returned. However, if the name is @@ -189,6 +213,9 @@ interface types { /// name, if they have been set. /// /// Fails with `header-error.immutable` if the `fields` are immutable. + /// + /// Fails with `header-error.size-exceeded` if the name or values would + /// exceed an implementation-defined size limit. set: func(name: field-name, value: list) -> result<_, header-error>; /// Delete all values for a name. Does nothing if no values for the name /// exist. @@ -206,6 +233,9 @@ interface types { /// values for that name. /// /// Fails with `header-error.immutable` if the `fields` are immutable. + /// + /// Fails with `header-error.size-exceeded` if the value would exceed + /// an implementation-defined size limit. append: func(name: field-name, value: field-value) -> result<_, header-error>; /// Retrieve the full set of names and values in the Fields. Like the /// constructor, the list represents each name-value pair. @@ -224,15 +254,15 @@ interface types { } /// Headers is an alias for Fields. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type headers = fields; /// Trailers is an alias for Fields. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type trailers = fields; /// Represents an HTTP Request. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource request { /// Construct a new `request` with a default `method` of `GET`, and /// `none` values for `path-with-query`, `scheme`, and `authority`. @@ -319,7 +349,7 @@ interface types { /// /// These timeouts are separate from any the user may use to bound an /// asynchronous call. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource request-options { /// Construct a default `request-options` value. constructor(); @@ -348,11 +378,11 @@ interface types { } /// This type corresponds to the HTTP standard Status Code. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type status-code = u16; /// Represents an HTTP Response. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource response { /// Construct a new `response`, with a default `status-code` of `200`. /// If a different `status-code` is needed, it must be set via the @@ -401,7 +431,7 @@ interface types { /// /// In `wasi:http/middleware` this interface is both exported and imported as /// the "downstream" and "upstream" directions of the middleware chain. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface handler { use types.{request, response, error-code}; @@ -420,7 +450,7 @@ interface handler { /// (including WIT itself) is unable to represent a component importing two /// instances of the same interface. A `client.send` import may be linked /// directly to a `handler.handle` export to bypass the network. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface client { use types.{request, response, error-code}; @@ -432,22 +462,22 @@ interface client { /// The `wasi:http/service` world captures a broad category of HTTP services /// including web applications, API servers, and proxies. It may be `include`d /// in more specific worlds such as `wasi:http/middleware`. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world service { - import wasi:cli/types@0.3.0-rc-2026-02-09; - import wasi:cli/stdout@0.3.0-rc-2026-02-09; - import wasi:cli/stderr@0.3.0-rc-2026-02-09; - import wasi:cli/stdin@0.3.0-rc-2026-02-09; - import wasi:clocks/types@0.3.0-rc-2026-02-09; + import wasi:cli/types@0.3.0-rc-2026-03-15; + import wasi:cli/stdout@0.3.0-rc-2026-03-15; + import wasi:cli/stderr@0.3.0-rc-2026-03-15; + import wasi:cli/stdin@0.3.0-rc-2026-03-15; + import wasi:clocks/types@0.3.0-rc-2026-03-15; import types; import client; - import wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09; - import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; + import wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15; + import wasi:clocks/system-clock@0.3.0-rc-2026-03-15; @unstable(feature = clocks-timezone) - import wasi:clocks/timezone@0.3.0-rc-2026-02-09; - import wasi:random/random@0.3.0-rc-2026-02-09; - import wasi:random/insecure@0.3.0-rc-2026-02-09; - import wasi:random/insecure-seed@0.3.0-rc-2026-02-09; + import wasi:clocks/timezone@0.3.0-rc-2026-03-15; + import wasi:random/random@0.3.0-rc-2026-03-15; + import wasi:random/insecure@0.3.0-rc-2026-03-15; + import wasi:random/insecure-seed@0.3.0-rc-2026-03-15; export handler; } @@ -457,23 +487,23 @@ world service { /// Components may implement this world to allow them to participate in handler /// "chains" where a `request` flows through handlers on its way to some terminal /// `service` and corresponding `response` flows in the opposite direction. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world middleware { - import wasi:clocks/types@0.3.0-rc-2026-02-09; + import wasi:clocks/types@0.3.0-rc-2026-03-15; import types; import handler; - import wasi:cli/types@0.3.0-rc-2026-02-09; - import wasi:cli/stdout@0.3.0-rc-2026-02-09; - import wasi:cli/stderr@0.3.0-rc-2026-02-09; - import wasi:cli/stdin@0.3.0-rc-2026-02-09; + import wasi:cli/types@0.3.0-rc-2026-03-15; + import wasi:cli/stdout@0.3.0-rc-2026-03-15; + import wasi:cli/stderr@0.3.0-rc-2026-03-15; + import wasi:cli/stdin@0.3.0-rc-2026-03-15; import client; - import wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09; - import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; + import wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15; + import wasi:clocks/system-clock@0.3.0-rc-2026-03-15; @unstable(feature = clocks-timezone) - import wasi:clocks/timezone@0.3.0-rc-2026-02-09; - import wasi:random/random@0.3.0-rc-2026-02-09; - import wasi:random/insecure@0.3.0-rc-2026-02-09; - import wasi:random/insecure-seed@0.3.0-rc-2026-02-09; + import wasi:clocks/timezone@0.3.0-rc-2026-03-15; + import wasi:random/random@0.3.0-rc-2026-03-15; + import wasi:random/insecure@0.3.0-rc-2026-03-15; + import wasi:random/insecure-seed@0.3.0-rc-2026-03-15; export handler; } diff --git a/crates/wasi-http/src/p3/wit/deps/random.wit b/crates/wasi-http/src/p3/wit/deps/random.wit index 521df6e50fc5..026f44a10949 100644 --- a/crates/wasi-http/src/p3/wit/deps/random.wit +++ b/crates/wasi-http/src/p3/wit/deps/random.wit @@ -1,10 +1,10 @@ -package wasi:random@0.3.0-rc-2026-02-09; +package wasi:random@0.3.0-rc-2026-03-15; /// The insecure-seed interface for seeding hash-map DoS resistance. /// /// It is intended to be portable at least between Unix-family platforms and /// Windows. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface insecure-seed { /// Return a 128-bit value that may contain a pseudo-random value. /// @@ -23,7 +23,7 @@ interface insecure-seed { /// This will likely be changed to a value import, to prevent it from being /// called multiple times and potentially used for purposes other than DoS /// protection. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-insecure-seed: func() -> tuple; } @@ -31,9 +31,9 @@ interface insecure-seed { /// /// It is intended to be portable at least between Unix-family platforms and /// Windows. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface insecure { - /// Return `len` insecure pseudo-random bytes. + /// Return up to `max-len` insecure pseudo-random bytes. /// /// This function is not cryptographically secure. Do not use it for /// anything related to security. @@ -41,14 +41,21 @@ interface insecure { /// There are no requirements on the values of the returned bytes, however /// implementations are encouraged to return evenly distributed values with /// a long period. - @since(version = 0.3.0-rc-2026-02-09) - get-insecure-random-bytes: func(len: u64) -> list; + /// + /// Implementations MAY return fewer bytes than requested (a short read). + /// Callers that require exactly `max-len` bytes MUST call this function in + /// a loop until the desired number of bytes has been accumulated. + /// Implementations MUST return at least 1 byte when `max-len` is greater + /// than zero. When `max-len` is zero, implementations MUST return an empty + /// list without trapping. + @since(version = 0.3.0-rc-2026-03-15) + get-insecure-random-bytes: func(max-len: u64) -> list; /// Return an insecure pseudo-random `u64` value. /// /// This function returns the same type of pseudo-random data as /// `get-insecure-random-bytes`, represented as a `u64`. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-insecure-random-u64: func() -> u64; } @@ -56,9 +63,10 @@ interface insecure { /// /// It is intended to be portable at least between Unix-family platforms and /// Windows. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface random { - /// Return `len` cryptographically-secure random or pseudo-random bytes. + /// Return up to `max-len` cryptographically-secure random or pseudo-random + /// bytes. /// /// This function must produce data at least as cryptographically secure and /// fast as an adequately seeded cryptographically-secure pseudo-random @@ -67,26 +75,33 @@ interface random { /// request and on requests for numbers of bytes. The returned data must /// always be unpredictable. /// + /// Implementations MAY return fewer bytes than requested (a short read). + /// Callers that require exactly `max-len` bytes MUST call this function in + /// a loop until the desired number of bytes has been accumulated. + /// Implementations MUST return at least 1 byte when `max-len` is greater + /// than zero. When `max-len` is zero, implementations MUST return an empty + /// list without trapping. + /// /// This function must always return fresh data. Deterministic environments /// must omit this function, rather than implementing it with deterministic /// data. - @since(version = 0.3.0-rc-2026-02-09) - get-random-bytes: func(len: u64) -> list; + @since(version = 0.3.0-rc-2026-03-15) + get-random-bytes: func(max-len: u64) -> list; /// Return a cryptographically-secure random or pseudo-random `u64` value. /// /// This function returns the same type of data as `get-random-bytes`, /// represented as a `u64`. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-random-u64: func() -> u64; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import random; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import insecure; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import insecure-seed; } diff --git a/crates/wasi-http/src/p3/wit/deps/sockets.wit b/crates/wasi-http/src/p3/wit/deps/sockets.wit index aa9d4d7f074e..cde2e4d6ebe0 100644 --- a/crates/wasi-http/src/p3/wit/deps/sockets.wit +++ b/crates/wasi-http/src/p3/wit/deps/sockets.wit @@ -1,63 +1,86 @@ -package wasi:sockets@0.3.0-rc-2026-02-09; +package wasi:sockets@0.3.0-rc-2026-03-15; -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface types { - @since(version = 0.3.0-rc-2026-02-09) - use wasi:clocks/types@0.3.0-rc-2026-02-09.{duration}; + @since(version = 0.3.0-rc-2026-03-15) + use wasi:clocks/types@0.3.0-rc-2026-03-15.{duration}; /// Error codes. /// /// In theory, every API can return any error code. /// In practice, API's typically only return the errors documented per API /// combined with a couple of errors that are always possible: - /// - `unknown` + /// - `other` /// - `access-denied` /// - `not-supported` /// - `out-of-memory` /// /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. - @since(version = 0.3.0-rc-2026-02-09) - enum error-code { - /// Unknown error - unknown, + @since(version = 0.3.0-rc-2026-03-15) + variant error-code { /// Access denied. /// /// POSIX equivalent: EACCES, EPERM access-denied, /// The operation is not supported. /// - /// POSIX equivalent: EOPNOTSUPP + /// POSIX equivalent: EOPNOTSUPP, ENOPROTOOPT, EPFNOSUPPORT, EPROTONOSUPPORT, ESOCKTNOSUPPORT not-supported, /// One of the arguments is invalid. /// - /// POSIX equivalent: EINVAL + /// POSIX equivalent: EINVAL, EDESTADDRREQ, EAFNOSUPPORT invalid-argument, /// Not enough memory to complete the operation. /// - /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY + /// POSIX equivalent: ENOMEM, ENOBUFS out-of-memory, /// The operation timed out before it could finish completely. + /// + /// POSIX equivalent: ETIMEDOUT timeout, /// The operation is not valid in the socket's current state. invalid-state, - /// A bind operation failed because the provided address is not an address that the `network` can bind to. + /// The local address is not available. + /// + /// POSIX equivalent: EADDRNOTAVAIL address-not-bindable, - /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available. + /// A bind operation failed because the provided address is already in + /// use or because there are no ephemeral ports available. + /// + /// POSIX equivalent: EADDRINUSE address-in-use, - /// The remote address is not reachable + /// The remote address is not reachable. + /// + /// POSIX equivalent: EHOSTUNREACH, EHOSTDOWN, ENETDOWN, ENETUNREACH, ENONET remote-unreachable, - /// The TCP connection was forcefully rejected + /// The connection was forcefully rejected. + /// + /// POSIX equivalent: ECONNREFUSED connection-refused, - /// The TCP connection was reset. + /// A write failed because the connection was broken. + /// + /// POSIX equivalent: EPIPE + connection-broken, + /// The connection was reset. + /// + /// POSIX equivalent: ECONNRESET connection-reset, - /// A TCP connection was aborted. + /// The connection was aborted. + /// + /// POSIX equivalent: ECONNABORTED connection-aborted, /// The size of a datagram sent to a UDP socket exceeded the maximum /// supported size. + /// + /// POSIX equivalent: EMSGSIZE datagram-too-large, + /// A catch-all for errors not captured by the existing variants. + /// Implementations can use this to extend the error type without + /// breaking existing code. + other(option), } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) enum ip-address-family { /// Similar to `AF_INET` in POSIX. ipv4, @@ -65,19 +88,19 @@ interface types { ipv6, } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type ipv4-address = tuple; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type ipv6-address = tuple; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant ip-address { ipv4(ipv4-address), ipv6(ipv6-address), } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record ipv4-socket-address { /// sin_port port: u16, @@ -85,7 +108,7 @@ interface types { address: ipv4-address, } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record ipv6-socket-address { /// sin6_port port: u16, @@ -97,7 +120,7 @@ interface types { scope-id: u32, } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant ip-socket-address { ipv4(ipv4-socket-address), ipv6(ipv6-socket-address), @@ -112,39 +135,57 @@ interface types { /// - `connecting` /// - `connected` /// - `closed` - /// See + /// See /// for more information. /// /// Note: Except where explicitly mentioned, whenever this documentation uses /// the term "bound" without backticks it actually means: in the `bound` state *or higher*. /// (i.e. `bound`, `listening`, `connecting` or `connected`) /// + /// WASI uses shared ownership semantics: the `tcp-socket` handle and all + /// derived `stream` and `future` values reference a single underlying OS + /// socket: + /// - Send/receive streams remain functional after the original `tcp-socket` + /// handle is dropped. + /// - The stream returned by `listen` behaves similarly. + /// - Client sockets returned by `tcp-socket::listen` are independent and do + /// not keep the listening socket alive. + /// + /// The OS socket is closed only after the last handle is dropped. This + /// model has observable effects; for example, it affects when the local + /// port binding is released. + /// /// In addition to the general error codes documented on the /// `types::error-code` type, TCP socket methods may always return /// `error(invalid-state)` when in the `closed` state. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource tcp-socket { /// Create a new TCP socket. /// - /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. - /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` + /// in POSIX. On IPv6 sockets, IPV6_V6ONLY is enabled by default and + /// can't be configured otherwise. /// /// Unlike POSIX, WASI sockets have no notion of a socket-level /// `O_NONBLOCK` flag. Instead they fully rely on the Component Model's /// async support. /// + /// # Typical errors + /// - `not-supported`: The `address-family` is not supported. (EAFNOSUPPORT) + /// /// # References /// - /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) create: static func(address-family: ip-address-family) -> result; /// Bind the socket to the provided IP address and port. /// - /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which - /// network interface(s) to bind to. - /// If the TCP/UDP port is zero, the socket will be bound to a random free port. + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is + /// left to the implementation to decide which network interface(s) to + /// bind to. If the TCP/UDP port is zero, the socket will be bound to a + /// random free port. /// /// Bind can be attempted multiple times on the same socket, even with /// different arguments on each iteration. But never concurrently and @@ -161,21 +202,27 @@ interface types { /// - `address-not-bindable`: `local-address` is not an address that can be bound to. (EADDRNOTAVAIL) /// /// # Implementors note - /// When binding to a non-zero port, this bind operation shouldn't be affected by the TIME_WAIT - /// state of a recently closed socket on the same local address. In practice this means that the SO_REUSEADDR - /// socket option should be set implicitly on all platforms, except on Windows where this is the default behavior - /// and SO_REUSEADDR performs something different entirely. + /// The bind operation shouldn't be affected by the TIME_WAIT state of a + /// recently closed socket on the same local address. In practice this + /// means that the SO_REUSEADDR socket option should be set implicitly + /// on all platforms, except on Windows where this is the default + /// behavior and SO_REUSEADDR performs something different. /// /// # References /// - /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) bind: func(local-address: ip-socket-address) -> result<_, error-code>; /// Connect to a remote endpoint. /// - /// On success, the socket is transitioned into the `connected` state and this function returns a connection resource. + /// On success, the socket is transitioned into the `connected` state + /// and the `remote-address` of the socket is updated. + /// The `local-address` may be updated as well, based on the best network + /// path to `remote-address`. If the socket was not already explicitly + /// bound, this function will implicitly bind the socket to a random + /// free port. /// /// After a failed connection attempt, the socket will be in the `closed` /// state and the only valid action left is to `drop` the socket. A single @@ -202,7 +249,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) connect: async func(remote-address: ip-socket-address) -> result<_, error-code>; /// Start listening and return a stream of new inbound connections. /// @@ -274,7 +321,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) listen: func() -> result, error-code>; /// Transmit data to peer. /// @@ -288,6 +335,8 @@ interface types { /// /// # Typical errors /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN) + /// - `invalid-state`: `send` has already been called on this socket. + /// - `connection-broken`: The connection is not writable anymore. (EPIPE, ECONNABORTED on Windows) /// - `connection-reset`: The connection was reset. (ECONNRESET) /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) /// @@ -296,31 +345,27 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) send: func(data: stream) -> future>; /// Read data from peer. /// - /// This function returns a `stream` which provides the data received from the - /// socket, and a `future` providing additional error information in case the - /// socket is closed abnormally. + /// Returns a `stream` of data sent by the peer. The implementation + /// drops the stream once no more data is available. At that point, the + /// returned `future` resolves to: + /// - `ok` after a graceful shutdown from the peer (i.e. a FIN packet), or + /// - `err` if the socket was closed abnormally. /// - /// If the socket is closed normally, `stream.read` on the `stream` will return - /// `read-status::closed` with no `error-context` and the future resolves to - /// the value `ok`. If the socket is closed abnormally, `stream.read` on the - /// `stream` returns `read-status::closed` with an `error-context` and the future - /// resolves to `err` with an `error-code`. + /// `receive` may be called only once per socket. Subsequent calls return + /// a closed stream and a future resolved to `err(invalid-state)`. /// - /// `receive` is meant to be called only once per socket. If it is called more - /// than once, the subsequent calls return a new `stream` that fails as if it - /// were closed abnormally. - /// - /// If the caller is not expecting to receive any data from the peer, - /// they may drop the stream. Any data still in the receive queue + /// If the caller is not expecting to receive any more data from the peer, + /// they should drop the stream. Any data still in the receive queue /// will be discarded. This is equivalent to calling `shutdown(SHUT_RD)` /// in POSIX. /// /// # Typical errors /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN) + /// - `invalid-state`: `receive` has already been called on this socket. /// - `connection-reset`: The connection was reset. (ECONNRESET) /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) /// @@ -329,7 +374,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) receive: func() -> tuple, future>>; /// Get the bound local address. /// @@ -337,7 +382,8 @@ interface types { /// > If the socket has not been bound to a local name, the value /// > stored in the object pointed to by `address` is unspecified. /// - /// WASI is stricter and requires `get-local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// WASI is stricter and requires `get-local-address` to return + /// `invalid-state` when the socket hasn't been bound yet. /// /// # Typical errors /// - `invalid-state`: The socket is not bound to any local address. @@ -347,7 +393,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-local-address: func() -> result; /// Get the remote address. /// @@ -359,30 +405,32 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-remote-address: func() -> result; /// Whether the socket is in the `listening` state. /// /// Equivalent to the SO_ACCEPTCONN socket option. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-is-listening: func() -> bool; /// Whether this is a IPv4 or IPv6 socket. /// /// This is the value passed to the constructor. /// /// Equivalent to the SO_DOMAIN socket option. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-address-family: func() -> ip-address-family; - /// Hints the desired listen queue size. Implementations are free to ignore this. + /// Hints the desired listen queue size. Implementations are free to + /// ignore this. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// Any other value will never cause an error, but it might be silently + /// clamped and/or rounded. /// /// # Typical errors /// - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen. /// - `invalid-argument`: (set) The provided value was 0. /// - `invalid-state`: (set) The socket is in the `connecting` or `connected` state. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-listen-backlog-size: func(value: u64) -> result<_, error-code>; /// Enables or disables keepalive. /// @@ -390,54 +438,60 @@ interface types { /// - `keep-alive-idle-time` /// - `keep-alive-interval` /// - `keep-alive-count` - /// These properties can be configured while `keep-alive-enabled` is false, but only come into effect when `keep-alive-enabled` is true. + /// These properties can be configured while `keep-alive-enabled` is + /// false, but only come into effect when `keep-alive-enabled` is true. /// /// Equivalent to the SO_KEEPALIVE socket option. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-keep-alive-enabled: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-keep-alive-enabled: func(value: bool) -> result<_, error-code>; - /// Amount of time the connection has to be idle before TCP starts sending keepalive packets. + /// Amount of time the connection has to be idle before TCP starts + /// sending keepalive packets. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. /// /// Equivalent to the TCP_KEEPIDLE socket option. (TCP_KEEPALIVE on MacOS) /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-keep-alive-idle-time: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>; /// The time between keepalive packets. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. /// /// Equivalent to the TCP_KEEPINTVL socket option. /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-keep-alive-interval: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-keep-alive-interval: func(value: duration) -> result<_, error-code>; - /// The maximum amount of keepalive packets TCP should send before aborting the connection. + /// The maximum amount of keepalive packets TCP should send before + /// aborting the connection. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. /// /// Equivalent to the TCP_KEEPCNT socket option. /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-keep-alive-count: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-keep-alive-count: func(value: u32) -> result<_, error-code>; /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. /// @@ -445,37 +499,49 @@ interface types { /// /// # Typical errors /// - `invalid-argument`: (set) The TTL value must be 1 or higher. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-hop-limit: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-hop-limit: func(value: u8) -> result<_, error-code>; - /// The kernel buffer space reserved for sends/receives on this socket. + /// Kernel buffer space reserved for sending/receiving on this socket. + /// Implementations usually treat this as a cap the buffer can grow to, + /// rather than allocating the full amount immediately. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. + /// + /// This is only a performance hint. The implementation may ignore it or + /// tweak it based on real traffic patterns. + /// Linux and macOS appear to behave differently depending on whether a + /// buffer size was explicitly set. When set, they tend to honor it; when + /// not set, they dynamically adjust the buffer size as the connection + /// progresses. This is especially noticeable when comparing the values + /// from before and after connection establishment. /// /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-receive-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-receive-buffer-size: func(value: u64) -> result<_, error-code>; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-send-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-send-buffer-size: func(value: u64) -> result<_, error-code>; } /// A UDP socket handle. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource udp-socket { /// Create a new UDP socket. /// - /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. - /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` + /// in POSIX. On IPv6 sockets, IPV6_V6ONLY is enabled by default and + /// can't be configured otherwise. /// /// Unlike POSIX, WASI sockets have no notion of a socket-level /// `O_NONBLOCK` flag. Instead they fully rely on the Component Model's @@ -486,13 +552,14 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) create: static func(address-family: ip-address-family) -> result; /// Bind the socket to the provided IP address and port. /// - /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which - /// network interface(s) to bind to. - /// If the port is zero, the socket will be bound to a random free port. + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is + /// left to the implementation to decide which network interface(s) to + /// bind to. If the port is zero, the socket will be bound to a random + /// free port. /// /// # Typical errors /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) @@ -506,7 +573,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) bind: func(local-address: ip-socket-address) -> result<_, error-code>; /// Associate this socket with a specific peer address. /// @@ -544,12 +611,12 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) connect: func(remote-address: ip-socket-address) -> result<_, error-code>; /// Dissociate this socket from its peer address. /// /// After calling this method, `send` & `receive` are free to communicate - /// with any address again. + /// with any remote address again. /// /// The POSIX equivalent of this is calling `connect` with an `AF_UNSPEC` address. /// @@ -561,7 +628,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) disconnect: func() -> result<_, error-code>; /// Send a message on the socket to a particular peer. /// @@ -573,6 +640,9 @@ interface types { /// _may_ be provided but then it must be identical to the address /// passed to `connect`. /// + /// If the socket has not been explicitly bound, it will be + /// implicitly bound to a random free port. + /// /// Implementations may trap if the `data` length exceeds 64 KiB. /// /// # Typical errors @@ -584,6 +654,14 @@ interface types { /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) /// - `connection-refused`: The connection was refused. (ECONNREFUSED) /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) + /// + /// # Implementors note + /// WASI requires `send` to perform an implicit bind if the socket + /// has not been bound. Not all platforms (notably Windows) exhibit + /// this behavior natively. On such platforms, the WASI implementation + /// should emulate it by performing the bind if the guest has not + /// already done so. /// /// # References /// - @@ -594,7 +672,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) send: async func(data: list, remote-address: option) -> result<_, error-code>; /// Receive a message on the socket. /// @@ -619,7 +697,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) receive: async func() -> result, ip-socket-address>, error-code>; /// Get the current bound address. /// @@ -627,7 +705,8 @@ interface types { /// > If the socket has not been bound to a local name, the value /// > stored in the object pointed to by `address` is unspecified. /// - /// WASI is stricter and requires `get-local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// WASI is stricter and requires `get-local-address` to return + /// `invalid-state` when the socket hasn't been bound yet. /// /// # Typical errors /// - `invalid-state`: The socket is not bound to any local address. @@ -637,7 +716,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-local-address: func() -> result; /// Get the address the socket is currently "connected" to. /// @@ -649,14 +728,14 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-remote-address: func() -> result; /// Whether this is a IPv4 or IPv6 socket. /// /// This is the value passed to the constructor. /// /// Equivalent to the SO_DOMAIN socket option. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-address-family: func() -> ip-address-family; /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. /// @@ -664,41 +743,42 @@ interface types { /// /// # Typical errors /// - `invalid-argument`: (set) The TTL value must be 1 or higher. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-unicast-hop-limit: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; - /// The kernel buffer space reserved for sends/receives on this socket. + /// Kernel buffer space reserved for sending/receiving on this socket. + /// Implementations usually treat this as a cap the buffer can grow to, + /// rather than allocating the full amount immediately. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. /// /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-receive-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-receive-buffer-size: func(value: u64) -> result<_, error-code>; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-send-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-send-buffer-size: func(value: u64) -> result<_, error-code>; } } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface ip-name-lookup { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use types.{ip-address}; /// Lookup error codes. - @since(version = 0.3.0-rc-2026-02-09) - enum error-code { - /// Unknown error - unknown, + @since(version = 0.3.0-rc-2026-03-15) + variant error-code { /// Access denied. /// /// POSIX equivalent: EACCES, EPERM @@ -719,13 +799,17 @@ interface ip-name-lookup { /// /// POSIX equivalent: EAI_FAIL permanent-resolver-failure, + /// A catch-all for errors not captured by the existing variants. + /// Implementations can use this to extend the error type without + /// breaking existing code. + other(option), } /// Resolve an internet host name to a list of IP addresses. /// - /// Unicode domain names are automatically converted to ASCII using IDNA encoding. - /// If the input is an IP address string, the address is parsed and returned - /// as-is without making any external requests. + /// Unicode domain names are automatically converted to ASCII using IDNA + /// encoding. If the input is an IP address string, the address is parsed + /// and returned as-is without making any external requests. /// /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. /// @@ -735,24 +819,21 @@ interface ip-name-lookup { /// with at least one address. Additionally, this function never returns /// IPv4-mapped IPv6 addresses. /// - /// The returned future will resolve to an error code in case of failure. - /// It will resolve to success once the returned stream is exhausted. - /// /// # References: /// - /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resolve-addresses: async func(name: string) -> result, error-code>; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) - import wasi:clocks/types@0.3.0-rc-2026-02-09; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) + import wasi:clocks/types@0.3.0-rc-2026-03-15; + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import ip-name-lookup; } diff --git a/crates/wasi-http/src/p3/wit/world.wit b/crates/wasi-http/src/p3/wit/world.wit index f95ca2ba765d..8e7c32424586 100644 --- a/crates/wasi-http/src/p3/wit/world.wit +++ b/crates/wasi-http/src/p3/wit/world.wit @@ -2,5 +2,5 @@ package wasmtime:wasi-http; world bindings { - include wasi:http/service@0.3.0-rc-2026-02-09; + include wasi:http/service@0.3.0-rc-2026-03-15; } diff --git a/crates/wasi-http/tests/all/p3/mod.rs b/crates/wasi-http/tests/all/p3/mod.rs index fd7f67e3073e..70c85e4f0c91 100644 --- a/crates/wasi-http/tests/all/p3/mod.rs +++ b/crates/wasi-http/tests/all/p3/mod.rs @@ -404,7 +404,7 @@ async fn test_http_middleware_with_chain(host_to_host: bool) -> Result<()> { "local:local/chain-http".to_owned(), InstantiationArg { instance: "local:local/chain-http".into(), - export: Some("wasi:http/handler@0.3.0-rc-2026-02-09".into()), + export: Some("wasi:http/handler@0.3.0-rc-2026-03-15".into()), }, )] .into_iter() diff --git a/crates/wasi/src/p3/bindings.rs b/crates/wasi/src/p3/bindings.rs index dd100a4fd8a1..d6c11da7703e 100644 --- a/crates/wasi/src/p3/bindings.rs +++ b/crates/wasi/src/p3/bindings.rs @@ -23,7 +23,7 @@ //! // An example of extending the `wasi:cli/command` world with a //! // custom host interface. //! world my-world { -//! include wasi:cli/command@0.3.0-rc-2026-02-09; +//! include wasi:cli/command@0.3.0-rc-2026-03-15; //! //! import custom-host; //! } diff --git a/crates/wasi/src/p3/filesystem/mod.rs b/crates/wasi/src/p3/filesystem/mod.rs index 9977d677044c..0ef9c3166c7f 100644 --- a/crates/wasi/src/p3/filesystem/mod.rs +++ b/crates/wasi/src/p3/filesystem/mod.rs @@ -255,7 +255,7 @@ impl From for types::DescriptorStat { impl From for types::DescriptorType { fn from(ty: crate::filesystem::DescriptorType) -> Self { match ty { - crate::filesystem::DescriptorType::Unknown => Self::Unknown, + crate::filesystem::DescriptorType::Unknown => Self::Other(None), crate::filesystem::DescriptorType::BlockDevice => Self::BlockDevice, crate::filesystem::DescriptorType::CharacterDevice => Self::CharacterDevice, crate::filesystem::DescriptorType::Directory => Self::Directory, @@ -279,7 +279,7 @@ impl From for types::DescriptorType { } else if ft.is_file() { Self::RegularFile } else { - Self::Unknown + Self::Other(None) } } } diff --git a/crates/wasi/src/p3/sockets/conv.rs b/crates/wasi/src/p3/sockets/conv.rs index 915bd3e96271..a6cc69528ed4 100644 --- a/crates/wasi/src/p3/sockets/conv.rs +++ b/crates/wasi/src/p3/sockets/conv.rs @@ -167,7 +167,7 @@ impl From<&std::io::Error> for types::ErrorCode { std::io::ErrorKind::Unsupported => Self::NotSupported, _ => { debug!("unknown I/O error: {value}"); - Self::Unknown + Self::Other(None) } } } @@ -216,7 +216,7 @@ impl From<&Errno> for types::ErrorCode { // FYI, EINPROGRESS should have already been handled by connect. _ => { debug!("unknown I/O error: {value}"); - Self::Unknown + Self::Other(None) } } } @@ -225,7 +225,7 @@ impl From<&Errno> for types::ErrorCode { impl From for types::ErrorCode { fn from(code: crate::sockets::util::ErrorCode) -> Self { match code { - crate::sockets::util::ErrorCode::Unknown => Self::Unknown, + crate::sockets::util::ErrorCode::Unknown => Self::Other(None), crate::sockets::util::ErrorCode::AccessDenied => Self::AccessDenied, crate::sockets::util::ErrorCode::NotSupported => Self::NotSupported, crate::sockets::util::ErrorCode::InvalidArgument => Self::InvalidArgument, diff --git a/crates/wasi/src/p3/wit/deps/cli.wit b/crates/wasi/src/p3/wit/deps/cli.wit index 4f8bbf8a24a5..8ba52c5cd594 100644 --- a/crates/wasi/src/p3/wit/deps/cli.wit +++ b/crates/wasi/src/p3/wit/deps/cli.wit @@ -1,6 +1,6 @@ -package wasi:cli@0.3.0-rc-2026-02-09; +package wasi:cli@0.3.0-rc-2026-03-15; -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface environment { /// Get the POSIX-style environment variables. /// @@ -10,23 +10,23 @@ interface environment { /// Morally, these are a value import, but until value imports are available /// in the component model, this import function should return the same /// values each time it is called. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-environment: func() -> list>; /// Get the POSIX-style arguments to the program. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-arguments: func() -> list; /// Return a path that programs should use as their initial current working /// directory, interpreting `.` as shorthand for this. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-initial-cwd: func() -> option; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface exit { /// Exit the current instance and any linked instances. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) exit: func(status: result); /// Exit the current instance and any linked instances, reporting the @@ -41,16 +41,16 @@ interface exit { exit-with-code: func(status-code: u8); } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface run { /// Run the program. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) run: async func() -> result; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface types { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) enum error-code { /// Input/output error io, @@ -61,7 +61,7 @@ interface types { } } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface stdin { use types.{error-code}; @@ -78,11 +78,11 @@ interface stdin { /// /// Multiple streams may be active at the same time. The behavior of concurrent /// reads is implementation-specific. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) read-via-stream: func() -> tuple, future>>; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface stdout { use types.{error-code}; @@ -94,11 +94,11 @@ interface stdout { /// /// Otherwise if there is an error the readable end of the stream will be /// dropped and this function will return an error-code. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) write-via-stream: func(data: stream) -> future>; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface stderr { use types.{error-code}; @@ -110,7 +110,7 @@ interface stderr { /// /// Otherwise if there is an error the readable end of the stream will be /// dropped and this function will return an error-code. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) write-via-stream: func(data: stream) -> future>; } @@ -119,10 +119,10 @@ interface stderr { /// In the future, this may include functions for disabling echoing, /// disabling input buffering so that keyboard events are sent through /// immediately, querying supported features, and so on. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-input { /// The input side of a terminal. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource terminal-input; } @@ -131,126 +131,126 @@ interface terminal-input { /// In the future, this may include functions for querying the terminal /// size, being notified of terminal size changes, querying supported /// features, and so on. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-output { /// The output side of a terminal. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource terminal-output; } /// An interface providing an optional `terminal-input` for stdin as a /// link-time authority. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-stdin { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use terminal-input.{terminal-input}; /// If stdin is connected to a terminal, return a `terminal-input` handle /// allowing further interaction with it. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-terminal-stdin: func() -> option; } /// An interface providing an optional `terminal-output` for stdout as a /// link-time authority. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-stdout { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use terminal-output.{terminal-output}; /// If stdout is connected to a terminal, return a `terminal-output` handle /// allowing further interaction with it. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-terminal-stdout: func() -> option; } /// An interface providing an optional `terminal-output` for stderr as a /// link-time authority. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-stderr { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use terminal-output.{terminal-output}; /// If stderr is connected to a terminal, return a `terminal-output` handle /// allowing further interaction with it. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-terminal-stderr: func() -> option; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import environment; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import exit; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stdin; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stdout; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stderr; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-input; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-output; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stdin; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stdout; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stderr; - import wasi:clocks/types@0.3.0-rc-2026-02-09; - import wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09; - import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; + import wasi:clocks/types@0.3.0-rc-2026-03-15; + import wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15; + import wasi:clocks/system-clock@0.3.0-rc-2026-03-15; @unstable(feature = clocks-timezone) - import wasi:clocks/timezone@0.3.0-rc-2026-02-09; - import wasi:filesystem/types@0.3.0-rc-2026-02-09; - import wasi:filesystem/preopens@0.3.0-rc-2026-02-09; - import wasi:sockets/types@0.3.0-rc-2026-02-09; - import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-02-09; - import wasi:random/random@0.3.0-rc-2026-02-09; - import wasi:random/insecure@0.3.0-rc-2026-02-09; - import wasi:random/insecure-seed@0.3.0-rc-2026-02-09; + import wasi:clocks/timezone@0.3.0-rc-2026-03-15; + import wasi:filesystem/types@0.3.0-rc-2026-03-15; + import wasi:filesystem/preopens@0.3.0-rc-2026-03-15; + import wasi:sockets/types@0.3.0-rc-2026-03-15; + import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-03-15; + import wasi:random/random@0.3.0-rc-2026-03-15; + import wasi:random/insecure@0.3.0-rc-2026-03-15; + import wasi:random/insecure-seed@0.3.0-rc-2026-03-15; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world command { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import environment; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import exit; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stdin; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stdout; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stderr; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-input; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-output; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stdin; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stdout; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stderr; - import wasi:clocks/types@0.3.0-rc-2026-02-09; - import wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09; - import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; + import wasi:clocks/types@0.3.0-rc-2026-03-15; + import wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15; + import wasi:clocks/system-clock@0.3.0-rc-2026-03-15; @unstable(feature = clocks-timezone) - import wasi:clocks/timezone@0.3.0-rc-2026-02-09; - import wasi:filesystem/types@0.3.0-rc-2026-02-09; - import wasi:filesystem/preopens@0.3.0-rc-2026-02-09; - import wasi:sockets/types@0.3.0-rc-2026-02-09; - import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-02-09; - import wasi:random/random@0.3.0-rc-2026-02-09; - import wasi:random/insecure@0.3.0-rc-2026-02-09; - import wasi:random/insecure-seed@0.3.0-rc-2026-02-09; + import wasi:clocks/timezone@0.3.0-rc-2026-03-15; + import wasi:filesystem/types@0.3.0-rc-2026-03-15; + import wasi:filesystem/preopens@0.3.0-rc-2026-03-15; + import wasi:sockets/types@0.3.0-rc-2026-03-15; + import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-03-15; + import wasi:random/random@0.3.0-rc-2026-03-15; + import wasi:random/insecure@0.3.0-rc-2026-03-15; + import wasi:random/insecure-seed@0.3.0-rc-2026-03-15; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) export run; } diff --git a/crates/wasi/src/p3/wit/deps/clocks.wit b/crates/wasi/src/p3/wit/deps/clocks.wit index 71986bab2bcc..19fc4bcd59e9 100644 --- a/crates/wasi/src/p3/wit/deps/clocks.wit +++ b/crates/wasi/src/p3/wit/deps/clocks.wit @@ -1,10 +1,10 @@ -package wasi:clocks@0.3.0-rc-2026-02-09; +package wasi:clocks@0.3.0-rc-2026-03-15; /// This interface common types used throughout wasi:clocks. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface types { /// A duration of time, in nanoseconds. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type duration = u64; } @@ -16,14 +16,14 @@ interface types { /// /// A monotonic clock is a clock which has an unspecified initial value, and /// successive reads of the clock will produce non-decreasing values. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface monotonic-clock { use types.{duration}; /// A mark on a monotonic clock is a number of nanoseconds since an /// unspecified initial value, and can only be compared to instances from /// the same monotonic-clock. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type mark = u64; /// Read the current value of the clock. @@ -35,20 +35,20 @@ interface monotonic-clock { /// the value of the clock in a `mark`. Consequently, implementations /// should ensure that the starting time is low enough to avoid the /// possibility of overflow in practice. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) now: func() -> mark; /// Query the resolution of the clock. Returns the duration of time /// corresponding to a clock tick. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-resolution: func() -> duration; /// Wait until the specified mark has occurred. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) wait-until: async func(when: mark); /// Wait for the specified duration to elapse. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) wait-for: async func(how-long: duration); } @@ -62,7 +62,7 @@ interface monotonic-clock { /// monotonic, making it unsuitable for measuring elapsed time. /// /// It is intended for reporting the current date and time for humans. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface system-clock { use types.{duration}; @@ -82,7 +82,7 @@ interface system-clock { /// /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record instant { seconds: s64, nanoseconds: u32, @@ -94,12 +94,12 @@ interface system-clock { /// will not necessarily produce a sequence of non-decreasing values. /// /// The nanoseconds field of the output is always less than 1000000000. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) now: func() -> instant; /// Query the resolution of the clock. Returns the smallest duration of time /// that the implementation permits distinguishing. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-resolution: func() -> duration; } @@ -148,13 +148,13 @@ interface timezone { to-debug-string: func() -> string; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import monotonic-clock; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import system-clock; @unstable(feature = clocks-timezone) import timezone; diff --git a/crates/wasi/src/p3/wit/deps/filesystem.wit b/crates/wasi/src/p3/wit/deps/filesystem.wit index 8fb83041a103..697681f30c5e 100644 --- a/crates/wasi/src/p3/wit/deps/filesystem.wit +++ b/crates/wasi/src/p3/wit/deps/filesystem.wit @@ -1,4 +1,4 @@ -package wasi:filesystem@0.3.0-rc-2026-02-09; +package wasi:filesystem@0.3.0-rc-2026-03-15; /// WASI filesystem is a filesystem API primarily intended to let users run WASI /// programs that access their files on their existing filesystems, without @@ -35,23 +35,20 @@ package wasi:filesystem@0.3.0-rc-2026-02-09; /// store or a database instead. /// /// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface types { - @since(version = 0.3.0-rc-2026-02-09) - use wasi:clocks/system-clock@0.3.0-rc-2026-02-09.{instant}; + @since(version = 0.3.0-rc-2026-03-15) + use wasi:clocks/system-clock@0.3.0-rc-2026-03-15.{instant}; /// File size or length of a region within a file. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type filesize = u64; /// The type of a filesystem object referenced by a descriptor. /// /// Note: This was called `filetype` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) - enum descriptor-type { - /// The type of the descriptor or file is unknown or is different from - /// any of the other types specified. - unknown, + @since(version = 0.3.0-rc-2026-03-15) + variant descriptor-type { /// The descriptor refers to a block device inode. block-device, /// The descriptor refers to a character device inode. @@ -66,12 +63,15 @@ interface types { regular-file, /// The descriptor refers to a socket. socket, + /// The type of the descriptor or file is different from any of the + /// other types specified. + other(option), } /// Descriptor flags. /// /// Note: This was called `fdflags` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) flags descriptor-flags { /// Read mode: Data can be read. read, @@ -113,7 +113,7 @@ interface types { } /// Flags determining the method of how paths are resolved. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) flags path-flags { /// As long as the resolved path corresponds to a symbolic link, it is /// expanded. @@ -121,7 +121,7 @@ interface types { } /// Open flags used by `open-at`. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) flags open-flags { /// Create file if it does not exist, similar to `O_CREAT` in POSIX. create, @@ -134,13 +134,13 @@ interface types { } /// Number of hard links to an inode. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type link-count = u64; /// File attributes. /// /// Note: This was called `filestat` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record descriptor-stat { /// File type. %type: descriptor-type, @@ -167,7 +167,7 @@ interface types { } /// When setting a timestamp, this gives the value to set it to. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant new-timestamp { /// Leave the timestamp set to its previous value. no-change, @@ -179,7 +179,7 @@ interface types { } /// A directory entry. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record directory-entry { /// The type of the file referred to by this directory entry. %type: descriptor-type, @@ -191,8 +191,8 @@ interface types { /// Not all of these error codes are returned by the functions provided by this /// API; some are used in higher-level library layers, and others are provided /// merely for alignment with POSIX. - @since(version = 0.3.0-rc-2026-02-09) - enum error-code { + @since(version = 0.3.0-rc-2026-03-15) + variant error-code { /// Permission denied, similar to `EACCES` in POSIX. access, /// Connection already in progress, similar to `EALREADY` in POSIX. @@ -265,10 +265,14 @@ interface types { text-file-busy, /// Cross-device link, similar to `EXDEV` in POSIX. cross-device, + /// A catch-all for errors not captured by the existing variants. + /// Implementations can use this to extend the error type without + /// breaking existing code. + other(option), } /// File or memory access pattern advisory information. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) enum advice { /// The application has no advice to give on its behavior with respect /// to the specified data. @@ -292,7 +296,7 @@ interface types { /// A 128-bit hash value, split into parts because wasm doesn't have a /// 128-bit integer type. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record metadata-hash-value { /// 64 bits of a 128-bit hash value. lower: u64, @@ -303,7 +307,7 @@ interface types { /// A descriptor is a reference to a filesystem object, which may be a file, /// directory, named pipe, special file, or other object on which filesystem /// calls may be made. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource descriptor { /// Return a stream for reading from a file. /// @@ -321,7 +325,7 @@ interface types { /// resolves to `err` with an `error-code`. /// /// Note: This is similar to `pread` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) read-via-stream: func(offset: filesize) -> tuple, future>>; /// Return a stream for writing to a file, if available. /// @@ -335,7 +339,7 @@ interface types { /// written or an error is encountered. /// /// Note: This is similar to `pwrite` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) write-via-stream: func(data: stream, offset: filesize) -> future>; /// Return a stream for appending to a file, if available. /// @@ -345,12 +349,12 @@ interface types { /// written or an error is encountered. /// /// Note: This is similar to `write` with `O_APPEND` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) append-via-stream: func(data: stream) -> future>; /// Provide file advisory information on a descriptor. /// /// This is similar to `posix_fadvise` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) advise: async func(offset: filesize, length: filesize, advice: advice) -> result<_, error-code>; /// Synchronize the data of a file to disk. /// @@ -358,7 +362,7 @@ interface types { /// opened for writing. /// /// Note: This is similar to `fdatasync` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) sync-data: async func() -> result<_, error-code>; /// Get flags associated with a descriptor. /// @@ -366,7 +370,7 @@ interface types { /// /// Note: This returns the value that was the `fs_flags` value returned /// from `fdstat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-flags: async func() -> result; /// Get the dynamic type of a descriptor. /// @@ -378,20 +382,20 @@ interface types { /// /// Note: This returns the value that was the `fs_filetype` value returned /// from `fdstat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-type: async func() -> result; /// Adjust the size of an open file. If this increases the file's size, the /// extra bytes are filled with zeros. /// /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-size: async func(size: filesize) -> result<_, error-code>; /// Adjust the timestamps of an open file or directory. /// /// Note: This is similar to `futimens` in POSIX. /// /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-times: async func(data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; /// Read directory entries from a directory. /// @@ -405,7 +409,7 @@ interface types { /// /// This function returns a future, which will resolve to an error code if /// reading full contents of the directory fails. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) read-directory: func() -> tuple, future>>; /// Synchronize the data and metadata of a file to disk. /// @@ -413,12 +417,12 @@ interface types { /// opened for writing. /// /// Note: This is similar to `fsync` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) sync: async func() -> result<_, error-code>; /// Create a directory. /// /// Note: This is similar to `mkdirat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) create-directory-at: async func(path: string) -> result<_, error-code>; /// Return the attributes of an open file or directory. /// @@ -429,7 +433,7 @@ interface types { /// modified, use `metadata-hash`. /// /// Note: This was called `fd_filestat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) stat: async func() -> result; /// Return the attributes of a file or directory. /// @@ -438,7 +442,7 @@ interface types { /// discussion of alternatives. /// /// Note: This was called `path_filestat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) stat-at: async func(path-flags: path-flags, path: string) -> result; /// Adjust the timestamps of a file or directory. /// @@ -446,7 +450,7 @@ interface types { /// /// Note: This was called `path_filestat_set_times` in earlier versions of /// WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-times-at: async func(path-flags: path-flags, path: string, data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; /// Create a hard link. /// @@ -455,7 +459,7 @@ interface types { /// `error-code::not-permitted` if the old path is not a file. /// /// Note: This is similar to `linkat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) link-at: async func(old-path-flags: path-flags, old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; /// Open a file or directory. /// @@ -469,7 +473,7 @@ interface types { /// `error-code::read-only`. /// /// Note: This is similar to `openat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) open-at: async func(path-flags: path-flags, path: string, open-flags: open-flags, %flags: descriptor-flags) -> result; /// Read the contents of a symbolic link. /// @@ -477,19 +481,19 @@ interface types { /// filesystem, this function fails with `error-code::not-permitted`. /// /// Note: This is similar to `readlinkat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) readlink-at: async func(path: string) -> result; /// Remove a directory. /// /// Return `error-code::not-empty` if the directory is not empty. /// /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) remove-directory-at: async func(path: string) -> result<_, error-code>; /// Rename a filesystem object. /// /// Note: This is similar to `renameat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) rename-at: async func(old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; /// Create a symbolic link (also known as a "symlink"). /// @@ -497,13 +501,18 @@ interface types { /// `error-code::not-permitted`. /// /// Note: This is similar to `symlinkat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) symlink-at: async func(old-path: string, new-path: string) -> result<_, error-code>; /// Unlink a filesystem object that is not a directory. /// - /// Return `error-code::is-directory` if the path refers to a directory. - /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + /// This is similar to `unlinkat(fd, path, 0)` in POSIX. + /// + /// Error returns are as specified by POSIX. + /// + /// If the filesystem object is a directory, `error-code::access` or + /// `error-code::is-directory` may be returned instead of the + /// POSIX-specified `error-code::not-permitted`. + @since(version = 0.3.0-rc-2026-03-15) unlink-file-at: async func(path: string) -> result<_, error-code>; /// Test whether two descriptors refer to the same filesystem object. /// @@ -511,7 +520,7 @@ interface types { /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. /// wasi-filesystem does not expose device and inode numbers, so this function /// may be used instead. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) is-same-object: async func(other: borrow) -> bool; /// Return a hash of the metadata associated with a filesystem object referred /// to by a descriptor. @@ -532,35 +541,35 @@ interface types { /// computed hash. /// /// However, none of these is required. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) metadata-hash: async func() -> result; /// Return a hash of the metadata associated with a filesystem object referred /// to by a directory descriptor and a relative path. /// /// This performs the same hash computation as `metadata-hash`. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) metadata-hash-at: async func(path-flags: path-flags, path: string) -> result; } } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface preopens { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use types.{descriptor}; /// Return the set of preopened directories, and their paths. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-directories: func() -> list>; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) - import wasi:clocks/types@0.3.0-rc-2026-02-09; - @since(version = 0.3.0-rc-2026-02-09) - import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) + import wasi:clocks/types@0.3.0-rc-2026-03-15; + @since(version = 0.3.0-rc-2026-03-15) + import wasi:clocks/system-clock@0.3.0-rc-2026-03-15; + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import preopens; } diff --git a/crates/wasi/src/p3/wit/deps/random.wit b/crates/wasi/src/p3/wit/deps/random.wit index 521df6e50fc5..026f44a10949 100644 --- a/crates/wasi/src/p3/wit/deps/random.wit +++ b/crates/wasi/src/p3/wit/deps/random.wit @@ -1,10 +1,10 @@ -package wasi:random@0.3.0-rc-2026-02-09; +package wasi:random@0.3.0-rc-2026-03-15; /// The insecure-seed interface for seeding hash-map DoS resistance. /// /// It is intended to be portable at least between Unix-family platforms and /// Windows. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface insecure-seed { /// Return a 128-bit value that may contain a pseudo-random value. /// @@ -23,7 +23,7 @@ interface insecure-seed { /// This will likely be changed to a value import, to prevent it from being /// called multiple times and potentially used for purposes other than DoS /// protection. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-insecure-seed: func() -> tuple; } @@ -31,9 +31,9 @@ interface insecure-seed { /// /// It is intended to be portable at least between Unix-family platforms and /// Windows. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface insecure { - /// Return `len` insecure pseudo-random bytes. + /// Return up to `max-len` insecure pseudo-random bytes. /// /// This function is not cryptographically secure. Do not use it for /// anything related to security. @@ -41,14 +41,21 @@ interface insecure { /// There are no requirements on the values of the returned bytes, however /// implementations are encouraged to return evenly distributed values with /// a long period. - @since(version = 0.3.0-rc-2026-02-09) - get-insecure-random-bytes: func(len: u64) -> list; + /// + /// Implementations MAY return fewer bytes than requested (a short read). + /// Callers that require exactly `max-len` bytes MUST call this function in + /// a loop until the desired number of bytes has been accumulated. + /// Implementations MUST return at least 1 byte when `max-len` is greater + /// than zero. When `max-len` is zero, implementations MUST return an empty + /// list without trapping. + @since(version = 0.3.0-rc-2026-03-15) + get-insecure-random-bytes: func(max-len: u64) -> list; /// Return an insecure pseudo-random `u64` value. /// /// This function returns the same type of pseudo-random data as /// `get-insecure-random-bytes`, represented as a `u64`. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-insecure-random-u64: func() -> u64; } @@ -56,9 +63,10 @@ interface insecure { /// /// It is intended to be portable at least between Unix-family platforms and /// Windows. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface random { - /// Return `len` cryptographically-secure random or pseudo-random bytes. + /// Return up to `max-len` cryptographically-secure random or pseudo-random + /// bytes. /// /// This function must produce data at least as cryptographically secure and /// fast as an adequately seeded cryptographically-secure pseudo-random @@ -67,26 +75,33 @@ interface random { /// request and on requests for numbers of bytes. The returned data must /// always be unpredictable. /// + /// Implementations MAY return fewer bytes than requested (a short read). + /// Callers that require exactly `max-len` bytes MUST call this function in + /// a loop until the desired number of bytes has been accumulated. + /// Implementations MUST return at least 1 byte when `max-len` is greater + /// than zero. When `max-len` is zero, implementations MUST return an empty + /// list without trapping. + /// /// This function must always return fresh data. Deterministic environments /// must omit this function, rather than implementing it with deterministic /// data. - @since(version = 0.3.0-rc-2026-02-09) - get-random-bytes: func(len: u64) -> list; + @since(version = 0.3.0-rc-2026-03-15) + get-random-bytes: func(max-len: u64) -> list; /// Return a cryptographically-secure random or pseudo-random `u64` value. /// /// This function returns the same type of data as `get-random-bytes`, /// represented as a `u64`. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-random-u64: func() -> u64; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import random; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import insecure; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import insecure-seed; } diff --git a/crates/wasi/src/p3/wit/deps/sockets.wit b/crates/wasi/src/p3/wit/deps/sockets.wit index aa9d4d7f074e..cde2e4d6ebe0 100644 --- a/crates/wasi/src/p3/wit/deps/sockets.wit +++ b/crates/wasi/src/p3/wit/deps/sockets.wit @@ -1,63 +1,86 @@ -package wasi:sockets@0.3.0-rc-2026-02-09; +package wasi:sockets@0.3.0-rc-2026-03-15; -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface types { - @since(version = 0.3.0-rc-2026-02-09) - use wasi:clocks/types@0.3.0-rc-2026-02-09.{duration}; + @since(version = 0.3.0-rc-2026-03-15) + use wasi:clocks/types@0.3.0-rc-2026-03-15.{duration}; /// Error codes. /// /// In theory, every API can return any error code. /// In practice, API's typically only return the errors documented per API /// combined with a couple of errors that are always possible: - /// - `unknown` + /// - `other` /// - `access-denied` /// - `not-supported` /// - `out-of-memory` /// /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. - @since(version = 0.3.0-rc-2026-02-09) - enum error-code { - /// Unknown error - unknown, + @since(version = 0.3.0-rc-2026-03-15) + variant error-code { /// Access denied. /// /// POSIX equivalent: EACCES, EPERM access-denied, /// The operation is not supported. /// - /// POSIX equivalent: EOPNOTSUPP + /// POSIX equivalent: EOPNOTSUPP, ENOPROTOOPT, EPFNOSUPPORT, EPROTONOSUPPORT, ESOCKTNOSUPPORT not-supported, /// One of the arguments is invalid. /// - /// POSIX equivalent: EINVAL + /// POSIX equivalent: EINVAL, EDESTADDRREQ, EAFNOSUPPORT invalid-argument, /// Not enough memory to complete the operation. /// - /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY + /// POSIX equivalent: ENOMEM, ENOBUFS out-of-memory, /// The operation timed out before it could finish completely. + /// + /// POSIX equivalent: ETIMEDOUT timeout, /// The operation is not valid in the socket's current state. invalid-state, - /// A bind operation failed because the provided address is not an address that the `network` can bind to. + /// The local address is not available. + /// + /// POSIX equivalent: EADDRNOTAVAIL address-not-bindable, - /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available. + /// A bind operation failed because the provided address is already in + /// use or because there are no ephemeral ports available. + /// + /// POSIX equivalent: EADDRINUSE address-in-use, - /// The remote address is not reachable + /// The remote address is not reachable. + /// + /// POSIX equivalent: EHOSTUNREACH, EHOSTDOWN, ENETDOWN, ENETUNREACH, ENONET remote-unreachable, - /// The TCP connection was forcefully rejected + /// The connection was forcefully rejected. + /// + /// POSIX equivalent: ECONNREFUSED connection-refused, - /// The TCP connection was reset. + /// A write failed because the connection was broken. + /// + /// POSIX equivalent: EPIPE + connection-broken, + /// The connection was reset. + /// + /// POSIX equivalent: ECONNRESET connection-reset, - /// A TCP connection was aborted. + /// The connection was aborted. + /// + /// POSIX equivalent: ECONNABORTED connection-aborted, /// The size of a datagram sent to a UDP socket exceeded the maximum /// supported size. + /// + /// POSIX equivalent: EMSGSIZE datagram-too-large, + /// A catch-all for errors not captured by the existing variants. + /// Implementations can use this to extend the error type without + /// breaking existing code. + other(option), } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) enum ip-address-family { /// Similar to `AF_INET` in POSIX. ipv4, @@ -65,19 +88,19 @@ interface types { ipv6, } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type ipv4-address = tuple; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type ipv6-address = tuple; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant ip-address { ipv4(ipv4-address), ipv6(ipv6-address), } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record ipv4-socket-address { /// sin_port port: u16, @@ -85,7 +108,7 @@ interface types { address: ipv4-address, } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record ipv6-socket-address { /// sin6_port port: u16, @@ -97,7 +120,7 @@ interface types { scope-id: u32, } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant ip-socket-address { ipv4(ipv4-socket-address), ipv6(ipv6-socket-address), @@ -112,39 +135,57 @@ interface types { /// - `connecting` /// - `connected` /// - `closed` - /// See + /// See /// for more information. /// /// Note: Except where explicitly mentioned, whenever this documentation uses /// the term "bound" without backticks it actually means: in the `bound` state *or higher*. /// (i.e. `bound`, `listening`, `connecting` or `connected`) /// + /// WASI uses shared ownership semantics: the `tcp-socket` handle and all + /// derived `stream` and `future` values reference a single underlying OS + /// socket: + /// - Send/receive streams remain functional after the original `tcp-socket` + /// handle is dropped. + /// - The stream returned by `listen` behaves similarly. + /// - Client sockets returned by `tcp-socket::listen` are independent and do + /// not keep the listening socket alive. + /// + /// The OS socket is closed only after the last handle is dropped. This + /// model has observable effects; for example, it affects when the local + /// port binding is released. + /// /// In addition to the general error codes documented on the /// `types::error-code` type, TCP socket methods may always return /// `error(invalid-state)` when in the `closed` state. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource tcp-socket { /// Create a new TCP socket. /// - /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. - /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` + /// in POSIX. On IPv6 sockets, IPV6_V6ONLY is enabled by default and + /// can't be configured otherwise. /// /// Unlike POSIX, WASI sockets have no notion of a socket-level /// `O_NONBLOCK` flag. Instead they fully rely on the Component Model's /// async support. /// + /// # Typical errors + /// - `not-supported`: The `address-family` is not supported. (EAFNOSUPPORT) + /// /// # References /// - /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) create: static func(address-family: ip-address-family) -> result; /// Bind the socket to the provided IP address and port. /// - /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which - /// network interface(s) to bind to. - /// If the TCP/UDP port is zero, the socket will be bound to a random free port. + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is + /// left to the implementation to decide which network interface(s) to + /// bind to. If the TCP/UDP port is zero, the socket will be bound to a + /// random free port. /// /// Bind can be attempted multiple times on the same socket, even with /// different arguments on each iteration. But never concurrently and @@ -161,21 +202,27 @@ interface types { /// - `address-not-bindable`: `local-address` is not an address that can be bound to. (EADDRNOTAVAIL) /// /// # Implementors note - /// When binding to a non-zero port, this bind operation shouldn't be affected by the TIME_WAIT - /// state of a recently closed socket on the same local address. In practice this means that the SO_REUSEADDR - /// socket option should be set implicitly on all platforms, except on Windows where this is the default behavior - /// and SO_REUSEADDR performs something different entirely. + /// The bind operation shouldn't be affected by the TIME_WAIT state of a + /// recently closed socket on the same local address. In practice this + /// means that the SO_REUSEADDR socket option should be set implicitly + /// on all platforms, except on Windows where this is the default + /// behavior and SO_REUSEADDR performs something different. /// /// # References /// - /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) bind: func(local-address: ip-socket-address) -> result<_, error-code>; /// Connect to a remote endpoint. /// - /// On success, the socket is transitioned into the `connected` state and this function returns a connection resource. + /// On success, the socket is transitioned into the `connected` state + /// and the `remote-address` of the socket is updated. + /// The `local-address` may be updated as well, based on the best network + /// path to `remote-address`. If the socket was not already explicitly + /// bound, this function will implicitly bind the socket to a random + /// free port. /// /// After a failed connection attempt, the socket will be in the `closed` /// state and the only valid action left is to `drop` the socket. A single @@ -202,7 +249,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) connect: async func(remote-address: ip-socket-address) -> result<_, error-code>; /// Start listening and return a stream of new inbound connections. /// @@ -274,7 +321,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) listen: func() -> result, error-code>; /// Transmit data to peer. /// @@ -288,6 +335,8 @@ interface types { /// /// # Typical errors /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN) + /// - `invalid-state`: `send` has already been called on this socket. + /// - `connection-broken`: The connection is not writable anymore. (EPIPE, ECONNABORTED on Windows) /// - `connection-reset`: The connection was reset. (ECONNRESET) /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) /// @@ -296,31 +345,27 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) send: func(data: stream) -> future>; /// Read data from peer. /// - /// This function returns a `stream` which provides the data received from the - /// socket, and a `future` providing additional error information in case the - /// socket is closed abnormally. + /// Returns a `stream` of data sent by the peer. The implementation + /// drops the stream once no more data is available. At that point, the + /// returned `future` resolves to: + /// - `ok` after a graceful shutdown from the peer (i.e. a FIN packet), or + /// - `err` if the socket was closed abnormally. /// - /// If the socket is closed normally, `stream.read` on the `stream` will return - /// `read-status::closed` with no `error-context` and the future resolves to - /// the value `ok`. If the socket is closed abnormally, `stream.read` on the - /// `stream` returns `read-status::closed` with an `error-context` and the future - /// resolves to `err` with an `error-code`. + /// `receive` may be called only once per socket. Subsequent calls return + /// a closed stream and a future resolved to `err(invalid-state)`. /// - /// `receive` is meant to be called only once per socket. If it is called more - /// than once, the subsequent calls return a new `stream` that fails as if it - /// were closed abnormally. - /// - /// If the caller is not expecting to receive any data from the peer, - /// they may drop the stream. Any data still in the receive queue + /// If the caller is not expecting to receive any more data from the peer, + /// they should drop the stream. Any data still in the receive queue /// will be discarded. This is equivalent to calling `shutdown(SHUT_RD)` /// in POSIX. /// /// # Typical errors /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN) + /// - `invalid-state`: `receive` has already been called on this socket. /// - `connection-reset`: The connection was reset. (ECONNRESET) /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) /// @@ -329,7 +374,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) receive: func() -> tuple, future>>; /// Get the bound local address. /// @@ -337,7 +382,8 @@ interface types { /// > If the socket has not been bound to a local name, the value /// > stored in the object pointed to by `address` is unspecified. /// - /// WASI is stricter and requires `get-local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// WASI is stricter and requires `get-local-address` to return + /// `invalid-state` when the socket hasn't been bound yet. /// /// # Typical errors /// - `invalid-state`: The socket is not bound to any local address. @@ -347,7 +393,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-local-address: func() -> result; /// Get the remote address. /// @@ -359,30 +405,32 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-remote-address: func() -> result; /// Whether the socket is in the `listening` state. /// /// Equivalent to the SO_ACCEPTCONN socket option. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-is-listening: func() -> bool; /// Whether this is a IPv4 or IPv6 socket. /// /// This is the value passed to the constructor. /// /// Equivalent to the SO_DOMAIN socket option. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-address-family: func() -> ip-address-family; - /// Hints the desired listen queue size. Implementations are free to ignore this. + /// Hints the desired listen queue size. Implementations are free to + /// ignore this. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// Any other value will never cause an error, but it might be silently + /// clamped and/or rounded. /// /// # Typical errors /// - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen. /// - `invalid-argument`: (set) The provided value was 0. /// - `invalid-state`: (set) The socket is in the `connecting` or `connected` state. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-listen-backlog-size: func(value: u64) -> result<_, error-code>; /// Enables or disables keepalive. /// @@ -390,54 +438,60 @@ interface types { /// - `keep-alive-idle-time` /// - `keep-alive-interval` /// - `keep-alive-count` - /// These properties can be configured while `keep-alive-enabled` is false, but only come into effect when `keep-alive-enabled` is true. + /// These properties can be configured while `keep-alive-enabled` is + /// false, but only come into effect when `keep-alive-enabled` is true. /// /// Equivalent to the SO_KEEPALIVE socket option. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-keep-alive-enabled: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-keep-alive-enabled: func(value: bool) -> result<_, error-code>; - /// Amount of time the connection has to be idle before TCP starts sending keepalive packets. + /// Amount of time the connection has to be idle before TCP starts + /// sending keepalive packets. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. /// /// Equivalent to the TCP_KEEPIDLE socket option. (TCP_KEEPALIVE on MacOS) /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-keep-alive-idle-time: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>; /// The time between keepalive packets. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. /// /// Equivalent to the TCP_KEEPINTVL socket option. /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-keep-alive-interval: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-keep-alive-interval: func(value: duration) -> result<_, error-code>; - /// The maximum amount of keepalive packets TCP should send before aborting the connection. + /// The maximum amount of keepalive packets TCP should send before + /// aborting the connection. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. /// /// Equivalent to the TCP_KEEPCNT socket option. /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-keep-alive-count: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-keep-alive-count: func(value: u32) -> result<_, error-code>; /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. /// @@ -445,37 +499,49 @@ interface types { /// /// # Typical errors /// - `invalid-argument`: (set) The TTL value must be 1 or higher. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-hop-limit: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-hop-limit: func(value: u8) -> result<_, error-code>; - /// The kernel buffer space reserved for sends/receives on this socket. + /// Kernel buffer space reserved for sending/receiving on this socket. + /// Implementations usually treat this as a cap the buffer can grow to, + /// rather than allocating the full amount immediately. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. + /// + /// This is only a performance hint. The implementation may ignore it or + /// tweak it based on real traffic patterns. + /// Linux and macOS appear to behave differently depending on whether a + /// buffer size was explicitly set. When set, they tend to honor it; when + /// not set, they dynamically adjust the buffer size as the connection + /// progresses. This is especially noticeable when comparing the values + /// from before and after connection establishment. /// /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-receive-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-receive-buffer-size: func(value: u64) -> result<_, error-code>; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-send-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-send-buffer-size: func(value: u64) -> result<_, error-code>; } /// A UDP socket handle. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource udp-socket { /// Create a new UDP socket. /// - /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. - /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` + /// in POSIX. On IPv6 sockets, IPV6_V6ONLY is enabled by default and + /// can't be configured otherwise. /// /// Unlike POSIX, WASI sockets have no notion of a socket-level /// `O_NONBLOCK` flag. Instead they fully rely on the Component Model's @@ -486,13 +552,14 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) create: static func(address-family: ip-address-family) -> result; /// Bind the socket to the provided IP address and port. /// - /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which - /// network interface(s) to bind to. - /// If the port is zero, the socket will be bound to a random free port. + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is + /// left to the implementation to decide which network interface(s) to + /// bind to. If the port is zero, the socket will be bound to a random + /// free port. /// /// # Typical errors /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) @@ -506,7 +573,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) bind: func(local-address: ip-socket-address) -> result<_, error-code>; /// Associate this socket with a specific peer address. /// @@ -544,12 +611,12 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) connect: func(remote-address: ip-socket-address) -> result<_, error-code>; /// Dissociate this socket from its peer address. /// /// After calling this method, `send` & `receive` are free to communicate - /// with any address again. + /// with any remote address again. /// /// The POSIX equivalent of this is calling `connect` with an `AF_UNSPEC` address. /// @@ -561,7 +628,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) disconnect: func() -> result<_, error-code>; /// Send a message on the socket to a particular peer. /// @@ -573,6 +640,9 @@ interface types { /// _may_ be provided but then it must be identical to the address /// passed to `connect`. /// + /// If the socket has not been explicitly bound, it will be + /// implicitly bound to a random free port. + /// /// Implementations may trap if the `data` length exceeds 64 KiB. /// /// # Typical errors @@ -584,6 +654,14 @@ interface types { /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) /// - `connection-refused`: The connection was refused. (ECONNREFUSED) /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) + /// + /// # Implementors note + /// WASI requires `send` to perform an implicit bind if the socket + /// has not been bound. Not all platforms (notably Windows) exhibit + /// this behavior natively. On such platforms, the WASI implementation + /// should emulate it by performing the bind if the guest has not + /// already done so. /// /// # References /// - @@ -594,7 +672,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) send: async func(data: list, remote-address: option) -> result<_, error-code>; /// Receive a message on the socket. /// @@ -619,7 +697,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) receive: async func() -> result, ip-socket-address>, error-code>; /// Get the current bound address. /// @@ -627,7 +705,8 @@ interface types { /// > If the socket has not been bound to a local name, the value /// > stored in the object pointed to by `address` is unspecified. /// - /// WASI is stricter and requires `get-local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// WASI is stricter and requires `get-local-address` to return + /// `invalid-state` when the socket hasn't been bound yet. /// /// # Typical errors /// - `invalid-state`: The socket is not bound to any local address. @@ -637,7 +716,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-local-address: func() -> result; /// Get the address the socket is currently "connected" to. /// @@ -649,14 +728,14 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-remote-address: func() -> result; /// Whether this is a IPv4 or IPv6 socket. /// /// This is the value passed to the constructor. /// /// Equivalent to the SO_DOMAIN socket option. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-address-family: func() -> ip-address-family; /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. /// @@ -664,41 +743,42 @@ interface types { /// /// # Typical errors /// - `invalid-argument`: (set) The TTL value must be 1 or higher. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-unicast-hop-limit: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; - /// The kernel buffer space reserved for sends/receives on this socket. + /// Kernel buffer space reserved for sending/receiving on this socket. + /// Implementations usually treat this as a cap the buffer can grow to, + /// rather than allocating the full amount immediately. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. /// /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-receive-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-receive-buffer-size: func(value: u64) -> result<_, error-code>; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-send-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-send-buffer-size: func(value: u64) -> result<_, error-code>; } } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface ip-name-lookup { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use types.{ip-address}; /// Lookup error codes. - @since(version = 0.3.0-rc-2026-02-09) - enum error-code { - /// Unknown error - unknown, + @since(version = 0.3.0-rc-2026-03-15) + variant error-code { /// Access denied. /// /// POSIX equivalent: EACCES, EPERM @@ -719,13 +799,17 @@ interface ip-name-lookup { /// /// POSIX equivalent: EAI_FAIL permanent-resolver-failure, + /// A catch-all for errors not captured by the existing variants. + /// Implementations can use this to extend the error type without + /// breaking existing code. + other(option), } /// Resolve an internet host name to a list of IP addresses. /// - /// Unicode domain names are automatically converted to ASCII using IDNA encoding. - /// If the input is an IP address string, the address is parsed and returned - /// as-is without making any external requests. + /// Unicode domain names are automatically converted to ASCII using IDNA + /// encoding. If the input is an IP address string, the address is parsed + /// and returned as-is without making any external requests. /// /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. /// @@ -735,24 +819,21 @@ interface ip-name-lookup { /// with at least one address. Additionally, this function never returns /// IPv4-mapped IPv6 addresses. /// - /// The returned future will resolve to an error code in case of failure. - /// It will resolve to success once the returned stream is exhausted. - /// /// # References: /// - /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resolve-addresses: async func(name: string) -> result, error-code>; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) - import wasi:clocks/types@0.3.0-rc-2026-02-09; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) + import wasi:clocks/types@0.3.0-rc-2026-03-15; + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import ip-name-lookup; }