pulp / pulp/pulpcore

Add If-Modified-Since / 304 Not Modified support to the content app

Open
#7,929 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Feature
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:

  1. Look up RepositoryContent.pulp_created for the content being served
  2. Set Last-Modified header on all 200 responses
  3. If the request includes If-Modified-Since and the content has not been modified since that datetime, return 304 Not Modified (no body)
Redis-cached path (AsyncContentCache)
  1. Include a last_modified timestamp in the cached entry JSON (alongside headers, status, expires, type)
  2. On cache hit, after ContentGuard authorization passes, check If-Modified-Since against the stored last_modified timestamp
  3. If not modified, return 304 instead of reconstructing the full cached response
  4. If modified or no If-Modified-Since header, return the cached response as normal with Last-Modified header 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.pyHandler.stream_content(), _match_and_stream(), _build_response_from_content_artifact()
  • pulpcore/cache/cache.pyAsyncContentCache, make_response(), make_entry()
  • pulpcore/responses.pyArtifactResponse

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.