Opened 3 weeks ago

Last modified 10 days ago

#37428 assigned New feature

Add support for HTTP QUERY method to CacheMiddleware/@cache_page

Reported by: Jacob Walls Owned by: Michiel Beijen
Component: Core (Cache system) Version: dev
Severity: Normal Keywords:
Cc: Jonathan Biemond, Michiel Beijen Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Michiel Beijen)

Sibling ticket for #37232, which is already merged in main.

#37232 left caching out of scope. We should consider adding support for caching HTTP QUERY methods to the cache middleware if we can answer these questions:

  • Cache key: I presume we need to append a hash of the request body to the cache key, without parsing.
  • Re-reading: what happens when a view calls request.read()? This would cause RawPostDataException when trying to read the raw body again to calculate the cache key afterward. (Calculating the key after the view could happen on cold-caches.)
  • Do we let RequestDataTooBig emanate from the cache middleware instead of the view? This could catch someone who has except RequestDataTooBig in a def post() decorated with @csrf_exempt that they are converting to def query(). Or do we treat the over-limit case as "don't cache" and let the view run? One way or the other we might need a backward incompatible change notice.

Change History (6)

comment:1 by Sarah Boyce, 2 weeks ago

Triage Stage: Unreviewed → Someday/Maybe

comment:2 by Michiel Beijen, 12 days ago

I'm interested in taking this one. I have a draft implementation stacked on the #37232 branch; this compare view shows only the caching commit:
​https://github.com/mbeijen/django/compare/ticket_37232...http-query-caching

On the three open questions:

  1. Cache key: for QUERY requests the key also hashes the Content-Type and the raw request body, without parsing it, as RFC 10008 section 2.7 requires. QUERY entries use their own method component in the key, so they never mix with cached GET/HEAD responses for the same URL (unlike HEAD, which can be served from a cached GET).
  1. Re-reading: FetchFromCacheMiddleware.process_request() (and so cache_page()) reads request.body before the view runs. A view that later calls request.read() then reads from the copy Django keeps in memory, and on a cold cache learn_cache_key() in the response phase no longer hits RawPostDataException. If an earlier middleware already consumed the stream, the request just isn't cached.
  1. Size limit: I used "don't cache". If the body exceeds DATA_UPLOAD_MAX_MEMORY_SIZE, the middleware catches RequestDataTooBig, marks the request as not cacheable and lets the view run as normal. No new exceptions are raised. Since caching QUERY is new, I don't think this needs a backwards-incompatible change note, but I'm happy to add one if you see it differently. get_cache_key()/learn_cache_key() called directly on such a request will still raise.

I'll open a PR once #37232 has landed, so the diff doesn't include those commits.

AI disclosure: I used Claude Code to draft and rebase the implementation and tests; I reviewed the changes and verified the new tests fail without the fix.

comment:3 by Jacob Walls, 12 days ago

Owner: changed from Participants on sibling ticket have right of first refusal to Michiel Beijen

comment:4 by Jacob Walls, 10 days ago

Triage Stage: Someday/Maybe → Accepted

comment:5 by Michiel Beijen, 10 days ago

Description: modified (diff)
Has patch: set

comment:6 by Michiel Beijen, 10 days ago

Note: See TracTickets for help on using tickets.
Back to Top