websocket

package
v0.9.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 27, 2024 License: MIT Imports: 25 Imported by: 0

README

Compliance

sonic.websocket uses the autobahn-testsuite to validate the WebSocket implementation. sonic.websocket implements most of the WebSocket protocol with the exception of:

  • DEFLATE compression
  • UTF8 handling.

Notes

There are two state machines that combined form a stateful WebSocket parser.

  • FrameCodec handles the state of a frame where the smallest unit is a byte.
  • WebsocketStream handles the state of the whole stream where the smallest unit is a frame. WebsocketStream uses FrameCodec.

Documentation

Overview

Based on https://datatracker.ietf.org/doc/html/rfc6455

Index

Constants

View Source
const (
	DefaultMaxMessageSize = 1024 * 512
	CloseTimeout          = 5 * time.Second
	DialTimeout           = 5 * time.Second
)
View Source
const (
	TypeText   = MessageType(OpcodeText)
	TypeBinary = MessageType(OpcodeBinary)
	TypeClose  = MessageType(OpcodeClose)
	TypePing   = MessageType(OpcodePing)
	TypePong   = MessageType(OpcodePong)

	TypeNone MessageType = 0xFF
)
View Source
const (
	MaxControlFramePayloadLength = 125
)

Variables

View Source
var (
	ErrPayloadOverMaxSize = errors.New("payload over maximum size")

	ErrPayloadTooBig = errors.New("frame payload too big")

	ErrWrongHandshakeRole = errors.New(
		"wrong role when initiating/accepting the handshake",
	)

	ErrCannotUpgrade = errors.New(
		"cannot upgrade connection to WebSocket",
	)

	ErrMessageTooBig = errors.New("message too big")

	ErrInvalidControlFrame = errors.New("invalid control frame")

	ErrControlFrameTooBig = errors.New("control frame too big")

	ErrSendAfterClose = errors.New("sending on a closed stream")

	ErrNonZeroReservedBits = errors.New("non zero reserved bits")

	ErrMaskedFramesFromServer = errors.New("masked frames from server")

	ErrUnmaskedFramesFromClient = errors.New("unmasked frames from server")

	ErrReservedOpcode = errors.New("reserved opcode")

	ErrUnexpectedContinuation = errors.New(
		"continue frame but nothing to continue",
	)

	ErrExpectedContinuation = errors.New("expected continue frame")

	ErrInvalidAddress = errors.New("invalid address")

	ErrInvalidUTF8 = errors.New("Invalid UTF-8 encoding")
)
View Source
var (
	ErrPartialPayload = errors.New("partial payload")
)
View Source
var GUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")

Used when constructing the server's Sec-WebSocket-Accept key based on the client's Sec-WebSocket-Key.

Functions

func EncodeCloseCode

func EncodeCloseCode(cc CloseCode) []byte

func EncodeCloseFramePayload

func EncodeCloseFramePayload(cc CloseCode, reason string) []byte

func GenMask

func GenMask(b []byte)

func IsUpgradeReq

func IsUpgradeReq(req *http.Request) bool

func IsUpgradeRes

func IsUpgradeRes(res *http.Response) bool

func MakeRequestKey

func MakeRequestKey() string

func MakeResponseKey

func MakeResponseKey(reqKey []byte) string

func Mask

func Mask(mask, b []byte)

func ValidCloseCode added in v0.8.0

func ValidCloseCode(closeCode CloseCode) bool

Types

type AsyncFrameCallback added in v0.8.0

type AsyncFrameCallback = func(err error, f Frame)

type AsyncMessageCallback added in v0.8.0

type AsyncMessageCallback = func(err error, n int, messageType MessageType)

type CloseCode

type CloseCode uint16

Close status codes that accompany close frames.

