Task graph covers all Phase 1 concerns: config system, TLS termination, proxy handler, operations (rate limiting, logging, health check, admin socket, signals, shutdown, body size limit), deployment artifacts, and two review checkpoints. No circular dependencies. Critical path length of 7. Risk distribution: 3 high-risk (ACME, TLS listener setup, startup orchestration), 7 medium, 11 low, 2 trivial.
4.0 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |
|---|---|---|---|---|---|---|---|---|
| proxy/headers-and-forwarding | Implement proxy header injection, hop-by-hop removal, and request forwarding with hyper Client | pending |
|
moderate | medium | component | implementation |
Description
Implement the core reverse proxy logic: inject proxy headers, remove hop-by-hop headers, and forward requests to the upstream via a shared hyper::Client.
Proxy Header Injection
The proxy is an edge proxy — it sits directly in front of the internet with no trusted proxies upstream. This means existing X-Forwarded-For headers from the client cannot be trusted.
| Header | Value Source | Behavior |
|---|---|---|
Host |
Original request Host header |
Preserved as-is |
X-Real-IP |
ConnectInfo<SocketAddr> remote IP |
Set to client's IP address |
X-Forwarded-For |
ConnectInfo<SocketAddr> remote IP |
Replaced, not appended |
X-Forwarded-Proto |
Determined by listener port | https for https_port, http for http_port |
Hop-by-Hop Header Removal
Remove these headers before forwarding to upstream (RFC 2616 §13.5.1):
Connection,Keep-Alive,Proxy-Authorization,Proxy-AuthenticateTE,Trailers,Transfer-Encoding,Upgrade
Also remove these from upstream responses before sending to client.
Request Forwarding
- Build the upstream URI:
{upstream_scheme}://{upstream}{path}?{query} - Copy request method, headers (with proxy headers injected, hop-by-hop removed), and body
- Send via shared
hyper::Clientwith per-site timeout overrides - Stream response back to client (chunk-by-chunk, not buffered)
- Handle client disconnect (log at debug, close upstream connection)
- Handle upstream disconnect (send whatever was already sent, close connection)
hyper Client Configuration
- Created once at startup, shared via axum State
- HTTP/1.1 only for upstream connections
- No redirect following (proxies should not follow redirects)
- Connection pooling (hyper default behavior)
- Per-site timeout overrides:
upstream_connect_timeout_secs(default 5s),upstream_request_timeout_secs(default 60s)
Upstream Scheme
Default is http://. When upstream_scheme is "https", validate the upstream's TLS certificate using the system's native TLS root certificates. Certificate validation failures result in 502 Bad Gateway.
Acceptance Criteria
X-Real-IPset fromConnectInfo<SocketAddr>remote IPX-Forwarded-Forreplaced (not appended) with client IPX-Forwarded-Protoset tohttpsorhttpbased on listener portHostheader preserved as-is- Hop-by-hop headers removed before forwarding to upstream
- Hop-by-hop headers removed from upstream response before sending to client
- No
Serverheader added to responses - No
Viaheader added in Phase 1 - Request body streamed (not buffered) to upstream
- Response body streamed (not buffered) to client
- Client disconnect logged at debug level, upstream connection closed
- Upstream disconnect: client receives whatever was already sent
- Per-site timeout overrides applied to hyper Client requests
upstream_scheme: "https"validates upstream TLS certificate with system roots- Shared
hyper::Clientinstance via axum State - Unit tests for header injection and removal
- Integration test: proxy request to upstream, verify headers and response
References
- docs/architecture/proxy.md — header injection, request forwarding, error handling
- docs/architecture/decisions/002-custom-proxy-handler.md — custom handler rationale
- docs/architecture/decisions/017-upstream-connection-defaults.md — HTTP/1.1, no redirects
- docs/architecture/decisions/021-x-forwarded-for-edge-proxy.md — edge proxy model
Notes
The
X-Forwarded-For: replace, don't appendbehavior is critical. The proxy is the edge — there are no trusted proxies upstream. ExistingX-Forwarded-Forvalues from the client could be spoofed and must not be trusted.
Summary
To be filled on completion