or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

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

server.mddocs/

Server-Side WebSocket

Server-side WebSocket functionality allows upgrading HTTP connections to WebSocket connections using the Upgrader type. The Upgrader provides full control over handshake parameters, origin checking, buffer configuration, and subprotocol negotiation.

Upgrader Type

The Upgrader type specifies parameters for upgrading an HTTP connection to a WebSocket connection. It is safe to call Upgrader's methods concurrently.

type Upgrader struct {
    // HandshakeTimeout specifies the duration for the handshake to complete.
    HandshakeTimeout time.Duration

    // ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer
    // size is zero, then buffers allocated by the HTTP server are used. The
    // I/O buffer sizes do not limit the size of the messages that can be sent
    // or received.
    ReadBufferSize  int
    WriteBufferSize int

    // WriteBufferPool is a pool of buffers for write operations. If the value
    // is not set, then write buffers are allocated to the connection for the
    // lifetime of the connection.
    //
    // A pool is most useful when the application has a modest volume of writes
    // across a large number of connections.
    //
    // Applications should use a single pool for each unique value of
    // WriteBufferSize.
    WriteBufferPool BufferPool

    // Subprotocols specifies the server's supported protocols in order of
    // preference. If this field is not nil, then the Upgrade method negotiates a
    // subprotocol by selecting the first match in this list with a protocol
    // requested by the client. If there's no match, then no protocol is
    // negotiated (the Sec-Websocket-Protocol header is not included in the
    // handshake response).
    Subprotocols []string

    // Error specifies the function for generating HTTP error responses. If Error
    // is nil, then http.Error is used to generate the HTTP response.
    Error func(w http.ResponseWriter, r *http.Request, status int, reason error)

    // CheckOrigin returns true if the request Origin header is acceptable. If
    // CheckOrigin is nil, then a safe default is used: return false if the
    // Origin request header is present and the origin host is not equal to
    // request Host header.
    //
    // A CheckOrigin function should carefully validate the request origin to
    // prevent cross-site request forgery.
    CheckOrigin func(r *http.Request) bool

    // EnableCompression specify if the server should attempt to negotiate per
    // message compression (RFC 7692). Setting this value to true does not
    // guarantee that compression will be supported. Currently only "no context
    // takeover" modes are supported.
    EnableCompression bool
}

Upgrade Method

Upgrades an HTTP server connection to the WebSocket protocol.

func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error)

The responseHeader is included in the response to the client's upgrade request. Use it to specify cookies (Set-Cookie). To specify subprotocols supported by the server, set Upgrader.Subprotocols directly.

If the upgrade fails, then Upgrade replies to the client with an HTTP error response.

BufferPool Interface

The BufferPool interface represents a pool of buffers for efficient memory management. The *sync.Pool type satisfies this interface.

See Constants and Types for the complete BufferPool interface definition.

Usage Examples

Basic HTTP to WebSocket Upgrade

package main

import (
    "log"
    "net/http"
    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
}

func wsHandler(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println("Upgrade error:", err)
        return
    }
    defer conn.Close()

    // Use the connection
    for {
        messageType, message, err := conn.ReadMessage()
        if err != nil {
            log.Println("Read error:", err)
            break
        }
        log.Printf("Received: %s", message)

        err = conn.WriteMessage(messageType, message)
        if err != nil {
            log.Println("Write error:", err)
            break
        }
    }
}

func main() {
    http.HandleFunc("/ws", wsHandler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Upgrader with Origin Checking

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin: func(r *http.Request) bool {
        // Allow requests from same origin
        origin := r.Header.Get("Origin")
        return origin == "http://"+r.Host || origin == "https://"+r.Host
    },
}

func wsHandler(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        // Origin check failed or other upgrade error
        log.Println("Upgrade failed:", err)
        return
    }
    defer conn.Close()
    // ... use connection
}

Upgrader with Subprotocol Negotiation

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    Subprotocols:    []string{"v1.chat.example.com", "v2.chat.example.com"},
}

func wsHandler(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println("Upgrade error:", err)
        return
    }
    defer conn.Close()

    // Check which subprotocol was negotiated
    protocol := conn.Subprotocol()
    log.Printf("Using subprotocol: %s", protocol)
    // ... use connection
}

Upgrader with Buffer Pool

import (
    "sync"
    "github.com/gorilla/websocket"
)

var bufferPool = sync.Pool{
    New: func() interface{} {
        return make([]byte, 1024)
    },
}

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    WriteBufferPool: &bufferPool,
}

func wsHandler(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println("Upgrade error:", err)
        return
    }
    defer conn.Close()
    // ... use connection
    // Write buffers will be pooled
}

Upgrader with Compression

var upgrader = websocket.Upgrader{
    ReadBufferSize:    1024,
    WriteBufferSize:   1024,
    EnableCompression: true,
}

func wsHandler(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println("Upgrade error:", err)
        return
    }
    defer conn.Close()

    // Messages will be automatically decompressed on read
    // Use conn.EnableWriteCompression(true) to compress writes
    // ... use connection
}

Upgrader with Custom Error Handler

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    Error: func(w http.ResponseWriter, r *http.Request, status int, reason error) {
        // Custom error handling
        log.Printf("WebSocket upgrade error: %v", reason)
        http.Error(w, "WebSocket upgrade failed", status)
    },
}

func wsHandler(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        // Error already handled by Error function
        return
    }
    defer conn.Close()
    // ... use connection
}

Setting Response Headers During Upgrade

func wsHandler(w http.ResponseWriter, r *http.Request) {
    // Prepare response headers
    responseHeader := http.Header{}
    responseHeader.Set("Set-Cookie", "session=abc123; Path=/; HttpOnly")

    conn, err := upgrader.Upgrade(w, r, responseHeader)
    if err != nil {
        log.Println("Upgrade error:", err)
        return
    }
    defer conn.Close()
    // ... use connection
}

Helper Functions

IsWebSocketUpgrade

Returns true if the client requested upgrade to the WebSocket protocol.

func IsWebSocketUpgrade(r *http.Request) bool

This can be used to check if a request is a WebSocket upgrade request before calling Upgrade:

func handler(w http.ResponseWriter, r *http.Request) {
    if !websocket.IsWebSocketUpgrade(r) {
        http.Error(w, "Not a WebSocket upgrade request", http.StatusBadRequest)
        return
    }

    conn, err := upgrader.Upgrade(w, r, nil)
    // ... handle connection
}

Subprotocols

Returns the subprotocols requested by the client in the Sec-Websocket-Protocol header.

func Subprotocols(r *http.Request) []string

This can be used for custom subprotocol negotiation logic:

func handler(w http.ResponseWriter, r *http.Request) {
    requestedProtocols := websocket.Subprotocols(r)
    log.Printf("Client requested protocols: %v", requestedProtocols)

    conn, err := upgrader.Upgrade(w, r, nil)
    // ... handle connection
}

Deprecated Server Functions

Upgrade (Package-Level Function)

func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error)

Deprecated: Use websocket.Upgrader instead.

This function upgrades the HTTP server connection to the WebSocket protocol but does not perform origin checking. The application is responsible for checking the Origin header before calling Upgrade.

Origin Checking

Web browsers allow JavaScript applications to open a WebSocket connection to any host. It's up to the server to enforce an origin policy using the Origin request header sent by the browser.

The Upgrader calls the function specified in the CheckOrigin field to check the origin. If the CheckOrigin function returns false, then the Upgrade method fails the WebSocket handshake with HTTP status 403.

If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail the handshake if the Origin request header is present and the Origin host is not equal to the Host request header.

Example Origin Policy

var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
        origin := r.Header.Get("Origin")
        // Allow requests from specific domains
        allowedOrigins := []string{
            "https://example.com",
            "https://www.example.com",
        }
        for _, allowed := range allowedOrigins {
            if origin == allowed {
                return true
            }
        }
        return false
    },
}

Buffer Configuration

Connections buffer network input and output to reduce the number of system calls when reading or writing messages. The buffer sizes in bytes are specified by the ReadBufferSize and WriteBufferSize fields in the Upgrader.

  • When a buffer size field is set to zero, the Upgrader reuses buffers created by the HTTP server (typically 4096 bytes)
  • Buffer sizes do not limit the size of messages that can be read or written
  • Buffers are held for the lifetime of the connection by default
  • If WriteBufferPool is set, then a connection holds the write buffer only when writing a message

Guidelines for Buffer Sizing

  1. Match Expected Message Size: Limit buffer sizes to the maximum expected message size. Buffers larger than the largest message provide no benefit.

  2. Trade Memory for Performance: If 99% of messages are smaller than 256 bytes but the maximum is 512 bytes, a buffer size of 256 bytes results in only 1.01 more system calls while saving 50% memory.

  3. Use Pooling for Many Connections: A write buffer pool is useful when the application has modest writes over a large number of connections. Pooling allows larger buffer sizes with reduced memory impact.