Skip to content

Commit 1c6ae8a

Browse files
committed
Implement RFC 9213 Targeted HTTP Cache Control
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
1 parent 5e3f052 commit 1c6ae8a

File tree

16 files changed

+583
-13
lines changed

16 files changed

+583
-13
lines changed

doc/admin-guide/configuration/cache-basics.en.rst

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,84 @@ Traffic Server applies ``Cache-Control`` servability criteria after HTTP
234234
freshness criteria. For example, an object might be considered fresh but will
235235
not be served if its age is greater than its ``max-age``.
236236

237+
Targeted Cache Control (RFC 9213)
238+
----------------------------------
239+
240+
Traffic Server supports `RFC 9213 <https://httpwg.org/specs/rfc9213.html>`_
241+
Targeted HTTP Cache Control, which allows origin servers to provide different
242+
cache directives for different classes of caches. This is particularly useful in CDN deployments where you want to
243+
give different caching instructions to CDN caches versus browser caches.
244+
245+
For example, an origin server might send::
246+
247+
Cache-Control: max-age=60
248+
CDN-Cache-Control: max-age=3600
249+
250+
When targeted cache control is enabled (via
251+
:ts:cv:`proxy.config.http.cache.targeted_cache_control_headers`), Traffic
252+
Server will use the ``CDN-Cache-Control`` directives instead of the standard
253+
``Cache-Control`` directives for caching decisions. The browser receiving the
254+
response will see both headers and use the standard ``Cache-Control``, allowing
255+
the object to be cached for 60 seconds in the browser but 3600 seconds in the CDN.
256+
257+
Configuration
258+
~~~~~~~~~~~~~
259+
260+
To enable targeted cache control, set
261+
:ts:cv:`proxy.config.http.cache.targeted_cache_control_headers` to a
262+
comma-separated list of header names to check in priority order::
263+
264+
# In records.yaml:
265+
proxy.config.http.cache.targeted_cache_control_headers: CDN-Cache-Control
266+
267+
Or with multiple targeted headers in priority order::
268+
269+
proxy.config.http.cache.targeted_cache_control_headers: ATS-Cache-Control,CDN-Cache-Control
270+
271+
This configuration is overridable per-remap, allowing different rules for
272+
different origins::
273+
274+
# In remap.config:
275+
map / https://origin.example.com/ @plugin=conf_remap.so \
276+
@pparam=proxy.config.http.cache.targeted_cache_control_headers=CDN-Cache-Control
277+
278+
Behavior
279+
~~~~~~~~
280+
281+
- When a targeted header is found (first match in the priority list), its
282+
directives replace the standard ``Cache-Control`` directives for all caching
283+
decisions.
284+
285+
- If no targeted headers are present or they are all empty, Traffic Server falls
286+
back to the standard ``Cache-Control`` header.
287+
288+
- Targeted headers are passed through to downstream caches, allowing CDN chains
289+
to use the same directives.
290+
291+
- All standard cache control directives are supported in targeted headers:
292+
``max-age``, ``s-maxage``, ``no-cache``, ``no-store``, ``private``,
293+
``must-revalidate``, etc.
294+
295+
Use Cases
296+
~~~~~~~~~
297+
298+
**CDN with origin cache**: An origin might have its own caching layer but want
299+
CDNs to cache more aggressively::
300+
301+
Cache-Control: max-age=60
302+
CDN-Cache-Control: max-age=86400
303+
304+
**Different CDN policies**: Using multiple CDN providers with different needs::
305+
306+
Cache-Control: max-age=300
307+
CDN1-Cache-Control: max-age=3600
308+
CDN2-Cache-Control: max-age=1800
309+
310+
**Prevent CDN caching while allowing browser caching**::
311+
312+
Cache-Control: max-age=300
313+
CDN-Cache-Control: no-store
314+
237315
Revalidating HTTP Objects
238316
-------------------------
239317

doc/admin-guide/files/records.yaml.en.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2517,6 +2517,31 @@ Cache Control
25172517
``Cache-Control: max-age``.
25182518
===== ======================================================================
25192519

2520+
.. ts:cv:: CONFIG proxy.config.http.cache.targeted_cache_control_headers STRING ""
2521+
:reloadable:
2522+
:overridable:
2523+
2524+
Comma-separated list of targeted cache control header names to check in priority
2525+
order before falling back to the standard ``Cache-Control`` header. This implements
2526+
`RFC 9213 <https://httpwg.org/specs/rfc9213.html>`_ Targeted HTTP Cache Control.
2527+
When empty (the default), targeted cache control is disabled and only the standard
2528+
``Cache-Control`` header is used.
2529+
2530+
Example values:
2531+
2532+
- ``CDN-Cache-Control`` - Use only CDN-Cache-Control if present
2533+
- ``ATS-Cache-Control,CDN-Cache-Control`` - Check ATS-Cache-Control first, then
2534+
CDN-Cache-Control, then fall back to Cache-Control
2535+
2536+
When a targeted header is found, its directives are used rather than those in the
2537+
standard ``Cache-Control`` header for caching decisions. The targeted headers are
2538+
passed through to downstream caches.
2539+
2540+
.. note::
2541+
2542+
This implementation uses the existing Cache-Control parser rather than the
2543+
strict RFC 8941 Structured Fields parser specified in RFC 9213.
2544+
25202545
.. ts:cv:: CONFIG proxy.config.http.cache.max_stale_age INT 604800
25212546
:reloadable:
25222547
:overridable:

doc/admin-guide/plugins/lua.en.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4396,6 +4396,7 @@ Http config constants
43964396
TS_LUA_CONFIG_NET_SOCK_NOTSENT_LOWAT
43974397
TS_LUA_CONFIG_BODY_FACTORY_RESPONSE_SUPPRESSION_MODE
43984398
TS_LUA_CONFIG_HTTP_CACHE_POST_METHOD
4399+
TS_LUA_CONFIG_HTTP_CACHE_TARGETED_CACHE_CONTROL_HEADERS
43994400
TS_LUA_CONFIG_LAST_ENTRY
44004401

44014402
:ref:`TOP <admin-plugins-ts-lua>`

doc/developer-guide/api/functions/TSHttpOverridableConfig.en.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ TSOverridableConfigKey Value Config
196196
:enumerator:`TS_CONFIG_NET_SOCK_NOTSENT_LOWAT` :ts:cv:`proxy.config.net.sock_notsent_lowat`
197197
:enumerator:`TS_CONFIG_BODY_FACTORY_RESPONSE_SUPPRESSION_MODE` :ts:cv:`proxy.config.body_factory.response_suppression_mode`
198198
:enumerator:`TS_CONFIG_HTTP_CACHE_POST_METHOD` :ts:cv:`proxy.config.http.cache.post_method`
199+
:enumerator:`TS_CONFIG_HTTP_CACHE_TARGETED_CACHE_CONTROL_HEADERS` :ts:cv:`proxy.config.http.cache.targeted_cache_control_headers`
199200
====================================================================== ====================================================================
200201

201202
Examples

doc/developer-guide/api/types/TSOverridableConfigKey.en.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ Enumeration Members
163163
.. enumerator:: TS_CONFIG_HTTP_NO_DNS_JUST_FORWARD_TO_PARENT
164164
.. enumerator:: TS_CONFIG_HTTP_CACHE_IGNORE_QUERY
165165
.. enumerator:: TS_CONFIG_HTTP_CACHE_POST_METHOD
166+
.. enumerator:: TS_CONFIG_HTTP_CACHE_TARGETED_CACHE_CONTROL_HEADERS
166167

167168

168169
Description

include/proxy/hdrs/MIME.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ struct MIMEHdrImpl : public HdrHeapObjImpl {
325325
void check_strings(HeapCheck *heaps, int num_heaps);
326326

327327
// Cooked values
328-
void recompute_cooked_stuff(MIMEField *changing_field_or_null = nullptr);
328+
void recompute_cooked_stuff(MIMEField *changing_field_or_null = nullptr, const char *targeted_headers_str = nullptr);
329329
void recompute_accelerators_and_presence_bits();
330330

331331
// Utility

include/proxy/http/HttpConfig.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,9 @@ struct OverridableHttpConfigParams {
550550
MgmtByte cache_range_write = 0;
551551
MgmtByte allow_multi_range = 0;
552552

553+
char *targeted_cache_control_headers = nullptr; // This does not get free'd by us!
554+
size_t targeted_cache_control_headers_len = 0; // Updated when targeted headers are set.
555+
553556
MgmtByte ignore_accept_mismatch = 0;
554557
MgmtByte ignore_accept_language_mismatch = 0;
555558
MgmtByte ignore_accept_encoding_mismatch = 0;

include/proxy/http/OverridableConfigDefs.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@
249249
X(HTTP_NEGATIVE_CACHING_LIST, negative_caching_list, "proxy.config.http.negative_caching_list", STRING, HttpStatusCodeList_Conv) \
250250
X(HTTP_CONNECT_ATTEMPTS_RETRY_BACKOFF_BASE, connect_attempts_retry_backoff_base, "proxy.config.http.connect_attempts_retry_backoff_base", INT, GENERIC) \
251251
X(HTTP_NEGATIVE_REVALIDATING_LIST, negative_revalidating_list, "proxy.config.http.negative_revalidating_list", STRING, HttpStatusCodeList_Conv) \
252-
X(HTTP_CACHE_POST_METHOD, cache_post_method, "proxy.config.http.cache.post_method", INT, GENERIC)
252+
X(HTTP_CACHE_POST_METHOD, cache_post_method, "proxy.config.http.cache.post_method", INT, GENERIC) \
253+
X(HTTP_CACHE_TARGETED_CACHE_CONTROL_HEADERS, targeted_cache_control_headers, "proxy.config.http.cache.targeted_cache_control_headers", STRING, GENERIC)
253254

254255
// clang-format on

include/ts/apidefs.h.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,7 @@ enum TSOverridableConfigKey {
908908
TS_CONFIG_HTTP_CONNECT_ATTEMPTS_RETRY_BACKOFF_BASE,
909909
TS_CONFIG_HTTP_NEGATIVE_REVALIDATING_LIST,
910910
TS_CONFIG_HTTP_CACHE_POST_METHOD,
911+
TS_CONFIG_HTTP_CACHE_TARGETED_CACHE_CONTROL_HEADERS,
911912
TS_CONFIG_LAST_ENTRY,
912913
};
913914

src/api/InkAPI.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7553,6 +7553,15 @@ TSHttpTxnConfigStringSet(TSHttpTxn txnp, TSOverridableConfigKey conf, const char
75537553
s->t_state.my_txn_conf().host_res_data = std::get<HostResData>(parsed.parsed);
75547554
}
75557555
break;
7556+
case TS_CONFIG_HTTP_CACHE_TARGETED_CACHE_CONTROL_HEADERS:
7557+
if (value && length > 0) {
7558+
s->t_state.my_txn_conf().targeted_cache_control_headers = const_cast<char *>(value);
7559+
s->t_state.my_txn_conf().targeted_cache_control_headers_len = length;
7560+
} else {
7561+
s->t_state.my_txn_conf().targeted_cache_control_headers = nullptr;
7562+
s->t_state.my_txn_conf().targeted_cache_control_headers_len = 0;
7563+
}
7564+
break;
75567565
default: {
75577566
if (value && length > 0) {
75587567
return _eval_conv(&(s->t_state.my_txn_conf()), conf, value, length);

0 commit comments

Comments
 (0)