websocket.bend relies on unsafe/foreign
raw source on the hub · import bend-kit-websocket@0.1.0.0/websocket.bend as Websocket
RFC 6455 WebSocket client: opening handshake, frames over packed Bytes, and messages over Wire. Source: https://github.com/paymog/bend-kit/tree/main/websocket
4 imports
import Base import bend-kit-bytes@0.3.1.0/bytes.bend as Bytes import bend-kit-crypto@0.1.0.0/crypto.bend as Crypto import bend-kit-wire@0.4.2.0/wire.bend as Wire
Types
type Split source · line 235 · raw
Type
An HTTP head cut at its blank line, with the bytes after it.
Found@head:String -> @rest:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Split
Short@acc:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Split
LongSplit
type Frame source · line 267 · raw
Type
Frame@fin:Bool -> @op:U32 -> @payload:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Frame
type Decode source · line 271 · raw
Type
Need: b does not hold a whole frame yet, and comes back intact.
Need@input:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Decode
Got@frame:Frame -> @rest:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Decode
Bad@code:U32 -> @why:String -> Decode
type Partial source · line 505 · raw
Type
IdlePartial
Open@op:U32 -> @data:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Partial
type Msg source · line 509 · raw
Type
Message@op:U32 -> @data:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Msg
Control@op:U32 -> @data:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Msg
MoreMsg
type Conn source · line 625 · raw
Type
server: this end is the server (peer frames arrive masked, ours go clear), for a socket an HTTP server upgraded. input holds bytes read past the last frame. closing: we sent close.
Conn@sock:Socket -> @tls:Bool -> @server:Bool -> @input:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @partial:Partial -> @max:U32 -> @closing:Bool -> Conn
type Incoming source · line 629 · raw
Type
What recv returns. Ping is answered with a pong inside recv and not returned.
Text@data:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Incoming
Binary@data:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Incoming
Pong@data:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Incoming
Closed@code:U32 -> @reason:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Incoming
type Head source · line 660 · raw
Type
HRead@r:Pair(Socket, Result<&1, &1, Pair(U32, String), Pair(U32, Array<U32>)>) -> @acc:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Head
HSplit@sock:Socket -> @s:Split -> Head
type Step source · line 849 · raw
Type
Parse@sock:Socket -> @input:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @partial:Partial -> Step
Decoded@sock:Socket -> @partial:Partial -> @d:Decode -> Step
Fed@sock:Socket -> @rest:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @r:Result<&1, &1, Pair(U32, String), Pair(Partial, Msg)> -> Step
Read@r:Pair(Socket, Result<&1, &1, Pair(U32, String), Pair(U32, Array<U32>)>) -> @input:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @partial:Partial -> Step
Ponged@r:Pair(Socket, Result<&1, &1, Pair(U32, String), Unit>) -> @input:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @partial:Partial -> Step
Definitions
def guid source · line 14 · raw
String
def accept.input source · line 18 · raw
@key:String -> 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes
The octets SHA-1 hashes for Sec-WebSocket-Accept: the key, then the GUID.
def accept.of source · line 22 · raw
@digest:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> String
Sec-WebSocket-Accept from the SHA-1 digest of accept.input(key).
def accept.fin source · line 25 · raw
@r:Result<&1, &1, Pair(U32, String), Pair(U32, Array<U32>)> -> Result<&1, &1, Pair(U32, String), String>
def accept.words source · line 32 · raw
@b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> IO(Result<&1, &1, Pair(U32, String), Pair(U32, Array<U32>)>)
def accept source · line 37 · raw
@key:String -> IO(Result<&1, &1, Pair(U32, String), String>)
The Sec-WebSocket-Accept a server must answer to key.
def key.of source · line 43 · raw
@nonce:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> String
Sec-WebSocket-Key from a 16-octet nonce.
def key.fin source · line 46 · raw
@r:Result<&1, &1, Pair(U32, String), Pair(U32, Array<U32>)> -> Result<&1, &1, Pair(U32, String), String>
def key source · line 54 · raw
IO(Result<&1, &1, Pair(U32, String), String>)
A fresh key from 16 octets of the OS secure random source.
def first source · line 59 · raw
@m:Maybe<&1, Pair(U32, String)> -> @n:Maybe<&1, Pair(U32, String)> -> Maybe<&1, Pair(U32, String)>
def reject source · line 66 · raw
@bad:Bool -> @+code:U32 -> @+why:String -> Maybe<&1, Pair(U32, String)>
def verdict source · line 73 · raw
@-A:Type -> @m:Maybe<&1, Pair(U32, String)> -> @v:A -> Result<&1, &1, Pair(U32, String), A>
def visible source · line 81 · raw
@s:String -> Bool
Every char is visible ASCII (0x21-0x7E): no space, CR, LF, or other control.
def request source · line 91 · raw
@+host:String -> @+path:String -> @+key:String -> Result<&1, &1, Pair(U32, String), 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes>
The GET that asks to upgrade. host is the Host header (with :port when not the default); path is the origin-form target ("/" and on, percent-encoded). A char outside visible ASCII could split the head and inject headers, so it fails with EINVAL (22).
def fields.line source · line 100 · raw
@parts:List<&2, String> -> @+name:String -> @+later:List<&2, String> -> List<&2, String>
def fields.go source · line 108 · raw
@ls:List<&2, String> -> @+name:String -> List<&2, String>
def after.first source · line 115 · raw
@ls:List<&2, String> -> List<&2, String>
def headers source · line 123 · raw
@head:String -> List<&2, String>
The header lines of an HTTP head: every line after the start line.
def fields source · line 127 · raw
@head:String -> @+name:String -> List<&2, String>
The trimmed values of every header line named name (lowercase), in order.
def only source · line 130 · raw
@xs:List<&2, String> -> Maybe<&2, String>
def field source · line 138 · raw
@head:String -> @+name:String -> Maybe<&2, String>
The value of the header named name when exactly one line has it.
def line.parts source · line 143 · raw
@parts:List<&2, String> -> Bool
A header line is name ":" value, with a non-empty name that holds no space or tab (RFC 9112 §5.1). That rules out obsolete line folding and bare text.
def lines.ok source · line 150 · raw
@ls:List<&2, String> -> Bool
def status.parts source · line 157 · raw
@parts:List<&2, String> -> Bool
def status source · line 165 · raw
@ls:List<&2, String> -> Bool
The first line is exactly "HTTP/1.1 101" and a reason, with nothing before it.
def token source · line 172 · raw
@xs:List<&2, String> -> @+want:String -> Bool
def value.is source · line 179 · raw
@m:Maybe<&2, String> -> @+want:String -> Bool
def value.lower source · line 186 · raw
@m:Maybe<&2, String> -> Maybe<&2, String>
def tokens source · line 194 · raw
@xs:List<&2, String> -> @+want:String -> Bool
Connection is a list header: its lines join into one comma list (RFC 9110 §5.3).
def none source · line 201 · raw
@xs:List<&2, String> -> Bool
def check source · line 211 · raw
@+head:String -> @+want:String -> Result<&1, &1, Pair(U32, String), Unit>
Does a server's head (up to its blank line) accept the upgrade? want is accept(key). Upgrade and Sec-WebSocket-Accept must each come once. No extension or subprotocol was asked for, so the server may name none (§4.1).
def with_number source · line 222 · raw
@-R:Type -> @r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>) -> @k:(@_:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @_:U32 -> R) -> R
def with_slice source · line 230 · raw
@-R:Type -> @r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes) -> @k:(@_:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @_:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> R) -> R
def split.long source · line 240 · raw
@long:Bool -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Split
def split.short source · line 247 · raw
@b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Split
def split.found source · line 251 · raw
@r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>) -> Split
def split source · line 262 · raw
@b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Split
Found once b holds "\r\n\r\n"; Long when 16 KiB came without it.
def mask.go source · line 276 · raw
@n:Nat -> @r:Pair(Array<U32>, U32) -> @+i:U32 -> @+key:U32 -> Array<U32>
def mask.trim.word source · line 286 · raw
@+k:U32 -> @+m:U32 -> @r:Pair(Array<U32>, U32) -> Array<U32>
def mask.trim.if source · line 290 · raw
@whole:Bool -> @+k:U32 -> @+m:U32 -> @a:Array<U32> -> Array<U32>
def mask.trim source · line 298 · raw
@+len:U32 -> @a:Array<U32> -> Array<U32>
Bytes at or past len stay 0, as Bytes requires.
def mask source · line 304 · raw
@+key:U32 -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes
The payload XOR the mask key (§5.3), a word at a time. key's byte k (low first) is mask octet k. Masking twice gives the payload back.
def known source · line 308 · raw
@+op:U32 -> Bool
def rules source · line 326 · raw
@+fin:Bool -> @+rsv:U32 -> @+op:U32 -> @+len:U32 -> Maybe<&1, Pair(U32, String)>
The §5.2 and §5.5 rules on a frame header. No extension is in use, so RSV must be 0.
def encode.ext source · line 332 · raw
@+ext:U32 -> @+len:U32 -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes
def encode.mkey source · line 341 · raw
@+keylen:U32 -> @+at:U32 -> @+key:U32 -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes
def encode.copy source · line 348 · raw
@+len:U32 -> @src:Array<U32> -> @+hs:U32 -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes
def encode.frame source · line 353 · raw
@+b0:U32 -> @+mbit:U32 -> @+keylen:U32 -> @+key:U32 -> @p:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes
Payload lengths use the shortest form (§5.2): 7 bits, then 16, then 64.
def encode.key source · line 361 · raw
@key:Maybe<&2, U32> -> @+b0:U32 -> @p:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes
def encode.checked source · line 368 · raw
@err:Maybe<&1, Pair(U32, String)> -> @+fin:Bool -> @+op:U32 -> @key:Maybe<&2, U32> -> @p:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Result<&1, &1, Pair(U32, String), 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes>
def encode source · line 377 · raw
@f:Frame -> @key:Maybe<&2, U32> -> Result<&1, &1, Pair(U32, String), 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes>
A frame's octets. A client passes Some{mask key} and the payload goes masked; None sends it clear, as a server does. Fails on a frame that breaks rules.
def unmask source · line 382 · raw
@m:Bool -> @+key:U32 -> @p:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes
def parse.done source · line 390 · raw
@+b0:U32 -> @+m:Bool -> @+key:U32 -> @+end:U32 -> @payload:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Decode
ponytail: rest is a copy, so a buffer of k frames costs O(k * size); keep an offset if many small frames share one read.
def parse.take source · line 395 · raw
@short:Bool -> @+b0:U32 -> @+m:Bool -> @+len:U32 -> @+hs:U32 -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Decode
def parse.body source · line 404 · raw
@err:Maybe<&1, Pair(U32, String)> -> @+b0:U32 -> @+m:Bool -> @+len:U32 -> @+hs:U32 -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Decode
def parse.check source · line 413 · raw
@+masked:Bool -> @+max:U32 -> @+b0:U32 -> @+hi:U32 -> @+len:U32 -> @+m:Bool -> @+hs:U32 -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Decode
A 64-bit length with a high word saturates to 2^32 - 1, over any max.
def parse.len source · line 421 · raw
@short:Bool -> @+masked:Bool -> @+max:U32 -> @+b0:U32 -> @+l7:U32 -> @+ext:U32 -> @+m:Bool -> @+hs:U32 -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Decode
def parse.head source · line 437 · raw
@+masked:Bool -> @+max:U32 -> @+b0:U32 -> @+b1:U32 -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Decode
def parse.start source · line 445 · raw
@short:Bool -> @+masked:Bool -> @+max:U32 -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Decode
def parse source · line 457 · raw
@+masked:Bool -> @+max:U32 -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Decode
One frame from the front of b, unmasked. masked: this peer's frames must be masked (true on a server, false on a client). max caps the payload length (1009 over it). The header is checked as soon as it is whole, before its payload arrives.
def st source · line 463 · raw
@+need:U32 -> @+lo:U32 -> @+hi:U32 -> U32
UTF-8 (RFC 3629) validity. The state packs need | lo << 8 | hi << 16: continuation bytes still needed and the range the next one must fall in. need 255 is a failure.
def utf8.lead source · line 466 · raw
@+b:U32 -> U32
def utf8.next source · line 477 · raw
@+s:U32 -> @+b:U32 -> U32
def utf8.go source · line 484 · raw
@n:Nat -> @r:Pair(Array<U32>, U32) -> @+i:U32 -> @+s:U32 -> Pair(Array<U32>, Bool)
def utf8.fin source · line 494 · raw
@+len:U32 -> @r:Pair(Array<U32>, Bool) -> Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Bool)
def utf8.valid source · line 499 · raw
@b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Bool)
b back, and whether it is well-formed UTF-8.
def done.text source · line 514 · raw
@ok:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Bool) -> Result<&1, &1, Pair(U32, String), Pair(Partial, Msg)>
def finish source · line 522 · raw
@+op:U32 -> @d:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Result<&1, &1, Pair(U32, String), Pair(Partial, Msg)>
def feed.more source · line 529 · raw
@fin:Bool -> @+op:U32 -> @d:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Result<&1, &1, Pair(U32, String), Pair(Partial, Msg)>
def feed.size source · line 536 · raw
@over:Bool -> @+fin:Bool -> @+op:U32 -> @d:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Result<&1, &1, Pair(U32, String), Pair(Partial, Msg)>
def feed.join source · line 543 · raw
@+fin:Bool -> @+op:U32 -> @d:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @+max:U32 -> Result<&1, &1, Pair(U32, String), Pair(Partial, Msg)>
def feed.data source · line 547 · raw
@p:Partial -> @+fin:Bool -> @+op:U32 -> @d:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @+max:U32 -> Result<&1, &1, Pair(U32, String), Pair(Partial, Msg)>
def feed.kind source · line 562 · raw
@ctl:Bool -> @p:Partial -> @+fin:Bool -> @+op:U32 -> @d:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @+max:U32 -> Result<&1, &1, Pair(U32, String), Pair(Partial, Msg)>
def feed source · line 571 · raw
@p:Partial -> @f:Frame -> @+max:U32 -> Result<&1, &1, Pair(U32, String), Pair(Partial, Msg)>
One parsed frame into the message being built. A whole text message must be UTF-8 (1007); max caps a message's total size (1009). Keep max at or below 2^31.
def close.payload source · line 578 · raw
@+code:U32 -> @reason:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes
code (big-endian) then a UTF-8 reason.
def close.code.ok source · line 582 · raw
@+c:U32 -> Bool
Codes a peer may send: §7.4.1, the IANA registry through 1014, and 3000-4999.
def close.utf8 source · line 587 · raw
@+c:U32 -> @r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Bool) -> Result<&1, &1, Pair(U32, String), Pair(U32, 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes)>
def close.reason source · line 595 · raw
@ok:Bool -> @+c:U32 -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Result<&1, &1, Pair(U32, String), Pair(U32, 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes)>
def close.code source · line 604 · raw
@r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>) -> Result<&1, &1, Pair(U32, String), Pair(U32, 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes)>
def close.parse source · line 613 · raw
@p:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Result<&1, &1, Pair(U32, String), Pair(U32, 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes)>
The code and reason of a close frame. An empty payload is 1005 (no status).
def io.send.if source · line 635 · raw
@tls:Bool -> @sock:Socket -> @+len:U32 -> @buf:Array<U32> -> IO(Pair(Socket, Result<&1, &1, Pair(U32, String), Unit>))
def io.send source · line 642 · raw
@sock:Socket -> @+tls:Bool -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> IO(Pair(Socket, Result<&1, &1, Pair(U32, String), Unit>))
def io.recv source · line 646 · raw
@tls:Bool -> @sock:Socket -> @+ms:U32 -> IO(Pair(Socket, Result<&1, &1, Pair(U32, String), Pair(U32, Array<U32>)>))
def io.close source · line 653 · raw
@tls:Bool -> @sock:Socket -> IO(Unit)
def head.loop source · line 664 · raw
@n:Nat -> @h:Head -> @+tls:Bool -> @+ms:U32 -> IO(Pair(Socket, Result<&1, &1, Pair(U32, String), Pair(String, 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes)>))
def head.read source · line 701 · raw
@sock:Socket -> @+tls:Bool -> @+ms:U32 -> IO(Pair(Socket, Result<&1, &1, Pair(U32, String), Pair(String, 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes)>))
Reads an HTTP head: the text before its blank line, and the bytes after it.
def hs.fail source · line 704 · raw
@sock:Socket -> @+tls:Bool -> @e:Pair(U32, String) -> IO(Result<&1, &1, Pair(U32, String), Conn>)
def hs.checked source · line 709 · raw
@v:Result<&1, &1, Pair(U32, String), Unit> -> @sock:Socket -> @+tls:Bool -> @+max:U32 -> @rest:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> IO(Result<&1, &1, Pair(U32, String), Conn>)
def hs.head source · line 717 · raw
@r:Pair(Socket, Result<&1, &1, Pair(U32, String), Pair(String, 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes)>) -> @+tls:Bool -> @+max:U32 -> @+want:String -> IO(Result<&1, &1, Pair(U32, String), Conn>)
def hs.sent source · line 724 · raw
@r:Pair(Socket, Result<&1, &1, Pair(U32, String), Unit>) -> @+tls:Bool -> @+max:U32 -> @+ms:U32 -> @+want:String -> IO(Result<&1, &1, Pair(U32, String), Conn>)
def hs.request source · line 733 · raw
@q:Result<&1, &1, Pair(U32, String), 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes> -> @sock:Socket -> @+tls:Bool -> @+max:U32 -> @+ms:U32 -> @+want:String -> IO(Result<&1, &1, Pair(U32, String), Conn>)
def hs.accepted source · line 742 · raw
@a:Result<&1, &1, Pair(U32, String), String> -> @sock:Socket -> @+tls:Bool -> @+host:String -> @+path:String -> @+max:U32 -> @+ms:U32 -> @+key:String -> IO(Result<&1, &1, Pair(U32, String), Conn>)
def hs.keyed source · line 749 · raw
@k:Result<&1, &1, Pair(U32, String), String> -> @sock:Socket -> @+tls:Bool -> @+host:String -> @+path:String -> @+max:U32 -> @+ms:U32 -> IO(Result<&1, &1, Pair(U32, String), Conn>)
def handshake source · line 761 · raw
@sock:Socket -> @+tls:Bool -> @+host:String -> @+path:String -> @+max:U32 -> @+ms:U32 -> IO(Result<&1, &1, Pair(U32, String), Conn>)
The opening handshake on a connected socket (TLS already up when tls). host is the Host header and path the request target. max caps frame and message sizes. On failure the socket is closed. Bytes the server sent after its head stay in the Conn.
def connect.tls source · line 766 · raw
@r:Pair(Socket, Result<&1, &1, Pair(U32, String), Unit>) -> @+authority:String -> @+path:String -> @+max:U32 -> @+ms:U32 -> IO(Result<&1, &1, Pair(U32, String), Conn>)
def connect.tcp source · line 773 · raw
@c:Result<&1, &1, Pair(U32, String), Socket> -> @+tls:Bool -> @+host:String -> @+authority:String -> @+path:String -> @+max:U32 -> @+ms:U32 -> IO(Result<&1, &1, Pair(U32, String), Conn>)
def connect source · line 789 · raw
@+addr:String -> @+port:U32 -> @+host:String -> @+path:String -> @+tls:Bool -> @+max:U32 -> @+ms:U32 -> IO(Result<&1, &1, Pair(U32, String), Conn>)
ws:// (tls false) or wss:// to a numeric address (resolve names with dns). host is the server name, for Host and TLS verification. ms bounds each socket step.
def raw.encoded source · line 796 · raw
@e:Result<&1, &1, Pair(U32, String), 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes> -> @sock:Socket -> @+tls:Bool -> IO(Pair(Socket, Result<&1, &1, Pair(U32, String), Unit>))
def raw.key source · line 804 · raw
@r:Pair(Array<U32>, U32) -> @sock:Socket -> @+tls:Bool -> @+fin:Bool -> @+op:U32 -> @payload:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> IO(Pair(Socket, Result<&1, &1, Pair(U32, String), Unit>))
def raw.keyed source · line 808 · raw
@k:Result<&1, &1, Pair(U32, String), Pair(U32, Array<U32>)> -> @sock:Socket -> @+tls:Bool -> @+fin:Bool -> @+op:U32 -> @payload:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> IO(Pair(Socket, Result<&1, &1, Pair(U32, String), Unit>))
def raw source · line 817 · raw
@server:Bool -> @sock:Socket -> @+tls:Bool -> @+fin:Bool -> @+op:U32 -> @payload:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> IO(Pair(Socket, Result<&1, &1, Pair(U32, String), Unit>))
A client masks every frame with a fresh key from the OS secure random source (§5.3).
def back source · line 826 · raw
@r:Pair(Socket, Result<&1, &1, Pair(U32, String), Unit>) -> @+tls:Bool -> @+server:Bool -> @input:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @partial:Partial -> @+max:U32 -> @+closing:Bool -> Pair(Conn, Result<&1, &1, Pair(U32, String), Unit>)
def send source · line 831 · raw
@c:Conn -> @+fin:Bool -> @+op:U32 -> @payload:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> IO(Pair(Conn, Result<&1, &1, Pair(U32, String), Unit>))
Sends one frame: fin false starts or continues a fragmented message (later frames use op 0).
def close source · line 838 · raw
@c:Conn -> @+code:U32 -> @reason:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> IO(Pair(Conn, Result<&1, &1, Pair(U32, String), Unit>))
Sends a close frame; recv then runs until the peer's close comes back as Closed.
def shutdown source · line 845 · raw
@c:Conn -> IO(Unit)
Closes the socket (and its TLS session).
def recv.out source · line 856 · raw
@sock:Socket -> @+tls:Bool -> @+server:Bool -> @input:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @partial:Partial -> @+max:U32 -> @+closing:Bool -> @r:Result<&1, &1, Pair(U32, String), Incoming> -> IO(Pair(Conn, Result<&1, &1, Pair(U32, String), Incoming>))
def recv.failed source · line 860 · raw
@r:Pair(Socket, Result<&1, &1, Pair(U32, String), Unit>) -> @+tls:Bool -> @+server:Bool -> @+max:U32 -> @e:Pair(U32, String) -> IO(Pair(Conn, Result<&1, &1, Pair(U32, String), Incoming>))
def recv.fail source · line 865 · raw
@sock:Socket -> @+tls:Bool -> @+server:Bool -> @+max:U32 -> @e:Pair(U32, String) -> IO(Pair(Conn, Result<&1, &1, Pair(U32, String), Incoming>))
A peer broke the protocol: send close with its code (§7.1.7), then report it.
def recv.echoed source · line 871 · raw
@r:Pair(Socket, Result<&1, &1, Pair(U32, String), Unit>) -> @+tls:Bool -> @+server:Bool -> @rest:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @partial:Partial -> @+max:U32 -> @+code:U32 -> @reason:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> IO(Pair(Conn, Result<&1, &1, Pair(U32, String), Incoming>))
def recv.closed source · line 875 · raw
@r:Result<&1, &1, Pair(U32, String), Pair(U32, 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes)> -> @sock:Socket -> @+tls:Bool -> @+server:Bool -> @rest:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @partial:Partial -> @+max:U32 -> @closing:Bool -> IO(Pair(Conn, Result<&1, &1, Pair(U32, String), Incoming>))
def recv.stuck source · line 889 · raw
@h:Step -> @+tls:Bool -> @+server:Bool -> @+max:U32 -> IO(Pair(Conn, Result<&1, &1, Pair(U32, String), Incoming>))
def recv.loop source · line 904 · raw
@n:Nat -> @h:Step -> @+tls:Bool -> @+server:Bool -> @+max:U32 -> @+closing:Bool -> @+ms:U32 -> IO(Pair(Conn, Result<&1, &1, Pair(U32, String), Incoming>))
def recv source · line 959 · raw
@c:Conn -> @+ms:U32 -> IO(Pair(Conn, Result<&1, &1, Pair(U32, String), Incoming>))
The next message, pong, or close. Fragments are joined and pings answered on the way. A protocol fault sends close with its code and fails with it. ms bounds each read.