app

package
v3.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 29, 2026 License: MIT Imports: 27 Imported by: 0

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

View Source
const JSONSchemaVersion = "1.0"

JSONSchemaVersion is the schema version for JSON output

Variables

This section is empty.

Functions

func EmitJSONError

func EmitJSONError(w io.Writer, err error) error

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.

const (
	// BodyAuto renders payloads pretty / multi-line.
	BodyAuto Body = "auto"
	// BodyCompact renders one line per message.
	BodyCompact Body = "compact"
)

func ParseBody

func ParseBody(s string) (Body, error)

ParseBody normalizes and validates a body-rendering string. An empty string defaults to BodyAuto.

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 NewClient

func NewClient(opts ...Option) *Client

NewClient creates a new Client with the given options.

func (*Client) Body

func (c *Client) Body() Body

Body returns the configured body rendering.

func (*Client) ColorMode

func (c *Client) ColorMode() string

ColorMode returns the configured color mode.

func (*Client) Count

func (c *Client) Count() int

Count returns the configured interaction count.

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) Once

func (c *Client) Once() bool

Once reports whether stream mode exits after the first event.

func (*Client) Output

func (c *Client) Output() Output

Output returns the configured output contract.

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) Quiet

func (c *Client) Quiet() bool

Quiet returns whether quiet mode is enabled.

func (*Client) RPCMethod

func (c *Client) RPCMethod() string

RPCMethod returns the configured RPC method.

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

func (c *Client) ResponseFilePath() string

ResponseFilePath returns the configured response-recording file path ("" when disabled).

func (*Client) SetResponseSink added in v3.1.0

func (c *Client) SetResponseSink(w io.Writer)

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

func (c *Client) StreamSubscription(ctx context.Context, target *url.URL) error

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

func (c *Client) StreamSubscriptionOnce(ctx context.Context, target *url.URL) error

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

func (c *Client) Validate() error

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

func (c *Client) VerbosityLevel() int

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 Mode

type Mode int

Mode is the operation the client performs.

const (
	// ModeMeasure measures connection latency.
	ModeMeasure Mode = iota
	// ModeStream streams subscription events.
	ModeStream
)

type Option

type Option func(*Client)

Option configures a Client.

func WithBodyRender

func WithBodyRender(body Body) Option

WithBodyRender sets the body rendering for text output (auto or compact).

func WithBuffer

func WithBuffer(size int) Option

WithBuffer sets the subscription delivery buffer size.

func WithClip

func WithClip(clip bool) Option

WithClip enables clipping each rendered line to terminal width (text output).

func WithCloseGrace

func WithCloseGrace(d time.Duration) Option

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

func WithColorMode(mode string) Option

WithColorMode sets color output behavior (auto, always, or never).

func WithCount

func WithCount(n int) Option

WithCount sets the number of measurement interactions.

func WithDebug

func WithDebug(enabled bool) Option

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

func WithDebugWriter(w io.Writer) Option

WithDebugWriter sets the destination for debug logs (default os.Stderr). Mainly for tests.

func WithHeaders

func WithHeaders(headers []string) Option

WithHeaders sets custom HTTP headers for the WebSocket handshake.

func WithInsecure

func WithInsecure(insecure bool) Option

WithInsecure configures whether to skip TLS certificate verification.

func WithMode

func WithMode(mode Mode) Option

WithMode sets the operation mode (measure or stream).

func WithOutput

func WithOutput(output Output) Option

WithOutput sets the whole-stdout contract (text, json, or raw).

func WithQuiet

func WithQuiet(quiet bool) Option

WithQuiet suppresses non-essential output.

func WithRPCMethod

func WithRPCMethod(method string) Option

WithRPCMethod configures JSON-RPC method to send.

func WithRPCVersion

func WithRPCVersion(version string) Option

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

func WithReadLimit(n int64) Option

WithReadLimit sets the max inbound message size in bytes. Zero uses the library default (16 MiB); a negative value disables the limit.

func WithResolves

func WithResolves(resolves map[string]string) Option

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

func WithResponseFile(path string) Option

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

func WithShowSecrets(show bool) Option

WithShowSecrets renders sensitive header values (Authorization, Cookie, etc.) in -vv output instead of masking them. Off by default.

func WithStreamOnce

func WithStreamOnce(once bool) Option

WithStreamOnce makes stream mode exit after the first event.

func WithSubprotocols

func WithSubprotocols(subprotocols []string) Option

WithSubprotocols sets the WebSocket subprotocols to offer during the handshake.

func WithSummaryInterval

func WithSummaryInterval(interval time.Duration) Option

WithSummaryInterval sets the subscription summary print interval.

func WithTextMessage

func WithTextMessage(msg string) Option

WithTextMessage configures a text message to send.

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets the read/dial timeout. Zero uses the library default (5s).

func WithValidateUTF8

func WithValidateUTF8(enabled bool) Option

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

func WithVerbosity(level int) Option

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.

const (
	// OutputText is human output, governed by Body/Clip/verbosity.
	OutputText Output = "text"
	// OutputJSON is schema-stable, newline-delimited JSON envelopes.
	OutputJSON Output = "json"
	// OutputRaw is verbatim payload bytes with nothing added.
	OutputRaw Output = "raw"
)

func ParseOutput

func ParseOutput(s string) (Output, error)

ParseOutput normalizes and validates an output contract string. An empty string defaults to OutputText.

Directories

Path Synopsis
Package color provides ANSI color support for terminal output.
Package color provides ANSI color support for terminal output.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL