Skip to content

Thread the Clock into cache-header parsing so an injected clock does not silently discard server expiries #34

Description

@RobertoIskandarani

CacheHeaderParser.parseExpiresAt turns a Cache-Control: max-age=N into an absolute expiry using System.currentTimeMillis(). DocumentCache records when it cached a document using the Clock it was given, and compares the two — it only shortens its configured TTL when the server expiry is in the future.

Under Clock.systemUTC() those agree. Under an injected clock offset far from wall time they do not: every real server expiry reads as already past, so Cache-Control and Expires: are discarded wholesale and the configured interval governs alone.

That fallback is safe — nothing breaks, the document just refreshes on the SDK's own schedule — but it is silent, and it contradicts what the Clock constructors on JwksCache and MetadataCache advertise. Those are documented as supported API for driving refresh intervals deterministically, "from a simulation clock", which is exactly the usage that trips this.

The constructors now state the constraint. That is a workaround for a caller who reads the javadoc, not a fix.

Scope

Thread the clock into the parser: parseExpiresAt(Map<String, String> headers, Clock clock) as an additive overload, with the existing single-argument form delegating with Clock.systemUTC().

The awkward part is the path in between. Parsing happens in DocumentFetcher.from(HttpTransport), and DocumentFetcher is an interface whose two static factories — from and ssrfSafe — would need a clock parameter to pass one down. Both are public, so that is an API change and wants doing deliberately rather than as a drive-by. An alternative worth weighing first: have the fetcher return the raw directives and let DocumentCache compute the absolute expiry against its own clock, which puts the only clock read in the class that owns one.

Once either lands, the constraint paragraph in the Clock constructor javadocs comes back out.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions