Documentation
¶
Overview ¶
Package websocket provides a zero-dependency native WebSocket middleware for celeris, implementing RFC 6455.
Basic Echo Server ¶
server.GET("/ws", websocket.New(websocket.Config{
Handler: func(c *websocket.Conn) {
for {
mt, msg, err := c.ReadMessage()
if err != nil {
break
}
if err := c.WriteMessage(mt, msg); err != nil {
break
}
}
},
}))
Origin Checking ¶
By default, a same-origin check is enforced (the Origin header must match the Host header). To allow all origins:
websocket.New(websocket.Config{
CheckOrigin: func(c *celeris.Context) bool { return true },
Handler: myHandler,
})
To restrict to specific origins:
websocket.New(websocket.Config{
CheckOrigin: func(c *celeris.Context) bool {
return c.Header("origin") == "https://example.com"
},
Handler: myHandler,
})
JSON Messaging ¶
websocket.New(websocket.Config{
Handler: func(c *websocket.Conn) {
var msg MyType
if err := c.ReadJSON(&msg); err != nil {
return
}
c.WriteJSON(msg)
},
})
Subprotocol Negotiation ¶
websocket.New(websocket.Config{
Subprotocols: []string{"graphql-transport-ws"},
Handler: func(c *websocket.Conn) {
proto := c.Subprotocol()
// handle based on negotiated protocol
},
})
HTTP/2 Limitation ¶
WebSocket requires HTTP/1.1 connection hijacking. HTTP/2 multiplexes streams over a single TCP connection, making hijack impossible. This middleware returns 426 Upgrade Required for HTTP/2 requests.
Concurrency ¶
All write methods (Conn.WriteMessage, Conn.WriteText, Conn.WriteBinary, Conn.WriteJSON, Conn.WritePing) are internally serialized and safe for concurrent use from multiple goroutines. A single goroutine may call Conn.ReadMessage while others write concurrently.
Conn.SetPingHandler, Conn.SetPongHandler, and Conn.SetCloseHandler must be called before starting the read loop.
Keepalive (Ping/Pong) ¶
Detect dead connections with periodic pings:
Handler: func(c *websocket.Conn) {
c.SetPongHandler(func(data []byte) error {
c.SetReadDeadline(time.Now().Add(60 * time.Second))
return nil
})
c.SetReadDeadline(time.Now().Add(60 * time.Second))
go func() {
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
for range ticker.C {
if err := c.WritePing(nil); err != nil {
return
}
}
}()
for {
mt, msg, err := c.ReadMessage()
if err != nil { break }
c.WriteMessage(mt, msg)
}
},
Compression (permessage-deflate) ¶
Enable RFC 7692 permessage-deflate compression:
websocket.New(websocket.Config{
EnableCompression: true,
Handler: myHandler,
})
Compression is negotiated during the upgrade handshake. Messages above the compression threshold (default 128 bytes) are compressed transparently.
Streaming Large Messages ¶
For large messages, use Conn.NextReader and Conn.NextWriter to avoid buffering the entire message in memory:
mt, reader, _ := c.NextReader() io.Copy(dst, reader) writer, _ := c.NextWriter(websocket.TextMessage) writer.Write(chunk1) writer.Write(chunk2) writer.Close()
ReadMessage vs ReadMessageReuse ¶
Conn.ReadMessage returns an owned copy (safe to retain). Conn.ReadMessageReuse returns a reused buffer (zero-alloc, only valid until the next read call). Use ReadMessageReuse for echo servers and message-forwarding proxies where the data is processed immediately.
Access Request Data ¶
Route params, query params, and headers are captured at upgrade time:
// Route: /ws/:room
c.Param("room") // route parameter
c.Query("token") // query parameter
c.Header("origin") // request header
Engine-integrated mode vs hijack mode ¶
On the std engine, WebSocket connections are upgraded via Go's standard connection hijacking — the handler runs on a goroutine and reads/writes directly on a *net.TCPConn. On the native engines (epoll, io_uring), the connection stays in the event loop after upgrade: inbound chunks are delivered to the handler goroutine via an internal chanReader (which applies TCP-level backpressure on overflow), and outbound writes go through the engine's per-connection write buffer (cs.writeBuf).
The same Handler signature works for both modes — the middleware picks the engine-integrated path automatically when the engine supports it and falls back to hijack on the std engine.
Backpressure semantics ¶
On the engine path, the WebSocket conn maintains a bounded chanReader between the event loop and the handler goroutine. When the buffer fills past the high-water mark (75% of [Config.MaxBackpressureBuffer]), the engine pauses inbound delivery for that connection (epoll: drops EPOLLIN; io_uring: cancels the in-flight RECV). The kernel then closes the TCP receive window, slowing the peer at the network level. When the buffer drains below 25%, the engine resumes inbound delivery.
On the std (hijack) path, backpressure is handled directly by the kernel's TCP stack via net.Conn.Read returning when the kernel buffer has data — no middleware-level buffering happens.
In healthy operation Conn.BackpressureDropped returns 0. A non-zero value indicates the engine pause/resume mechanism is malfunctioning.
IdleTimeout semantics ¶
On the std (hijack) path, [Config.IdleTimeout] is enforced via net.Conn.SetReadDeadline, which is reset before each blocking read. On the engine path, the WS middleware extends an absolute deadline via [Context.SetWSIdleDeadline] after each successful frame read, and the engine's idle sweep closes connections whose deadline has expired. Both paths converge to the same observable behavior.
WriteControl deadline semantics ¶
Conn.WriteControl applies the supplied deadline to the channel-based write semaphore (so a stalled large NextWriter cannot indefinitely block pings/pongs). On the std path, the deadline is also pinned to the underlying net.Conn via net.Conn.SetWriteDeadline so a peer that has stopped reading cannot stall the actual flush. On the engine path, writes go into the engine's write buffer and never block at the syscall level — only the lock-acquisition deadline applies.
Index ¶
- Constants
- Variables
- func FormatCloseMessage(code int, text string) []byte
- func IsCloseError(err error, codes ...int) bool
- func IsUnexpectedCloseError(err error, expectedCodes ...int) bool
- func New(config ...Config) celeris.HandlerFunc
- type BufferPool
- type CloseError
- type Config
- type Conn
- func (c *Conn) BackpressureDropped() uint64
- func (c *Conn) Close() error
- func (c *Conn) CloseHandler() func(code int, text string) error
- func (c *Conn) Context() context.Context
- func (c *Conn) EnableWriteCompression(enable bool)
- func (c *Conn) GracefulClose(code int, text string) error
- func (c *Conn) Header(key string) string
- func (c *Conn) IP() string
- func (c *Conn) LocalAddr() net.Addr
- func (c *Conn) Locals(key string) any
- func (c *Conn) NetConn() net.Conn
- func (c *Conn) NextReader() (MessageType, io.Reader, error)
- func (c *Conn) NextWriter(messageType MessageType) (io.WriteCloser, error)
- func (c *Conn) Param(key string) string
- func (c *Conn) PingHandler() func(data []byte) error
- func (c *Conn) PongHandler() func(data []byte) error
- func (c *Conn) Query(key string) string
- func (c *Conn) ReadJSON(v any) error
- func (c *Conn) ReadMessage() (MessageType, []byte, error)
- func (c *Conn) ReadMessageReuse() (MessageType, []byte, error)
- func (c *Conn) RemoteAddr() net.Addr
- func (c *Conn) SetCloseHandler(h func(code int, text string) error)
- func (c *Conn) SetCompressionLevel(level int) error
- func (c *Conn) SetLocals(key string, val any)
- func (c *Conn) SetPingHandler(h func(data []byte) error)
- func (c *Conn) SetPongHandler(h func(data []byte) error)
- func (c *Conn) SetReadDeadline(t time.Time) error
- func (c *Conn) SetReadLimit(limit int64)
- func (c *Conn) SetWriteDeadline(t time.Time) error
- func (c *Conn) Subprotocol() string
- func (c *Conn) WriteBinary(data []byte) error
- func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) error
- func (c *Conn) WriteJSON(v any) error
- func (c *Conn) WriteMessage(messageType MessageType, data []byte) error
- func (c *Conn) WritePing(data []byte) error
- func (c *Conn) WritePreparedMessage(pm *PreparedMessage) error
- func (c *Conn) WriteText(data []byte) error
- type Handler
- type MessageType
- type Opcode
- type PreparedMessage
Examples ¶
Constants ¶
const ( CompressionLevelDefault = flate.DefaultCompression // -1 CompressionLevelBestSpeed = flate.BestSpeed // 1 CompressionLevelBestSize = flate.BestCompression // 9 CompressionLevelHuffman = flate.HuffmanOnly // -2 )
Compression levels matching compress/flate.
const ( // TextMessage denotes a UTF-8 text message. TextMessage = OpText // BinaryMessage denotes a binary message. BinaryMessage = OpBinary )
const ( CloseNormalClosure = 1000 CloseGoingAway = 1001 CloseProtocolError = 1002 CloseUnsupportedData = 1003 CloseNoStatusReceived = 1005 CloseAbnormalClosure = 1006 CloseInvalidPayload = 1007 ClosePolicyViolation = 1008 CloseMessageTooBig = 1009 CloseMandatoryExt = 1010 CloseInternalError = 1011 CloseServiceRestart = 1012 CloseTryAgainLater = 1013 )
Close status codes (RFC 6455 Section 7.4.1).
Variables ¶
var ( ErrProtocol = errors.New("websocket: protocol error") ErrFrameTooLarge = errors.New("websocket: frame payload too large") ErrReservedBits = errors.New("websocket: reserved bits set") ErrFragmentedControl = errors.New("websocket: fragmented control frame") ErrControlTooLarge = errors.New("websocket: control frame payload > 125") ErrInvalidCloseData = errors.New("websocket: invalid close frame data") ErrInvalidUTF8 = errors.New("websocket: invalid UTF-8 in text frame") ErrReadLimit = errors.New("websocket: message exceeds read limit") ErrClosed = errors.New("websocket: connection closed") ErrWriteClosed = errors.New("websocket: write on closed connection") ErrWriteTimeout = errors.New("websocket: write deadline exceeded") )
Errors.
Functions ¶
func FormatCloseMessage ¶
FormatCloseMessage creates a close frame payload with the given code and text.
func IsCloseError ¶
IsCloseError returns true if err is a CloseError with one of the given codes.
func IsUnexpectedCloseError ¶
IsUnexpectedCloseError returns true if err is a CloseError whose code is NOT in the given list.
func New ¶
func New(config ...Config) celeris.HandlerFunc
New creates a WebSocket middleware that upgrades matching requests.
Non-WebSocket requests are passed through to the next handler. HTTP/2 requests receive a 426 Upgrade Required response because connection hijacking is not possible over multiplexed streams.
On native engines (epoll, io_uring), the connection remains in the event loop after upgrade — reads are delivered by the engine, writes go through the engine's write buffer with backpressure. On the std engine, the connection is hijacked for direct I/O.
This is a zero-dependency native WebSocket implementation (RFC 6455).
Usage:
server.GET("/ws", websocket.New(websocket.Config{
Handler: func(c *websocket.Conn) {
for {
mt, msg, err := c.ReadMessage()
if err != nil {
break
}
c.WriteMessage(mt, msg)
}
},
}))
Example ¶
Echo each message back to the client. The middleware automatically uses the engine-integrated path on epoll/io_uring (with TCP-level backpressure) and falls back to net/http Hijack on the std engine.
package main
import (
"fmt"
"time"
"github.com/goceleris/celeris"
"github.com/goceleris/celeris/middleware/websocket"
)
func main() {
s := celeris.New(celeris.Config{})
s.GET("/ws", websocket.New(websocket.Config{
EnableCompression: true,
IdleTimeout: 60 * time.Second,
Handler: func(c *websocket.Conn) {
for {
mt, data, err := c.ReadMessageReuse()
if err != nil {
return
}
if err := c.WriteMessage(mt, data); err != nil {
return
}
}
},
}))
fmt.Println("WebSocket echo handler installed at /ws")
}
Output: WebSocket echo handler installed at /ws
Types ¶
type BufferPool ¶
type BufferPool interface {
// Get returns a bufio.Writer reset to write into dst. The pool
// should Reset(dst) on borrow so the returned writer has no stale
// buffered bytes. If the pool is empty, it must allocate a new
// [bufio.Writer] (typically with [bufio.NewWriterSize]).
Get(dst io.Writer) *bufio.Writer
// Put returns a bufio.Writer to the pool. The caller has already
// called Flush on the writer. Implementations may discard the
// writer if e.g. its buffer grew beyond an acceptable size.
Put(bw *bufio.Writer)
}
BufferPool is an interface for borrowing and returning bufio.Writer instances that the WebSocket writer uses on the hijack (std engine) path. A typical production implementation wraps a sync.Pool:
type wsPool struct{ p sync.Pool }
func (w *wsPool) Get(dst io.Writer) *bufio.Writer {
if v := w.p.Get(); v != nil {
bw := v.(*bufio.Writer)
bw.Reset(dst)
return bw
}
return bufio.NewWriterSize(dst, 4096)
}
func (w *wsPool) Put(bw *bufio.Writer) { w.p.Put(bw) }
BufferPool is not consulted on the native-engine path (epoll/io_uring), which uses the engine's per-connection write buffer internally.
type CloseError ¶
CloseError is returned when a close frame is received.
func (*CloseError) Error ¶
func (e *CloseError) Error() string
type Config ¶
type Config struct {
// Handler is called after a successful WebSocket upgrade.
// Required. Panics if nil.
Handler Handler
// Skip defines a function to skip this middleware for certain requests.
Skip func(c *celeris.Context) bool
// SkipPaths lists paths to skip (exact match on c.Path()).
SkipPaths []string
// CheckOrigin returns true if the request origin is acceptable.
// If nil, the default same-origin check is used (Origin header must
// match the Host header). Set to func(*celeris.Context) bool { return true }
// to allow all origins.
CheckOrigin func(c *celeris.Context) bool
// Subprotocols specifies the server's supported protocols in preference order.
Subprotocols []string
// ReadBufferSize specifies the I/O read buffer size in bytes.
// Default: 4096.
ReadBufferSize int
// WriteBufferSize specifies the I/O write buffer size in bytes.
// Default: 4096.
WriteBufferSize int
// ReadLimit is the maximum message size in bytes.
// Default: 64MB.
ReadLimit int64
// HandshakeTimeout specifies the duration for the handshake to complete.
// Default: 0 (no timeout).
HandshakeTimeout time.Duration
// WriteBufferPool is an optional pool for write buffers. When set,
// write buffers are obtained from the pool before each write and
// returned after flush, reducing memory for idle connections.
// If nil, each connection allocates its own permanent write buffer.
WriteBufferPool BufferPool
// EnableCompression enables permessage-deflate compression (RFC 7692).
// When enabled, the server negotiates compression during the upgrade
// handshake. Messages are compressed transparently.
EnableCompression bool
// CompressionLevel controls the deflate compression level.
// Valid range: -2 (Huffman only) to 9 (best compression).
// Default: 1 (best speed). Use [CompressionLevelDefault] for the
// flate library default (-1).
CompressionLevel int
// CompressionThreshold is the minimum payload size in bytes for
// compression. Messages smaller than this are sent uncompressed.
// Default: 128.
CompressionThreshold int
// IdleTimeout is the maximum time between messages before the connection
// is closed. When set, the next read deadline is extended after each
// successful frame read. On the std (hijack) path this is enforced via
// net.Conn.SetReadDeadline; on native engines (epoll/io_uring) it is
// enforced via the engine's idle sweep using SetWSIdleDeadline.
// Zero means no idle timeout.
IdleTimeout time.Duration
// MaxBackpressureBuffer is the maximum number of inbound chunks
// buffered between the engine event loop and the WebSocket handler
// goroutine on the engine-integrated path. When the buffer fills past
// BackpressureHighPct, the engine pauses inbound delivery for this
// connection (TCP-level backpressure); when it drains below
// BackpressureLowPct, delivery is resumed.
// Default: 256. Ignored on the std (hijack) engine path.
MaxBackpressureBuffer int
// BackpressureHighPct is the buffer fill percentage (0-100) at which
// the engine is asked to pause inbound delivery. Default: 75.
BackpressureHighPct int
// BackpressureLowPct is the buffer fill percentage (0-100) at which
// the engine is asked to resume inbound delivery after a pause. Must
// be lower than BackpressureHighPct or it falls back to the default
// (25). Default: 25.
BackpressureLowPct int
// OnConnect is called after upgrade succeeds, before Handler.
// If it returns a non-nil error, the connection is closed.
OnConnect func(*Conn) error
// OnDisconnect is called after the Handler returns.
OnDisconnect func(*Conn)
}
Config defines the WebSocket middleware configuration.
Example (Backpressure) ¶
Tune the engine-integrated backpressure watermarks. Default is 75% pause / 25% resume on a 256-chunk buffer; raise the pause point when the handler is bursty but generally fast.
package main
import (
"fmt"
"github.com/goceleris/celeris/middleware/websocket"
)
func main() {
cfg := websocket.Config{
MaxBackpressureBuffer: 1024,
BackpressureHighPct: 90,
BackpressureLowPct: 50,
Handler: func(c *websocket.Conn) {
_ = c
},
}
_ = websocket.New(cfg)
fmt.Println("ok")
}
Output: ok
Example (LifecycleHooks) ¶
Use OnConnect to authenticate and OnDisconnect to release resources.
package main
import (
"fmt"
"github.com/goceleris/celeris/middleware/websocket"
)
func main() {
cfg := websocket.Config{
OnConnect: func(c *websocket.Conn) error {
// Reject unauthenticated peers — return non-nil to close the
// connection before the Handler runs.
if c.Subprotocol() != "v1.echo" {
return fmt.Errorf("subprotocol required")
}
return nil
},
OnDisconnect: func(c *websocket.Conn) {
// Release per-connection resources (DB handles, channels…).
_ = c
},
Handler: func(c *websocket.Conn) {
_ = c
},
}
_ = websocket.New(cfg)
fmt.Println("ok")
}
Output: ok
type Conn ¶
type Conn struct {
// contains filtered or unexported fields
}
Conn represents a WebSocket connection. It is safe for one goroutine to read and another to write concurrently, but not for multiple readers or multiple writers.
func (*Conn) BackpressureDropped ¶
BackpressureDropped returns the number of inbound chunks dropped because the engine-path read buffer overflowed despite TCP-level backpressure. Should be 0 in healthy operation. Returns 0 on the std (hijack) path, where backpressure is handled directly by the kernel TCP stack.
func (*Conn) Close ¶
Close closes the underlying connection.
On the engine path (net.Conn unavailable), Close asks the engine to drop the FD on its next idle sweep by setting the WS idle deadline to the past. This is the server-side TCP close that RFC 6455 §7.1.1 and Autobahn 7.7.x (requireClean=True) require after the close handshake — without it the peer waits indefinitely for the server FIN. The underlying drain order (drainDetachQueue → flush writes → checkTimeouts → closeConn) guarantees any buffered close-frame echo reaches the wire before the FD is shut down.
func (*Conn) CloseHandler ¶
CloseHandler returns the current close handler.
func (*Conn) EnableWriteCompression ¶
EnableWriteCompression enables or disables write compression for this connection. Compression must have been negotiated during the upgrade handshake; this only controls whether subsequent writes actually compress.
func (*Conn) GracefulClose ¶
GracefulClose sends a close frame and waits for the peer's response.
On the hijack path the deadline is enforced via net.Conn.SetReadDeadline. On the engine path (chanReader-backed reads) net.Conn is nil, so a time.AfterFunc closes the engineReader to unblock ReadMessageReuse if the peer never sends its close frame.
func (*Conn) IP ¶
IP returns the remote IP address (without port). The result is cached after the first call so per-message log loops don't re-parse RemoteAddr().String() on every iteration.
func (*Conn) NextReader ¶
func (c *Conn) NextReader() (MessageType, io.Reader, error)
NextReader returns the next data message received. The io.Reader returned reads the message payload across fragmented frames. The reader is valid until the next call to NextReader, ReadMessage, or Close.
Control frames (ping, pong, close) are handled transparently.
For compressed messages, NextReader decompresses the entire message before returning. Use Conn.ReadMessage for the same behavior with a simpler API.
func (*Conn) NextWriter ¶
func (c *Conn) NextWriter(messageType MessageType) (io.WriteCloser, error)
NextWriter returns an io.WriteCloser for sending a message of the given type. The writer sends the message as one or more WebSocket frames. The caller must call Close to complete the message.
Only one writer may be active at a time. Starting a new writer while the previous one is open is not supported.
Control frames (ping/pong/close) can still be sent concurrently while a NextWriter is active — they are not blocked.
If compression is negotiated and enabled, the writer buffers all data and compresses on Close (since permessage-deflate context spans the entire message).
func (*Conn) PingHandler ¶
PingHandler returns the current ping handler.
func (*Conn) PongHandler ¶
PongHandler returns the current pong handler.
func (*Conn) ReadMessage ¶
func (c *Conn) ReadMessage() (MessageType, []byte, error)
ReadMessage reads the next complete message from the connection. Returns the message type and an owned copy of the payload. The returned slice is safe to retain, pass to other goroutines, or store.
For zero-allocation reads (advanced usage), use Conn.ReadMessageReuse.
func (*Conn) ReadMessageReuse ¶
func (c *Conn) ReadMessageReuse() (MessageType, []byte, error)
ReadMessageReuse reads the next complete message from the connection. The returned byte slice is reused across calls and is only valid until the next call to ReadMessageReuse or ReadMessage.
Use this for zero-allocation reads when you process each message immediately without retaining the slice.
func (*Conn) RemoteAddr ¶
RemoteAddr returns the peer's network address.
func (*Conn) SetCloseHandler ¶
SetCloseHandler sets the handler for close frames. The default handler echoes the close frame back and returns a CloseError.
func (*Conn) SetCompressionLevel ¶
SetCompressionLevel sets the flate compression level for subsequent writes. Valid range: -2 (HuffmanOnly) to 9 (BestCompression), or -1 (DefaultCompression).
func (*Conn) SetPingHandler ¶
SetPingHandler sets the handler for ping frames. The default handler replies with a pong containing the same payload.
func (*Conn) SetPongHandler ¶
SetPongHandler sets the handler for pong frames. The default is a no-op.
func (*Conn) SetReadDeadline ¶
SetReadDeadline sets the deadline for future reads. Returns nil on engine-integrated connections where deadlines are not supported.
func (*Conn) SetReadLimit ¶
SetReadLimit sets the maximum message size in bytes. The default is 64MB.
func (*Conn) SetWriteDeadline ¶
SetWriteDeadline sets the deadline for future writes. Returns nil on engine-integrated connections where deadlines are not supported.
func (*Conn) Subprotocol ¶
Subprotocol returns the negotiated subprotocol, or "" if none.
func (*Conn) WriteBinary ¶
WriteBinary writes a binary message.
func (*Conn) WriteControl ¶
WriteControl sends a control frame with a per-frame deadline. It can be called concurrently with Conn.NextWriter — the channel-based write semaphore is acquired with the deadline, returning ErrWriteTimeout if it cannot be obtained in time.
On the std (hijack) path the deadline is also applied to the underlying net.Conn via SetWriteDeadline so that a slow peer cannot indefinitely block the actual flush. On the engine path the write goes into the engine's per-connection write buffer (cs.writeBuf) and never blocks at the syscall level, so only the lock-acquisition deadline applies.
messageType must be a control opcode (OpClose, OpPing, OpPong). data must be <= 125 bytes.
func (*Conn) WriteMessage ¶
func (c *Conn) WriteMessage(messageType MessageType, data []byte) error
WriteMessage writes a complete message to the connection. If compression is negotiated and the payload exceeds the compression threshold, the message is compressed transparently. Compression runs before the write lock is taken so that ping/pong control frames can interleave with large compressed data writes.
func (*Conn) WritePing ¶
WritePing sends a ping control frame. The payload must be <= 125 bytes. Use this with Conn.SetPongHandler to implement keepalive.
func (*Conn) WritePreparedMessage ¶
func (c *Conn) WritePreparedMessage(pm *PreparedMessage) error
WritePreparedMessage sends a pre-encoded message. This is efficient for broadcasting the same message to many connections — the frame is encoded once and reused.
type Handler ¶
type Handler func(*Conn)
Handler is called with the upgraded WebSocket connection. The function should block until the connection is done. When Handler returns, the connection is closed automatically.
type Opcode ¶
type Opcode byte
Opcode represents a WebSocket frame opcode (RFC 6455 Section 5.2).
const ( // OpContinuation is the continuation frame opcode. OpContinuation Opcode = 0x0 // OpText is the text data frame opcode. OpText Opcode = 0x1 // OpBinary is the binary data frame opcode. OpBinary Opcode = 0x2 // OpClose is the connection close control frame opcode. OpClose Opcode = 0x8 // OpPing is the ping control frame opcode. OpPing Opcode = 0x9 // OpPong is the pong control frame opcode. OpPong Opcode = 0xA )
type PreparedMessage ¶
type PreparedMessage struct {
// contains filtered or unexported fields
}
PreparedMessage caches the wire-format encoding of a message for efficient broadcast to multiple connections. Create one with NewPreparedMessage and send it via Conn.WritePreparedMessage.
func NewPreparedMessage ¶
func NewPreparedMessage(messageType MessageType, data []byte) (*PreparedMessage, error)
NewPreparedMessage creates a PreparedMessage from the given payload. The data is copied; the caller retains ownership of the original slice.