Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions examples/tcp_echo_server.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
#![cfg_attr(not(all(target_os = "wasi", target_env = "p2")), no_main)]
#![cfg(all(target_os = "wasi", target_env = "p2"))]
#![cfg_attr(not(target_os = "wasi"), no_main)]
#![cfg(target_os = "wasi")]

use wstd::io;
use wstd::iter::AsyncIterator;
use wstd::net::TcpListener;

#[wstd::main]
async fn main() -> io::Result<()> {
let mut listener = TcpListener::bind("127.0.0.1:8080").await?;
let mut listener = TcpListener::bind("127.0.0.1:0").await?;
println!("Listening on {}", listener.local_addr()?);
println!("type `nc localhost 8080` to create a TCP client");
println!("type `nc localhost <PORT>` to create a TCP client");

let mut incoming = listener.incoming();
while let Some(stream) = incoming.next().await {
Expand Down
4 changes: 2 additions & 2 deletions examples/tcp_stream_client.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![cfg_attr(not(all(target_os = "wasi", target_env = "p2")), no_main)]
#![cfg(all(target_os = "wasi", target_env = "p2"))]
#![cfg_attr(not(target_os = "wasi"), no_main)]
#![cfg(target_os = "wasi")]

use wstd::io::{self, AsyncRead, AsyncWrite};
use wstd::net::TcpStream;
Expand Down
8 changes: 4 additions & 4 deletions examples/udp_echo_server.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
#![cfg_attr(not(all(target_os = "wasi", target_env = "p2")), no_main)]
#![cfg(all(target_os = "wasi", target_env = "p2"))]
#![cfg_attr(not(target_os = "wasi"), no_main)]
#![cfg(target_os = "wasi")]

use wstd::io;
use wstd::net::UdpSocket;

#[wstd::main]
async fn main() -> io::Result<()> {
let socket = UdpSocket::bind("127.0.0.1:8080").await?;
let socket = UdpSocket::bind("127.0.0.1:0").await?;
println!("Listening on {}", socket.local_addr()?);
println!("type `nc -u localhost 8080` to create a UDP client");
println!("type `nc -u localhost <PORT>` to create a UDP client");

let mut buf = vec![0; 65535];
loop {
Expand Down
4 changes: 2 additions & 2 deletions examples/udp_stream_client.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![cfg_attr(not(all(target_os = "wasi", target_env = "p2")), no_main)]
#![cfg(all(target_os = "wasi", target_env = "p2"))]
#![cfg_attr(not(target_os = "wasi"), no_main)]
#![cfg(target_os = "wasi")]

use wstd::io;
use wstd::net::{UdpSocket, UdpStream};
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ pub mod future;
pub mod http;
pub mod io;
pub mod iter;
#[cfg(all(target_os = "wasi", target_env = "p2"))]
pub mod net;
pub mod rand;
pub mod runtime;
Expand Down
88 changes: 79 additions & 9 deletions src/net/mod.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,95 @@
//! Async network abstractions.

use std::io::{self, ErrorKind};
use wasip2::sockets::network::{ErrorCode, IpSocketAddress, Ipv4SocketAddress};
#[cfg(target_env = "p2")]
use wasip2::sockets::{
network::{ErrorCode, IpSocketAddress, Ipv4SocketAddress, Ipv6SocketAddress},
tcp_create_socket::create_tcp_socket,
udp_create_socket::create_udp_socket,
};
#[cfg(target_env = "p3")]
use wasip3::sockets::types::{
ErrorCode, IpAddressFamily, IpSocketAddress, Ipv4SocketAddress, Ipv6SocketAddress, TcpSocket,
};

mod tcp_listener;
mod tcp_stream;
mod udp;
mod tcp_listener {
mod sys {
#[cfg(target_env = "p2")]
pub(super) mod p2;
#[cfg(target_env = "p3")]
pub(super) mod p3;
}
#[cfg(target_env = "p2")]
pub use sys::p2::*;
#[cfg(target_env = "p3")]
pub use sys::p3::*;
}

mod tcp_stream {
mod sys {
#[cfg(target_env = "p2")]
pub(super) mod p2;
#[cfg(target_env = "p3")]
pub(super) mod p3;
}
#[cfg(target_env = "p2")]
pub use sys::p2::*;
#[cfg(target_env = "p3")]
pub use sys::p3::*;
}

mod udp {
mod sys {
#[cfg(target_env = "p2")]
pub(super) mod p2;
#[cfg(target_env = "p3")]
pub(super) mod p3;
}
#[cfg(target_env = "p2")]
pub use sys::p2::*;
#[cfg(target_env = "p3")]
pub use sys::p3::*;
}

pub use tcp_listener::*;
pub use tcp_stream::*;
pub use udp::*;

fn to_io_err(err: ErrorCode) -> io::Error {
match err {
ErrorCode::Unknown => ErrorKind::Other.into(),
ErrorCode::AccessDenied => ErrorKind::PermissionDenied.into(),
ErrorCode::NotSupported => ErrorKind::Unsupported.into(),
ErrorCode::InvalidArgument => ErrorKind::InvalidInput.into(),
ErrorCode::OutOfMemory => ErrorKind::OutOfMemory.into(),
ErrorCode::Timeout => ErrorKind::TimedOut.into(),
ErrorCode::WouldBlock => ErrorKind::WouldBlock.into(),
ErrorCode::InvalidState => ErrorKind::InvalidData.into(),
ErrorCode::AddressInUse => ErrorKind::AddrInUse.into(),
ErrorCode::ConnectionRefused => ErrorKind::ConnectionRefused.into(),
ErrorCode::ConnectionReset => ErrorKind::ConnectionReset.into(),
ErrorCode::ConnectionAborted => ErrorKind::ConnectionAborted.into(),
ErrorCode::ConcurrencyConflict => ErrorKind::AlreadyExists.into(),
ErrorCode::DatagramTooLarge => ErrorKind::InvalidInput.into(),

#[cfg(target_env = "p2")]
ErrorCode::Unknown => ErrorKind::Other.into(),
#[cfg(target_env = "p2")]
ErrorCode::WouldBlock => ErrorKind::WouldBlock.into(),
#[cfg(target_env = "p2")]
ErrorCode::ConcurrencyConflict => ErrorKind::AlreadyExists.into(),
#[cfg(target_env = "p2")]
_ => ErrorKind::Other.into(),

#[cfg(target_env = "p3")]
ErrorCode::AddressNotBindable => ErrorKind::AddrNotAvailable.into(),
#[cfg(target_env = "p3")]
ErrorCode::RemoteUnreachable => ErrorKind::HostUnreachable.into(),
#[cfg(target_env = "p3")]
ErrorCode::ConnectionBroken => ErrorKind::BrokenPipe.into(),
#[cfg(target_env = "p3")]
ErrorCode::Other(s) => io::Error::other(s.unwrap_or_default()),
}
}

fn sockaddr_from_wasi(addr: IpSocketAddress) -> std::net::SocketAddr {
use wasip2::sockets::network::Ipv6SocketAddress;
match addr {
IpSocketAddress::Ipv4(Ipv4SocketAddress { address, port }) => {
std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
Expand All @@ -58,7 +115,6 @@ fn sockaddr_from_wasi(addr: IpSocketAddress) -> std::net::SocketAddr {
}

fn sockaddr_to_wasi(addr: std::net::SocketAddr) -> IpSocketAddress {
use wasip2::sockets::network::Ipv6SocketAddress;
match addr {
std::net::SocketAddr::V4(addr) => {
let ip = addr.ip().octets();
Expand All @@ -78,3 +134,17 @@ fn sockaddr_to_wasi(addr: std::net::SocketAddr) -> IpSocketAddress {
}
}
}

#[cfg(target_env = "p3")]
fn create_tcp_socket(
family: IpAddressFamily,
) -> Result<TcpSocket, wasip3::sockets::types::ErrorCode> {
TcpSocket::create(family)
}

#[cfg(target_env = "p3")]
fn create_udp_socket(
family: IpAddressFamily,
) -> Result<wasip3::sockets::types::UdpSocket, wasip3::sockets::types::ErrorCode> {
wasip3::sockets::types::UdpSocket::create(family)
}
14 changes: 5 additions & 9 deletions src/net/tcp_listener.rs → src/net/tcp_listener/sys/p2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::io;
use crate::iter::AsyncIterator;
use std::net::SocketAddr;

use super::{TcpStream, sockaddr_from_wasi, sockaddr_to_wasi, to_io_err};
use crate::net::{TcpStream, create_tcp_socket, sockaddr_from_wasi, sockaddr_to_wasi, to_io_err};
use crate::runtime::AsyncPollable;

/// A TCP socket server, listening for connections.
Expand All @@ -27,12 +27,10 @@ impl TcpListener {
SocketAddr::V4(_) => IpAddressFamily::Ipv4,
SocketAddr::V6(_) => IpAddressFamily::Ipv6,
};
let socket =
wasip2::sockets::tcp_create_socket::create_tcp_socket(family).map_err(to_io_err)?;
let network = wasip2::sockets::instance_network::instance_network();

let socket = create_tcp_socket(family).map_err(to_io_err)?;
let local_address = sockaddr_to_wasi(addr);

let network = wasip2::sockets::instance_network::instance_network();
socket
.start_bind(&network, local_address)
.map_err(to_io_err)?;
Expand All @@ -48,10 +46,8 @@ impl TcpListener {

/// Returns the local socket address of this listener.
pub fn local_addr(&self) -> io::Result<std::net::SocketAddr> {
self.socket
.local_address()
.map_err(to_io_err)
.map(sockaddr_from_wasi)
let addr = self.socket.local_address();
addr.map_err(to_io_err).map(sockaddr_from_wasi)
}

/// Returns an iterator over the connections being received on this listener.
Expand Down
71 changes: 71 additions & 0 deletions src/net/tcp_listener/sys/p3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use wasip3::{
sockets::types::{IpAddressFamily, TcpSocket},
wit_bindgen::StreamReader,
};

use crate::io;
use crate::iter::AsyncIterator;
use std::net::SocketAddr;

use crate::net::{TcpStream, create_tcp_socket, sockaddr_from_wasi, sockaddr_to_wasi, to_io_err};

/// A TCP socket server, listening for connections.
#[derive(Debug)]
pub struct TcpListener {
connections: StreamReader<TcpSocket>,
socket: TcpSocket,
}

impl TcpListener {
/// Creates a new TcpListener which will be bound to the specified address.
///
/// The returned listener is ready for accepting connections.
pub async fn bind(addr: &str) -> io::Result<Self> {
let addr: SocketAddr = addr
.parse()
.map_err(|_| io::Error::other("failed to parse string to socket addr"))?;
let family = match addr {
SocketAddr::V4(_) => IpAddressFamily::Ipv4,
SocketAddr::V6(_) => IpAddressFamily::Ipv6,
};
let socket = create_tcp_socket(family).map_err(to_io_err)?;
let local_address = sockaddr_to_wasi(addr);

socket.bind(local_address).map_err(to_io_err)?;
let connections = socket.listen().map_err(to_io_err)?;
Ok(Self {
connections,
socket,
})
}

/// Returns the local socket address of this listener.
pub fn local_addr(&self) -> io::Result<std::net::SocketAddr> {
let addr = self.socket.get_local_address();
addr.map_err(to_io_err).map(sockaddr_from_wasi)
}

/// Returns an iterator over the connections being received on this listener.
pub fn incoming(&mut self) -> Incoming<'_> {
Incoming { listener: self }
}
}

/// An iterator that infinitely accepts connections on a TcpListener.
#[derive(Debug)]
pub struct Incoming<'a> {
listener: &'a mut TcpListener,
}

impl<'a> AsyncIterator for Incoming<'a> {
type Item = io::Result<TcpStream>;

async fn next(&mut self) -> Option<Self::Item> {
self.listener.connections.next().await.map(|socket| {
let (input, _receive_result) = socket.receive();
let (output, receiver) = wasip3::wit_stream::new();
let _send_result = socket.send(receiver);
Ok(TcpStream::new(input, output, socket))
})
}
}
16 changes: 8 additions & 8 deletions src/net/tcp_stream.rs → src/net/tcp_stream/sys/p2.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
use std::io::ErrorKind;
use std::net::{SocketAddr, ToSocketAddrs};
use wasip2::sockets::instance_network::instance_network;
use wasip2::sockets::network::Ipv4SocketAddress;
use wasip2::sockets::tcp::{IpAddressFamily, IpSocketAddress};
use wasip2::sockets::tcp_create_socket::create_tcp_socket;

use wasip2::{
io::streams::{InputStream, OutputStream},
sockets::tcp::TcpSocket,
sockets::{
instance_network::instance_network,
network::Ipv4SocketAddress,
tcp::{IpAddressFamily, IpSocketAddress, TcpSocket},
},
};

use super::to_io_err;
use crate::io::{self, AsyncInputStream, AsyncOutputStream};
use crate::net::{create_tcp_socket, to_io_err};
use crate::runtime::AsyncPollable;

/// A TCP stream between a local and a remote socket.
Expand Down Expand Up @@ -59,7 +60,6 @@ impl TcpStream {
SocketAddr::V6(_) => IpAddressFamily::Ipv6,
};
let socket = create_tcp_socket(family).map_err(to_io_err)?;
let network = instance_network();

let remote_address = match addr {
SocketAddr::V4(addr) => {
Expand All @@ -70,13 +70,13 @@ impl TcpStream {
}
SocketAddr::V6(_) => todo!("IPv6 not yet supported in `wstd::net::TcpStream`"),
};
let network = instance_network();
socket
.start_connect(&network, remote_address)
.map_err(to_io_err)?;
let pollable = AsyncPollable::new(socket.subscribe());
pollable.wait_for().await;
let (input, output) = socket.finish_connect().map_err(to_io_err)?;

Ok(TcpStream::new(input, output, socket))
}

Expand Down
Loading
Loading