Serve HTTP validators on the remaining server-rendered pages - #760
Draft
Wavesonics wants to merge 1 commit into
Draft
Serve HTTP validators on the remaining server-rendered pages#760Wavesonics wants to merge 1 commit into
Wavesonics wants to merge 1 commit into
Conversation
Home, author, about, privacy and terms now answer 304 to a reader who already holds the page, through a respondPage helper that hashes the completed Mustache model just before responding. Hashing there rather than per-page means every value shaping the page is in the validator by construction; anything that shapes the response without living in the model, such as the X-Robots-Tag that community_member gates on the author page, is passed explicitly. The public story reader keeps its own early-hash path: it has a cheap fingerprint available before the render, so it can skip the render entirely, which respond-time hashing cannot do. Fixes a staleness hole this exposed. pageETag skips the bulk msg bundle on the grounds that it is determined by locale and version, but the msg(model, ...) overload writes messages formatted at request time into that same map. On the home page that hid the admin contact email from the validator, so changing the address would leave every browser holding the old one on a 304 forever. Formatted messages are now mirrored into a hashed key, covering future uses without per-page wiring. Measured through the e2e harness, the 304 path is not faster than a full render: the cost of these pages is model construction, not the template or the body. The win is bandwidth, roughly 16 KB per revalidation.
Contributor
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 0 |
| Duplication | 0 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
Wavesonics
marked this pull request as draft
July 21, 2026 22:35
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.
Follow-on to #758, which added
pageETag/matchesETag/applyRevalidationHeadersand used them only on the public story reader.What changed
Home, author, about, privacy and terms now answer
304to a reader who already holds the page. They go through one new helper,ApplicationCall.respondPage(template, model, vararg extra), which hashes the completed Mustache model just before responding.Hashing at respond time rather than wiring
matchesETag+applyRevalidationHeadersinto each page means every value that shapes the page is in the validator by construction — a field added to a page joins its validator instead of quietly serving a stale copy. Anything that shapes the response without living in the model must be passed asextra; on the author page that iscommunity_member, which gates theX-Robots-Tagheader.The public story reader keeps its own early-hash path and is deliberately not converted. It has a cheap fingerprint available before the expensive render, so it can skip the render entirely — something respond-time hashing cannot do. The two patterns are not interchangeable, and the other pages have no such cheap probe.
Staleness bug this exposed
pageETagskips the bulkymsgbundle, on the grounds that it is fully determined by the hashedlocaleandversionentries. Themsg(model, …)overload breaks that invariant: it writes messages formatted at request time into that same map.On the home page this hid the admin contact email from the validator — it reaches the page only through
home_servermessage_whitelistand appears nowhere else in the model. An admin changing the address would produce an identical ETag, and every browser holding the old one would be served a304with the stale address indefinitely.Fixed at the source rather than by hand-wiring
contactEmailinto one page'spageETagcall: formatted messages are mirrored into a hashed model key, so any future use of that overload is covered automatically.Written test-first per the repo convention. With the mirror removed, the new test fails with exactly the predicted symptom (
a changed contact address must not answer 304).Is it worth it? Measured, not assumed
Through the
EndToEndTestharness:GET /(full render, 16.7 KB body)GET /withIf-None-Match→ 304GET /about(one-line body)GET /robots.txt(floor)The 304 path is not measurably faster than a full render. Essentially none of a page's cost is the Mustache render or the body — it is all model construction, before the template is touched. Roughly 157 ms is a baseline every page pays in the
withDefaults/withMessagesshell; home adds ~160 ms of its own.Absolute numbers are harness-inflated (SQLite over a FakeFileSystem, debug logging), so treat them as a ratio rather than a production figure. The ratio is the point, and it says the remaining benefit here is bandwidth only — ~16 KB per revalidation, which is real for mobile readers but is not a server-load argument. That baseline page latency looks like the more valuable target and is worth its own investigation; it is out of scope here.
Testing
PageValidatorTest.kt, driving the real routes through theEndToEndTestharness: validator present and weak,Cache-Control/Vary, the 304, and invalidation after a server-message, contact-email, about-text, bio, or community-membership change.