1 # Byte-exact TCP, UDP, and TLS sockets. Source: https://github.com/paymog/bend-kit/tree/main/wire 2 import 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. 10 def 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. 15 def 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 19 def 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. 24 def 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 30 def 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 36 def 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. 45 def 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. 53 def 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"). 62 def 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. 70 def 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. 75 def 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. 82 def 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. 87 def 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. 93 def recv.words(sock: Socket, 94 max: U32, 95 ms: U32) -> IO(Socket & Result<&1, &1, U32 & String, U32 & Array>): 96 import "./effs/wire.c" 97 import "./effs/wire.js" 98 99 # len past the end of words fails with EINVAL. 100 def send.words(sock: Socket, 101 len: U32, 102 words: Array) -> 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. 107 def recv_from.words(sock: Socket, 108 max: U32, 109 ms: U32) -> IO(Socket & Result<&1, &1, U32 & String, String & U32 & (U32 & Array)>): 110 import "./effs/wire.c" 111 import "./effs/wire.js" 112 113 # len past the end of words fails with EINVAL. 114 def send_to.words(sock: Socket, 115 host: String, 116 port: U32, 117 len: U32, 118 words: Array) -> 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. 123 def tls.recv.words(sock: Socket, 124 max: U32, 125 ms: U32) -> IO(Socket & Result<&1, &1, U32 & String, U32 & Array>): 126 import "./effs/wire.c" 127 import "./effs/wire.js" 128 129 def tls.send.words(sock: Socket, 130 len: U32, 131 words: Array) -> IO(Socket & Result<&1, &1, U32 & String, Unit>): 132 import "./effs/wire.c" 133 import "./effs/wire.js"