std/network/unix

std/network/src/unix.trb

Unix domain sockets: UnixListener accepts connections at a path, UnixStream is one, and the two directions of a stream are a Source and a Sink of Bytes - the shape of the TCP types of ./tcp, over a path of the file system instead of an address and a port (docs/design/NETWORK.md section 4). Stream sockets only.

A socket is a handle of the runtime, an Int, and the handle is the one a TCP stream has: accepting, receiving, sending, shutting down and closing are the natives ./tcp declares. Only listening and connecting are new.

type UnixListener

shared type UnixListener with Close

A socket that listens for connections at a path of the file system. It has an identity - the operating system's socket - so it is a shared type, and it closes when the last reference to it goes; closing removes the socket file it bound.

using listener = UnixListener.listen("/run/app/control.sock")?
var connection = listener.accept().await()?

Related

fn listen

static fn listen(path: String, backlog: Int = 128): Result<Self, NetworkError>

Listens at path, which creates the socket file. A path that is too long for the system's sun_path (108 bytes on Linux and Windows, 104 on macOS and the BSDs, the terminating zero included) or holds a NUL byte is refused, never cut short.

Errors

  • NetworkError that isAddressInUse() where something exists at path already: the file is never replaced.
  • NetworkError that isUnreachable() where the directory of path does not exist.
  • NetworkError for a path that is too long or holds a NUL byte.

fn path

fn path(): String

The path it listens at, as it was given.

fn accept

fn accept(): Task<Result<UnixStream, NetworkError>>

The next connection. Waits until one arrives; a cancelled accept leaves the connection to the next one.

fn stop

fn stop()

Stops listening before the listener is released, and removes the socket file: an accept that waits fails with a closed socket, and so does every later one. Stopping twice is nothing. A server that shuts down gracefully calls it; the release of the listener does the same by itself.

fn close

var fn close()

Stops listening, as UnixListener.stop does: what the release of the listener runs.

type UnixStream

shared type UnixStream with Close

One connection over a Unix domain socket: bytes in both directions, in order, until either side ends its half. A shared type, because it is the operating system's socket; it closes when the last reference to it - the stream, its source or its sink - goes. An accepted stream has no peer path, and a stream has no socket options: those of a TcpStream are not here.

using stream = UnixStream.connect("/run/app/control.sock").await()?
var writing = stream.sink()
writing.addText("ping").await()?

Related

fn of

static fn of(handle: Int): Self

A stream over a handle the runtime answered.

fn connect

static fn connect(path: String): Task<Result<Self, NetworkError>>

A connection to the socket at path.

Errors

  • NetworkError that isUnreachable() where there is no file at path.
  • NetworkError that isConnectionRefused() where the file is there and nobody listens.
  • NetworkError for a path that is too long or holds a NUL byte.

fn receive

fn receive(maximum: Int = 65536): Task<Result<Bytes?, NetworkError>>

At most maximum bytes, as soon as any arrived: Some(bytes), or None once the peer ended its half. One receive at a time; bytes that arrive while nobody receives wait in the operating system. Some always holds at least one byte: a receive whose stream was closed under it fails with NetworkError.closed rather than answer an empty chunk, which would be neither bytes nor the end of the stream.

fn send

fn send(bytes: Bytes): Task<Result<Void, NetworkError>>

Writes all of bytes; finishes once the operating system took them, which is when a full peer has read.

fn shutdown

fn shutdown(): Result<Void, NetworkError>

Ends this half: the peer reads the end of the stream, and may still answer.

fn source

fn source(chunk: Int = 65536): UnixSource

The reading direction as a Source: at most chunk bytes per next(), and None once the peer ended.

fn sink

fn sink(): UnixSink

The writing direction as a Sink: end() ends this half, close() does nothing of its own.

fn close

var fn close()

Closes the socket. What waits on it fails; the peer reads the end of the stream, or a reset.

type UnixSource

shared type UnixSource with Source<Bytes, NetworkError>

The reading direction of a UnixStream: a Source<Bytes, NetworkError>.

fn of

static fn of(stream: UnixStream, chunk: Int): Self

The source of stream: what UnixStream.source answers.

fn next

var fn next(): Task<Result<Bytes?, NetworkError>>

The next chunk of at most the chunk size, as UnixStream.receive answers it: None once the peer is done.

fn close

var fn close()

Nothing of its own: the stream closes once its last holder is gone.

type UnixSink

shared type UnixSink with Sink<Bytes, NetworkError>

The writing direction of a UnixStream: a Sink<Bytes, NetworkError>.

fn of

static fn of(stream: UnixStream): Self

The sink of stream: what UnixStream.sink answers.

fn add

var fn add(item: Bytes): Task<Result<Void, NetworkError>>

Writes all of item, as UnixStream.send does: finished once the operating system took the bytes.

fn end

var fn end(): Task<Result<Void, NetworkError>>

Ends this half of the connection: the peer reads the end of the stream.

fn close

var fn close()

Nothing of its own: the stream closes once its last holder is gone.