Skip to content

HEAD /query answers an empty page with 404 where POST /query answers 200 [] #304

Closed

Description

@thehabes

Summary

POST requests do not support HEAD. HEAD request handling should be removed from all POST routes and fail into a 'wrong method' response.

RFC 9110 defines HEAD as GET without content, so a route with no GET has nothing for HEAD to mirror. /query was the only POST route with a HEAD handler of its own (routes/query.js:9, queryHeadRequest at controllers/history.js:86). Every other POST route already answered HEAD with a 405 through its .all() fallback.

Why this matters

The 404 in the title was the smallest of the handler's problems:

  • It answered a request it could not read. /query needs its filter in the request body, and HEAD has no defined body semantics. Some clients refuse to send one at all; supertest (superagent) is one of them.
  • With no body it matched everything. queryHeadRequest lacked the empty-body guard POST /query has, so it ran db.find(undefined). A bodiless HEAD /v1/api/query returned 200 with Content-Length: 536127 against the dev collection, while POST /query with {} is a 400.
  • It disagreed with POST on an empty result, answering 404 where POST /query answers 200 [].

Removing the handler resolves all three rather than repairing one.

Changes

  • routes/query.js: the .head(controller.queryHeadRequest) line is removed. HEAD /query now falls into the route's existing .all() 405, and so does HEAD on the legacy /v1/api/getByProperties.action alias, which is rewritten to /query.
  • controllers/history.js: queryHeadRequest is deleted, along with its export and the getPagination import only it used. db-controller.js no longer exports it.
  • openapi/contracts/core-provider.openapi.yaml: the head: operation under /api/query is removed, and __tests__/core_provider_contract.test.js no longer requires response codes for HEAD /api/query.
  • routes/__tests__/query.test.js: the HEAD /query tests are removed.
  • routes/__tests__/route_wrappers.test.js: every route in the existing 405 table that has no GET handler is now asserted to have no HEAD handler of its own, so a HEAD handler cannot be reintroduced on a POST, PUT, PATCH, or DELETE route without a failing test.
  • public/API.html: the result-ordering section no longer says /query pages "on both POST and HEAD".

Not changed

  • HEAD on GET routes — /id/:_id, /id/:_id/expanded, /since/:_id, and /history/:_id — still answers 200. Their identifier is in the URL, and Express answers HEAD from the GET handler, which is valid HTTP. HEAD history - since - query #96 should not be read as removing those.
  • The .all() 405 fallbacks still send no Allow header, which RFC 9110 requires on a 405. That applies to every route with a method fallback and is left for a separate issue.

Verification

Against the local pm2 app:

HEAD /v1/api/query                          -> 405
HEAD /v1/api/getByProperties.action         -> 405
HEAD /v1/api/search                         -> 405
HEAD /v1/api/create                         -> 405
HEAD /gog/fragmentsInManuscript             -> 405
HEAD /v1/id/000000000000000000000001        -> 200
HEAD /v1/since/000000000000000000000001     -> 200
HEAD /v1/history/000000000000000000000001   -> 200

Acceptance criteria

  • HEAD on every POST route answers 405 through the route's method fallback, including /query and its getByProperties alias
  • A test fails if a route without a GET handler defines its own HEAD handler
  • queryHeadRequest and its export are removed
  • HEAD /api/query is removed from the OpenAPI contract and from the contract test's required response codes
  • HEAD on the GET routes is unchanged
  • public/API.html no longer describes HEAD on /query
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions