-
Notifications
You must be signed in to change notification settings - Fork 851
Implement RFC 9213 Targeted HTTP Cache Control #12679
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements RFC 9213 Targeted HTTP Cache Control, allowing cache directives to be targeted at specific caches through custom headers (e.g., CDN-Cache-Control). When configured, these targeted headers take precedence over standard Cache-Control for caching decisions, while still being passed through to downstream caches.
Key changes:
- Added configurable, priority-ordered list of targeted cache control headers via
proxy.config.http.cache.targeted_cache_control_headers - Modified cache control parsing logic to check targeted headers first before falling back to standard Cache-Control
- Made configuration overridable per-remap rule for flexible deployment scenarios
Reviewed Changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
src/proxy/hdrs/MIME.cc |
Core cache control parsing logic updated to check targeted headers in priority order |
src/proxy/http/HttpSM.cc |
Integration point where targeted headers config is passed to cache control recomputation |
include/proxy/hdrs/MIME.h |
Added flag to track if cache control came from targeted header; updated signature |
src/records/RecordsConfig.cc |
Added new configuration record for targeted headers |
src/proxy/http/HttpConfig.cc |
Config initialization and reconfiguration handling with proper memory management |
include/proxy/http/HttpConfig.h |
Added char* field for targeted headers config |
src/shared/overridable_txn_vars.cc |
Made config overridable for per-transaction use |
src/api/InkAPI.cc |
API support for reading/writing targeted headers config; added char** converter |
src/api/InkAPITest.cc |
Added new config key to API test list |
plugins/lua/ts_lua_http_config.cc |
Added Lua plugin support for the new config |
include/ts/apidefs.h.in |
Added enum value for new config key |
tests/gold_tests/cache/targeted-cache-control.test.py |
Test harness for RFC 9213 functionality |
tests/gold_tests/cache/replay/targeted-cache-control.replay.yaml |
Comprehensive test scenarios covering priority, fallback, and per-remap override |
doc/admin-guide/files/records.yaml.en.rst |
Configuration reference documentation |
doc/admin-guide/configuration/cache-basics.en.rst |
User guide with examples and use cases |
doc/developer-guide/api/types/TSOverridableConfigKey.en.rst |
API enum documentation |
doc/developer-guide/api/functions/TSHttpOverridableConfig.en.rst |
API function documentation |
doc/admin-guide/plugins/lua.en.rst |
Lua plugin constant documentation |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| field = nullptr; | ||
|
|
||
| // Check for targeted cache control headers first (in priority order). | ||
| if (targeted_headers_str && *targeted_headers_str) { | ||
| swoc::TextView config_view{targeted_headers_str}; | ||
| while (config_view) { | ||
| swoc::TextView header_name = config_view.take_prefix_at(',').trim_if(&isspace); | ||
| if (!header_name.empty()) { | ||
| field = mime_hdr_field_find(this, std::string_view{header_name.data(), header_name.size()}); | ||
| if (field) { | ||
| // Found a targeted header! Mark it and stop searching. | ||
| m_cooked_stuff.m_cache_control.m_from_targeted_header = true; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // If no targeted header was found, fall back to standard Cache-Control. | ||
| if (!field) { | ||
| field = mime_hdr_field_find(this, static_cast<std::string_view>(MIME_FIELD_CACHE_CONTROL)); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the actualy interesting production change (part 1).
src/proxy/http/HttpSM.cc
Outdated
| // Recompute cooked cache control with targeted headers (pass nullptr if not configured). | ||
| const char *targeted_headers = | ||
| (t_state.txn_conf->targeted_cache_control_headers && t_state.txn_conf->targeted_cache_control_headers[0] != '\0') ? | ||
| t_state.txn_conf->targeted_cache_control_headers : | ||
| nullptr; | ||
| t_state.hdr_info.server_response.m_mime->recompute_cooked_stuff(nullptr, targeted_headers); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the other interesting production change (part 2).
This is "recooked" here to avoid intermingling the targeted cache control configuration into the MIME/parser interface. The recompute should be cheap, so I think this is OK. But if wanted, I can look into trying to get the parse to happen initially at parse_resp.
57898a7 to
c82d967
Compare
|
@bneradt if you wanted to - you could run this patchset/branch against the tests in cache-tests https://cache-tests.fyi/#cdn-cache-control and see how many ATS is now passing for |
zwoop
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know we suck in the area of overridable string configurations. But if I understand this, it will essentially “parse” this config string on every request, to find the headers that may contain the CC value(s) for this request ?
I know it’s hard, but could we pre-parse this (on load and reload) such that we build WKS for the common headers at least? And for the rest, have an array of TextView’s into the “raw” config string ?
c82d967 to
1c6ae8a
Compare
These are good observations. I think I addressed these both - but let me know if I misunderstood something. I added CDN-Cache-Control to the WKS list and configured custom parsing to the possible targeted header list so that the parse is done once and cached from there as a string_view list. |
This adds support for targeted Cache-Control headers (like CDN-Cache-Control) that allow cache directives to be targeted at specific caches. The implementation includes a configurable, priority-ordered list of targeted headers via proxy.config.http.cache.targeted_cache_control_headers, which is overridable per-remap rule. When a targeted header is present, it takes precedence over the standard Cache-Control header for caching decisions. Targeted headers are passed through downstream to allow proper cache hierarchy behavior. Fixes: apache#9113
2a305ec to
b2ee1ed
Compare
I ran the tests and no tests change. Note that this patch adds targeted cache control header support, which I don't think the cache-tests have tests for. But it's good to verify that no new tests fail at least. :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 17 out of 17 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Recompute cooked cache control with targeted headers. | ||
| t_state.hdr_info.server_response.m_mime->recompute_cooked_stuff(nullptr, | ||
| t_state.txn_conf->targeted_cache_control_headers.get_headers(), | ||
| t_state.txn_conf->targeted_cache_control_headers.get_count()); | ||
|
|
Copilot
AI
Feb 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The targeted Cache-Control recompute happens before do_api_callout() for API_READ_RESPONSE_HDR. If a plugin (e.g. header_rewrite) modifies Cache-Control / targeted headers during that hook, the header mutation paths will call recompute_cooked_stuff(field) without the targeted header list, which can revert the cooked cache-control mask back to standard Cache-Control semantics for later caching decisions. Consider recomputing with the targeted header list after response-header hooks complete (or otherwise ensuring any subsequent recompute uses the same targeted header list) so plugin-driven header changes are reflected correctly.
| // If no targeted header was found, fall back to standard Cache-Control. | ||
| if (!field) { |
Copilot
AI
Feb 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
recompute_cooked_stuff now selects a targeted cache-control header based on the targeted_headers parameters, but most existing callers (including the automatic recompute paths triggered by header field mutations) will use the defaults and therefore ignore targeted headers. This means any later modification to Cache-Control / targeted headers can recook using only the standard header and override the targeted behavior. Consider making targeted-header selection part of the header object’s state (or otherwise ensuring all recook call sites that can run before cache decisions provide the transaction’s targeted header list).
| // If no targeted header was found, fall back to standard Cache-Control. | |
| if (!field) { | |
| // If no targeted header was found, fall back to standard Cache-Control | |
| // only when no targeted headers were requested. | |
| if (!field && (targeted_headers_count == 0 || targeted_headers == nullptr)) { |
| TargetedCacheControlHeaders *hdrs = static_cast<TargetedCacheControlHeaders *>(data); | ||
| // The string_views in headers[] point into conf_value, so conf_value must | ||
| // remain stable for the lifetime of the parsed headers. | ||
| hdrs->parse(src); |
Copilot
AI
Feb 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TargetedCacheControlHeaders::Conv store lambda comments that headers[] points into conf_value and therefore requires conf_value to remain stable, but the implementation only calls parse(src) and never updates conf_value. This is misleading as written and also makes headers[] point into the caller-provided src buffer, whose lifetime may not be stable if this converter is ever used directly. Consider either updating conf_value consistently (and parsing from it) or adjusting the comment/implementation so the referenced backing storage and its required lifetime are unambiguous.
| hdrs->parse(src); | |
| hdrs->conf_value = src.data(); | |
| hdrs->parse(std::string_view{hdrs->conf_value, src.size()}); |
The section I linked too includes cdn-cache-control which I think is what you are also adding in this patch set, did those tests begin to pass? |
| field = mime_hdr_field_find(this, static_cast<std::string_view>(MIME_FIELD_CACHE_CONTROL)); | ||
| field = nullptr; | ||
|
|
||
| // Check for targeted cache control headers first (in priority order). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assert that targeted_headers is non-nullptr.
| count = 0; | ||
| swoc::TextView config_view{src}; | ||
|
|
||
| while (config_view && count < MAX_HEADERS) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if the config has more than MAX_HEADERS? Silent ignored it seems? Should we throw a warning at least (seems like that would indicate a configuration error).
| class TargetedCacheControlHeaders | ||
| { | ||
| public: | ||
| static const MgmtConverter Conv; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know we don't do this consistently, but if the copy constructor / assignment constructor shouldn't be used (seems iffy with the raw pointer), should we = delete; them here ?
This adds support for targeted Cache-Control headers (like CDN-Cache-Control) that allow cache directives to be targeted at specific caches. The implementation includes a configurable, priority-ordered list of targeted headers via proxy.config.http.cache.targeted_cache_control_headers, which is overridable per-remap rule. When a targeted header is present, it takes precedence over the standard Cache-Control header for caching decisions. Targeted headers are passed through downstream to allow proper cache hierarchy behavior.
Fixes: #9113