|
9 | 9 | module Protocol |
10 | 10 | module HTTP |
11 | 11 | module Header |
| 12 | + # Represents the `cache-control` header, which is a list of cache directives. |
12 | 13 | class CacheControl < Split |
| 14 | + # The `private` directive indicates that the response is intended for a single user and must not be stored by shared caches. |
13 | 15 | PRIVATE = "private" |
| 16 | + |
| 17 | + # The `public` directive indicates that the response may be stored by any cache, even if it would normally be considered non-cacheable. |
14 | 18 | PUBLIC = "public" |
| 19 | + |
| 20 | + # The `no-cache` directive indicates that caches must revalidate the response with the origin server before serving it to clients. |
15 | 21 | NO_CACHE = "no-cache" |
| 22 | + |
| 23 | + # The `no-store` directive indicates that caches must not store the response under any circumstances. |
16 | 24 | NO_STORE = "no-store" |
| 25 | + |
| 26 | + # The `max-age` directive indicates the maximum amount of time, in seconds, that a response is considered fresh. |
17 | 27 | MAX_AGE = "max-age" |
| 28 | + |
| 29 | + # The `s-maxage` directive is similar to `max-age` but applies only to shared caches. If both `s-maxage` and `max-age` are present, `s-maxage` takes precedence in shared caches. |
18 | 30 | S_MAXAGE = "s-maxage" |
19 | 31 |
|
| 32 | + # The `static` directive is a custom directive often used to indicate that the resource is immutable or rarely changes, allowing longer caching periods. |
20 | 33 | STATIC = "static" |
| 34 | + |
| 35 | + # The `dynamic` directive is a custom directive used to indicate that the resource is generated dynamically and may change frequently, requiring shorter caching periods. |
21 | 36 | DYNAMIC = "dynamic" |
| 37 | + |
| 38 | + # The `streaming` directive is a custom directive used to indicate that the resource is intended for progressive or chunked delivery, such as live video streams. |
22 | 39 | STREAMING = "streaming" |
23 | 40 |
|
| 41 | + # The `must-revalidate` directive indicates that once a response becomes stale, caches must not use it to satisfy subsequent requests without revalidating it with the origin server. |
24 | 42 | MUST_REVALIDATE = "must-revalidate" |
| 43 | + |
| 44 | + # The `proxy-revalidate` directive is similar to `must-revalidate` but applies only to shared caches. |
25 | 45 | PROXY_REVALIDATE = "proxy-revalidate" |
26 | 46 |
|
| 47 | + # Initializes the cache control header with the given value. The value is expected to be a comma-separated string of cache directives. |
| 48 | + # |
| 49 | + # @parameter value [String | Nil] the raw Cache-Control header value. |
27 | 50 | def initialize(value = nil) |
28 | 51 | super(value&.downcase) |
29 | 52 | end |
30 | 53 |
|
| 54 | + # Adds a directive to the Cache-Control header. The value will be normalized to lowercase before being added. |
| 55 | + # |
| 56 | + # @parameter value [String] the directive to add. |
31 | 57 | def << value |
32 | 58 | super(value.downcase) |
33 | 59 | end |
34 | 60 |
|
| 61 | + # @returns [Boolean] whether the `static` directive is present. |
35 | 62 | def static? |
36 | 63 | self.include?(STATIC) |
37 | 64 | end |
38 | 65 |
|
| 66 | + # @returns [Boolean] whether the `dynamic` directive is present. |
39 | 67 | def dynamic? |
40 | 68 | self.include?(DYNAMIC) |
41 | 69 | end |
42 | 70 |
|
| 71 | + # @returns [Boolean] whether the `streaming` directive is present. |
43 | 72 | def streaming? |
44 | 73 | self.include?(STREAMING) |
45 | 74 | end |
46 | 75 |
|
| 76 | + # @returns [Boolean] whether the `private` directive is present. |
47 | 77 | def private? |
48 | 78 | self.include?(PRIVATE) |
49 | 79 | end |
50 | 80 |
|
| 81 | + # @returns [Boolean] whether the `public` directive is present. |
51 | 82 | def public? |
52 | 83 | self.include?(PUBLIC) |
53 | 84 | end |
54 | 85 |
|
| 86 | + # @returns [Boolean] whether the `no-cache` directive is present. |
55 | 87 | def no_cache? |
56 | 88 | self.include?(NO_CACHE) |
57 | 89 | end |
58 | 90 |
|
| 91 | + # @returns [Boolean] whether the `no-store` directive is present. |
59 | 92 | def no_store? |
60 | 93 | self.include?(NO_STORE) |
61 | 94 | end |
62 | 95 |
|
63 | | - # Indicates that a response must not be used once it is stale. |
64 | | - # See https://www.rfc-editor.org/rfc/rfc9111.html#name-must-revalidate |
| 96 | + # @returns [Boolean] whether the `must-revalidate` directive is present. |
65 | 97 | def must_revalidate? |
66 | 98 | self.include?(MUST_REVALIDATE) |
67 | 99 | end |
68 | 100 |
|
69 | | - # Like must-revalidate, but for shared caches only. |
70 | | - # See https://www.rfc-editor.org/rfc/rfc9111.html#name-proxy-revalidate |
| 101 | + # @returns [Boolean] whether the `proxy-revalidate` directive is present. |
71 | 102 | def proxy_revalidate? |
72 | 103 | self.include?(PROXY_REVALIDATE) |
73 | 104 | end |
74 | 105 |
|
75 | | - # The maximum time, in seconds, a response should be considered fresh. |
76 | | - # See https://www.rfc-editor.org/rfc/rfc9111.html#name-max-age-2 |
| 106 | + # @returns [Integer | Nil] the value of the `max-age` directive in seconds, or `nil` if the directive is not present or invalid. |
77 | 107 | def max_age |
78 | 108 | find_integer_value(MAX_AGE) |
79 | 109 | end |
80 | 110 |
|
81 | | - # Like max-age, but for shared caches only, which should use it before |
82 | | - # max-age when present. |
83 | | - # See https://www.rfc-editor.org/rfc/rfc9111.html#name-s-maxage |
| 111 | + # @returns [Integer | Nil] the value of the `s-maxage` directive in seconds, or `nil` if the directive is not present or invalid. |
84 | 112 | def s_maxage |
85 | 113 | find_integer_value(S_MAXAGE) |
86 | 114 | end |
87 | 115 |
|
88 | 116 | private |
89 | 117 |
|
| 118 | + # Finds and parses an integer value from a directive. |
| 119 | + # |
| 120 | + # @parameter value_name [String] the directive name to search for (e.g., "max-age"). |
| 121 | + # @returns [Integer | Nil] the parsed integer value, or `nil` if not found or invalid. |
90 | 122 | def find_integer_value(value_name) |
91 | | - if value = self.find{|value| value.start_with?(value_name)} |
| 123 | + if value = self.find { |value| value.start_with?(value_name) } |
92 | 124 | _, age = value.split("=", 2) |
93 | 125 |
|
94 | 126 | if age =~ /\A[0-9]+\z/ |
|
0 commit comments