Documentation
¶
Overview ¶
Package wsstat measures the latency of WebSocket connections. It wraps the coder/websocket package and includes latency measurements in the Result struct.
Index ¶
- Constants
- Variables
- type CertificateDetails
- type Option
- func WithBufferSize(n int) Option
- func WithCloseGrace(d time.Duration) Option
- func WithCompression(enabled bool) Option
- func WithDiscardReads() Option
- func WithHeaders(headers http.Header) Option
- func WithLogger(logger zerolog.Logger) Option
- func WithReadLimit(n int64) Option
- func WithResolves(resolves map[string]string) Option
- func WithSubprotocols(subprotocols []string) Option
- func WithTLSConfig(cfg *tls.Config) Option
- func WithTimeout(d time.Duration) Option
- func WithUnboundedReads() Option
- func WithValidateUTF8(enabled bool) Option
- type Result
- func MeasureJSON(ctx context.Context, target *url.URL, msgs []any, opts ...Option) (*Result, []any, error)
- func MeasurePing(ctx context.Context, target *url.URL, count int, opts ...Option) (*Result, error)
- func MeasureText(ctx context.Context, target *url.URL, msgs []string, opts ...Option) (*Result, []string, error)
- type Subscription
- type SubscriptionMessage
- type SubscriptionOptions
- type SubscriptionStats
- type WSStat
- func (ws *WSStat) Close()
- func (ws *WSStat) CloseWith(code int, reason string) error
- func (ws *WSStat) DialContext(ctx context.Context, targetURL *url.URL, customHeaders http.Header) error
- func (ws *WSStat) ExtractResult() *Result
- func (ws *WSStat) PingPong() error
- func (ws *WSStat) PingPongContext(ctx context.Context) error
- func (ws *WSStat) ReadMessage() (int, []byte, error)
- func (ws *WSStat) ReadMessageJSON() (any, error)
- func (ws *WSStat) Subscribe(ctx context.Context, opts SubscriptionOptions) (*Subscription, error)
- func (ws *WSStat) SubscribeOnce(ctx context.Context, opts SubscriptionOptions) (SubscriptionMessage, error)
- func (ws *WSStat) WriteMessage(messageType int, data []byte)
- func (ws *WSStat) WriteMessageJSON(v any)
Constants ¶
const ( // TextMessage denotes a UTF-8 encoded text message (e.g. JSON). Numerically identical to // websocket.MessageText so the public int-based API stays stable across the transport swap. TextMessage = 1 // BinaryMessage denotes a binary data message. Numerically identical to // websocket.MessageBinary. BinaryMessage = 2 )
Variables ¶
var ( // ErrConnectionNotEstablished is returned when a read or ping is attempted before a // successful dial. ErrConnectionNotEstablished = errors.New("wsstat: connection not established") // ErrClosed is returned when an operation is attempted on a closed connection. ErrClosed = errors.New("wsstat: connection closed") )
Exported error sentinels for the failure classes library consumers branch on. Returned (via errors.Is) by the connection methods; the one-shot Measure* functions propagate them.
Functions ¶
This section is empty.
Types ¶
type CertificateDetails ¶
type CertificateDetails struct {
CommonName string
Issuer string
NotBefore time.Time
NotAfter time.Time
PublicKeyAlgorithm x509.PublicKeyAlgorithm
SignatureAlgorithm x509.SignatureAlgorithm
DNSNames []string
IPAddresses []net.IP
URIs []*url.URL
}
CertificateDetails holds details regarding a certificate.
type Option ¶
type Option func(*options)
Option configures a WSStat instance.
func WithBufferSize ¶
WithBufferSize sets the buffer size for read/write/pong channels.
func WithCloseGrace ¶
WithCloseGrace bounds how long Close waits for the peer's closing-handshake echo before forcing the connection shut. Zero or negative fires the teardown immediately rather than granting a grace window; a peer that echoes in that instant may still complete the handshake cleanly. Defaults to 3s.
Only values below 5s take effect: the underlying coder/websocket library caps its own close handshake at a hard-coded 5s, so Close returns by then regardless and a larger grace cannot extend the wait. The useful range is (0, 5s).
func WithCompression ¶
WithCompression enables negotiation of the permessage-deflate extension. Disabled by default. The negotiated extension is reported in Result.Compression.
func WithDiscardReads ¶ added in v3.2.0
func WithDiscardReads() Option
WithDiscardReads drops inbound data frames not claimed by a subscription instead of queueing them on the read channel. Without it, a session that never calls ReadMessage (such as a ping/pong monitor) fills the read channel after bufferSize unsolicited frames from a chatty peer; the blocked read pump then stops calling Read, which also starves pong control-frame processing. Error reads are still delivered to the read channel.
func WithHeaders ¶
WithHeaders sets HTTP headers merged into every handshake request. Headers passed directly to Dial/DialContext take precedence over these on a per-key basis.
func WithLogger ¶
WithLogger sets the logger for the WSStat instance.
func WithReadLimit ¶
WithReadLimit bounds the size in bytes of a single inbound message. A value of 0 keeps the default (16 MiB); a negative value disables the limit (use with care: an unbounded message can exhaust memory). Defaults to 16 MiB.
func WithResolves ¶
WithResolves sets DNS resolution overrides for specific host:port combinations. Map key format: "host:port", value: "ip_address".
func WithSubprotocols ¶
WithSubprotocols sets the WebSocket subprotocols to offer during the handshake, in preference order. The negotiated value is reported in Result.Subprotocol.
func WithTLSConfig ¶
WithTLSConfig sets the TLS configuration for the connection.
func WithTimeout ¶
WithTimeout sets the timeout used for dialing and read deadlines.
func WithUnboundedReads ¶ added in v3.2.0
func WithUnboundedReads() Option
WithUnboundedReads drops the read pump's per-read timeout so an idle connection is not torn down after the read/dial timeout elapses with no inbound data frame. It matches how reads behave while a subscription is active. Use it for long-lived sessions driven by control frames the read pump never sees as reads: a ping/pong monitor sends ping frames and receives pongs (both handled below Read), so without this the connection would be closed one timeout after the last data frame. PingPong keeps its own per-ping timeout, so a lost pong is still detected; it just no longer kills the connection.
func WithValidateUTF8 ¶
WithValidateUTF8 enables UTF-8 validation of inbound text frames. coder/websocket performs none (RFC 6455 §5.6 requires text payloads to be valid UTF-8); when enabled, an invalid text frame is logged at warn level and counted in Result.InvalidUTF8Frames rather than failing the connection. Disabled by default.
type Result ¶
type Result struct {
IPs []string // IP addresses of the WebSocket connection
URL *url.URL // URL of the WebSocket connection
Subprotocol string // Negotiated WebSocket subprotocol ("" if none)
Compression string // Negotiated Sec-WebSocket-Extensions value ("" if none)
RequestHeaders http.Header // Headers of the initial request
ResponseHeaders http.Header // Headers of the response
TLSState *tls.ConnectionState // State of the TLS connection
MessageCount int // Number of messages sent and received
// InvalidUTF8Frames counts inbound text frames that failed UTF-8 validation. Always 0
// unless WithValidateUTF8 is set (coder/websocket does not validate text frames itself).
InvalidUTF8Frames int
// Subscription statistics captured when long-lived streams are active.
Subscriptions map[string]SubscriptionStats // Metrics by subscription ID
SubscriptionFirstEvent time.Duration // Time until first subscription event
SubscriptionLastEvent time.Duration // Time until last subscription event
// Duration of each phase of the connection
DNSLookup time.Duration // Time to resolve DNS
TCPConnection time.Duration // TCP connection establishment time
TLSHandshake time.Duration // Time to perform TLS handshake
WSHandshake time.Duration // Time to perform WebSocket handshake
MessageRTT time.Duration // Time to send message and receive response
// Cumulative durations over the connection timeline
DNSLookupDone time.Duration // Time to resolve DNS (might be redundant with DNSLookup)
TCPConnected time.Duration // Time until the TCP connection is established
TLSHandshakeDone time.Duration // Time until the TLS handshake is completed
WSHandshakeDone time.Duration // Time until the WS handshake is completed
FirstMessageResponse time.Duration // Time until the first message is received
TotalTime time.Duration // Total time from opening to closing the connection
}
Result holds durations of each phase of a WebSocket connection, cumulative durations over the connection timeline, and other relevant connection details.
func MeasureJSON ¶
func MeasureJSON( ctx context.Context, target *url.URL, msgs []any, opts ...Option, ) (*Result, []any, error)
MeasureJSON behaves like MeasureText but encodes each element of msgs as JSON and decodes each response as JSON. Settings are supplied via opts.
func MeasurePing ¶
func MeasurePing( ctx context.Context, target *url.URL, count int, opts ...Option, ) (*Result, error)
MeasurePing establishes a connection to target, performs count ping/pong round-trips, closes the connection, and returns the measured Result. Settings are supplied via opts.
func MeasureText ¶
func MeasureText( ctx context.Context, target *url.URL, msgs []string, opts ...Option, ) (*Result, []string, error)
MeasureText establishes a WebSocket connection to target, sends each message in msgs as a text frame, reads one response per message, closes the connection, and returns the measured Result together with the responses in order. A single message is a one-element slice. Custom headers and other settings are supplied via opts (e.g. WithHeaders, WithTimeout). The whole operation honors ctx; a nil or already-canceled ctx aborts before dialing.
func (*Result) CertificateDetails ¶
func (r *Result) CertificateDetails() []CertificateDetails
CertificateDetails returns a slice of CertificateDetails for each certificate in the TLS connection.
type Subscription ¶
type Subscription struct {
ID string
// contains filtered or unexported fields
}
Subscription captures a long-lived stream registered through Subscribe. Counters are updated atomically by the WSStat instance.
func (*Subscription) ByteCount ¶
func (s *Subscription) ByteCount() uint64
ByteCount reports the aggregate payload size delivered to the subscription.
func (*Subscription) Cancel ¶
func (s *Subscription) Cancel()
Cancel stops the subscription and prevents further deliveries.
func (*Subscription) Done ¶
func (s *Subscription) Done() <-chan struct{}
Done returns a channel that closes once the subscription is fully torn down.
func (*Subscription) MessageCount ¶
func (s *Subscription) MessageCount() uint64
MessageCount reports the total number of messages delivered to the subscription.
func (*Subscription) Unsubscribe ¶
func (s *Subscription) Unsubscribe()
Unsubscribe is an alias for Cancel and preserves semantic clarity for callers.
func (*Subscription) Updates ¶
func (s *Subscription) Updates() <-chan SubscriptionMessage
Updates exposes the buffered stream of subscription messages.
type SubscriptionMessage ¶
type SubscriptionMessage struct {
MessageType int
Data []byte
Decoded any
Received time.Time
Err error
Size int
}
SubscriptionMessage represents a single frame delivered to a subscription consumer.
type SubscriptionOptions ¶
type SubscriptionOptions struct {
// ID can be provided to preassign a human-readable identifier. If empty, WSStat
// allocates an incremental identifier.
ID string
// MessageType and Payload describe the initial frame sent to initiate the subscription.
MessageType int
Payload []byte
// Buffer controls the per-subscription delivery queue length. Zero implies the default.
Buffer int
// contains filtered or unexported fields
}
SubscriptionOptions configures how WSStat establishes and demultiplexes a subscription.
type SubscriptionStats ¶
type SubscriptionStats struct {
FirstEvent time.Duration
LastEvent time.Duration
MessageCount uint64
ByteCount uint64
MeanInterArrival time.Duration
Error error
}
SubscriptionStats snapshots per-subscription metrics for reporting through Result.
type WSStat ¶
type WSStat struct {
// contains filtered or unexported fields
}
WSStat wraps the coder/websocket package with latency measuring capabilities.
Concurrency: DialContext must complete (single-threaded) before any other method is called. After a successful dial, ExtractResult and Close are safe to call concurrently with each other and with the read/write/subscription methods; ExtractResult takes a consistent snapshot even while Close is finalizing the Result. Close is idempotent and may be called from multiple goroutines. WriteMessage/WriteMessageJSON and ReadMessage/ReadMessageJSON/PingPong are not internally serialized against each other: concurrent writers (or concurrent readers) interleave on the shared channels, so callers that need ordering must coordinate it. Subscribe/SubscribeOnce are safe to call concurrently.
func New ¶
New creates and returns a new WSStat instance. To adjust channel buffer size or timeouts, use options. If not provided, package defaults are used for compatibility.
func (*WSStat) Close ¶
func (ws *WSStat) Close()
Close closes the WebSocket connection and cleans up the WSStat instance. Sets result times: CloseDone
func (*WSStat) CloseWith ¶
CloseWith closes the connection sending a chosen close code and reason in the RFC 6455 closing handshake, instead of Close's default StatusNormalClosure (1000) with an empty reason. code must be a sendable close status (1000-1003, 1007-1011, or 3000-4999) and reason at most 123 bytes (the control-frame payload limit minus the 2-byte code); an invalid code or over-long reason returns an error without closing. Returns ErrClosed if the connection is already closing. Otherwise teardown proceeds exactly as Close, which it calls; like Close it is idempotent, but the code/reason only take effect when CloseWith wins the race to initiate the close.
func (*WSStat) DialContext ¶
func (ws *WSStat) DialContext( ctx context.Context, targetURL *url.URL, customHeaders http.Header, ) error
DialContext establishes a new WebSocket connection bound to ctx. Canceling ctx (or calling Close) tears down the connection and unblocks in-flight reads and writes. If required, specify custom headers to merge with the default headers.
A WSStat instance is single-use: after a successful dial (or a Close), DialContext returns an error; create a new instance to reconnect. A failed dial leaves the instance reusable. Sets times: dialStart, wsHandshakeDone
func (*WSStat) ExtractResult ¶
ExtractResult calculate the current results and returns a copy of the Result object. Safe to call concurrently with the read/write/subscription methods and with Close.
func (*WSStat) PingPong ¶
PingPong sends a ping through the WebSocket connection and blocks until the matching pong is received. coder's Ping is a synchronous round-trip, so both the write and read timings are recorded around the single call. Sets result times: MessageReads, MessageWrites
func (*WSStat) PingPongContext ¶ added in v3.2.0
PingPongContext is PingPong with the pong wait additionally bounded by ctx: the round-trip ends at the earliest of ctx's cancellation, the connection's context, and the read timeout. It lets a caller-side deadline or interrupt cut short a ping blocked on an unresponsive peer.
func (*WSStat) ReadMessage ¶
ReadMessage reads a message from the WebSocket connection and measures the round-trip time. If an error occurs, it will be returned. Sets time: MessageReads
func (*WSStat) ReadMessageJSON ¶
ReadMessageJSON reads a message from the WebSocket connection and measures the round-trip time. Sets time: MessageReads
func (*WSStat) Subscribe ¶
func (ws *WSStat) Subscribe(ctx context.Context, opts SubscriptionOptions) (*Subscription, error)
Subscribe registers a long-lived listener using the supplied options and context. The returned Subscription can be used to consume streamed frames until cancellation.
func (*WSStat) SubscribeOnce ¶
func (ws *WSStat) SubscribeOnce( ctx context.Context, opts SubscriptionOptions, ) (SubscriptionMessage, error)
SubscribeOnce registers a subscription and waits for the first delivered message before canceling the subscription. The returned message is a snapshot of the first delivery.
func (*WSStat) WriteMessage ¶
WriteMessage sends a message through the WebSocket connection. Sets time: MessageWrites. A message dropped because the connection is closing records no timing, keeping the write/read ledgers pairable.
func (*WSStat) WriteMessageJSON ¶
WriteMessageJSON sends a message through the WebSocket connection. Sets time: MessageWrites. A message dropped because the connection is closing (or one that fails to marshal) records no timing, keeping the write/read ledgers pairable.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
wsstat
command
Package main implements the wsstat command-line tool for measuring WebSocket connection latency and streaming subscription events.
|
Package main implements the wsstat command-line tool for measuring WebSocket connection latency and streaming subscription events. |
|
Package main provides examples of how to use the wsstat package.
|
Package main provides examples of how to use the wsstat package. |
|
internal
|
|
|
app
Package app provides a high-level client for measuring WebSocket latency and streaming subscription events.
|
Package app provides a high-level client for measuring WebSocket latency and streaming subscription events. |
|
app/color
Package color provides ANSI color support for terminal output.
|
Package color provides ANSI color support for terminal output. |