fix: include accurate Allow headers on 405 responses - #295
Conversation
33e4bf9 to
b8295d6
Compare
Static Review CommentsBranch:
Verification performedEvery finding below was checked against the running app (
The core of the issue is solved. Findings are about the edges. Critical Issues 🔴None. Major Issues 🟠🟠 Issue 1:
|
| Request | Today | Should be |
|---|---|---|
POST /v1/api |
404 | 405 Allow: GET,HEAD,OPTIONS |
POST / |
404 | 405 Allow: GET,HEAD,OPTIONS |
POST /client/register |
404 | 405 Allow: GET,HEAD,OPTIONS |
GET /client/request-new-access-token |
404 | 405 Allow: POST,OPTIONS |
GET /client/request-new-refresh-token |
404 | 405 Allow: POST,OPTIONS |
POST /client/verify |
404 | 405 Allow: GET,HEAD,OPTIONS |
Returning 404 for a valid resource reached with the wrong method is misleading — it tells the client the resource does not exist. Recommend a follow-up issue rather than growing this PR.
🔵 Suggestion 3: OPTIONS responses carry no Allow at all
Related to Issue 1 and to the utils.js removal. Before this PR, configureWebAnnoHeadersFor() put an Allow on 200 responses; it was wrong, and removing it was the right call. The side effect is that Allow now appears only on 405s.
RFC 9110 §9.3.7 says a successful OPTIONS response SHOULD advertise supported methods, and OPTIONS is the request a client makes precisely to ask that question. Today it answers 204 with nothing:
OPTIONS /v1/id/abc123 -> 204 Allow: <none>
Clients can read Access-Control-Allow-Methods from the CORS layer, but that is the blanket GET,OPTIONS,HEAD,PUT,PATCH,DELETE,POST from app.js:34 — the same over-advertisement issue #287 objected to, just relocated. Worth its own issue.
🔵 Suggestion 4: The 405 explanation is invisible to most clients
Every 405 sends Content-Length: 0 and puts its message in the HTTP reason phrase:
HTTP/1.1 405 Improper request method for creating, please use POST.
Allow: POST
Content-Length: 0
The reason phrase is not surfaced by fetch(), is dropped entirely by HTTP/2, and is invisible in most tooling. Since rest.messenger already returns text/plain bodies for other error classes, 405 could send its message as a body for consistency. Pre-existing behavior and a client-visible change, so it belongs in its own issue — noting it because this PR consolidated all 20 of these into one helper, which makes it a one-line change if the team wants it.
🔵 Suggestion 5: req and next are unused in all 20 fallback handlers
.all((req, res, next) => {
rest.sendMethodNotAllowed(res, '...', 'POST')
})Neither is read. Harmless and consistent with the pre-existing style, so leaving it is fine — but adopting Suggestion 1 removes the whole signature anyway, and if that is declined, .all((req, res) => ...) is more honest about what the handler touches.
What this PR gets right
- It fixes the real bug. The blanket
Allow: GET,OPTIONS,HEAD,PUT,PATCH,DELETE,POSTinutils.jswas actively lying on every 200 — issue 405 Responses NeedAllowHeader #287 named it, and removing it was the correct call rather than trying to make it per-route. - Express's implicit HEAD→GET dispatch is handled correctly. Adding
HEADalongsideGETis easy to get wrong in either direction, and it is right on all five GET routes — confirmed against a live record, not assumed. /v1/api/queryis exactly right. It has an explicit.head()and no.get(), and its value isPOST,HEADrather than a reflexivePOST,GET,HEAD. That is the case a careless pass would have broken.- The legacy compatibility layer works for free.
express-urlrewritepaths land on the rewritten route's fallback and inherit the correct value with no special casing. - One helper, not 20 copy-pasted blocks. The consolidation is what makes Issues 1 and 2 one-line fixes instead of 20-file sweeps.
- The tests derive expectations from Express route metadata rather than restating the same literals the implementation uses, so they can actually catch drift.
If there are significant code changes in response to this review please test those changes. Run the application manually and test. Run internal programmatic tests when applicable.
Summary
Resolves #287.
GitHub Copilot implemented this change to ensure every route-generated 405 response includes an accurate RFC 9110
Allowheader.Allow: PATCH,POSTto invalid PATCH override responses.HEADwhere routes explicitly support it.Allowvalues that advertised unsupported methods.Verification
node --env-file-if-exists=.env --import ./test/bootstrap.js --test routes/__tests__/route_wrappers.test.js: 27 passed.npm test: 22 test files passed, 0 failed.Allowvalues.git diff --check: passed.The standalone
npm startprobe was attempted, but requests did not complete reliably in the debug-instrumented terminal environment; the in-process server checks completed successfully.