rokdocs
v0.1

3.2. net:: - networking (TCP / UDP / Unix + TLS 1.3)

The rok::std::net module provides low-level network primitives together with TLS 1.3 as one bundle. High-level protocols such as HTTP, HTTP/2 and HTTP/3 are deliberately out of scope and delegated to separate packages such as rok/http. This choice also avoids Go's path, where the standard library froze at 1.0 and stopped evolving.

3.2.0. Common rules

  • Data being written (write / write_all / send / send_to and their _async forms) accepts str, List<u8> and Slice<u8> through AsBytes (3.5.1). The read buffer is a List<u8>. read(buf) fills the first n bytes of a buf whose length was set up in advance and returns n. 0 is the end of the stream.
  • Socket types are OS handles. Copying a value shares the same socket; only try_clone duplicates the fd.
  • Sockets implement Drop, so they are closed on leaving a using block (2.13.4). Otherwise, close them with close(). Using a closed socket is an IoError (InvalidInput). A *_async operation pending on the socket when another task closes it completes with that error; after shutdown(Read | Both) a pending read completes with 0.
  • TcpStream implements Reader + Writer (3.5.1).

Blocking and *_async

Methods come in two sets.

FormHow it waits
accept, read, write ..Blocks the calling OS thread (and, inside a task, its worker)
accept_async, read_async ..Suspends only the task; the worker runs other tasks

*_async methods are async fns, used as try await s.read_async(buf). Waiting for readiness is the job of the I/O driver. An ordinary function cannot suspend a stackless coroutine, so the sync call of the same name does not suspend on its own inside a task.

blockingasync
TcpListener::acceptTcpListener::accept_async
TcpStream::connectTcpStream::connect_async
TcpStream::readTcpStream::read_async
TcpStream::read_to_endTcpStream::read_to_end_async
TcpStream::writeTcpStream::write_async
TcpStream::write_allTcpStream::write_all_async
TcpStream::write_strTcpStream::write_str_async
UdpSocket::send_toUdpSocket::send_to_async
UdpSocket::recv_fromUdpSocket::recv_from_async
UdpSocket::sendUdpSocket::send_async
UdpSocket::recvUdpSocket::recv_async
tcp_connecttcp_connect_async
tls::Client::new / with_configtls::Client::new_async / with_config_async
tls::Server::newtls::Server::new_async
tls::Client::read ... key_update (and Server)read_async ... key_update_async

Timeouts apply only to the blocking side (set_read_timeout / set_write_timeout; exceeding them gives TimedOut). On the async side, wrap with task::timeout (waits on a task) or task::select (2.18.1). select cancels the losing side, which makes it a good fit for abandoning a read.

enum Got { Data(usize), Timeout }

let (_, got) = await task::select([
    async { Got::Data(try await s.read_async(buf) catch _ { 0 }) },
    async { await task::sleep(Duration::sec(5)); Got::Timeout },
]);

With set_nonblocking(true), blocking methods do not wait and throw WouldBlock.

On wasm32-wasi, std::net (TCP / UDP connect, bind, listen, accept, DNS) works through wasi:sockets; the host must grant network access (e.g. wasmtime -S inherit-network -S allow-ip-name-lookup). On wasm32-wasi-threads only TcpListener::inherited() listeners work; connect / bind / DNS throw Unsupported.

3.2.1. TCP

TCP is built around two types, TcpListener and TcpStream. The listener handles bind and the accept flow; the stream exposes reading, writing and option control.

struct TcpListener { /* OS handle */ }
struct TcpStream { /* OS handle */ }

impl TcpListener {
    fn bind(addr: SocketAddr) throws (IoError) -> TcpListener;      // SO_REUSEADDR, backlog 128
    fn inherited() -> List<TcpListener>;                            // listeners the host hands to the process
    fn accept(self) throws (IoError) -> (TcpStream, SocketAddr);
    async fn accept_async(self) throws (IoError) -> (TcpStream, SocketAddr);
    fn local_addr(self) throws (IoError) -> SocketAddr;              // the actual port after binding to port 0
    fn try_clone(self) throws (IoError) -> TcpListener;
    fn set_nonblocking(self, nb: bool) throws (IoError);
    fn incoming(self) -> Incoming;       // implements Iterator<Item = Result<TcpStream, IoError>>
    fn fd(self) -> i32;
    fn close(self);
}

impl TcpStream {
    fn connect(addr: SocketAddr) throws (IoError) -> TcpStream;
    fn connect_timeout(addr: SocketAddr, dur: Duration) throws (IoError) -> TcpStream;
    async fn connect_async(addr: SocketAddr) throws (IoError) -> TcpStream;

    fn read(self, buf: List<u8>) throws (IoError) -> usize;
    fn read_to_end(self) throws (IoError) -> List<u8>;                  // to the end
    fn write<B: AsBytes>(self, buf: B) throws (IoError) -> usize;
    fn write_all<B: AsBytes>(self, buf: B) throws (IoError);
    fn write_str(self, s: str) throws (IoError);
    fn flush(self) throws (IoError);                                 // no-op (no buffer)
    // + read_async / read_to_end_async / write_async / write_all_async / write_str_async

    fn peer_addr(self) throws (IoError) -> SocketAddr;
    fn local_addr(self) throws (IoError) -> SocketAddr;
    fn shutdown(self, how: Shutdown) throws (IoError);
    fn try_clone(self) throws (IoError) -> TcpStream;
    fn fd(self) -> i32;
    fn close(self);

    // TCP options
    fn set_nodelay(self, nodelay: bool) throws (IoError);           // TCP_NODELAY
    fn set_keepalive(self, alive: Duration | none) throws (IoError); // SO_KEEPALIVE + idle (seconds), none = off
    fn set_read_timeout(self, dur: Duration | none) throws (IoError);
    fn set_write_timeout(self, dur: Duration | none) throws (IoError);
    fn read_timeout(self) -> Duration | none;
    fn write_timeout(self) -> Duration | none;
    fn set_nonblocking(self, nb: bool) throws (IoError);
}

impl Reader for TcpStream { ... }
impl Writer for TcpStream { ... }
impl Drop for TcpStream { ... }          // close
impl Drop for TcpListener { ... }

enum Shutdown { Read, Write, Both }

TcpListener::inherited() returns the listeners the host hands to the process: WASI pre-opened sockets, systemd LISTEN_FDS socket activation on Unix, and none on Windows.

Incoming never ends. Each element calls accept once, and its errors come out as Err elements. If the peer closes during a write, the process is not terminated by SIGPIPE; the write yields an IoError (BrokenPipe).

Examples

use rok::std::net::{tcp_listen, TcpListener, TcpStream};

// blocking - this thread waits in accept and handle
using l = try tcp_listen("127.0.0.1:8080") {
    for c in l.incoming() {
        handle(try c.get());
    }
}

// async - one task per connection; while waiting, the worker runs other tasks
async fn serve_all(l: TcpListener) throws (IoError) {
    loop {
        let (s, peer) = try await l.accept_async();
        task::spawn(serve(s));          // async fn serve(s: TcpStream)
    }
}

3.2.2. UDP

For UDP, a single UdpSocket type covers every role. Datagram send/receive is the baseline, and once a peer is fixed with connect, send / recv alone suffice. Options such as multicast membership and TTL are exposed too, and with workloads like QUIC in mind it includes segment size control for GSO/GRO.

struct UdpSocket { /* OS handle */ }

impl UdpSocket {
    fn bind(addr: SocketAddr) throws (IoError) -> UdpSocket;

    fn send_to<B: AsBytes>(self, buf: B, addr: SocketAddr) throws (IoError) -> usize;
    fn recv_from(self, buf: List<u8>) throws (IoError) -> (usize, SocketAddr);
    fn connect(self, addr: SocketAddr) throws (IoError);             // fix the peer
    fn send<B: AsBytes>(self, buf: B) throws (IoError) -> usize;
    fn recv(self, buf: List<u8>) throws (IoError) -> usize;
    // + send_to_async / recv_from_async / send_async / recv_async

    fn local_addr(self) throws (IoError) -> SocketAddr;
    fn peer_addr(self) throws (IoError) -> SocketAddr;
    fn set_broadcast(self, on: bool) throws (IoError);              // SO_BROADCAST
    fn set_read_timeout(self, dur: Duration | none) throws (IoError);
    fn set_write_timeout(self, dur: Duration | none) throws (IoError);
    fn set_nonblocking(self, nb: bool) throws (IoError);
    fn try_clone(self) throws (IoError) -> UdpSocket;
    fn fd(self) -> i32;
    fn close(self);

    // multicast (IPv4)
    fn join_multicast_v4(self, addr: Ipv4Addr, iface: Ipv4Addr) throws (IoError);
    fn leave_multicast_v4(self, addr: Ipv4Addr, iface: Ipv4Addr) throws (IoError);
    fn set_multicast_loop_v4(self, on: bool) throws (IoError);
    fn set_multicast_ttl_v4(self, ttl: u32) throws (IoError);

    // for QUIC (GSO / GRO)
    fn set_segment_size(self, size: u16) throws (IoError);           // UDP_SEGMENT, Unsupported outside Linux
}

impl Drop for UdpSocket { ... }          // close

recv_from / recv receive a single datagram into buf. The remainder of a datagram longer than buf is discarded.

3.2.3. Unix domain socket

Unix domain sockets come in three types: UnixListener, UnixStream and UnixDatagram. Their overall shape resembles TCP, but since they communicate only within the same host they use file paths as identifiers, and they provide convenience methods such as pair(), corresponding to socketpair(2). Fd passing through SCM_RIGHTS is meaningful only on Unix sockets, so it is split out into send_with_fd / recv_with_fd.

struct UnixListener { /* unix only */ }
struct UnixStream { /* unix only */ }
struct UnixDatagram { /* unix only */ }

#[cfg(target_family = "unix")]
impl UnixListener {
    fn bind(path: Path) throws (IoError) -> UnixListener;
    fn accept(self) throws (IoError) -> (UnixStream, Path | none);   // peer's bound path
    // ... similar to TcpListener
}

#[cfg(target_family = "unix")]
impl UnixStream {
    fn connect(path: Path) throws (IoError) -> UnixStream;
    fn pair() throws (IoError) -> (UnixStream, UnixStream);          // socketpair

    // SCM_RIGHTS - fd passing
    fn send_with_fd(self, buf: List<u8>, fd: RawFd) throws (IoError) -> usize;
    fn recv_with_fd(self, buf: List<u8>) throws (IoError) -> (usize, RawFd | none);
}

3.2.4. SocketAddr / IpAddr

Address representation centers on the SocketAddr enum, the union of IPv4 and IPv6. Each variant exposes the IP and port, and for IPv6 also the flow info and scope id, so they can be inspected directly when needed. String parsing works through FromStr implementations, and DNS lookup of host names is provided by separate functions.

enum SocketAddr {
    V4(SocketAddrV4),
    V6(SocketAddrV6),
}

struct SocketAddrV4 { ip: Ipv4Addr; port: u16; }
struct SocketAddrV6 { ip: Ipv6Addr; port: u16; flow_info: u32; scope_id: u32; }

impl SocketAddr {
    fn v4(ip: Ipv4Addr, port: u16) -> SocketAddr;
    fn v6(ip: Ipv6Addr, port: u16) -> SocketAddr;    // flow_info = scope_id = 0
    fn port(self) -> u16;
    fn with_port(self, port: u16) -> SocketAddr;
    fn is_ipv4(self) -> bool;
    fn is_ipv6(self) -> bool;
}

struct Ipv4Addr { octets: [u8; 4]; }
struct Ipv6Addr { segments: [u16; 8]; }

