Skip to content

Answer conditional requests with 304 - #72

Merged
beetlebugorg merged 1 commit into
mainfrom
feat/conditional-requests
Aug 27, 2026
Merged

beetlebugorg merged 1 commit into
mainfrom
feat/conditional-requests

Conversation

@beetlebugorg

Copy link
Copy Markdown
Owner

The service sent an ETag and never read If-None-Match, so it answered every revalidation in full. A CDN checking whether its copy is still good got the whole image back regardless.

Where the check goes

After the source is fetched, before anything is decoded.

That placement is forced by how the etag is built: it hashes the commands, the image URL, and the origin's own etag, so it cannot be known until the source has been retrieved. There is no cache mapping a response etag back to a source etag, so the fetch cannot be skipped.

What a 304 does skip is the decode, the transformation, and the encode. Measurements taken while looking at request cost put that at 200 to 400 ms for an ordinary resize and 3.2 s for a large upscale, against a download that is usually a fraction of it. The expensive half is the half this avoids.

Matching

If-None-Match is compared per RFC 9110: * matches anything, a comma separated list is searched, and the comparison is weak, so a W/ prefix on either side is ignored.

No origin etag means no response etag, and then no conditional handling. That is unchanged behaviour, since Etag() already returned empty in that case.

Lambda

aws.NewRequest built its http.Request with a URL and nothing else, so no request header ever reached the shared code. The incoming headers are now copied across, which makes the conditional check work there as well rather than silently returning false.

SendNotModified is implemented separately for that transport, as it builds a streaming response rather than writing to a ResponseWriter.

Tests

TestNotModified covers an exact match, a weak validator, a list, a wildcard, a non-matching value, an absent header, and an absent origin etag.

TestSendNotModified asserts the response carries the cache headers, has status 304, and has no body or Content-Length. TestSendNotModifiedOnlyCommitsOnce extends the commit guard from #63 to this path.

Verification

In ghcr.io/beetlebugorg/go-dims:builder: gofmt, go vet, and go test -race ./... all pass.

- Compare If-None-Match against the response etag after fetching the source.
- Skip decode, transformation, and encode on a match.
- Carry the incoming headers through the Lambda transport.
@beetlebugorg
beetlebugorg merged commit 4e88d00 into main Aug 27, 2026
4 checks passed
@beetlebugorg
beetlebugorg deleted the feat/conditional-requests branch August 27, 2026 20:18
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.

1 participant