const (
	// CloseNormal signifies normal closure; the connection successfully completed whatever purpose for which it was
	// created.
	CloseNormal CloseCode = 1000

	// GoingAway means endpoint is going away, either because of a server failure or because the browser is navigating
	// away from the page that opened the connection.
	CloseGoingAway CloseCode = 1001

	// CloseProtocolError means the endpoint is terminating the connection due to a protocol error.
	CloseProtocolError CloseCode = 1002

	// CloseUnknownData means the connection is being terminated because the endpoint received data of a type it cannot
	// accept (for example, a text-only endpoint received binary data).
	CloseUnknownData CloseCode = 1003

	// CloseBadPayload means the endpoint is terminating the connection because a message was received that contained
	// inconsistent data (e.g., non-UTF-8 data within a text message).
	CloseBadPayload CloseCode = 1007

	// ClosePolicyError means the endpoint is terminating the connection because it received a message that violates its
	// policy. This is a generic status code, used when codes 1003 and 1009 are not suitable.
	ClosePolicyError CloseCode = 1008

	// CloseTooBig means the endpoint is terminating the connection because a data frame was received that is too large.
	CloseTooBig CloseCode = 1009

	// CloseNeedsExtension means the client is terminating the connection because it expected the server to negotiate
	// one or more extensions, but the server didn't.
	CloseNeedsExtension CloseCode = 1010

	// CloseInternalError means the server is terminating the connection because it encountered an unexpected condition
	// that prevented it from fulfilling the request.
	CloseInternalError CloseCode = 1011

	// CloseServiceRestart means the server is terminating the connection because it is restarting.
	CloseServiceRestart CloseCode = 1012

	// CloseTryAgainLater means the server is terminating the connection due to a temporary condition, e.g. it is
	// overloaded and is casting off some of its clients.
	CloseTryAgainLater CloseCode = 1013

	// CloseNone is used internally to mean "no error" This code is reserved and may not be sent.
	CloseNone CloseCode = 0

	// CloseNoStatus means no status code was provided in the close frame sent by the peer, even though one was
	// expected.  This code is reserved for internal use and may not be sent in-between peers.
	CloseNoStatus CloseCode = 1005

	// CloseAbnormal means the connection was closed without receiving a close frame. This code is reserved and may not
	// be sent.
	CloseAbnormal CloseCode = 1006

	// CloseReserved1 is reserved for future use by the WebSocket standard. This code is reserved and may not be sent.
	CloseReserved1 CloseCode = 1004

	// CloseReserved2 is reserved for future use by the WebSocket standard. This code is reserved and may not be sent.
	CloseReserved2 CloseCode = 1014

	// CloseReserved3 is reserved for future use by the WebSocket standard. This code is reserved and may not be sent.
	CloseReserved3 CloseCode = 1015

	// CloseReserved4 is reserved for future use by the WebSocket standard. This code is reserved and may not be sent.
	CloseReserved4 CloseCode = 1016

	CloseReservedForFuture CloseCode = 1004
)

func DecodeCloseCode

func DecodeCloseCode(b []byte) CloseCode

func DecodeCloseFramePayload

func DecodeCloseFramePayload(b []byte) (cc CloseCode, reason string)

type ControlCallback

type ControlCallback = func(messageType MessageType, payload []byte)

type Frame

type Frame []byte

func NewFrame

func NewFrame() Frame

NOTE use stream.AcquireFrame() instead of NewFrame if you intend to write this frame onto a WebSocket stream.

func (Frame) ExtendedPayloadLengthBytes added in v0.8.0

func (f Frame) ExtendedPayloadLengthBytes() int

func (Frame) Header added in v0.8.0

func (f Frame) Header() []byte

func (Frame) IsFIN added in v0.8.0

func (f Frame) IsFIN() bool

An unfragmented message consists of a single frame with the FIN bit set and an opcode other than 0.

A fragmented message consists of a single frame with the FIN bit clear and an opcode other than 0, followed by zero or more frames with the FIN bit clear and the opcode set to 0, and terminated by a single frame with the FIN bit set and an opcode of 0.

func (Frame) IsMasked

func (f Frame) IsMasked() bool

func (Frame) IsRSV1

func (f Frame) IsRSV1() bool

func (Frame) IsRSV2

func (f Frame) IsRSV2() bool

func (Frame) IsRSV3

func (f Frame) IsRSV3() bool

func (Frame) Mask

func (f Frame) Mask() []byte

func (Frame) MaskBytes added in v0.8.0

func (f Frame) MaskBytes() int

func (*Frame) MaskPayload added in v0.8.0

func (f *Frame) MaskPayload()

func (Frame) Opcode

func (f Frame) Opcode() Opcode

func (Frame) Payload

