Skip to content

fix: harden HTTP gateway robustness and completeness#22

Merged
rbaliyan merged 2 commits intomainfrom
fix/gateway-hardening
Apr 24, 2026
Merged

fix: harden HTTP gateway robustness and completeness#22
rbaliyan merged 2 commits intomainfrom
fix/gateway-hardening

Conversation

@rbaliyan
Copy link
Copy Markdown
Owner

Summary

Addresses all findings from the design quality review of the HTTP gateway.

  • HTTP timeouts: ReadHeaderTimeout: 10s + IdleTimeout: 60s on http.Server — prevents slowloris and connection exhaustion
  • Joined goroutines: Serve() fans both gRPC and HTTP into a shared errCh(2); first failure surfaces to the caller immediately with no goroutine leaks
  • Context lifetime: RegisterLedgerServiceHandlerFromEndpoint now uses context.Background() so the loopback conn lives for the server's lifetime, not the caller's short-lived ctx
  • Loopback dial target: gatewayDialTarget() rewrites 0.0.0.0127.0.0.1 so gateway→gRPC traffic never traverses external interfaces
  • Header matcher: returns lowercase constants, not canonical-case HTTP header keys
  • errors.Is: errors.Is(err, http.ErrServerClosed) in Serve goroutine
  • Docs: http_listen comment now notes base64 encoding of bytes fields and restart requirement
  • Tests: TestGatewayErrorMapping (NotFound → HTTP 404), TestGatewayTLS (self-signed cert loopback dial)

Test plan

  • All 5 gateway tests pass: Health, AppendRead, APIKeyAuth, ErrorMapping, TLS
  • Full test suite passes with race detector
  • golangci-lint: 0 issues
  • gosec: 0 issues

- server.go: add ReadHeaderTimeout(10s) and IdleTimeout(60s) to http.Server
  to prevent slowloris attacks and resource exhaustion
- server.go: rewrite Serve() to fan gRPC and HTTP into a shared errCh(2) so
  the first failure surfaces immediately and neither goroutine leaks
- server.go: use errors.Is(err, http.ErrServerClosed) per Go convention
- gateway.go: drop caller ctx from RegisterLedgerServiceHandlerFromEndpoint;
  use context.Background() so the loopback conn lives for the server lifetime
- gateway.go: add gatewayDialTarget() to rewrite wildcard bind host (0.0.0.0)
  to 127.0.0.1 for the loopback dial, keeping traffic off external interfaces
- gateway.go: return lowercase constants from gatewayHeaderMatcher, not
  canonical-case HTTP header keys
- start.go: document base64 encoding and restart requirement for http_listen
- gateway_test.go: add TestGatewayErrorMapping (NotFound → 404)
- gateway_test.go: add TestGatewayTLS (self-signed cert loopback dial)
…tion

Gateway hardening:
- server.go: fix hook-init failure path to close httpListener if allocated
- server.go: drain second Serve goroutine error and log it to prevent silent drops
- server.go: filter grpc.ErrServerStopped symmetrically with http.ErrServerClosed
- start.go: document that gateway serves plain HTTP (reverse proxy for HTTPS)
- gateway_internal_test.go: unit tests for gatewayDialTarget (6 cases) and
  gatewayHeaderMatcher (6 cases including canonical-case input)

CI:
- release.yml: bump goreleaser/goreleaser-action to e24998b (v7.1.0)
@rbaliyan rbaliyan force-pushed the fix/gateway-hardening branch from 9aebae5 to 5218b41 Compare April 24, 2026 06:01
@rbaliyan rbaliyan merged commit c726c42 into main Apr 24, 2026
9 checks passed
@rbaliyan rbaliyan deleted the fix/gateway-hardening branch April 24, 2026 06:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant