websocket

package module
v0.0.0-...-268dd68 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 23, 2026 License: MIT Imports: 24 Imported by: 0

README

websocket

A WebSocket (RFC 6455) client and server implementation for Go.

Go Reference

Features

  • No dependencies beyond the Go standard library.
  • Built-in ping/pong liveness checks with per-read controls for keepalive cadence, pong deadlines, idle timeouts, and message progress bounds.
  • Closing handshake support through the Shutdown API: send a close code and reason, then collect the peer's response from the read loop.
  • Passes the complete WebSocket Autobahn test suite for all supported features.

Installation

go get github.com/garyburd/websocket

Documentation

API documentation and examples covering clients, servers, reading, writing, and liveness options are available on pkg.go.dev.

For complete applications, see these examples:

  • chat — a multi-user chat server.
  • command — bridges a subprocess (a calculator by default) to a browser and demonstrates a message-scanning read loop built on the API.

Roadmap

Support for the permessage-deflate compression extension (RFC 7692) is planned but not yet implemented.

Why this package?

When writing gorilla/websocket, I made several design decisions that I later came to regret. Once those decisions became part of the public API, correcting them would have broken existing applications.

I wanted to build a gorilla/websocket v2 that addressed those issues, but for a long time the work was hard to justify: it was a substantial undertaking, and I no longer needed a WebSocket package myself.

That changed when a recent project required WebSockets. LLM assistance made it practical to turn the design into Go code, and this package is the result.

Documentation

Overview

Package websocket implements RFC 6455 clients and servers with a message-oriented API.

Connections

Clients use Dial. HTTP handlers use Upgrade, which requires HTTP/1.x and rejects cross-origin requests by default. Use AllowedOrigins to configure origin policy and SupportedSubprotocols to select a subprotocol.

Messages

Conn.ReadMessage and Conn.WriteMessage use complete byte slices. Conn.Read and Conn.Write stream messages through scoped callbacks.

Concurrency

One goroutine may read while any number write. Concurrent reads return ErrInvalidArgument. A write context bounds only writer acquisition; ReadOptions and WriteOptions bound message I/O.

Connection management

The package does not read in the background. Keep a Conn.Read or Conn.ReadMessage call active for the connection's lifetime, even when the application discards data messages. Reads answer pings, process pongs and close frames, and drive keepalive.

This loop discards data messages while preserving connection management. Returning nil from the callback drains the current message:

func readUntilClosed(c *websocket.Conn, opts *websocket.ReadOptions) error {
	defer c.Close()
	for {
		err := c.Read(opts, func(*websocket.Reader) error {
			return nil
		})
		if err != nil {
			return err
		}
	}
}

Conn.Shutdown sends a close frame and returns without waiting. Continue reading until a read returns an error, then call Conn.Close. Without an active read, the peer's close response is not processed and graceful shutdown cannot complete.

Closing and errors

Conn.Close immediately tears down the transport, unblocking in-flight reads and writes.

Terminal read errors are sticky. Use errors.As for CloseError and errors.Is for ErrClosed, ErrTimeout, ErrProtocol, and ErrInvalidArgument. Stop the read loop on any terminal error.

A write returns ErrCloseSent while a closing handshake is in progress: stop writing data, but keep reading so the handshake completes.

Text messages

Inbound text is validated as UTF-8 unless ReadOptions.SkipUTF8Validation is set. Outbound text is not validated; check untrusted text (e.g. utf8.Valid) before sending, or the peer may close the connection with 1007.

Example (Echo)

A roundtrip between a server built with websocket.Upgrade and a client built with websocket.Dial. The server echoes each message back; the client sends one message and prints the echo.

package main

import (
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"

	"github.com/garyburd/websocket"
)

