websocks
Types
Status codes that may appear in a close frame.
The codes reserved for local use only, such as 1005, 1006 and 1015, are absent. They must never be sent, and receiving one is a protocol violation.
pub type CloseCode {
NormalClosure
GoingAway
ProtocolError
UnsupportedData
InvalidPayloadData
PolicyViolation
MessageTooBig
MandatoryExtension
InternalError
ServiceRestart
TryAgainLater
BadGateway
ApplicationCode(code: Int)
}
Constructors
-
NormalClosureThe connection successfully completed its purpose and is closing normally.
-
GoingAwayThe endpoint is going away, either due to server shutdown or browser navigation.
-
ProtocolErrorA WebSocket protocol violation was detected.
-
UnsupportedDataThe endpoint received data it cannot accept.
-
InvalidPayloadDataThe message data doesn’t match the declared type.
-
PolicyViolationGeneric status for policy violations when no other code applies.
-
MessageTooBigMessage exceeds the maximum size the endpoint can handle.
-
MandatoryExtensionThe server encountered an unexpected condition preventing request fulfillment.
-
InternalErrorThe server encountered unexpected error.
-
ServiceRestartServer is restarting.
-
TryAgainLaterTemporary server overload.
-
BadGatewayGateway/proxy received invalid response.
-
ApplicationCode(code: Int)An application-specific code, between 3000 and 4999.
Why the connection is closing. Close frame is allowed to carry neither code nor reason.
pub type CloseReason {
NoCloseReason
CloseReason(code: CloseCode, reason: String)
}
Constructors
-
NoCloseReasonA close frame with an empty payload.
-
CloseReason(code: CloseCode, reason: String)A close frame carrying a status code and an optional description.
Negotiated compression extension parameters from the WebSocket handshake.
These parameters control per-message-deflate compression behavior. Obtained
by calling get_compression_extensions on the Sec-WebSocket-Extensions
header value.
pub type CompressionExtensions {
CompressionExtensions(
client_no_context_takeover: Bool,
client_max_window_bits: option.Option(Int),
server_no_context_takeover: Bool,
server_max_window_bits: option.Option(Int),
)
}
Constructors
-
CompressionExtensions( client_no_context_takeover: Bool, client_max_window_bits: option.Option(Int), server_no_context_takeover: Bool, server_max_window_bits: option.Option(Int), )Arguments
- client_no_context_takeover
-
Client does not use context takeover
- client_max_window_bits
-
Client’s maximum LZ77 window size for compression
- server_no_context_takeover
-
Server does not use context takeover
- server_max_window_bits
-
Server’s maximum LZ77 window size for compression
Context is the internal state of the WebSocket connection. It stores the remaining bytes from the decoding process, fragment accumulation and compression states.
pub opaque type Context
Control frames that can appear in WebSocket communication.
pub type Control {
Ping(payload: BitArray)
Pong(payload: BitArray)
Close(reason: CloseReason)
}
Constructors
-
Ping(payload: BitArray)A ping control frame. Used for keepalive.
-
Pong(payload: BitArray)A pong control frame. Response to ping.
-
Close(reason: CloseReason)A close control frame. Contains the reason for closing if present.
Errors that can occur during the decoding process.
pub type DecodeError {
InvalidFrame
NotEnoughData(data: BitArray)
FrameTooLarge(length: Int, limit: Int)
}
Constructors
-
InvalidFrameThe frame is invalid.
-
NotEnoughData(data: BitArray)The data is not enough to decode the frame.
-
FrameTooLarge(length: Int, limit: Int)The frame declares a payload larger than the context’s
max_frame_size.
The outcome of decoding one frame with next_frame.
pub type Decoded {
MoreData(context: Context)
Decoded(frame: Frame, context: Context)
}
Constructors
-
MoreData(context: Context)The buffer holds no further complete frame. Its leftover bytes are kept in the returned context, to be joined with the next read.
-
A frame, with fragmentation reassembled and compression applied.
Each variant corresponds to a type of WebSocket frame, carrying the appropriate payload.
pub type Frame {
Continuation(payload: BitArray)
Text(payload: BitArray)
Binary(payload: BitArray)
Control(control: Control)
}
Constructors
-
Continuation(payload: BitArray)A continuation frame, used for fragmented messages.
-
Text(payload: BitArray)A text frame, containing UTF-8 encoded payload.
-
Binary(payload: BitArray)A binary frame, containing arbitrary binary data.
-
Control(control: Control)A control frame, controlling WebSocket connection.
Caps on how much data a peer can make this endpoint hold at once. Without them a peer can declare an arbitrarily large frame, or fragment a single message indefinitely, and exhaust memory.
pub type Limits {
Limits(max_frame_size: Int, max_message_size: Int)
}
Constructors
-
Limits(max_frame_size: Int, max_message_size: Int)Arguments
- max_frame_size
-
Largest payload a single frame may declare.
- max_message_size
-
Largest payload a fragmented message may accumulate to.
Errors that can occur in next_frame. Each means the peer has broken the
protocol and the connection should be closed.
pub type ProcessError {
DecodeFailed(reason: DecodeError)
ResolveFailed(reason: ResolveError)
}
Constructors
-
DecodeFailed(reason: DecodeError)Frame decoding failed with the given decode error.
-
ResolveFailed(reason: ResolveError)Frame resolution failed with the given resolve error.
Errors that can occur during the resolving fragments process.
pub type ResolveError {
NotUtf8
OrphanedContinuation
FragmentationInterrupted
ConcurrentFragmentation
MessageTooLarge(size: Int, limit: Int)
DecompressionFailed
}
Constructors
-
NotUtf8The complete text frame payload is not UTF-8.
-
OrphanedContinuationReceive continuation frame as the first frame in a fragmentation sequence.
-
FragmentationInterruptedReceive complete text/binary frame when resolving fragmented frame.
-
ConcurrentFragmentationReceive incomplete text/binary frame when resolving fragmented frame.
-
MessageTooLarge(size: Int, limit: Int)The fragments accumulated past the context’s
max_message_size. -
DecompressionFailedThe payload is not a valid deflate stream, or inflating it would exceed the context’s
max_message_size.
Values
pub fn close_context(context: Context) -> Nil
Frees the compression resources. Should be called when the context is no longer needed.
Example
websocks.close_context(context)
// => Nil
pub fn compute_accept(key: String) -> String
Computes the value of the Sec-WebSocket-Accept header during the handshake.
Requires Sec-WebSocket-Key header value to be present.
Example
websocks.compute_accept("dGhlIHNhbXBsZSBub25jZQ==")
// => "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
pub fn create_context(
extensions: option.Option(CompressionExtensions),
role: Role,
) -> Context
Creates a new context with optional compression settings and role specification. The role parameter determines how compression parameters are interpreted.
Example
let extensions = websocks.get_compression_extensions([
"permessage-deflate",
"client_no_context_takeover",
])
websocks.create_context(Some(extensions), websocks.Client)
// => Context
pub const default_limits: Limits
16 MiB per frame, 64 MiB per reassembled message. Use with_limits function
to override the limits.
pub fn encode_binary_frame(
payload payload: BitArray,
context context: Context,
masking masking: option.Option(BitArray),
) -> BitArray
Encodes a binary frame with the given payload, context and mask. If the context has compression enabled, the payload will be compressed.
pub fn encode_close_frame(
reason reason: CloseReason,
masking masking: option.Option(BitArray),
) -> BitArray
Encodes a close frame with the given reason and mask.
pub fn encode_ping_frame(
payload payload: BitArray,
masking masking: option.Option(BitArray),
) -> BitArray
Encodes a ping frame with the given payload and mask.
pub fn encode_pong_frame(
payload payload: BitArray,
masking masking: option.Option(BitArray),
) -> BitArray
Encodes a pong frame with the given payload and mask.
pub fn encode_text_frame(
payload payload: BitArray,
context context: Context,
masking masking: option.Option(BitArray),
) -> BitArray
Encodes a text frame with the given payload, context and masking. If the context has compression enabled, the payload will be compressed.
pub fn get_compression_extensions(
header: String,
) -> CompressionExtensions
Parses compression extension parameters out of a Sec-WebSocket-Extensions
header value. Extracts context takeover settings as well as window bits.
Note that a header offering several alternatives is read as one set of parameters rather than as competing offers to choose between.
Example
let header =
"permessage-deflate; client_no_context_takeover; client_max_window_bits=15"
websocks.get_compression_extensions(header)
// => CompressionExtensions(
// client_no_context_takeover: True,
// client_max_window_bits: Some(15),
// server_no_context_takeover: False,
// server_max_window_bits: None,
// )
pub fn has_deflate(header: String) -> Bool
Checks whether a Sec-WebSocket-Extensions header value offers
permessage-deflate.
Example
let header =
request.get_header(req, "sec-websocket-extensions")
|> result.unwrap("")
// => "permessage-deflate; client_no_context_takeover"
websocks.has_deflate(header)
// => True
pub const magic_string: String
Sequence of characters that is used to compute the Sec-WebSocket-Accept
header during the handshake.
pub fn mask(payload: BitArray, mask: BitArray) -> BitArray
Masks the payload of any length using the provided mask. Masking is its own inverse, so this unmasks too. A mask key of no bytes leaves the payload alone.
Example
let payload = bit_array.from_string("Hello")
// Original: H(0x48) e(0x65) l(0x6c) l(0x6c) o(0x6f)
// Mask: 0x37 0xfa 0x21 0x3d 0x37
// Masked: 0x7f 0x9f 0x4d 0x51 0x58
websocks.mask(payload, <<0x37, 0xfa, 0x21, 0x3d>>)
// => <<0x7f, 0x9f, 0x4d, 0x51, 0x58>>
pub fn next_frame(
context: Context,
) -> Result(Decoded, ProcessError)
Decodes the next frame from the context’s buffer, resolving fragmentation and
decompression. Fragments are consumed internally, so MoreData always means
the connection needs another read rather than another call.
Example
// 0x81 : fin=1, rsv1-3=0, opcode=1
// 0x05 : mask=0, payload length=5
let frame = <<0x81, 0x05>>
// 0x48 0x65 0x6c 0x6c 0x6f : "Hello"
let payload = <<0x48, 0x65, 0x6c, 0x6c, 0x6f>>
let context =
websocks.create_context(None, websocks.Client)
|> websocks.push_data(<<frame:bits, payload:bits, frame:bits>>)
let assert Ok(websocks.Decoded(frame:, context:)) =
websocks.next_frame(context)
// frame => Text(<<"Hello">>)
// The trailing header alone is not a whole frame, so it stays buffered.
websocks.next_frame(context)
// => Ok(MoreData(context))
pub fn push_data(context: Context, data: BitArray) -> Context
Adds a read to the context’s buffer, ready to be drained by next_frame.
pub fn websocket_key() -> String
Generates a random WebSocket key for the Sec-WebSocket-Key header.
Used by clients during the handshake.
Example
websocks.websocket_key()
// => "dGhlIHNhbXBsZSBub25jZQ=="