From 1939225f116226fec34d28bd036c21d2fb98c15b Mon Sep 17 00:00:00 2001 From: youfak Date: Sun, 2 Aug 2026 08:23:58 +0800 Subject: [PATCH] feat: bound egress check URL references --- docs/configuration/reference.md | 2 ++ internal/config/config.go | 4 ++++ internal/config/effective_check_test.go | 14 ++++++++++++++ internal/config/validate.go | 3 +++ 4 files changed, 23 insertions(+) diff --git a/docs/configuration/reference.md b/docs/configuration/reference.md index 7091555..d8e6e80 100644 --- a/docs/configuration/reference.md +++ b/docs/configuration/reference.md @@ -464,6 +464,8 @@ proxyAuth: - 明确绝对过期时间优先于响应 TTL,响应 TTL 优先于配置 `lifecycle.ttl`。 - 距离过期不足 `allocationSafetyMargin` 时停止新分配。 - `check.jitter` 为调度抖动百分比,避免所有 Proxy 同时探测。 +- `check.urls` 最多 16 个规范化 HTTP/HTTPS URL,作为 EGRESS 检查的有界目标集; + BASIC 检查不依赖该字段。 - 第一次有意义失败进入 SUSPECT;达到 `maxConsecutiveFailures` 后才进入 UNHEALTHY。 diff --git a/internal/config/config.go b/internal/config/config.go index 73234df..f709dba 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -5,6 +5,10 @@ import ( "time" ) +// MaximumCheckURLs bounds per-upstream EGRESS references. The bound keeps +// scheduling and proxy cleanup proportional to a fixed configuration limit. +const MaximumCheckURLs = 16 + const ( MaximumPoolSize = 1_000_000 MaximumExactCounter = int64(1<<53 - 1) diff --git a/internal/config/effective_check_test.go b/internal/config/effective_check_test.go index b318691..0d3af33 100644 --- a/internal/config/effective_check_test.go +++ b/internal/config/effective_check_test.go @@ -1,6 +1,7 @@ package config import ( + "fmt" "strings" "testing" "time" @@ -59,3 +60,16 @@ func TestValidateUsesEffectiveCheckAndRejectsUnsafeURLs(t *testing.T) { } } } + +func TestValidateRejectsUnboundedCheckURLReferences(t *testing.T) { + cfg := mustLoadValidConfig(t) + upstream := cfg.Upstreams["provider-a"] + upstream.Check.URLs = make([]string, MaximumCheckURLs+1) + for index := range upstream.Check.URLs { + upstream.Check.URLs[index] = fmt.Sprintf("https://egress-%d.example/check", index) + } + cfg.Upstreams["provider-a"] = upstream + if err := Validate(cfg); err == nil || !strings.Contains(err.Error(), "at most") { + t.Fatalf("Validate(unbounded check URLs) error = %v, want maximum URL error", err) + } +} diff --git a/internal/config/validate.go b/internal/config/validate.go index dbb8808..e1bccff 100644 --- a/internal/config/validate.go +++ b/internal/config/validate.go @@ -588,6 +588,9 @@ func validateCheck(scope string, check Check) error { if err := requirePositive(scope+".maxConsecutiveFailures", check.MaxConsecutiveFailures); err != nil { return err } + if len(check.URLs) > MaximumCheckURLs { + return fmt.Errorf("validate %s.urls: supports at most %d URLs", scope, MaximumCheckURLs) + } seenURLs := make(map[string]struct{}, len(check.URLs)) for index, rawURL := range check.URLs { if rawURL == "" || strings.TrimSpace(rawURL) != rawURL {