func main() {
	// Server: echo every message back to the client until it disconnects.
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		c, err := websocket.Upgrade(w, r, nil)
		if err != nil {
			return // Upgrade has already written an error response
		}
		defer c.Close()
		for {
			data, mt, err := c.ReadMessage(nil)
			if err != nil {
				return
			}
			if err := c.WriteMessage(context.Background(), mt, nil, data); err != nil {
				return
			}
		}
	}))
	defer srv.Close()

	// Client: httptest serves http://, so swap in the ws:// scheme to dial.
	url := "ws" + strings.TrimPrefix(srv.URL, "http")
	c, _, err := websocket.Dial(context.Background(), url, nil, nil)
	if err != nil {
		fmt.Println("dial:", err)
		return
	}
	defer c.Close()

	if err := c.WriteMessage(context.Background(), websocket.MessageText, nil, []byte("hello")); err != nil {
		fmt.Println("write:", err)
		return
	}
	data, _, err := c.ReadMessage(nil)
	if err != nil {
		fmt.Println("read:", err)
		return
	}
	fmt.Printf("%s\n", data)

}
Output:
hello
Example (Json)

The package has no JSON helpers; use encoding/json directly over the streaming websocket.Writer and websocket.Reader. Sending encodes into the writer; receiving decodes from the reader within the read callback. The server here decodes each message and replies with a JSON message of its own.

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"

	"github.com/garyburd/websocket"
)

func main() {
	type message struct {
		Greeting string `json:"greeting"`
		N        int    `json:"n"`
	}

	// Server: decode each message, then reply with a JSON message.
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		c, err := websocket.Upgrade(w, r, nil)
		if err != nil {
			return // Upgrade has already written an error response
		}
		defer c.Close()
		for {
			var in message
			err := c.Read(nil, func(rd *websocket.Reader) error {
				return json.NewDecoder(rd).Decode(&in)
			})
			if err != nil {
				return
			}
			reply := message{Greeting: "hi " + in.Greeting, N: in.N + 1}
			err = c.Write(context.Background(), websocket.MessageText, nil, func(wr *websocket.Writer) error {
				return json.NewEncoder(wr).Encode(reply)
			})
			if err != nil {
				return
			}
		}
	}))
	defer srv.Close()

	url := "ws" + strings.TrimPrefix(srv.URL, "http")
	c, _, err := websocket.Dial(context.Background(), url, nil, nil)
	if err != nil {
		fmt.Println("dial:", err)
		return
	}
	defer c.Close()

	// Send: encode a value as one text message.
	err = c.Write(context.Background(), websocket.MessageText, nil, func(w *websocket.Writer) error {
		return json.NewEncoder(w).Encode(message{Greeting: "there", N: 1})
	})
	if err != nil {
		fmt.Println("write:", err)
		return
	}

	// Receive: decode the reply into a value.
	var in message
	err = c.Read(nil, func(r *websocket.Reader) error {
		return json.NewDecoder(r).Decode(&in)
	})
	if err != nil {
		fmt.Println("read:", err)
		return
	}
	fmt.Printf("%s %d\n", in.Greeting, in.N)

}
Output:
hi there 2

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrClosed reports local teardown by [Conn.Close], [Reader.Abort], or a
	// panicking callback. A timed-out [Conn.Shutdown] reports [ErrTimeout].
	ErrClosed = errors.New("websocket: connection closed")

	// ErrTimeout reports a liveness or I/O deadline failure.
	ErrTimeout = errors.New("websocket: timeout")

	// ErrProtocol reports a peer protocol violation.
	ErrProtocol = errors.New("websocket: protocol error")

	// ErrBadHandshake reports a rejected opening handshake. [Dial] and [Upgrade]
	// wrap it with details.
	ErrBadHandshake = errors.New("websocket: bad handshake")

	// ErrCloseSent reports a data write attempted after this endpoint sent a
	// close frame.
	ErrCloseSent = errors.New("websocket: cannot write after close")

	// ErrInvalidArgument reports a rejected argument or option. The connection
	// remains usable.
	ErrInvalidArgument = errors.New("websocket: invalid argument")
)

Functions

func AllowedOrigins

func AllowedOrigins(allowSameHost bool, patterns ...string) func(*http.Request) bool

AllowedOrigins returns an origin check using case-insensitive path.Match patterns. Patterns containing "://" match scheme://host; others match only the host. allowSameHost accepts an Origin host equal to the request Host. An absent Origin is accepted.

Examples:

AllowedOrigins(true, "*.example.com")              // request host, or any host under example.com
AllowedOrigins(false, "https://app.example.com")   // exactly this scheme and host
AllowedOrigins(false, "https://*.example.com:443") // scheme + host + port

