Add If-Modified-Since / 304 Not Modified support to the content app
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 598
- Forks
- 168
- Avg merge
- 1d 4h
- Merged PRs (30d)
- 86
Description
Problem
The content app always returns a full response (200 with body or 302 redirect) regardless of whether the content has changed since the client last requested it. This prevents edge caches (Akamai, nginx) from using conditional requests to revalidate cached content — every request results in a full download or redirect, even for immutable artifacts that never change.
Proposed Solution
Add support for If-Modified-Since conditional requests and Last-Modified response headers to the content handler. When a client sends If-Modified-Since and the requested content has not changed, Pulp should return 304 Not Modified with no body.
The Last-Modified value should be set to RepositoryContent.pulp_created — the timestamp of when the specific content unit was added to the repository being served. This is more accurate than using the repository version creation time, since some repositories get new versions frequently. pulp_python's Simple API already uses this same approach for its upload_time field.
Implementation
Both the non-cached and Redis-cached content serving paths need to handle this.
Non-cached path (_match_and_stream)
After ContentGuard authorization passes and the content artifact is resolved, but before building the response:
- Look up
RepositoryContent.pulp_createdfor the content being served - Set
Last-Modifiedheader on all 200 responses - If the request includes
If-Modified-Sinceand the content has not been modified since that datetime, return304 Not Modified(no body)
Redis-cached path (AsyncContentCache)
- Include a
last_modifiedtimestamp in the cached entry JSON (alongsideheaders,status,expires,type) - On cache hit, after ContentGuard authorization passes, check
If-Modified-Sinceagainst the storedlast_modifiedtimestamp - If not modified, return 304 instead of reconstructing the full cached response
- If modified or no
If-Modified-Sinceheader, return the cached response as normal withLast-Modifiedheader set
Response behavior summary
| Condition | Response |
|---|---|
| Not authorized | 403 Forbidden |
Authorized, no If-Modified-Since |
200 with body + Last-Modified header |
Authorized, content changed since If-Modified-Since |
200 with body + Last-Modified header |
Authorized, content not changed since If-Modified-Since |
304 Not Modified (no body) |
Use Case
This enables edge caching architectures where a CDN (e.g., Akamai with Centralized Authorization) or a reverse proxy (e.g., nginx with proxy_cache_revalidate) caches content at the edge and uses lightweight conditional requests to Pulp for revalidation. Pulp still performs authorization on every request, but avoids transferring the full binary when the cached copy is still valid.
Related Code
pulpcore/content/handler.py—Handler.stream_content(),_match_and_stream(),_build_response_from_content_artifact()pulpcore/cache/cache.py—AsyncContentCache,make_response(),make_entry()pulpcore/responses.py—ArtifactResponse
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in pulpcore/content/handler.py with Handler.stream_content(), _match_and_stream(), and _build_response_from_content_artifact(), then read pulpcore/cache/cache.py for AsyncContentCache, make_response(), and make_entry(). Trace how RepositoryContent.pulp_created and authorization are handled in both serving paths. Done means authorized unchanged content returns 304 without a body, while other authorized responses include Last-Modified and cached entries preserve the timestamp.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- api, authentication, backend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100