websocket

package module
v1.10.0 Latest Latest
Warning

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

Go to latest
Published: Mar 29, 2024 License: ISC Imports: 28 Imported by: 1

README

websocket

this is a fork of github.com/hnooyr/websocket

Documentation

Overview

Example (CrossOrigin)
package main

import (
	"log"
	"net/http"

	"gfx.cafe/open/websocket"
)

func main() {
	// This handler demonstrates how to safely accept cross origin WebSockets
	// from the origin example.com.
	fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		c, err := websocket.Accept(w, r, &websocket.AcceptOptions{
			OriginPatterns: []string{"example.com"},
		})
		if err != nil {
			log.Println(err)
			return
		}
		c.Close(websocket.StatusNormalClosure, "cross origin WebSocket accepted")
	})

	err := http.ListenAndServe("localhost:8080", fn)
	log.Fatal(err)
}
Output:

Example (Echo)

This example demonstrates a echo server.

package main

import ()

func main() {
	// https://github.com/nhooyr/websocket/tree/master/internal/examples/echo
}
Output:

Example (FullStackChat)

This example demonstrates full stack chat with an automated test.

package main

import ()

func main() {
	// https://github.com/nhooyr/websocket/tree/master/internal/examples/chat
}
Output:

Example (WriteOnly)
package main

import (
	"context"
	"log"
	"net/http"
	"time"

	"gfx.cafe/open/websocket"
	"gfx.cafe/open/websocket/wsjson"
)

func main() {
	// This handler demonstrates how to correctly handle a write only WebSocket connection.
	// i.e you only expect to write messages and do not expect to read any messages.
	fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		c, err := websocket.Accept(w, r, nil)
		if err != nil {
			log.Println(err)
			return
		}
		defer c.CloseNow()

		ctx, cancel := context.WithTimeout(r.Context(), time.Minute*10)
		defer cancel()

		ctx = c.CloseRead(ctx)

		t := time.NewTicker(time.Second * 30)
		defer t.Stop()

		for {
			select {
			case <-ctx.Done():
				c.Close(websocket.StatusNormalClosure, "")
				return
			case <-t.C:
				err = wsjson.Write(ctx, c, "hi")
				if err != nil {
					log.Println(err)
					return
				}
			}
		}
	})

	err := http.ListenAndServe("localhost:8080", fn)
	log.Fatal(err)
}
Output:

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func NetConn

func NetConn(ctx context.Context, c *Conn, msgType MessageType) net.Conn

NetConn converts a *websocket.Conn into a net.Conn.

It's for tunneling arbitrary protocols over WebSockets. Few users of the library will need this but it's tricky to implement correctly and so provided in the library. See https://github.com/nhooyr/websocket/issues/100.

Every Write to the net.Conn will correspond to a message write of the given type on *websocket.Conn.

The passed ctx bounds the lifetime of the net.Conn. If cancelled, all reads and writes on the net.Conn will be cancelled.

If a message is read that is not of the correct type, the connection will be closed with StatusUnsupportedData and an error will be returned.

Close will close the *websocket.Conn with StatusNormalClosure.

When a deadline is hit and there is an active read or write goroutine, the connection will be closed. This is different from most net.Conn implementations where only the reading/writing goroutines are interrupted but the connection is kept alive.

The Addr methods will return the real addresses for connections obtained from websocket.Accept. But for connections obtained from websocket.Dial, a mock net.Addr will be returned that gives "websocket" for Network() and "websocket/unknown-addr" for String(). This is because websocket.Dial only exposes a io.ReadWriteCloser instead of the full net.Conn to us.

When running as WASM, the Addr methods will always return the mock address described above.

A received StatusNormalClosure or StatusGoingAway close frame will be translated to io.EOF when reading.

Furthermore, the ReadLimit is set to -1 to disable it.

Types

type AcceptOptions

type AcceptOptions struct {
	Subprotocols         []string
	InsecureSkipVerify   bool
	OriginPatterns       []string
	CompressionMode      CompressionMode
	CompressionThreshold int
}

AcceptOptions represents Accept's options.

type CloseError

type CloseError struct {
	Code   StatusCode
	Reason string
}

CloseError is returned when the connection is closed with a status and reason.

Use Go 1.13's errors.As to check for this error. Also see the CloseStatus helper.

func (CloseError) Error

func (ce CloseError) Error() string

type CompressionMode

type CompressionMode int

CompressionMode represents the modes available to the deflate extension. See https://tools.ietf.org/html/rfc7692 Works in all browsers except Safari which does not implement the deflate extension.

const (
	// CompressionNoContextTakeover grabs a new flate.Reader and flate.Writer as needed
	// for every message. This applies to both server and client side.
	//
	// This means less efficient compression as the sliding window from previous messages
	// will not be used but the memory overhead will be lower if the connections
	// are long lived and seldom used.
	//
	// The message will only be compressed if greater than 512 bytes.
	CompressionNoContextTakeover CompressionMode = iota

	// CompressionContextTakeover uses a flate.Reader and flate.Writer per connection.
	// This enables reusing the sliding window from previous messages.
	// As most WebSocket protocols are repetitive, this can be very efficient.
	// It carries an overhead of 8 kB for every connection compared to CompressionNoContextTakeover.
	//
	// If the peer negotiates NoContextTakeover on the client or server side, it will be
	// used instead as this is required by the RFC.
	CompressionContextTakeover

	// CompressionDisabled disables the deflate extension.
	//
	// Use this if you are using a predominantly binary protocol with very
	// little duplication in between messages or CPU and memory are more
	// important than bandwidth.
	CompressionDisabled
)

type Conn

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

Conn provides a wrapper around the browser WebSocket API.

func Accept

func Accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (*Conn, error)

Accept is stubbed out for Wasm.

Example
package main

import (
	"context"
	"log"
	"net/http"
	"time"

	"gfx.cafe/open/websocket"
	"gfx.cafe/open/websocket/wsjson"
)

func main() {
	// This handler accepts a WebSocket connection, reads a single JSON
	// message from the client and then closes the connection.

	fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		c, err := websocket.Accept(w, r, nil)
		if err != nil {
			log.Println(err)
			return
		}
		defer c.CloseNow()

		ctx, cancel := context.WithTimeout(r.Context(), time.Second*10)
		defer cancel()

		var v interface{}
		err = wsjson.Read(ctx, c, &v)
		if err != nil {
			log.Println(err)
			return
		}

		c.Close(websocket.StatusNormalClosure, "")
	})

	err := http.ListenAndServe("localhost:8080", fn)
	log.Fatal(err)
}
Output:

func Dial

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

Dial creates a new WebSocket connection to the given url with the given options. The passed context bounds the maximum time spent waiting for the connection to open. The returned *http.Response is always nil or a mock. It's only in the signature to match the core API.

Example
package main

import (
	"context"
	"log"
	"time"

	"gfx.cafe/open/websocket"
	"gfx.cafe/open/websocket/wsjson"
)

func main() {
	// Dials a server, writes a single JSON message and then
	// closes the connection.

	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()

	c, _, err := websocket.Dial(ctx, "ws://localhost:8080", nil)
	if err != nil {
		log.Fatal(err)
	}
	defer c.CloseNow()

	err = wsjson.Write(ctx, c, "hi")
	if err != nil {
		log.Fatal(err)
	}

	c.Close(websocket.StatusNormalClosure, "")
}
Output:

func (*Conn) Close

func (c *Conn) Close(code StatusCode, reason string) error

Close closes the WebSocket with the given code and reason. It will wait until the peer responds with a close frame or the connection is closed. It thus performs the full WebSocket close handshake.

func (*Conn) CloseNow added in v1.10.0

func (c *Conn) CloseNow() error

CloseNow closes the WebSocket connection without attempting a close handshake. Use when you do not want the overhead of the close handshake.

note: No different from Close(StatusGoingAway, "") in WASM as there is no way to close a WebSocket without the close handshake.

func (*Conn) CloseRead

func (c *Conn) CloseRead(ctx context.Context) context.Context

CloseRead implements *Conn.CloseRead for wasm.

func (*Conn) Ping

func (c *Conn) Ping(ctx context.Context) error

Ping is mocked out for Wasm.

Example
package main

import (
	"context"
	"log"
	"time"

	"gfx.cafe/open/websocket"
)

func main() {
	// Dials a server and pings it 5 times.

	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()

	c, _, err := websocket.Dial(ctx, "ws://localhost:8080", nil)
	if err != nil {
		log.Fatal(err)
	}
	defer c.CloseNow()

	// Required to read the Pongs from the server.
	ctx = c.CloseRead(ctx)

	for i := 0; i < 5; i++ {
		err = c.Ping(ctx)
		if err != nil {
			log.Fatal(err)
		}
	}

	c.Close(websocket.StatusNormalClosure, "")
}
Output:

func (*Conn) Read

func (c *Conn) Read(ctx context.Context) (MessageType, []byte, error)

Read attempts to read a message from the connection. The maximum time spent waiting is bounded by the context.

func (*Conn) Reader

func (c *Conn) Reader(ctx context.Context) (MessageType, io.Reader, error)

Reader attempts to read a message from the connection. The maximum time spent waiting is bounded by the context.

func (*Conn) SetReadLimit

func (c *Conn) SetReadLimit(n int64)

SetReadLimit implements *Conn.SetReadLimit for wasm.

func (*Conn) Subprotocol

func (c *Conn) Subprotocol() string

Subprotocol returns the negotiated subprotocol. An empty string means the default protocol.

func (*Conn) Write

func (c *Conn) Write(ctx context.Context, typ MessageType, p []byte) error

Write writes a message of the given type to the connection. Always non blocking.

func (*Conn) Writer

func (c *Conn) Writer(ctx context.Context, typ MessageType) (io.WriteCloser, error)

Writer returns a writer to write a WebSocket data message to the connection. It buffers the entire message in memory and then sends it when the writer is closed.

type DialOptions

type DialOptions struct {
	// Subprotocols lists the subprotocols to negotiate with the server.
	Subprotocols []string
}

DialOptions represents the options available to pass to Dial.

type MessageType

type MessageType int

MessageType represents the type of a WebSocket message. See https://tools.ietf.org/html/rfc6455#section-5.6

const (
	// MessageText is for UTF-8 encoded text messages like JSON.
	MessageText MessageType = iota + 1
	// MessageBinary is for binary messages like protobufs.
	MessageBinary
)

MessageType constants.

func (MessageType) String

func (i MessageType) String() string

type StatusCode

type StatusCode int

StatusCode represents a WebSocket status code. https://tools.ietf.org/html/rfc6455#section-7.4

const (
	StatusNormalClosure   StatusCode = 1000
	StatusGoingAway       StatusCode = 1001
	StatusProtocolError   StatusCode = 1002
	StatusUnsupportedData StatusCode = 1003

	// StatusNoStatusRcvd cannot be sent in a close message.
	// It is reserved for when a close message is received without
	// a status code.
	StatusNoStatusRcvd StatusCode = 1005

	// StatusAbnormalClosure is exported for use only with Wasm.
	// In non Wasm Go, the returned error will indicate whether the
	// connection was closed abnormally.
	StatusAbnormalClosure StatusCode = 1006

	StatusInvalidFramePayloadData StatusCode = 1007
	StatusPolicyViolation         StatusCode = 1008
	StatusMessageTooBig           StatusCode = 1009
	StatusMandatoryExtension      StatusCode = 1010
	StatusInternalError           StatusCode = 1011
	StatusServiceRestart          StatusCode = 1012
	StatusTryAgainLater           StatusCode = 1013
	StatusBadGateway              StatusCode = 1014

	// StatusTLSHandshake is only exported for use with Wasm.
	// In non Wasm Go, the returned error will indicate whether there was
	// a TLS handshake failure.
	StatusTLSHandshake StatusCode = 1015
)

https://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number

These are only the status codes defined by the protocol.

You can define custom codes in the 3000-4999 range. The 3000-3999 range is reserved for use by libraries, frameworks and applications. The 4000-4999 range is reserved for private use.

func CloseStatus

func CloseStatus(err error) StatusCode

CloseStatus is a convenience wrapper around Go 1.13's errors.As to grab the status code from a CloseError.

-1 will be returned if the passed error is nil or not a CloseError.

Example
package main

import (
	"context"
	"log"
	"time"

	"gfx.cafe/open/websocket"
)

func main() {
	// Dials a server and then expects to be disconnected with status code
	// websocket.StatusNormalClosure.

	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()

	c, _, err := websocket.Dial(ctx, "ws://localhost:8080", nil)
	if err != nil {
		log.Fatal(err)
	}
	defer c.CloseNow()

	_, _, err = c.Reader(ctx)
	if websocket.CloseStatus(err) != websocket.StatusNormalClosure {
		log.Fatalf("expected to be disconnected with StatusNormalClosure but got: %v", err)
	}
}
Output:

func (StatusCode) String

func (i StatusCode) String() string

Directories

Path Synopsis
internal
test
Package test contains subpackages only used in tests.
Package test contains subpackages only used in tests.
wsjs
Package wsjs implements typed access to the browser javascript WebSocket API.
Package wsjs implements typed access to the browser javascript WebSocket API.
thirdparty Module
Package wsjson provides helpers for reading and writing JSON messages.
Package wsjson provides helpers for reading and writing JSON messages.

Jump to

Keyboard shortcuts

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