Skip to content

feat(interstitial): stream live route-setup progress via chunked encoding - #4054

Merged
0pcom merged 1 commit into
skycoin:developfrom
0pcom:feat/interstitial-chunked-progress
Aug 21, 2026
Merged

feat(interstitial): stream live route-setup progress via chunked encoding#4054
0pcom merged 1 commit into
skycoin:developfrom
0pcom:feat/interstitial-chunked-progress

Conversation

@0pcom

@0pcom 0pcom commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Third refinement in the proxy status/interstitial series (after #4050 status pages and #4053 HTTPS+contrast). Makes the route interstitial show live route-setup progress instead of a canned CSS animation + blind meta-refresh.

What changed

New pkg/proxyinterstitial/stream.go: the interstitial can hold the browser connection open and stream progress via HTTP chunked transfer-encoding, flushing a chunk per real attempt, then — once the route is up — flushing a final chunk that reloads the page into the now-live content.

  • StreamConn(ctx, StreamConfig) — a chunked net.Conn for the SOCKS mint path (handed to go-socks5 like the one-shot Conn).
  • DriveStream + StreamOpen/StreamStep/StreamClose — shared progressive-HTML rendering, reused by the reverse-proxy path.
  • A caller-supplied Probe drives a real route-setup attempt; each attempt's real StatusLine is streamed as a progress line; success streams a location.replace() reload.

The event-source seam (investigated first, reported honestly)

The interstitial is minted after a dial has already failed transiently — there's no in-flight setup to subscribe to at that point, and pkg/router exposes no per-hop / noise-handshake progress hook: DialOptions has no observer, and even the mux-telemetry harness only synthesizes coarse RouteEstablished/failed events around its own DialRoutes call. So the granular finding route → 2-hop via <pk> → noise handshake → route group up narrative is not observable today and is not fabricated here. Two real (coarse) sources are used:

  • browse-origin (meshInterstitialRT): it already runs the real cold-route round-trip in the background — the one surface with a genuine in-flight attempt — so the stream renders that attempt's outcome and reloads on success (streamingInterstitialResponse; ReverseProxy.FlushInterval < 0 flushes fragments).
  • SOCKS resolving proxies (dmsgweb / skynetweb): no live attempt at the mint point, so the streamer drives a fresh one via a re-dial Probe (dmsgRedialProbe / skynetRedialProbe) and streams each real attempt.

Missing seam to add later (in pkg/router, owned separately): a DialOptions.OnProgress func(phase) callback invoked by DialRoutes / the cascade setup path as hops resolve and the noise handshake completes — that's what would turn the coarse lines into the granular narrative. Not added here. pkg/skysocks ServeSOCKS5 is also left one-shot (its mint point tears the session down for reconnect — nothing to observe/drive from there).

Fallback

StreamConn reads the request first and serves the existing one-shot meta-refresh page to an HTTP/1.0 client; a nil probe / absent source likewise degrades to the static page. The pre-soft-deadline error paths (browse-origin) remain one-shot.

Liveness tie-in

The probe is the "is-my-connection-up" signal; a future status.* mux view can share it as a WS/WT liveness indicator — noted as an extension point, not built here.

Testing / scope

pkg/proxyinterstitial: streamed success / hard-error / HTTP-1.0-fallback / escaping. pkg/visor: updated the cold meshInterstitialRT test for the streamed body + added a streamed-success test. go build ., go vet, goimports -local clean; touched-package tests pass. Contained to pkg/proxyinterstitial, the two resolving-proxy runtimes, and pkg/visor/meshproxy.go; pkg/router/policy untouched.

Live-validation caveat: the chunked serve + go-socks5 splice and the browse-origin flush path are unit-tested via http.ReadResponse/pipe mechanics, but not exercised against a real browser in this environment — worth a live smoke test (cold-route navigation shows streamed lines then reloads).

…ding

The route interstitial can now hold the browser connection open and stream
live progress via HTTP chunked transfer-encoding, reloading into the live
content once the route is up — instead of a one-shot page + blind meta-refresh.

New pkg/proxyinterstitial/stream.go: StreamConn (chunked net.Conn for the
SOCKS path) + DriveStream + StreamOpen/StreamStep/StreamClose (shared
progressive-HTML rendering). A caller-supplied Probe drives a real route-setup
attempt; each attempt's real StatusLine is flushed as a progress line, and on
success a final chunk reloads the page. HTTP/1.0 clients fall back to the
existing one-shot meta-refresh page.

Wiring (only real signals; no fabricated per-hop events):
- browse-origin (meshInterstitialRT) streams the SAME detached round-trip it
  already runs — the one surface with a genuine in-flight attempt to observe —
  reloading on success; ReverseProxy FlushInterval<0 flushes each fragment.
- dmsgweb / skynetweb SOCKS plaintext path drive a fresh dial via a re-dial
  Probe (dmsgRedialProbe / skynetRedialProbe) and stream each attempt.

Seam reported (not built here): the granular "2-hop via <pk> -> noise
handshake -> route group up" narrative needs a router-side progress callback
(DialOptions has no observer; the mux harness only synthesizes coarse events
around its own DialRoutes). pkg/skysocks ServeSOCKS5 is left one-shot (its
mint point tears the session down with nothing to observe). Details in
docs/proxy-status-and-interstitial.md.

Tests: streaming success/hard-error/HTTP1.0-fallback/escaping for the
primitive; updated the cold meshInterstitialRT test + a streamed-success test.
@0pcom
0pcom merged commit 82ee557 into skycoin:develop Aug 21, 2026
11 of 16 checks passed
@0pcom
0pcom deleted the feat/interstitial-chunked-progress branch August 24, 2026 23:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant