libs/wire/wire.bend

qasim-bend-kit@0.1.0.2 · 133 lines · 19 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"