diff --git a/crates/test-programs/src/bin/preview2_tcp_bind.rs b/crates/test-programs/src/bin/preview2_tcp_bind.rs index 8523dbe475df..5cc54022e460 100644 --- a/crates/test-programs/src/bin/preview2_tcp_bind.rs +++ b/crates/test-programs/src/bin/preview2_tcp_bind.rs @@ -138,23 +138,11 @@ fn test_tcp_bind_dual_stack(net: &Network) { let sock = TcpSocket::new(IpAddressFamily::Ipv6).unwrap(); let addr = IpSocketAddress::new(IpAddress::IPV4_MAPPED_LOOPBACK, 0); - // Even on platforms that don't support dualstack sockets, - // setting ipv6_only to true (disabling dualstack mode) should work. - sock.set_ipv6_only(true).unwrap(); - // Binding an IPv4-mapped-IPv6 address on a ipv6-only socket should fail: assert!(matches!( sock.blocking_bind(net, addr), Err(ErrorCode::InvalidArgument) )); - - sock.set_ipv6_only(false).unwrap(); - - sock.blocking_bind(net, addr).unwrap(); - - let bound_addr = sock.local_address().unwrap(); - - assert_eq!(bound_addr.family(), IpAddressFamily::Ipv6); } fn main() { diff --git a/crates/test-programs/src/bin/preview2_tcp_connect.rs b/crates/test-programs/src/bin/preview2_tcp_connect.rs index f304e46a5d5d..d840cc5934ff 100644 --- a/crates/test-programs/src/bin/preview2_tcp_connect.rs +++ b/crates/test-programs/src/bin/preview2_tcp_connect.rs @@ -83,23 +83,16 @@ fn test_tcp_connect_dual_stack(net: &Network) { // Tests: - // Even on platforms that don't support dualstack sockets, - // setting ipv6_only to true (disabling dualstack mode) should work. - v6_client.set_ipv6_only(true).unwrap(); - - // Connecting to an IPv4-mapped-IPv6 address on an ipv6-only socket should fail: + // Connecting to an IPv4 address on an IPv6 socket should fail: + assert!(matches!( + v6_client.blocking_connect(net, v4_listener_addr), + Err(ErrorCode::InvalidArgument) + )); + // Connecting to an IPv4-mapped-IPv6 address on an IPv6 socket should fail: assert!(matches!( v6_client.blocking_connect(net, v6_listener_addr), Err(ErrorCode::InvalidArgument) )); - - v6_client.set_ipv6_only(false).unwrap(); - - v6_client.blocking_connect(net, v6_listener_addr).unwrap(); - - let connected_addr = v6_client.local_address().unwrap(); - - assert_eq!(connected_addr.family(), IpAddressFamily::Ipv6); } fn main() { diff --git a/crates/test-programs/src/bin/preview2_tcp_sockopts.rs b/crates/test-programs/src/bin/preview2_tcp_sockopts.rs index 8bc1ef49bca8..d52f7c8f902d 100644 --- a/crates/test-programs/src/bin/preview2_tcp_sockopts.rs +++ b/crates/test-programs/src/bin/preview2_tcp_sockopts.rs @@ -10,10 +10,6 @@ fn test_tcp_sockopt_defaults(family: IpAddressFamily) { assert_eq!(sock.address_family(), family); - if family == IpAddressFamily::Ipv6 { - sock.ipv6_only().unwrap(); // Only verify that it has a default value at all, but either value is valid. - } - sock.keep_alive_enabled().unwrap(); // Only verify that it has a default value at all, but either value is valid. assert!(sock.keep_alive_idle_time().unwrap() > 0); assert!(sock.keep_alive_interval().unwrap() > 0); @@ -26,11 +22,6 @@ fn test_tcp_sockopt_defaults(family: IpAddressFamily) { fn test_tcp_sockopt_input_ranges(family: IpAddressFamily) { let sock = TcpSocket::new(family).unwrap(); - if family == IpAddressFamily::Ipv6 { - assert!(matches!(sock.set_ipv6_only(true), Ok(_))); - assert!(matches!(sock.set_ipv6_only(false), Ok(_))); - } - assert!(matches!( sock.set_listen_backlog_size(0), Err(ErrorCode::InvalidArgument) @@ -90,13 +81,6 @@ fn test_tcp_sockopt_input_ranges(family: IpAddressFamily) { fn test_tcp_sockopt_readback(family: IpAddressFamily) { let sock = TcpSocket::new(family).unwrap(); - if family == IpAddressFamily::Ipv6 { - sock.set_ipv6_only(true).unwrap(); - assert_eq!(sock.ipv6_only().unwrap(), true); - sock.set_ipv6_only(false).unwrap(); - assert_eq!(sock.ipv6_only().unwrap(), false); - } - sock.set_keep_alive_enabled(true).unwrap(); assert_eq!(sock.keep_alive_enabled().unwrap(), true); sock.set_keep_alive_enabled(false).unwrap(); @@ -125,15 +109,10 @@ fn test_tcp_sockopt_inheritance(net: &Network, family: IpAddressFamily) { let bind_addr = IpSocketAddress::new(IpAddress::new_loopback(family), 0); let listener = TcpSocket::new(family).unwrap(); - let default_ipv6_only = listener.ipv6_only().unwrap_or(false); let default_keep_alive = listener.keep_alive_enabled().unwrap(); // Configure options on listener: { - if family == IpAddressFamily::Ipv6 { - listener.set_ipv6_only(!default_ipv6_only).unwrap(); - } - listener .set_keep_alive_enabled(!default_keep_alive) .unwrap(); @@ -154,10 +133,6 @@ fn test_tcp_sockopt_inheritance(net: &Network, family: IpAddressFamily) { // Verify options on accepted socket: { - if family == IpAddressFamily::Ipv6 { - assert_eq!(accepted_client.ipv6_only().unwrap(), !default_ipv6_only); - } - assert_eq!( accepted_client.keep_alive_enabled().unwrap(), !default_keep_alive diff --git a/crates/test-programs/src/bin/preview2_tcp_states.rs b/crates/test-programs/src/bin/preview2_tcp_states.rs index 308c6cfd6c2d..4d31e36cf0c7 100644 --- a/crates/test-programs/src/bin/preview2_tcp_states.rs +++ b/crates/test-programs/src/bin/preview2_tcp_states.rs @@ -35,20 +35,6 @@ fn test_tcp_unbound_state_invariants(family: IpAddressFamily) { assert_eq!(sock.is_listening(), false); assert_eq!(sock.address_family(), family); - if family == IpAddressFamily::Ipv6 { - assert!(matches!(sock.ipv6_only(), Ok(_))); - - // Even on platforms that don't support dualstack sockets, - // setting ipv6_only to true (disabling dualstack mode) should work. - assert!(matches!(sock.set_ipv6_only(true), Ok(_))); - } else { - assert!(matches!(sock.ipv6_only(), Err(ErrorCode::NotSupported))); - assert!(matches!( - sock.set_ipv6_only(true), - Err(ErrorCode::NotSupported) - )); - } - assert!(matches!(sock.set_listen_backlog_size(32), Ok(_))); assert!(matches!(sock.keep_alive_enabled(), Ok(_))); assert!(matches!(sock.set_keep_alive_enabled(false), Ok(_))); @@ -100,20 +86,6 @@ fn test_tcp_bound_state_invariants(net: &Network, family: IpAddressFamily) { assert_eq!(sock.is_listening(), false); assert_eq!(sock.address_family(), family); - if family == IpAddressFamily::Ipv6 { - assert!(matches!(sock.ipv6_only(), Ok(_))); - assert!(matches!( - sock.set_ipv6_only(true), - Err(ErrorCode::InvalidState) - )); - } else { - assert!(matches!(sock.ipv6_only(), Err(ErrorCode::NotSupported))); - assert!(matches!( - sock.set_ipv6_only(true), - Err(ErrorCode::NotSupported) - )); - } - assert!(matches!(sock.set_listen_backlog_size(32), Ok(_))); assert!(matches!(sock.keep_alive_enabled(), Ok(_))); assert!(matches!(sock.set_keep_alive_enabled(false), Ok(_))); @@ -169,20 +141,6 @@ fn test_tcp_listening_state_invariants(net: &Network, family: IpAddressFamily) { assert_eq!(sock.is_listening(), true); assert_eq!(sock.address_family(), family); - if family == IpAddressFamily::Ipv6 { - assert!(matches!(sock.ipv6_only(), Ok(_))); - assert!(matches!( - sock.set_ipv6_only(true), - Err(ErrorCode::InvalidState) - )); - } else { - assert!(matches!(sock.ipv6_only(), Err(ErrorCode::NotSupported))); - assert!(matches!( - sock.set_ipv6_only(true), - Err(ErrorCode::NotSupported) - )); - } - assert!(matches!( sock.set_listen_backlog_size(32), Ok(_) | Err(ErrorCode::NotSupported) @@ -238,20 +196,6 @@ fn test_tcp_connected_state_invariants(net: &Network, family: IpAddressFamily) { assert_eq!(sock.is_listening(), false); assert_eq!(sock.address_family(), family); - if family == IpAddressFamily::Ipv6 { - assert!(matches!(sock.ipv6_only(), Ok(_))); - assert!(matches!( - sock.set_ipv6_only(true), - Err(ErrorCode::InvalidState) - )); - } else { - assert!(matches!(sock.ipv6_only(), Err(ErrorCode::NotSupported))); - assert!(matches!( - sock.set_ipv6_only(true), - Err(ErrorCode::NotSupported) - )); - } - assert!(matches!(sock.keep_alive_enabled(), Ok(_))); assert!(matches!(sock.set_keep_alive_enabled(false), Ok(_))); assert!(matches!(sock.keep_alive_idle_time(), Ok(_))); diff --git a/crates/test-programs/src/bin/preview2_udp_bind.rs b/crates/test-programs/src/bin/preview2_udp_bind.rs index f320a043026b..8b366bcf9e3e 100644 --- a/crates/test-programs/src/bin/preview2_udp_bind.rs +++ b/crates/test-programs/src/bin/preview2_udp_bind.rs @@ -75,23 +75,11 @@ fn test_udp_bind_dual_stack(net: &Network) { let sock = UdpSocket::new(IpAddressFamily::Ipv6).unwrap(); let addr = IpSocketAddress::new(IpAddress::IPV4_MAPPED_LOOPBACK, 0); - // Even on platforms that don't support dualstack sockets, - // setting ipv6_only to true (disabling dualstack mode) should work. - sock.set_ipv6_only(true).unwrap(); - // Binding an IPv4-mapped-IPv6 address on a ipv6-only socket should fail: assert!(matches!( sock.blocking_bind(net, addr), Err(ErrorCode::InvalidArgument) )); - - sock.set_ipv6_only(false).unwrap(); - - sock.blocking_bind(net, addr).unwrap(); - - let bound_addr = sock.local_address().unwrap(); - - assert_eq!(bound_addr.family(), IpAddressFamily::Ipv6); } fn main() { diff --git a/crates/test-programs/src/bin/preview2_udp_connect.rs b/crates/test-programs/src/bin/preview2_udp_connect.rs index 7e21c7e7914a..7ce1ad56f689 100644 --- a/crates/test-programs/src/bin/preview2_udp_connect.rs +++ b/crates/test-programs/src/bin/preview2_udp_connect.rs @@ -88,38 +88,21 @@ fn test_udp_connect_dual_stack(net: &Network) { IpSocketAddress::new(IpAddress::IPV4_MAPPED_LOOPBACK, v4_server_addr.port()); // Tests: - { - let v6_client = UdpSocket::new(IpAddressFamily::Ipv6).unwrap(); - - // Even on platforms that don't support dualstack sockets, - // setting ipv6_only to true (disabling dualstack mode) should work. - v6_client.set_ipv6_only(true).unwrap(); - - v6_client.blocking_bind_unspecified(&net).unwrap(); - - // Connecting to an IPv4-mapped-IPv6 address on an ipv6-only socket should fail: - assert!(matches!( - v6_client.stream(Some(v6_server_addr)), - Err(ErrorCode::InvalidArgument) - )); - } - - { - let v6_client = UdpSocket::new(IpAddressFamily::Ipv6).unwrap(); - - v6_client.set_ipv6_only(false).unwrap(); - v6_client.blocking_bind_unspecified(&net).unwrap(); - v6_client.stream(Some(v6_server_addr)).unwrap(); - - assert_eq!( - v6_client.local_address().unwrap().family(), - IpAddressFamily::Ipv6 - ); - assert_eq!( - v6_client.remote_address().unwrap().family(), - IpAddressFamily::Ipv6 - ); - } + let v6_client = UdpSocket::new(IpAddressFamily::Ipv6).unwrap(); + + v6_client.blocking_bind_unspecified(&net).unwrap(); + + // Connecting to an IPv4 address on an IPv6 socket should fail: + assert!(matches!( + v6_client.stream(Some(v4_server_addr)), + Err(ErrorCode::InvalidArgument) + )); + + // Connecting to an IPv4-mapped-IPv6 address on an IPv6 socket should fail: + assert!(matches!( + v6_client.stream(Some(v6_server_addr)), + Err(ErrorCode::InvalidArgument) + )); } fn main() { diff --git a/crates/test-programs/src/bin/preview2_udp_sample_application.rs b/crates/test-programs/src/bin/preview2_udp_sample_application.rs index 171258e5e188..ca001769f69c 100644 --- a/crates/test-programs/src/bin/preview2_udp_sample_application.rs +++ b/crates/test-programs/src/bin/preview2_udp_sample_application.rs @@ -72,52 +72,6 @@ fn test_udp_sample_application(family: IpAddressFamily, bind_address: IpSocketAd } } -fn test_udp_dual_stack_conversation() { - let net = Network::default(); - - let v4_server = UdpSocket::new(IpAddressFamily::Ipv4).unwrap(); - v4_server - .blocking_bind(&net, IpSocketAddress::new(IpAddress::IPV4_LOOPBACK, 0)) - .unwrap(); - let (server_incoming, server_outgoing) = v4_server.stream(None).unwrap(); - - let v4_server_addr = v4_server.local_address().unwrap(); - let v6_server_addr = - IpSocketAddress::new(IpAddress::IPV4_MAPPED_LOOPBACK, v4_server_addr.port()); - - let v6_client = UdpSocket::new(IpAddressFamily::Ipv6).unwrap(); - - v6_client.set_ipv6_only(false).unwrap(); - v6_client.blocking_bind_unspecified(&net).unwrap(); - let (client_incoming, client_outgoing) = v6_client.stream(None).unwrap(); - - // Send from v6 client to v4 server: - client_outgoing - .blocking_send(&[OutgoingDatagram { - data: "Hi!".into(), - remote_address: Some(v6_server_addr), - }]) - .unwrap(); - - // Receive from v6 client on v4 server: - let results = server_incoming.blocking_receive(1..1).unwrap(); - let msg = results.first().unwrap(); - assert_eq!(msg.remote_address.family(), IpAddressFamily::Ipv4); - - // Send from v4 server to v6 client: - server_outgoing - .blocking_send(&[OutgoingDatagram { - data: msg.data.clone(), - remote_address: Some(msg.remote_address), - }]) - .unwrap(); - - // Receive from v4 server on v6 client: - let results = client_incoming.blocking_receive(1..1).unwrap(); - let msg = results.first().unwrap(); - assert_eq!(msg.remote_address.family(), IpAddressFamily::Ipv6); -} - fn main() { test_udp_sample_application( IpAddressFamily::Ipv4, @@ -135,6 +89,4 @@ fn main() { scope_id: 0, }), ); - - test_udp_dual_stack_conversation(); } diff --git a/crates/test-programs/src/bin/preview2_udp_sockopts.rs b/crates/test-programs/src/bin/preview2_udp_sockopts.rs index f705b476c36e..893789b508b4 100644 --- a/crates/test-programs/src/bin/preview2_udp_sockopts.rs +++ b/crates/test-programs/src/bin/preview2_udp_sockopts.rs @@ -6,10 +6,6 @@ fn test_udp_sockopt_defaults(family: IpAddressFamily) { assert_eq!(sock.address_family(), family); - if family == IpAddressFamily::Ipv6 { - sock.ipv6_only().unwrap(); // Only verify that it has a default value at all, but either value is valid. - } - assert!(sock.unicast_hop_limit().unwrap() > 0); assert!(sock.receive_buffer_size().unwrap() > 0); assert!(sock.send_buffer_size().unwrap() > 0); @@ -18,11 +14,6 @@ fn test_udp_sockopt_defaults(family: IpAddressFamily) { fn test_udp_sockopt_input_ranges(family: IpAddressFamily) { let sock = UdpSocket::new(family).unwrap(); - if family == IpAddressFamily::Ipv6 { - assert!(matches!(sock.set_ipv6_only(true), Ok(_))); - assert!(matches!(sock.set_ipv6_only(false), Ok(_))); - } - assert!(matches!( sock.set_unicast_hop_limit(0), Err(ErrorCode::InvalidArgument) @@ -47,13 +38,6 @@ fn test_udp_sockopt_input_ranges(family: IpAddressFamily) { fn test_udp_sockopt_readback(family: IpAddressFamily) { let sock = UdpSocket::new(family).unwrap(); - if family == IpAddressFamily::Ipv6 { - sock.set_ipv6_only(true).unwrap(); - assert_eq!(sock.ipv6_only().unwrap(), true); - sock.set_ipv6_only(false).unwrap(); - assert_eq!(sock.ipv6_only().unwrap(), false); - } - sock.set_unicast_hop_limit(42).unwrap(); assert_eq!(sock.unicast_hop_limit().unwrap(), 42); diff --git a/crates/test-programs/src/bin/preview2_udp_states.rs b/crates/test-programs/src/bin/preview2_udp_states.rs index 4081b971e8d7..314f94a25bd8 100644 --- a/crates/test-programs/src/bin/preview2_udp_states.rs +++ b/crates/test-programs/src/bin/preview2_udp_states.rs @@ -18,20 +18,6 @@ fn test_udp_unbound_state_invariants(family: IpAddressFamily) { )); assert_eq!(sock.address_family(), family); - if family == IpAddressFamily::Ipv6 { - assert!(matches!(sock.ipv6_only(), Ok(_))); - - // Even on platforms that don't support dualstack sockets, - // setting ipv6_only to true (disabling dualstack mode) should work. - assert!(matches!(sock.set_ipv6_only(true), Ok(_))); - } else { - assert!(matches!(sock.ipv6_only(), Err(ErrorCode::NotSupported))); - assert!(matches!( - sock.set_ipv6_only(true), - Err(ErrorCode::NotSupported) - )); - } - assert!(matches!(sock.unicast_hop_limit(), Ok(_))); assert!(matches!(sock.set_unicast_hop_limit(255), Ok(_))); assert!(matches!(sock.receive_buffer_size(), Ok(_))); @@ -59,20 +45,6 @@ fn test_udp_bound_state_invariants(net: &Network, family: IpAddressFamily) { )); assert_eq!(sock.address_family(), family); - if family == IpAddressFamily::Ipv6 { - assert!(matches!(sock.ipv6_only(), Ok(_))); - assert!(matches!( - sock.set_ipv6_only(true), - Err(ErrorCode::InvalidState) - )); - } else { - assert!(matches!(sock.ipv6_only(), Err(ErrorCode::NotSupported))); - assert!(matches!( - sock.set_ipv6_only(true), - Err(ErrorCode::NotSupported) - )); - } - assert!(matches!(sock.unicast_hop_limit(), Ok(_))); assert!(matches!(sock.set_unicast_hop_limit(255), Ok(_))); assert!(matches!(sock.receive_buffer_size(), Ok(_))); @@ -99,20 +71,6 @@ fn test_udp_connected_state_invariants(net: &Network, family: IpAddressFamily) { assert!(matches!(sock.remote_address(), Ok(_))); assert_eq!(sock.address_family(), family); - if family == IpAddressFamily::Ipv6 { - assert!(matches!(sock.ipv6_only(), Ok(_))); - assert!(matches!( - sock.set_ipv6_only(true), - Err(ErrorCode::InvalidState) - )); - } else { - assert!(matches!(sock.ipv6_only(), Err(ErrorCode::NotSupported))); - assert!(matches!( - sock.set_ipv6_only(true), - Err(ErrorCode::NotSupported) - )); - } - assert!(matches!(sock.unicast_hop_limit(), Ok(_))); assert!(matches!(sock.set_unicast_hop_limit(255), Ok(_))); assert!(matches!(sock.receive_buffer_size(), Ok(_))); diff --git a/crates/wasi-http/src/lib.rs b/crates/wasi-http/src/lib.rs index acda4e7e9815..d331eb56b0a0 100644 --- a/crates/wasi-http/src/lib.rs +++ b/crates/wasi-http/src/lib.rs @@ -11,9 +11,9 @@ pub mod bindings { wasmtime::component::bindgen!({ path: "wit", interfaces: " - import wasi:http/incoming-handler@0.2.0-rc-2023-12-05; - import wasi:http/outgoing-handler@0.2.0-rc-2023-12-05; - import wasi:http/types@0.2.0-rc-2023-12-05; + import wasi:http/incoming-handler@0.2.0-rc-2024-01-16; + import wasi:http/outgoing-handler@0.2.0-rc-2024-01-16; + import wasi:http/types@0.2.0-rc-2024-01-16; ", tracing: true, async: false, diff --git a/crates/wasi-http/wit/command-extended.wit b/crates/wasi-http/wit/command-extended.wit index 16f01bc23d7a..2f1491ef99f6 100644 --- a/crates/wasi-http/wit/command-extended.wit +++ b/crates/wasi-http/wit/command-extended.wit @@ -1,6 +1,6 @@ // All of the same imports and exports available in the wasi:cli/command world // with addition of HTTP proxy related imports: world command-extended { - include wasi:cli/command@0.2.0-rc-2023-12-05; - import wasi:http/outgoing-handler@0.2.0-rc-2023-12-05; + include wasi:cli/command@0.2.0-rc-2024-01-16; + import wasi:http/outgoing-handler@0.2.0-rc-2024-01-16; } diff --git a/crates/wasi-http/wit/deps/cli/command.wit b/crates/wasi-http/wit/deps/cli/command.wit index cc82ae5dc5be..a9889164fe37 100644 --- a/crates/wasi-http/wit/deps/cli/command.wit +++ b/crates/wasi-http/wit/deps/cli/command.wit @@ -1,4 +1,4 @@ -package wasi:cli@0.2.0-rc-2023-12-05; +package wasi:cli@0.2.0-rc-2024-01-16; world command { include imports; diff --git a/crates/wasi-http/wit/deps/cli/imports.wit b/crates/wasi-http/wit/deps/cli/imports.wit index 9965ea35ec02..8ce1abec397c 100644 --- a/crates/wasi-http/wit/deps/cli/imports.wit +++ b/crates/wasi-http/wit/deps/cli/imports.wit @@ -1,9 +1,9 @@ -package wasi:cli@0.2.0-rc-2023-12-05; +package wasi:cli@0.2.0-rc-2024-01-16; world imports { include wasi:clocks/imports@0.2.0-rc-2023-11-10; include wasi:filesystem/imports@0.2.0-rc-2023-11-10; - include wasi:sockets/imports@0.2.0-rc-2023-11-10; + include wasi:sockets/imports@0.2.0-rc-2024-01-16; include wasi:random/imports@0.2.0-rc-2023-11-10; include wasi:io/imports@0.2.0-rc-2023-11-10; diff --git a/crates/wasi-http/wit/deps/cli/terminal.wit b/crates/wasi-http/wit/deps/cli/terminal.wit index 47495769b31f..38c724efc840 100644 --- a/crates/wasi-http/wit/deps/cli/terminal.wit +++ b/crates/wasi-http/wit/deps/cli/terminal.wit @@ -1,19 +1,21 @@ +/// Terminal input. +/// +/// 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. interface terminal-input { /// The input side of a terminal. resource terminal-input; - - // 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. } +/// Terminal output. +/// +/// In the future, this may include functions for querying the terminal +/// size, being notified of terminal size changes, querying supported +/// features, and so on. interface terminal-output { /// The output side of a terminal. resource terminal-output; - - // In the future, this may include functions for querying the terminal - // size, being notified of terminal size changes, querying supported - // features, and so on. } /// An interface providing an optional `terminal-input` for stdin as a diff --git a/crates/wasi-http/wit/deps/http/proxy.wit b/crates/wasi-http/wit/deps/http/proxy.wit index 0f466c93c1b0..2b0ce4b8a67a 100644 --- a/crates/wasi-http/wit/deps/http/proxy.wit +++ b/crates/wasi-http/wit/deps/http/proxy.wit @@ -1,4 +1,4 @@ -package wasi:http@0.2.0-rc-2023-12-05; +package wasi:http@0.2.0-rc-2024-01-16; /// The `wasi:http/proxy` world captures a widely-implementable intersection of /// hosts that includes HTTP forward and reverse proxies. Components targeting @@ -11,14 +11,14 @@ world proxy { /// Proxies have standard output and error streams which are expected to /// terminate in a developer-facing console provided by the host. - import wasi:cli/stdout@0.2.0-rc-2023-12-05; - import wasi:cli/stderr@0.2.0-rc-2023-12-05; + import wasi:cli/stdout@0.2.0-rc-2024-01-16; + import wasi:cli/stderr@0.2.0-rc-2024-01-16; /// TODO: this is a temporary workaround until component tooling is able to /// gracefully handle the absence of stdin. Hosts must return an eof stream /// for this import, which is what wasi-libc + tooling will do automatically /// when this import is properly removed. - import wasi:cli/stdin@0.2.0-rc-2023-12-05; + import wasi:cli/stdin@0.2.0-rc-2024-01-16; /// This is the default handler to use when user code simply wants to make an /// HTTP request (e.g., via `fetch()`). diff --git a/crates/wasi-http/wit/deps/sockets/network.wit b/crates/wasi-http/wit/deps/sockets/network.wit index 6bb07cd6faa7..9cadf0650a49 100644 --- a/crates/wasi-http/wit/deps/sockets/network.wit +++ b/crates/wasi-http/wit/deps/sockets/network.wit @@ -18,8 +18,6 @@ interface network { /// /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. enum error-code { - // ### GENERAL ERRORS ### - /// Unknown error unknown, @@ -64,9 +62,6 @@ interface network { would-block, - - // ### TCP & UDP SOCKET ERRORS ### - /// The operation is not valid in the socket's current state. invalid-state, @@ -83,24 +78,21 @@ interface network { remote-unreachable, - // ### TCP SOCKET ERRORS ### - - /// The connection was forcefully rejected + /// The TCP connection was forcefully rejected connection-refused, - /// The connection was reset. + /// The TCP connection was reset. connection-reset, - /// A connection was aborted. + /// A TCP connection was aborted. connection-aborted, - // ### UDP SOCKET ERRORS ### + /// The size of a datagram sent to a UDP socket exceeded the maximum + /// supported size. datagram-too-large, - // ### NAME LOOKUP ERRORS ### - /// Name does not exist or has no suitable associated IP addresses. name-unresolvable, @@ -128,15 +120,21 @@ interface network { } record ipv4-socket-address { - port: u16, // sin_port - address: ipv4-address, // sin_addr + /// sin_port + port: u16, + /// sin_addr + address: ipv4-address, } record ipv6-socket-address { - port: u16, // sin6_port - flow-info: u32, // sin6_flowinfo - address: ipv6-address, // sin6_addr - scope-id: u32, // sin6_scope_id + /// sin6_port + port: u16, + /// sin6_flowinfo + flow-info: u32, + /// sin6_addr + address: ipv6-address, + /// sin6_scope_id + scope-id: u32, } variant ip-socket-address { diff --git a/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit b/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit index 768a07c85021..c7ddf1f228ef 100644 --- a/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit +++ b/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit @@ -6,9 +6,10 @@ interface tcp-create-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. /// /// This function does not require a network capability handle. This is considered to be safe because - /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect` + /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. /// /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. diff --git a/crates/wasi-http/wit/deps/sockets/tcp.wit b/crates/wasi-http/wit/deps/sockets/tcp.wit index 976b272c00a8..e045a436a9f6 100644 --- a/crates/wasi-http/wit/deps/sockets/tcp.wit +++ b/crates/wasi-http/wit/deps/sockets/tcp.wit @@ -25,15 +25,12 @@ interface tcp { /// network interface(s) to bind to. /// If the TCP/UDP port is zero, the socket will be bound to a random free port. /// - /// When a socket is not explicitly bound, the first invocation to a listen or connect operation will - /// implicitly bind the socket. - /// /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. /// /// # Typical `start` errors /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) /// - `invalid-argument`: `local-address` is not a unicast address. (EINVAL) - /// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL) + /// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address. (EINVAL) /// - `invalid-state`: The socket is already bound. (EINVAL) /// /// # Typical `finish` errors @@ -45,8 +42,9 @@ interface tcp { /// /// # 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 (i.e. the SO_REUSEADDR socket - /// option should be set implicitly on platforms that require it). + /// 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. /// /// # References /// - @@ -62,20 +60,13 @@ interface tcp { /// - the socket is transitioned into the Connection state /// - a pair of streams is returned that can be used to read & write to the connection /// - /// POSIX mentions: - /// > If connect() fails, the state of the socket is unspecified. Conforming applications should - /// > close the file descriptor and create a new socket before attempting to reconnect. - /// - /// WASI prescribes the following behavior: - /// - If `connect` fails because an input/state validation error, the socket should remain usable. - /// - If a connection was actually attempted but failed, the socket should become unusable for further network communication. - /// Besides `drop`, any method after such a failure may return an error. + /// After a failed connection attempt, the only valid action left is to + /// `drop` the socket. A single socket can not be used to connect more than once. /// /// # Typical `start` errors /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) /// - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS) - /// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL, EADDRNOTAVAIL on Illumos) - /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) + /// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address. (EINVAL, EADDRNOTAVAIL on Illumos) /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) /// - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) /// - `invalid-argument`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. @@ -130,7 +121,6 @@ interface tcp { /// /// The returned socket is bound and in the Connection state. The following properties are inherited from the listener socket: /// - `address-family` - /// - `ipv6-only` /// - `keep-alive-enabled` /// - `keep-alive-idle-time` /// - `keep-alive-interval` @@ -195,17 +185,6 @@ interface tcp { /// Equivalent to the SO_DOMAIN socket option. address-family: func() -> ip-address-family; - /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// - /// Equivalent to the IPV6_V6ONLY socket option. - /// - /// # Typical errors - /// - `invalid-state`: (set) The socket is already bound. - /// - `not-supported`: (get/set) `this` socket is an IPv4 socket. - /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) - ipv6-only: func() -> result; - set-ipv6-only: func(value: bool) -> result<_, error-code>; - /// Hints the desired listen queue size. Implementations are free to ignore this. /// /// If the provided value is 0, an `invalid-argument` error is returned. @@ -304,12 +283,16 @@ interface tcp { /// Initiate a graceful shutdown. /// - /// - receive: the socket is not expecting to receive any more data from the peer. All subsequent read - /// operations on the `input-stream` associated with this socket will return an End Of Stream indication. - /// Any data still in the receive queue at time of calling `shutdown` will be discarded. - /// - send: the socket is not expecting to send any more data to the peer. All subsequent write - /// operations on the `output-stream` associated with this socket will return an error. - /// - both: same effect as receive & send combined. + /// - `receive`: The socket is not expecting to receive any data from + /// the peer. The `input-stream` associated with this socket will be + /// closed. Any data still in the receive queue at time of calling + /// this method will be discarded. + /// - `send`: The socket has no more data to send to the peer. The `output-stream` + /// associated with this socket will be closed and a FIN packet will be sent. + /// - `both`: Same effect as `receive` & `send` combined. + /// + /// This function is idempotent. Shutting a down a direction more than once + /// has no effect and returns `ok`. /// /// The shutdown function does not close (drop) the socket. /// diff --git a/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit b/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit index cc58234d8455..0482d1fe7350 100644 --- a/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit +++ b/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit @@ -6,6 +6,7 @@ interface udp-create-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. /// /// This function does not require a network capability handle. This is considered to be safe because /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind` is called, diff --git a/crates/wasi-http/wit/deps/sockets/udp.wit b/crates/wasi-http/wit/deps/sockets/udp.wit index c8dafadfcb21..ae82b97fc1a6 100644 --- a/crates/wasi-http/wit/deps/sockets/udp.wit +++ b/crates/wasi-http/wit/deps/sockets/udp.wit @@ -92,7 +92,6 @@ interface udp { /// /// # Typical errors /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) /// - `invalid-state`: The socket is not bound. @@ -142,17 +141,6 @@ interface udp { /// Equivalent to the SO_DOMAIN socket option. address-family: func() -> ip-address-family; - /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// - /// Equivalent to the IPV6_V6ONLY socket option. - /// - /// # Typical errors - /// - `not-supported`: (get/set) `this` socket is an IPv4 socket. - /// - `invalid-state`: (set) The socket is already bound. - /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) - ipv6-only: func() -> result; - set-ipv6-only: func(value: bool) -> result<_, error-code>; - /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. /// /// If the provided value is 0, an `invalid-argument` error is returned. @@ -248,7 +236,6 @@ interface udp { /// /// # Typical errors /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) /// - `invalid-argument`: The socket is in "connected" mode and `remote-address` is `some` value that does not match the address passed to `stream`. (EISCONN) diff --git a/crates/wasi-http/wit/deps/sockets/world.wit b/crates/wasi-http/wit/deps/sockets/world.wit index 49ad8d3d9fde..8588cc6c1678 100644 --- a/crates/wasi-http/wit/deps/sockets/world.wit +++ b/crates/wasi-http/wit/deps/sockets/world.wit @@ -1,4 +1,4 @@ -package wasi:sockets@0.2.0-rc-2023-11-10; +package wasi:sockets@0.2.0-rc-2024-01-16; world imports { import instance-network; diff --git a/crates/wasi-http/wit/test.wit b/crates/wasi-http/wit/test.wit index f3cc6151b9e2..15294b4aaeae 100644 --- a/crates/wasi-http/wit/test.wit +++ b/crates/wasi-http/wit/test.wit @@ -2,7 +2,7 @@ package wasmtime:wasi; // only used as part of `test-programs` world test-reactor { - include wasi:cli/imports@0.2.0-rc-2023-12-05; + include wasi:cli/imports@0.2.0-rc-2024-01-16; export add-strings: func(s: list) -> u32; export get-strings: func() -> list; @@ -16,7 +16,7 @@ world test-reactor { } world test-command { - include wasi:cli/imports@0.2.0-rc-2023-12-05; - import wasi:http/types@0.2.0-rc-2023-12-05; - import wasi:http/outgoing-handler@0.2.0-rc-2023-12-05; + include wasi:cli/imports@0.2.0-rc-2024-01-16; + import wasi:http/types@0.2.0-rc-2024-01-16; + import wasi:http/outgoing-handler@0.2.0-rc-2024-01-16; } diff --git a/crates/wasi-preview1-component-adapter/src/lib.rs b/crates/wasi-preview1-component-adapter/src/lib.rs index 8ef496d56441..bd1d94e403b1 100644 --- a/crates/wasi-preview1-component-adapter/src/lib.rs +++ b/crates/wasi-preview1-component-adapter/src/lib.rs @@ -87,9 +87,9 @@ pub mod bindings { import wasi:clocks/wall-clock@0.2.0-rc-2023-11-10; import wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10; import wasi:random/random@0.2.0-rc-2023-11-10; - import wasi:cli/stdout@0.2.0-rc-2023-12-05; - import wasi:cli/stderr@0.2.0-rc-2023-12-05; - import wasi:cli/stdin@0.2.0-rc-2023-12-05; + import wasi:cli/stdout@0.2.0-rc-2024-01-16; + import wasi:cli/stderr@0.2.0-rc-2024-01-16; + import wasi:cli/stdin@0.2.0-rc-2024-01-16; } "#, std_feature, @@ -98,7 +98,7 @@ pub mod bindings { }); } -#[export_name = "wasi:cli/run@0.2.0-rc-2023-12-05#run"] +#[export_name = "wasi:cli/run@0.2.0-rc-2024-01-16#run"] #[cfg(feature = "command")] pub unsafe extern "C" fn run() -> u32 { #[link(wasm_import_module = "__main_module__")] @@ -2665,7 +2665,7 @@ impl State { #[cfg(not(feature = "proxy"))] fn get_environment(&self) -> &[StrTuple] { if self.env_vars.get().is_none() { - #[link(wasm_import_module = "wasi:cli/environment@0.2.0-rc-2023-12-05")] + #[link(wasm_import_module = "wasi:cli/environment@0.2.0-rc-2024-01-16")] extern "C" { #[link_name = "get-environment"] fn get_environment_import(rval: *mut StrTupleList); @@ -2690,7 +2690,7 @@ impl State { #[cfg(not(feature = "proxy"))] fn get_args(&self) -> &[WasmStr] { if self.args.get().is_none() { - #[link(wasm_import_module = "wasi:cli/environment@0.2.0-rc-2023-12-05")] + #[link(wasm_import_module = "wasi:cli/environment@0.2.0-rc-2024-01-16")] extern "C" { #[link_name = "get-arguments"] fn get_args_import(rval: *mut WasmStrList); diff --git a/crates/wasi/src/preview2/host/network.rs b/crates/wasi/src/preview2/host/network.rs index 63a09e2939a4..31ef2cb61cc4 100644 --- a/crates/wasi/src/preview2/host/network.rs +++ b/crates/wasi/src/preview2/host/network.rs @@ -261,14 +261,14 @@ pub(crate) mod util { ) -> SocketResult<()> { match (socket_family, addr.ip()) { (SocketAddressFamily::Ipv4, IpAddr::V4(_)) => Ok(()), - (SocketAddressFamily::Ipv6 { v6only }, IpAddr::V6(ipv6)) => { + (SocketAddressFamily::Ipv6, IpAddr::V6(ipv6)) => { if is_deprecated_ipv4_compatible(&ipv6) { // Reject IPv4-*compatible* IPv6 addresses. They have been deprecated // since 2006, OS handling of them is inconsistent and our own // validations don't take them into account either. // Note that these are not the same as IPv4-*mapped* IPv6 addresses. Err(ErrorCode::InvalidArgument.into()) - } else if *v6only && ipv6.to_ipv4_mapped().is_some() { + } else if ipv6.to_ipv4_mapped().is_some() { Err(ErrorCode::InvalidArgument.into()) } else { Ok(()) diff --git a/crates/wasi/src/preview2/host/tcp.rs b/crates/wasi/src/preview2/host/tcp.rs index e9587af70b33..67bd04429c64 100644 --- a/crates/wasi/src/preview2/host/tcp.rs +++ b/crates/wasi/src/preview2/host/tcp.rs @@ -270,8 +270,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { } // For some reason, IP_TTL is inherited, but IPV6_UNICAST_HOPS isn't. - if let (SocketAddressFamily::Ipv6 { .. }, Some(ttl)) = (socket.family, socket.hop_limit) - { + if let (SocketAddressFamily::Ipv6, Some(ttl)) = (socket.family, socket.hop_limit) { _ = util::set_ipv6_unicast_hops(&client_fd, ttl); // Ignore potential error. } @@ -350,38 +349,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { match socket.family { SocketAddressFamily::Ipv4 => Ok(IpAddressFamily::Ipv4), - SocketAddressFamily::Ipv6 { .. } => Ok(IpAddressFamily::Ipv6), - } - } - - fn ipv6_only(&mut self, this: Resource) -> SocketResult { - let table = self.table(); - let socket = table.get(&this)?; - - // Instead of just calling the OS we return our own internal state, because - // MacOS doesn't propagate the V6ONLY state on to accepted client sockets. - - match socket.family { - SocketAddressFamily::Ipv4 => Err(ErrorCode::NotSupported.into()), - SocketAddressFamily::Ipv6 { v6only } => Ok(v6only), - } - } - - fn set_ipv6_only(&mut self, this: Resource, value: bool) -> SocketResult<()> { - let table = self.table_mut(); - let socket = table.get_mut(&this)?; - - match socket.family { - SocketAddressFamily::Ipv4 => Err(ErrorCode::NotSupported.into()), - SocketAddressFamily::Ipv6 { .. } => match socket.tcp_state { - TcpState::Default => { - sockopt::set_ipv6_v6only(socket.tcp_socket(), value)?; - socket.family = SocketAddressFamily::Ipv6 { v6only: value }; - Ok(()) - } - TcpState::BindStarted => Err(ErrorCode::ConcurrencyConflict.into()), - _ => Err(ErrorCode::InvalidState.into()), - }, + SocketAddressFamily::Ipv6 => Ok(IpAddressFamily::Ipv6), } } @@ -514,7 +482,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { let ttl = match socket.family { SocketAddressFamily::Ipv4 => util::get_ip_ttl(socket.tcp_socket())?, - SocketAddressFamily::Ipv6 { .. } => util::get_ipv6_unicast_hops(socket.tcp_socket())?, + SocketAddressFamily::Ipv6 => util::get_ipv6_unicast_hops(socket.tcp_socket())?, }; Ok(ttl) @@ -526,9 +494,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { match socket.family { SocketAddressFamily::Ipv4 => util::set_ip_ttl(socket.tcp_socket(), value)?, - SocketAddressFamily::Ipv6 { .. } => { - util::set_ipv6_unicast_hops(socket.tcp_socket(), value)? - } + SocketAddressFamily::Ipv6 => util::set_ipv6_unicast_hops(socket.tcp_socket(), value)?, } #[cfg(target_os = "macos")] diff --git a/crates/wasi/src/preview2/host/udp.rs b/crates/wasi/src/preview2/host/udp.rs index b256fd861760..8b96a686a089 100644 --- a/crates/wasi/src/preview2/host/udp.rs +++ b/crates/wasi/src/preview2/host/udp.rs @@ -13,7 +13,6 @@ use anyhow::anyhow; use async_trait::async_trait; use io_lifetimes::AsSocketlike; use rustix::io::Errno; -use rustix::net::sockopt; use std::net::SocketAddr; use tokio::io::Interest; use wasmtime::component::Resource; @@ -209,35 +208,7 @@ impl udp::HostUdpSocket for T { match socket.family { SocketAddressFamily::Ipv4 => Ok(IpAddressFamily::Ipv4), - SocketAddressFamily::Ipv6 { .. } => Ok(IpAddressFamily::Ipv6), - } - } - - fn ipv6_only(&mut self, this: Resource) -> SocketResult { - let table = self.table(); - let socket = table.get(&this)?; - - match socket.family { - SocketAddressFamily::Ipv4 => Err(ErrorCode::NotSupported.into()), - SocketAddressFamily::Ipv6 { v6only } => Ok(v6only), - } - } - - fn set_ipv6_only(&mut self, this: Resource, value: bool) -> SocketResult<()> { - let table = self.table_mut(); - let socket = table.get_mut(&this)?; - - match socket.family { - SocketAddressFamily::Ipv4 => Err(ErrorCode::NotSupported.into()), - SocketAddressFamily::Ipv6 { .. } => match socket.udp_state { - UdpState::Default => { - sockopt::set_ipv6_v6only(socket.udp_socket(), value)?; - socket.family = SocketAddressFamily::Ipv6 { v6only: value }; - Ok(()) - } - UdpState::BindStarted => Err(ErrorCode::ConcurrencyConflict.into()), - _ => Err(ErrorCode::InvalidState.into()), - }, + SocketAddressFamily::Ipv6 => Ok(IpAddressFamily::Ipv6), } } @@ -247,7 +218,7 @@ impl udp::HostUdpSocket for T { let ttl = match socket.family { SocketAddressFamily::Ipv4 => util::get_ip_ttl(socket.udp_socket())?, - SocketAddressFamily::Ipv6 { .. } => util::get_ipv6_unicast_hops(socket.udp_socket())?, + SocketAddressFamily::Ipv6 => util::get_ipv6_unicast_hops(socket.udp_socket())?, }; Ok(ttl) @@ -263,9 +234,7 @@ impl udp::HostUdpSocket for T { match socket.family { SocketAddressFamily::Ipv4 => util::set_ip_ttl(socket.udp_socket(), value)?, - SocketAddressFamily::Ipv6 { .. } => { - util::set_ipv6_unicast_hops(socket.udp_socket(), value)? - } + SocketAddressFamily::Ipv6 => util::set_ipv6_unicast_hops(socket.udp_socket(), value)?, } Ok(()) diff --git a/crates/wasi/src/preview2/network.rs b/crates/wasi/src/preview2/network.rs index 0cd86cba8fe2..82b0929bcda3 100644 --- a/crates/wasi/src/preview2/network.rs +++ b/crates/wasi/src/preview2/network.rs @@ -84,7 +84,7 @@ impl From for SocketError { #[derive(Copy, Clone)] pub enum SocketAddressFamily { Ipv4, - Ipv6 { v6only: bool }, + Ipv6, } pub(crate) fn to_ipv4_addr(addr: Ipv4Address) -> std::net::Ipv4Addr { diff --git a/crates/wasi/src/preview2/tcp.rs b/crates/wasi/src/preview2/tcp.rs index fe803306fc3a..fc766b167cf0 100644 --- a/crates/wasi/src/preview2/tcp.rs +++ b/crates/wasi/src/preview2/tcp.rs @@ -265,9 +265,10 @@ impl TcpSocket { let socket_address_family = match family { AddressFamily::Ipv4 => SocketAddressFamily::Ipv4, - AddressFamily::Ipv6 => SocketAddressFamily::Ipv6 { - v6only: sockopt::get_ipv6_v6only(&fd)?, - }, + AddressFamily::Ipv6 => { + sockopt::set_ipv6_v6only(&fd, true)?; + SocketAddressFamily::Ipv6 + } }; Self::from_fd(fd, socket_address_family) diff --git a/crates/wasi/src/preview2/udp.rs b/crates/wasi/src/preview2/udp.rs index 34d46402475b..e6a5269a80a6 100644 --- a/crates/wasi/src/preview2/udp.rs +++ b/crates/wasi/src/preview2/udp.rs @@ -64,9 +64,10 @@ impl UdpSocket { let socket_address_family = match family { AddressFamily::Ipv4 => SocketAddressFamily::Ipv4, - AddressFamily::Ipv6 => SocketAddressFamily::Ipv6 { - v6only: rustix::net::sockopt::get_ipv6_v6only(&fd)?, - }, + AddressFamily::Ipv6 => { + rustix::net::sockopt::set_ipv6_v6only(&fd, true)?; + SocketAddressFamily::Ipv6 + } }; let socket = Self::setup_tokio_udp_socket(fd)?; diff --git a/crates/wasi/wit/command-extended.wit b/crates/wasi/wit/command-extended.wit index 16f01bc23d7a..2f1491ef99f6 100644 --- a/crates/wasi/wit/command-extended.wit +++ b/crates/wasi/wit/command-extended.wit @@ -1,6 +1,6 @@ // All of the same imports and exports available in the wasi:cli/command world // with addition of HTTP proxy related imports: world command-extended { - include wasi:cli/command@0.2.0-rc-2023-12-05; - import wasi:http/outgoing-handler@0.2.0-rc-2023-12-05; + include wasi:cli/command@0.2.0-rc-2024-01-16; + import wasi:http/outgoing-handler@0.2.0-rc-2024-01-16; } diff --git a/crates/wasi/wit/deps/cli/command.wit b/crates/wasi/wit/deps/cli/command.wit index cc82ae5dc5be..a9889164fe37 100644 --- a/crates/wasi/wit/deps/cli/command.wit +++ b/crates/wasi/wit/deps/cli/command.wit @@ -1,4 +1,4 @@ -package wasi:cli@0.2.0-rc-2023-12-05; +package wasi:cli@0.2.0-rc-2024-01-16; world command { include imports; diff --git a/crates/wasi/wit/deps/cli/imports.wit b/crates/wasi/wit/deps/cli/imports.wit index 9965ea35ec02..8ce1abec397c 100644 --- a/crates/wasi/wit/deps/cli/imports.wit +++ b/crates/wasi/wit/deps/cli/imports.wit @@ -1,9 +1,9 @@ -package wasi:cli@0.2.0-rc-2023-12-05; +package wasi:cli@0.2.0-rc-2024-01-16; world imports { include wasi:clocks/imports@0.2.0-rc-2023-11-10; include wasi:filesystem/imports@0.2.0-rc-2023-11-10; - include wasi:sockets/imports@0.2.0-rc-2023-11-10; + include wasi:sockets/imports@0.2.0-rc-2024-01-16; include wasi:random/imports@0.2.0-rc-2023-11-10; include wasi:io/imports@0.2.0-rc-2023-11-10; diff --git a/crates/wasi/wit/deps/cli/terminal.wit b/crates/wasi/wit/deps/cli/terminal.wit index 47495769b31f..38c724efc840 100644 --- a/crates/wasi/wit/deps/cli/terminal.wit +++ b/crates/wasi/wit/deps/cli/terminal.wit @@ -1,19 +1,21 @@ +/// Terminal input. +/// +/// 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. interface terminal-input { /// The input side of a terminal. resource terminal-input; - - // 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. } +/// Terminal output. +/// +/// In the future, this may include functions for querying the terminal +/// size, being notified of terminal size changes, querying supported +/// features, and so on. interface terminal-output { /// The output side of a terminal. resource terminal-output; - - // In the future, this may include functions for querying the terminal - // size, being notified of terminal size changes, querying supported - // features, and so on. } /// An interface providing an optional `terminal-input` for stdin as a diff --git a/crates/wasi/wit/deps/http/proxy.wit b/crates/wasi/wit/deps/http/proxy.wit index 0f466c93c1b0..2b0ce4b8a67a 100644 --- a/crates/wasi/wit/deps/http/proxy.wit +++ b/crates/wasi/wit/deps/http/proxy.wit @@ -1,4 +1,4 @@ -package wasi:http@0.2.0-rc-2023-12-05; +package wasi:http@0.2.0-rc-2024-01-16; /// The `wasi:http/proxy` world captures a widely-implementable intersection of /// hosts that includes HTTP forward and reverse proxies. Components targeting @@ -11,14 +11,14 @@ world proxy { /// Proxies have standard output and error streams which are expected to /// terminate in a developer-facing console provided by the host. - import wasi:cli/stdout@0.2.0-rc-2023-12-05; - import wasi:cli/stderr@0.2.0-rc-2023-12-05; + import wasi:cli/stdout@0.2.0-rc-2024-01-16; + import wasi:cli/stderr@0.2.0-rc-2024-01-16; /// TODO: this is a temporary workaround until component tooling is able to /// gracefully handle the absence of stdin. Hosts must return an eof stream /// for this import, which is what wasi-libc + tooling will do automatically /// when this import is properly removed. - import wasi:cli/stdin@0.2.0-rc-2023-12-05; + import wasi:cli/stdin@0.2.0-rc-2024-01-16; /// This is the default handler to use when user code simply wants to make an /// HTTP request (e.g., via `fetch()`). diff --git a/crates/wasi/wit/deps/sockets/network.wit b/crates/wasi/wit/deps/sockets/network.wit index 6bb07cd6faa7..9cadf0650a49 100644 --- a/crates/wasi/wit/deps/sockets/network.wit +++ b/crates/wasi/wit/deps/sockets/network.wit @@ -18,8 +18,6 @@ interface network { /// /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. enum error-code { - // ### GENERAL ERRORS ### - /// Unknown error unknown, @@ -64,9 +62,6 @@ interface network { would-block, - - // ### TCP & UDP SOCKET ERRORS ### - /// The operation is not valid in the socket's current state. invalid-state, @@ -83,24 +78,21 @@ interface network { remote-unreachable, - // ### TCP SOCKET ERRORS ### - - /// The connection was forcefully rejected + /// The TCP connection was forcefully rejected connection-refused, - /// The connection was reset. + /// The TCP connection was reset. connection-reset, - /// A connection was aborted. + /// A TCP connection was aborted. connection-aborted, - // ### UDP SOCKET ERRORS ### + /// The size of a datagram sent to a UDP socket exceeded the maximum + /// supported size. datagram-too-large, - // ### NAME LOOKUP ERRORS ### - /// Name does not exist or has no suitable associated IP addresses. name-unresolvable, @@ -128,15 +120,21 @@ interface network { } record ipv4-socket-address { - port: u16, // sin_port - address: ipv4-address, // sin_addr + /// sin_port + port: u16, + /// sin_addr + address: ipv4-address, } record ipv6-socket-address { - port: u16, // sin6_port - flow-info: u32, // sin6_flowinfo - address: ipv6-address, // sin6_addr - scope-id: u32, // sin6_scope_id + /// sin6_port + port: u16, + /// sin6_flowinfo + flow-info: u32, + /// sin6_addr + address: ipv6-address, + /// sin6_scope_id + scope-id: u32, } variant ip-socket-address { diff --git a/crates/wasi/wit/deps/sockets/tcp-create-socket.wit b/crates/wasi/wit/deps/sockets/tcp-create-socket.wit index 768a07c85021..c7ddf1f228ef 100644 --- a/crates/wasi/wit/deps/sockets/tcp-create-socket.wit +++ b/crates/wasi/wit/deps/sockets/tcp-create-socket.wit @@ -6,9 +6,10 @@ interface tcp-create-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. /// /// This function does not require a network capability handle. This is considered to be safe because - /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect` + /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. /// /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. diff --git a/crates/wasi/wit/deps/sockets/tcp.wit b/crates/wasi/wit/deps/sockets/tcp.wit index 976b272c00a8..e045a436a9f6 100644 --- a/crates/wasi/wit/deps/sockets/tcp.wit +++ b/crates/wasi/wit/deps/sockets/tcp.wit @@ -25,15 +25,12 @@ interface tcp { /// network interface(s) to bind to. /// If the TCP/UDP port is zero, the socket will be bound to a random free port. /// - /// When a socket is not explicitly bound, the first invocation to a listen or connect operation will - /// implicitly bind the socket. - /// /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. /// /// # Typical `start` errors /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) /// - `invalid-argument`: `local-address` is not a unicast address. (EINVAL) - /// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL) + /// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address. (EINVAL) /// - `invalid-state`: The socket is already bound. (EINVAL) /// /// # Typical `finish` errors @@ -45,8 +42,9 @@ interface tcp { /// /// # 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 (i.e. the SO_REUSEADDR socket - /// option should be set implicitly on platforms that require it). + /// 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. /// /// # References /// - @@ -62,20 +60,13 @@ interface tcp { /// - the socket is transitioned into the Connection state /// - a pair of streams is returned that can be used to read & write to the connection /// - /// POSIX mentions: - /// > If connect() fails, the state of the socket is unspecified. Conforming applications should - /// > close the file descriptor and create a new socket before attempting to reconnect. - /// - /// WASI prescribes the following behavior: - /// - If `connect` fails because an input/state validation error, the socket should remain usable. - /// - If a connection was actually attempted but failed, the socket should become unusable for further network communication. - /// Besides `drop`, any method after such a failure may return an error. + /// After a failed connection attempt, the only valid action left is to + /// `drop` the socket. A single socket can not be used to connect more than once. /// /// # Typical `start` errors /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) /// - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS) - /// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL, EADDRNOTAVAIL on Illumos) - /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) + /// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address. (EINVAL, EADDRNOTAVAIL on Illumos) /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) /// - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) /// - `invalid-argument`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. @@ -130,7 +121,6 @@ interface tcp { /// /// The returned socket is bound and in the Connection state. The following properties are inherited from the listener socket: /// - `address-family` - /// - `ipv6-only` /// - `keep-alive-enabled` /// - `keep-alive-idle-time` /// - `keep-alive-interval` @@ -195,17 +185,6 @@ interface tcp { /// Equivalent to the SO_DOMAIN socket option. address-family: func() -> ip-address-family; - /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// - /// Equivalent to the IPV6_V6ONLY socket option. - /// - /// # Typical errors - /// - `invalid-state`: (set) The socket is already bound. - /// - `not-supported`: (get/set) `this` socket is an IPv4 socket. - /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) - ipv6-only: func() -> result; - set-ipv6-only: func(value: bool) -> result<_, error-code>; - /// Hints the desired listen queue size. Implementations are free to ignore this. /// /// If the provided value is 0, an `invalid-argument` error is returned. @@ -304,12 +283,16 @@ interface tcp { /// Initiate a graceful shutdown. /// - /// - receive: the socket is not expecting to receive any more data from the peer. All subsequent read - /// operations on the `input-stream` associated with this socket will return an End Of Stream indication. - /// Any data still in the receive queue at time of calling `shutdown` will be discarded. - /// - send: the socket is not expecting to send any more data to the peer. All subsequent write - /// operations on the `output-stream` associated with this socket will return an error. - /// - both: same effect as receive & send combined. + /// - `receive`: The socket is not expecting to receive any data from + /// the peer. The `input-stream` associated with this socket will be + /// closed. Any data still in the receive queue at time of calling + /// this method will be discarded. + /// - `send`: The socket has no more data to send to the peer. The `output-stream` + /// associated with this socket will be closed and a FIN packet will be sent. + /// - `both`: Same effect as `receive` & `send` combined. + /// + /// This function is idempotent. Shutting a down a direction more than once + /// has no effect and returns `ok`. /// /// The shutdown function does not close (drop) the socket. /// diff --git a/crates/wasi/wit/deps/sockets/udp-create-socket.wit b/crates/wasi/wit/deps/sockets/udp-create-socket.wit index cc58234d8455..0482d1fe7350 100644 --- a/crates/wasi/wit/deps/sockets/udp-create-socket.wit +++ b/crates/wasi/wit/deps/sockets/udp-create-socket.wit @@ -6,6 +6,7 @@ interface udp-create-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. /// /// This function does not require a network capability handle. This is considered to be safe because /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind` is called, diff --git a/crates/wasi/wit/deps/sockets/udp.wit b/crates/wasi/wit/deps/sockets/udp.wit index c8dafadfcb21..ae82b97fc1a6 100644 --- a/crates/wasi/wit/deps/sockets/udp.wit +++ b/crates/wasi/wit/deps/sockets/udp.wit @@ -92,7 +92,6 @@ interface udp { /// /// # Typical errors /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) /// - `invalid-state`: The socket is not bound. @@ -142,17 +141,6 @@ interface udp { /// Equivalent to the SO_DOMAIN socket option. address-family: func() -> ip-address-family; - /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// - /// Equivalent to the IPV6_V6ONLY socket option. - /// - /// # Typical errors - /// - `not-supported`: (get/set) `this` socket is an IPv4 socket. - /// - `invalid-state`: (set) The socket is already bound. - /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) - ipv6-only: func() -> result; - set-ipv6-only: func(value: bool) -> result<_, error-code>; - /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. /// /// If the provided value is 0, an `invalid-argument` error is returned. @@ -248,7 +236,6 @@ interface udp { /// /// # Typical errors /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) /// - `invalid-argument`: The socket is in "connected" mode and `remote-address` is `some` value that does not match the address passed to `stream`. (EISCONN) diff --git a/crates/wasi/wit/deps/sockets/world.wit b/crates/wasi/wit/deps/sockets/world.wit index 49ad8d3d9fde..8588cc6c1678 100644 --- a/crates/wasi/wit/deps/sockets/world.wit +++ b/crates/wasi/wit/deps/sockets/world.wit @@ -1,4 +1,4 @@ -package wasi:sockets@0.2.0-rc-2023-11-10; +package wasi:sockets@0.2.0-rc-2024-01-16; world imports { import instance-network; diff --git a/crates/wasi/wit/test.wit b/crates/wasi/wit/test.wit index f3cc6151b9e2..15294b4aaeae 100644 --- a/crates/wasi/wit/test.wit +++ b/crates/wasi/wit/test.wit @@ -2,7 +2,7 @@ package wasmtime:wasi; // only used as part of `test-programs` world test-reactor { - include wasi:cli/imports@0.2.0-rc-2023-12-05; + include wasi:cli/imports@0.2.0-rc-2024-01-16; export add-strings: func(s: list) -> u32; export get-strings: func() -> list; @@ -16,7 +16,7 @@ world test-reactor { } world test-command { - include wasi:cli/imports@0.2.0-rc-2023-12-05; - import wasi:http/types@0.2.0-rc-2023-12-05; - import wasi:http/outgoing-handler@0.2.0-rc-2023-12-05; + include wasi:cli/imports@0.2.0-rc-2024-01-16; + import wasi:http/types@0.2.0-rc-2024-01-16; + import wasi:http/outgoing-handler@0.2.0-rc-2024-01-16; } diff --git a/tests/all/cli_tests/component-basic.wat b/tests/all/cli_tests/component-basic.wat index 6e009cab9e5f..99600f1f008b 100644 --- a/tests/all/cli_tests/component-basic.wat +++ b/tests/all/cli_tests/component-basic.wat @@ -7,6 +7,6 @@ (func $run (result (result)) (canon lift (core func $i "run"))) - (instance (export (interface "wasi:cli/run@0.2.0-rc-2023-12-05")) + (instance (export (interface "wasi:cli/run@0.2.0-rc-2024-01-16")) (export "run" (func $run))) )