From a4ed9b6b8d9a9dfc4a0f92e8a1e1de56a877d9c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andre=CC=81=20R?= Date: Tue, 7 Nov 2017 11:02:36 +0100 Subject: [PATCH] EZP-28183: Requests (incl from REST) with X-Location-Id not tagged --- .../XLocationIdResponseSubscriberSpec.php | 65 ++++++++++++++++++ .../EzSystemsPlatformHttpCacheExtension.php | 7 +- .../XLocationIdResponseSubscriber.php | 68 +++++++++++++++++++ src/Proxy/TagAwareStore.php | 4 -- src/Resources/config/default_settings.yml | 1 + src/Resources/config/fos_http_cache.yml | 2 +- src/Resources/config/view_cache.yml | 6 ++ 7 files changed, 146 insertions(+), 7 deletions(-) create mode 100644 spec/EventSubscriber/XLocationIdResponseSubscriberSpec.php create mode 100644 src/EventSubscriber/XLocationIdResponseSubscriber.php diff --git a/spec/EventSubscriber/XLocationIdResponseSubscriberSpec.php b/spec/EventSubscriber/XLocationIdResponseSubscriberSpec.php new file mode 100644 index 00000000..5c1949a9 --- /dev/null +++ b/spec/EventSubscriber/XLocationIdResponseSubscriberSpec.php @@ -0,0 +1,65 @@ +headers = $responseHeaders; + $event->getResponse()->willReturn($response); + + $this->beConstructedWith('Surrogate-Key'); + } + + public function it_does_not_rewrite_header_if_there_is_none( + FilterResponseEvent $event, + ResponseHeaderBag $responseHeaders + ) { + $responseHeaders->has('X-Location-Id')->willReturn(false); + $responseHeaders->set()->shouldNotBecalled(); + + $this->rewriteCacheHeader($event); + } + + public function it_rewrite_header_if_there( + FilterResponseEvent $event, + ResponseHeaderBag $responseHeaders + ) { + $responseHeaders->has('X-Location-Id')->willReturn(true); + $responseHeaders->get('X-Location-Id')->willReturn('123'); + $responseHeaders->has('Surrogate-Key')->willReturn(false); + + $responseHeaders->set('Surrogate-Key', ['location-123'])->willReturn(null); + $responseHeaders->remove('X-Location-Id')->willReturn(null); + + $this->rewriteCacheHeader($event); + } + + public function it_rewrite_header_also_in_unofficial_plural_form_and_merges_exisitng_value( + FilterResponseEvent $event, + ResponseHeaderBag $responseHeaders + ) { + $responseHeaders->has('X-Location-Id')->willReturn(true); + $responseHeaders->get('X-Location-Id')->willReturn('123,34'); + $responseHeaders->has('Surrogate-Key')->willReturn(true); + $responseHeaders->get('Surrogate-Key', null, false)->willReturn(['content-44']); + + $responseHeaders->set('Surrogate-Key', ['content-44', 'location-123', 'location-34'])->willReturn(null); + $responseHeaders->remove('X-Location-Id')->willReturn(null); + + $this->rewriteCacheHeader($event); + } +} diff --git a/src/DependencyInjection/EzSystemsPlatformHttpCacheExtension.php b/src/DependencyInjection/EzSystemsPlatformHttpCacheExtension.php index d5b4ac58..6a8eab3a 100644 --- a/src/DependencyInjection/EzSystemsPlatformHttpCacheExtension.php +++ b/src/DependencyInjection/EzSystemsPlatformHttpCacheExtension.php @@ -18,7 +18,6 @@ public function load(array $configs, ContainerBuilder $container) $config = $this->processConfiguration($configuration, $configs); $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); - $loader->load('default_settings.yml'); $loader->load('services.yml'); $loader->load('slot.yml'); $loader->load('view_cache.yml'); @@ -26,7 +25,11 @@ public function load(array $configs, ContainerBuilder $container) public function prepend(ContainerBuilder $container) { - // Default settings for FOSHttpCacheBundle + // Load params early as we use them in below + $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('default_settings.yml'); + + // Override default settings for FOSHttpCacheBundle $configFile = __DIR__ . '/../Resources/config/fos_http_cache.yml'; $config = Yaml::parse(file_get_contents($configFile)); $container->prependExtensionConfig('fos_http_cache', $config); diff --git a/src/EventSubscriber/XLocationIdResponseSubscriber.php b/src/EventSubscriber/XLocationIdResponseSubscriber.php new file mode 100644 index 00000000..8f790477 --- /dev/null +++ b/src/EventSubscriber/XLocationIdResponseSubscriber.php @@ -0,0 +1,68 @@ +tagHeader = $tagHeader; + } + + public static function getSubscribedEvents() + { + return [KernelEvents::RESPONSE => ['rewriteCacheHeader', -5]]; + } + + public function rewriteCacheHeader(FilterResponseEvent $event) + { + $response = $event->getResponse(); + if (!$response->headers->has(static::LOCATION_ID_HEADER)) { + return; + } + + @trigger_error( + 'X-Location-Id is no longer preferred way to tag content responses, see ezplatform-http-cache/docs/using_tags.md', + E_USER_DEPRECATED + ); + + // Map the tag, even if not officially supported, handle comma separated values as was possible with Varnish + $tags = array_map( + function ($id) { + return 'location-' . trim($id); + }, + explode(',', $response->headers->get(static::LOCATION_ID_HEADER)) + ); + + if ($response->headers->has($this->tagHeader)) { + $tags = array_merge($response->headers->get($this->tagHeader, null, false) , $tags); + } + + // @todo we need to use abstract tag writer to also be able to support Fastly + // FOS has some stuff around this in 2.x but not in a good way in 1.x + $response->headers->set($this->tagHeader, array_unique($tags)); + $response->headers->remove(static::LOCATION_ID_HEADER); + } +} diff --git a/src/Proxy/TagAwareStore.php b/src/Proxy/TagAwareStore.php index 6acb8abd..b8412f9b 100644 --- a/src/Proxy/TagAwareStore.php +++ b/src/Proxy/TagAwareStore.php @@ -73,10 +73,6 @@ public function write(Request $request, Response $response) $digest = $response->headers->get('X-Content-Digest'); $tags = $response->headers->get('xkey', null, false); - if ($response->headers->has('X-Location-Id')) { - $tags[] = 'location-' . $response->headers->get('X-Location-Id'); - } - foreach (array_unique($tags) as $tag) { if (false === $this->saveTag($tag, $digest)) { throw new \RuntimeException('Unable to store the cache tag meta information.'); diff --git a/src/Resources/config/default_settings.yml b/src/Resources/config/default_settings.yml index d66c2182..8ed72c50 100644 --- a/src/Resources/config/default_settings.yml +++ b/src/Resources/config/default_settings.yml @@ -1,2 +1,3 @@ parameters: ezplatform.http_cache.store.root: "%kernel.cache_dir%/http_cache" + ezplatform.http_cache.tags.header: 'xkey' diff --git a/src/Resources/config/fos_http_cache.yml b/src/Resources/config/fos_http_cache.yml index 6eed34c0..bce3308c 100644 --- a/src/Resources/config/fos_http_cache.yml +++ b/src/Resources/config/fos_http_cache.yml @@ -14,4 +14,4 @@ tags: # 1. Until 2.x it is hardcoded to use comma separated list of tags, which won't work on Fastly and # will conflict with the one header per key format we currently use with xkey (as per their doc) # 2. In FosHttpCache 1.x it is only available if proxy client is configured as "varnish" or "custom" - header: xkey + header: '%ezplatform.http_cache.tags.header%' diff --git a/src/Resources/config/view_cache.yml b/src/Resources/config/view_cache.yml index 18e1eb27..1e53b787 100644 --- a/src/Resources/config/view_cache.yml +++ b/src/Resources/config/view_cache.yml @@ -7,6 +7,12 @@ services: tags: - { name: kernel.event_subscriber } + ezplatform.x_location_id.response_subscriber: + class: EzSystems\PlatformHttpCacheBundle\EventSubscriber\XLocationIdResponseSubscriber + arguments: ['%ezplatform.http_cache.tags.header%'] + tags: + - { name: kernel.event_subscriber } + ezplatform.view_cache.response_configurator: class: EzSystems\PlatformHttpCacheBundle\ResponseConfigurator\ConfigurableResponseCacheConfigurator arguments: