plugin/cache: configure stale TTL and failure recheck (#8411)

* plugin/cache: configure stale response TTL

Signed-off-by: houyuwushang <liuluoqianqiu@outlook.com>

* plugin/cache: delay stale refresh retries

Signed-off-by: houyuwushang <liuluoqianqiu@outlook.com>

* plugin/cache: validate stale refresh responses

Signed-off-by: houyuwushang <liuluoqianqiu@outlook.com>

---------

Signed-off-by: houyuwushang <liuluoqianqiu@outlook.com>
This commit is contained in:
houyuwushang
2026-08-14 14:27:42 +08:00
committed by GitHub
parent 6199dc50a5
commit 9520d725d6
8 changed files with 757 additions and 72 deletions

81
plugin/cache/setup.go vendored
View File

@@ -172,10 +172,12 @@ func cacheParse(c *caddy.Controller) (*Cache, error) {
case "serve_stale":
args := c.RemainingArgs()
if len(args) > 3 {
if len(args) > 5 {
return nil, c.ArgErr()
}
ca.staleUpTo = 1 * time.Hour
ca.staleTTL = 0
ca.staleRecheck = 0
if len(args) > 0 {
d, err := time.ParseDuration(args[0])
if err != nil {
@@ -196,17 +198,43 @@ func cacheParse(c *caddy.Controller) (*Cache, error) {
ca.verifyStale = mode == "verify"
}
if len(args) > 2 {
if !ca.verifyStale {
return nil, errors.New("serve_stale timeout is only valid with the verify refresh mode")
if ca.verifyStale {
t, err := time.ParseDuration(args[2])
if err != nil {
return nil, fmt.Errorf("invalid serve_stale verify timeout: %w", err)
}
if t < 0 {
return nil, errors.New("invalid negative timeout for serve_stale verify")
}
ca.verifyStaleTimeout = t
if len(args) > 3 {
ca.staleTTL, err = parseServeStaleTTL(args[3])
if err != nil {
return nil, err
}
}
if len(args) > 4 {
ca.staleRecheck, err = parseServeStaleRecheck(args[4])
if err != nil {
return nil, err
}
}
} else {
if len(args) > 4 {
return nil, c.ArgErr()
}
var err error
ca.staleTTL, err = parseServeStaleTTL(args[2])
if err != nil {
return nil, err
}
if len(args) > 3 {
ca.staleRecheck, err = parseServeStaleRecheck(args[3])
if err != nil {
return nil, err
}
}
}
t, err := time.ParseDuration(args[2])
if err != nil {
return nil, fmt.Errorf("invalid serve_stale verify timeout: %w", err)
}
if t < 0 {
return nil, errors.New("invalid negative timeout for serve_stale verify")
}
ca.verifyStaleTimeout = t
}
case "servfail":
args := c.RemainingArgs()
@@ -273,3 +301,34 @@ func cacheParse(c *caddy.Controller) (*Cache, error) {
return ca, nil
}
func parseServeStaleTTL(value string) (time.Duration, error) {
ttl, err := time.ParseDuration(value)
if err != nil {
return 0, fmt.Errorf("invalid serve_stale response TTL: %w", err)
}
if ttl < 0 {
return 0, errors.New("invalid negative response TTL for serve_stale")
}
if ttl%time.Second != 0 {
return 0, errors.New("serve_stale response TTL must be a whole number of seconds")
}
if ttl/time.Second > time.Duration(^uint32(0)) {
return 0, errors.New("serve_stale response TTL exceeds the DNS TTL range")
}
return ttl, nil
}
func parseServeStaleRecheck(value string) (time.Duration, error) {
recheck, err := time.ParseDuration(value)
if err != nil {
return 0, fmt.Errorf("invalid serve_stale failure recheck: %w", err)
}
if recheck < 0 {
return 0, errors.New("invalid negative failure recheck for serve_stale")
}
if recheck > 5*time.Minute {
return 0, errors.New("serve_stale failure recheck cannot exceed 5 minutes")
}
return recheck, nil
}