impl Ipv4Addr {
    const LOCALHOST: Ipv4Addr = Ipv4Addr { octets: [127, 0, 0, 1] };
    const UNSPECIFIED: Ipv4Addr = Ipv4Addr { octets: [0, 0, 0, 0] };
    fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr;
    fn to_bits(self) -> u32;
    fn is_loopback(self) -> bool;
    fn is_unspecified(self) -> bool;
    fn is_multicast(self) -> bool;
}

impl Ipv6Addr {
    const LOCALHOST: Ipv6Addr;           // ::1
    const UNSPECIFIED: Ipv6Addr;         // ::
    fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr;
    fn is_loopback(self) -> bool;
}

// parsing / printing - all three types implement FromStr (Err = ParseError), Display, Debug, PartialEq
impl FromStr for SocketAddr { ... }       // "127.0.0.1:8080" / "[::1]:8080"
impl FromStr for Ipv4Addr { ... }
impl FromStr for Ipv6Addr { ... }         // "::" abbreviation allowed

// DNS (getaddrinfo)
fn resolve(host: str) throws (IoError) -> List<SocketAddr>;
fn resolve_one(host: str, port: u16) throws (IoError) -> SocketAddr;

resolve obtains a list of addresses with the OS's getaddrinfo and blocks the calling thread. host accepts:

hostResult
A socket address ("10.0.0.1:80", "[::1]:80")That single address (no lookup)
"name:port"The addresses of name, with port port
"[v6]:port"That IPv6 address, with port port
A name or IP onlyIts addresses, with port 0

resolve_one takes the name and port separately and returns the first address. If the name is not found it is an IoError (NotFound), and the message includes the resolver's description.

3.2.5. TLS 1.3 - net::tls

TLS lives in the net::tls submodule and assumes 1.3 only from the start. ClientConfig and ServerConfig express certificates, cipher suites and the ALPN protocol list, and the post-quantum option enables ML-KEM hybrid key exchange. After the handshake, Client / Server behave like plaintext streams, so existing I/O code can sit on top almost unchanged.

use rok::std::net::tls;

struct ClientConfig {
    root_certs: List<Certificate>;          // trust anchors
    cipher_suites: List<CipherSuite>;       // TLS 1.3 suites, preference order
    alpn_protocols: List<List<u8>>;         // ["h2", "http/1.1"], etc.
    enable_pq: bool;                        // post-quantum (X25519MLKEM768 hybrid)
    client_cert_chain: List<Certificate>;   // sent when the server asks: leaf first; empty: none
    client_key: PrivateKey | none;          // the client leaf's key
    session_cache: SessionCache | none;     // tickets to resume from; none: never resume
    early_data: List<u8>;                   // 0-RTT data, else the first data after the handshake
}

impl ClientConfig {
    fn new() -> ClientConfig;               // system_roots(), all suites, no ALPN, enable_pq = true,
                                            // no client certificate, SessionCache::new(), no early data
}

struct ServerConfig {
    cert_chain: List<Certificate>;          // leaf first, then intermediates
    private_key: PrivateKey;                // the leaf's key
    cipher_suites: List<CipherSuite>;
    alpn_protocols: List<List<u8>>;
    client_roots: List<Certificate>;        // non-empty: ask for a client certificate, validated against these
    require_client_cert: bool;              // with client_roots: refuse a client without one
    ocsp_response: List<u8>;                // DER OCSPResponse stapled when asked; empty: none
    ticket_store: TicketStore | none;       // resumption; none: no tickets
    max_early_data: u32;                    // 0-RTT bytes accepted per connection; 0: none
}

impl ServerConfig {
    fn new(cert_chain: List<Certificate>, private_key: PrivateKey) throws (TlsError) -> ServerConfig;
        // the key must belong to cert_chain[0]; all suites, no ALPN, no client certificates,
        // no OCSP response, TicketStore::new(), require_client_cert = true, max_early_data = 0
}

struct SessionCache {                       // the client's tickets by server name; copies share them
    fn new() -> SessionCache;
    fn len(self) -> usize;
    fn clear(self);
}

