sysl

Module

sysl.posix.net

resolve resolve_passive socket timed_out Address Family Shutdown Socket Display for Address

Import import sysl.posix.net
Requires
requires { posix }

Index

resolve resolve_passive socket timed_out Address Family Shutdown Socket Display for Address

Functions

resolve

resolve(host: string, port: int) -> Result[Buf[Address], IoError]

Every address a host and a port resolve to, in the order the machine ranked them.

Both protocol versions are asked for and both come back. That is what makes a program work on a v6-only network without knowing it is on one, and the order is the system’s answer to which to prefer — RFC 6724 says how a machine sorts them, and second-guessing it here would be this module inventing policy that is not its to have. So the ordinary client is a loop:

for a in resolve("example.com", 80)?.view()
    var s = socket(a)?
    if s.connect(a).is_ok() then break
    s.close()

A failure to resolve is reported as NotFound, whatever the resolver’s own reason was. The EAI_* numbers are a numbering of their own and overlap errno‘s — EAI_AGAIN is 2 on Darwin, which is ENOENT — so passing them through would produce an IoError that means something else entirely.

resolve_passive

resolve_passive(port: int) -> Result[Buf[Address], IoError]

Where a listener binds: the wildcard, meaning every address this machine answers on.

A separate call rather than an empty host, because the same question has two answers and nothing about the arguments alone says which is wanted — AI_PASSIVE is the flag that decides it, and a caller that got the wrong one binds to the loopback and wonders why nobody can reach it.

socket

socket(for_address: Address) -> Result[Socket, IoError]

A socket of the family an address belongs to, ready to be connected or bound.

The family comes from the address rather than being said separately: a socket of the wrong one cannot be connected to it, so letting the two be given apart would only make it possible to disagree.

timed_out

timed_out(e: IoError) -> bool

Whether an error is a call that ran out of its timeout rather than one that failed.

A timed-out blocking call reports EAGAIN, which is a number rather than a case of IoError — so this is the question to ask instead of comparing against one. It is a function rather than an enum case because the alternative is adding a variant to an error type every module in the library shares, for a condition only this one can produce.

Types

Address

struct Address
    bytes: [addr_bytes]u8
    used: i32

Somewhere a socket can be connected to, or bound to.

It carries the platform’s own bytes and sysl never looks inside them. A sockaddr_in and a sockaddr_in6 are different layouts, and Darwin’s carry a length byte glibc’s do not, so a struct written here would be a transcription of a layout — which reference/ffi.md § A library may carry C says is the thing that compiles everywhere and is wrong somewhere. What crosses is the bytes and their length; everything a program wants to know about an address it asks for.

MemberSignatureDescription
familyfamily(self) -> FamilyWhich protocol version this is for.
texttext(self) -> stringThe numeric form, as a person writes it — 127.0.0.1, or ::1.
portport(self) -> intThe port.

Family

enum Family
    Ipv4
    Ipv6

Which version of the protocol an address is for.

Named by the version rather than by AF_INET‘s number, which is the thing a program must not be handed: AF_INET6 is 30 on Darwin and 10 under glibc, so a program that compared against a literal would be right on one machine and silently wrong on the other.

Shutdown

enum Shutdown
    Read
    Write
    Both

How much of a connection is being given up.

Socket

struct Socket
    fd: i32

A connection, or something listening for one. One type, because POSIX has one.

A socket is not closed by going out of scope. close is a call, exactly as it is in C, and a program that drops one without closing it leaks a descriptor until it exits — which is C’s own rule rather than something this binding introduced, and is the same shape sysl-lang/libuv states about a handle. defer s.close() is the idiom.

MemberSignatureDescription
connectconnect(*self, to: Address) -> Result[unit, IoError]Connect to somewhere.
bindbind(*self, to: Address) -> Result[unit, IoError]Take a local address, which is what a listener does before it listens.
listenlisten(*self, backlog: int = 128) -> Result[unit, IoError]Start accepting connections, with backlog of them allowed to queue.
acceptaccept(*self) -> Result[(Socket, Address), IoError]Wait for a connection, and answer it together with where it came from.
locallocal(self) -> Result[Address, IoError]Where this socket is, which is how a listener that asked for port 0 learns which port it got.
sendsend(*self, bytes: []const u8) -> Result[usize, IoError]Write some of bytes, and say how many.
send_allsend_all(*self, bytes: []const u8) -> Result[unit, IoError]Every byte of bytes, however many calls that takes.
recvrecv(*self, into: []u8) -> Result[usize, IoError]Read into into, and say how many bytes arrived.
shutdownshutdown(*self, how: Shutdown = Both) -> Result[unit, IoError]Stop reading, stop writing, or stop both — while the descriptor stays open.
closeclose(*self) -> Result[unit, IoError]Give the descriptor back.
read_timeoutread_timeout(*self, ms: int) -> Result[unit, IoError]How long a recv may wait before giving up, in milliseconds.
write_timeoutwrite_timeout(*self, ms: int) -> Result[unit, IoError]How long a send may wait before giving up, in milliseconds.
reuse_addressreuse_address(*self, on: bool = true) -> Result[unit, IoError]Let a listener take a port that a recently stopped one was using.

Implementations

Display for Address

impl Display for Address

So that printing one says where it is rather than how it is stored.

Search

Esc
to navigate to open Esc to close