Skip to content

fix: clamp current_age to >=0 for future Date headers (RFC 9111 §4.2.3)#3

Merged
sandrolain merged 1 commit into
sandrolain:masterfrom
andig:fix/clock-skew-future-date
Jun 22, 2026
Merged

fix: clamp current_age to >=0 for future Date headers (RFC 9111 §4.2.3)#3
sandrolain merged 1 commit into
sandrolain:masterfrom
andig:fix/clock-skew-future-date

Conversation

@andig

@andig andig commented Jun 22, 2026

Copy link
Copy Markdown

Closes #2

Problem

When a response carries a Date header ahead of the local clock (clock skew), the freshness logic computes a negative current age. A response with no freshness information (no Cache-Control, no Expires) has a freshness lifetime of 0, so the test lifetime > currentAge becomes 0 > -2strue, and the response is wrongly marked fresh and served from cache.

RFC 9111 §4.2.3

The age algorithm clamps the apparent age:

apparent_age = max(0, response_time - date_value)

so current_age can never be negative. The max(0, ...) was missing from the three freshness sites (getFreshness, isActuallyStale, checkStaleIfErrorLifetime), all of which used clock.since(date) directly.

Change

Add a small clampedAge(date) helper (max(0, now - date)) and use it at the three sites. With the clamp, a future-dated response with no freshness info evaluates 0 > 0false → correctly stale.

A regression test (TestFreshnessFutureDateClockSkew) is added; it fails before the change and passes after. Full test suite remains green.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes HTTP freshness evaluation when a cached response’s Date header is ahead of the local clock (clock skew), ensuring current_age never becomes negative per RFC 9111 §4.2.3 so future-dated responses aren’t incorrectly treated as fresh.

Changes:

  • Introduce clampedAge(date) to clamp now - date to >= 0 per RFC 9111 §4.2.3.
  • Use clampedAge in getFreshness, isActuallyStale, and checkStaleIfErrorLifetime to prevent negative age affecting freshness decisions.
  • Add a regression test covering the future-Date clock-skew scenario.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
httpcache.go Adds clampedAge helper and applies it to freshness/staleness calculations to prevent negative current_age.
httpcache_clockskew_test.go Adds regression test ensuring a future Date header with no freshness info is treated as stale.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +15 to +20
// Date 2s in the future, no Cache-Control and no Expires (lifetime 0).
respHeaders.Set("Date", time.Now().Add(2*time.Second).UTC().Format(time.RFC1123))

if got := getFreshness(respHeaders, http.Header{}); got == fresh {
t.Fatalf("future Date with no freshness info treated as fresh; want stale (RFC 9111 §4.2.3); got %s", freshnessString(got))
}
@sandrolain sandrolain merged commit 474c501 into sandrolain:master Jun 22, 2026
5 of 6 checks passed
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.

Future Date header (clock skew) marks responses fresh, violating RFC 9111 §4.2.3

3 participants