struct TicketStore {                        // the server's resumable sessions; copies share them
    fn new() -> TicketStore;
    fn len(self) -> usize;
}

enum CipherSuite { Aes128GcmSha256, Aes256GcmSha384, ChaCha20Poly1305Sha256 }

struct Certificate {
    fn from_der(der: List<u8> | Slice<u8>) throws (TlsError) -> Certificate;
    fn from_pem(text: str) throws (TlsError) -> List<Certificate>;
    fn der(self) -> List<u8>;
    fn dns_names(self) -> List<str>;
    fn not_before(self) -> i64;             // Unix seconds
    fn not_after(self) -> i64;
}

struct PrivateKey {                         // Ed25519, ECDSA P-256 / P-384, RSA (2048..4096 bits), RSASSA-PSS
    fn from_der(der: List<u8> | Slice<u8>) throws (TlsError) -> PrivateKey;   // PKCS #8
    fn from_pem(text: str) throws (TlsError) -> PrivateKey;  // PRIVATE KEY, EC PRIVATE KEY or RSA PRIVATE KEY
}

fn system_roots() throws (TlsError) -> List<Certificate>;   // the OS trust store (read on each call)

enum TlsErrorKind { Io, Certificate, Protocol, PeerAlert }
struct TlsError { kind: TlsErrorKind; msg: str; alert: u8; }

struct Client {
    fn new(stream: TcpStream, server_name: str) throws (TlsError) -> Client;
    fn with_config(stream: TcpStream, server_name: str, cfg: ClientConfig) throws (TlsError) -> Client;
    async fn new_async(stream: TcpStream, server_name: str) throws (TlsError) -> Client;
    async fn with_config_async(stream: TcpStream, server_name: str, cfg: ClientConfig) throws (TlsError) -> Client;

    fn read(self, buf: List<u8>) throws (IoError) -> usize;
    fn read_to_end(self) throws (IoError) -> List<u8>;
    fn write<B: AsBytes>(self, buf: B) throws (IoError) -> usize;
    fn write_all<B: AsBytes>(self, buf: B) throws (IoError);
    fn flush(self) throws (IoError);
    fn close_notify(self) throws (IoError);
    fn key_update(self) throws (IoError);
    fn close(self);

    async fn read_async(self, buf: List<u8>) throws (IoError) -> usize;
    async fn read_to_end_async(self) throws (IoError) -> List<u8>;
    async fn write_async<B: AsBytes>(self, buf: B) throws (IoError) -> usize;
    async fn write_all_async<B: AsBytes>(self, buf: B) throws (IoError);
    async fn flush_async(self) throws (IoError);
    async fn close_notify_async(self) throws (IoError);
    async fn key_update_async(self) throws (IoError);

    fn negotiated_alpn(self) -> List<u8> | none;
    fn peer_certificates(self) -> List<Certificate>;
    fn resumed(self) -> bool;               // the handshake resumed a session
    fn early_data_accepted(self) -> bool;   // early_data went out as 0-RTT data
}

struct Server {
    fn new(stream: TcpStream, cfg: ServerConfig) throws (TlsError) -> Server;
    async fn new_async(stream: TcpStream, cfg: ServerConfig) throws (TlsError) -> Server;
    // read, read_to_end, write, write_all, flush, close_notify, key_update,
    // close, their *_async forms, negotiated_alpn and peer_certificates
    // as on Client, and:
    fn server_name(self) -> str | none;     // the client's SNI
    fn resumed(self) -> bool;
    fn early_data(self) -> List<u8>;        // the 0-RTT data accepted; read returns what follows
}

Client and Server implement Reader and Writer (3.5.1) and Drop (closing the socket after a close_notify). Copies share one connection; a connection is used by one thread at a time (!Sync) and by one operation at a time.

The *_async methods follow 3.2.0: they suspend only the task while the socket is not ready, and the handshake (new_async, with_config_async) does the same between the peer's messages. The blocking and async forms may be mixed on one connection.

