or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

client.mdconnection.mdhandlers.mdindex.mdserver.mdtypes.md
tile.json

types.mddocs/

Constants and Types

This document covers the constants, error types, and helper functions used throughout the gorilla/websocket package.

Message Type Constants

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
)

Data Messages

  • TextMessage (1): UTF-8 encoded text data. The application is responsible for ensuring text messages are valid UTF-8.
  • BinaryMessage (2): Binary data. The interpretation of binary messages is left to the application.

Control Messages

  • CloseMessage (8): Initiates connection close
  • PingMessage (9): Keepalive check from sender
  • PongMessage (10): Response to PingMessage

Close Code Constants

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
)

Close Code Meanings

  • CloseNormalClosure (1000): Normal closure; the connection successfully completed
  • CloseGoingAway (1001): Endpoint going away (e.g., server shutdown, browser navigating away)
  • CloseProtocolError (1002): Protocol error
  • CloseUnsupportedData (1003): Received data type that cannot be accepted
  • CloseNoStatusReceived (1005): No status code was present (used internally)
  • CloseAbnormalClosure (1006): Connection closed abnormally without close frame
  • CloseInvalidFramePayloadData (1007): Invalid payload data (e.g., non-UTF-8 in text message)
  • ClosePolicyViolation (1008): Message violates policy
  • CloseMessageTooBig (1009): Message too large to process
  • CloseMandatoryExtension (1010): Expected extension not negotiated
  • CloseInternalServerErr (1011): Internal server error
  • CloseServiceRestart (1012): Service is restarting
  • CloseTryAgainLater (1013): Service is overloaded, try again later
  • CloseTLSHandshake (1015): TLS handshake failure (used internally)

Interfaces

BufferPool

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,
}

Error Types

CloseError

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() string

Example 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)
    }
}

HandshakeError

Describes an error with the handshake from the peer. All fields are unexported.

type HandshakeError struct {
    // unexported fields
}

func (e HandshakeError) Error() string

Example usage:

conn, _, err := upgrader.Upgrade(w, r, nil)
if err != nil {
    if _, ok := err.(websocket.HandshakeError); ok {
        log.Println("Handshake error:", err)
    }
    return
}

Package-Level Errors

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.

Helper Functions

FormatCloseMessage

Formats a close code and text as a WebSocket close message payload.

func FormatCloseMessage(closeCode int, text string) []byte

Returns 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)
}

IsCloseError

Returns boolean indicating whether the error is a *CloseError with one of the specified codes.

func IsCloseError(err error, codes ...int) bool

Example:

_, _, 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)
}

IsUnexpectedCloseError

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) bool

This 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)
}

Usage Examples

Sending Different Message Types

// 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)
}

Handling Different Close Codes

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)
}

Categorizing Errors

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"
}

Close Message With Custom Code

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")

Detecting Message Type

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)
    }
}

Error Handling Best Practices

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)
    }
}

Validating Text Messages

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
}

Summary

The gorilla/websocket package provides:

  • Message Types: Text, binary, and three control message types
  • Close Codes: Standard RFC 6455 codes for various close scenarios
  • Error Types: Structured errors for handshake failures and close messages
  • Helper Functions: Utilities for formatting close messages and checking error types

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.