R3: gateway edge hardening — body cap, h2c sizing, rate-limit observability

- GATEWAY_MAX_BODY_BYTES (1 MiB): connect WithReadMaxBytes + http.MaxBytesReader
  on the public mux; explicit http2.Server MaxConcurrentStreams/IdleTimeout and
  an http.Server ReadHeaderTimeout (R2 report follow-up).
- gateway_rate_limited_total{class} counter, Debug per rejection, a rejection
  tracker drained every 30 s into a Warn summary per key and a report POST to
  /api/v1/internal/ratelimit/report (feeds the admin view + auto-flag).
- The dead AdminPerMinute/AdminBurst policy now guards the /_gm mount (429),
  ahead of its Basic-Auth.
- resolve() logs the cause of infra session-resolve failures at Warn (the
  transient unauthenticated dips from the R2 run); unknown tokens stay silent.
This commit is contained in:
Ilia Denisov
2026-06-10 01:58:48 +02:00
parent c23ac94c4e
commit 8878711cf3
12 changed files with 549 additions and 35 deletions
+16
View File
@@ -29,3 +29,19 @@ func TestLoadRejectsUnsupportedExporter(t *testing.T) {
t.Fatal("Load: expected an error for an unsupported exporter, got nil")
}
}
// TestLoadMaxBodyBytes verifies the body-cap default and that a non-positive
// override fails validation.
func TestLoadMaxBodyBytes(t *testing.T) {
c, err := Load()
if err != nil {
t.Fatalf("Load: %v", err)
}
if c.MaxBodyBytes != DefaultMaxBodyBytes {
t.Errorf("MaxBodyBytes = %d, want %d", c.MaxBodyBytes, DefaultMaxBodyBytes)
}
t.Setenv("GATEWAY_MAX_BODY_BYTES", "0")
if _, err := Load(); err == nil {
t.Fatal("Load: expected an error for a non-positive body cap, got nil")
}
}