The stack is implementation-provided and written in rok on rok::core::crypto (2.21): no operating-system or third-party TLS library is used, and users never call one. Only TLS 1.3 (RFC 8446) is spoken; TLS 1.2 and earlier are refused. The handshake supports X25519 and the X25519MLKEM768 hybrid (ML-KEM-768 + X25519, enable_pq), HelloRetryRequest (for a group or a cookie), ALPN, SNI, KeyUpdate, close_notify, client certificates, OCSP stapling, and resumption with 0-RTT data.

Resumption: a server with a ticket_store sends one NewSessionTicket after each handshake; the store keeps the session (at most 4096, the oldest dropped; lifetime 2 hours) and redeems a ticket at most once. A client with a session_cache keeps the tickets it receives under the server name it connected to (at most 64), and offers the newest live one whose cipher suite's hash it also offers, once (psk_dhe_ke only: the resumed handshake still does a fresh key exchange). A resumed connection skips certificate authentication; peer_certificates() returns the original connection's chain. A ticket is received while the client reads.

0-RTT: when early_data is non-empty and the ticket allows that many bytes, the client sends it as early data with the ClientHello; otherwise, or if the server declines, it is sent as the first application data after the handshake (early_data_accepted() tells which). A server accepts early data only with max_early_data > 0, on the first offered ticket, without HelloRetryRequest, with the ticket's cipher suite and ALPN protocol, and when the client's ticket age is within 10 seconds of the server's; it hands the data out through early_data() and skips early data it declines. Since every ticket is redeemed once from its TicketStore, a replayed ClientHello resumes nothing and its early data is skipped (RFC 8446 8.1); servers that should reject each other's replays must share one store. Early data is still not forward-secret with respect to the ticket and should be idempotent.

Client certificates: a server with non-empty client_roots sends a CertificateRequest; the client's chain is validated against client_roots like a server's (below) with extendedKeyUsage clientAuth and no name check, and its CertificateVerify checked. A client without a certificate fails the handshake with certificate_required if require_client_cert, else continues with no peer_certificates(). A client answers a CertificateRequest with client_cert_chain and a CertificateVerify by client_key when the server accepts a scheme for that key, else with an empty Certificate. A client_key that does not belong to the first certificate makes Client::with_config fail before anything is sent.

Server authentication: the certificate path is built from the peer's chain to one of root_certs and validated per RFC 5280 (signatures, validity at the current time, basicConstraints, keyUsage, extendedKeyUsage serverAuth, DNS and IP name constraints; unknown critical extensions fail). The server name is matched against subjectAltName only (RFC 6125: case-insensitive, a wildcard is the whole leftmost label); an IP literal is matched against iPAddress entries and sends no SNI. Accepted signatures: Ed25519, ECDSA P-256 / P-384 with SHA-256/384/512, RSA PKCS #1 v1.5 (certificates only) and RSA-PSS with SHA-256/384/512 (2048-4096-bit keys, rsaEncryption or id-RSASSA-PSS; in certificates any salt length, honoring an id-RSASSA-PSS key's hash and minimum salt). Revocation: the client asks for an OCSP response (status_request). A stapled response must be a successful basic OCSP response (RFC 6960) signed by the leaf's issuer or by a certificate the issuer issued with extendedKeyUsage OCSPSigning, with a SHA-1 or SHA-2 CertID naming the leaf, thisUpdate not more than 5 minutes ahead and nextUpdate present and not past; status revoked fails with certificate_revoked, any other problem (unknown status included) with bad_certificate_status_response. A leaf with the TLS feature extension for status_request (RFC 7633) requires a stapled response. Without a staple and without that extension the certificate is accepted; CRLs and OCSP queries over the network are not used.

A certificate that is expired or not yet valid fails with certificate_expired, one whose extendedKeyUsage does not allow the role with unsupported_certificate, one with no path to root_certs with unknown_ca, and any other failure with bad_certificate. An extension in ServerHello, HelloRetryRequest, EncryptedExtensions or a Certificate entry that the client did not request fails with unsupported_extension. A failed handshake sends its alert, then closes the stream.