func (f Frame) Payload() []byte

func (Frame) PayloadLength added in v0.8.0

func (f Frame) PayloadLength() int

func (*Frame) ReadFrom

func (f *Frame) ReadFrom(r io.Reader) (n int64, err error)

func (Frame) Reset

func (f Frame) Reset()

func (*Frame) SetBinary

func (f *Frame) SetBinary() *Frame

func (*Frame) SetClose

func (f *Frame) SetClose() *Frame

func (*Frame) SetContinuation

func (f *Frame) SetContinuation() *Frame

func (*Frame) SetFIN added in v0.8.0

func (f *Frame) SetFIN() *Frame

func (*Frame) SetIsMasked added in v0.8.0

func (f *Frame) SetIsMasked() *Frame

func (*Frame) SetOpcode

func (f *Frame) SetOpcode(c Opcode) *Frame

func (*Frame) SetPayload

func (f *Frame) SetPayload(b []byte) *Frame

func (*Frame) SetPing

func (f *Frame) SetPing() *Frame

func (*Frame) SetPong

func (f *Frame) SetPong() *Frame

func (*Frame) SetRSV1

func (f *Frame) SetRSV1() *Frame

func (*Frame) SetRSV2

func (f *Frame) SetRSV2() *Frame

func (*Frame) SetRSV3

func (f *Frame) SetRSV3() *Frame

func (*Frame) SetText

func (f *Frame) SetText() *Frame

func (*Frame) UnmaskPayload added in v0.8.0

func (f *Frame) UnmaskPayload()

func (*Frame) UnsetIsMasked added in v0.8.0

func (f *Frame) UnsetIsMasked() *Frame

func (Frame) WriteTo

func (f Frame) WriteTo(w io.Writer) (int64, error)

type FrameCodec

type FrameCodec struct {
	// contains filtered or unexported fields
}

FrameCodec is a stateful streaming parser handling the encoding and decoding of WebSocket `Frame`s.

func NewFrameCodec

func NewFrameCodec(src, dst *sonic.ByteBuffer, maxMessageSize int) *FrameCodec

func (*FrameCodec) Decode

func (c *FrameCodec) Decode(src *sonic.ByteBuffer) (Frame, error)

Decode decodes the raw bytes from `src` into a `Frame`.

Two things can happen while decoding a raw stream of bytes into a frame:

1. There are not enough bytes to construct a frame with: in this case, a `nil` `Frame` and `ErrNeedMore` are returned. The caller should perform another read into `src` later.

2. `src` contains at least the bytes of one `Frame`: we decode the next `Frame` and leave the remainder bytes composing a partial `Frame` or a set of `Frame`s in the `src` buffer.

func (*FrameCodec) Encode

func (c *FrameCodec) Encode(frame Frame, dst *sonic.ByteBuffer) error

Encode encodes the `Frame` into `dst`.

type Header struct {
	Key          string
	Values       []string
	CanonicalKey bool
}

func ExtraHeader

func ExtraHeader(canonicalKey bool, key string, values ...string) Header

type MessageType

type MessageType byte

func (MessageType) String

func (t MessageType) String() string

type MockServer

type MockServer struct {
	Upgrade *http.Request
	// contains filtered or unexported fields
}

MockServer is a server which can be used to test the WebSocket client.

func (*MockServer) Accept

func (s *MockServer) Accept(addr string) (err error)

func (*MockServer) Close

func (s *MockServer) Close()

func (*MockServer) IsClosed

func (s *MockServer) IsClosed() bool

func (*MockServer) Port

func (s *MockServer) Port() int

func (*MockServer) Read

func (s *MockServer) Read(b []byte) (n int, err error)

func (*MockServer) Write

func (s *MockServer) Write(b []byte) error

type MockStream

type MockStream struct {
	// contains filtered or unexported fields
}

MockStream is a mock TCP stream that's not attached to any operating system IO executor. It is used to test reads and writes for WebSocket servers and clients.

A WebsocketStream can be set to use a MockStream only if it is in StateActive, which occurs after a successful handshake or a call to init().

func NewMockStream

func NewMockStream() *MockStream

func (*MockStream) AsyncClose

func (s *MockStream) AsyncClose(cb func(err error))

func (*MockStream) AsyncRead

func (s *MockStream) AsyncRead(b []byte, cb sonic.AsyncCallback)

func (*MockStream) AsyncReadAll

func (s *MockStream) AsyncReadAll(b []byte, cb sonic.AsyncCallback)

func (*MockStream) AsyncWrite

func (s *MockStream) AsyncWrite(b []byte, cb sonic.AsyncCallback)

func (*MockStream) AsyncWriteAll

func (s *MockStream) AsyncWriteAll(b []byte, cb sonic.AsyncCallback)

func (*MockStream) Cancel

func (s *MockStream) Cancel()

func (*MockStream) Close

func (s *MockStream) Close() error

func (*MockStream) RawFd

func (s *MockStream) RawFd() int

func (*MockStream) Read

func (s *MockStream) Read(b []byte) (n int, err error)

func (*MockStream) Write

func (s *MockStream) Write(b []byte) (n int, err error)

type Opcode

type Opcode byte
const (
	OpcodeContinuation Opcode = 0
	OpcodeText         Opcode = 1
	OpcodeBinary       Opcode = 2
	OpcodeClose        Opcode = 8
	OpcodePing         Opcode = 9
	OpcodePong         Opcode = 10
)

func (Opcode) IsBinary added in v0.8.0

func (c Opcode) IsBinary() bool

func (Opcode) IsClose added in v0.8.0

func (c Opcode) IsClose() bool

func (Opcode) IsContinuation added in v0.8.0

func (c Opcode) IsContinuation() bool

func (Opcode) IsControl added in v0.8.0

func (c Opcode) IsControl() bool

func (Opcode) IsPing added in v0.8.0

func (c Opcode) IsPing() bool

func (Opcode) IsPong added in v0.8.0

func (c Opcode) IsPong() bool

func (Opcode) IsReserved added in v0.8.0

func (c Opcode) IsReserved() bool

func (Opcode) IsText added in v0.8.0

func (c Opcode) IsText() bool

func (Opcode) String

func (c Opcode) String() string

type Role

type Role uint8
const (
	RoleClient Role = iota
	RoleServer
)

func (Role) String

func (r Role) String() string

type Stream

type Stream struct {
	// contains filtered or unexported fields
}

func NewWebsocketStream

func NewWebsocketStream(ioc *sonic.IO, tls *tls.Config, role Role) (s *Stream, err error)

func (*Stream) AcquireFrame added in v0.8.0

func (s *Stream) AcquireFrame() *Frame

The recommended way to create a `Frame`. This function takes care to allocate enough bytes to encode the WebSocket header and apply client side masking if the `Stream`'s role is `RoleClient`.

func (*Stream) AsyncClose

func (s *Stream) AsyncClose(closeCode CloseCode, reason string, callback func(err error))

AsyncClose sends a websocket close control frame asynchronously.

This function is used to send a close frame which begins the WebSocket closing handshake. The session ends when both ends of the connection have sent and received a close frame.

The callback is called if one of the following conditions is true:

  • the close frame is written
  • an error occurs

After beginning the closing handshake, the program should not write further messages, pings, pongs or close frames. Instead, the program should continue reading messages until the closing handshake is complete or an error occurs.

func (*Stream) AsyncFlush

func (s *Stream) AsyncFlush(callback func(err error))

Flush writes any pending control frames to the underlying stream asynchronously.

This call does not block.

func (*Stream) AsyncHandshake

func (s *Stream) AsyncHandshake(addr string, callback func(error), extraHeaders ...Header)

AsyncHandshake performs the WebSocket handshake asynchronously in the client role.

This call does not block. The provided callback is called when the request is sent and the response is received or when an error occurs.

Extra headers should be generated by calling `ExtraHeader(...)`.

func (*Stream) AsyncNextFrame

func (s *Stream) AsyncNextFrame(callback AsyncFrameCallback)

AsyncNextFrame reads and returns the next frame asynchronously.

This call first flushes any pending control frames to the underlying stream asynchronously.

This call does not block. The provided callback is invoked when one of the following happens:

  • an error occurs while flushing the pending control frames
  • an error occurs when reading/decoding the message bytes from the underlying stream
  • a frame is successfully read from the underlying stream

func (*Stream) AsyncNextMessage

