Repository navigation
fix(gateway-interceptors): configure connect timeout and HTTP/2 keepalive on interceptor gRPC channel #2612
Description
Agent Diagnostic
- Skills loaded:
create-github-issue - OpenShell version tested:
mainat83284129 - Latest release checked: tested against current
main, which is ahead of the latest release - Known fixes reviewed: reviewed fix(supervisor-middleware): configure HTTP/2 keepalive on the middleware gRPC channel #2474 (middleware channel keepalive), which explicitly defers this file in its Proposed Fix section
- Possible duplicates reviewed: yes — searched
keepalive/connect_timeout/gateway-interceptors; no dedicated issue exists for the interceptor channel. fix(supervisor-middleware): configure HTTP/2 keepalive on the middleware gRPC channel #2474 covers the supervisor-middleware channel and only mentions this file as a follow-up - Findings:
crates/openshell-gateway-interceptors/src/plan.rs:860-872connect_endpointbuilds a bareEndpoint::from_shared(endpoint).connect(). It sets noconnect_timeout, no HTTP/2 keepalive, no TCP keepalive, no idle timeout.- The unix-socket branch
connect_unix_endpointatcrates/openshell-gateway-interceptors/src/plan.rs:874-884is likewise bare. - These channels are long-lived:
ExecutionPlan::load(plan.rs:186) dials once at startup and the resultingChannelis cloned into everyBindingPlanandGatewayInterceptorProfileSource, reused for the process lifetime. - Every other long-lived gRPC client in the repo already configures keepalive. Canonical helper
standard_endpointatcrates/openshell-sdk/src/transport.rs:31-39; most complete variantcrates/openshell-core/src/grpc_client.rs:139-145(http2_keep_alive_interval(10s),keep_alive_while_idle(true),keep_alive_timeout(20s),http2_adaptive_window(true)). The interceptor channel is an outlier. - tonic resolves to 0.14.6 (
Cargo.lock); all keepalive setters plusconnect_timeoutare available onEndpoint.
- Remaining reason for filing: interceptors sit at the gateway gRPC routing boundary and run on the hot path of intercepted RPCs. A stale idle channel or an unreachable interceptor host degrades every affected RPC, and with no
connect_timeoutthe initial dial can hang far longer than intended.
Description
Actual behavior: connect_endpoint in crates/openshell-gateway-interceptors/src/plan.rs establishes the interceptor gRPC channel with no connect timeout and no keepalive. An idle channel is not kept alive, so an intermediary idle-reap, a load-balancer timeout, an interceptor redeploy, or a GOAWAY silently invalidates the connection; the failure only surfaces on the next interceptor evaluation. With no connect_timeout, an unreachable interceptor host at startup or reconnect can hang on the OS default TCP connect timeout.
Expected behavior: the interceptor channel follows the same connect-timeout and HTTP/2 keepalive configuration as every other long-lived gRPC client in the repo, so idle connections survive intermediary idle timeouts, dead peers are detected proactively, and dials are bounded.
This is the follow-up deferred by #2474, which fixed the supervisor-middleware channel and noted: "Consider whether crates/openshell-gateway-interceptors/src/plan.rs should be fixed in the same change. It builds a bare Endpoint::from_shared(...).connect() with no keepalive and no connect timeout at all, so it has the same gap plus a missing connect bound."
Reproduction Steps
- Configure a gateway interceptor (
[[...interceptor]]) pointing at an external gRPC interceptor service. - Place any idle-reaping hop between the gateway and the interceptor service, or restart the interceptor service.
- Leave the interceptor channel idle past that idle timeout without an intercepted RPC.
- Issue an RPC that matches an interceptor binding. The evaluation runs on a stale connection and fails, subject to the binding's
FailurePolicy.
Observable without an intermediary by inspecting the constructed Endpoint: no keepalive is set, so no HTTP/2 PING frames are ever emitted on an idle interceptor channel, and no connect_timeout bounds the dial.
Environment
- OS: macOS (darwin 25.5.0)
- OpenShell:
mainat83284129 - tonic: 0.14.6
- Latest release checked: yes — tested against current
main, ahead of latest release - Possible duplicates checked: yes — related to fix(supervisor-middleware): configure HTTP/2 keepalive on the middleware gRPC channel #2474, no dedicated issue for this file
Proposed Fix
Apply the repo-standard channel configuration in connect_endpoint (and the unix branch), matching crates/openshell-core/src/grpc_client.rs:139-145:
connect_timeout(bounded dial; pick a value consistent with the repo's other clients / the middleware channel's 5s)http2_keep_alive_interval(10s)keep_alive_while_idle(true)keep_alive_timeout(20s)http2_adaptive_window(true)
Prefer routing this through the existing canonical helper (standard_endpoint in openshell-sdk or the openshell-core variant) rather than open-coding the setters, so the interceptor channel stays consistent with the rest of the repo. These are platform constants, not operator-tunable config.
Testing
- Assert the constructed endpoint carries the expected connect-timeout and keepalive settings, following whatever pattern
openshell-core/openshell-sdkalready use. - Verify an interceptor evaluation succeeds against a service that has been idle past a configured intermediary idle timeout.
Activity
letv1nnn commentedon Aug 4, 2026