Either side signs its CertificateVerify with its key's scheme: ed25519, ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, for an rsaEncryption certificate the first of rsa_pss_rsae_sha256 / _sha384 / _sha512 the peer offers, and for an id-RSASSA-PSS certificate the first of rsa_pss_pss_sha256 / _sha384 / _sha512 the peer offers that the key's parameters allow. PrivateKey reads PKCS #8 (Ed25519, id-ecPublicKey on P-256 / P-384, rsaEncryption, id-RSASSA-PSS), RFC 5915 EC PRIVATE KEY and PKCS #1 RSA PRIVATE KEY. ServerConfig::new fails (TlsErrorKind::Certificate) when the key's public half is not the leaf's.

The stack keeps its traffic, handshake and resumption secrets, record keys, key-exchange secrets, ticket PSKs and Ed25519 / EC private keys in secret storage (2.21.5), and wipes them on KeyUpdate, at the end of the handshake and on close.

system_roots() reads the OS trust store: the macOS keychain anchors (plus admin / user certificates trusted unconditionally), the Windows ROOT store, the first CA bundle found on Linux (/etc/ssl/certs/ca-certificates.crt, /etc/pki/tls/certs/ca-bundle.crt, ...). SSL_CERT_FILE overrides it. wasm targets have no store: pass root_certs.

read returns 0 after the peer's close_notify; a connection that ends without one is an IoError (UnexpectedEof); a protocol failure is an IoError (InvalidData) after a fatal alert is sent. TLS records are written whole, so write writes all of buf.

Example

Actual use is simple: put a TLS client on top of a plaintext TCP connection and call plaintext I/O on it.

use rok::std::net::{tcp_connect, tls};

let conn = try tcp_connect("example.com:443");
let tls_conn = try tls::Client::new(conn, "example.com");
try tls_conn.write_all(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
let resp = try tls_conn.read_to_end();
try tls_conn.close_notify();

3.2.6. Convenience functions

Common patterns are wrapped in short helpers. Forms that take a string perform DNS resolution with resolve (3.2.4), so a host name can be passed directly, as in tcp_connect("example.com:80").

fn tcp_connect(addr: str | SocketAddr) throws (IoError) -> TcpStream;
async fn tcp_connect_async(addr: str | SocketAddr) throws (IoError) -> TcpStream;
fn tcp_listen(addr: str | SocketAddr) throws (IoError) -> TcpListener;
fn udp_bind(addr: str | SocketAddr) throws (IoError) -> UdpSocket;

tcp_connect tries the resolved addresses in order and returns the first that connects; if all fail, it throws the last error. tcp_listen / udp_bind bind to the first address.

3.2.7. zerocopy hooks (advanced)

For places where throughput is cost, such as proxies or file servers, zerocopy hooks are exposed separately. Most users do not need them; the goal is only to keep them within reach when they really are needed.

impl TcpStream {
    fn sendfile(self, ref file: File, offset: u64, count: u64) throws (IoError) -> u64;
                                            // sendfile(2) / TransmitFile
    fn splice(self, ref src: File, count: u64) throws (IoError) -> u64;
                                            // Linux splice(2)

    fn write_zerocopy(self, buf: List<u8>) throws (IoError) -> usize;
                                            // MSG_ZEROCOPY (Linux 4.14+)
}

3.2.8. Module mapping

Per-platform support is summarized below. Core features work on both desktop OSes and wasi, but items that depend on Linux-specific facilities, such as Unix sockets and zerocopy hooks, are enabled only in limited places.

Itemlinux/macos/windowswasiNotes
TCP✓✓wasi:sockets
UDP✓✓wasi:sockets
Unix socketlinux/macos✗Windows partial (named pipes separate)
TLS 1.3✓✓ (no trust store: pass root_certs)rok's own, on rok::core::crypto
sendfilelinux✗
splicelinux✗
MSG_ZEROCOPYlinux 4.14+✗

Edit this page on GitHub