wsclient

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: May 19, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package wsclient is the aspect-and-Outpost-side WebSocket client. Handles dial + auth + reconnect + framed send/receive + correlation- id tracking for request/response pairs. Used by the aspect runtime in part 3 and the Outpost in part 6.

Design: one Client per logical connection. Run() blocks until ctx is cancelled, transparently reconnecting on drop. Callers send frames via Send (fire-and-forget) or Request (awaits correlated response). Incoming frames with no pending correlation are delivered via the Handler callback.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a persistent WS connection that handles reconnection and request/response correlation. Safe for concurrent use of Send and Request after Run has started.

func New

func New(cfg Config) (*Client, error)

New constructs a Client.

func (*Client) Connected

func (c *Client) Connected() bool

Connected reports whether the client currently has an active WS. Useful for tests and observability.

func (*Client) Events

func (c *Client) Events() <-chan ConnectEvent

Events returns the connect-event channel. Subscribers get a ConnectEvent on every dial-success and every disconnect. The channel is shared (single channel, not fan-out) — only one consumer per Client; that consumer must read promptly or events get dropped. Use this instead of polling Connected() for register-on-connect flows (#29).

func (*Client) Request

func (c *Client) Request(ctx context.Context, env frames.Envelope) (frames.Envelope, error)

Request sends a frame and waits for the correlated response. The frame must be a NewRequest-shaped envelope (non-empty ID). Returns the response envelope or an error (timeout via ctx, or disconnect).

func (*Client) Run

func (c *Client) Run(ctx context.Context) error

Run drives the dial+serve+reconnect loop. Blocks until ctx done. Returns the first-connect error if FailFirstConnect is true and the initial dial fails; otherwise always returns ctx.Err() on exit.

Backoff: on dial failure (never connected), exponential backoff applies. On read-loop failure (connection was established then dropped), backoff resets to MinReconnectDelay — a sleep gap or transient network flap shouldn't permanently accumulate delay.

func (*Client) Send

func (c *Client) Send(ctx context.Context, env frames.Envelope) error

Send writes a frame, fire-and-forget. Waits for connection if currently reconnecting. Blocking is bounded by ctx.

type Config

type Config struct {
	// URL is the WS endpoint to dial, e.g. wss://nexus.host:7888/connect.
	URL string

	// AuthToken is sent as Authorization: Bearer on the upgrade.
	// When TokenProvider is set, AuthToken is the fallback used if
	// TokenProvider returns an error. In the common JWT case set
	// AuthToken to the initial token and wire TokenProvider to return
	// a fresh token on each reconnect.
	AuthToken string

	// TokenProvider, when set, is called before each dial attempt to
	// obtain a fresh auth token. The returned string is used instead
	// of Config.AuthToken for this dial. If TokenProvider returns an
	// error, AuthToken is used as a fallback (retaining whatever was
	// previously set). Callers with JWT-based auth should wire a
	// TokenProvider that re-validates against the keyfile endpoint.
	// When nil, AuthToken is used directly (backward compatible).
	TokenProvider func(ctx context.Context) (string, error)

	// Handler receives uncorrelated incoming frames. Required.
	Handler Handler

	// Logger is optional; nil falls back to slog.Default().
	Logger *slog.Logger

	// FailFirstConnect, when true, causes Run to return an error if
	// the initial dial fails. Aspects with NEXUS_OUTPOST set use this
	// to fail-loudly per transport spec §3.5. Aspects doing a pure
	// reconnect (no explicit outpost override) set false.
	FailFirstConnect bool

	// MinReconnectDelay / MaxReconnectDelay bound the exponential
	// backoff on reconnect. Defaults: 1s → 60s.
	MinReconnectDelay time.Duration
	MaxReconnectDelay time.Duration
}

Config configures a Client.

type ConnectEvent

type ConnectEvent struct {
	Connected bool
}

ConnectEvent is delivered on the channel returned by Events() each time the connection transitions. Connected=true means a fresh dial succeeded; Connected=false means the active connection dropped. Pre-#29 callers polled Connected() on a 50ms tick — wasteful and missed mid-tick disconnects so a reconnect didn't trigger re-register.

type Handler

type Handler interface {
	Handle(env frames.Envelope)
}

Handler processes incoming frames that aren't correlated to a pending Request. The client calls Handle from its read goroutine; handlers should not block.

type HandlerFunc

type HandlerFunc func(env frames.Envelope)

HandlerFunc adapts a plain function to the Handler interface.

func (HandlerFunc) Handle

func (f HandlerFunc) Handle(env frames.Envelope)

Handle implements Handler.

Jump to

Keyboard shortcuts

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