Return and pass on request IDs, and give /v1/e/ ETag, 304 and HEAD (closes #84) #179

Merged
clawbot merged 8 commits from issue-84-response-headers into next 2026-10-04 12:41:55 +02:00
Collaborator

Closes #84, per its plan.

  • Every response carries X-Request-ID, from pixa's own RequestID middleware, which replaces chi's: it keeps the request's own X-Request-ID when that is at most 64 letters, digits, -, _ or ., and otherwise makes a random one with crypto/rand, which says nothing about the host or the traffic. It stores the ID where chi's did, so the access log and GetReqID read it as before.
  • The upstream fetch sends that ID, and the "upstream fetched", "image converted" and "image served" log lines carry it as request_id. A fetch shared by several requests carries the first request's ID; README.md says so.
  • /v1/e/ sets ETag, answers a matching If-None-Match with 304 and is routed for HEAD. The ETag/304 code is now notModified, which both image handlers call; nothing else in them is merged (#168).
  • Vary: none added. go-chi/cors already sends Vary: Origin on every image-route response. Vary: Accept comes with #88.

Disclosures:

  • Deviation: HandleImageEnc was at the 80-line funlen limit, so its token checks moved, unchanged, into parseImageEncRequest, as parseImageRequest does for /v1/image/.
  • Deviation: CONVENTIONS.md names chi's RequestID; pixa no longer uses it.
  • Judgement call: a request whose own ID is not short and plain is served with a fresh ID, which the response shows, not refused.
  • Judgement call: the test helper newSignedHostServer now takes a logger; its two callers pass a discarding one, and no existing assertion changed.
  • Judgement call: only the three log lines the issue names carry request_id.

Model: opus-5-5

Closes https://git.eeqj.de/sneak/pixa/issues/84, per its plan. - Every response carries `X-Request-ID`, from pixa's own `RequestID` middleware, which replaces chi's: it keeps the request's own `X-Request-ID` when that is at most 64 letters, digits, `-`, `_` or `.`, and otherwise makes a random one with `crypto/rand`, which says nothing about the host or the traffic. It stores the ID where chi's did, so the access log and `GetReqID` read it as before. - The upstream fetch sends that ID, and the "upstream fetched", "image converted" and "image served" log lines carry it as `request_id`. A fetch shared by several requests carries the first request's ID; `README.md` says so. - `/v1/e/` sets `ETag`, answers a matching `If-None-Match` with 304 and is routed for `HEAD`. The `ETag`/304 code is now `notModified`, which both image handlers call; nothing else in them is merged (https://git.eeqj.de/sneak/pixa/issues/168). - `Vary`: none added. `go-chi/cors` already sends `Vary: Origin` on every image-route response. `Vary: Accept` comes with https://git.eeqj.de/sneak/pixa/issues/88. Disclosures: - Deviation: `HandleImageEnc` was at the 80-line `funlen` limit, so its token checks moved, unchanged, into `parseImageEncRequest`, as `parseImageRequest` does for `/v1/image/`. - Deviation: `CONVENTIONS.md` names chi's `RequestID`; pixa no longer uses it. - Judgement call: a request whose own ID is not short and plain is served with a fresh ID, which the response shows, not refused. - Judgement call: the test helper `newSignedHostServer` now takes a logger; its two callers pass a discarding one, and no existing assertion changed. - Judgement call: only the three log lines the issue names carry `request_id`. Model: opus-5-5
clawbot added the needs-review label 2026-10-04 10:51:29 +02:00
clawbot self-assigned this 2026-10-04 10:51:29 +02:00
clawbot added 6 commits 2026-10-04 10:51:30 +02:00
A response of any route and status must carry the request's ID: one pixa
made up when the request brought none, otherwise the request's own
X-Request-Id. No response carries it yet.

Model: opus-5-5
Send the request's ID back as X-Request-Id
check / check (push) Failing after 2s
afe4b3bb70
A middleware right after chi's RequestID sets the X-Request-Id response
header from the ID RequestID stored in the request context, so every
response carries it and a client can quote it when reporting a problem.
README "Routes" says where the ID comes from.

Model: opus-5-5
A fetch must send the ID of the request it serves as X-Request-Id, and
the "upstream fetched", "image converted" and "image served" lines must
carry it as request_id, through either image route. newSignedHostServer
takes the logger its handlers and image service write to; its existing
callers pass a discarding one.

Model: opus-5-5
The fetcher sends the request ID chi's RequestID middleware put in the
context as X-Request-Id, next to User-Agent and Accept, and the
"upstream fetched", "image converted" and "image served" lines carry it
as request_id, as the request log line does. A fetch shared by several
requests runs with the first request's context values, so it carries
that request's ID; README says so where it describes the shared fetch.

Model: opus-5-5
An image served through an encrypted URL must carry an ETag, a request
whose If-None-Match is that ETag must get 304 with no body, HEAD must get
200 with the headers and no body, and the server must route HEAD on
/v1/e/ to its handler instead of answering 405.

Model: opus-5-5
The ETag and If-None-Match code of the /v1/image/ handler becomes
notModified, which both image handlers call; /v1/e/ answers HEAD with
the headers only and is routed for HEAD. HandleImageEnc was at the
80-line function limit, so its token checks move unchanged into
parseImageEncRequest, as parseImageRequest does for /v1/image/. No Vary
is added: only the image routes' CORS headers depend on a request
header, and go-chi/cors already sends Vary: Origin with them.

Model: opus-5-5
Author
Collaborator

FAIL (needs-rework)

  1. A client's own X-Request-ID reaches the upstream host exactly as it came, whatever its length or content (chi's RequestID in internal/server/routes.go, sent on in internal/httpfetcher/httpfetcher.go). pixa accepts a value of about 9 KB, which nginx and Apache refuse with their default limits; pixa then answers 502 for that source URL to every client for 5 minutes, as after any upstream error. So one request can make an image pixa has not fetched yet unavailable to everyone, again and again, and any client can put text of its choosing into pixa's requests to other sites. Acceptable: pixa takes a request's own ID only when it is short and plain (for example at most 64 letters, digits, -, _ or .) and makes one up otherwise, with a test that an over-long or unusual incoming ID is neither sent back nor sent upstream.
  2. The ID pixa makes up is chi's: pixa's host name, a string fixed at startup and a running count, as README.md "Routes" now says. Until now it stayed in pixa's logs; with this change it goes to every client and every upstream host, telling them the machine or container name and how many requests pixa has served since it started. Acceptable: an ID that tells nothing about the machine or the traffic, such as a random string made for each request with Go's crypto/rand, and "Routes" saying so. Together with finding 1 this most likely means a small middleware of pixa's own in place of chi's RequestID.

Judgement call: both are taken as defects, not as questions for the owner, because the exposure is new with this change and REPO_POLICIES.md asks for hardening by default.

Model: opus-5-5

**FAIL** (needs-rework) 1. A client's own `X-Request-ID` reaches the upstream host exactly as it came, whatever its length or content (chi's `RequestID` in `internal/server/routes.go`, sent on in `internal/httpfetcher/httpfetcher.go`). pixa accepts a value of about 9 KB, which nginx and Apache refuse with their default limits; pixa then answers 502 for that source URL to every client for 5 minutes, as after any upstream error. So one request can make an image pixa has not fetched yet unavailable to everyone, again and again, and any client can put text of its choosing into pixa's requests to other sites. Acceptable: pixa takes a request's own ID only when it is short and plain (for example at most 64 letters, digits, `-`, `_` or `.`) and makes one up otherwise, with a test that an over-long or unusual incoming ID is neither sent back nor sent upstream. 2. The ID pixa makes up is chi's: pixa's host name, a string fixed at startup and a running count, as `README.md` "Routes" now says. Until now it stayed in pixa's logs; with this change it goes to every client and every upstream host, telling them the machine or container name and how many requests pixa has served since it started. Acceptable: an ID that tells nothing about the machine or the traffic, such as a random string made for each request with Go's `crypto/rand`, and "Routes" saying so. Together with finding 1 this most likely means a small middleware of pixa's own in place of chi's `RequestID`. Judgement call: both are taken as defects, not as questions for the owner, because the exposure is new with this change and `REPO_POLICIES.md` asks for hardening by default. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-04 11:20:20 +02:00
clawbot added 1 commit 2026-10-04 11:43:49 +02:00
A request's own X-Request-Id must be kept only when it is at most 64
letters, digits, '-', '_' or '.'; any other must be neither sent back
nor sent upstream, and the request gets a fresh ID instead. An ID made
up for a request must not hold the host name and must differ for every
request. pixa has no middleware of its own for this yet.

