echo

command
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jul 13, 2026 License: MIT Imports: 13 Imported by: 0

README

echo

A minimal, runnable demonstration of the gateway package: a WebSocket echo endpoint plus an ordinary HTTP handler that delivers a message to a registered WebSocket connection - the "HTTP handler talks to a websocket connection" pattern the whole package exists for.

Run it

go run ./examples/echo

This starts an HTTP server on http://127.0.0.1:8080 with three endpoints:

  • GET /ws?id=<connection-id> - upgrades to a WebSocket connection and echoes back whatever the client sends.
  • GET /send?id=<connection-id>&msg=<text> - an ordinary HTTP handler that delivers msg to the WebSocket connection registered under id, via Registry.SendToConnection.
  • GET /healthz - returns HTTP 204 for Kubernetes liveness and readiness probes.

The process accepts SIGINT and SIGTERM. Shutdown uses a 30 second deadline and drains the WebSocket handler through Server.Shutdown before the process exits.

Default behavior worth knowing before you connect

NewWSHandler ships opinionated defaults; this sample changes none of them, so what you see below is the library's out-of-the-box behavior, not something main.go configured:

  • Text frames only. WithWSOnMessage fires for websocket.MessageText frames (WithWSMessageType default). A client sending binary frames is rejected by the underlying coder/websocket library before your handler ever sees it.
  • Origin check: same-Host only. A browser always sends an Origin header, and the default policy (no WithWSOriginPatterns) accepts it only when it matches the request's own Host. Non-browser clients (websocat, curl, wscat, this sample's browser console snippet run from file:// or a different port) send no Origin header at all and are exempt from the check - see authorizeOrigin in ws.go. Cross- origin browser pages need WithWSOriginPatterns or WithWSInsecureSkipOriginCheck, neither of which this sample sets.
  • Ping/pong keepalive. Every 30s (WithWSPingInterval default) the handler pings the socket and expects a pong within 10s, so a half-open connection (client vanished without a close frame - the common case on mobile networks) is dropped instead of leaking forever. You won't observe this on a short-lived local test; it matters once connections live for minutes.
  • Takeover on reconnect. Registering the same id twice does not fail: the new connection replaces the old one (WithWSReplaceExisting, on by default) and the old socket is closed. Reconnect with ?id=alice while a previous alice connection is still open to see the first one close.

Try it

In one terminal, connect a WebSocket client. Any client works - websocat is used here because it is a single static binary; wscat (npx wscat -c ...) is an equivalent Node-based alternative:

websocat ws://127.0.0.1:8080/ws?id=alice

Typing into that connection echoes back immediately (the /ws handler's own OnMessage callback). In another terminal, push a message to the same connection from plain HTTP instead:

curl "http://127.0.0.1:8080/send?id=alice&msg=hello-from-http"

The text appears in the WebSocket client. That single curl call is Registry.SendToConnection taking its local fast path: since the connection is held by this same process, the payload is written directly to the socket - no actor mailbox, no cluster lookup.

Browser console alternative (no extra tools required - open any page served from http://127.0.0.1:8080, or any other origin since this sample sets no origin restriction and non-browser-style requests are exempt; a page's own fetch/WebSocket calls do carry Origin, so if you instead open the console on a page served from a different origin than 127.0.0.1:8080, the upgrade is rejected with HTTP 403 unless you widen WithWSOriginPatterns):

const ws = new WebSocket("ws://127.0.0.1:8080/ws?id=alice");
ws.onmessage = (e) => console.log("received:", e.data);
ws.onopen = () => ws.send("ping");

Success looks like: the terminal running go run ./examples/echo logs connection "alice" joined; typing in the WebSocket client echoes the same text back; the curl /send command's message appears in the WebSocket client without you typing anything; closing the client (or reconnecting with the same id) logs connection "alice" left.

What this sample does not show: cross-instance delivery

This sample runs a single, non-clustered actor system, so Registry.SendToConnection always takes the local, direct-write path shown above. The other half of the two-tier delivery model - resolving a connection held by another node through the cluster-aware actor directory and delivering to it remotely - needs a real multi-node cluster to demonstrate honestly, which is more infrastructure than a sample should carry.

That path is exercised end-to-end by the module's own test suite instead:

go test -race -run TestGatewayMultiNodesSendToConnection .
go test -race -run TestGatewayMultiNodesBroadcast .

To turn this sample into a real multi-node one, replace the single actor.NewActorSystem call with actor.WithCluster(...) (see any GoAkt discovery provider), run two copies of this binary with different --addr/ports pointed at the same discovery backend, and register a connection against one instance's /ws while calling /send against the other.

Documentation

Overview

Command echo is a minimal, runnable demonstration of the gateway package:

  • A WebSocket echo endpoint (/ws?id=<connection-id>) registered in a gateway.Registry.
  • An ordinary HTTP handler (/send?id=<connection-id>&msg=<text>) that delivers a message to a registered connection via Registry.SendToConnection - the "HTTP handler talks to a websocket connection" pattern that motivates the whole package.

This sample runs a single, non-clustered actor system so it has no external dependencies (no discovery backend to stand up). Registry.SendToConnection's local fast path (direct socket write, no actor/cluster machinery) is exactly what runs here regardless of cluster size; see README.md for how to extend this sample into a real multi-node one.

Jump to

Keyboard shortcuts

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