AllowedOrigins panics on a malformed pattern.

func ParseOrigin

func ParseOrigin(r *http.Request) (*url.URL, error)

ParseOrigin parses the request Origin. It returns (nil, nil) when the header is absent and an error when it is malformed or has no host.

func SupportedSubprotocols

func SupportedSubprotocols(supported ...string) func(*http.Request) string

SupportedSubprotocols returns a selector for the client's first offered value present in supported. Matching is case-sensitive.

Types

type CloseCode

type CloseCode int

CloseCode is an RFC 6455 close status code.

CloseNoStatusReceived and CloseAbnormalClosure are receive-only.

const (
	CloseNormalClosure   CloseCode = 1000
	CloseGoingAway       CloseCode = 1001
	CloseProtocolError   CloseCode = 1002
	CloseUnsupportedData CloseCode = 1003
	CloseInvalidData     CloseCode = 1007
	ClosePolicyViolation CloseCode = 1008
	CloseMessageTooBig   CloseCode = 1009
	CloseInternalError   CloseCode = 1011

	CloseNoStatusReceived CloseCode = 1005
	CloseAbnormalClosure  CloseCode = 1006
)

func (CloseCode) String

func (c CloseCode) String() string

String returns the code name or its decimal value.

type CloseError

type CloseError struct {
	// Code is the peer status, [CloseNoStatusReceived], or
	// [CloseAbnormalClosure].
	Code CloseCode
	// Text is the UTF-8 close reason.
	Text string
}

CloseError reports a peer close. Use errors.As to inspect Code and Text. CloseAbnormalClosure means the transport ended without a close frame.

Example

A read loop ends on the first error. Use errors.As to recover the peer's close code and reason from a websocket.CloseError; a websocket.CloseAbnormalClosure code means the peer vanished without sending a close message. A local websocket.Conn.Close reports websocket.ErrClosed, and other failures (timeouts, protocol errors) end the loop the same way.

package main

import (
	"errors"
	"log"

	"github.com/garyburd/websocket"
)

func main() {
	var c *websocket.Conn // obtained from websocket.Dial or websocket.Upgrade
	defer c.Close()

	for {
		_, _, err := c.ReadMessage(nil)
		if err == nil {
			continue // ... handle the message ...
		}

		var ce *websocket.CloseError
		switch {
		case errors.As(err, &ce):
			if ce.Code == websocket.CloseAbnormalClosure {
				log.Print("peer vanished without a close message")
			} else {
				log.Printf("peer closed: code=%s reason=%q", ce.Code, ce.Text)
			}
		case errors.Is(err, websocket.ErrClosed):
			log.Print("connection closed locally")
		default:
			log.Printf("connection failed: %v", err)
		}
		return
	}
}

func (*CloseError) Error

func (e *CloseError) Error() string

Error formats the close code and reason.

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

Conn is a WebSocket connection created by Dial or Upgrade. One goroutine may read while any number of goroutines write concurrently.

func Dial

func Dial(ctx context.Context, rawURL string, opts *DialOptions, header http.Header) (*Conn, *http.Response, error)

Dial opens a WebSocket connection. header may contain application headers such as Origin, Cookie, and Authorization.

Dial rejects Sec-WebSocket-* headers, which it manages. Configure subprotocols through DialOptions.Subprotocols.

ctx bounds only the handshake. Use Conn.Shutdown, Conn.Close, ReadOptions, and WriteOptions after Dial returns.

Dial does not follow redirects. A response other than 101 returns ErrBadHandshake and the response with up to DialOptions.ResponseBodyLimit body bytes. On success, the Conn owns the response body. The caller need not close either returned body.

func Upgrade

func Upgrade(w http.ResponseWriter, r *http.Request, opts *UpgradeOptions) (*Conn, error)

Upgrade accepts a WebSocket handshake and takes ownership of the underlying HTTP/1.x connection.

On failure, Upgrade writes an HTTP error and returns ErrBadHandshake. On success, the returned Conn owns w; the handler must not use w again.

func (*Conn) Close

func (c *Conn) Close() error

