Documentation
¶
Overview ¶
Package wire formats and parses the line-based JaWS WebSocket protocol.
The package has two message types. Message is the in-process dispatch record: a command plus payload routed to a destination (Message.Dest). WsMsg is the serialized frame produced by WsMsg.Append and recovered by Parse — the bytes that actually travel over the WebSocket.
Each frame is encoded as What<TAB>Jid<TAB>Data<LF>. Data for most commands is written by WsMsg.Append as a JSON-compatible quoted string so the browser can decode it with JSON.parse. Parse decodes quoted inbound data with strconv.Unquote for the common case, falls back to JSON string decoding for browser-valid strings that strconv rejects, and sanitizes the result as valid UTF-8. AppendJSONQuote stays inside the overlap between those string grammars so server-generated frames round-trip through either decoder.
The Set and Call commands carry path/function payloads directly, so callers must keep those payloads free of raw tabs and newlines. The path/function side also uses '=' as its separator from the JSON value; jaws.JsCall normalizes its function path and compacts or escapes JSON before the payload enters the wire layer.
Index ¶
- func AppendJSONQuote(b []byte, s string) []byte
- func PingLoop(ctx context.Context, ccf context.CancelCauseFunc, doneCh <-chan struct{}, ...)
- func ReadLoop(ctx context.Context, ccf context.CancelCauseFunc, doneCh <-chan struct{}, ...)
- func WriteLoop(ctx context.Context, ccf context.CancelCauseFunc, doneCh <-chan struct{}, ...)
- type Message
- type WsMsg
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AppendJSONQuote ¶ added in v0.500.0
AppendJSONQuote appends s to b as a double-quoted JSON string literal that the browser's JSON.parse accepts. Use it instead of strconv.AppendQuote when the quoted data is written into a WebSocket frame: strconv emits Go-only escapes (\xNN, \UXXXXXXXX) for control bytes, DEL and invalid UTF-8 that JSON.parse rejects. See appendJSONQuote for the exact behavior, which is pinned by Fuzz_appendJSONQuote.
func PingLoop ¶ added in v0.300.2
func PingLoop(ctx context.Context, ccf context.CancelCauseFunc, doneCh <-chan struct{}, interval, timeout time.Duration, ws *websocket.Conn)
PingLoop sends periodic WebSocket pings and reports ping errors through ccf.
Returns immediately when interval is non-positive.
ccf may be nil, in which case errors are not reported and only the loop exits.
func ReadLoop ¶
func ReadLoop(ctx context.Context, ccf context.CancelCauseFunc, doneCh <-chan struct{}, incomingMsgCh chan<- WsMsg, ws *websocket.Conn)
ReadLoop reads WebSocket text messages, parses them, and sends parsed messages on incomingMsgCh.
Closes incomingMsgCh on exit.
ccf may be nil, in which case errors are not reported and only the loop exits.
func WriteLoop ¶
func WriteLoop(ctx context.Context, ccf context.CancelCauseFunc, doneCh <-chan struct{}, outboundMsgCh <-chan WsMsg, ws *websocket.Conn)
WriteLoop reads messages from outboundMsgCh, formats them, and writes them to the WebSocket.
Closes the WebSocket on exit.
ccf may be nil, in which case errors are not reported and only the loop exits.
Types ¶
type Message ¶
type Message struct {
// Dest selects recipients: nil targets every Request, a request key targets a
// single Request, an HTML id string targets matching elements in all Requests,
// and any other value is expanded into a tag or tag list.
Dest any
What what.What // command to perform
Data string // payload: inner HTML content or a tag list
}
Message contains the elements of a message to be sent to requests.
type WsMsg ¶
type WsMsg struct {
Data string // data to send
Jid jid.Jid // Jid to send, or -1 if Data contains that already
What what.What // command
}
WsMsg is a message sent to or from a WebSocket.
func Parse ¶
Parse parses an incoming text buffer into a message.
The wire format mirrors WsMsg.Append. For commands other than what.Set and what.Call, if the Data field begins with a double quote it is decoded as a JSON string: strconv.Unquote handles the common case, with a fallback to a JSON string decode for inputs it rejects but the browser's JSON.stringify can produce (notably a lone UTF-16 surrogate, which the fallback maps to U+FFFD). The message is rejected only if both decoders fail. Data that does not begin with a double quote is taken verbatim, as is all Set and Call data. In all cases the resulting data is sanitized with strings.ToValidUTF8.
Inbound what.Set and what.Call data is taken verbatim at the field boundaries and is best-effort: the field ends at the first tab, so a tab inside an inbound Set or Call payload truncates the field.
func (*WsMsg) Append ¶
Append appends m in wire format to b and returns the extended buffer.
When Jid is non-negative the frame is What<TAB>Jid<TAB>Data<LF>, where the Jid field is empty if Jid is zero. The Data field is written verbatim for what.Set and what.Call and JSON-quoted for every other command.
When Jid is negative the frame is What<TAB>Data<LF> (a single tab): Data is written verbatim and is expected to already contain the Jid and any remaining fields, as noted on the WsMsg.Jid field.
Verbatim Data must contain no tab or newline bytes, which would corrupt the frame; ensuring that is the caller's responsibility.