func (s *Stream) AsyncNextMessage(b []byte, callback AsyncMessageCallback)

AsyncNextMessage reads the payload of the next message into the supplied buffer asynchronously. Message fragmentation is automatically handled by the implementation.

This call first flushes any pending control frames to the underlying stream asynchronously.

This call does not block. The provided callback is invoked when one of the following happens:

  • an error occurs while flushing the pending control frames
  • an error occurs when reading/decoding the message bytes from the underlying stream
  • the payload of the message is successfully read into the supplied buffer, after all message fragments are read

func (*Stream) AsyncWrite

func (s *Stream) AsyncWrite(b []byte, messageType MessageType, callback func(err error))

AsyncWrite writes the supplied buffer as a single message with the given type to the underlying stream asynchronously.

This call first flushes any pending control frames to the underlying stream asynchronously.

This call does not block. The provided callback is invoked when one of the following happens:

  • an error occurs while flushing the pending control frames
  • an error occurs during the write
  • the message is successfully written to the underlying stream

func (*Stream) AsyncWriteFrame

func (s *Stream) AsyncWriteFrame(f *Frame, callback func(err error))

AsyncWriteFrame writes the supplied frame to the underlying stream asynchronously.

This call first flushes any pending control frames to the underlying stream asynchronously.

This call does not block. The provided callback is invoked when one of the following happens:

  • an error occurs while flushing the pending control frames
  • an error occurs during the write
  • the frame is successfully written to the underlying stream

func (*Stream) Close

func (s *Stream) Close(cc CloseCode, reason string) error

Close sends a websocket close control frame asynchronously.

This function is used to send a close frame which begins the WebSocket closing handshake. The session ends when both ends of the connection have sent and received a close frame.

The call blocks until one of the following conditions is true:

  • the close frame is written
  • an error occurs

After beginning the closing handshake, the program should not write further messages, pings, pongs or close frames. Instead, the program should continue reading messages until the closing handshake is complete or an error occurs.

func (*Stream) CloseNextLayer

func (s *Stream) CloseNextLayer() (err error)

func (*Stream) ControlCallback

func (s *Stream) ControlCallback() ControlCallback

func (*Stream) Flush

func (s *Stream) Flush() (err error)

Flush writes any pending control frames to the underlying stream.

This call blocks.

func (*Stream) Handshake

func (s *Stream) Handshake(addr string, extraHeaders ...Header) (err error)

Handshake performs the client handshake. This call blocks.

The call blocks until one of the following conditions is true:

  • the HTTP1.1 request is sent and the response is received
  • an error occurs

Extra headers should be generated by calling `ExtraHeader(...)`.

func (*Stream) LocalAddr

func (s *Stream) LocalAddr() net.Addr

func (*Stream) MaxMessageSize added in v0.8.0

func (s *Stream) MaxMessageSize() int

func (*Stream) NextFrame

func (s *Stream) NextFrame() (f Frame, err error)

NextFrame reads and returns the next frame.

This call first flushes any pending control frames to the underlying stream.

This call blocks until one of the following conditions is true:

  • an error occurs while flushing the pending control frames
  • an error occurs when reading/decoding the message bytes from the underlying stream
  • a frame is successfully read from the underlying stream

func (*Stream) NextLayer

func (s *Stream) NextLayer() sonic.Stream

Returns the stream through which IO is done.

func (*Stream) NextMessage

func (s *Stream) NextMessage(b []byte) (messageType MessageType, readBytes int, err error)

NextMessage reads the payload of the next message into the supplied buffer. Message fragmentation is automatically handled by the implementation.

This call first flushes any pending control frames to the underlying stream.

This call blocks until one of the following conditions is true:

  • an error occurs while flushing the pending control frames
  • an error occurs when reading/decoding the message from the underlying stream
  • the payload of the message is successfully read into the supplied buffer, after all message fragments are read

func (*Stream) Pending

func (s *Stream) Pending() int

Pending returns the number of currently pending control frames waiting to be flushed.

func (*Stream) RawFd

func (s *Stream) RawFd() int

func (*Stream) RemoteAddr

func (s *Stream) RemoteAddr() net.Addr

func (*Stream) Role added in v0.8.0

func (s *Stream) Role() Role

This stream is either a client or a server. The main differences are in how the opening/closing handshake is done and in the fact that payloads sent by the client to the server are masked.

func (*Stream) SetControlCallback

func (s *Stream) SetControlCallback(controlCallback ControlCallback)

SetControlCallback sets a function that will be invoked when a Ping/Pong/Close is received while reading a message. This callback is only invoked when reading complete messages, not frames.

The caller must not perform any operations on the stream in the provided callback.

func (*Stream) SetMaxMessageSize

func (s *Stream) SetMaxMessageSize(bytes int)

SetMaxMessageSize sets the maximum size of a message that can be read from or written to a peer.

- If a message exceeds the limit while reading, the connection is closed abnormally. - If a message exceeds the limit while writing, the operation is cancelled.

func (*Stream) SetUpgradeRequestCallback

func (s *Stream) SetUpgradeRequestCallback(upgradeRequestCallback UpgradeRequestCallback)

SetUpgradeRequestCallback sets a function that will be invoked during the handshake just before the upgrade request is sent.

The caller must not perform any operations on the stream in the provided callback.

func (*Stream) SetUpgradeResponseCallback

func (s *Stream) SetUpgradeResponseCallback(upgradeResponseCallback UpgradeResponseCallback)

SetUpgradeResponseCallback sets a function that will be invoked during the handshake just after the upgrade response is received.

The caller must not perform any operations on the stream in the provided callback.

func (*Stream) State

func (s *Stream) State() StreamState

func (*Stream) SupportsDeflate

func (s *Stream) SupportsDeflate() bool

func (*Stream) SupportsUTF8

func (s *Stream) SupportsUTF8() bool

SupportsUTF8 indicates that the stream can optionally perform UTF-8 validation on the payloads of Text frames. Validation is disabled by default and can be toggled with `ValidateUTF8(bool)`.

func (*Stream) UpgradeRequestCallback

func (s *Stream) UpgradeRequestCallback() UpgradeRequestCallback

func (*Stream) UpgradeResponseCallback

func (s *Stream) UpgradeResponseCallback() UpgradeResponseCallback

func (*Stream) ValidateUTF8 added in v0.8.0

func (s *Stream) ValidateUTF8(v bool) *Stream

ValidateUTF8 toggles UTF8 validation done on the payloads of Text frames.

func (*Stream) ValidatesUTF8 added in v0.8.0

func (s *Stream) ValidatesUTF8() bool

ValidatesUTF8 indicates if UTF-8 validation is performed on the payloads of Text frames. Validation is disabled by default and can be toggled with `ValidateUTF8(bool)`.

func (*Stream) Write

func (s *Stream) Write(b []byte, messageType MessageType) error

Write writes the supplied buffer as a single message with the given type to the underlying stream.

This call first flushes any pending control frames to the underlying stream.

This call blocks until one of the following conditions is true:

  • an error occurs while flushing the pending control frames
  • an error occurs during the write
  • the message is successfully written to the underlying stream

func (*Stream) WriteFrame

func (s *Stream) WriteFrame(f *Frame) error

WriteFrame writes the supplied frame to the underlying stream.

This call first flushes any pending control frames to the underlying stream.

This call blocks until one of the following conditions is true:

  • an error occurs while flushing the pending control frames
  • an error occurs during the write
  • the frame is successfully written to the underlying stream

type StreamState

type StreamState uint8
const (
	// Start state. Handshake is ongoing.
	StateHandshake StreamState = iota

	// Intermediate state. Connection is active, can read/write/close.
	StateActive

	// Intermediate state. We initiated the closing handshake and are waiting for a reply from the peer.
	StateClosedByUs

	// Terminal state. The peer initiated the closing handshake, we received a close frame and immediately replied.
	StateClosedByPeer

	// Terminal state. The peer replied to our closing handshake. Can only end up here from StateClosedByUs.
	StateCloseAcked

	// Terminal state. The connection is closed or some error occurred which rendered the stream unusable.
	StateTerminated
)

func (StreamState) String

func (s StreamState) String() string

type UpgradeRequestCallback

type UpgradeRequestCallback = func(req *http.Request)

type UpgradeResponseCallback

type UpgradeResponseCallback = func(res *http.Response)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL