Add ETag support and fix conditional request handling in ServeDir#691
Open
jlizen wants to merge 1 commit into
Open
Add ETag support and fix conditional request handling in ServeDir#691jlizen wants to merge 1 commit into
jlizen wants to merge 1 commit into
Conversation
rcoh
approved these changes
May 26, 2026
08240fb to
333503f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #324, closes #411
Motivation
ServeDironly supports time-based conditional requests today. This causes real problems: if you roll back a deploy, files get an older mtime and clients keep getting 304s for stale content. HTTP dates also have 1-second granularity, so rapid redeploys within the same second are invisible toIf-Modified-Since.Solution
Generate a strong ETag from file metadata (mtime with nanosecond precision + file size) and implement the full RFC 9110 §13.2.2 precondition evaluation order:
If-Match→ 412 (enables safe range request resumption)If-Unmodified-Since(whenIf-Matchabsent) → 412If-None-Match→ 304If-Modified-Since(whenIf-None-Matchabsent) → 304The ETag format is
"<hex_secs>.<hex_nanos>-<hex_size>". Similar to what nginx does (<mtime>-<size>) but with nanosecond precision. No content hashing, no extra I/O; we already callmetadata()on every request.304 responses now include
ETagandLast-Modifiedheaders per RFC 9110 §15.4.5 (previously they were bare, which was a spec violation).No new public API surface. All new types are
pub(super). The ETag is always-on, matching the existingLast-Modifiedbehavior.Testing
Covers: ETag presence on 200,
If-None-Matchproducing 304 (exact match, wildcard, weak prefix),If-Matchproducing 412 (non-matching, weak prefix rejected by strong comparison), RFC precedence (ETag conditions override time-based when both present), and validator headers on 304 responses.