Close immediately closes the transport and unblocks active reads and writes. It is concurrent-safe and idempotent. Use Conn.Shutdown first for a graceful close.

func (*Conn) NetConn

func (c *Conn) NetConn() net.Conn

NetConn returns the underlying network connection, or nil when unavailable.

The Conn owns the result. Do not read, write, close, or set deadlines on it.

func (*Conn) Read

func (c *Conn) Read(opts *ReadOptions, fn func(r *Reader) error) error

Read passes the next message to fn. The Reader is valid only during fn; unread payload is discarded afterward unless Reader.Abort is called.

An fn error is returned unchanged, so applications can match their own errors with errors.Is or errors.As; unless fn failed a read or called Reader.Abort, the connection remains usable. ErrInvalidArgument (invalid options or a concurrent read; reads are single-goroutine) also leaves the connection untouched. Any other error is terminal and sticky: every later Read returns it, so stop the read loop.

A panic in fn tears down the connection and propagates.

Example (WriteOnly)

An application that only sends data must still keep a read outstanding, so the package can answer the peer's pings and observe its close. Run a discarding read loop in its own goroutine alongside the writer; the keepalive options on the read detect a peer that has gone away.

package main

import (
	"context"
	"time"

	"github.com/garyburd/websocket"
)

func main() {
	var c *websocket.Conn // obtained from websocket.Dial or websocket.Upgrade

	// Discard inbound data, but let the package handle control frames and surface
	// the peer's close. The first error ends the loop and closes the connection.
	go func() {
		opts := &websocket.ReadOptions{
			KeepaliveInterval: 30 * time.Second,
			KeepaliveTimeout:  10 * time.Second,
			MatchPong:         true,
		}
		for {
			if err := c.Read(opts, func(*websocket.Reader) error { return nil }); err != nil {
				c.Close()
				return
			}
		}
	}()

	// Send the time once a second until the connection goes away.
	tick := time.NewTicker(time.Second)
	defer tick.Stop()
	for range tick.C {
		msg := []byte(time.Now().Format(time.RFC3339))
		if err := c.WriteMessage(context.Background(), websocket.MessageText, nil, msg); err != nil {
			return // the read goroutine has already closed the connection
		}
	}
}

func (*Conn) ReadMessage

func (c *Conn) ReadMessage(opts *ReadOptions) ([]byte, MessageType, error)

ReadMessage reads and returns one complete message.

func (*Conn) Shutdown

func (c *Conn) Shutdown(code CloseCode, reason string) error

Shutdown sends a close frame and returns without waiting for the response. Continue reading, then call Conn.Close. A message the peer is still sending may be cut off; read any inbound message you need to completion first.

An invalid code or invalid UTF-8 in reason returns ErrInvalidArgument. The reason is truncated to 123 bytes at a rune boundary.

DialOptions.ShutdownTimeout or UpgradeOptions.ShutdownTimeout bounds both sending and waiting. Expiry tears down the connection with ErrTimeout. Shutdown is safe for concurrent use and is a no-op when closing has begun.

Example

To close gracefully, call websocket.Conn.Shutdown to send the close message, then keep reading until the read loop drains the peer's echo and returns an error, and finally call websocket.Conn.Close. The ShutdownTimeout option bounds the wait for the echo.

package main

import (
	"github.com/garyburd/websocket"
)

func main() {
	var c *websocket.Conn // obtained from websocket.Dial or websocket.Upgrade
	defer c.Close()

	if err := c.Shutdown(websocket.CloseNormalClosure, "bye"); err != nil {
		return // ... handle error ...
	}
	// Drain until the closing handshake completes (or the connection fails); the
	// peer's echoed close surfaces here as the terminal read error.
	for {
		if _, _, err := c.ReadMessage(nil); err != nil {
			break
		}
	}
}

func (*Conn) Subprotocol

func (c *Conn) Subprotocol() string

Subprotocol returns the negotiated subprotocol, or "" if none.

func (*Conn) Write

func (c *Conn) Write(ctx context.Context, mt MessageType, opts *WriteOptions, fn func(*Writer) error) error

Write passes a streaming message writer to fn and finishes the message when fn returns.

ctx bounds only writer acquisition; opts bounds the body.

