Documentation
¶
Overview ¶
Package websocket implements RFC 6455 WebSocket protocol for real-time bidirectional communication.
This package provides frame-level parsing and writing according to RFC 6455 Section 5. It handles:
- Text and binary data frames
- Control frames (close, ping, pong)
- Fragmentation and continuation
- Client-to-server masking
- Payload length encoding (7-bit, 16-bit, 64-bit)
RFC Reference: https://datatracker.ietf.org/doc/html/rfc6455
Index ¶
- Variables
- func IsCloseError(err error) bool
- func IsTemporaryError(err error) bool
- type CloseCode
- type Conn
- func (c *Conn) Close() error
- func (c *Conn) CloseWithCode(code CloseCode, reason string) error
- func (c *Conn) Ping(data []byte) error
- func (c *Conn) Pong(data []byte) error
- func (c *Conn) Read() (MessageType, []byte, error)
- func (c *Conn) ReadJSON(v any) error
- func (c *Conn) ReadText() (string, error)
- func (c *Conn) Write(messageType MessageType, data []byte) error
- func (c *Conn) WriteJSON(v any) error
- func (c *Conn) WriteText(text string) error
- type Hub
- type MessageType
- type UpgradeOptions
Constants ¶
This section is empty.
Variables ¶
var ( // ErrProtocolError indicates a violation of the WebSocket protocol. // RFC 6455 Section 7.4.1: Status code 1002. // // Causes: // - Invalid frame format // - Unexpected RSV bits // - Invalid opcode sequence ErrProtocolError = errors.New("websocket: protocol error") // ErrInvalidUTF8 indicates text frame contains invalid UTF-8. // RFC 6455 Section 8.1: Text frames must contain valid UTF-8. // Status code 1007. ErrInvalidUTF8 = errors.New("websocket: invalid UTF-8 in text frame") // ErrFrameTooLarge indicates frame exceeds maximum allowed size. // Implementation-specific limit (not defined in RFC). // // Typical limits: // - Control frames: 125 bytes (RFC requirement) // - Data frames: configurable (e.g., 32 MB) ErrFrameTooLarge = errors.New("websocket: frame too large") // ErrReservedBits indicates RSV1/RSV2/RSV3 bits are set. // RFC 6455 Section 5.2: Reserved bits must be 0 unless extension negotiated. // Status code 1002 (protocol error). ErrReservedBits = errors.New("websocket: reserved bits must be 0") // ErrInvalidOpcode indicates an unknown or reserved opcode. // RFC 6455 Section 5.2: Opcodes 0x3-0x7 and 0xB-0xF are reserved. // Status code 1002 (protocol error). ErrInvalidOpcode = errors.New("websocket: invalid opcode") // ErrControlFragmented indicates a control frame with FIN=0. // RFC 6455 Section 5.5: Control frames must NOT be fragmented. // Status code 1002 (protocol error). ErrControlFragmented = errors.New("websocket: control frame must not be fragmented") // ErrControlTooLarge indicates control frame payload > 125 bytes. // RFC 6455 Section 5.5: Control frame payload length must be <= 125. // Status code 1002 (protocol error). ErrControlTooLarge = errors.New("websocket: control frame payload too large") // ErrUnexpectedContinuation indicates continuation frame without initial frame. // RFC 6455 Section 5.4: Continuation requires prior data frame with FIN=0. // Status code 1002 (protocol error). ErrUnexpectedContinuation = errors.New("websocket: unexpected continuation frame") // ErrMaskRequired indicates client frame without masking. // RFC 6455 Section 5.3: Client-to-server frames MUST be masked. // Status code 1002 (protocol error). ErrMaskRequired = errors.New("websocket: client frames must be masked") // ErrMaskUnexpected indicates server frame with masking. // RFC 6455 Section 5.3: Server-to-client frames MUST NOT be masked. // Status code 1002 (protocol error). ErrMaskUnexpected = errors.New("websocket: server frames must not be masked") // ErrInvalidMethod indicates HTTP method is not GET. // RFC 6455 Section 4.1: Handshake MUST use GET method. ErrInvalidMethod = errors.New("websocket: method must be GET") // ErrMissingUpgrade indicates missing or invalid Upgrade header. // RFC 6455 Section 4.2.1: Must contain "websocket" (case-insensitive). ErrMissingUpgrade = errors.New("websocket: missing or invalid Upgrade header") // ErrMissingConnection indicates missing or invalid Connection header. // RFC 6455 Section 4.2.1: Must contain "Upgrade" (case-insensitive). ErrMissingConnection = errors.New("websocket: missing or invalid Connection header") // ErrMissingSecKey indicates missing Sec-WebSocket-Key header. // RFC 6455 Section 4.2.1: Required for handshake. ErrMissingSecKey = errors.New("websocket: missing Sec-WebSocket-Key header") // ErrInvalidVersion indicates unsupported WebSocket version. // RFC 6455 Section 4.4: Only version 13 is supported. ErrInvalidVersion = errors.New("websocket: unsupported WebSocket version") // ErrOriginDenied indicates origin check failed. // Application-level security check (not RFC requirement). ErrOriginDenied = errors.New("websocket: origin check failed") // ErrHijackFailed indicates HTTP connection cannot be hijacked. // Required for upgrading to WebSocket protocol. ErrHijackFailed = errors.New("websocket: cannot hijack connection") // ErrClosed indicates connection is already closed. // Returned when attempting to read/write on closed connection. ErrClosed = errors.New("websocket: connection closed") // ErrInvalidMessageType indicates invalid message type for operation. // For example, calling ReadText() on binary message. ErrInvalidMessageType = errors.New("websocket: invalid message type") // ErrMessageTooLarge indicates message exceeds maximum size. // Configurable via UpgradeOptions.MaxMessageSize (default: 32 MB). // Status code 1009 (message too big). ErrMessageTooLarge = errors.New("websocket: message too large") )
Functions ¶
func IsCloseError ¶
IsCloseError checks if error represents a WebSocket close frame.
Returns true if the error is a clean close (close frame received). Returns false for other errors (network errors, protocol errors, etc.).
func IsTemporaryError ¶
IsTemporaryError checks if error is temporary and operation can be retried.
Returns true for transient network errors. Returns false for permanent errors (close frame, protocol errors).
Types ¶
type CloseCode ¶
type CloseCode int
CloseCode represents WebSocket close status codes (RFC 6455 Section 7.4).
Close frames MAY contain a status code indicating the reason for closure. Status codes 1000-4999 are defined by the WebSocket protocol.
const ( // CloseNormalClosure indicates normal closure (1000). // Connection purpose fulfilled. CloseNormalClosure CloseCode = 1000 // CloseGoingAway indicates endpoint going away (1001). // Server shutting down or browser navigating away. CloseGoingAway CloseCode = 1001 // CloseProtocolError indicates protocol error (1002). // Endpoint received frame it cannot understand. CloseProtocolError CloseCode = 1002 // CloseUnsupportedData indicates unsupported data type (1003). // Endpoint received data type it cannot accept (e.g. text-only endpoint got binary). CloseUnsupportedData CloseCode = 1003 // CloseNoStatusReceived indicates no status code was received (1005). // This is a reserved value and MUST NOT be set in close frame. // Used internally when close frame has no status code. CloseNoStatusReceived CloseCode = 1005 // CloseAbnormalClosure indicates abnormal closure (1006). // This is a reserved value and MUST NOT be set in close frame. // Used internally when connection closed without close frame (e.g. TCP error). CloseAbnormalClosure CloseCode = 1006 // CloseInvalidFramePayloadData indicates invalid frame payload (1007). // Message payload contains invalid data (e.g. invalid UTF-8 in text frame). CloseInvalidFramePayloadData CloseCode = 1007 // ClosePolicyViolation indicates policy violation (1008). // Endpoint received message violating its policy (generic status code). ClosePolicyViolation CloseCode = 1008 // CloseMessageTooBig indicates message too large (1009). // Endpoint received message too big to process. CloseMessageTooBig CloseCode = 1009 // CloseMandatoryExtension indicates missing extension (1010). // Client expected server to negotiate one or more extensions, but server didn't. CloseMandatoryExtension CloseCode = 1010 // CloseInternalServerErr indicates internal server error (1011). // Server encountered unexpected condition preventing it from fulfilling request. CloseInternalServerErr CloseCode = 1011 // CloseServiceRestart indicates service restart (1012). // Server is restarting. CloseServiceRestart CloseCode = 1012 // CloseTryAgainLater indicates try again later (1013). // Server is temporarily unable to process request (e.g. overloaded). CloseTryAgainLater CloseCode = 1013 // CloseTLSHandshake indicates TLS handshake failure (1015). // This is a reserved value and MUST NOT be set in close frame. // Used internally when TLS handshake fails. CloseTLSHandshake CloseCode = 1015 )
type Conn ¶
type Conn struct {
// contains filtered or unexported fields
}
Conn represents a WebSocket connection (RFC 6455).
Conn provides high-level methods for reading and writing messages, automatically handling:
- Message fragmentation (reassembly of multi-frame messages)
- Control frames (Ping, Pong, Close)
- UTF-8 validation for text messages
- Thread-safe writes
Example Usage:
conn, err := websocket.Upgrade(w, r, nil)
if err != nil {
return err
}
defer conn.Close()
// Read message
msgType, data, err := conn.Read()
// Write text message
conn.WriteText("Hello, WebSocket!")
// Write JSON
conn.WriteJSON(map[string]string{"status": "ok"})
func Upgrade ¶
func Upgrade(w http.ResponseWriter, r *http.Request, opts *UpgradeOptions) (*Conn, error)
Upgrade upgrades an HTTP connection to the WebSocket protocol.
Implements RFC 6455 Section 4: Opening Handshake.
Steps:
- Verify HTTP method is GET
- Check Upgrade: websocket header
- Check Connection: Upgrade header
- Verify Sec-WebSocket-Version: 13
- Get Sec-WebSocket-Key
- Check origin (if configured)
- Negotiate subprotocol (if configured)
- Compute Sec-WebSocket-Accept
- Send 101 Switching Protocols response
- Hijack connection
- Create and return WebSocket connection
Returns *Conn for reading/writing WebSocket messages.
Example:
func handler(w http.ResponseWriter, r *http.Request) {
conn, err := websocket.Upgrade(w, r, nil)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
defer conn.Close()
// Read and write messages
msgType, data, _ := conn.Read()
conn.Write(msgType, data)
}
func (*Conn) Close ¶
Close sends close frame and closes connection.
Uses CloseNormalClosure (1000) status code. Idempotent - safe to call multiple times.
RFC 6455 Section 7.1.1: "The Close frame MAY contain a body that indicates a reason for closing.".
func (*Conn) CloseWithCode ¶
CloseWithCode sends close frame with specific status code and reason.
Status codes defined in RFC 6455 Section 7.4. Reason is optional UTF-8 text (max ~123 bytes to fit in 125 byte frame).
Close handshake (RFC 6455 Section 7.1.2):
- Send Close frame
- Peer responds with Close frame
- Close TCP connection
Idempotent - safe to call multiple times.
func (*Conn) Ping ¶
Ping sends a ping frame (for keep-alive).
Application data is optional (max 125 bytes per RFC 6455 Section 5.5). Peer should respond with Pong containing same application data.
Use case: Heartbeat to detect dead connections.
ticker := time.NewTicker(30 * time.Second)
go func() {
for range ticker.C {
conn.Ping(nil)
}
}()
func (*Conn) Pong ¶
Pong sends a pong frame (response to ping or unsolicited).
Application data should echo ping data (RFC 6455 Section 5.5.3). Max 125 bytes.
Note: Read() automatically responds to Ping frames, so manual Pong usually not needed.
func (*Conn) Read ¶
func (c *Conn) Read() (MessageType, []byte, error)
Read reads the next complete message from the connection.
Automatically handles:
- Fragmentation: Reassembles multi-frame messages (FIN=0 → FIN=1)
- Control frames: Processes Ping/Pong/Close during message reading
- UTF-8 validation: For text messages (RFC 6455 Section 8.1)
Returns:
- MessageType: TextMessage or BinaryMessage
- []byte: Complete message payload
- error: ErrClosed if connection closed, protocol errors, network errors
Thread-Safety: Safe for concurrent reads (each goroutine gets separate message).
RFC 6455 Section 5.4: "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 (*Conn) ReadJSON ¶
ReadJSON reads the next message as JSON.
Convenience wrapper around Read() that:
- Ensures message is TextMessage
- Unmarshals JSON into v
Returns ErrInvalidMessageType if message is not text. Returns json.SyntaxError if JSON is malformed.
func (*Conn) ReadText ¶
ReadText reads the next text message.
Convenience wrapper around Read() that:
- Ensures message is TextMessage (returns error otherwise)
- Returns string directly
Returns ErrInvalidMessageType if message is not text.
func (*Conn) Write ¶
func (c *Conn) Write(messageType MessageType, data []byte) error
Write writes a message to the connection.
Automatically handles:
- Masking: Server frames NOT masked, client frames masked (RFC 6455 Section 5.1)
- Flushing: Ensures data sent immediately
Thread-Safety: Safe for concurrent writes (serialized by mutex).
Note: Currently does NOT fragment large messages (sends as single frame). Future enhancement: Fragment messages > WriteBufferSize.
type Hub ¶
type Hub struct {
// contains filtered or unexported fields
}
Hub manages multiple WebSocket connections for broadcasting.
Hub provides a central point for managing WebSocket clients and broadcasting messages to all connected clients simultaneously.
Thread-safe operations allow concurrent client registration, unregistration, and broadcasting from multiple goroutines.
Example Usage:
hub := websocket.NewHub()
go hub.Run()
defer hub.Close()
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
conn, _ := websocket.Upgrade(w, r, nil)
hub.Register(conn)
go func() {
defer hub.Unregister(conn)
for {
_, data, err := conn.Read()
if err != nil {
break
}
hub.Broadcast(data)
}
}()
})
func NewHub ¶
func NewHub() *Hub
NewHub creates a new WebSocket Hub.
The Hub must be started by calling Run() in a goroutine:
hub := websocket.NewHub() go hub.Run() defer hub.Close()
Returns a ready-to-use Hub with initialized channels.
func (*Hub) Broadcast ¶
Broadcast sends a message to all connected clients.
The message is queued for delivery. Actual delivery happens asynchronously in the event loop.
If a client write fails, that client is automatically unregistered.
Example:
hub.Broadcast([]byte("Hello, everyone!"))
Thread-safe: can be called from multiple goroutines. Non-blocking: queues message and returns immediately.
func (*Hub) BroadcastJSON ¶
BroadcastJSON sends a JSON message to all connected clients.
Marshals the value to JSON and broadcasts as text message.
Example:
type Message struct {
Type string `json:"type"`
Text string `json:"text"`
}
hub.BroadcastJSON(Message{Type: "notification", Text: "Hello"})
Returns error if JSON marshaling fails. Thread-safe: can be called from multiple goroutines.
func (*Hub) BroadcastText ¶
BroadcastText sends a text message to all connected clients.
Convenience wrapper around Broadcast() for text messages.
Example:
hub.BroadcastText("Server notification")
Thread-safe: can be called from multiple goroutines.
func (*Hub) ClientCount ¶
ClientCount returns the number of currently connected clients.
Thread-safe: can be called from multiple goroutines.
func (*Hub) Close ¶
Close stops the Hub and disconnects all clients.
Performs graceful shutdown:
- Sets closed flag to prevent new operations
- Stops the event loop
- Waits for Run() to exit
- Closes all client connections
- Closes all channels
Safe to call multiple times (no-op after first call).
Example:
defer hub.Close()
func (*Hub) Register ¶
Register adds a client to the Hub.
The client will receive all messages sent via Broadcast().
Typically called after successful WebSocket upgrade:
conn, _ := websocket.Upgrade(w, r, nil) hub.Register(conn)
Thread-safe: can be called from multiple goroutines.
func (*Hub) Run ¶
func (h *Hub) Run()
Run starts the Hub's event loop.
This method blocks and should be called in a goroutine:
go hub.Run()
The event loop handles:
- Client registration/unregistration
- Message broadcasting to all clients
- Graceful shutdown
Run exits when Close() is called.
func (*Hub) Unregister ¶
Unregister removes a client from the Hub.
The client's connection will be closed.
Typically called in a defer after client registration:
defer hub.Unregister(conn)
Thread-safe: can be called from multiple goroutines. Safe to call multiple times for the same client (no-op after first call).
type MessageType ¶
type MessageType int
MessageType represents WebSocket message type.
WebSocket supports two application message types (RFC 6455 Section 5.6): - Text (UTF-8 encoded text). - Binary (arbitrary binary data).
const ( // TextMessage represents a UTF-8 text message (opcode 0x1). // Text frames MUST contain valid UTF-8 data (RFC 6455 Section 8.1). TextMessage MessageType = 1 // BinaryMessage represents a binary data message (opcode 0x2). // Binary frames can contain arbitrary binary data. BinaryMessage MessageType = 2 )
func (MessageType) String ¶
func (mt MessageType) String() string
String returns string representation of message type.
type UpgradeOptions ¶
type UpgradeOptions struct {
// Subprotocols is the list of subprotocols advertised by server.
// Server will select first match from client's requested subprotocols.
// Empty list = no subprotocol negotiation.
Subprotocols []string
// CheckOrigin verifies the Origin header.
// nil = allow all origins (INSECURE in production!)
// Return false to reject the connection.
//
// Example:
// CheckOrigin: func(r *http.Request) bool {
// origin := r.Header.Get("Origin")
// return origin == "https://example.com"
// }
CheckOrigin func(*http.Request) bool
// ReadBufferSize sets size of read buffer (default: 4096).
// Larger buffers reduce syscalls for large messages.
ReadBufferSize int
// WriteBufferSize sets size of write buffer (default: 4096).
// Larger buffers reduce syscalls for large messages.
WriteBufferSize int
}
UpgradeOptions configures WebSocket upgrade behavior.
All fields are optional. Zero values use sensible defaults.