Commit Graph

23 Commits

Author SHA1 Message Date
d9b3a436f1 Merge remote-tracking branch 'origin/fix/fix/rename-misleading-test' 2026-06-12 14:28:41 +00:00
855c0f1d67 fix(rename-misleading-test): rename misleading test and use from_sites in dynamic config test 2026-06-12 14:28:19 +00:00
c2201707bb Merge remote-tracking branch 'origin/fix/fix/rate-limiter-connectinfo-tests' 2026-06-12 14:25:22 +00:00
603d722ad0 feat(rate-limiter): add ConnectInfo-based tests for rate limiter (ADR-025) 2026-06-12 14:24:17 +00:00
77117c29eb feat(http-port-type): change http_port from u32 to u16 per spec (W12) 2026-06-12 14:20:15 +00:00
ad9b9b9b78 fix(rate_limit): use ConnectInfo as sole IP source, reject without it
The rate limiter previously extracted client IP from the X-Forwarded-For
header first, falling back to ConnectInfo. This allowed attackers to bypass
rate limits by sending spoofed X-Forwarded-For headers. Per ADR-025, the
rate limiter now uses ConnectInfo<SocketAddr> exclusively and rejects
requests with 429 when ConnectInfo is absent.
2026-06-12 14:00:31 +00:00
6cb0f8e6fe Merge branch 'fix/fix/graceful-shutdown' into fix/acme-contact-and-challenge 2026-06-12 04:59:32 +00:00
280fe782a1 Implement graceful shutdown for listeners, admin socket, eviction task, and ACME
- Replace handle.abort() for HTTPS server tasks with timeout-based join,
  allowing in-flight requests to drain before forceful shutdown
- Add shutdown_rx to start_admin_socket with tokio::select! for clean
  accept loop exit and Unix socket file cleanup on shutdown
- Add shutdown_rx to start_eviction_task with tokio::select! for
  cancellable eviction loop
- Add shutdown channel to spawn_acme_state for cancellable ACME state
  machine via tokio::select!
- Pass Arc<GracefulShutdown> through setup_tls to ACME state machine
- Move GracefulShutdown creation before admin socket and TLS setup
- Update integration test for new start_eviction_task signature
2026-06-12 04:59:18 +00:00
9bdc2b72af Add acme_contact to test config TOML strings
The main code changes were already committed (3f2550f), but test config
TOML strings in cli.rs, admin/socket.rs, shutdown.rs, and
integration_test.rs still needed the new acme_contact field to pass
validation rule 19.
2026-06-12 04:48:25 +00:00
3f2550fa20 Fix ACME contact email wiring and remove unused challenge config 2026-06-12 04:44:41 +00:00
d24148dae9 Add http_port range validation (0 or 1-65535)
Change http_port type from u16 to u32 to allow out-of-range values to be
caught by validation. Add HttpPortInvalid error variant and validation check
for http_port > 65535. Add test for http_port=65536 producing HttpPortInvalid.
http_port=0 (disabled) remains valid per existing test.
2026-06-12 04:28:35 +00:00
c4872cb88c fix: correct TOML nesting from [[listeners.listeners.sites]] to [[listeners.sites]] 2026-06-12 04:22:46 +00:00
78a518acd4 Implement signal handling and graceful shutdown
- Add GracefulShutdown struct with watch channel for shutdown signaling
- Handle SIGTERM/SIGINT via signal-hook to trigger graceful shutdown
- Handle SIGHUP via signal-hook for config reload (same code path as admin socket)
- Implement graceful shutdown sequence: stop accepting -> drain -> force-close -> cancel tasks -> exit 0
- Wire up main.rs with full server startup (health check, admin socket, HTTP redirect, HTTPS proxy)
- Add integration tests for GracefulShutdown and SIGHUP reload
- shutdown_timeout_secs configurable in StaticConfig (default 30)
2026-06-11 13:33:26 +00:00
c25d19c63f Merge feat/tls/http-redirect into main 2026-06-11 13:18:46 +00:00
f280a04d4b Remove accidentally staged worktree dirs 2026-06-11 13:16:45 +00:00
d893187c40 Implement HTTP to HTTPS redirect with per-listener binding
Adds the HTTP redirect listener that redirects all plain HTTP requests to
the HTTPS equivalent URL. Each listener with http_port > 0 runs its own
redirect server on bind_addr:http_port.

