Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork9.6k
Description
Symfony version(s) affected
6.2.8
Description
I was writing a crawler for an API on Drupal.org and I tried to be nice for the provider by caching responses locally. I have followedthe official documentation for enabling response caching on the HTTP client. I was surprised to see that the responses are not being cached. I dig deeper and deeper and I spotted thatprivate
Cache-Control header appears in responses, however the API backend definitely does not send that header.
I think the root cause is related to the implementation in the HTTP.
- ˙\Symfony\Component\HttpKernel\HttpClientKernel::handle()` builds a new response in
$response =newResponse($response->getContent(!$catch),$response->getStatusCode(),$response->getHeaders(!$catch)); - Which calls
\Symfony\Component\HttpFoundation\ResponseHeaderBag::computeCacheControlValue()
marks the response areprivate
due to the lack of explicitly specifiedpublic
header value. This bubbles up the following calls...
https://github.com/symfony/symfony/blob/v6.2.8/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php#L255-L259 - ...even if there is a workaround (I suppose) here for ignoring calculated headers
symfony/src/Symfony/Component/HttpKernel/HttpClientKernel.php
Lines 64 to 69 in93fbfe0
$response->headers =newclass($response->headers->all())extends ResponseHeaderBag { protectedfunctioncomputeCacheControlValue():string { return$this->getCacheControlHeader();// preserve the original value } };
Stack trace:
How to reproduce
POC
$ php guzzle.php Array( [0] => store, no-cache, must-revalidate, post-check=0, pre-check=0)$ php symfony.php Array( [0] => store, no-cache, must-revalidate, post-check=0, pre-check=0)$ php symfony_with_cache.php Array( [0] => must-revalidate, no-cache, post-check=0, pre-check=0, private, store)
symfony.php
useSymfony\Component\HttpClient\HttpClient;$client = HttpClient::create();$response =$client->request('GET','https://updates.drupal.org/release-history/drupal/current?version=10.0.0', ['headers' => ['Accept' =>'text/xml', ],]);print_r($response->getHeaders()['cache-control']);
symfony-with-cache.php
use Symfony\Component\HttpClient\CachingHttpClient;use Symfony\Component\HttpClient\HttpClient;use Symfony\Component\HttpKernel\HttpCache\Store;$client = HttpClient::create();$client = new CachingHttpClient($client, new Store(__DIR__ . '/cache'));$response = $client->request('GET', 'https://updates.drupal.org/release-history/drupal/current?version=10.0.0', [ 'headers' => [ 'Accept' => 'text/xml', ],]);print_r($response->getHeaders()['cache-control']);
Possible Solution
No response
Additional Context
The root cause of the issue can be the interpretation ofhttps://www.rfc-editor.org/rfc/rfc7234#section-3.
Only one of these is required, sos-maxage
should not be required on a GET request with HTTP 200 response.
the response either:
* contains an Expires header field (see [Section 5.3](https://www.rfc-editor.org/rfc/rfc7234#section-5.3)), or * contains a max-age response directive (see [Section 5.2.2.8](https://www.rfc-editor.org/rfc/rfc7234#section-5.2.2.8)), or * contains a s-maxage response directive (see [Section 5.2.2.9](https://www.rfc-editor.org/rfc/rfc7234#section-5.2.2.9)) and the cache is shared, or * contains a Cache Control Extension (see [Section 5.2.3](https://www.rfc-editor.org/rfc/rfc7234#section-5.2.3)) that allows it to be cached, or * has a status code that is defined as cacheable by default (see [Section 4.2.2](https://www.rfc-editor.org/rfc/rfc7234#section-4.2.2)), or