### Run Chat Example Source: https://github.com/coder/websocket/blob/master/internal/examples/chat/README.md Navigate to the chat example directory and run the Go application. The server will listen on a local address and print the WebSocket URL to connect to. ```bash $ cd examples/chat $ go run . listening on ws://127.0.0.1:51055 ``` -------------------------------- ### Install websocket Library Source: https://github.com/coder/websocket/blob/master/README.md Use this command to install the websocket library for your Go project. ```sh go get github.com/coder/websocket ``` -------------------------------- ### Run Echo Server Source: https://github.com/coder/websocket/blob/master/internal/examples/echo/README.md Navigate to the example directory and run the echo server. The server will listen on a random available port. ```bash $ cd examples/echo $ go run . listening on ws://127.0.0.1:51055 ``` -------------------------------- ### WebSocket Server and Client Defaults Source: https://github.com/coder/websocket/blob/master/_autodocs/00-index.md Use these examples to establish a WebSocket connection with default settings for both server and client. No specific options are required. ```go // Server - no options conn, _ := websocket.Accept(w, r, nil) ``` ```go // Client - no options conn, _, _ := websocket.Dial(ctx, "ws://localhost:8080", nil) ``` -------------------------------- ### Configure WebSocket Dial Options with Compression Source: https://github.com/coder/websocket/blob/master/_autodocs/06-types.md Example of configuring DialOptions to use CompressionContextTakeover with a specific compression threshold. ```go opts := &websocket.DialOptions{ CompressionMode: websocket.CompressionContextTakeover, CompressionThreshold: 256, } conn, _, err := websocket.Dial(ctx, "ws://localhost:8080", opts) ``` -------------------------------- ### Enable WebSocket Compression Source: https://github.com/coder/websocket/blob/master/_autodocs/00-index.md Configure the client to use compression during the WebSocket handshake. This example sets the compression mode to ContextTakeover. ```go opts := &websocket.DialOptions{ CompressionMode: websocket.CompressionContextTakeover, } conn, _, _ := websocket.Dial(ctx, "ws://localhost:8080", opts) ``` -------------------------------- ### Configure WebSocket Cross-Origin Requests Source: https://github.com/coder/websocket/blob/master/_autodocs/00-index.md Set up the WebSocket server to accept connections from specific origins. This example allows connections from 'example.com' and any subdomain of 'example.com'. ```go opts := &websocket.AcceptOptions{ OriginPatterns: []string{"example.com", "*.example.com"}, } conn, _ := websocket.Accept(w, r, opts) ``` -------------------------------- ### WebSocket Client Loop with JSON Source: https://github.com/coder/websocket/blob/master/_autodocs/05-wsjson.md Example of a client establishing a WebSocket connection, sending a JSON request, and receiving a JSON response. Includes context management and error handling. ```go ctx, cancel := context.WithCancel(context.Background()) defer cancel() conn, _, err := websocket.Dial(ctx, "ws://localhost:8080/ws", nil) if err != nil { log.Fatal(err) } defer conn.CloseNow() // Send request req := map[string]string{"action": "list"} err = wsjson.Write(ctx, conn, req) if err != nil { log.Fatal(err) } // Read response var resp interface{} err = wsjson.Read(ctx, conn, &resp) if err != nil { log.Fatal(err) } fmt.Printf("Response: %v\n", resp) ``` -------------------------------- ### WebSocket Client Example Source: https://github.com/coder/websocket/blob/master/README.md This Go code snippet demonstrates how to establish a WebSocket client connection, send a message, and then close the connection. It includes context management with a timeout for the connection and message operations. ```go ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() c, _, err := websocket.Dial(ctx, "ws://localhost:8080", nil) if err != nil { // ... } deffer c.CloseNow() err = wsjson.Write(ctx, c, "hi") if err != nil { // ... } c.Close(websocket.StatusNormalClosure, "") ``` -------------------------------- ### Basic Server Handler Source: https://github.com/coder/websocket/blob/master/_autodocs/02-accept.md A basic example of how to use websocket.Accept to handle incoming WebSocket requests. It demonstrates the core functionality of accepting a connection and basic error handling. ```APIDOC ## websocket.Accept (Basic Usage) ### Description Accepts an incoming HTTP request as a WebSocket connection. This is the primary function for establishing a WebSocket connection on the server side. ### Method `websocket.Accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (Conn, error)` ### Parameters - `w` (http.ResponseWriter) - The HTTP response writer. - `r` (*http.Request) - The incoming HTTP request. - `opts` (*AcceptOptions) - Optional configuration for accepting the connection. If nil, default options are used. ### Response - `Conn` - The established WebSocket connection. - `error` - An error if the connection could not be established. ### Request Example ```go http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { conn, err := websocket.Accept(w, r, nil) if err != nil { log.Println(err) return } defer conn.CloseNow() // Handle connection... }) ``` ``` -------------------------------- ### WebSocket Server Example Source: https://github.com/coder/websocket/blob/master/README.md This Go code snippet shows how to create a WebSocket server that accepts incoming connections, reads messages, and closes the connection. Ensure proper error handling and context management for production use. ```go http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) { c, err := websocket.Accept(w, r, nil) if err != nil { // ... } defer c.CloseNow() // Set the context as needed. Use of r.Context() is not recommended // to avoid surprising behavior (see http.Hijacker). ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() var v any err = wsjson.Read(ctx, c, &v) if err != nil { // ... } log.Printf("received: %v", v) c.Close(websocket.StatusNormalClosure, "") }) ``` -------------------------------- ### With Compression Enabled Source: https://github.com/coder/websocket/blob/master/_autodocs/02-accept.md Configures the WebSocket connection to use permessage-deflate compression. This example shows how to enable compression and set a threshold for when it should be applied. ```APIDOC ## websocket.Accept (Compression Enabled) ### Description Negotiates and enables the permessage-deflate compression extension if both the client and server support it. This can reduce bandwidth usage. ### Method `websocket.Accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (Conn, error)` ### Parameters - `opts.CompressionMode` (websocket.CompressionMode) - Specifies the compression mode. `websocket.CompressionContextTakeover` is a common choice. - `opts.CompressionThreshold` (int) - The minimum message size in bytes to trigger compression. ### Request Example ```go opts := &websocket.AcceptOptions{ CompressionMode: websocket.CompressionContextTakeover, CompressionThreshold: 256, } conn, err := websocket.Accept(w, r, opts) if err != nil { log.Println(err) return } ``` ``` -------------------------------- ### Handle ErrMessageTooBig on Read Source: https://github.com/coder/websocket/blob/master/_autodocs/06-types.md Example of setting a read limit and checking for ErrMessageTooBig when reading a message. ```go conn.SetReadLimit(1024 * 1024) // 1MB typ, data, err := conn.Read(ctx) if err != nil { if errors.Is(err, websocket.ErrMessageTooBig) { log.Println("Message exceeded 1MB limit") } } ``` -------------------------------- ### Server Response to Compression Negotiation Source: https://github.com/coder/websocket/blob/master/_autodocs/07-compression.md Example of the Sec-WebSocket-Extensions header sent by a server accepting the client's requested compression mode. ```http Sec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover ``` -------------------------------- ### Client Requested Compression Header Source: https://github.com/coder/websocket/blob/master/_autodocs/07-compression.md Example of the Sec-WebSocket-Extensions header sent by a client requesting permessage-deflate with specific takeover modes. ```http Sec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover; server_no_context_takeover ``` -------------------------------- ### WebSocket Dial with Custom Options Source: https://github.com/coder/websocket/blob/master/_autodocs/01-dial.md Connects to a secure WebSocket server using custom dial options. This example specifies subprotocols, enables compression, and includes an authorization header. ```go opts := &websocket.DialOptions{ Subprotocols: []string{"chat", "binary"}, CompressionMode: websocket.CompressionContextTakeover, HTTPHeader: http.Header{"Authorization": {"Bearer token"}}, } conn, resp, err := websocket.Dial(ctx, "wss://api.example.com/ws", opts) if err != nil { log.Fatal(err) } ``` -------------------------------- ### Multi-Goroutine Websocket Architecture Source: https://github.com/coder/websocket/blob/master/_autodocs/11-concurrency.md An example of a robust multi-goroutine architecture for handling websocket connections, including reading, heartbeats, and broadcasting messages. ```go // Connection handler func handleConn(conn *websocket.Conn) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Goroutine 1: Reader go func() { for { typ, r, err := conn.Reader(ctx) if err != nil { cancel() return } var msg interface{} json.NewDecoder(r).Decode(&msg) // Dispatch to processor go handleMessage(ctx, msg) } }() // Goroutine 2: Heartbeat writer go func() { ticker := time.NewTicker(30 * time.Second) defer ticker.Stop() for { select { case <-ctx.Done(): return case <-ticker.C: conn.Ping(ctx) // Safe concurrent call } } }() // Goroutine 3: Message broadcaster go func() { for msg := range broadcastChan { select { case <-ctx.Done(): return default: if err := wsjson.Write(ctx, conn, msg); err != nil { cancel() return } } } }() <-ctx.Done() conn.CloseNow() } ``` -------------------------------- ### Manage Write-Only Connections Source: https://github.com/coder/websocket/blob/master/_autodocs/03-conn.md Starts a background goroutine to handle incoming frames, allowing the connection to be used for writing only. The returned context is cancelled when the connection closes or a data message is received, which will close the connection with StatusPolicyViolation. ```go conn, err := websocket.Dial(ctx, "ws://localhost:8080", nil) if err != nil { log.Fatal(err) } // Don't read any messages, just write ctx = conn.CloseRead(ctx) for { select { case <-ctx.Done(): return case <-time.After(1*time.Second): err := wsjson.Write(ctx, conn, "ping") if err != nil { return } } } ``` -------------------------------- ### Set Websocket Message Size Limits Source: https://github.com/coder/websocket/blob/master/_autodocs/12-best-practices.md Prevent unbounded memory usage by setting a read limit on the websocket connection. This example shows how to set a 10MB limit and handle oversized messages gracefully by closing the connection with an appropriate status. ```go // Prevent unbounded memory usage conn.SetReadLimit(10 * 1024 * 1024) // 10MB max per message // Handle oversized messages typ, data, err := conn.Read(ctx) if errors.Is(err, websocket.ErrMessageTooBig) { conn.Close(websocket.StatusMessageTooBig, "message too large") return } ``` -------------------------------- ### Client for Establishing WebSocket Connections Source: https://github.com/coder/websocket/blob/master/_autodocs/00-index.md Establishes a WebSocket connection to a server and sends/receives JSON messages. Ensure the server is running and accessible at the specified URL. ```go ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defcancel() conn, _, err := websocket.Dial(ctx, "ws://localhost:8080", nil) if err != nil { log.Fatal(err) } defconn.CloseNow() err = wsjson.Write(ctx, conn, "hello") if err != nil { log.Fatal(err) } var resp interface{} err = wsjson.Read(ctx, conn, &resp) ``` -------------------------------- ### Test Echo Server with Websocket Source: https://github.com/coder/websocket/blob/master/_autodocs/12-best-practices.md This snippet demonstrates how to test a websocket echo server by setting up an HTTP test server, connecting a client, sending a message, and verifying the response. ```go func TestEchoServer(t *testing.T) { // Start server server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { conn, _ := websocket.Accept(w, r, nil) defer conn.CloseNow() for { var msg interface{} err := wsjson.Read(r.Context(), conn, &msg) if err != nil { return } wsjson.Write(r.Context(), conn, msg) } })) defer server.Close() // Connect client wsURL := "ws" + strings.TrimPrefix(server.URL, "http") ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() conn, _, err := websocket.Dial(ctx, wsURL, nil) if err != nil { t.Fatal(err) } defer conn.CloseNow() // Test testMsg := map[string]string{"msg": "hello"} wsjson.Write(ctx, conn, testMsg) var resp map[string]string wsjson.Read(ctx, conn, &resp) if resp["msg"] != "hello" { t.Errorf("expected hello, got %s", resp["msg"]) } } ``` -------------------------------- ### With Ping/Pong Callbacks Source: https://github.com/coder/websocket/blob/master/_autodocs/02-accept.md Demonstrates how to set up callback functions for handling received Ping and Pong frames. This allows for custom logic when these control frames are encountered. ```APIDOC ## websocket.Accept (Ping/Pong Callbacks) ### Description Allows defining callback functions to handle received Ping and Pong control frames. This enables custom logic, such as logging or specific responses, when these frames are received. ### Method `websocket.Accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (Conn, error)` ### Parameters - `opts.OnPingReceived` (func(ctx context.Context, payload []byte) bool) - A callback function executed when a Ping frame is received. Returning `true` automatically sends a Pong response. - `opts.OnPongReceived` (func(ctx context.Context, payload []byte)) - A callback function executed when a Pong frame is received. ### Request Example ```go opts := &websocket.AcceptOptions{ OnPingReceived: func(ctx context.Context, payload []byte) bool { log.Printf("Ping received with payload: %s", payload) return true // Send pong automatically }, OnPongReceived: func(ctx context.Context, payload []byte) { log.Printf("Pong received with payload: %s", payload) }, } conn, err := websocket.Accept(w, r, opts) ``` ``` -------------------------------- ### Get Negotiated Subprotocol Source: https://github.com/coder/websocket/blob/master/_autodocs/03-conn.md Retrieves the WebSocket subprotocol that was successfully negotiated during the connection handshake. Returns an empty string if the default protocol was used. ```go proto := conn.Subprotocol() if proto == "chat" { // Handle chat protocol } ``` -------------------------------- ### Set WebSocket Message Size Limit Source: https://github.com/coder/websocket/blob/master/_autodocs/00-index.md Limit the maximum size of incoming messages for a WebSocket connection. This example sets the read limit to 1MB. ```go conn.SetReadLimit(1024 * 1024) // 1MB max per message ``` -------------------------------- ### With Subprotocol Negotiation Source: https://github.com/coder/websocket/blob/master/_autodocs/02-accept.md Demonstrates how to configure websocket.Accept to negotiate a specific subprotocol for the WebSocket connection. The server specifies a list of supported subprotocols, and the client's preferred subprotocol is selected if there's a match. ```APIDOC ## websocket.Accept (Subprotocol Negotiation) ### Description Configures the WebSocket connection to negotiate subprotocols. The server provides a list of supported subprotocols, and the function selects the first matching subprotocol from the client's list. ### Method `websocket.Accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (Conn, error)` ### Parameters - `opts.Subprotocols` ([]string) - A slice of strings representing the subprotocols supported by the server. ### Response - `Conn.Subprotocol()` - Returns the negotiated subprotocol. ### Request Example ```go opts := &websocket.AcceptOptions{ Subprotocols: []string{"chat", "binary"}, } conn, err := websocket.Accept(w, r, opts) if err != nil { log.Println(err) return } log.Printf("Negotiated protocol: %s", conn.Subprotocol()) ``` ``` -------------------------------- ### WebSocket Server with Ping/Pong Callbacks Source: https://github.com/coder/websocket/blob/master/_autodocs/02-accept.md Sets up callbacks for handling received Ping and Pong frames, allowing custom logic and automatic Pong responses. ```go opts := &websocket.AcceptOptions{ OnPingReceived: func(ctx context.Context, payload []byte) bool { log.Printf("Ping received with payload: %s", payload) return true // Send pong automatically }, OnPongReceived: func(ctx context.Context, payload []byte) { log.Printf("Pong received with payload: %s", payload) }, } conn, err := websocket.Accept(w, r, opts) ``` -------------------------------- ### Check WebSocket Close Status Source: https://github.com/coder/websocket/blob/master/_autodocs/06-types.md Uses the CloseStatus helper function to check the WebSocket connection's closure code. This example shows how to log different closure reasons. ```go typ, r, err := conn.Reader(ctx) code := websocket.CloseStatus(err) if code == websocket.StatusNormalClosure { log.Println("Connection closed normally") } else if code == websocket.StatusGoingAway { log.Println("Peer is going away") } else if code == -1 { // err is not a CloseError log.Println("Non-close error:", err) } ``` -------------------------------- ### Client with Compression Enabled Source: https://github.com/coder/websocket/blob/master/_autodocs/07-compression.md Configure the WebSocket client to establish connections with compression enabled. Messages larger than the specified threshold will be automatically compressed. Requires `websocket.DialOptions`. ```go opts := &websocket.DialOptions{ CompressionMode: websocket.CompressionNoContextTakeover, CompressionThreshold: 256, } conn, _, err := websocket.Dial(ctx, "wss://api.example.com/ws", opts) if err != nil { log.Fatal(err) } defer conn.CloseNow() // Messages ≥ 256 bytes will be automatically compressed largeData := make([]byte, 1024) err = conn.Write(ctx, websocket.MessageBinary, largeData) ``` -------------------------------- ### Client with Compression Disabled Source: https://github.com/coder/websocket/blob/master/_autodocs/07-compression.md Configure the WebSocket client to establish connections with compression disabled for the lowest latency. Requires `websocket.DialOptions`. ```go opts := &websocket.DialOptions{ CompressionMode: websocket.CompressionDisabled, } conn, _, err := websocket.Dial(ctx, "ws://localhost:8080", opts) ``` -------------------------------- ### Tunnel SSH over WebSocket Source: https://github.com/coder/websocket/blob/master/_autodocs/04-netconn.md Example of tunneling SSH traffic over a WebSocket connection. It establishes a WebSocket connection, wraps it as a net.Conn, and then bridges it with a local TCP connection to a remote host. ```go // Example: tunnel SSH over WebSocket wsConn, _, err := websocket.Dial(ctx, "wss://gateway.example.com/ssh", nil) if err != nil { log.Fatal(err) } defers wsConn.CloseNow() netConn := websocket.NetConn(ctx, wsConn, websocket.MessageBinary) defers netConn.Close() // Now use netConn as a standard net.Conn conn, err := net.DialContext(ctx, "tcp", "remote-host:22") if err != nil { log.Fatal(err) } defers conn.Close() // Bridge the connections go io.Copy(netConn, conn) io.Copy(conn, netConn) ``` -------------------------------- ### Server with Compression Enabled Source: https://github.com/coder/websocket/blob/master/_autodocs/07-compression.md Configure the WebSocket server to accept connections with compression enabled. Messages larger than the specified threshold will be automatically compressed. Requires `websocket.AcceptOptions`. ```go http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { opts := &websocket.AcceptOptions{ CompressionMode: websocket.CompressionContextTakeover, CompressionThreshold: 512, } conn, err := websocket.Accept(w, r, opts) if err != nil { log.Println(err) return } defer conn.CloseNow() // Messages ≥ 512 bytes will be automatically compressed for { typ, r, err := conn.Reader(context.Background()) if err != nil { return } // Process message io.Copy(io.Discard, r) } }) ``` -------------------------------- ### Server-Side Protocol Tunneling Source: https://github.com/coder/websocket/blob/master/_autodocs/04-netconn.md Example of a server accepting WebSocket connections and tunneling them to a backend service. This is useful for exposing internal services securely. Ensure the server handles WebSocket upgrade requests correctly. ```go // Server side - accept WebSocket, tunnel to real server http.HandleFunc("/tunnel", func(w http.ResponseWriter, r *http.Request) { wsConn, err := websocket.Accept(w, r, nil) if err != nil { return } defers wsConn.CloseNow() ctx, cancel := context.WithCancel(r.Context()) defers cancel() netConn := websocket.NetConn(ctx, wsConn, websocket.MessageBinary) defers netConn.Close() // Connect to actual service realConn, err := net.DialTimeout("tcp", "backend:5000", 5*time.Second) if err != nil { log.Println(err) return } defers realConn.Close() // Bidirectional tunnel go io.Copy(netConn, realConn) io.Copy(realConn, netConn) }) ``` -------------------------------- ### Zero Dependencies Source: https://github.com/coder/websocket/blob/master/_autodocs/00-index.md The main websocket package has no external dependencies, relying only on built-in packages and internal utilities. ```go github.com/coder/websocket ├── builtin packages only └── internal utilities (not exported) ``` -------------------------------- ### WebSocket Server with Subprotocol Negotiation Source: https://github.com/coder/websocket/blob/master/_autodocs/02-accept.md Configures the server to accept specific subprotocols for the WebSocket connection. Logs the negotiated subprotocol. ```go opts := &websocket.AcceptOptions{ Subprotocols: []string{"chat", "binary"}, } conn, err := websocket.Accept(w, r, opts) if err != nil { log.Println(err) return } log.Printf("Negotiated protocol: %s", conn.Subprotocol()) ``` -------------------------------- ### Setting Read Deadline on net.Conn Source: https://github.com/coder/websocket/blob/master/_autodocs/04-netconn.md Demonstrates how to set a read deadline on a WebSocket-wrapped net.Conn. This allows for timeouts on read operations, preventing indefinite blocking. Handle context.DeadlineExceeded errors appropriately. ```go netConn := websocket.NetConn(ctx, wsConn, websocket.MessageBinary) // Set read deadline netConn.SetReadDeadline(time.Now().Add(10 * time.Second)) buf := make([]byte, 1024) n, err := netConn.Read(buf) if err == context.DeadlineExceeded { log.Println("Read timeout") } ``` -------------------------------- ### Write-Only Websocket Connection with CloseRead Source: https://github.com/coder/websocket/blob/master/_autodocs/11-concurrency.md Use CloseRead for connections that only need to write. It starts a background reader to handle control frames like pings, pongs, and closes automatically. This allows safe concurrent writes from multiple goroutines. ```go conn, _, _ := websocket.Dial(ctx, "ws://localhost:8080", nil) defer conn.CloseNow() // Start background reader to handle control frames ctx = conn.CloseRead(ctx) // Safe to write from any goroutine go func() { conn.Write(ctx, websocket.MessageText, "message 1") }() go func() { conn.Write(ctx, websocket.MessageText, "message 2") }() // CloseRead ensures ping/pong/close are handled automatically ``` -------------------------------- ### DialOptions Structure Source: https://github.com/coder/websocket/blob/master/_autodocs/01-dial.md Defines configuration options for establishing a WebSocket connection, including HTTP client, headers, subprotocols, and compression settings. ```go type DialOptions struct { HTTPClient *http.Client HTTPHeader http.Header Host string Subprotocols []string CompressionMode CompressionMode CompressionThreshold int OnPingReceived func(ctx context.Context, payload []byte) bool OnPongReceived func(ctx context.Context, payload []byte) } ``` -------------------------------- ### Basic WebSocket Server Handler Source: https://github.com/coder/websocket/blob/master/_autodocs/02-accept.md Handles incoming HTTP requests and upgrades them to WebSocket connections using default options. Ensure to close the connection when done. ```go http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { conn, err := websocket.Accept(w, r, nil) if err != nil { log.Println(err) return } defer conn.CloseNow() // Handle connection... }) ``` -------------------------------- ### Separate Timeouts for Read, Process, and Write Source: https://github.com/coder/websocket/blob/master/_autodocs/10-streaming.md Demonstrates setting individual timeouts for reading, processing, and writing operations within a WebSocket connection. Each phase uses a separate context with a specific duration. ```go ctx := context.Background() // Read timeout readCtx, cancel := context.WithTimeout(ctx, 10*time.Second) typ, r, _ := conn.Reader(readCtx) data, _ := io.ReadAll(r) cancel() // Process timeout processCtx, cancel := context.WithTimeout(ctx, 5*time.Second) process(data, processCtx) cancel() // Write timeout writeCtx, cancel := context.WithTimeout(ctx, 10*time.Second) w, _ := conn.Writer(writeCtx, websocket.MessageBinary) io.Copy(w, result) w.Close() cancel() ``` -------------------------------- ### Server-Side WebSocket Acceptance Source: https://github.com/coder/websocket/blob/master/_autodocs/README.md This snippet demonstrates how to set up a WebSocket server endpoint using the `websocket.Accept` function. It handles upgrading an HTTP connection to a WebSocket connection, reading a JSON message, and writing a response. ```APIDOC ## websocket.Accept ### Description Accept upgrades an http.ResponseWriter and *http.Request to a WebSocket connection. This is the primary function for implementing a WebSocket server. ### Method POST ### Endpoint /ws ### Parameters #### Request Body - **w** (http.ResponseWriter) - Required - The HTTP response writer. - **r** (*http.Request) - Required - The incoming HTTP request. - **config** (*websocket.Config) - Optional - Configuration options for the WebSocket connection. ### Request Example ```go http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { conn, err := websocket.Accept(w, r, nil) if err != nil { return } defer conn.CloseNow() var msg interface{} err = wsjson.Read(r.Context(), conn, &msg) if err != nil { return } wsjson.Write(r.Context(), conn, msg) }) ``` ### Response #### Success Response (200) - **conn** (*websocket.Conn) - The established WebSocket connection. #### Response Example (No specific response body example provided for the `Accept` function itself, as it returns a `Conn` object and handles the HTTP upgrade.) ### Error Handling - Errors during the upgrade process will be returned. ``` -------------------------------- ### Accept Source: https://github.com/coder/websocket/blob/master/_autodocs/00-index.md Accepts an incoming client connection to a WebSocket server. ```APIDOC ## Accept ### Description Accept a client connection. ### Method `Accept(listener net.Listener, opts *AcceptOptions)` ### Parameters - **listener** (net.Listener) - The network listener to accept connections from. - **opts** (*AcceptOptions) - Optional configuration for accepting the connection. ``` -------------------------------- ### Send Multipart WebSocket Messages Source: https://github.com/coder/websocket/blob/master/_autodocs/10-streaming.md Demonstrates how a client can send a message composed of multiple parts (frames) and how the server receives it as a single, combined message. The `Writer` abstracts the fragmentation. ```go // Client sends message in multiple frames w, err := conn.Writer(ctx, websocket.MessageText) if err != nil { return } deferr w.Close() w.Write([]byte("Part 1")) w.Write([]byte("Part 2")) w.Write([]byte("Part 3")) // When writer closes, all parts are combined into a single message // Server receives as single message typ, r, err := conn.Reader(ctx) if err != nil { return } data, _ := io.ReadAll(r) // data contains "Part 1Part 2Part 3" ``` -------------------------------- ### AcceptOptions Struct Definition Source: https://github.com/coder/websocket/blob/master/_autodocs/02-accept.md Defines the configuration options for establishing a WebSocket connection using the Accept function. This includes subprotocols, origin verification, compression settings, and callbacks for ping/pong messages. ```go type AcceptOptions struct { Subprotocols []string InsecureSkipVerify bool OriginPatterns []string CompressionMode CompressionMode CompressionThreshold int OnPingReceived func(ctx context.Context, payload []byte) bool OnPongReceived func(ctx context.Context, payload []byte) } ``` -------------------------------- ### Client-Side WebSocket Connection and Communication Source: https://github.com/coder/websocket/blob/master/_autodocs/12-best-practices.md Establishes a WebSocket connection to a server, sends a message, receives a response, and handles graceful closure. Includes a connection timeout. ```go func connectAndCommunicate(ctx context.Context, url string) error { // 1. Dial with timeout dialCtx, cancel := context.WithTimeout(ctx, 10*time.Second) conn, resp, err := websocket.Dial(dialCtx, url, nil) cancel() if err != nil { return fmt.Errorf("dial failed: %w", err) } defer conn.CloseNow() log.Printf("Connected, subprotocol: %s, status: %d", conn.Subprotocol(), resp.StatusCode) // 2. Create operation context opCtx, cancel := context.WithCancel(ctx) defer cancel() // 3. Communicate if err := wsjson.Write(opCtx, conn, "hello"); err != nil { return fmt.Errorf("write failed: %w", err) } var resp interface{} if err := wsjson.Read(opCtx, conn, &resp); err != nil { return fmt.Errorf("read failed: %w", err) } log.Printf("Response: %v", resp) // 4. Graceful close return conn.Close(websocket.StatusNormalClosure, "") } ``` -------------------------------- ### WebSocket Server with Cross-Origin Support Source: https://github.com/coder/websocket/blob/master/_autodocs/02-accept.md Enables cross-origin requests by specifying allowed origin patterns. Supports wildcard matching for hostnames. ```go opts := &websocket.AcceptOptions{ OriginPatterns: []string{ "example.com", "*.example.com", "localhost", }, } conn, err := websocket.Accept(w, r, opts) if err != nil { log.Println(err) return } ``` -------------------------------- ### Efficient WebSocket Streaming Source: https://github.com/coder/websocket/blob/master/_autodocs/10-streaming.md Illustrates efficient streaming of WebSocket messages using `io.Copy` which leverages a default 32KB buffer, avoiding full message buffering. Contrasts with the less efficient `conn.Read` convenience wrapper that buffers the entire message. ```go // Efficient: streams without full buffering typ, r, err := conn.Reader(ctx) io.Copy(dst, r) // 32KB default buffer // Less efficient: buffers entire message typ, data, _ := conn.Read(ctx) // Uses Read convenience wrapper ``` -------------------------------- ### Safe Pattern for Concurrent Writing Source: https://github.com/coder/websocket/blob/master/_autodocs/11-concurrency.md Demonstrates a safe pattern for concurrent writing to a websocket connection using multiple goroutines. Each goroutine obtains a writer, writes data, and closes the writer. ```go go func() { // Goroutine 1: writing w, _ := conn.Writer(ctx, websocket.MessageText) io.WriteString(w, "data from goroutine 1") w.Close() }() go func() { // Goroutine 2: writing w, _ := conn.Writer(ctx, websocket.MessageText) io.WriteString(w, "data from goroutine 2") w.Close() }() ``` -------------------------------- ### Server Handler for WebSocket Connections Source: https://github.com/coder/websocket/blob/master/_autodocs/00-index.md Upgrades an HTTP connection to a WebSocket connection and handles message echoing. Use this to set up a WebSocket server endpoint. ```go http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { conn, err := websocket.Accept(w, r, nil) if err != nil { return } defer conn.CloseNow() for { var msg interface{} err := wsjson.Read(r.Context(), conn, &msg) if err != nil { return } wsjson.Write(r.Context(), conn, msg) } }) ``` -------------------------------- ### Enable WebSocket Compression with Context Takeover Source: https://github.com/coder/websocket/blob/master/_autodocs/07-compression.md Use `CompressionContextTakeover` to reuse the deflate compression context across messages. This offers a higher compression ratio, especially for repetitive data, but has a higher memory overhead. Messages smaller than 128 bytes are not compressed by default. ```go CompressionMode: websocket.CompressionContextTakeover ``` -------------------------------- ### Write Message Payload with io.WriteCloser Source: https://github.com/coder/websocket/blob/master/_autodocs/10-streaming.md Demonstrates the basic pattern for obtaining an io.WriteCloser to write a message payload. Remember to close the writer to finalize the message. ```go w, err := conn.Writer(ctx, websocket.MessageBinary) if err != nil { return } deferr w.Close() // Write message payload _, err = io.WriteString(w, "Hello") if err != nil { return } ``` -------------------------------- ### With Cross-Origin Support Source: https://github.com/coder/websocket/blob/master/_autodocs/02-accept.md Shows how to enable cross-origin WebSocket connections by specifying allowed origin patterns. This is crucial for allowing connections from different domains or subdomains. ```APIDOC ## websocket.Accept (Cross-Origin Support) ### Description Enables cross-origin resource sharing (CORS) for WebSocket connections by specifying a list of allowed origin patterns. Requests from origins matching these patterns will be accepted. ### Method `websocket.Accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (Conn, error)` ### Parameters - `opts.OriginPatterns` ([]string) - A slice of strings representing the allowed origin patterns. Pattern matching is case-insensitive. ### Request Example ```go opts := &websocket.AcceptOptions{ OriginPatterns: []string{ "example.com", "*.example.com", "localhost", }, } conn, err := websocket.Accept(w, r, opts) if err != nil { log.Println(err) return } ``` ``` -------------------------------- ### Client-Side WebSocket Dial Source: https://github.com/coder/websocket/blob/master/_autodocs/README.md This snippet shows how to establish a WebSocket connection to a server from a client using the `websocket.Dial` function. It includes context management for timeouts and demonstrates sending and receiving JSON messages. ```APIDOC ## websocket.Dial ### Description Dial establishes a WebSocket connection to the given URL. This is the primary function for implementing a WebSocket client. ### Method GET ### Endpoint ws://[host]:[port]/[path] ### Parameters #### Path Parameters - **ctx** (context.Context) - Required - Context for managing request lifecycle and timeouts. - **url** (string) - Required - The WebSocket server URL (e.g., "ws://localhost:8080"). - **config** (*websocket.Config) - Optional - Configuration options for the WebSocket connection. ### Request Example ```go ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() conn, _, err := websocket.Dial(ctx, "ws://localhost:8080", nil) if err != nil { log.Fatal(err) } defer conn.CloseNow() wsjson.Write(ctx, conn, "hello") var resp interface{} wsjson.Read(ctx, conn, &resp) ``` ### Response #### Success Response (200) - **conn** (*websocket.Conn) - The established WebSocket connection. - **resp** (*http.Response) - The underlying HTTP response from the upgrade request. #### Response Example (The primary return is the `Conn` object. The `http.Response` provides details about the initial HTTP handshake.) ### Error Handling - Errors during the connection attempt or handshake will be returned. ``` -------------------------------- ### Dial Source: https://github.com/coder/websocket/blob/master/_autodocs/00-index.md Initiates a connection to a WebSocket server. ```APIDOC ## Dial ### Description Connect to a WebSocket server. ### Method `Dial(url string, opts *DialOptions)` ### Parameters - **url** (string) - The server URL to connect to. - **opts** (*DialOptions) - Optional configuration for the connection. ``` -------------------------------- ### Enable WebSocket Compression without Context Takeover Source: https://github.com/coder/websocket/blob/master/_autodocs/07-compression.md Use `CompressionNoContextTakeover` for a new deflate window per message. This provides a lower compression ratio and reduced memory overhead for idle connections, making it suitable for occasional high-volume messages. Messages smaller than 512 bytes are not compressed by default. ```go CompressionMode: websocket.CompressionNoContextTakeover ``` -------------------------------- ### WebSocket Server with Compression Enabled Source: https://github.com/coder/websocket/blob/master/_autodocs/02-accept.md Configures permessage-deflate compression for the WebSocket connection, specifying the compression mode and threshold. ```go opts := &websocket.AcceptOptions{ CompressionMode: websocket.CompressionContextTakeover, CompressionThreshold: 256, } conn, err := websocket.Accept(w, r, opts) if err != nil { log.Println(err) return } ``` -------------------------------- ### Usage of StatusCode for Closing Connection Source: https://github.com/coder/websocket/blob/master/_autodocs/06-types.md Demonstrates how to close a WebSocket connection with a specific status code and message. It also shows how to check for and handle `websocket.CloseError` using `errors.As` and a switch statement on the error code. ```go err := conn.Close(websocket.StatusNormalClosure, "goodbye") var ce websocket.CloseError if errors.As(err, &ce) { switch ce.Code { case websocket.StatusNormalClosure: log.Println("Normal close") case websocket.StatusProtocolError: log.Println("Protocol error") } } ``` -------------------------------- ### Read Message Payload with io.Reader Source: https://github.com/coder/websocket/blob/master/_autodocs/10-streaming.md Demonstrates the basic pattern for obtaining an io.Reader to consume a message payload. Ensure to read the entire message to EOF to avoid connection hangs. ```go typ, r, err := conn.Reader(ctx) if err != nil { return } // Read message payload data, err := io.ReadAll(r) if err != nil { return } ``` -------------------------------- ### Configure OnPingReceived Callback Source: https://github.com/coder/websocket/blob/master/_autodocs/09-control-frames.md Sets a callback function that is invoked synchronously when a ping frame is received. Returning true automatically sends a pong response. Avoid blocking operations within this callback. ```go func(ctx context.Context, payload []byte) bool ``` ```go opts := &websocket.AcceptOptions{ OnPingReceived: func(ctx context.Context, payload []byte) bool { log.Printf("Ping received: %s", payload) // Automatic pong will be sent return true }, } conn, err := websocket.Accept(w, r, opts) ``` -------------------------------- ### Safe Concurrent Reading and Writing Source: https://github.com/coder/websocket/blob/master/_autodocs/11-concurrency.md This pattern shows how to safely handle concurrent reading and writing. A dedicated goroutine reads messages and sends them to a channel, while another goroutine consumes from a channel to write messages. ```Go // Reading go func() { for { typ, r, _ := conn.Reader(ctx) var msg interface{} json.NewDecoder(r).Decode(&msg) msgChan <- msg // Send to processor } }() // Writing (safe concurrent) go func() { for msg := range outChan { conn.Write(ctx, websocket.MessageText, msg) } }() ``` -------------------------------- ### Set Custom Compression Threshold Source: https://github.com/coder/websocket/blob/master/_autodocs/07-compression.md Configure the minimum message size in bytes for applying compression. Set to -1 to disable compression while still allowing negotiation. ```go opts := &websocket.DialOptions{ CompressionMode: websocket.CompressionContextTakeover, CompressionThreshold: 256, // Compress messages ≥ 256 bytes } conn, _, err := websocket.Dial(ctx, "ws://localhost:8080", opts) ``` -------------------------------- ### Reading Negotiated Subprotocol Source: https://github.com/coder/websocket/blob/master/_autodocs/01-dial.md Connects to a WebSocket server and logs the negotiated subprotocol. This is useful for verifying that the client and server agreed on a specific protocol. ```go conn, resp, err := websocket.Dial(ctx, "ws://localhost:8080", nil) if err != nil { log.Fatal(err) } deffer conn.CloseNow() log.Printf("Negotiated subprotocol: %s", conn.Subprotocol()) ``` -------------------------------- ### Goroutine Cleanup on Connection Close Source: https://github.com/coder/websocket/blob/master/_autodocs/11-concurrency.md This pattern demonstrates how to use context cancellation with defer to ensure all associated goroutines are cleaned up when the connection is closed or an external signal is received. ```Go func handleConn(conn *websocket.Conn) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Reader goroutine go func() { for { typ, r, err := conn.Reader(ctx) if err != nil { // Automatically stops on conn.Close() return } // Process... } }() // Other goroutines go heartbeat(ctx, conn) go broadcaster(ctx, conn, broadcastChan) // Main goroutine can close <-someSignalChan // This cancels ctx and closes connection cancel() conn.CloseNow() // All goroutines will exit from context cancellation } ``` -------------------------------- ### Client-Side WebSocket Dialing Source: https://github.com/coder/websocket/blob/master/_autodocs/README.md Establishes a WebSocket connection to a remote server. Includes context for timeouts and defer closing the connection. ```go ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() conn, _, err := websocket.Dial(ctx, "ws://localhost:8080", nil) if err != nil { log.Fatal(err) } deferr conn.CloseNow() wsjson.Write(ctx, conn, "hello") var resp interface{} wsjson.Read(ctx, conn, &resp) ``` -------------------------------- ### Send File Stream (Client) Source: https://github.com/coder/websocket/blob/master/_autodocs/10-streaming.md Client-side code to connect to a WebSocket server and stream a local file as binary data. This avoids loading the entire file into memory. ```go // Client: send file ctx := context.Background() conn, _, _ := websocket.Dial(ctx, "ws://localhost:8080/upload", nil) defer conn.CloseNow() w, _ := conn.Writer(ctx, websocket.MessageBinary) deferr w.Close() f, _ := os.Open("largefile.bin") deferr f.Close() io.Copy(w, f) // Stream without buffering ``` -------------------------------- ### Write Message with Compression Enabled Source: https://github.com/coder/websocket/blob/master/_autodocs/10-streaming.md Illustrates writing data to a websocket connection where compression is enabled. The writer automatically compresses data that meets the configured threshold. ```go opts := &websocket.DialOptions{ CompressionMode: websocket.CompressionContextTakeover, } conn, _, _ := websocket.Dial(ctx, "ws://localhost:8080", opts) w, err := conn.Writer(ctx, websocket.MessageBinary) if err != nil { return } deferr w.Close() // Data is automatically compressed if it meets the threshold io.Copy(w, largeFile) ``` -------------------------------- ### Basic Text Message Bridging with net.Conn Source: https://github.com/coder/websocket/blob/master/_autodocs/04-netconn.md Demonstrates wrapping a WebSocket connection for text messages and using it with standard Go APIs like bufio.Scanner. Ensure the WebSocket server is configured to handle text messages. ```go wsConn, _, err := websocket.Dial(ctx, "ws://localhost:8080", nil) if err != nil { log.Fatal(err) } defers wsConn.CloseNow() // Wrap as net.Conn, messages are text netConn := websocket.NetConn(ctx, wsConn, websocket.MessageText) defers netConn.Close() // Use with standard Go APIs scanner := bufio.NewScanner(netConn) for scanner.Scan() { fmt.Println(scanner.Text()) } ``` -------------------------------- ### Basic WebSocket Dial Source: https://github.com/coder/websocket/blob/master/_autodocs/01-dial.md Establishes a basic WebSocket connection to a local server. Ensure a WebSocket server is running at the specified address. The context is used to manage the dial operation's timeout and lifetime. ```go ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() conn, resp, err := websocket.Dial(ctx, "ws://localhost:8080/ws", nil) if err != nil { log.Fatal(err) } defer conn.CloseNow() log.Printf("Status: %d, Subprotocol: %s", resp.StatusCode, conn.Subprotocol()) ``` -------------------------------- ### WebSocket JSON Request-Response Source: https://github.com/coder/websocket/blob/master/_autodocs/05-wsjson.md Demonstrates reading a JSON request and writing a JSON response over a WebSocket connection. Ensure the context is managed correctly for cancellation. ```go var req struct { ID int `json:"id"` Query string `json:"query"` } err := wsjson.Read(ctx, conn, &req) if err != nil { return } var resp struct { ID int `json:"id"` Result string `json:"result"` }{ ID: req.ID, Result: "processed", } err = wsjson.Write(ctx, conn, &resp) ``` -------------------------------- ### Implement Heartbeat Source: https://github.com/coder/websocket/blob/master/_autodocs/09-control-frames.md Use a ticker to periodically send PING frames to keep the connection alive and detect disconnections. Ensure the context is checked for cancellation. ```go func heartbeat(ctx context.Context, conn *websocket.Conn, interval time.Duration) { ticker := time.NewTicker(interval) defer ticker.Stop() for { select { case <-ctx.Done(): return case <-ticker.C: pingCtx, cancel := context.WithTimeout(ctx, 5*time.Second) err := conn.Ping(pingCtx) cancel() if err != nil { log.Println("Ping failed:", err) return } } } } // Usage go heartbeat(ctx, conn, 30*time.Second) ``` -------------------------------- ### Track Latency with PongReceived Source: https://github.com/coder/websocket/blob/master/_autodocs/09-control-frames.md Implement a latency tracker by recording the send time of PING frames and calculating the round-trip time (RTT) in the OnPongReceived callback. This requires a map to store pending PINGs and a counter for unique identifiers. ```go type Tracker struct { mu sync.Mutex pendingPings map[int64]time.Time counter int64 } opts := &websocket.DialOptions{ OnPongReceived: func(ctx context.Context, payload []byte) { tracker.mu.Lock() if t, ok := tracker.pendingPings[int64(binary.LittleEndian.Uint64(payload))]; ok { delete(tracker.pendingPings, int64(binary.LittleEndian.Uint64(payload))) latency := time.Since(t) fmt.Printf("RTT: %v\n", latency) } tracker.mu.Unlock() }, } conn, _, err := websocket.Dial(ctx, "ws://localhost:8080", opts) ``` -------------------------------- ### Server-Side WebSocket Handler Source: https://github.com/coder/websocket/blob/master/_autodocs/12-best-practices.md Handles incoming WebSocket connections, including accepting the connection, managing context, processing messages, and graceful closure. ```go http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { // 1. Accept connection conn, err := websocket.Accept(w, r, &websocket.AcceptOptions{ Subprotocols: []string{"v1"}, CompressionMode: websocket.CompressionContextTakeover, }) if err != nil { log.Println("Accept error:", err) return } defer conn.CloseNow() // 2. Create context from request ctx, cancel := context.WithCancel(r.Context()) defer cancel() // 3. Handle connection handleConnection(ctx, conn) // 4. Graceful close (automatic via defer) }) func handleConnection(ctx context.Context, conn *websocket.Conn) { for { var msg interface{} err := wsjson.Read(ctx, conn, &msg) // 5. Handle close gracefully var ce websocket.CloseError if errors.As(err, &ce) { log.Printf("Peer closed: %d", ce.Code) return } if err != nil { log.Println("Read error:", err) return } // 6. Process and respond resp := process(msg) if err := wsjson.Write(ctx, conn, resp); err != nil { log.Println("Write error:", err) return } } } ```