|
34 | 34 | * Adds caching on top of an HTTP client (per RFC 9111).
|
35 | 35 | *
|
36 | 36 | * Known omissions / partially supported features per RFC 9111:
|
37 |
| - * 1. Range requests: |
38 |
| - * - All range requests ("partial content") are passed through and never cached. |
39 |
| - * 2. stale-while-revalidate: |
| 37 | + * 1. stale-while-revalidate: |
40 | 38 | * - There's no actual "background revalidation" for stale responses, they will
|
41 | 39 | * always be revalidated.
|
42 |
| - * 3. min-fresh, max-stale, only-if-cached: |
| 40 | + * 2. min-fresh, max-stale, only-if-cached: |
43 | 41 | * - Request directives are not parsed; the client ignores them.
|
44 | 42 | *
|
45 | 43 | * @see https://www.rfc-editor.org/rfc/rfc9111
|
@@ -68,6 +66,21 @@ class CachingHttpClient implements HttpClientInterface, ResetInterface
|
68 | 66 | * The HTTP methods that will trigger a cache invalidation.
|
69 | 67 | */
|
70 | 68 | private const UNSAFE_METHODS = ['POST', 'PUT', 'DELETE', 'PATCH'];
|
| 69 | + /** |
| 70 | + * Headers that influence the response and may affect caching behavior. |
| 71 | + */ |
| 72 | + private const RESPONSE_INFLUENCING_HEADERS = [ |
| 73 | + 'accept' => true, |
| 74 | + 'accept-charset' => true, |
| 75 | + 'accept-encoding' => true, |
| 76 | + 'accept-language' => true, |
| 77 | + 'authorization' => true, |
| 78 | + 'cookie' => true, |
| 79 | + 'expect' => true, |
| 80 | + 'host' => true, |
| 81 | + 'range' => true, |
| 82 | + 'user-agent' => true, |
| 83 | + ]; |
71 | 84 | /**
|
72 | 85 | * Headers that MUST NOT be stored as per RFC 9111 Section 3.1.
|
73 | 86 | */
|
@@ -144,16 +157,24 @@ public function request(string $method, string $url, array $options = []): Respo
|
144 | 157 | $this->cache->invalidateTags([$fullUrlTag]);
|
145 | 158 | }
|
146 | 159 |
|
147 |
| - if ('' !== $options['body'] || isset($options['normalized_headers']['range']) || !\in_array($method, self::CACHEABLE_METHODS, true)) { |
| 160 | + if ('' !== $options['body'] || !\in_array($method, self::CACHEABLE_METHODS, true)) { |
148 | 161 | return new AsyncResponse($this->client, $method, $url, $options);
|
149 | 162 | }
|
150 | 163 |
|
151 |
| - $requestHash = self::hash($method.$fullUrl); |
| 164 | + $requestHash = self::hash($method.$fullUrl.json_encode(array_intersect_key($options['normalized_headers'], self::RESPONSE_INFLUENCING_HEADERS), \JSON_THROW_ON_ERROR)); |
152 | 165 | $varyKey = "vary_{$requestHash}";
|
153 |
| - $varyFields = $this->cache->get($varyKey, static fn (): array => []); |
| 166 | + $varyFields = $this->cache->get($varyKey, static function (ItemInterface $item, bool &$save): array { |
| 167 | + $save = false; |
| 168 | + |
| 169 | + return []; |
| 170 | + }); |
154 | 171 |
|
155 | 172 | $metadataKey = self::getMetadataKey($requestHash, $options['normalized_headers'], $varyFields);
|
156 |
| - $cachedData = $this->cache->get($metadataKey, static fn (): null => null); |
| 173 | + $cachedData = $this->cache->get($metadataKey, static function (ItemInterface $item, bool &$save): null { |
| 174 | + $save = false; |
| 175 | + |
| 176 | + return null; |
| 177 | + }); |
157 | 178 |
|
158 | 179 | $freshness = null;
|
159 | 180 | if (\is_array($cachedData)) {
|
|
0 commit comments