Landin library reference discuss source

hosted/system/network.ldn

1--  The hosted provider's `net.network`: a socket is a descriptor of
2--  platform/posix's, a file of this provider like any other, so `io.read`
3--  and `io.write` take a connection as they take a file.  Written once,
4--  over platform/posix, like the rest.
5
6import core/io
7import core/net
8import core/time
9import platform/posix
10
11--  How many connections may wait to be accepted, which each system caps at
12--  its own limit: the BSDs' SOMAXCONN.
13backlog: i32 = 128
14
15--  The family of socket an address needs.
16family_of: (at: net.address) -> (family: posix.socket_family) =
17    family = if at.family == net.ipv4 then posix.internet
18             else posix.internet6 end if
19end family_of
20
21--  Whether an errno says the system does not have the address here: no
22--  interface holds it, or the family is missing altogether.
23unavailable: (problem: posix.errno) -> (yes: bool) =
24    yes = problem == posix.eaddrnotavail or problem == posix.eafnosupport
25          or problem == posix.epfnosupport
26end unavailable
27
28--  Whether an errno says the peer has gone: it closed or reset the
29--  connection, or the system gave up on it.
30gone: (problem: posix.errno) -> (yes: bool) =
31    yes = problem == posix.epipe or problem == posix.econnreset
32          or problem == posix.etimedout or problem == posix.enotconn
33end gone
34
35--  A socket bound to `at`, a listener when `listening`: an IPv6 socket
36--  takes IPv6 alone on every system, and a listener may take an address
37--  its earlier connections still hold.
38bound: (at: net.address, kind: posix.socket_kind, listening: bool)
39       -> (socket: posix.descriptor) ! posix.errno =
40    socket = try posix.socket(family_of(at), kind)
41    undo quietly_closed(socket)
42    if at.family == net.ipv6 then
43        try posix.set_ipv6_only(socket, true)
44    end if
45    if listening then
46        try posix.set_reuse_address(socket, true)
47    end if
48    try posix.bind(socket, posix.internet_address(at))
49    if listening then
50        try posix.listen(socket, backlog)
51    end if
52end bound
53
54--  The errno a binding failed with, as the concept's atoms.
55bind_failure: (self: host, problem: posix.errno)
56              -> none ! net.address_in_use | net.address_unavailable
57                      | io.no_access | io.io_failed =
58    record(self, problem)
59    fail net.address_in_use when problem == posix.eaddrinuse
60    fail net.address_unavailable when unavailable(problem)
61    fail io.no_access when refused(problem)
62    fail io.io_failed
63end bind_failure
64
65system_listen: (self: host, at: net.address) -> (listener: io.file)
66               ! net.address_in_use | net.address_unavailable | io.no_access
67               | io.io_failed =
68    self.failure.val = 0
69    made: posix.descriptor = bound(at, posix.stream, true) else (problem)
70        try bind_failure(self, problem)
71        fail io.io_failed
72    end
73    listener = socket_of(made)
74end system_listen
75
76system_open_datagram: (self: host, at: net.address) -> (socket: io.file)
77                      ! net.address_in_use | net.address_unavailable
78                      | io.no_access | io.io_failed =
79    self.failure.val = 0
80    made: posix.descriptor = bound(at, posix.datagram, false) else (problem)
81        try bind_failure(self, problem)
82        fail io.io_failed
83    end
84    socket = socket_of(made)
85end system_open_datagram
86
87--  A connection a signal interrupted goes on without its caller: wait
88--  until it is writable, which it is once it has ended either way, and
89--  take its outcome from the socket.
90finished: (socket: posix.descriptor) -> none ! posix.errno =
91    mut entries: [1]posix.poll_entry =
92        [posix.watch(socket, posix.poll_output)]
93    loop do
94        _ = posix.poll(entries[0..<1], -1) else (problem)
95            continue when problem == posix.eintr
96            fail problem
97        end
98        break
99    end loop
100    try posix.pending_error(socket)
101end finished
102
103connected: (to: net.address) -> (socket: posix.descriptor) ! posix.errno =
104    socket = try posix.socket(family_of(to), posix.stream)
105    undo quietly_closed(socket)
106    posix.connect(socket, posix.internet_address(to)) else (problem)
107        fail problem when problem <> posix.eintr
108        try finished(socket)
109    end
110end connected
111
112system_connect: (self: host, to: net.address) -> (opened: io.file)
113                ! net.refused | net.unreachable | net.address_unavailable
114                | net.timed_out | io.io_failed =
115    self.failure.val = 0
116    made: posix.descriptor = connected(to) else (problem)
117        record(self, problem)
118        fail net.refused when problem == posix.econnrefused
119        fail net.unreachable when problem == posix.enetunreach
120          or problem == posix.ehostunreach
121        fail net.address_unavailable when unavailable(problem)
122        fail net.timed_out when problem == posix.etimedout
123        fail io.io_failed
124    end
125    opened = socket_of(made)
126end system_connect
127
128system_accept: (self: host, listener: io.file) -> (accepted: io.file)
129               ! net.timed_out | io.io_failed =
130    self.failure.val = 0
131    loop do
132        made: posix.descriptor =
133            posix.accept(descriptor_of(listener)) else (problem)
134                --  An interrupted accept took nothing, and a connection
135                --  its peer reset before it was taken is no connection.
136                continue when problem == posix.eintr
137                  or problem == posix.econnaborted
138                record(self, problem)
139                fail net.timed_out when problem == posix.eagain
140                fail io.io_failed
141            end
142        accepted = socket_of(made)
143        return
144    end loop
145end system_accept
146
147system_send: (self: host, connection: io.file, bytes: []u8)
148             -> none ! net.disconnected | net.timed_out | io.io_failed =
149    self.failure.val = 0
150    mut sent: usize = 0
151    while sent < lenof bytes do
152        --  A send interrupted before it sent anything is made again with
153        --  what is left; a short one goes on from where it stopped.
154        accepted: usize =
155            posix.send(descriptor_of(connection),
156                       bytes[sent..<lenof bytes]) else (problem)
157                continue when problem == posix.eintr
158                record(self, problem)
159                fail net.disconnected when gone(problem)
160                fail net.timed_out when problem == posix.eagain
161                fail io.io_failed
162            end
163        fail io.io_failed when accepted == 0
164          or accepted > lenof bytes - sent
165        sent += accepted
166    end while
167end system_send
168
169system_receive: (self: host, connection: io.file, into: []mut u8)
170                -> (count: usize)
171                ! net.disconnected | net.timed_out | io.io_failed =
172    self.failure.val = 0
173    count = 0
174    return when lenof into == 0
175    loop do
176        got: usize = posix.receive(descriptor_of(connection), into)
177            else (problem)
178            continue when problem == posix.eintr
179            record(self, problem)
180            fail net.disconnected when gone(problem)
181            fail net.timed_out when problem == posix.eagain
182            fail io.io_failed
183        end
184        fail io.io_failed when got > lenof into
185        count = got
186        return
187    end loop
188end system_receive
189
190system_send_to: (self: host, socket: io.file, bytes: []u8,
191                 to: net.address) -> none
192                ! net.unreachable | net.timed_out | io.io_failed =
193    self.failure.val = 0
194    loop do
195        accepted: usize =
196            posix.send_to(descriptor_of(socket), bytes,
197                          posix.internet_address(to)) else (problem)
198                continue when problem == posix.eintr
199                record(self, problem)
200                fail net.unreachable when problem == posix.enetunreach
201                  or problem == posix.ehostunreach
202                fail net.timed_out when problem == posix.eagain
203                fail io.io_failed
204            end
205        --  A datagram goes whole or not at all.
206        fail io.io_failed when accepted <> lenof bytes
207        return
208    end loop
209end system_send_to
210
211system_receive_from: (self: host, socket: io.file, into: []mut u8)
212                     -> (taken: net.datagram) ! net.timed_out | io.io_failed =
213    self.failure.val = 0
214    loop do
215        got: posix.received =
216            posix.receive_from(descriptor_of(socket), into) else (problem)
217                continue when problem == posix.eintr
218                record(self, problem)
219                fail net.timed_out when problem == posix.eagain
220                fail io.io_failed
221            end
222        fail io.io_failed when got.count > lenof into
223        sender: net.address = posix.address_of(got.sender) else (problem)
224            record(self, problem)
225            fail io.io_failed
226        end
227        taken = (count: got.count, sender: sender)
228        return
229    end loop
230end system_receive_from
231
232system_shutdown: (self: host, connection: io.file,
233                  stopping: net.direction) -> none ! io.io_failed =
234    self.failure.val = 0
235    side: posix.shutdown_side = match stopping
236        net.receiving: posix.shut_reading
237        net.sending: posix.shut_writing
238        net.both_directions: posix.shut_both
239    end match
240    posix.shutdown(descriptor_of(connection), side) else (problem)
241        record(self, problem)
242        fail io.io_failed
243    end
244end system_shutdown
245
246--  The internet address of a socket's own end, or with `peer` of the
247--  other end.
248named: (socket: io.file, peer: bool) -> (at: net.address) ! posix.errno =
249    record: posix.socket_address =
250        if peer then try posix.peer_name(descriptor_of(socket))
251        else try posix.socket_name(descriptor_of(socket)) end if
252    at = try posix.address_of(record)
253end named
254
255system_local_address: (self: host, socket: io.file) -> (at: net.address)
256                      ! io.io_failed =
257    self.failure.val = 0
258    at = named(socket, false) else (problem)
259        record(self, problem)
260        fail io.io_failed
261    end
262end system_local_address
263
264system_peer_address: (self: host, connection: io.file)
265                     -> (at: net.address) ! io.io_failed =
266    self.failure.val = 0
267    at = named(connection, true) else (problem)
268        record(self, problem)
269        fail io.io_failed
270    end
271end system_peer_address
272
273--  A span as the system's seconds and nanoseconds.
274time_of: (span: time.duration) -> (reading: posix.time_value) =
275    whole: u64 = u64(span) / 1000000000
276    reading = (seconds: i64(whole),
277               nanoseconds: u32(u64(span) - whole * 1000000000))
278end time_of
279
280limited: (socket: io.file, receive_limit: time.duration,
281          send_limit: time.duration) -> none ! posix.errno =
282    try posix.set_receive_timeout(descriptor_of(socket),
283                                  time_of(receive_limit))
284    try posix.set_send_timeout(descriptor_of(socket), time_of(send_limit))
285end limited
286
287system_set_timeouts: (self: host, socket: io.file,
288                      receive_limit: time.duration, send_limit: time.duration)
289                     -> none ! io.io_failed =
290    self.failure.val = 0
291    limited(socket, receive_limit, send_limit) else (problem)
292        record(self, problem)
293        fail io.io_failed
294    end
295end system_set_timeouts
296
297system_set_no_delay: (self: host, connection: io.file, on: bool)
298                     -> none ! io.io_failed =
299    self.failure.val = 0
300    posix.set_no_delay(descriptor_of(connection), on) else (problem)
301        record(self, problem)
302        fail io.io_failed
303    end
304end system_set_no_delay
305
306--  A failed close may already have released the descriptor, as a file's
307--  may, so it is never retried.
308system_close_socket: (self: host, sink socket: io.file)
309                     -> none ! io.io_failed =
310    self.failure.val = 0
311    posix.close(descriptor_of(socket)) else (problem)
312        record(self, problem)
313        fail io.io_failed
314    end
315end system_close_socket
316
317host is net.network
318    (connect: system_connect, listen: system_listen,
319     accept: system_accept, open_datagram: system_open_datagram,
320     send: system_send, receive: system_receive, send_to: system_send_to,
321     receive_from: system_receive_from, shutdown: system_shutdown,
322     local_address: system_local_address,
323     peer_address: system_peer_address,
324     set_timeouts: system_set_timeouts, set_no_delay: system_set_no_delay,
325     close_socket: system_close_socket)