Skip to content

Commit

Permalink
Merge pull request #16 from Flowpack/task/extractPublicCacheHeadersIn…
Browse files Browse the repository at this point in the history
…toSeperateComponent

TASK: Make caching behavior opt-in via `X-FullPageCache-EnableFusionAutoconfiguration` http header
  • Loading branch information
mficzel authored Dec 2, 2021
2 parents af5682c + d7e9df0 commit a64fda1
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
use Flowpack\FullPageCache\Aspects\ContentCacheAspect;
use Flowpack\FullPageCache\Cache\MetadataAwareStringFrontend;

class CacheHeaderMiddleware implements MiddlewareInterface
class FusionAutoconfigurationMiddleware implements MiddlewareInterface
{
public const HEADER_ENABLED = 'X-FullPageCache-EnableFusionAutoconfiguration';

/**
* @Flow\Inject
Expand All @@ -32,41 +33,24 @@ class CacheHeaderMiddleware implements MiddlewareInterface
*/
protected $enabled;

/**
* @var boolean
* @Flow\InjectConfiguration(path="maxPublicCacheTime")
*/
protected $maxPublicCacheTime;

public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface
{
if (!$this->enabled || !$request->hasHeader(RequestCacheMiddleware::HEADER_ENABLED)) {
return $next->handle($request);
return $next->handle($request)->withoutHeader(self::HEADER_ENABLED);
}

$response = $next->handle($request);

list($hasUncachedSegments, $tags, $lifetime) = $this->getFusionCacheInformations();

if ($response->hasHeader('Set-Cookie') || $hasUncachedSegments) {
if (!$response->hasHeader(self::HEADER_ENABLED)) {
return $response;
} else {
$response = $response->withoutHeader(self::HEADER_ENABLED);
}

$publicLifetime = 0;
if ($this->maxPublicCacheTime > 0) {
if ($lifetime > 0 && $lifetime < $this->maxPublicCacheTime) {
$publicLifetime = $lifetime;
} else {
$publicLifetime = $this->maxPublicCacheTime;
}
}
list($hasUncachedSegments, $tags, $lifetime) = $this->getFusionCacheInformations();

if ($publicLifetime > 0) {
$entryContentHash = md5($response->getBody()->getContents());
$response->getBody()->rewind();
$response = $response
->withHeader('ETag', '"' . $entryContentHash . '"')
->withHeader('CacheControl', 'public, max-age=' . $publicLifetime);
if ($response->hasHeader('Set-Cookie') || $hasUncachedSegments) {
return $response;
}

$response = $response
Expand Down
23 changes: 23 additions & 0 deletions Classes/Middleware/RequestCacheMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ class RequestCacheMiddleware implements MiddlewareInterface
*/
protected $ignoredCookieParams;

/**
* @var boolean
* @Flow\InjectConfiguration(path="maxPublicCacheTime")
*/
protected $maxPublicCacheTime;

public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface
{
if (!$this->enabled) {
Expand Down Expand Up @@ -82,6 +88,23 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
->withoutHeader(self::HEADER_LIFTIME)
->withoutHeader(self::HEADER_TAGS);

$publicLifetime = 0;
if ($this->maxPublicCacheTime > 0) {
if ($lifetime > 0 && $lifetime < $this->maxPublicCacheTime) {
$publicLifetime = $lifetime;
} else {
$publicLifetime = $this->maxPublicCacheTime;
}
}

if ($publicLifetime > 0) {
$entryContentHash = md5($response->getBody()->getContents());
$response->getBody()->rewind();
$response = $response
->withHeader('ETag', '"' . $entryContentHash . '"')
->withHeader('Cache-Control', 'public, max-age=' . $publicLifetime);
}

$this->cacheFrontend->set($entryIdentifier,[ 'timestamp' => time(), 'response' => str($response) ], $tags, $lifetime);
$response->getBody()->rewind();
return $response->withHeader(self::HEADER_INFO, 'MISS: ' . $entryIdentifier);
Expand Down
2 changes: 1 addition & 1 deletion Configuration/Objects.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Flowpack\FullPageCache\Middleware\CacheHeaderMiddleware:
Flowpack\FullPageCache\Middleware\FusionAutoconfigurationMiddleware:
properties:
contentCache:
object:
Expand Down
8 changes: 6 additions & 2 deletions Configuration/Settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ Neos:
'fullPageRequestCache':
middleware: 'Flowpack\FullPageCache\Middleware\RequestCacheMiddleware'
position: 'after trustedProxies'
'fullPageCacheHeader':
middleware: 'Flowpack\FullPageCache\Middleware\CacheHeaderMiddleware'
'fullPageCacheFusionAutoconfiguration':
middleware: 'Flowpack\FullPageCache\Middleware\FusionAutoconfigurationMiddleware'
position: 'after fullPageRequestCache'
Neos:
fusion:
autoInclude:
Flowpack.FullPageCache: true
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,26 @@ Flowpack:

You can also move the cache backend to something faster if available, to improve performance even more.

How it works
------------

The package defines two http middlewares:

- `RequestCacheMiddleware`: If a request is cacheable the cache is asked first and only if no response is found the
request is passed down the middleware chain. The cache lifetime and tags are determined from the
`X-FullPageCache-Enabled`, `X-FullPageCache-Lifetime` and `X-FullPageCache-Tags` that are set by upstream middlewares
or controllers. Additionally the middleware adds `ETag` and `Cache-Control` Headers taking the lifetime and setting
`maxPublicCacheTime` into account.

- `FusionAutoconfigurationMiddleware`: Connects to the fusion cache and extracts tags plus the allowed lifetime which is then
stored in the response headers `X-FullPageCache-Enabled`, `X-FullPageCache-Lifetime` and `X-FullPageCache-Tags`.
This component is only active if the header `X-FullPageCache-EnableFusionAutoconfiguration` is present in the response
which is set automatically for `Neos.Neos:Page`.

Custom controllers that want to control the caching behavior directly can set the headers `X-FullPageCache-Enabled`,
`X-FullPageCache-Lifetime` and `X-FullPageCache-Tags` directly while fusion based controllers can enable the autoconfiguration
by setting the header `X-FullPageCache-EnableFusionAutoconfiguration`.

Warning
-------

Expand Down
8 changes: 8 additions & 0 deletions Resources/Private/Fusion/Root.fusion
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
prototype(Neos.Neos:Page) {
httpResponseHead {
headers {
'X-FullPageCache-EnableFusionAutoconfiguration' = ''
'X-FullPageCache-EnableFusionAutoconfiguration'.@if.isEnabled = ${Configuration.setting('Flowpack.FullPageCache.enabled') == true}
}
}
}

0 comments on commit a64fda1

Please sign in to comment.