Skip to content

Commit 84046cd

Browse files
committed
TASK: Extract the generation of public CacheControl Headers into separate middleware
This allows the FullPageCache Package to be used in cases where cacheability and lifetime cannot be extracted from the fusionCache. A possible useCase for that are custom controllers may or may not use fusion for rendering. The public lifetime is now calculated from the header `X-FullPageCache-Lifetime` limited by the Setting `maxPublicCacheTime`. If the response already contains a `CacheControl` header this is skipped.
1 parent 5e9664b commit 84046cd

4 files changed

Lines changed: 82 additions & 23 deletions

File tree

Classes/Middleware/CacheHeaderMiddleware.php

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,6 @@ class CacheHeaderMiddleware implements MiddlewareInterface
3232
*/
3333
protected $enabled;
3434

35-
/**
36-
* @var boolean
37-
* @Flow\InjectConfiguration(path="maxPublicCacheTime")
38-
*/
39-
protected $maxPublicCacheTime;
40-
4135
public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface
4236
{
4337
if (!$this->enabled || !$request->hasHeader(RequestCacheMiddleware::HEADER_ENABLED)) {
@@ -52,23 +46,6 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
5246
return $response;
5347
}
5448

55-
$publicLifetime = 0;
56-
if ($this->maxPublicCacheTime > 0) {
57-
if ($lifetime > 0 && $lifetime < $this->maxPublicCacheTime) {
58-
$publicLifetime = $lifetime;
59-
} else {
60-
$publicLifetime = $this->maxPublicCacheTime;
61-
}
62-
}
63-
64-
if ($publicLifetime > 0) {
65-
$entryContentHash = md5($response->getBody()->getContents());
66-
$response->getBody()->rewind();
67-
$response = $response
68-
->withHeader('ETag', '"' . $entryContentHash . '"')
69-
->withHeader('CacheControl', 'public, max-age=' . $publicLifetime);
70-
}
71-
7249
$response = $response
7350
->withHeader(RequestCacheMiddleware::HEADER_ENABLED, "");
7451

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Flowpack\FullPageCache\Middleware;
5+
6+
use Neos\Flow\Annotations as Flow;
7+
use Psr\Http\Message\ResponseInterface;
8+
use Psr\Http\Message\ServerRequestInterface;
9+
use Psr\Http\Server\MiddlewareInterface;
10+
use Psr\Http\Server\RequestHandlerInterface;
11+
12+
class PublicCacheHeaderMiddleware implements MiddlewareInterface
13+
{
14+
15+
/**
16+
* @var boolean
17+
* @Flow\InjectConfiguration(path="enabled")
18+
*/
19+
protected $enabled;
20+
21+
/**
22+
* @var boolean
23+
* @Flow\InjectConfiguration(path="maxPublicCacheTime")
24+
*/
25+
protected $maxPublicCacheTime;
26+
27+
public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface
28+
{
29+
if (!$this->enabled || !$request->hasHeader(RequestCacheMiddleware::HEADER_ENABLED) || $this->maxPublicCacheTime == 0) {
30+
return $next->handle($request);
31+
}
32+
33+
$response = $next->handle($request);
34+
35+
if ($response->hasHeader("CacheControl")) {
36+
return $response;
37+
}
38+
39+
$lifetime = (int)$response->getHeaderLine(RequestCacheMiddleware::HEADER_LIFTIME);
40+
41+
$publicLifetime = 0;
42+
if ($this->maxPublicCacheTime > 0) {
43+
if ($lifetime > 0 && $lifetime < $this->maxPublicCacheTime) {
44+
$publicLifetime = $lifetime;
45+
} else {
46+
$publicLifetime = $this->maxPublicCacheTime;
47+
}
48+
}
49+
50+
if ($publicLifetime > 0) {
51+
$entryContentHash = md5($response->getBody()->getContents());
52+
$response->getBody()->rewind();
53+
$response = $response
54+
->withHeader('ETag', '"' . $entryContentHash . '"')
55+
->withHeader('CacheControl', 'public, max-age=' . $publicLifetime);
56+
}
57+
58+
return $response;
59+
}
60+
}

Configuration/Settings.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ Neos:
3838
'fullPageRequestCache':
3939
middleware: 'Flowpack\FullPageCache\Middleware\RequestCacheMiddleware'
4040
position: 'after trustedProxies'
41+
'fullPagePublicCacheHeader':
42+
middleware: 'Flowpack\FullPageCache\Middleware\PublicCacheHeaderMiddleware'
43+
position: 'before fullPageCacheHeader'
4144
'fullPageCacheHeader':
4245
middleware: 'Flowpack\FullPageCache\Middleware\CacheHeaderMiddleware'
4346
position: 'after fullPageRequestCache'

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,25 @@ Flowpack:
5151

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

54+
How it works
55+
------------
56+
57+
The package defines three http middlewares:
58+
59+
- RequestCacheMiddleware: If a request is cacheble the cache is asked first and only if no response is found the
60+
reqeust is passed down the middleware chain. The cache lifetime and tags are determined from the
61+
`X-FullPageCache-Enabled`, `X-FullPageCache-Lifetime` and `X-FullPageCache-Tags` that are set by upstream middlewares
62+
or controllers.
63+
64+
- PublicCacheHeaderMiddleware: Set `ETag` and `CacheControl` Headers based on the `X-FullPageCache-Enabled` and the
65+
`X-FullPageCache-Lifetime` headers taking the `maxPublicCacheTime` into account.
66+
67+
- CacheHeaderMiddleware: Connects to the fusion cache and extracts tags plus the allowed lifetime which is then
68+
stored in the response headers `X-FullPageCache-Enabled`, `X-FullPageCache-Lifetime` and `X-FullPageCache-Tags`
69+
70+
Controllers that want to control the caching behavior directly can set the headers `X-FullPageCache-Enabled`,
71+
`X-FullPageCache-Lifetime` and `X-FullPageCache-Tags` directly.
72+
5473
Warning
5574
-------
5675

0 commit comments

Comments
 (0)