WebSocket implementation built on top of net/http
  • Go 99.1%
  • Shell 0.9%
Find a file
2026-04-16 18:32:05 -05:00
autobahn autobahn test infrastructure: echo server, fuzzingclient config, runner script 2026-04-10 16:26:52 -05:00
.gitignore add .gitignore, remove committed binary 2026-04-10 16:27:05 -05:00
benchmark_test.go Add lifecycle and message benchmarks 2026-04-16 17:47:43 -05:00
close.go polish: RFC compliance fixes for Autobahn test suite 2026-04-10 16:37:45 -05:00
close_test.go polish: RFC compliance fixes for Autobahn test suite 2026-04-10 16:37:45 -05:00
compress.go Optimize deflate reader allocation: zero-copy payload composition and pooled reader detachment 2026-04-16 18:32:05 -05:00
compress_test.go Improve WebSocket correctness and docs 2026-04-16 17:12:55 -05:00
dial.go Improve WebSocket correctness and docs 2026-04-16 17:12:55 -05:00
dial_test.go Improve WebSocket correctness and docs 2026-04-16 17:12:55 -05:00
frame.go Improve WebSocket correctness and docs 2026-04-16 17:12:55 -05:00
frame_test.go Improve WebSocket correctness and docs 2026-04-16 17:12:55 -05:00
go.mod frame types and constants: opcodes, bit masks, frameHeader struct 2026-04-10 16:04:14 -05:00
handshake.go handshake: Sec-WebSocket-Accept computation, header parsing, subprotocol selection 2026-04-10 16:10:46 -05:00
handshake_test.go handshake: Sec-WebSocket-Accept computation, header parsing, subprotocol selection 2026-04-10 16:10:46 -05:00
mask.go masking: word-at-a-time XOR mask/unmask with position continuation 2026-04-10 16:07:43 -05:00
mask_test.go masking: word-at-a-time XOR mask/unmask with position continuation 2026-04-10 16:07:43 -05:00
README.md Improve WebSocket correctness and docs 2026-04-16 17:12:55 -05:00
upgrade.go Improve WebSocket correctness and docs 2026-04-16 17:12:55 -05:00
upgrade_test.go Improve WebSocket correctness and docs 2026-04-16 17:12:55 -05:00
utf8.go utf-8 streaming validator: DFA-based incremental validation for fragmented messages 2026-04-10 16:09:05 -05:00
utf8_test.go utf-8 streaming validator: DFA-based incremental validation for fragmented messages 2026-04-10 16:09:05 -05:00
ws.go Optimize read-path allocations: reuse single-frame payload, init fragmented from first, split control-frame allocation (close heap, ping/pong stack) 2026-04-16 17:47:59 -05:00
ws_test.go Improve WebSocket correctness and docs 2026-04-16 17:12:55 -05:00

ws

A minimal WebSocket library for Go. Zero dependencies outside the standard library.

Handles both server and client connections, supports permessage-deflate compression, and passes the current Autobahn WebSocket test suite configuration for crossbario/autobahn-testsuite:25.10.1 (673 passing cases across the deflate and no-deflate testees).

go get git.sharkk.net/go/ws

Server

Use Upgrade inside any net/http handler to hijack the connection:

http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
    conn, err := ws.Upgrade(w, r, &ws.UpgradeOptions{
        EnableDeflate: true,
    })
    if err != nil {
        return
    }
    defer conn.Close()

    for {
        msgType, data, err := conn.ReadMessage()
        if err != nil {
            break
        }
        conn.WriteMessage(msgType, data)
    }
})

UpgradeOptions lets you set allowed subprotocols, a custom origin checker, and whether to negotiate compression. If CheckOrigin is nil it defaults to matching the Origin header against Host.

Client

conn, resp, err := ws.Dial("ws://localhost:8080/ws", &ws.DialOptions{
    EnableDeflate: true,
})
if err != nil {
    log.Fatal(err)
}
defer conn.Close()

conn.WriteMessage(ws.MessageText, []byte("hello"))
msgType, data, err := conn.ReadMessage()

Dial supports ws:// and wss:// schemes, custom TLS configs, extra headers, subprotocol negotiation, and a configurable handshake timeout (default 10s).

API

Conn methods:

Method What it does
ReadMessage() (MessageType, []byte, error) Read the next complete message. Handles fragmentation, control frames, decompression, and UTF-8 validation internally. Returns a *CloseError when the peer closes.
WriteMessage(msgType, data) Write a complete message. Compresses automatically when deflate is negotiated.
WriteClose(code, reason) Send a close frame.
WritePing(data) / WritePong(data) Send ping/pong frames.
SetReadLimit(n) Set max incoming message size (default 32 MB). Sends 1009 and errors if exceeded.
NetConn() Get the underlying net.Conn for deadlines, etc.
Close() Close the TCP connection directly.

Message types: ws.MessageText, ws.MessageBinary.

Close codes: ws.CloseNormalClosure, ws.CloseGoingAway, ws.CloseProtocolError, etc. Check the close reason with a type assertion on *ws.CloseError.

Compression

When EnableDeflate is set, the library negotiates permessage-deflate with server_no_context_takeover and client_no_context_takeover. Flate writers and readers are pooled with sync.Pool to keep allocations down. Compression is transparent — ReadMessage and WriteMessage handle it automatically.

Testing

Unit tests:

go test -c -o ws_test.exe . && ./ws_test.exe -test.v

Autobahn (requires Docker via WSL on Windows):

# Cross-compile the echo server
GOOS=linux GOARCH=amd64 go build -o autobahn/echoserver_linux ./autobahn/

# Run the suite
wsl -d archlinux -- bash autobahn/run_wsl.sh

# Check results
go run autobahn/check.go autobahn/reports/index.json