Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

websocket_en.md

maoxiaoyue edited this page May 14, 2026 · 2 revisions

WebSocket Package (pkg/websocket)

The websocket package provides a high-performance optimized WebSocket implementation for HypGo, supporting four protocols (JSON / Protobuf / FlatBuffers / MessagePack), built-in AES-256-GCM encryption and HMAC-SHA256 signing, permessage-deflate compression, WSS/TLS, broadcast and room management, with seamless integration with hypcontext.Context.

Key Features

  • Four-Protocol Serialization: Clients select serialization format during WebSocket handshake via Sec-WebSocket-Protocol sub-protocol (json, protobuf, flatbuffers, msgpack), with automatic server negotiation. Defaults to JSON with full backward compatibility.
  • AES-256-GCM Encryption: Message payloads encrypted via AES-256-GCM, using random 12-byte nonce per encryption, with Hub-level and per-client key override support.
  • HMAC-SHA256 Signing: Message integrity verified via HMAC-SHA256, detecting in-transit tampering attacks.
  • Composable Security Pipeline: Supports Sign-then-Encrypt (default) or Encrypt-then-Sign ordering, with AES and HMAC independently or simultaneously enabled.
  • permessage-deflate Compression: Configurable compression level (1-9), reducing bandwidth usage.
  • WSS/TLS Support: Standalone mode provides ListenAndServeTLS for quick secure WebSocket server startup.
  • Zero-Configuration Upgrade: Built-in Gorilla WebSocket Upgrader wrapper with standard configuration for cross-origin checks and security settings.
  • Object Pooling: Client, Room, Message, buffers, and broadcast client slices are all managed via sync.Pool. Client metadata maps are pre-allocated in the pool, and reset() uses map rebuild instead of individual delete, minimizing GC triggers under high-concurrency connections.
  • Built-in Channel and Room System: Provides centralized management via Hub. Developers can easily manage individual connection subscriptions, unsubscriptions, and broadcasts for Channels and Rooms.
  • Cross-Protocol Broadcasting: Clients in the same channel or room can use different serialization formats. During broadcast, each codec serializes at most once (lazy N-serialization), not scaling linearly with client count.
  • Health Detection: Built-in Ping/Pong heartbeat mechanism and automatic dead connection cleanup loop to maintain connection pool health.

Basic Usage

Initialize a Hub and define message and connection handling logic:

package main
import (
	"context"
	"log"
	hypcontext "github.com/maoxiaoyue/hypgo/pkg/context"
	"github.com/maoxiaoyue/hypgo/pkg/logger"
	"github.com/maoxiaoyue/hypgo/pkg/router"
	"github.com/maoxiaoyue/hypgo/pkg/websocket"
)
func main() {
	r := router.New()
	l := logger.NewLogger()
	// 1. Create Hub to manage all connections
	hub := websocket.NewHub(l, websocket.DefaultConfig)
	// 2. Define event callbacks
	hub.SetCallbacks(
		func(client *websocket.Client) {
			log.Printf("New connection joined! ID: %s, Codec: %s", client.ID, client.Codec().Name())
		},
		func(client *websocket.Client) {
			log.Printf("Connection disconnected! ID: %s", client.ID)
		},
		func(client *websocket.Client, msg *websocket.Message) {
			log.Printf("Message received! Type: %s, Data: %s", msg.Type, string(msg.Data))
		},
	)
	// 3. Start Hub's message dispatch and dead connection cleanup in background
	ctx := context.Background()
	go hub.Run(ctx)
	// 4. Define route and WebSocket connection upgrade endpoint
	r.GET("/ws", func(c *hypcontext.Context) {
		hub.ServeHTTP(c)
	})
	// Start server...
}

Four-Protocol Support (JSON / Protobuf / FlatBuffers / MessagePack)

Clients select serialization format when establishing WebSocket connections via the standard Sec-WebSocket-Protocol header:

// JavaScript client -- JSON (default)
const ws = new WebSocket("ws://localhost:8080/ws", ["json"]);
// Protobuf (binary, smallest payload)
const ws = new WebSocket("ws://localhost:8080/ws", ["protobuf"]);
// FlatBuffers (zero-copy binary)
const ws = new WebSocket("ws://localhost:8080/ws", ["flatbuffers"]);
// MessagePack (compact binary)
const ws = new WebSocket("ws://localhost:8080/ws", ["msgpack"]);
// No sub-protocol specified = auto JSON (backward compatible)
const ws = new WebSocket("ws://localhost:8080/ws");
// Go client -- using Protobuf sub-protocol
dialer := websocket.Dialer{
	Subprotocols: []string{"protobuf"},
}
conn, _, err := dialer.Dial("ws://localhost:8080/ws", nil)

Codec Interface

The Codec interface abstracts encoding/decoding differences across all serialization formats:

