Socket
TCP socket module: a clean interface over the tcp_* builtins.
Wraps the low-level tcp_connect / tcp_send_all / tcp_recv_all / tcp_close builtins with an ergonomic Result-based API. All operations are synchronous. For async use, wrap in task_spawn.
File descriptors are plain Int values. Always call Socket.close when done, ideally via Socket.with_connection which ensures cleanup.
Public API: Socket.connect(host, port) : Result(Int, SocketError) Socket.write(fd, data) : Result((), SocketError) Socket.recv(fd, max_bytes) : Result(String, SocketError) Socket.recv_timeout(fd, max, ms) : Result(String, SocketError) Socket.set_recv_timeout(fd, ms) : Result((), SocketError) Socket.recv_deadline(fd, max, ms) : Result(Option(String), SocketError) Socket.close(fd) : () Socket.with_connection(host, port, f) : Result(a, SocketError) Socket.error_message(e) : String
Types
Functions
Close fd. Silently ignores errors (already-closed fds, etc.).
Open a TCP connection to host:port. Returns Ok(fd) or Err.
Return a human-readable description of a SocketError.
Receive up to max_bytes from fd, with no per-call deadline.
This still reports Err(RecvTimeout(0)) if the fd carries a deadline from
set_recv_timeout and that deadline expires -- the 0 records that the bound
came from the descriptor rather than from this call, which had none of its
own. Otherwise it blocks until the peer sends or the connection closes.As recv_timeout, but an expired deadline is Ok(None) rather than an Err.
The same read, reported differently. `Err(RecvTimeout(ms))` says a timeout
is a kind of failure; `Ok(None)` says it is the absence of an event, which
is what a caller polling a quiet peer usually means. Pick whichever makes
the calling code say what it means — a reader that treats silence as normal
and one that treats it as an error should not have to share a spelling.
`Ok(Some(""))` is a clean EOF, as in `recv`.Receive up to max_bytes from fd, waiting at most timeout_ms for the peer to send anything. timeout_ms <= 0 means no timeout.
Returns Err(RecvTimeout(timeout_ms)) when the deadline expires with the peer
still silent -- a distinct fact from Err(RecvFailed(...)), because only one
of the two is worth retrying. The deadline applies to this call alone and
leaves no lasting property on fd; to bound reads made through TLS, use
set_recv_timeout instead.
On a peer that accepts the connection and then sends nothing, this returns
Err(RecvTimeout(timeout_ms)) rather than blocking.Bound every subsequent blocking read on fd to timeout_ms (SO_RCVTIMEO). timeout_ms <= 0 clears the deadline.
Unlike recv_timeout this is a persistent property of the descriptor, and
that is the point: it is the only mechanism that reaches reads made through
OpenSSL, so a TLS client calls it on the fd BEFORE Tls.connect to bound both
the handshake and every later Tls.read against a peer that goes silent.
The cost of that reach: a deadline firing part-way through a TLS record
leaves the session unusable. On timeout, close the connection -- do not
retry the read.
Returns Ok(()) once the option is applied.Open a connection, run f(fd), then close. fd is always closed.
Write all bytes of data over fd. Returns Ok(()) or Err(WriteFailed(...)).