libs/wire/wire.bend
qasim-bend-kit@0.1.0.1 · 133 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) -> IO(Result<&1, &1, U32 & String, Socket>):
11 import "./effs/wire.c"
12 import "./effs/wire.js"
13
14# Octet-String TCP and UDP effects are the slow path: one Char cell per byte.
15def recv(sock: Socket, max: U32, ms: U32) -> IO(Socket & Result<&1, &1, U32 & String, String>):
16 import "./effs/wire.c"
17 import "./effs/wire.js"
18
19def send(sock: Socket, data: String) -> IO(Socket & Result<&1, &1, U32 & String, Unit>):
20 import "./effs/wire.c"
21 import "./effs/wire.js"
22
23# Like send, but interrupts a blocked write at the supplied millisecond deadline.
24def send.timeout(sock: Socket,
25 data: String,
26 ms: U32) -> IO(Socket & Result<&1, &1, U32 & String, Unit>):
27 import "./effs/wire.c"
28 import "./effs/wire.js"
29
30def recv_from(sock: Socket,
31 max: U32,
32 ms: U32) -> IO(Socket & Result<&1, &1, U32 & String, String & U32 & String>):
33 import "./effs/wire.c"
34 import "./effs/wire.js"
35
36def send_to(sock: Socket,
37 host: String,
38 port: U32,
39 data: String) -> IO(Socket & Result<&1, &1, U32 & String, Unit>):
40 import "./effs/wire.c"
41 import "./effs/wire.js"
42
43# TLS over a connected TCP socket (OpenSSL 3, loaded at run time; BEND_LIBSSL
44# overrides its path). The certificate chain and host name are always checked.
45def tls.connect(sock: Socket,
46 host: String,
47 ms: U32) -> IO(Socket & Result<&1, &1, U32 & String, Unit>):
48 import "./effs/wire.c"
49 import "./effs/wire.js"
50
51# Use a PEM certificate chain and its private key for mutual TLS.
52# A load failure is EINVAL; a handshake or server verification failure is EPROTO.
53def tls.connect.cert(sock: Socket,
54 host: String,
55 ms: U32,
56 cert_path: String,
57 key_path: String) -> IO(Socket & Result<&1, &1, U32 & String, Unit>):
58 import "./effs/wire.c"
59 import "./effs/wire.js"
60
61# protos is a comma-separated ALPN list (for example "h2,http/1.1").
62def tls.connect.alpn(sock: Socket,
63 host: String,
64 ms: U32,
65 protos: String) -> IO(Socket & Result<&1, &1, U32 & String, String>):
66 import "./effs/wire.c"
67 import "./effs/wire.js"
68
69# The String TLS effects are the slow path: one Char cell per octet.
70def tls.send(sock: Socket, data: String) -> IO(Socket & Result<&1, &1, U32 & String, Unit>):
71 import "./effs/wire.c"
72 import "./effs/wire.js"
73
74# Like tls.send, but interrupts a blocked SSL write at the supplied deadline.
75def tls.send.timeout(sock: Socket,
76 data: String,
77 ms: U32) -> IO(Socket & Result<&1, &1, U32 & String, Unit>):
78 import "./effs/wire.c"
79 import "./effs/wire.js"
80
81# "" once the peer sends close_notify. A bare EOF is an error.
82def tls.recv(sock: Socket, max: U32, ms: U32) -> IO(Socket & Result<&1, &1, U32 & String, String>):
83 import "./effs/wire.c"
84 import "./effs/wire.js"
85
86# Ends the TLS session and closes the socket.
87def tls.close(sock: Socket) -> IO(Unit):
88 import "./effs/wire.c"
89 import "./effs/wire.js"
90
91# The .words forms move (len, words): bytes packed four to a U32, laid out as
92# bytes/bytes.bend packs them, so no String cell is made per byte.
93def recv.words(sock: Socket,
94 max: U32,
95 ms: U32) -> IO(Socket & Result<&1, &1, U32 & String, U32 & Array<U32>>):
96 import "./effs/wire.c"
97 import "./effs/wire.js"
98
99# len past the end of words fails with EINVAL.
100def send.words(sock: Socket,
101 len: U32,
102 words: Array<U32>) -> IO(Socket & Result<&1, &1, U32 & String, Unit>):
103 import "./effs/wire.c"
104 import "./effs/wire.js"
105
106# UDP keeps the sender address and port beside the packed datagram.
107def recv_from.words(sock: Socket,
108 max: U32,
109 ms: U32) -> IO(Socket & Result<&1, &1, U32 & String, String & U32 & (U32 & Array<U32>)>):
110 import "./effs/wire.c"
111 import "./effs/wire.js"
112
113# len past the end of words fails with EINVAL.
114def send_to.words(sock: Socket,
115 host: String,
116 port: U32,
117 len: U32,
118 words: Array<U32>) -> IO(Socket & Result<&1, &1, U32 & String, Unit>):
119 import "./effs/wire.c"
120 import "./effs/wire.js"
121
122# len 0 once the peer sends close_notify.
123def tls.recv.words(sock: Socket,
124 max: U32,
125 ms: U32) -> IO(Socket & Result<&1, &1, U32 & String, U32 & Array<U32>>):
126 import "./effs/wire.c"
127 import "./effs/wire.js"
128
129def tls.send.words(sock: Socket,
130 len: U32,
131 words: Array<U32>) -> IO(Socket & Result<&1, &1, U32 & String, Unit>):
132 import "./effs/wire.c"
133 import "./effs/wire.js"