Documentation
¶
Overview ¶
Package cache implements the cache rule evaluation CloudFront performs at the edge, per RFC 7234 + the CloudFront-specific extensions documented at:
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Expiration.html
The package is pure — no HTTP, no storage. The edge handler in edge.go applies these rules to live requests/responses.
Index ¶
- func ConditionalHeaders(cachedHeader http.Header) http.Header
- func EffectiveTTL(respHeader http.Header, cfg DistributionConfig, now time.Time) time.Duration
- func IfModifiedSinceSatisfied(reqHeader, respHeader http.Header) bool
- func IfNoneMatchSatisfied(reqHeader, respHeader http.Header) bool
- func InitialAge(respHeader http.Header) time.Duration
- func IsCacheable(respHeader http.Header, statusCode int) (bool, string)
- func Key(req *http.Request, vary []string) string
- func MustRevalidate(respHeader http.Header) bool
- func ParseRange(header string, totalSize int64) (int64, int64, bool)
- func VaryDisablesCache(respHeader http.Header) bool
- func VaryHeaders(respHeader http.Header) []string
- type CDNStaleDirectives
- type ClientDecision
- type Control
- type DistributionConfig
- type RequestDirectives
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ConditionalHeaders ¶
ConditionalHeaders builds the If-None-Match / If-Modified-Since header pair the cache should send on a revalidation request to the origin, derived from the cached entry's validators.
func EffectiveTTL ¶
EffectiveTTL implements the CloudFront precedence:
- `CDN-Cache-Control` (RFC 9213) — CDN-targeted directives win over the Cache-Control browsers see. CloudFront supports it.
- If the origin sends `Cache-Control: s-maxage=N`, use N.
- Otherwise if `Cache-Control: max-age=N`, use N.
- Otherwise if `Expires: <date>` is in the future, use that delta.
- Otherwise fall back to the distribution's DefaultTTL.
The result is then clamped to [MinTTL, MaxTTL].
Special cases that override the above:
- `Cache-Control: no-store` → returns 0 (do not cache).
- `Cache-Control: private` → returns 0 (CloudFront treats this as "shared cache must not store").
`no-cache` is **not** zero — it caches but always revalidates; that distinction belongs to NoCacheDirective, not the TTL.
func IfModifiedSinceSatisfied ¶
IfModifiedSinceSatisfied reports whether the cached entry's Last-Modified is at or before the request's If-Modified-Since. When true the cache should respond 304 (the client's copy is fresh enough).
func IfNoneMatchSatisfied ¶
IfNoneMatchSatisfied reports whether the request's If-None-Match matches the cached entry's ETag. Per RFC 9110 §13.1.2:
- `*` matches any existing representation
- any listed entity-tag (weak or strong) matching the cached one returns true
When this returns true, the cache should respond 304 Not Modified instead of the full body.
func InitialAge ¶
InitialAge parses the upstream's `Age:` response header. Used to pre-age a freshly-stored cache entry — the origin may have sat in an upstream cache for some time before reaching us, and RFC 9111 §5.1 says we must carry that age forward.
Returns 0 when the header is absent / malformed / negative.
func IsCacheable ¶
IsCacheable decides whether the response can be put in the cache at all. Returns (false, reason) when storage is forbidden.
Per RFC 9111 §3: any status code may be cached when the response carries **explicit** freshness information (Cache-Control max-age / s-maxage / CDN-Cache-Control max-age, or an Expires header). Without explicit freshness only the "heuristically cacheable" set (RFC 9110 §15) is stored — that's the small list CloudFront defaults to.
func Key ¶
Key builds the deterministic cache key for a request. The base is method+URL; adding the values of any Vary headers (lowercased, in stable order) yields the per-variant key.
Query string handling matches CloudFront's "all" forwarded mode — the full sorted query string contributes to the key. Cookie / header forwarding beyond Vary is out of scope for this PR.
func MustRevalidate ¶
MustRevalidate reports whether a **fresh** cached entry must still trigger origin revalidation before it can be served. RFC 9111 §5.2.2 distinguishes:
- `no-cache` — yes, revalidate even when fresh.
- `must-revalidate` / `proxy-revalidate` — only matters once the entry is stale (and even then it just forbids serving stale while disconnected); fresh entries are unaffected.
So this returns true iff the no-cache directive is present.
func ParseRange ¶
ParseRange interprets a single-range "Range: bytes=START-END" header against a known content length. Returns (start, end, true) where end is inclusive (matching the Content-Range wire format).
Multi-range requests (`bytes=0-99,200-299`) and unsatisfiable ranges return ok=false — the cache should fall through to a full fetch / 416 in those cases. Suffix ranges (`bytes=-100`) and open-ended (`bytes=100-`) are supported.
func VaryDisablesCache ¶
VaryDisablesCache returns true when the origin sent `Vary: *`, which RFC 7234 says forbids any further caching.
func VaryHeaders ¶
VaryHeaders extracts the comma-separated list of request headers the origin's `Vary` response header pins as part of the cache key. The names are lowercased and deduplicated for stable key building.
`Vary: *` is special — it disables caching entirely; the caller should treat that as "do not cache" rather than feeding it here.
Types ¶
type CDNStaleDirectives ¶
CDNStaleDirectives is the (stale-while-revalidate, stale-if-error) pair from a response. CloudFront only honours these when they come from CDN-Cache-Control — the Cache-Control header is the browser-targeted policy and intentionally has no influence on the CDN's stale-serving behaviour. RFC 9213 §3.1 endorses the same split.
Returns zero durations when neither directive is set or CDN-Cache-Control is absent.
func ReadCDNStaleDirectives ¶
func ReadCDNStaleDirectives(respHeader http.Header) CDNStaleDirectives
ReadCDNStaleDirectives parses CDN-Cache-Control for swr / sie. It intentionally ignores Cache-Control to match CloudFront's published behaviour.
type ClientDecision ¶
ClientDecision is the verdict EvaluateClient returns.
func EvaluateClient ¶
func EvaluateClient(req RequestDirectives, age, ttl time.Duration) ClientDecision
EvaluateClient runs the request-side directives against an entry.
type Control ¶
type Control struct {
NoStore bool
NoCache bool
Private bool
Public bool
MustRevalidate bool
MaxAge *int64
SMaxAge *int64
}
Control is the parsed shape of a Cache-Control header. Pointers distinguish "absent" from "zero".
type DistributionConfig ¶
type DistributionConfig struct {
MinTTL time.Duration
DefaultTTL time.Duration
MaxTTL time.Duration
}
DistributionConfig is the subset of CloudFront's CacheBehavior that the rule evaluation needs. Real DefaultCacheBehavior carries more, but TTL clamping only depends on these three values.
type RequestDirectives ¶
type RequestDirectives struct {
NoCache bool // "always revalidate this hit before serving"
NoStore bool // "do not store the response"
OnlyIfCached bool // "serve from cache or 504"
MaxAge *int64 // "I'll only accept entries whose age <= MaxAge"
MinFresh *int64 // "I want at least MinFresh seconds of remaining freshness"
MaxStale *int64 // "I'll accept stale entries up to MaxStale seconds past TTL"
HasMaxStale bool // distinguishes `max-stale` (no value, means infinity) from absent
}
RequestDirectives holds the subset of `Cache-Control` directives a client may put on a *request* (RFC 9111 §5.2.1).
func ParseRequestCacheControl ¶
func ParseRequestCacheControl(reqHeader http.Header) RequestDirectives
ParseRequestCacheControl reads the request's Cache-Control header. Same parsing logic as the response side, but exposes the request-applicable directives via a separate type so callers don't confuse the two.