silk.http.websocket_stream.
WebSocket sessions over a byte duplex, for clients and servers, with a split form for fibers.
When to use#
After an opening handshake, make a WebSocket around the connection with WebSocket.make.
Then call WebSocket.readEvent in a loop, and answer with WebSocket.writeText or
WebSocket.writeBinary. silk.http.websocket_client and silk.http.websocket_upgrade do the
handshake and make the session for you.
When one fiber must wait for messages while other fibers send, split the session with
fromHalves. The ReadHalf reads and answers Pings and Closes, and each WriteHalf
(clone it for each fiber) sends. Writes from several fibers are sent one at a time, never mixed.
Details#
A session reads into its own buffer, and keeps the bytes that came after a frame for the next
call. Each readEvent returns one complete event:
TextandBinary: the message is inWebSocket.messageuntil the nextreadEvent.Ping: the Pong was already sent.Pong: the payload of a Pong.Close: the peer sent Close. The answer Close was already sent, and the session isClosed. Close the connection.
A frame that breaks a rule fails the call with Protocol. Before that, the session sends a
Close with the code of the error. Every later call fails.
Errors#
StreamError keeps the frame error or the transport error. OutOfMemoryError if a buffer
cannot grow.
Gotchas#
A deadline applies to one call. A read that passes its deadline fails the session, as any transport error does.
The session does not close the transport. Close it when the session is
Closedor failed.
Examples#
Answer a message over a memory duplex#
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.collections.bytes { Bytes }
import silk.collections.list { List }
import silk.core.effect { Effect }
import silk.core.option { Option }
import silk.io.memory_byte_duplex { MemoryByteDuplex, MemoryReadEvent, MemoryWriteAction, MemoryWriteEvent }
import silk.system.monotonic_clock { MonotonicClock }
import silk.system.monotonic_clock_native { OsMonotonicClock }
import silk.system.random { Random }
import silk.system.random_native { OsRandom }
import silk.system.system_clock { Instant, SystemClock }
import silk.http.transport as Transport
import silk.http.websocket { Event, Opcode, Role, appendFrame }
import silk.http.websocket_stream { Options, StreamError, WebSocket }
effect fn program() -> i32
! StreamError | OutOfMemoryError
? &mut Allocator | &mut MonotonicClock | &mut Random {
// The client sends one masked Text frame, and the server accepts all writes.
let mut wire = Bytes.make()
run appendFrame(&mut wire, Opcode.Text, true, b"ping", Option<[u8; 4]>.some([9, 8, 7, 6]))
let mut reads = List<MemoryReadEvent>.make()
run reads.append(MemoryReadEvent.Data {readyAt: SystemClock.make(0, 0), bytes: move wire})
let mut writes = List<MemoryWriteEvent>.make()
run writes.append(MemoryWriteEvent {
readyAt: SystemClock.make(0, 0),
action: MemoryWriteAction.Accept {count: 64},
})
let mut memory = run MemoryByteDuplex.make(move reads, move writes, 64, 8, Option<i32>.none())
let mut socket = run WebSocket.make(Transport.plain(&mut memory), Role.Server, Options.defaults(), b"")
match run socket.readEvent(Option<Instant>.none()) {
Event.Text {..} => {
if socket.messageText() != "ping" { return 1 }
}
_ => { return 2 }
}
run socket.writeText("pong", Option<Instant>.none())
drop socket
// An unmasked Text frame: 0x81, length 4, then "pong".
if memory.outbound() != b"\x81\x04pong" { return 3 }
return 0
}
effect fn failed<E>(error: E) -> i32 {
drop error
return 1
}
pub fn main() -> i32 {
let mut allocator = Allocator.systemAllocatorProvider()
let mut clock = OsMonotonicClock.make()
let mut random = OsRandom.make()
return run Effect.catchAll(program(), failed)
|> Effect.provideMut<Allocator>(&mut allocator)
|> Effect.provideMut<MonotonicClock>(&mut clock)
|> Effect.provideMut<Random>(&mut random)
}Import as WebSocket with import silk.http.websocket_stream { WebSocket }.
Public declarations: 10.
StreamError#
pub union StreamErrorThe error of a session over a byte duplex.
Protocol#
ProtocolA frame or state rule failed. See WebSocketError of silk.http.websocket.
Field error#
pub error: WebSocketErrorThe rule that failed.
Transport#
TransportThe transport failed, or a deadline passed.
Field error#
pub error: TransportErrorThe transport error.
Truncated#
TruncatedThe transport ended before the Close of the peer.
PreviousFailure#
PreviousFailureAn earlier transport error or truncation ended the session.
Implementation Debug for StreamError#
impl Debug for StreamErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the variant and its fields, such as Io { error: ... }.
Options#
pub struct OptionsThe options of a session.
Field limits#
pub limits: LimitsThe frame and message limits.
Field readCapacity#
pub readCapacity: usizeThe size of one read from the transport. It is more than zero.
Implementation Copy for Options#
impl Copy for OptionsImplementation Options#
impl OptionsAssociated function defaults#
pub fn defaults() -> OptionsReturns the default options: the default limits and reads of 16384 bytes.
WebSocket#
pub struct WebSocket<P>A WebSocket session that owns its transport P.
Details#
Make it with WebSocket.make after the opening handshake. P must provide HttpTransport,
as NativeTransport of silk.http.exchange_native does for a socket or a TLS connection.
Implementation WebSocket<P>#
impl<P> WebSocket<P>Associated function make#
pub effect fn make(
transport: P,
side: Role,
options: Options,
buffered: &[u8],
) -> WebSocket<P> ! StreamError | OutOfMemoryError ? &mut Allocator | &mut RandomReturns an open session for role over transport.
Details#
buffered holds bytes that were read with the handshake and come before any later read. A
client draws the seed of its mask keys from Random.
Errors#
Protocol with InvalidLimits if maxMessageBytes or readCapacity is zero.
OutOfMemoryError if the buffers cannot be allocated.
Method state#
pub fn state(self: &Self) -> StateReturns the state of the session.
Method message#
pub fn message<'socket>(self: &'socket Self) -> &'socket [u8]Returns the last Text or Binary message. It is valid until the next readEvent.
Method messageText#
pub fn messageText<'socket>(self: &'socket Self) -> string<'socket>Returns the last Text message as text. It is empty after a Binary message.
Method transport#
pub fn transport<'socket>(self: &'socket mut Self) -> &'socket mut PReturns the transport, to close it or to look at it.
Method readEvent#
pub effect fn readEvent(self: &mut Self, deadline: Option<Instant>) -> Event
! StreamError | OutOfMemoryError
? &mut Allocator | &mut MonotonicClock | &mut Random
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomReads frames until one event is complete, and returns it.
Details#
A Ping is answered and a Close is answered before the call returns. The message of a Text or
Binary event is in WebSocket.message.
Errors#
Protocolfor a frame that breaks a rule, or when the session isClosed.Ioif the transport fails ordeadlinepasses,Truncatedif it ends before a Close.OutOfMemoryErrorif a buffer cannot grow.
Method writeText#
pub effect fn writeText(self: &mut Self, text: string, deadline: Option<Instant>) -> ()
! StreamError | OutOfMemoryError
? &mut Allocator | &mut MonotonicClock | &mut Random
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomSends text as one Text frame, and flushes it.
Errors#
Protocol if the session is not open, Io if the transport fails or deadline passes.
OutOfMemoryError if a buffer cannot grow.
Method writeBinary#
pub effect fn writeBinary(self: &mut Self, bytes: &[u8], deadline: Option<Instant>) -> ()
! StreamError | OutOfMemoryError
? &mut Allocator | &mut MonotonicClock | &mut Random
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomSends bytes as one Binary frame, and flushes it.
Errors#
Protocol if the session is not open, Io if the transport fails or deadline passes.
OutOfMemoryError if a buffer cannot grow.
Method writeFragment#
pub effect fn writeFragment(
self: &mut Self,
kind: MessageKind,
payload: &[u8],
fin: bool,
deadline: Option<Instant>,
) -> ()
! StreamError | OutOfMemoryError
? &mut Allocator | &mut MonotonicClock | &mut Random
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomSends one fragment of a message, and flushes it. kind counts only for the first fragment,
and fin marks the last one.
Errors#
Protocol if the session is not open, Io if the transport fails or deadline passes.
OutOfMemoryError if a buffer cannot grow.
Method writePing#
pub effect fn writePing(self: &mut Self, payload: &[u8], deadline: Option<Instant>) -> ()
! StreamError | OutOfMemoryError
? &mut Allocator | &mut MonotonicClock | &mut Random
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomSends a Ping with payload (at most 125 bytes), and flushes it.
Errors#
Protocol if the session is not open or the payload is too large, Io if the transport
fails or deadline passes.
OutOfMemoryError if a buffer cannot grow.
Method sendClose#
pub effect fn sendClose(self: &mut Self, data: Option<CloseData>, deadline: Option<Instant>) -> ()
! StreamError | OutOfMemoryError
? &mut Allocator | &mut MonotonicClock | &mut Random
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomSends a Close, and flushes it. Then read with readEvent until the Close event of the peer.
Details#
data gives the code and reason. With None, the Close has no payload. It does nothing if
this side already sent Close.
Errors#
Protocol if the session failed, Io if the transport fails or deadline passes.
OutOfMemoryError if a buffer cannot grow.
TransportHalves#
pub struct TransportHalves<R, W>The two halves of a transport, for fromHalves.
Field reader#
pub reader: RThe half that reads.
Field writer#
pub writer: WThe half that writes.
Parts#
pub struct PartsThe state of a session without its transport, from WebSocket.detach. Give it to
fromHalves.
Detached#
pub struct Detached<P>A session and its transport, taken apart by WebSocket.detach.
Field transport#
pub transport: PThe transport.
Field parts#
pub parts: PartsThe rest of the session.
Implementation WebSocket<P>#
impl<P> WebSocket<P>Method detach#
pub fn detach(self: Self) -> Detached<P>Takes the session apart, to split its transport and then call fromHalves.
Split#
pub struct Split<R, W>A split session: the ReadHalf for one fiber, and a WriteHalf to clone for each fiber
that sends.
Field reader#
pub reader: ReadHalf<R, W>The half that reads.
Field writer#
pub writer: WriteHalf<W>The half that sends.
ReadHalf#
pub struct ReadHalf<R, W>The receiving half of a split session. One fiber reads with it.
Details#
ReadHalf.readEvent answers a Ping with a Pong and a Close with a Close. It sends the answer
at once when no WriteHalf is sending, or else the writer that sends sends it next.
WriteHalf#
pub struct WriteHalf<W>A sending handle of a split session. Clone it with WriteHalf.clone for each fiber that
sends.
Details#
Each send waits until no other writer is sending, so frames are never mixed. A Close from the peer, or a failure of the reader, makes later sends fail.
fromHalves#
pub effect fn fromHalves<R, W>(halves: TransportHalves<R, W>, parts: Parts) -> Split<R, W>
! OutOfMemoryError
? &mut AllocatorMakes a split session from the halves of a transport and the Parts of a session.
When to use#
Call it with the halves of the transport that WebSocket.detach gave you, for example from
Connection.split of silk.net.socket_native.
Errors#
OutOfMemoryError if the shared state cannot be allocated.
Implementation ReadHalf<R, W>#
impl<R, W> ReadHalf<R, W>Method state#
pub fn state(self: &Self) -> StateReturns the state of the receive side.
Method message#
pub fn message<'reader>(self: &'reader Self) -> &'reader [u8]Returns the last Text or Binary message. It is valid until the next readEvent.
Method messageText#
pub fn messageText<'reader>(self: &'reader Self) -> string<'reader>Returns the last Text message as text. It is empty after a Binary message.
Method readEvent#
pub effect fn readEvent(self: &mut Self, deadline: Option<Instant>) -> Event
! StreamError | OutOfMemoryError
? &mut Allocator | &mut MonotonicClock | &mut Random
where
&mut R provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut Random,
&mut W provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomReads frames until one event is complete, and returns it.
Details#
A Ping is answered and a Close is answered: at once if no writer is sending, else right after
the frame that is being sent. The message of a Text or Binary event is in
ReadHalf.message. deadline applies to the reads and to the answers this call sends.
Errors#
Protocolfor a frame that breaks a rule, or when the session isClosed.Ioif the transport fails ordeadlinepasses,Truncatedif it ends before a Close.OutOfMemoryErrorif a buffer cannot grow.
Implementation WriteHalf<W>#
impl<W> WriteHalf<W>Method clone#
pub effect fn clone(self: &Self) -> WriteHalf<W> ! OutOfMemoryError ? &mut AllocatorReturns another handle to the same session, for another fiber.
Errors#
OutOfMemoryError if the handle cannot be allocated.
Method state#
pub fn state(self: &Self) -> StateReturns the state of the send side.
Method writeText#
pub effect fn writeText(self: &Self, text: string, deadline: Option<Instant>) -> ()
! StreamError | OutOfMemoryError
? &mut Allocator | &mut MonotonicClock | &mut Random
where
&mut W provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomSends text as one Text frame, and flushes it. It waits while another writer sends.
Errors#
Protocol if the session is not open, Io if the transport fails or deadline passes.
OutOfMemoryError if a buffer cannot grow.
Method writeBinary#
pub effect fn writeBinary(self: &Self, bytes: &[u8], deadline: Option<Instant>) -> ()
! StreamError | OutOfMemoryError
? &mut Allocator | &mut MonotonicClock | &mut Random
where
&mut W provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomSends bytes as one Binary frame, and flushes it. It waits while another writer sends.
Errors#
Protocol if the session is not open, Io if the transport fails or deadline passes.
OutOfMemoryError if a buffer cannot grow.
Method writePing#
pub effect fn writePing(self: &Self, payload: &[u8], deadline: Option<Instant>) -> ()
! StreamError | OutOfMemoryError
? &mut Allocator | &mut MonotonicClock | &mut Random
where
&mut W provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomSends a Ping with payload (at most 125 bytes), and flushes it.
Errors#
Protocol if the session is not open or the payload is too large, Io if the transport
fails or deadline passes.
OutOfMemoryError if a buffer cannot grow.
Method sendClose#
pub effect fn sendClose(self: &Self, data: Option<CloseData>, deadline: Option<Instant>) -> ()
! StreamError | OutOfMemoryError
? &mut Allocator | &mut MonotonicClock | &mut Random
where
&mut W provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomSends a Close, and flushes it. The reader then reads until the Close event of the peer.
Details#
data gives the code and reason. With None, the Close has no payload. It does nothing if
this side already sent Close.
Errors#
Protocol if the session failed, Io if the transport fails or deadline passes.
OutOfMemoryError if a buffer cannot grow.
Category: HTTP.
Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2, x86_64-pc-windows-msvc, wasm64-unknown-unknown.