- build_redirect_url: constructs https://{host}:{port}/{path}?{query},
  omitting port 443 and stripping the host port from the Host header
- redirect_handler: axum handler returning 301 with Location header,
  400 for missing/empty Host, 404 for ACME challenge paths
- redirect_router: creates axum Router with fallback handler
- start_http_redirect_listener: binds TCP and spawns redirect server
- ACME HTTP-01 challenge path returns 404 (placeholder for future)
- 19 unit tests for URL construction and host parsing
- 8 integration tests covering 301 redirect, 400 on missing Host,
  port 443 omission, non-443 port inclusion, query preservation,
  ACME challenge 404
2026-06-11 13:14:27 +00:00
d89ab71f85 Implement CLI argument parsing with clap and config file loading
- Add Cli struct with clap derive macros for --config, --validate, --allow-wildcard-bind flags
- Config loading: reads TOML, deserializes into StaticConfig + DynamicConfig, validates
- --validate: load, validate, print success/errors, exit 0 or 1
- --allow-wildcard-bind is OR'd with config allow_wildcard_bind field
- Default config path: /etc/reverse-proxy/config.toml
- Version from Cargo.toml via clap
- Unit tests for CLI argument parsing and config loading
- Integration tests for --validate with valid/invalid config and --allow-wildcard-bind
2026-06-11 13:12:28 +00:00
24a7f9ed86 Merge feat/ops/body-size-limit into main 2026-06-11 13:12:27 +00:00
5fa0fc600e Implement body size limit middleware with dynamic config reload
Add body_limit middleware that reads limit from ArcSwap<DynamicConfig>
on each request, enabling runtime config changes without restart.
Uses Content-Length header check for fast rejection and http_body_util::Limited
for streaming body enforcement. Default limit: 100 MB (104,857,600 bytes).
Returns 413 Payload Too Large when exceeded.
2026-06-11 13:02:59 +00:00
2791070971 Implement token bucket rate limiting with IPv6 /64 normalization
- Add TokenBucket with nodelay semantics (nginx limit_req burst nodelay)
- Per-IP rate limiting: IPv4 /32, IPv6 /64 prefix normalization
- DashMap for concurrent access, ArcSwap for lock-free config reads
- Background eviction task for stale entry cleanup
- 429 response with plain text body, RATE_LIMIT log prefix
- Config reload adopts new rate/burst on next request without clearing state
- Unit tests for bucket algorithm and IPv6 normalization
- Integration tests for 429 responses and per-IP independence
2026-06-11 13:01:25 +00:00
e9a1d25909 Merge feat/config/dynamic-config into main 2026-06-11 12:47:59 +00:00
c423a58778 Implement health check endpoint on separate local port and HTTPS fallback
- Add health.rs module with start_health_check_listener() that binds to
  127.0.0.1:{health_check_port} and serves GET /health returning 200 OK
  with empty body
- Add health_route() in proxy/handler.rs for HTTPS listener fallback
- Add port conflict detection in config validation: health_check_port
  must not conflict with listener ports on 127.0.0.1/localhost/0.0.0.0
- health_check_port = 0 disables the separate listener (handled at call
  site)
- Add unit and integration tests for health check functionality
2026-06-11 12:39:24 +00:00
75f7b778df Add test infrastructure with fixtures, helpers, and integration tests
- Add [lib] target to enable integration test imports
- Add rcgen and reqwest dev-dependencies for TLS and HTTP test helpers
- Create src/config/test_fixtures.rs with test_static_config() and test_dynamic_config()
- Create tests/ with integration tests, HTTP test helper (TestUpstream), and TLS test helper (SelfSignedCert)
- Add Clone derives to StaticConfig and related structs for test fixture construction
- All existing tests continue to pass
2026-06-11 11:46:43 +00:00