feat(interstitial): stream live route-setup progress via chunked encoding - #4054
Merged
Merged
Conversation
…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.
This was referenced Aug 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 chunkednet.Connfor the SOCKS mint path (handed to go-socks5 like the one-shotConn).DriveStream+StreamOpen/StreamStep/StreamClose— shared progressive-HTML rendering, reused by the reverse-proxy path.Probedrives a real route-setup attempt; each attempt's realStatusLineis streamed as a progress line; success streams alocation.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/routerexposes no per-hop / noise-handshake progress hook:DialOptionshas no observer, and even the mux-telemetry harness only synthesizes coarseRouteEstablished/failed events around its ownDialRoutescall. So the granularfinding route → 2-hop via <pk> → noise handshake → route group upnarrative is not observable today and is not fabricated here. Two real (coarse) sources are used: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 < 0flushes fragments).dmsgweb/skynetweb): no live attempt at the mint point, so the streamer drives a fresh one via a re-dialProbe(dmsgRedialProbe/skynetRedialProbe) and streams each real attempt.Missing seam to add later (in
pkg/router, owned separately): aDialOptions.OnProgress func(phase)callback invoked byDialRoutes/ 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/skysocksServeSOCKS5is also left one-shot (its mint point tears the session down for reconnect — nothing to observe/drive from there).Fallback
StreamConnreads 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 coldmeshInterstitialRTtest for the streamed body + added a streamed-success test.go build .,go vet,goimports -localclean; touched-package tests pass. Contained topkg/proxyinterstitial, the two resolving-proxy runtimes, andpkg/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).