This document covers the constants, error types, and helper functions used throughout the gorilla/websocket package.
The message types are defined in RFC 6455, section 11.8.
const (
// TextMessage denotes a text data message. The text message payload is
// interpreted as UTF-8 encoded text data.
TextMessage = 1
// BinaryMessage denotes a binary data message.
BinaryMessage = 2
// CloseMessage denotes a close control message. The optional message
// payload contains a numeric code and text. Use the FormatCloseMessage
// function to format a close message payload.
CloseMessage = 8
// PingMessage denotes a ping control message. The optional message payload
// is UTF-8 encoded text.
PingMessage = 9
// PongMessage denotes a pong control message. The optional message payload
// is UTF-8 encoded text.
PongMessage = 10
)Close codes are defined in RFC 6455, section 11.7.
const (
CloseNormalClosure = 1000
CloseGoingAway = 1001
CloseProtocolError = 1002
CloseUnsupportedData = 1003
CloseNoStatusReceived = 1005
CloseAbnormalClosure = 1006
CloseInvalidFramePayloadData = 1007
ClosePolicyViolation = 1008
CloseMessageTooBig = 1009
CloseMandatoryExtension = 1010
CloseInternalServerErr = 1011
CloseServiceRestart = 1012
CloseTryAgainLater = 1013
CloseTLSHandshake = 1015
)The BufferPool interface represents a pool of buffers for efficient memory management. It is used by Upgrader and Dialer to manage write buffers. The *sync.Pool type satisfies this interface.
type BufferPool interface {
// Get gets a value from the pool or returns nil if the pool is empty.
Get() interface{}
// Put adds a value to the pool.
Put(interface{})
}A buffer pool is useful when the application has a modest volume of writes across a large number of connections. When WriteBufferPool is set on an Upgrader or Dialer, connections hold write buffers only when writing messages, rather than for the lifetime of the connection.
Example using sync.Pool:
import (
"sync"
"github.com/gorilla/websocket"
)
var bufferPool = sync.Pool{
New: func() interface{} {
return make([]byte, 1024)
},
}
var upgrader = websocket.Upgrader{
WriteBufferSize: 1024,
WriteBufferPool: &bufferPool,
}
// Or for a dialer
var dialer = websocket.Dialer{
WriteBufferSize: 1024,
WriteBufferPool: &bufferPool,
}Represents a close message received from the peer.
type CloseError struct {
// Code is defined in RFC 6455, section 11.7.
Code int
// Text is the optional text payload.
Text string
}
func (e *CloseError) Error() stringExample usage:
_, _, err := conn.ReadMessage()
if err != nil {
if closeErr, ok := err.(*websocket.CloseError); ok {
log.Printf("Connection closed with code %d: %s", closeErr.Code, closeErr.Text)
} else {
log.Printf("Read error: %v", err)
}
}Describes an error with the handshake from the peer. All fields are unexported.
type HandshakeError struct {
// unexported fields
}
func (e HandshakeError) Error() stringExample usage:
conn, _, err := upgrader.Upgrade(w, r, nil)
if err != nil {
if _, ok := err.(websocket.HandshakeError); ok {
log.Println("Handshake error:", err)
}
return
}var ErrBadHandshake = errors.New("websocket: bad handshake")Returned when the server response to the opening handshake is invalid.
var ErrCloseSent = errors.New("websocket: close sent")Returned when the application writes a message to the connection after sending a close message.
var ErrReadLimit = errors.New("websocket: read limit exceeded")Returned when reading a message that is larger than the read limit set for the connection.
Formats a close code and text as a WebSocket close message payload.
func FormatCloseMessage(closeCode int, text string) []byteReturns an empty message for code CloseNoStatusReceived.
Example:
// Format a close message with code and text
message := websocket.FormatCloseMessage(websocket.CloseNormalClosure, "Goodbye")
// Send the close message
deadline := time.Now().Add(5 * time.Second)
err := conn.WriteControl(websocket.CloseMessage, message, deadline)
if err != nil {
log.Println("Close error:", err)
}Returns boolean indicating whether the error is a *CloseError with one of the specified codes.
func IsCloseError(err error, codes ...int) boolExample:
_, _, err := conn.ReadMessage()
if websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway) {
log.Println("Connection closed normally")
return
}
if err != nil {
log.Println("Unexpected error:", err)
}Returns boolean indicating whether the error is a *CloseError with a code not in the list of expected codes.
func IsUnexpectedCloseError(err error, expectedCodes ...int) boolThis is useful for logging unexpected close codes while treating expected ones as normal.
Example:
_, _, err := conn.ReadMessage()
if websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway) {
log.Printf("Unexpected close error: %v", err)
}// Send text message
err := conn.WriteMessage(websocket.TextMessage, []byte("Hello, World!"))
if err != nil {
log.Println("Error:", err)
}
// Send binary message
binaryData := []byte{0x01, 0x02, 0x03, 0x04}
err = conn.WriteMessage(websocket.BinaryMessage, binaryData)
if err != nil {
log.Println("Error:", err)
}
// Send ping
err = conn.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(5*time.Second))
if err != nil {
log.Println("Error:", err)
}
// Send close
closeMsg := websocket.FormatCloseMessage(websocket.CloseNormalClosure, "Goodbye")
err = conn.WriteControl(websocket.CloseMessage, closeMsg, time.Now().Add(5*time.Second))
if err != nil {
log.Println("Error:", err)
}func handleClose(err error) {
if err == nil {
return
}
if closeErr, ok := err.(*websocket.CloseError); ok {
switch closeErr.Code {
case websocket.CloseNormalClosure:
log.Println("Normal closure")
case websocket.CloseGoingAway:
log.Println("Client going away")
case websocket.CloseProtocolError:
log.Println("Protocol error")
case websocket.CloseInternalServerErr:
log.Println("Server error")
default:
log.Printf("Close code: %d, text: %s", closeErr.Code, closeErr.Text)
}
return
}
log.Printf("Other error: %v", err)
}func categorizeError(err error) string {
if err == nil {
return "no error"
}
// Check for close errors
if websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway) {
return "normal close"
}
if websocket.IsCloseError(err,
websocket.CloseProtocolError,
websocket.CloseUnsupportedData,
websocket.CloseInvalidFramePayloadData) {
return "protocol error"
}
// Check for specific errors
if err == websocket.ErrReadLimit {
return "message too large"
}
if err == websocket.ErrCloseSent {
return "close already sent"
}
if err == websocket.ErrBadHandshake {
return "bad handshake"
}
// Check for timeout
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
return "timeout"
}
return "unknown error"
}func closeWithCustomCode(conn *websocket.Conn, code int, reason string) error {
message := websocket.FormatCloseMessage(code, reason)
deadline := time.Now().Add(5 * time.Second)
err := conn.WriteControl(websocket.CloseMessage, message, deadline)
if err != nil {
return err
}
return conn.Close()
}
// Usage examples
closeWithCustomCode(conn, websocket.CloseNormalClosure, "Session ended")
closeWithCustomCode(conn, websocket.CloseGoingAway, "Server shutting down")
closeWithCustomCode(conn, websocket.ClosePolicyViolation, "Unauthorized")func processMessage(messageType int, data []byte) {
switch messageType {
case websocket.TextMessage:
log.Printf("Text message: %s", string(data))
// Validate UTF-8 if needed
if !utf8.Valid(data) {
log.Println("Invalid UTF-8 in text message")
}
case websocket.BinaryMessage:
log.Printf("Binary message: %d bytes", len(data))
// Process binary data
case websocket.CloseMessage:
log.Println("Close message received")
case websocket.PingMessage:
log.Println("Ping message received")
case websocket.PongMessage:
log.Println("Pong message received")
default:
log.Printf("Unknown message type: %d", messageType)
}
}func robustRead(conn *websocket.Conn) {
for {
messageType, message, err := conn.ReadMessage()
if err != nil {
// Handle expected close codes
if websocket.IsCloseError(err,
websocket.CloseNormalClosure,
websocket.CloseGoingAway) {
log.Println("Connection closed normally")
return
}
// Log unexpected close codes
if websocket.IsUnexpectedCloseError(err,
websocket.CloseNormalClosure,
websocket.CloseGoingAway) {
log.Printf("Unexpected close: %v", err)
return
}
// Handle read limit
if err == websocket.ErrReadLimit {
log.Println("Message too large")
// Send close message
closeMsg := websocket.FormatCloseMessage(
websocket.CloseMessageTooBig,
"Message exceeds limit",
)
conn.WriteControl(websocket.CloseMessage, closeMsg,
time.Now().Add(time.Second))
return
}
// Handle other errors
log.Printf("Read error: %v", err)
return
}
// Process message
processMessage(messageType, message)
}
}import "unicode/utf8"
func sendTextMessage(conn *websocket.Conn, text string) error {
// Validate UTF-8
if !utf8.ValidString(text) {
return errors.New("invalid UTF-8 string")
}
return conn.WriteMessage(websocket.TextMessage, []byte(text))
}
func receiveTextMessage(conn *websocket.Conn) (string, error) {
messageType, data, err := conn.ReadMessage()
if err != nil {
return "", err
}
if messageType != websocket.TextMessage {
return "", errors.New("expected text message")
}
// Validate UTF-8
if !utf8.Valid(data) {
// Close connection with appropriate code
closeMsg := websocket.FormatCloseMessage(
websocket.CloseInvalidFramePayloadData,
"Invalid UTF-8",
)
conn.WriteControl(websocket.CloseMessage, closeMsg,
time.Now().Add(time.Second))
return "", errors.New("invalid UTF-8 in text message")
}
return string(data), nil
}The gorilla/websocket package provides:
These constants and types form the foundation for WebSocket communication, enabling applications to handle different message types, manage connection lifecycle, and respond appropriately to errors.