feat(proxy): HTTP/2 and gRPC support through TLS interception#34
Conversation
Advertise h2 in ALPN on the client-facing TLS connection so gRPC clients (and any other h2 client) can negotiate HTTP/2 through the CONNECT intercept path. When h2 is negotiated, http2.ConfigureServer wires up correct h2 framing on the inner http.Server and the upstream transport switches to http2.Transport; HTTP/1.1 clients continue to use the original http.Transport unchanged. Credential injection works identically on h2 connections. Also adds black-box test coverage for previously uncovered proxy paths (LLM gateway policy, response transformers, token substitution, tunnel mode) and two new multiHandler slog tests in gatekeeper_test.go.
📦 Previous Review (superseded by newer review)Claude finished @andybons's task in 3m 28s —— View job Code Review
SummaryThe core approach is sound: advertise Three issues worth addressing before merge: Issues[High] The h2 upstream transport has no dial timeout, no TLS handshake timeout, and no idle-ping settings — the h1 branch has [Medium] Unconditional h2 upstream may fail for h1-only backends (line 2187) When the client negotiates h2, [Medium] If Minor[Low] In What looks good
|
- Add dial timeout, KeepAlive, ReadIdleTimeout, PingTimeout to http2.Transport upstream to match the connection-safety posture of the h1 path - Document the known limitation: http2.Transport never falls back to h1, so h2-negotiating clients hitting h1-only upstreams will fail - Bail out early in handleConnectWithInterception if ConfigureServer fails and the client already negotiated h2, rather than serve h1 to an h2 client - Fix bufferedConn fragility in test: wrap conn with bufferedConn shim so bytes pre-fetched into the bufio.Reader are not lost before tls.Client consumes them
📦 Previous Review (superseded by newer review)Claude finished @andybons's task in 2m 58s —— View job Code Review
SummaryThe previous review's three issues (missing h2 transport timeouts, undocumented h1-only upstream failure mode, Two remaining issues: Issues[Medium]
[Low]
What looks good
|
…ct dep Replace tls.DialWithDialer (no context support) with tls.Dialer.DialContext so upstream h2 dials respect cancellation and deadlines from the client request context, matching the h1 path's dialer.DialContext behavior. Run go mod tidy to promote golang.org/x/net to a direct require entry now that proxy/proxy.go imports golang.org/x/net/http2 directly.
📦 Previous Review (superseded by newer review)Claude finished @andybons's task in 4m 17s —— View job Code Review
SummaryThree rounds of review have produced a clean implementation. All previously flagged issues are resolved:
What looks goodCore implementation (
Tests (
Coverage tests (
No remaining issuesThe implementation is correct and ready to merge. |
Remove the omnibus coverage_test.go file and move each test to the file where related tests already live: - Tunnel tests (ForwardsPlainHTTPS, NetworkPolicyBlocked, InvalidHostFormat, NewTokenSubstitution) → proxy_test.go - Intercept path tests (PathRulesWarning, LLMPolicy_DeniedLogged, ResponseTransformer, ResponseTransformer_NoMatch, SetTokenSubstitution) and the newAnthropicInterceptSetup helper → intercept_test.go - LLM gateway integration tests (LLMPolicy_Deny, Allow, ResponseTooLarge, NoEnginePassesThrough) and llmGatewayDenyEditPolicy const → llmpolicy_test.go - Exported host pattern and grant host tests → hosts_test.go
|
Claude finished @andybons's task in 3m 1s —— View job Code Review
SummaryThree rounds of review have produced a clean, correct implementation. All previously flagged issues are resolved. No new issues found in this sync.
What looks goodCore implementation (
Tests (
Coverage tests
No remaining issues. Ready to merge. |
Summary
h2first in ALPNNextProtoson the client-facing TLS connection insidehandleConnectWithInterception, enabling gRPC and other HTTP/2 clients to negotiate h2 through the CONNECT proxyhttp2.ConfigureServerhandles correct h2 framing on the innerhttp.Serverand the upstream transport switches tohttp2.Transport— credentials are injected identicallyhttp.Transportis used unchangedupstreamTLSconfig to eliminate the duplicate&tls.Config{...}across both transport brancheshttp2.NextProtoTLSconstant instead of bare"h2"string literals so the advertise/detect/route triple stays in syncNew tests
proxy/http2_test.go—TestHTTP2_GRPCCredentialInjection: black-box acceptance test that spins up a real HTTP/2 gRPC backend, routes through the intercepting proxy with Modal-style credentials configured forapi.modal.com, and asserts credential injection + correct gRPC response (written as a failing test first, then made green by the implementation)proxy/coverage_test.go— black-box coverage tests for previously uncovered paths: tunnel mode, network policy, LLM gateway policy (deny/allow/size-limit/no-engine), response transformers, token substitution, host pattern matching, grant host registrationgatekeeper_test.go—TestMultiHandler_WithAttrs/TestMultiHandler_WithGroupTest plan
go test -race ./...passesTestHTTP2_GRPCCredentialInjectionpasses — h2 ALPN negotiated, credentials injected, gRPC trailer received🤖 Generated with Claude Code