Documentation
¶
Overview ¶
Package app provides a high-level client for measuring WebSocket latency and streaming subscription events. It builds on the wsstat package with configuration management, output formatting (terminal and JSON), and subscription handling.
Basic Usage ¶
Create a client with functional options and measure latency:
client := app.NewClient(
app.WithTextMessage("ping"),
app.WithCount(5),
)
if err := client.Validate(); err != nil {
log.Fatal(err)
}
ctx := context.Background()
result, err := client.MeasureLatency(ctx, targetURL)
if err != nil {
log.Fatal(err)
}
client.PrintTimingResults(targetURL, result)
Subscription Mode ¶
Stream events from a WebSocket server:
client := app.NewClient(
app.WithMode(app.ModeStream),
app.WithCount(10),
app.WithTextMessage("subscribe"),
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := client.StreamSubscription(ctx, targetURL); err != nil {
log.Fatal(err)
}
Index ¶
- Constants
- func EmitJSONError(w io.Writer, err error) error
- type Body
- type Client
- func (c *Client) Body() Body
- func (c *Client) ColorMode() string
- func (c *Client) Count() int
- func (c *Client) MeasureLatency(ctx context.Context, target *url.URL) (*MeasurementResult, error)
- func (c *Client) Once() bool
- func (c *Client) Output() Output
- func (c *Client) PrintRequestDetails(result *MeasurementResult) error
- func (c *Client) PrintResponse(result *MeasurementResult) error
- func (c *Client) PrintTimingResults(u *url.URL, result *MeasurementResult) error
- func (c *Client) Quiet() bool
- func (c *Client) RPCMethod() string
- func (c *Client) RecordResponse(result *MeasurementResult) error
- func (c *Client) ResponseFilePath() string
- func (c *Client) SetResponseSink(w io.Writer)
- func (c *Client) StreamSubscription(ctx context.Context, target *url.URL) error
- func (c *Client) StreamSubscriptionOnce(ctx context.Context, target *url.URL) error
- func (c *Client) Validate() error
- func (c *Client) VerbosityLevel() int
- type MeasurementResult
- type Mode
- type Option
- func WithBodyRender(body Body) Option
- func WithBuffer(size int) Option
- func WithClip(clip bool) Option
- func WithCloseGrace(d time.Duration) Option
- func WithColorMode(mode string) Option
- func WithCount(n int) Option
- func WithDebug(enabled bool) Option
- func WithDebugWriter(w io.Writer) Option
- func WithHeaders(headers []string) Option
- func WithInsecure(insecure bool) Option
- func WithMode(mode Mode) Option
- func WithOutput(output Output) Option
- func WithQuiet(quiet bool) Option
- func WithRPCMethod(method string) Option
- func WithRPCVersion(version string) Option
- func WithReadLimit(n int64) Option
- func WithResolves(resolves map[string]string) Option
- func WithResponseFile(path string) Option
- func WithShowSecrets(show bool) Option
- func WithStreamOnce(once bool) Option
- func WithSubprotocols(subprotocols []string) Option
- func WithSummaryInterval(interval time.Duration) Option
- func WithTextMessage(msg string) Option
- func WithTimeout(d time.Duration) Option
- func WithValidateUTF8(enabled bool) Option
- func WithVerbosity(level int) Option
- type Output
Constants ¶
const JSONSchemaVersion = "1.0"
JSONSchemaVersion is the schema version for JSON output
Variables ¶
This section is empty.
Functions ¶
func EmitJSONError ¶
EmitJSONError writes a structured error envelope (type "error") to w, terminated by a newline to match the NDJSON data stream. The CLI calls this on the runtime-failure path under the JSON output contract; the envelope goes to stdout alongside any data already streamed, so consumers parsing the stream see the failure as one final record.
Types ¶
type Body ¶
type Body string
Body is the human rendering of a payload body. It applies only to text output and governs both streamed messages and the measured response.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client measures the latency of a WebSocket connection and manages subscription streams. Use NewClient with functional options to create and configure a Client.
Fields are private; use accessor methods (Count(), Output(), etc.) to read configuration, or use MeasureLatency's return value to access results.
func (*Client) MeasureLatency ¶
func (c *Client) MeasureLatency( ctx context.Context, target *url.URL, ) (*MeasurementResult, error)
MeasureLatency measures WebSocket connection latency using ping, text, or JSON-RPC messages based on client configuration. Returns timing results and the server response.
The measurement method is determined by client settings:
- If textMessage is set: sends text messages and measures echo latency
- If rpcMethod is set: sends JSON-RPC requests and measures response latency
- Otherwise: sends WebSocket ping frames and measures pong latency
The context can be used to cancel the measurement operation.
func (*Client) PrintRequestDetails ¶
func (c *Client) PrintRequestDetails(result *MeasurementResult) error
PrintRequestDetails prints connection and request information to stdout. Output verbosity is controlled by client configuration:
- verbosity 0: URL and IP only
- verbosity 1: adds target summary, message count, TLS version
- verbosity 2+: adds full TLS details, certificates, and all headers
In JSON format mode, this outputs nothing (details are in timing JSON). Returns an error if result is nil or contains no result data.
func (*Client) PrintResponse ¶
func (c *Client) PrintResponse(result *MeasurementResult) error
PrintResponse prints the WebSocket server response to stdout, returning any write error so a failed or closed stdout propagates as a non-zero exit. Output depends on the configured output contract:
- json: structured JSON envelope with schema_version
- raw: payload bytes verbatim, no label/color/newline (structured JSON-RPC responses are re-marshaled compactly, since the original frame bytes are decoded before reaching this layer)
- text: "Response:" label; any JSON body (JSON-RPC or plain) rendered per --body (auto pretty, compact one-line), non-JSON printed as-is
Does nothing if result is nil or result.Response is nil.
func (*Client) PrintTimingResults ¶
func (c *Client) PrintTimingResults(u *url.URL, result *MeasurementResult) error
PrintTimingResults prints connection timing statistics to stdout. Output format depends on client configuration:
- JSON mode: outputs structured timing JSON with schema_version
- verbosity 0: outputs simple round-trip time and total
- verbosity 1+: outputs detailed timing diagram showing all phases
Returns an error if result is nil or contains no result data.
func (*Client) RecordResponse ¶ added in v3.1.0
func (c *Client) RecordResponse(result *MeasurementResult) error
RecordResponse appends a measured response payload to the response sink (see recordResponse). It is a no-op when no sink is configured or the result carries no response (e.g. ping-mode measure), so a measure run with --file but no payload simply produces an empty file.
func (*Client) ResponseFilePath ¶ added in v3.1.0
ResponseFilePath returns the configured response-recording file path ("" when disabled).
func (*Client) SetResponseSink ¶ added in v3.1.0
SetResponseSink injects the writer that records response payloads. It is a setter rather than a construction-time option because the sink is an open file whose lifecycle (open and close) is owned by the caller; the caller opens the file, calls SetResponseSink, and defers Close. A nil writer disables recording.
func (*Client) StreamSubscription ¶
StreamSubscription establishes a WebSocket connection and streams events from the server. Events are printed as they arrive. The stream continues until:
- The configured message count is reached (if count > 0), or
- The context is canceled (if count == 0 for unlimited), or
- The server closes the connection
If summaryInterval is configured, periodic subscription summaries are printed. Use context cancellation for graceful shutdown.
func (*Client) StreamSubscriptionOnce ¶
StreamSubscriptionOnce establishes a WebSocket connection, receives exactly one event, prints it, and exits. This is equivalent to StreamSubscription with count=1, but optimized for the single-message case.
Validation ensures count equals 1 when using this mode.
func (*Client) Validate ¶
Validate checks client configuration for validity and applies defaults. It must be called after construction and before MeasureLatency or subscription methods.
Validation includes:
- Ensures count is non-negative
- Verifies text and rpc-method are not both set (mutually exclusive)
- Normalizes the output ("text"/"json"/"raw") and body ("auto"/"compact") enums
- Validates colorMode is "auto", "always", or "never"
- Ensures buffer and summaryInterval are non-negative
Mode-specific count bounds are validated by the caller (cmd) before construction; this method only applies the measure-mode default (count=1 when unset).
func (*Client) VerbosityLevel ¶
VerbosityLevel returns the configured verbosity level.
type MeasurementResult ¶
type MeasurementResult struct {
Result *wsstat.Result // Timing and connection details
Response any // Response payload (type varies by message type)
}
MeasurementResult holds the outcome of a WebSocket measurement operation
type Option ¶
type Option func(*Client)
Option configures a Client.
func WithBodyRender ¶
WithBodyRender sets the body rendering for text output (auto or compact).
func WithBuffer ¶
WithBuffer sets the subscription delivery buffer size.
func WithCloseGrace ¶
WithCloseGrace sets the close-handshake echo wait. Zero uses the library default (3s). Values of 5s or more have no effect; the transport caps its close handshake at 5s.
func WithColorMode ¶
WithColorMode sets color output behavior (auto, always, or never).
func WithDebug ¶
WithDebug enables the core's zerolog debug logs, written to stderr (or the WithDebugWriter destination). It is independent of the -v/-vv output verbosity: it never touches the stdout output contract, so it is safe to combine with any -o mode or -q.
func WithDebugWriter ¶
WithDebugWriter sets the destination for debug logs (default os.Stderr). Mainly for tests.
func WithHeaders ¶
WithHeaders sets custom HTTP headers for the WebSocket handshake.
func WithInsecure ¶
WithInsecure configures whether to skip TLS certificate verification.
func WithOutput ¶
WithOutput sets the whole-stdout contract (text, json, or raw).
func WithRPCMethod ¶
WithRPCMethod configures JSON-RPC method to send.
func WithRPCVersion ¶
WithRPCVersion sets the JSON-RPC version to speak: "2.0" (default) or "1.0". 1.0 emits a version-less request with a positional params array and relaxes response decoding to accept 1.0 / version-less replies.
func WithReadLimit ¶
WithReadLimit sets the max inbound message size in bytes. Zero uses the library default (16 MiB); a negative value disables the limit.
func WithResolves ¶
WithResolves sets DNS resolution overrides for specific host:port combinations. Map key format: "host:port", value: "ip_address".
func WithResponseFile ¶ added in v3.1.0
WithResponseFile sets the path of a file that records response payloads as NDJSON, one per line. It is additive and orthogonal to the stdout output contract: setting it only configures the path. The caller opens the file and injects the writer via SetResponseSink. An empty path disables recording.
func WithShowSecrets ¶
WithShowSecrets renders sensitive header values (Authorization, Cookie, etc.) in -vv output instead of masking them. Off by default.
func WithStreamOnce ¶
WithStreamOnce makes stream mode exit after the first event.
func WithSubprotocols ¶
WithSubprotocols sets the WebSocket subprotocols to offer during the handshake.
func WithSummaryInterval ¶
WithSummaryInterval sets the subscription summary print interval.
func WithTextMessage ¶
WithTextMessage configures a text message to send.
func WithTimeout ¶
WithTimeout sets the read/dial timeout. Zero uses the library default (5s).
func WithValidateUTF8 ¶
WithValidateUTF8 enables UTF-8 validation of inbound text frames. Invalid frames are counted and surfaced as a warning rather than failing the connection.
func WithVerbosity ¶
WithVerbosity sets the verbosity level (0 = summary, 1 = extended, 2+ = full).
type Output ¶
type Output string
Output is the whole-stdout contract. It controls what stdout *is*, not how a single payload body is rendered.
func ParseOutput ¶
ParseOutput normalizes and validates an output contract string. An empty string defaults to OutputText.