Commit Graph

3 Commits

Author SHA1 Message Date
5bc8f4fc8e Fix three doc-vs-implementation gaps found during audit
1. DB migration: add CHECK(mode IN ('l4', 'l7')) constraint on the
   routes.mode column. ARCHITECTURE.md documented this constraint but
   migration v2 omitted it. Enforces mode validity at the database
   level in addition to application-level validation.

2. L7 reverse proxy: distinguish timeout errors from connection errors
   in the ErrorHandler. Backend timeouts now return HTTP 504 Gateway
   Timeout instead of 502. Uses errors.Is(context.DeadlineExceeded)
   and net.Error.Timeout() detection. Added isTimeoutError unit tests.

3. Config validation: warn when L4 routes have tls_cert or tls_key set
   (they are silently ignored). ARCHITECTURE.md documented this warning
   but config.validate() did not emit it. Uses slog.Warn.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 14:25:41 -07:00
b6c96ad42f Add integration tests for multi-hop, gRPC trailers, and HTTP/1.1
Multi-hop integration tests (server package):
- TestMultiHopProxyProtocol: full edge→origin deployment with two
  mc-proxy instances. Edge uses L4 passthrough with send_proxy_protocol,
  origin has proxy_protocol listener with L7 route. Verifies the real
  client IP (127.0.0.1) flows through PROXY protocol into the origin's
  X-Forwarded-For header on the h2c backend.
- TestMultiHopFirewallBlocksRealIP: origin firewall blocks an IP from
  the PROXY header while allowing the TCP peer (edge proxy). Verifies
  the backend is never reached.

L7 package integration tests:
- TestL7LargeResponse: 1 MB response through the reverse proxy.
- TestL7GRPCTrailers: HTTP/2 trailer propagation (Grpc-Status,
  Grpc-Message) through the reverse proxy, validating gRPC
  compatibility.
- TestL7HTTP11Fallback: client negotiates HTTP/1.1 only (no h2 ALPN),
  verifies the proxy falls back to HTTP/1.1 serving and still
  forwards to the h2c backend successfully.

Also updates PROGRESS.md to mark all five phases complete.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 14:02:45 -07:00
97909b7fbc Add L7 TLS-terminating HTTP/2 reverse proxy
New internal/l7 package implements TLS termination and HTTP/2 reverse
proxying for L7 routes. The proxy terminates the client TLS connection
using per-route certificates, then forwards HTTP/2 traffic to backends
over h2c (plaintext HTTP/2) or h2 (re-encrypted TLS).

PrefixConn replays the peeked ClientHello bytes into crypto/tls.Server
so the TLS handshake sees the complete ClientHello despite SNI
extraction having already read it.

Serve() is the L7 entry point: TLS handshake with route certificate,
ALPN negotiation (h2 preferred, HTTP/1.1 fallback), then HTTP reverse
proxy via httputil.ReverseProxy. Backend transport uses h2c by default
(AllowHTTP + plain TCP dial) or h2-over-TLS when backend_tls is set.

Forwarding headers (X-Forwarded-For, X-Forwarded-Proto, X-Real-IP)
are injected from the real client IP in the Rewrite function. PROXY
protocol v2 is sent to backends when send_proxy_protocol is enabled,
using the request context to carry the client address through the
HTTP/2 transport's dial function.

Server integration: handleConn dispatches to handleL7 when route.Mode
is "l7". The L7 handler converts RouteInfo to l7.RouteConfig and
delegates to l7.Serve.

L7 package tests: PrefixConn (4 tests), h2c backend round-trip,
forwarding header injection, backend unreachable (502), multiple
HTTP/2 requests over one connection.

Server integration tests: L7 route through full server pipeline with
TLS client, mixed L4+L7 routes on the same listener verifying both
paths work independently.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 13:43:20 -07:00