An fn error is returned unchanged, so applications can match their own errors with errors.Is or errors.As. ErrInvalidArgument and acquisition cancellation (errors.Is matches context.Canceled or context.DeadlineExceeded) leave the connection untouched; after any other error the connection is unusable. A writer timeout tears it down, and other fn errors start a graceful CloseInternalError handshake.

Outbound text is not validated; check untrusted text (e.g. utf8.Valid) before sending, or the peer may close with 1007. A panic in fn tears down the connection and propagates.

func (*Conn) WriteMessage

func (c *Conn) WriteMessage(ctx context.Context, mt MessageType, opts *WriteOptions, data []byte) error

WriteMessage sends data as one message and one frame. ctx bounds only writer acquisition. Outbound text is not validated.

type DialOptions

type DialOptions struct {
	// Transport performs the HTTP upgrade. If nil, Dial uses an
	// [http.Transport] with HTTP/2 disabled and [http.ProxyFromEnvironment].
	//
	// A custom transport must use HTTP/1.1 and return an [io.ReadWriteCloser]
	// as the body of a 101 response. The standard [http.Transport] does so.
	// Do not share a [tls.Config] (or the whole transport) with net/http: that
	// puts "h2" in NextProtos, ALPN negotiates HTTP/2, and the handshake fails.
	Transport http.RoundTripper

	// Subprotocols lists Sec-WebSocket-Protocol values in preference order.
	// Each value must be a nonempty HTTP token. See [Conn.Subprotocol].
	Subprotocols []string

	// ControlReplyTimeout bounds automatic pong and close writes. Zero waits
	// indefinitely. It does not apply to keepalive pings, [Conn.Shutdown], or
	// data messages.
	ControlReplyTimeout time.Duration

	// ShutdownTimeout bounds both sending a close and waiting for its response.
	// Expiry tears down the connection with [ErrTimeout]. Zero waits indefinitely.
	ShutdownTimeout time.Duration

	// MaxMessageSize limits one inbound message. Exceeding it sends
	// [CloseMessageTooBig] and returns [ErrProtocol]. Zero is unlimited.
	MaxMessageSize int

	// WriteBufferSize is the per-frame coalescing buffer size. Zero uses 4 KiB;
	// values below 64 use 64.
	WriteBufferSize int

	// ResponseBodyLimit limits the captured body of a failed handshake. Zero
	// discards it. A malformed 101 body is always discarded because it is the
	// upgraded connection, not an HTTP error body.
	ResponseBodyLimit int

	// ManualCloseResponse disables the automatic response to a peer close.
	// The application may finish pending writes, then call [Conn.Shutdown].
	ManualCloseResponse bool
}

DialOptions configures Dial. Zero values disable optional behavior; WriteBufferSize defaults to 4 KiB.

Dial reads the options only during the call.

type MessageType

type MessageType int

MessageType identifies a binary or text message. Its zero value is MessageBinary.

const (
	MessageBinary MessageType = iota
	MessageText
)

func (MessageType) String

func (m MessageType) String() string

String returns the type name or its decimal value.

type ReadOptions

type ReadOptions struct {
	// IdleTimeout bounds the wait for the next data message. Control frames do
	// not reset it. Zero waits indefinitely; expiry is terminal.
	IdleTimeout time.Duration

	// KeepaliveInterval sends a ping after this much inbound silence. Qualifying
	// inbound frames reset the interval. Zero disables keepalive.
	KeepaliveInterval time.Duration

	// KeepaliveTimeout bounds sending a keepalive ping and receiving a qualifying
	// response. Zero sends pings without requiring a response. It requires a
	// nonzero KeepaliveInterval.
	//
	// Allow enough time for any in-flight message plus the round trip. The
	// interval restarts when a check completes, so the gap between pings is
	// KeepaliveInterval plus the check's duration; to survive an external idle
	// limit (a proxy or load balancer), keep KeepaliveInterval +
	// KeepaliveTimeout below that limit.
	KeepaliveTimeout time.Duration

	// MatchPong requires a pong matching this connection's ping. Otherwise any
	// inbound control frame refreshes keepalive.
	MatchPong bool

	// MessageTimeout bounds a message body from message start. Zero is unbounded.
	// It conflicts with MinReadRate; [Reader.SetReadDeadline] overrides it.
	MessageTimeout time.Duration

	// MinReadRate is the minimum body rate in bytes per second. A read receives
	// size/rate time, so large buffers delay stall detection. Zero disables the
	// rate check. It conflicts with MessageTimeout and [Reader.SetReadDeadline].
	MinReadRate int

	// SkipUTF8Validation disables validation of inbound text messages. Invalid
	// text otherwise closes the connection with [CloseInvalidData].
	SkipUTF8Validation bool
}

ReadOptions configures one Conn.Read or Conn.ReadMessage. Nil disables read-side liveness checks.

A read copies the options when it begins. Idle and keepalive settings apply before a message; message timeout, rate, and Reader.SetReadDeadline apply to its body.

Example (PerReadIdleTimeout)

A server that expects the client to send a message immediately after connecting and then messages at any interval uses two *ReadOptions: a 10s IdleTimeout (a hard cap on receiving the first message), and for the steady state no cap. Keepalive instead detects a dead client. IdleTimeout is per-call precisely because the first read and the steady reads differ.

package main

import (
	"time"

	"github.com/garyburd/websocket"
)

func main() {
	var c *websocket.Conn // obtained from websocket.Upgrade

	first := &websocket.ReadOptions{IdleTimeout: 10 * time.Second}
	steady := &websocket.ReadOptions{
		KeepaliveInterval: 30 * time.Second,
		KeepaliveTimeout:  10 * time.Second,
		MatchPong:         true,
	}

	if _, _, err := c.ReadMessage(first); err != nil {
		return // client stayed silent within 10s
	}
	for {
		_, _, err := c.ReadMessage(steady)
		if err != nil {
			return
		}
		// ... handle the message ...
	}
}

type Reader

type Reader struct {
	// contains filtered or unexported fields
}

Reader streams one inbound message. It is valid only during its Conn.Read callback.

func (*Reader) Abort

func (r *Reader) Abort(code CloseCode, reason string) error

Abort stops reading without draining the current message and sends a best-effort close with code and reason.

An invalid code or invalid UTF-8 in reason returns ErrInvalidArgument without aborting. After a successful Abort, Conn.Read returns the callback error or ErrClosed.

func (*Reader) Read

func (r *Reader) Read(p []byte) (int, error)

Read reads message payload and returns io.EOF at the message boundary.

func (*Reader) SetReadDeadline

func (r *Reader) SetReadDeadline(t time.Time) error

SetReadDeadline sets the absolute deadline for the current message body. A zero value removes the deadline. It overrides ReadOptions.MessageTimeout.

Expiry is terminal. SetReadDeadline conflicts with ReadOptions.MinReadRate.

func (*Reader) Type

func (r *Reader) Type() MessageType

Type returns MessageBinary or MessageText.

func (*Reader) WriteTo

func (r *Reader) WriteTo(dst io.Writer) (int64, error)

WriteTo copies the remaining message payload to dst.

type UpgradeOptions

type UpgradeOptions struct {
	// SelectSubprotocol selects a value offered in Sec-WebSocket-Protocol.
	// Return "" for none. See [SupportedSubprotocols].
	SelectSubprotocol func(*http.Request) string

	// CheckOrigin approves the Origin header. If nil, Upgrade accepts an absent
	// Origin or one whose host equals the request Host. See [AllowedOrigins].
	// Browsers do not apply CORS to WebSocket upgrades, so a permissive check
	// invites cross-site WebSocket hijacking.
	CheckOrigin func(*http.Request) bool

	// ResponseHeader adds headers to the 101 response. Upgrade overwrites its
	// protocol-managed headers.
	ResponseHeader http.Header

	// OnError writes a rejected handshake response. err wraps
	// [ErrBadHandshake]. The callback must not hijack or write a success status.
	// If nil, Upgrade uses [http.Error].
	OnError func(w http.ResponseWriter, r *http.Request, status int, err error)

	// ControlReplyTimeout bounds automatic pong and close writes. Zero waits
	// indefinitely. It does not apply to keepalive pings, [Conn.Shutdown], or
	// data messages.
	ControlReplyTimeout time.Duration

	// ShutdownTimeout bounds both sending a close and waiting for its response.
	// Expiry tears down the connection with [ErrTimeout]. Zero waits indefinitely.
	ShutdownTimeout time.Duration

	// MaxMessageSize limits one inbound message. Exceeding it sends
	// [CloseMessageTooBig] and returns [ErrProtocol]. Zero is unlimited.
	MaxMessageSize int

	// WriteBufferSize is the per-frame coalescing buffer size. Zero uses 4 KiB;
	// values below 64 use 64.
	WriteBufferSize int

	// ManualCloseResponse disables the automatic response to a peer close.
	// The application may finish pending writes, then call [Conn.Shutdown].
	ManualCloseResponse bool
}

UpgradeOptions configures Upgrade. Zero values disable optional behavior; WriteBufferSize defaults to 4 KiB.

Upgrade reads the options only during the call.

type WriteOptions

type WriteOptions struct {
	// MessageTimeout bounds a message from callback entry. Zero is unbounded.
	// It conflicts with MinWriteRate; [Writer.SetWriteDeadline] overrides it.
	MessageTimeout time.Duration

	// MinWriteRate is the minimum body rate in bytes per second. A frame receives
	// size/rate time, so large frames delay stall detection. Zero disables the
	// rate check. It conflicts with MessageTimeout and [Writer.SetWriteDeadline].
	MinWriteRate int
}

WriteOptions configures one Conn.Write or Conn.WriteMessage. Nil disables write-side liveness checks.

A write copies the options when it begins. Its context bounds only writer acquisition; these options bound the message body.

type Writer

type Writer struct {
	// contains filtered or unexported fields
}

Writer streams one outbound message and coalesces small writes. It is valid only during its Conn.Write callback.

func (*Writer) Final

func (w *Writer) Final()

Final makes the next Writer.Write or Writer.WriteString finish the message. It is an optional optimization; Conn.Write otherwise finishes the message when the callback returns.

Example

When a buffered writer is layered on the websocket.Writer, call websocket.Writer.Final just before flushing it. The flush then emits the message's last bytes as the final frame (FIN set), instead of the message ending with a separate empty terminating frame when the callback returns.

package main

import (
	"bufio"
	"context"

	"github.com/garyburd/websocket"
)

func main() {
	var c *websocket.Conn // obtained from websocket.Dial or websocket.Upgrade

	err := c.Write(context.Background(), websocket.MessageText, nil, func(w *websocket.Writer) error {
		bw := bufio.NewWriter(w)
		// ... write the message to bw, e.g. with a json.Encoder ...
		bw.WriteString("hello, world")

		w.Final()         // the next flush ends the message
		return bw.Flush() // sends the buffered bytes as the final frame
	})
	if err != nil {
		return // ... handle write error ...
	}
}

func (*Writer) ReadFrom

func (w *Writer) ReadFrom(r io.Reader) (int64, error)

ReadFrom copies r into the message without finishing it.

A source error fails the message and causes Conn.Write to close gracefully.

func (*Writer) SetWriteDeadline

func (w *Writer) SetWriteDeadline(t time.Time) error

SetWriteDeadline sets the absolute deadline for the current message body. A zero value removes the deadline. It overrides WriteOptions.MessageTimeout.

Expiry is terminal. SetWriteDeadline conflicts with WriteOptions.MinWriteRate.

func (*Writer) Write

func (w *Writer) Write(p []byte) (int, error)

Write adds p to the message. After Writer.Final, it also finishes it.

func (*Writer) WriteString

func (w *Writer) WriteString(s string) (int, error)

WriteString is the io.StringWriter form of Writer.Write.

Directories

Path Synopsis
examples
chat command
Command chat is a multi-user chat server demonstrating the websocket package.
Command chat is a multi-user chat server demonstrating the websocket package.
command command
Command command runs a subprocess and bridges its standard input, output, and error streams to a websocket connection, demonstrating the websocket package and a MessageScanner read loop.
Command command runs a subprocess and bridges its standard input, output, and error streams to a websocket connection, demonstrating the websocket package and a MessageScanner read loop.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL