Documentation
¶
Overview ¶
Package snapws is a minimal WebSocket library for Go.
SnapWS makes WebSockets effortless: you connect, read, and write, while the library takes care of everything else — ping/pong, safe concurrent access, connection lifecycle management, rate-limiting, and many other things.
Philosophy ¶
No boilerplate, no juggling goroutines, no protocol details. You write application logic; SnapWS handles the hard parts.
Features ¶
- Minimal and easy to use API. - Fully passes the autobahn-testsuite - Automatic handling of ping/pong and close frames. - Connection management built-in (useful when communicating between different clients like chat apps). - Built-in easy to use rate limiter. - Written completely in standard library amd Go offical libraries (golang.org/x), no external libraries imported. - Support for middlewares and connect/disconnect hooks.
SnapWS is for developers who want WebSockets to “just work” with minimal effort and minimal code.
Index ¶
- Constants
- Variables
- func GetArg[T any](args []any, index int) (T, bool)
- func IsFatalErr(err error) bool
- type BackpressureStrategy
- type BatchStrategy
- type Conn
- func (conn *Conn) Batch(data []byte) error
- func (conn *Conn) BatchJSON(v interface{}) error
- func (conn *Conn) Close()
- func (conn *Conn) CloseWithCode(code uint16, reason string)
- func (conn *Conn) NetConn() net.Conn
- func (conn *Conn) NextReader() (io.Reader, uint8, error)
- func (conn *Conn) NextWriter(ctx context.Context, msgType uint8) (*ConnWriter, error)
- func (conn *Conn) Ping() error
- func (conn *Conn) ReadBinary() (data []byte, err error)
- func (conn *Conn) ReadJSON(v any) error
- func (conn *Conn) ReadMessage() (msgType uint8, data []byte, err error)
- func (conn *Conn) ReadString() ([]byte, error)
- func (conn *Conn) SendBytes(ctx context.Context, p []byte) error
- func (conn *Conn) SendJSON(ctx context.Context, v any) error
- func (conn *Conn) SendMessage(ctx context.Context, opcode uint8, b []byte) error
- func (conn *Conn) SendString(ctx context.Context, p []byte) error
- type ConnReader
- type ConnWriter
- type ControlWriter
- type FatalError
- type ManagedConn
- type Manager
- func (m *Manager[KeyType]) BatchBroadcast(ctx context.Context, data []byte, exclude ...KeyType) (int, error)
- func (m *Manager[KeyType]) BatchBroadcastJSON(ctx context.Context, v interface{}, exclude ...KeyType) (int, error)
- func (m *Manager[KeyType]) BroadcastBytes(ctx context.Context, data []byte, exclude ...KeyType) (int, error)
- func (m *Manager[KeyType]) BroadcastJSON(ctx context.Context, v interface{}, exclude ...KeyType) (int, error)
- func (m *Manager[KeyType]) BroadcastString(ctx context.Context, data []byte, exclude ...KeyType) (int, error)
- func (m *Manager[KeyType]) Connect(key KeyType, w http.ResponseWriter, r *http.Request) (*ManagedConn[KeyType], error)
- func (m *Manager[KeyType]) Get(key KeyType) *ManagedConn[KeyType]
- func (m *Manager[KeyType]) GetAllConns(exclude ...KeyType) []*ManagedConn[KeyType]
- func (m *Manager[KeyType]) GetAllConnsAsConn(exclude ...KeyType) []*Conn
- func (m *Manager[KeyType]) Register(key KeyType, conn *ManagedConn[KeyType])
- func (m *Manager[KeyType]) Shutdown()
- type MiddlewareErr
- type Middlware
- type Middlwares
- type Options
- type PrefixSuffixFunc
- type RateLimiter
- type Room
- func (r *Room[keyType]) Add(conn *Conn, args ...any)
- func (r *Room[keyType]) BatchBroadcast(ctx context.Context, data []byte, exclude ...*Conn) (int, error)
- func (r *Room[keyType]) BatchBroadcastJSON(ctx context.Context, v interface{}, exclude ...*Conn) (int, error)
- func (r *Room[keyType]) BroadcastBytes(ctx context.Context, data []byte, exclude ...*Conn) (int, error)
- func (r *Room[keyType]) BroadcastJSON(ctx context.Context, v interface{}, exclude ...*Conn) (int, error)
- func (r *Room[keyType]) BroadcastString(ctx context.Context, data []byte, exclude ...*Conn) (int, error)
- func (r *Room[keyType]) Close()
- func (r *Room[keyType]) GetAllConns(exclude ...*Conn) []*Conn
- func (r *Room[keyType]) Move(conn *Conn, newRoom keyType)
- func (r *Room[keyType]) Remove(conn *Conn, args ...any)
- func (r *Room[keyType]) RemoveAll()
- type RoomManager
- func (rm *RoomManager[keyType]) Add(key keyType) *Room[keyType]
- func (rm *RoomManager[keyType]) Connect(w http.ResponseWriter, r *http.Request, roomKey keyType, args ...any) (*Conn, *Room[keyType], error)
- func (rm *RoomManager[keyType]) Get(key keyType) *Room[keyType]
- func (rm *RoomManager[keyType]) GetRoomKeys() []keyType
- func (rm *RoomManager[keyType]) Shutdown()
- type SendFunc
- type Upgrader
- func (u *Upgrader) EnableBatching(ctx context.Context, flushEvery time.Duration, sendFunc SendFunc)
- func (u *Upgrader) EnableJSONBatching(ctx context.Context, flushEvery time.Duration)
- func (u *Upgrader) EnablePrefixBatching(ctx context.Context, flushEvery time.Duration)
- func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request) (*Conn, error)
- func (u *Upgrader) Use(mw Middlware)
Constants ¶
const ( CloseNormalClosure uint16 = 1000 CloseGoingAway uint16 = 1001 CloseProtocolError uint16 = 1002 CloseUnsupportedData uint16 = 1003 CloseInvalidFramePayloadData uint16 = 1007 ClosePolicyViolation uint16 = 1008 CloseMessageTooBig uint16 = 1009 CloseMandatoryExtension uint16 = 1010 CloseInternalServerErr uint16 = 1011 )
const ( OpcodeContinuation = 0x0 // Continuation frame OpcodeText = 0x1 // Text frame (UTF-8) OpcodeBinary = 0x2 // Binary frame OpcodeClose = 0x8 // Connection close OpcodePing = 0x9 // Ping OpcodePong = 0xA // Pong )
const ( // Drops the message without closing the connection. BackpressureDrop = iota // Drops and closes the connection. BackpressureClose // Blocks until the message is sent. // // Not recommended to use excpet for very very special cases. BackpressureWait )
const ( DefaultMaxMessageSize = 1 << 20 // 1MB DefaultReadBufferSize = 4096 DefaultWriteBufferSize = 4096 DefaultBroadcastChannelsSize = 8 )
const GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
const MaxControlFramePayload = 125
const MaxHeaderSize = 14
Variables ¶
var ( ErrTest = errors.New("this is just an error for testing") ErrWrongMethod = errors.New("wrong method, the request method must be GET") ErrMissingUpgradeHeader = errors.New("mssing Upgrade header") ErrInvalidUpgradeHeader = errors.New("invalid Upgrade header") ErrMissingConnectionHeader = errors.New("mssing connection header") ErrInvalidConnectionHeader = errors.New("invalid connection header") ErrMissingVersionHeader = errors.New("mssing version header") ErrInvalidVersionHeader = errors.New("invalid version header") ErrMissingSecKey = errors.New("mssing Sec-WebSocket-Key header") ErrInvalidSecKey = errors.New("invalid Sec-WebSocket-Key header") ErrUnsupportedSubProtocols = errors.New("unsupported Sec-WebSocket-Protocol") ErrConnNotFound = errors.New("can't unregister a non existing connection") ErrInvalidOPCODE = errors.New("invalid OPCODE") ErrTooLargePayload = errors.New("payload length too large") ErrExpectedContinuation = errors.New("invalid frame sequence: expected continuation") ErrInvalidControlFrame = errors.New("invalid control frame") ErrUnnegotiatedRsvBits = errors.New("receve unnegotiated reserved bits") ErrMessageTooLarge = errors.New("message received from client was too large") ErrTooMuchFragments = errors.New("receive too much fragments for a signle message") ErrInvalidUTF8 = errors.New("invalid utf8 data") // ErrMessageTypeMismatch is returned when the received WebSocket message type // does not match the expected type (e.g., expecting text but received binary). ErrMessageTypeMismatch = errors.New("websocket message type did not match expected type") ErrChannelClosed = errors.New("channel is closed") ErrConnClosed = errors.New("connection is closed") ErrWriterClosed = errors.New("writer is closed") ErrWriterUnintialized = errors.New("writer is not intialized") ErrWriterNotClosed = errors.New("cannot get the next writer without closing the previous one") ErrInvalidPayloadLength = errors.New("invalid payload length") ErrInsufficientHeaderSpace = errors.New("no enough space to write headers") ErrInternalServer = errors.New("something went wrong") ErrTimeout = errors.New("time out") ErrPingAlreadySent = errors.New("ping already sent") // Batch errors ErrBatchingUninitialized = errors.New("uninitialized flusher or message batch") ErrFlusherClosed = errors.New("flusher closed") )
Functions ¶
func GetArg ¶ added in v1.0.2
A helper fucntion for room hooks.
Receives a type as a generic value, an args slice, and an index. If the args slice has a value at args[index] of the given type. it returns the casted value and true. Otherwise it returns the zero value of the given generic type and false.
Types ¶
type BackpressureStrategy ¶ added in v0.2.1
type BackpressureStrategy int
type BatchStrategy ¶ added in v1.1.0
type BatchStrategy int
BatchStrategy defines the method used for combining multiple messages into a single batch.
const ( // StrategyJSON batches messages as elements in a JSON array and sends as binary websocket message. // Format: [message1, message2, message3, ...] StrategyJSON BatchStrategy = iota // StrategyLengthPrefix batches messages with 4-byte big-endian length prefixes before each message. // Format: [uint32][message1][uint32][message2][uint32][message3]... StrategyLengthPrefix // Custom strategy set by the user. StrategyCustom )
type Conn ¶
type Conn struct {
// Empty string means its a raw websocket
SubProtocol string
MetaData sync.Map
// contains filtered or unexported fields
}
Conn represents a single WebSocket connection. It owns the underlying network connection and manages reading/writing frames, assembling messages, handling control frames (ping/pong/close), and lifecycle state.
func (*Conn) Batch ¶ added in v1.1.0
Batch adds raw byte data to the current message batch.
The data is stored as-is without any encoding or validation. For JSON batching, the data should already be valid JSON. For prefix batching, any binary data is acceptable.
This method will fail if:
- Batching is not enabled
- The flusher has been closed
- The flusher's context has been cancelled
Returns:
- error: nil on success, or an error describing why the message couldn't be added to the batch.
func (*Conn) BatchJSON ¶ added in v1.1.0
BatchJSON marshals the provided value to JSON and adds it to the current message batch.
This is a convenience method that combines json.Marshal with Batch. The JSON encoding is performed immediately, so any marshaling errors are returned synchronously.
Returns:
- error: JSON marshaling error or batching error.
func (*Conn) CloseWithCode ¶ added in v0.2.7
CloseWithCode closes the connection with the given code and reason.
func (*Conn) NextReader ¶
NextReader returns an io.Reader for the next WebSocket message. It blocks until a data frame is available. The returned reader allows streaming the message payload frame-by-frame, and the second return value indicates the message type (e.g., Text or Binary). All errors return are of type snap.FatalError indicating that the error is fatal and the connection got closed.
func (*Conn) NextWriter ¶
NextWriter locks the data write stream and returns a new writer for the given message type. Must call Close() on the returned writer to release the lock. The context given, is to be used for all the writer's functions as long is its not closed, This mean its used when its trying to optain the next writer and flushing. After you close the writer and call NextWriter again, you must give it a new context.
func (*Conn) Ping ¶
Ping sends a WebSocket ping frame and waits for it to be sent. Ping\Pong frames are already handeled by the library, you dont need to habdle them manually. If a ping has been sent and a pong frame hasn't been received yet, the function will return a non-nil error snapws.ErrPingAlreadySent. So, if you wish to send a ping manually, you should keep trying until the error is not snapws.ErrPingAlreadySent.
func (*Conn) ReadBinary ¶
ReadBinary returns the binary payload from a WebSocket binary message.
If the received message is not of type binary, it returns snapws.ErrMessageTypeMismatch without closing the connection. The returned error must be checked. If it's of type snapws.FatalError, that indicates the connection was closed due to an I/O or protocol error. Any other error means the connection is still open, and you may retry or continue using it.
func (*Conn) ReadJSON ¶
ReadJSON reads a text WebSocket message and unmarshals its payload into the given value.
This method expects the message to be of type text and contain valid UTF-8 encoded JSON. If the message is not of type text, it returns snapws.ErrMessageTypeMismatch without closing the connection. if the text is not a valid json, an error will be returned without closing the connection. The returned error must be checked. If it's of type snapws.FatalError, that indicates the connection was closed due to an I/O or protocol error. Any other error means the connection is still open, and you may retry or continue using it.
func (*Conn) ReadMessage ¶
ReadMessage reads the next complete WebSocket message into memory. It returns the message type (e.g., Text or Binary), the full payload, and any error encountered. If SkipUTF8Validation is not enabled, the message will be UTF8 validated, if it fails, the connection would be closed for InvalidFramePayloadData.
func (*Conn) ReadString ¶
ReadString returns the payload from a WebSocket text message.
If SkipUTF8Validation is not enabled, the message will be UTF8 validated, if it fails, the connection would be closed for InvalidFramePayloadData. If the received message is not of type text, it returns snapws.ErrMessageTypeMismatch without closing the connection. The returned error must be checked. If it's of type snapws.FatalError, that indicates the connection was closed due to an I/O or protocol error. Any other error means the connection is still open, and you may retry or continue using it.
func (*Conn) SendBytes ¶
SendBytes sends the given byte slice as a WebSocket binary message.
This is a shorthand for SendMessage with OpcodeBinary. The returned error must be checked. If it's a snapws.FatalError, the connection was closed due to an I/O or protocol error. Other errors indicate the connection is still alive and can be reused.
func (*Conn) SendJSON ¶
SendJSON sends the given value as a JSON-encoded WebSocket text message.
If marshaling fails, the method returns the original marshaling error. The message will be split into fragments if necessary.
The returned error must be checked. If it's of type snapws.FatalError, that indicates the connection was closed due to an I/O or protocol error. Any other error means the connection is still open, and you may retry or continue using it.
func (*Conn) SendMessage ¶ added in v0.4.2
Receives a context, opcode (text or binary), and a slice of bytes.
- Tries to optain the next writer
- sends a message of opcode (the given opcode) with the data of the given byte slice.
It returns an error. Errors must be checked if fatal.
func (*Conn) SendString ¶
SendString sends the given byte slice as a WebSocket binary message.
The Websocket protocl states that text messages must be a valid UTF-8, but SnapWS doesnt enforce UTF-8 validation in send methods even if SkipUTF8Validation is set to false. You're trusted to send valid data.
This is a shorthand for SendMessage with OpcodeText. The returned error must be checked. If it's a snapws.FatalError, the connection was closed due to an I/O or protocol error. Other errors indicate the connection is still alive and can be reused.
type ConnReader ¶
type ConnReader struct {
// contains filtered or unexported fields
}
ConnReader provides an io.Reader for a single WebSocket message.
Concurrency:
- Only one goroutine may read from a Conn (and thus its ConnReader) at a time. Concurrent reads on the same connection or the same ConnReader are not supported.
func (*ConnReader) Read ¶
func (r *ConnReader) Read(p []byte) (n int, err error)
Read implements the io.Reader interface for ConnReader. It reads WebSocket message payload data into p, handling continuation frames, unmasking (if required), and connection closure. This function allows the application to stream data frame-by-frame.
Behavior:
- Returns io.EOF when the final frame of a message has been fully read.
- If additional frames are part of the same message (continuation frames), it will transparently fetch and continue reading them.
- If a fatal error occurs (protocol violation, closed connection, etc.), the connection will be closed and a fatal error will be returned.
type ConnWriter ¶
type ConnWriter struct {
// contains filtered or unexported fields
}
ConnWriter is NOT safe for concurrent use. Only one goroutine may call Write/Flush/Close at a time. Use Conn.NextWriter to safely obtain exclusive access to a writer.
func (*ConnWriter) Close ¶
func (w *ConnWriter) Close() error
Close flushes the final frame and releases the writer lock.
func (*ConnWriter) Flush ¶
func (w *ConnWriter) Flush() error
flush sends the current buffer as a WebSocket frame with FIN set to false. If you want to end the message (FIN=true), you have to use the Close() method of the writer.
Return values:
- If it returns a fatal error, the connection is closed and cannot be reused.
- If it returns a non-fatal, non-nil error, the connection is still alive, and you may attempt to flush again. note: If err == context.Canceled or DeadlineExceeded, connection is alive but ctx is done. No point in retrying.
- If it returns nil, the flush was successful and the message has been sent.
type ControlWriter ¶ added in v0.4.0
type ControlWriter struct {
// contains filtered or unexported fields
}
Used to write control frames. This is needed because control frames can be written mid data frames.
type FatalError ¶
type FatalError struct {
Err error
}
func (*FatalError) Error ¶
func (e *FatalError) Error() string
type ManagedConn ¶ added in v0.3.0
type ManagedConn[KeyType comparable] struct { *Conn Key KeyType Manager *Manager[KeyType] }
ManagedConn is a Conn that is tracked by a Manager. It links a connection to a unique key so that the Manager can manage it safely (fetch/add/remove).
type Manager ¶
type Manager[KeyType comparable] struct { Upgrader *Upgrader OnRegister func(conn *ManagedConn[KeyType]) OnUnregister func(conn *ManagedConn[KeyType]) // contains filtered or unexported fields }
Manager tracks active WebSocket connections in a thread-safe way.
Key points:
- Generic over KeyType (e.g., user ID), which must be comparable.
- Each connection is stored as *ManagedConn[KeyType] in the Conns map.
- Manager provides safe fetch/add/remove of connections without requiring additional synchronization in user code.
- Thread-safety is enforced with sync.RWMutex (expect some performance overhead).
- Broadcast messages.
The Upgrader field handles the WebSocket upgrade, and the optional OnRegister / OnUnregister callbacks are invoked when connections are added or removed.
func NewManager ¶
func NewManager[KeyType comparable](u *Upgrader) *Manager[KeyType]
Creates a new manager. KeyType is the type of the key of the conns map. KeyType must be comparable.
func (*Manager[KeyType]) BatchBroadcast ¶ added in v1.1.0
func (m *Manager[KeyType]) BatchBroadcast(ctx context.Context, data []byte, exclude ...KeyType) (int, error)
BatchBroadcast loops over the manager's connections and adds the given data into their batch.
Returns:
- int: number of connections that successfully received the message in thier queue (doesnt necessarily mean they sent/batched it successfully)
- error: context cancellation error, flusher errors, or nil if completed normally
func (*Manager[KeyType]) BatchBroadcastJSON ¶ added in v1.1.0
func (m *Manager[KeyType]) BatchBroadcastJSON(ctx context.Context, v interface{}, exclude ...KeyType) (int, error)
BatchBroadcastJSON is just a helper method that marshals the given v into json and calls BatchBraodcast.
Returns:
- int: number of connections that successfully received the message in thier queue (doesnt necessarily mean they sent/batched it successfully)
- error: context cancellation error, mrashal error, flusher errors, or nil if completed normally
func (*Manager[KeyType]) BroadcastBytes ¶
func (m *Manager[KeyType]) BroadcastBytes(ctx context.Context, data []byte, exclude ...KeyType) (int, error)
broadcast sends a message of opcode binary to all active connections except the connection of key "exclude". It takes a context.Context, data as a slice of bytes, and an optional "exclude" which are the keys of connections to exclude from the broadcast It returns "n" the number of successfull writes, and an error.
func (*Manager[KeyType]) BroadcastJSON ¶ added in v1.1.2
func (m *Manager[KeyType]) BroadcastJSON(ctx context.Context, v interface{}, exclude ...KeyType) (int, error)
This is a convenience method that marshals the given value into json and calls BroadcastString.
func (*Manager[KeyType]) BroadcastString ¶
func (m *Manager[KeyType]) BroadcastString(ctx context.Context, data []byte, exclude ...KeyType) (int, error)
broadcast sends a message of opcode text to all active connections except the connection of key "exclude". It takes a context.Context, data as a slice of bytes, and an optional "exclude" which are the keys of connections to exclude from the broadcast. It returns "n" the number of successfull writes, and an error.
func (*Manager[KeyType]) Connect ¶
func (m *Manager[KeyType]) Connect(key KeyType, w http.ResponseWriter, r *http.Request) (*ManagedConn[KeyType], error)
Connect does 2 things:
- Upgrades the connection.
- connect the user to the manager & runs the hooks.
Any error retuened by this method is a handhshake error, and the response is handled by the handshake. You shouln't write to the http writer after this fucntion is called even if it return a non-nil error.
func (*Manager[KeyType]) Get ¶ added in v1.1.0
func (m *Manager[KeyType]) Get(key KeyType) *ManagedConn[KeyType]
Receives a key, returns a pointer to the connection associated with the key. If the connection exists, it will return a pointer to it. If the connection deosn't exists, it will return a nil pointer.
func (*Manager[KeyType]) GetAllConns ¶
func (m *Manager[KeyType]) GetAllConns(exclude ...KeyType) []*ManagedConn[KeyType]
except the conns that appear in "exclude".
func (*Manager[KeyType]) GetAllConnsAsConn ¶ added in v1.1.0
Get all connections associated with the manager as a slice of pointers to a conn except the conn that appear in "exclude".
func (*Manager[KeyType]) Register ¶
func (m *Manager[KeyType]) Register(key KeyType, conn *ManagedConn[KeyType])
Adds a conn[KeyType] to the manager (Manager[KeyType]). Receives a key and a pointer to a conn. If the key already exists, it will close the connection associated with the key, and replace it with the new connection received by the fucntion.
type MiddlewareErr ¶ added in v0.2.7
An error that can be returned by a middleware. It's usefull if you want to explicitly set the HTTP response code and error message.
func AsMiddlewareErr ¶ added in v0.2.7
func AsMiddlewareErr(err error) (*MiddlewareErr, bool)
func NewMiddlewareErr ¶ added in v0.2.7
func NewMiddlewareErr(code int, message string) *MiddlewareErr
func (*MiddlewareErr) Error ¶ added in v0.2.7
func (err *MiddlewareErr) Error() string
type Middlware ¶
type Middlware func(w http.ResponseWriter, r *http.Request) error
A function representing a middleware that will be ran after validating the websocket upgrade request and before switching protocols. If a non-nil error returns the connection wont be accepted. It is prefered to return an error of type snapws.MiddlewareErr.
type Middlwares ¶
type Middlwares []Middlware
type Options ¶ added in v0.2.1
type Options struct {
// Ran before finalizing and accepting the handshake.
Middlwares []Middlware
// Ran when the connection finalizes.
OnConnect func(conn *Conn)
// // Ran when connection closes.
OnDisconnect func(conn *Conn)
// If not set it will default to 5 seconds.
WriteWait time.Duration
// Should be larger than PingEvery. If not set it will default to 60 seconds.
ReadWait time.Duration
// If not set it will default to 50 seconds.
PingEvery time.Duration
// This is the max size of message sent by the client. if not set it will default to 1MB
// -1 means there is no max size.
// Note: the size includes the frame/frames header/header and masking key/keys.
// PLEASE DONT USER -1 UNLESS YOU KNOW WHAT YOU ARE DOING
MaxMessageSize int
// maximum fragremnt allowed per an incoming message.
// if not set it will default to 0, which means there is no limit.
ReaderMaxFragments int
// if not set it will use the default http buffer (4kb)
ReadBufferSize int
// if not set it will use the default http buffer (4kb)
WriteBufferSize int
// Buffer pooling can reduce GC pressure in workloads with large messages and very high throughput,
// but may increase latency in some scenarios. Enabled by default.
DisableWriteBuffersPooling bool
// subProtocols defines the list of supported WebSocket sub-protocols by the server.
// During the handshake, the server will select the first matching protocol from the
// client's Sec-WebSocket-Protocol header, based on the client's order of preference.
// If no match is found, the behavior depends on the value of rejectRaw.
SubProtocols []string
// rejectRaw determines whether to reject clients that do not propose any matching
// sub-protocols. If set to true, the connection will be rejected when:
// - The client does not include any Sec-WebSocket-Protocol header.
// - Or none of the client's protocols match the supported subProtocols list.
//
// If false, such connections will be accepted as raw WebSocket connections.
RejectRaw bool
// Defines the size of the buffered channel per connection that receive broadcast messages.
//
// Default is 8.
BroadcastChannelsSize int
// BackpressureStrategy controls the behavior when the per-conn broadcast channel is full:
// - snapws.BackpressureDrop (default): when the channel is full the message will be droped.
// - snapws.BackpressureClose: when the channel is full the connection will close.
// - snapws.BackpressureWait (not recommended): when the channel is full the reading loop will block
// until it succeeds to send the message.
BroadcastBackpressure BackpressureStrategy
// SkipUTF8Validation disables UTF-8 validation for text frames.
// if UTF8 validtion is enabled, the library would be validating text messages only when
// ReadString() is called. Not on every read (NextReader(), Read()).
// UTF-8 for text messages is required by RFC 6455.
// If you need max performance and don't care about the validity of the message.
// you can disable it.
// Default: false (validation enabled)
SkipUTF8Validation bool
// If not set it will default to DefaultMaxMessageSize (1MB).
//
// Max size of the batched messages. For instance, you have 3 batched messages each of size 4kb,
// the batch is of size 12kb < 1MB.
MaxBatchSize int
}
func (*Options) WithDefault ¶ added in v0.2.1
func (opt *Options) WithDefault()
type PrefixSuffixFunc ¶ added in v1.1.2
PrefixSuffixFunc defines the signature of "PrefixFunc" and "SuffixFunc" fields for the Flusher.
type RateLimiter ¶ added in v0.6.0
type RateLimiter struct {
// onLimitExceeded is called when a connection exceeds its rate limit.
// The function receives the offending connection and can perform actions
// like logging, metrics collection, or closing the connection.
//
// Important: If this function closes the connection, it MUST return a
// non-nil error to signal that the connection should be terminated.
// Returning nil means the message should be dropped but the connection
// should remain open.
OnRateLimitHit func(conn *Conn) error
// contains filtered or unexported fields
}
RateLimiter manages per-connection message rate limiting for WebSocket connections. It uses a token bucket algorithm to limit the number of messages each connection can send per second, with configurable burst capacity.
The rate limiter operates on WebSocket messages (not frames), meaning: - Only data frames (text/binary) count toward the limit - Control frames (ping, pong, close) are ignored - Fragmented messages count as a single message
note: if you want to close the connection you must close it manually in SetOnLimitExceeded and return a non-nil error.
Example usage:
limiter := NewRateLimiter(10, 5) // 10 msg/sec, burst of 5
limiter.SetOnLimitExceeded(func(conn *Conn) error {
log.Printf("Rate limit exceeded for connection %v", conn.RemoteAddr())
return nil // Don't close connection
})
upgrader.Limiter = limiter
func NewRateLimiter ¶ added in v0.6.0
func NewRateLimiter(mps, burst int) *RateLimiter
NewRateLimiter creates and returns a new RateLimiter. The limiter uses a token bucket algorithm, where:
- mps: number of tokens (messages) added per second.
- burst: maximum number of tokens the bucket can hold at once.
Example:
mps = 3, burst = 5 - At most 5 messages can be allowed immediately if the bucket is full. - Each second, 3 new tokens are added, up to the burst limit.
type Room ¶ added in v0.7.0
type Room[keyType comparable] struct { Key keyType // key in the parent roomManager // Called when a connection is added to the room. //If not set it will default to "DefaultOnJoin" in the RoomManager. OnJoin roomHook[keyType] // Called when a connection is removed from the room. //If not set it will default to "DefaultOnLeave" in the RoomManager. OnLeave roomHook[keyType] // contains filtered or unexported fields }
Room represents a group of WebSocket connections that can receive broadcast messages together. Rooms are identified by a comparable key type and provide thread-safe operations for managing connections and broadcasting messages.
Typical usage:
- Chat rooms where users in the same room receive each other's messages
- Game lobbies where players receive game state updates
- Collaborative editing where document collaborators receive changes
func (*Room[keyType]) Add ¶ added in v0.7.0
Add adds a connection to the room. The connection will be automatically removed from the room when it closes.
Receives args as an optional value, these args will be passed to the OnJoin hook.
Thread-safe: Multiple goroutines can call this concurrently. If the connection is already in the room, this is a no-op.
func (*Room[keyType]) BatchBroadcast ¶ added in v1.1.0
func (r *Room[keyType]) BatchBroadcast(ctx context.Context, data []byte, exclude ...*Conn) (int, error)
BatchBroadcast loops over the room's connections and adds the given data into their batch.
Returns:
- int: number of connections that successfully received the message in thier queue (doesnt necessarily mean they sent/batched it successfully)
- error: context cancellation error, flusher errors, or nil if completed normally
func (*Room[keyType]) BatchBroadcastJSON ¶ added in v1.1.0
func (r *Room[keyType]) BatchBroadcastJSON(ctx context.Context, v interface{}, exclude ...*Conn) (int, error)
BatchBroadcastJSON is just a helper method that marshals the given v into json and calls BatchBraodcast.
Returns:
- int: number of connections that successfully received the message in thier queue (doesnt necessarily mean they sent/batched it successfully)
- error: context cancellation error, mrashal error, flusher errors, or nil if completed normally
func (*Room[keyType]) BroadcastBytes ¶ added in v0.7.0
func (r *Room[keyType]) BroadcastBytes(ctx context.Context, data []byte, exclude ...*Conn) (int, error)
BroadcastBytes sends a binary message to all connections in the room.
Returns the number of connections that successfully received the message and any error encountered during broadcasting.
Thread-safe: Can be called concurrently from multiple goroutines.
func (*Room[keyType]) BroadcastJSON ¶ added in v1.1.2
func (r *Room[keyType]) BroadcastJSON(ctx context.Context, v interface{}, exclude ...*Conn) (int, error)
This is a convenience method that marshals the given value into json and calls BroadcastString.
func (*Room[keyType]) BroadcastString ¶ added in v0.7.0
func (r *Room[keyType]) BroadcastString(ctx context.Context, data []byte, exclude ...*Conn) (int, error)
BroadcastString sends a text message to all connections in the room.
Returns the number of connections that successfully received the message and any error encountered during broadcasting.
Thread-safe: Can be called concurrently from multiple goroutines.
func (*Room[keyType]) Close ¶ added in v0.7.0
func (r *Room[keyType]) Close()
Close removes all connections from the room and deletes the room from its manager. All connections in the room will be removed but not closed - they remain active and can be added to other rooms.
After calling Close, this room instance should not be used.
func (*Room[keyType]) GetAllConns ¶ added in v1.1.0
Receives optional values "exclude". Returns a slice of pointers to connections excluding the "excludes".
func (*Room[keyType]) Move ¶ added in v0.7.0
Move removes a connection from this room and adds it to another room. If the target room doesn't exist, the connection is only removed from the current room.
The target room must exist in the same RoomManager as the current room.
Thread-safe: The move operation is atomic from the perspective of the connection.
type RoomManager ¶ added in v0.7.0
type RoomManager[keyType comparable] struct { Upgrader *Upgrader // This is used when the "OnJoin" field of a room is unset. DefaultOnJoin roomHook[keyType] // This is used when the "OnLeave" field of a room is unset. DefaultOnLeave roomHook[keyType] // contains filtered or unexported fields }
RoomManager handles creation, lookup, and lifecycle of rooms. It provides thread-safe operations for managing multiple rooms and their connections. Each room is identified by a unique comparable key.
func NewRoomManager ¶ added in v0.7.0
func NewRoomManager[keyType comparable](upgrader *Upgrader) *RoomManager[keyType]
NewRoomManager creates a new RoomManager with the given WebSocket upgrader. If upgrader is nil, a default upgrader with standard options will be created.
The keyType must be a comparable type (string, int, custom types with comparable fields). This key will be used to uniquely identify and retrieve rooms.
func (*RoomManager[keyType]) Add ¶ added in v0.7.0
func (rm *RoomManager[keyType]) Add(key keyType) *Room[keyType]
Add creates a new room with the given key, or returns the existing room if it already exists. This operation is idempotent - calling it multiple times with the same key is safe and will always return the same room instance.
Thread-safe: Multiple goroutines can call this concurrently.
func (*RoomManager[keyType]) Connect ¶ added in v0.7.0
func (rm *RoomManager[keyType]) Connect(w http.ResponseWriter, r *http.Request, roomKey keyType, args ...any) (*Conn, *Room[keyType], error)
Connect upgrades an HTTP connection to WebSocket and adds it to the specified room. If the room doesn't exist, it will be created automatically.
Receives args as an optinal value, these args will be passed to both the OnJoin AND OnLeave hooks.
This is a convenience method that combines WebSocket upgrade, room creation/lookup, and connection addition in a single atomic operation.
Returns the upgraded connection, the room it was added to, and any error from the upgrade process.
func (*RoomManager[keyType]) Get ¶ added in v0.7.0
func (rm *RoomManager[keyType]) Get(key keyType) *Room[keyType]
Get retrieves a room by its key. Returns nil if the room doesn't exist.
The returned room pointer must be checked for nil before use:
if room := manager.Get("lobby"); room != nil {
room.BroadcastString(ctx, message)
}
Thread-safe: Multiple goroutines can call this concurrently.
func (*RoomManager[keyType]) GetRoomKeys ¶ added in v1.1.1
func (rm *RoomManager[keyType]) GetRoomKeys() []keyType
GetRoomKeys returns a slice of all rooms keys associated with the room manager.
func (*RoomManager[keyType]) Shutdown ¶ added in v0.7.0
func (rm *RoomManager[keyType]) Shutdown()
Shutdown closes all rooms and their connections. This should be called when shutting down the application to ensure all WebSocket connections are properly closed.
After calling Shutdown, the RoomManager should not be used.
type SendFunc ¶ added in v1.1.0
SendFunc is invoked for each connection when sending the batched messages.
The function is responsible for encoding and writing the messages according to the user’s own format or transport rules.
type Upgrader ¶ added in v0.3.0
type Upgrader struct {
*Options
Limiter *RateLimiter
Flusher *batchFlusher
// contains filtered or unexported fields
}
Used to upgrader HTTP connections to Websocket connections. Hold snap.Options. If you wanna learn more about the options go see their docs.
func NewUpgrader ¶ added in v0.3.0
Created a new upgrader with the given options. If options is nil, then it will assign a new options with default values.
func (*Upgrader) EnableBatching ¶ added in v1.1.0
EnableBatching configures connections to batch messages using a custon defined format. (only if you use methods like: Batch(), BatchJSON(), BroadcastBatch(), etc...)
It's recommended to use the pre-configured formats (EnableJSONBatching or EnablePrefixBatching) unless you want something speceific.
If batching is already enabled, it will be properly closed before the new one starts.
Parameters:
- ctx: context for the flusher lifecycle control
- flushEvery: time interval between automatic batch flushes (0 uses default 50ms)
- sendFunc: SendFunc is invoked for each connection when sending the batched messages. The function is responsible for encoding and writing the messages according to the user’s own format or transport rules.
func (*Upgrader) EnableJSONBatching ¶ added in v1.1.0
EnableJSONBatching configures connections to batch messages using JSON array format. (only if you use methods like: Batch(), BatchJSON(), BroadcastBatch(), etc...)
Messages are collected and sent as a single websocket binary message containing a JSON array. This is ideal for JavaScript clients that can easily parse JSON arrays.
If batching is already enabled, it will be properly closed before the new one starts.
Parameters:
- ctx: context for the flusher lifecycle control
- flushEvery: time interval between automatic batch flushes (0 uses default 50ms)
func (*Upgrader) EnablePrefixBatching ¶ added in v1.1.0
EnablePrefixBatching configures connections to batch messages using length-prefixed binary format. (only if you use methods like: Batch(), BatchJSON(), BroadcastBatch(), etc...)
Each message is prefixed with its length as a 4-byte big-endian integer, allowing clients to parse individual messages from the batch. This format is more efficient than JSON for binary data and provides precise message boundaries.
Binary format: [uint32][message1][uint32][message2][uint32][message3]...
If batching is already enabled, it will be properly closed before the new one starts.
Parameters:
- ctx: context for the flusher lifecycle control
- flushEvery: time interval between automatic batch flushes (0 uses default 50ms)
func (*Upgrader) Upgrade ¶ added in v0.3.0
Upgrades an HTTP connection to a Websocket connection. Receives (w http.ResponseWriter, r *http.Request) and returns a pointer to a snapws.Conn and an err. It checks method, headers, and selects an appopiate sub-protocol and runs the middlewares and the onConnect hook if they exist, and finnaly it responds to the client (both if the upgrade succeeds or fails).
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
examples/autobahn
command
|
|
|
examples/batching
command
|
|
|
examples/direct-messages
command
|
|
|
examples/echo
command
|
|
|
examples/file-streaming
command
|
|
|
examples/room-chat
command
|
|
|
test
command
|