Bind a native pathname and listen. An existing pathname is never removed.
After closing, explicitly call path.delete!() before binding it again.
UnixSocket
UnixSocket :: # (opaque)
Exchange bytes and lines over native pathname AF_UNIX stream sockets. Supported on Linux, macOS, and Windows 10 1803+. No path is removed implicitly. The buffering, timeouts, and record limits match Tcp. Unix path bytes are preserved; the native filesystem decides which names it accepts. Windows paths must be representable as UTF-8. Native pathname length limits apply, and abstract or NUL-containing addresses are rejected.
connect! : Path, { connect_timeout : Duration, io_timeout : Duration } => Try(Connection, [Interrupted, UnixSocketErr(IOErr)])
Connect to a native pathname stream socket. Unix paths preserve raw bytes; Windows 10 1803+ uses UTF-8 pathname addresses. Native length limits apply.
Listener
UnixSocket.Listener :: # (opaque)
A listening socket.
The socket is closed when the last reference to the listener is released, or earlier by [close!]. Copies of a listener share one socket, so closing one closes them all.
to_inspect : Listener -> Str
Render the listener without exposing its host handle.
accept! : Listener, { accept_timeout : [NoTimeout, After(Duration)], io_timeout : Duration } => Try(Connection, [Closed, Interrupted, UnixSocketErr(IOErr)])
Wait for a client to connect.
accept_timeout bounds this call only: NoTimeout waits
indefinitely, and After(duration) gives up with
Err(UnixSocketErr(TimedOut)), after which the listener can accept again.
io_timeout is the timeout the new Connection holds for its reads
and writes; a connection never inherits the accept wait.
Closed after [close!].
match listener.accept!({ accept_timeout: After(Duration.seconds(1)), io_timeout: Duration.seconds(30) }) {
Ok(conn) => serve!(conn)?
Err(UnixSocketErr(TimedOut)) => {}
Err(Closed) => { return Ok({}) }
Err(UnixSocketErr(err)) => { return Err(UnixSocketErr(err)) }
}
Stop listening. Closing again succeeds; the pathname remains. Connections already accepted stay open.
Connection
UnixSocket.Connection :: # (opaque)
A buffered stream connection, with the I/O timeout and line limit its reads and writes use.
The connection is closed when the last reference to it is released.
Copies of a connection share one socket and one buffer, so every read,
whether of lines, delimited bytes or binary data, continues where the
previous one stopped. Connection.with_timeout and
Connection.with_line_limit change the settings of the copy they return,
not the socket.
Every read ends the same way: once the peer has closed its side and all
data has been returned, the next read is Err(EndOfInput). An empty
Ok is real data, such as an empty line.
A read that times out consumes nothing. Bytes that had arrived stay buffered for the next read, so a client may simply read again. A write that times out may have sent any prefix of its bytes: drop the connection unless the protocol can resynchronise.
to_inspect : Connection -> Str
Render the connection without exposing its host handle.
with_timeout : Connection, Duration -> Connection
The same connection with another I/O timeout. Each read or write
through the result is bounded as a whole by timeout.
# Poll: is a line already here?
match conn.with_timeout(Duration.millis(10)).read_line!() {
Ok(line) => handle!(line)?
Err(UnixSocketErr(TimedOut)) => {}
Err(err) => { return Err(err) }
}
with_line_limit : Connection, U64 -> Connection
The same connection with another limit, in bytes, on one record read
by [read_until!], [read_line!] or Connection.lines. The delimiter
or line terminator counts towards the limit. The default is
1,048,576 (1 MiB).
read_up_to! : Connection, U64 => Try(List(U8), [EndOfInput, Interrupted, UnixSocketErr(IOErr)])
Read at most max_bytes bytes, returning as soon as any have
arrived. Short reads are valid. Asking for zero bytes returns
Ok([]) without waiting; otherwise the result is never empty, and a
closed peer is Err(EndOfInput).
read_exactly! : Connection, U64 => Try(List(U8), [UnexpectedEndOfInput, Interrupted, UnixSocketErr(IOErr)])
Read exactly count bytes. Zero succeeds without waiting.
If the peer closes before count bytes arrive the result is
Err(UnexpectedEndOfInput). The bytes that did arrive are not
consumed: [read_up_to!] returns them.
read_until! : Connection, U8 => Try(List(U8), [EndOfInput, Interrupted, ReadLimitExceeded(U64), UnixSocketErr(IOErr)])
Read bytes through and including the next delimiter.
If the peer closes first, the bytes before the end are returned
without a delimiter, and the read after that is Err(EndOfInput).
So the result is never empty, and its last byte shows whether the
delimiter arrived.
A record longer than the connection's line limit is
Err(ReadLimitExceeded(limit)). The rest of that record, through
its delimiter, is then discarded before the next read returns, as
part of that read and within its timeout.
field = conn.read_until!('|')?
read_line! : Connection => Try(Str, [EndOfInput, ReadLimitExceeded(U64), BadUtf8({ problem : Utf8Problem, index : U64 }), Interrupted, UnixSocketErr(IOErr)])
Read a line of text.
A line ends at \n. The terminator is not returned, and one \r
before it is removed. If the peer closes without a final \n the
last line is still returned; after that the result is
Err(EndOfInput). An empty line is Ok("").
A line longer than the connection's line limit is
Err(ReadLimitExceeded(limit)), and a line that is not valid UTF-8
is BadUtf8. Either way that line is skipped and the next call
reads the line after it. For the bytes of a line, use
conn.read_until!('\n').
write_bytes! : Connection, List(U8) => Try({}, [Interrupted, UnixSocketErr(IOErr)])
Write all of bytes. They are sent at once, not buffered.
write_utf8! : Connection, Str => Try({}, [Interrupted, UnixSocketErr(IOErr)])
Write a string as UTF-8. It is sent at once, not buffered.
Lazily read this connection as non-empty chunks of at most 64 KiB, each returned as soon as it arrives. Chunk boundaries do not correspond to the peer's writes, records, or text encoding boundaries. For a chosen size, call [read_up_to!] in a loop.
Building the stream reads nothing; each item is one read with the
connection's timeout. The stream ends when the peer closes. A
UnixSocketErr item, including UnixSocketErr(TimedOut), is the last one; the
connection stays usable and a new stream may be started.
for! item in conn.chunks() {
bytes = item?
$total = $total + bytes.len()
}
The stream holds no data of its own, so it can be mixed with the other reads and with writes. It keeps the connection open until it has finished or is released.
lines : Connection -> Stream(Try(Str, [ReadLimitExceeded(U64), BadUtf8({ problem : Utf8Problem, index : U64 }), Interrupted, UnixSocketErr(IOErr)]))
Lazily read this connection a line at a time, as [read_line!] does.
Building the stream reads nothing; each item is one read with the
connection's timeout and line limit. The stream ends when the peer
closes. BadUtf8 and ReadLimitExceeded items concern one line, and
the stream continues with the next. A UnixSocketErr item, including
UnixSocketErr(TimedOut), is the last one; the connection stays usable, so
a client that tolerates idle periods calls conn.lines() again.
for! item in conn.lines() {
match item {
Ok(line) => Stdout.line!(line)?
Err(BadUtf8(_)) => {}
Err(ReadLimitExceeded(_)) => {}
Err(UnixSocketErr(err)) => { return Err(UnixSocketErr(err)) }
}
}
The stream holds no data of its own, so it can be mixed with the other reads and with writes, and leaving the loop early loses nothing. It keeps the connection open until it has finished or is released.