Model: opus-5-5
clawbot added 1 commit 2026-10-04 11:44:50 +02:00
pixa's own RequestID middleware replaces chi's and the response-header
middleware. It keeps a request's own X-Request-Id only when it is at
most 64 letters, digits, '-', '_' or '.', so an over-long or odd value
is never sent back or upstream, and otherwise makes a random ID with
crypto/rand, which tells nothing about the host or how many requests
pixa served. It stores the ID under chi's RequestIDKey, where the
logging middleware, the handlers and the fetcher read it.

Model: opus-5-5
clawbot added needs-review and removed needs-rework labels 2026-10-04 11:50:31 +02:00
Author
Collaborator
  1. pixa's own RequestID middleware replaces chi's: a request's own X-Request-ID is kept only when it is at most 64 letters, digits, -, _ or .; any other is neither sent back nor sent upstream, and the request gets a fresh ID.
  2. A made-up ID is now a random string from crypto/rand, with nothing about the host or the traffic in it; "Routes" in README.md says how the ID is chosen.

Model: opus-5-5

1. pixa's own `RequestID` middleware replaces chi's: a request's own `X-Request-ID` is kept only when it is at most 64 letters, digits, `-`, `_` or `.`; any other is neither sent back nor sent upstream, and the request gets a fresh ID. 2. A made-up ID is now a random string from `crypto/rand`, with nothing about the host or the traffic in it; "Routes" in `README.md` says how the ID is chosen. Model: opus-5-5
Author
Collaborator

PASS at c1c28204a1bb9e91e3a973a49b28a90a0e38f1ec, rebased onto next at 363774c058d2583c5d241e5a669de8e7143ca1b5.

Model: opus-5-5

**PASS** at `c1c28204a1bb9e91e3a973a49b28a90a0e38f1ec`, rebased onto `next` at `363774c058d2583c5d241e5a669de8e7143ca1b5`. Model: opus-5-5
clawbot merged commit c7173c47d8 into next 2026-10-04 12:41:55 +02:00
clawbot deleted branch issue-84-response-headers 2026-10-04 12:41:56 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/pixa#179