type Codec interface {
	Name() string // "json", "protobuf", "flatbuffers", "msgpack"
	Index() int // Stable unique index (for cache keys)
	Marshal(msg *Message) ([]byte, error) // Serialize
	Unmarshal(data []byte, msg *Message) error // Deserialize
	WebSocketMessageType() int // TextMessage (JSON) or BinaryMessage (others)
}
Codec Index WebSocket Frame Characteristics
JSON 0 TextMessage Human-readable, maximum compatibility
Protobuf 1 BinaryMessage Smallest payload (manual protowire encoding)
FlatBuffers 2 BinaryMessage Zero-copy access (manual Builder API)
MessagePack 3 BinaryMessage Compact binary, JSON superset

Get the client's negotiated Codec via client.Codec():

hub.SetCallbacks(
	func(client *websocket.Client) {
		codec := client.Codec()
		log.Printf("Client %s using %s encoding (index %d)", client.ID, codec.Name(), codec.Index())
	},
	nil, nil,
)

ControlDecoder Optional Interface

How control messages (subscribe / join_room, etc.) data fields are parsed depends on whether the codec implements ControlDecoder:

type ControlDecoder interface {
	DecodeChannel(data []byte) string
	DecodeRoomID(data []byte) string
}
  • ProtobufCodec: Implements this interface, parsing with ChannelRequest / RoomRequest Protobuf structures.
  • Other Codecs: Control message data fields use JSON encoding internally (default path).

Custom Sub-Protocol Configuration

config := websocket.DefaultConfig
config.Subprotocols = []string{"json"} // Allow JSON only
// Or
config.Subprotocols = []string{"json", "protobuf", "flatbuffers", "msgpack"} // Default: all four
hub := websocket.NewHub(l, config)

Protobuf Message Format

The Protobuf schema for WebSocket messages is defined in proto/message.proto:

message WsMessage {
 string type = 1; // Message type
 string channel = 2; // Channel name
 bytes data = 3; // Opaque payload
 int64 timestamp = 4; // Server timestamp
 string client_id = 5; // Client identifier
}

Control messages (subscribe / join_room, etc.) data fields use separate Protobuf structures:

message ChannelRequest { string channel = 1; }
message RoomRequest { string room_id = 1; }

Security Layer (AES-256-GCM + HMAC-SHA256)

Configuration

config := websocket.DefaultConfig
config.Security = &websocket.SecurityConfig{
	AESKey: myAES256Key, // 32 bytes, nil = no encryption
	HMACKey: myHMACKey, // Any length, nil = no signing
	SignThenEncrypt: true, // true (default): sign first then encrypt
}
hub := websocket.NewHub(l, config)

Security Pipeline

Messages are automatically processed through the security pipeline before/after codec serialization/deserialization:

Sign-then-Encrypt (default, SignThenEncrypt: true):

Outbound: codec.Marshal -> HMAC-Sign -> AES-Encrypt -> wire
Inbound: wire -> AES-Decrypt -> HMAC-Verify -> codec.Unmarshal

Encrypt-then-Sign (SignThenEncrypt: false):

Outbound: codec.Marshal -> AES-Encrypt -> HMAC-Sign -> wire
Inbound: wire -> HMAC-Verify -> AES-Decrypt -> codec.Unmarshal

AES-256-GCM Encryption

  • Uses Go standard library crypto/aes + crypto/cipher
  • Each encryption uses crypto/rand to generate a unique 12-byte nonce
  • Nonce is prefixed to ciphertext: [nonce(12) | ciphertext | GCM-tag(16)]
  • Key must be 32 bytes (AES-256)

HMAC-SHA256 Signing

  • Uses Go standard library crypto/hmac + crypto/sha256
  • 32-byte signature prefixed to original data: [HMAC(32) | data]
  • Verification uses hmac.Equal for constant-time comparison, preventing timing attacks

Per-client Key Override

When different keys are needed for different clients:

hub.SetCallbacks(
	func(client *websocket.Client) {
		// Set client-specific keys based on authentication results
		clientAESKey := deriveKeyForUser(client.ID)
		client.SetEncryptionKey(clientAESKey)
		clientHMACKey := deriveHMACKeyForUser(client.ID)
		client.SetHMACKey(clientHMACKey)
	},
	nil, nil,
)

If no per-client key is set, Hub-level SecurityConfig keys are used.

Standalone Encryption/Decryption Functions

Security functions can be used independently, not limited to WebSocket scenarios:

key := make([]byte, 32) // AES-256 key
plaintext := []byte("sensitive data")
// Encrypt
ciphertext, err := websocket.Encrypt(plaintext, key)
// Decrypt
decrypted, err := websocket.Decrypt(ciphertext, key)
// Sign
hmacKey := []byte("my-hmac-secret")
signed := websocket.Sign(plaintext, hmacKey)
// Verify
verified, err := websocket.Verify(signed, hmacKey)

permessage-deflate Compression

Configuration

