wire.bend
bend-kit-wire@0.4.4.0 · 116 lines · 0 definitions
1# Byte-exact TCP, UDP, and TLS sockets. Source: https://github.com/paymog/bend-kit/tree/main/wire
2import Base
3
4# Socket IO on octets: one Char per byte (0..255). Base's TCP/UDP effects
5# decode UTF-8, which breaks byte counts and binary protocols.
6# ms arguments are deadlines in milliseconds (0: none); a miss is ETIMEDOUT.
7# import ./wire.bend as Wire
8
9# TCP connect to a numeric IPv4 or IPv6 address, with a deadline.
10def connect(host: String, port: U32, ms: U32) ->
11 IO(Result<&1, &1, U32 & String, Socket>):
12 import "./effs/wire.c"
13 import "./effs/wire.js"
14
15# Octet-String TCP and UDP effects are the slow path: one Char cell per byte.
16def recv(sock: Socket, max: U32, ms: U32) ->
17 IO(Socket & Result<&1, &1, U32 & String, String>):
18 import "./effs/wire.c"
19 import "./effs/wire.js"
20
21def send(sock: Socket, data: String) ->
22 IO(Socket & Result<&1, &1, U32 & String, Unit>):
23 import "./effs/wire.c"
24 import "./effs/wire.js"
25
26def recv_from(sock: Socket, max: U32, ms: U32) ->
27 IO(Socket & Result<&1, &1, U32 & String, String & U32 & String>):
28 import "./effs/wire.c"
29 import "./effs/wire.js"
30
31def send_to(sock: Socket, host: String, port: U32, data: String) ->
32 IO(Socket & Result<&1, &1, U32 & String, Unit>):
33 import "./effs/wire.c"
34 import "./effs/wire.js"
35
36# TLS over a connected TCP socket (OpenSSL 3, loaded at run time; BEND_LIBSSL
37# overrides its path). The certificate chain and host name are always checked.
38def tls.connect(sock: Socket, host: String, ms: U32) ->
39 IO(Socket & Result<&1, &1, U32 & String, Unit>):
40 import "./effs/wire.c"
41 import "./effs/wire.js"
42
43# Use a PEM certificate chain and its private key for mutual TLS.
44# A load failure is EINVAL; a handshake or server verification failure is EPROTO.
45def tls.connect.cert(sock: Socket, host: String, ms: U32, cert_path: String, key_path: String) ->
46 IO(Socket & Result<&1, &1, U32 & String, Unit>):
47 import "./effs/wire.c"
48 import "./effs/wire.js"
49
50# protos is a comma-separated ALPN list (for example "h2,http/1.1").
51def tls.connect.alpn(sock: Socket, host: String, ms: U32, protos: String) ->
52 IO(Socket & Result<&1, &1, U32 & String, String>):
53 import "./effs/wire.c"
54 import "./effs/wire.js"
55
56# The String TLS effects are the slow path: one Char cell per octet.
57def tls.send(sock: Socket, data: String) ->
58 IO(Socket & Result<&1, &1, U32 & String, Unit>):
59 import "./effs/wire.c"
60 import "./effs/wire.js"
61
62# "" once the peer sends close_notify. A bare EOF is an error.
63def tls.recv(sock: Socket, max: U32, ms: U32) ->
64 IO(Socket & Result<&1, &1, U32 & String, String>):
65 import "./effs/wire.c"
66 import "./effs/wire.js"
67
68# Ends the TLS session and closes the socket.
69def tls.close(sock: Socket) -> IO(Unit):
70 import "./effs/wire.c"
71 import "./effs/wire.js"
72
73# The .words forms move (len, words): bytes packed four to a U32, laid out as
74# bytes/bytes.bend packs them, so no String cell is made per byte.
75def recv.words(sock: Socket, max: U32, ms: U32) ->
76 IO(Socket & Result<&1, &1, U32 & String, U32 & Array<U32>>):
77 import "./effs/wire.c"
78 import "./effs/wire.js"
79
80# len past the end of words fails with EINVAL.
81def send.words(sock: Socket, len: U32, words: Array<U32>) ->
82 IO(Socket & Result<&1, &1, U32 & String, Unit>):
83 import "./effs/wire.c"
84 import "./effs/wire.js"
85
86# Total write budget, including packing; ms must be 1..2147483647.
87# Returns socket, bytes accepted locally, and outcome on every path. Consumes
88# words on every path. Timeout/disconnect may leave a sent prefix: close the
89# returned socket, do not replay the whole buffer. Success is not peer receipt.
90def send.words.deadline(sock: Socket, len: U32, words: Array<U32>, ms: U32) ->
91 IO(Socket & (U32 & Result<&1, &1, U32 & String, Unit>)):
92 import "./effs/wire.c"
93 import "./effs/wire.js"
94
95# UDP keeps the sender address and port beside the packed datagram.
96def recv_from.words(sock: Socket, max: U32, ms: U32) ->
97 IO(Socket & Result<&1, &1, U32 & String, String & U32 & (U32 & Array<U32>)>):
98 import "./effs/wire.c"
99 import "./effs/wire.js"
100
101# len past the end of words fails with EINVAL.
102def send_to.words(sock: Socket, host: String, port: U32, len: U32, words: Array<U32>) ->
103 IO(Socket & Result<&1, &1, U32 & String, Unit>):
104 import "./effs/wire.c"
105 import "./effs/wire.js"
106
107# len 0 once the peer sends close_notify.
108def tls.recv.words(sock: Socket, max: U32, ms: U32) ->
109 IO(Socket & Result<&1, &1, U32 & String, U32 & Array<U32>>):
110 import "./effs/wire.c"
111 import "./effs/wire.js"
112
113def tls.send.words(sock: Socket, len: U32, words: Array<U32>) ->
114 IO(Socket & Result<&1, &1, U32 & String, Unit>):
115 import "./effs/wire.c"
116 import "./effs/wire.js"