diff --git a/Classes/Http/CacheControlHeaderComponent.php b/Classes/Http/CacheControlHeaderComponent.php index 3ed11ee..83c7ed4 100644 --- a/Classes/Http/CacheControlHeaderComponent.php +++ b/Classes/Http/CacheControlHeaderComponent.php @@ -5,6 +5,7 @@ use MOC\Varnish\Aspects\ContentCacheAspect; use MOC\Varnish\Cache\MetadataAwareStringFrontend; +use MOC\Varnish\Service\CacheTagService; use MOC\Varnish\Service\TokenStorage; use Neos\ContentRepository\Domain\Model\NodeInterface; use Neos\Flow\Annotations as Flow; @@ -49,6 +50,12 @@ class CacheControlHeaderComponent implements ComponentInterface */ protected $propertyMapper; + /** + * @var CacheTagService + * @Flow\Inject + */ + protected $cacheTagService; + /** * @var MetadataAwareStringFrontend */ @@ -109,7 +116,8 @@ public function handle(ComponentContext $componentContext) list($tags, $cacheLifetime) = $this->getCacheTagsAndLifetime(); if (count($tags) > 0) { - $modifiedResponse = $modifiedResponse->withHeader('X-Cache-Tags', implode(',', $tags)); + $shortenedTags = $this->cacheTagService->shortenTags($tags); + $modifiedResponse = $modifiedResponse->withHeader('X-Cache-Tags', implode(',', $shortenedTags)); } $modifiedResponse = $modifiedResponse->withHeader('X-Site', $this->tokenStorage->getToken()); diff --git a/Classes/Service/CacheTagService.php b/Classes/Service/CacheTagService.php new file mode 100644 index 0000000..88521ea --- /dev/null +++ b/Classes/Service/CacheTagService.php @@ -0,0 +1,59 @@ +shouldShortenTags()) { + return $tags; + } + + return array_map(function (string $tag) { + return substr(md5($tag), 0, (int)$this->cacheHeaderConfiguration['cacheTagLength'] ?? 8); + }, $tags); + } + + protected function shouldShortenTags(): bool + { + return (bool)$this->cacheHeaderConfiguration['shortenCacheTags'] ?? false; + } + +} diff --git a/Classes/Service/ContentCacheFlusherService.php b/Classes/Service/ContentCacheFlusherService.php index 02f43a7..afbdcde 100644 --- a/Classes/Service/ContentCacheFlusherService.php +++ b/Classes/Service/ContentCacheFlusherService.php @@ -111,8 +111,7 @@ protected function generateCacheTags(NodeInterface $node): void if ($nodeInWorkspace === null) { break; } - $tagName = 'DescendantOf_' . $workspaceHash . '_' . $nodeInWorkspace->getIdentifier(); - $this->tagsToFlush[$tagName] = sprintf('which were tagged with "%s" because node "%s" has changed.', $tagName, $node->getPath()); + $this->generateCacheTagsForDescendantOf($workspaceHash . '_' . $nodeInWorkspace->getIdentifier()); } } @@ -133,8 +132,17 @@ protected function generateCacheTags(NodeInterface $node): void */ protected function generateCacheTagsForNodeIdentifier(string $cacheIdentifier): void { - $this->tagsToFlush['Node_' . $cacheIdentifier] = sprintf('which were tagged with "Node_%s" because that identifier has changed.', $cacheIdentifier); + $tagName = 'Node_' . $cacheIdentifier; + $this->tagsToFlush[$tagName] = sprintf('which were tagged with "%s" because node identifier "%s" has changed.', $tagName, $cacheIdentifier); // Note, as we don't have a node here we cannot go up the structure. + $this->generateCacheTagsForDescendantOf($cacheIdentifier); + } + + /** + * @param string $cacheIdentifier + */ + protected function generateCacheTagsForDescendantOf(string $cacheIdentifier): void + { $tagName = 'DescendantOf_' . $cacheIdentifier; $this->tagsToFlush[$tagName] = sprintf('which were tagged with "%s" because node "%s" has changed.', $tagName, $cacheIdentifier); } @@ -154,7 +162,8 @@ protected function generateCacheTagsForNodeType(string $nodeTypeName, string $re $nodeTypePrefix = rtrim($nodeTypePrefix, '_') . '_'; } foreach ($nodeTypesToFlush as $nodeTypeNameToFlush) { - $this->tagsToFlush['NodeType_' . $nodeTypePrefix . $nodeTypeNameToFlush] = sprintf('which were tagged with "NodeType_%s" because node "%s" has changed and was of type "%s".', $nodeTypeNameToFlush, ($referenceNodeIdentifier ? $referenceNodeIdentifier : ''), $nodeTypeName); + $tagName = 'NodeType_' . $nodeTypePrefix . $nodeTypeNameToFlush; + $this->tagsToFlush[$tagName] = sprintf('which were tagged with "%s" because node "%s" has changed and was of type "%s".', $tagName, $referenceNodeIdentifier ?: '', $nodeTypeName); } } diff --git a/Classes/Service/VarnishBanService.php b/Classes/Service/VarnishBanService.php index 809d178..780861b 100644 --- a/Classes/Service/VarnishBanService.php +++ b/Classes/Service/VarnishBanService.php @@ -29,6 +29,12 @@ class VarnishBanService */ protected $tokenStorage; + /** + * @Flow\Inject + * @var CacheTagService + */ + protected $cacheTagService; + /** * @var array */ @@ -109,13 +115,8 @@ public function banByTags(array $tags, $domains = null): void $tags = array_diff($tags, $this->settings['ignoredCacheTags']); } - /** - * Sanitize tags - * @see \Neos\Fusion\Core\Cache\ContentCache - */ - foreach ($tags as $key => $tag) { - $tags[$key] = strtr($tag, '.:', '_-'); - } + $tags = $this->cacheTagService->sanitizeTags($tags); + $tags = $this->cacheTagService->shortenTags($tags); $this->varnishProxyClient->forHosts(...$this->domainsToArray($domains)); $this->cacheInvalidator->invalidateTags($tags); diff --git a/Configuration/Settings.yaml b/Configuration/Settings.yaml index 97ecfdf..05d08fa 100644 --- a/Configuration/Settings.yaml +++ b/Configuration/Settings.yaml @@ -6,10 +6,14 @@ MOC: varnishUrl: 'http://127.0.0.1' # Cache header sending configuration cacheHeaders: - # Default and maximum TTL in seconds - defaultSharedMaximumAge: null # Disable sending headers (useful for staging environments) disabled: false + # Default and maximum TTL in seconds + defaultSharedMaximumAge: null + # Enable shortening of Cache Tags (useful when response headers get too large) + shortenCacheTags: false + # Length of the short md5 if shortenCacheTags is enabled + cacheTagLength: 8 # Maximum length of header in bytes for requests to varnish. Used when banning. Large requests will be split across multiple smaller ones maximumHeaderLength: 7500 diff --git a/Documentation/Index.rst b/Documentation/Index.rst index ce8783b..be30a16 100644 --- a/Documentation/Index.rst +++ b/Documentation/Index.rst @@ -19,6 +19,10 @@ There are several configuration options can/needs to be set: If not set, the Varnish configuration needs to cache by default since no ``Cache-Control`` header will be sent - Disable sending of cache headers - can be used to disable Varnish on staging environment e.g. ``MOC.Varnish.cacheHeaders.disabled`` accepts boolean value (defaults to ``FALSE``) +- Since 4.1.0: The generated Cache Tags can be shortened using this option + ``MOC.Varnish.cacheHeaders.shortenCacheTags`` accepts boolean value (defaults to ``FALSE``) +- Since 4.1.0: If shortenCacheTags is enabled, this option controls the length of the used md5 for tags + ``MOC.Varnish.cacheHeaders.cacheTagLength`` accepts integer value (defaults to ``8``) - Reverse lookup port can be set to allow debugging in the backend module if the web server port is not ``80`` ``MOC.Varnish.reverseLookupPort`` accepts integer (defaults to ``NULL``) - Ignored cache tags can be used to ignore certain cache tags from being cleared at all (useful for optimizing)