config := websocket.DefaultConfig
config.Compression = &websocket.CompressionConfig{
	Enabled: true, // Default true
	Level: 6, // flate compression level 1-9, 0=default
}
hub := websocket.NewHub(l, config)
  • Higher Level values mean better compression but more CPU usage
  • Set to 0 for Go's default compression level
  • Backward compatible with the original EnableCompression field: Compression overrides EnableCompression when non-nil

WSS/TLS Support

Within HypGo Framework

TLS is typically handled by the pkg/server layer (config.Server.TLS), and the WebSocket layer automatically uses wss://.

Standalone Mode

When the WebSocket Hub is used as a standalone server, quickly start via ListenAndServeTLS:

config := websocket.DefaultConfig
config.TLS = &websocket.TLSConfig{
	CertFile: "/path/to/cert.pem",
	KeyFile: "/path/to/key.pem",
}
// Or provide a pre-configured tls.Config
config.TLS = &websocket.TLSConfig{
	TLSConfig: myTLSConfig,
}
hub := websocket.NewHub(l, config)
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	// ... handle upgrade
})
// Start WSS server
err := hub.ListenAndServeTLS(":8443", handler)

Channel and Room Management

After connection establishment, you can actively change a client's group state for segmented broadcasting:

// Subscribe a client to a specific channel
client.Subscribe("news")
// Push structured Message to all subscribers in a channel (supports cross-protocol + security pipeline)
msg := websocket.AcquireMessage()
msg.Type = "message"
msg.Channel = "news"
msg.Data = []byte(`{"headline": "Breaking News!"}`)
hub.PublishToChannel("news", msg)
msg.Release()
// Or use the backward-compatible raw bytes API
hub.PublishToChannelRaw("news", []byte(`{"headline": "Breaking News!"}`))
// Join a Room for games or chat
client.JoinRoom("room_101")
// Global broadcast
hub.Broadcast([]byte(`{"event": "server_restart"}`))
// Send to a specific client (automatically uses that client's codec + security pipeline)
hub.SendToClient("client-123", msg)

Complete Configuration Example

config := websocket.Config{
	ReadBufferSize: 1024,
	WriteBufferSize: 1024,
	MaxMessageSize: 65536,
	WriteWait: 10 * time.Second,
	PongWait: 60 * time.Second,
	PingPeriod: 54 * time.Second,
	EnableCompression: true,
	Subprotocols: []string{"json", "protobuf", "flatbuffers", "msgpack"},
	// Compression configuration (overrides EnableCompression)
	Compression: &websocket.CompressionConfig{
		Enabled: true,
		Level: 6,
	},
	// Security configuration
	Security: &websocket.SecurityConfig{
		AESKey: aes256Key, // 32 bytes
		HMACKey: hmacSecret,
		SignThenEncrypt: true,
	},
	// TLS configuration (standalone mode)
	TLS: &websocket.TLSConfig{
		CertFile: "cert.pem",
		KeyFile: "key.pem",
	},
}
hub := websocket.NewHub(l, config)

File Structure

pkg/websocket/
├── websocket.go # Core: Client, Hub, Room, Config, readPump/writePump,
│ # TLS/Security/Compression integration, ListenAndServeTLS
├── codec.go # Codec/ControlDecoder interfaces, JSONCodec, ProtobufCodec,
│ # marshalForClients (map-based N-codec cache + security pipeline)
├── codec_flatbuffers.go # FlatBuffersCodec (manual Builder API, zero-copy)
├── codec_msgpack.go # MsgpackCodec (vmihailenco/msgpack/v5)
├── security.go # AES-256-GCM encrypt/decrypt, HMAC-SHA256 sign/verify, security pipeline
├── proto/
│ ├── message.proto # Protobuf schema definition
│ └── message.pb.go # Protobuf encoding/decoding implementation
├── codec_test.go # Cross-codec round-trip, marshalForClients, index uniqueness
├── codec_flatbuffers_test.go # FlatBuffers specific tests
├── codec_msgpack_test.go # MessagePack specific tests
├── security_test.go # AES/HMAC/pipeline/per-client key tests
├── websocket_test.go # WebSocket core + sub-protocol negotiation tests
└── README.md

Dependencies

Package Purpose
github.com/gorilla/websocket WebSocket protocol implementation
github.com/google/flatbuffers/go FlatBuffers binary serialization
github.com/vmihailenco/msgpack/v5 MessagePack binary serialization
crypto/aes, crypto/cipher AES-256-GCM encryption (Go stdlib)
crypto/hmac, crypto/sha256 HMAC-SHA256 signing (Go stdlib)
crypto/tls TLS/WSS support (Go stdlib)

HypGo

繁體中文 | English


中文文件

設計文件

套件

AI 協作工具鏈

CLI 命令


English Docs

Design Docs

Packages

AI Collaboration Toolchain

CLI Commands

Clone this wiki locally

AltStyle によって変換されたページ (->オリジナル) /