fix(extension-server): deterministic tunnel-offline 503 on the user path for offline connectors#207
Merged
Merged
Conversation
…line connector When a Connector is offline, NSO still emits its backendRef, so Envoy Gateway programs an endpoint-less cluster for the connector backend. The ext-server's offline handling only prepended a connect_matcher route (503 "Tunnel not online"), which matches CONNECT (tunnel-control) clients only. A normal user GET fell through to the prefix:"/" route, still targeting the zero-endpoint cluster, so Envoy returned a generic 503 no_healthy_upstream (UH) plus retry/cluster-stat noise instead of a deterministic tunnel-offline response. ApplyConnectorRoutes now, in the offline branch, also rewrites the user-facing forwarding route(s) that target the offline connector cluster into an immediate direct_response 503, reusing the same "Tunnel not online" body. Only the route's Action oneof is replaced, so each route's match, metadata, and typed_per_filter_config are preserved. The existing connect_matcher offline route is still prepended for CONNECT clients. The online (replaced) path is unchanged. Idempotent: converted routes become direct_responses, so routeCluster returns "" for them and they never re-match the connector cluster. Surfaces a new counter (nso_extension_connector_offline_routes_total) and a "connector_offline_routes" field on the PostTranslateModify log line. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove step-by-step narration; keep the higher-level rationale (route to a deterministic 503 instead of an endpoint-less cluster, action-only replace preserves match/metadata, idempotent because direct_responses carry no cluster). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dbf8854 to
cf70fa4
Compare
mattdjenkinson
approved these changes
Jun 19, 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.
What a visitor experiences
A visitor reaching a proxy whose Connector is offline gets a generic routed-to-nothing
503 no_healthy_upstream— Envoy's "I had nowhere to send this" — instead of a deliberate tunnel-offline response.Why: when a Connector is offline, NSO still emits its backendRef, so Envoy Gateway programs a backend cluster with zero endpoints. The ext-server's offline handling only added a route for CONNECT (tunnel-control) clients, so a normal browser GET fell through to the catch-all
/route, hit that empty cluster, and produced the generic 503 plus empty-cluster side effects (upstream-health flags, retries, cluster-stat noise).This makes the offline experience intentional: user traffic returns a deterministic tunnel-offline 503 and never reaches an endpoint-less cluster.
What changed
ApplyConnectorRoutes, user-facing forwarding route(s) targeting the offline connector cluster are rewritten to adirect_response503 ("Tunnel not online") instead of routing to the empty cluster.connect_matcheroffline route is still prepended for CONNECT clients.match,metadata, andtyped_per_filter_configare preserved.nso_extension_connector_offline_routes_total) and aconnector_offline_routesfield on thePostTranslateModifylog line.Notes
direct_response, so re-running never double-applies.Related
Validation
gofmtclean ·go build ./...·go vet ./internal/extensionserver/...·go test ./internal/extensionserver/...all pass.golangci-lintcould not run (installed binary built with go1.25; project targets go1.26.4).🤖 Generated with Claude Code