Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix custom ttl with symfony HttpCache to work regardless of s-maxage #577

Merged
merged 1 commit into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,24 @@ Changelog

See also the [GitHub releases page](https://github.com/FriendsOfSymfony/FOSHttpCache/releases).

2.16 (unreleased)
2.16
----

* Add flag `fallbackToSmaxage` to `CustomTtlListener` to allow controlling fallback to s-maxage if custom TTL header is not defined on the response.
* Fix for Symfony HttpCache: Do not call store if Response object is not longer cacheable after event listeners. If you use the custom TTL system, this is only a performance improvement, because the TTL of the response would still be 0. With a custom listener that changes the response explicitly to not be cached but does not change s-maxage, this bug might have led to caching responses that should not have been cached
### Symfony HttpCache

* Add events `PRE_FORWARD` and `POST_FORWARD` to allow event listeners to alter
the request before and after it is sent to the backend.
* Changed CustomTtlListener to use the `POST_FORWARD` event instead of
`PRE_STORE`. Using PRE_STORE, requests that are not considered cacheable by
Symfony were never cached, even when they had a custom TTL header.
* Add flag `fallbackToSmaxage` to `CustomTtlListener` to allow controlling
fallback to s-maxage if custom TTL header is not defined on the response.
* Fix: Do not call store if Response object is not longer cacheable after event
listeners. If you use the custom TTL system, this is only a performance
improvement, because the TTL of the response would still be 0. With a custom
listener that changes the response explicitly to not be cached but does not
change s-maxage, this bug might have led to caching responses that should not
have been cached.

2.15.3
------
Expand Down
9 changes: 4 additions & 5 deletions src/SymfonyCache/CustomTtlListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,9 @@ public function useCustomTtl(CacheEvent $e)
: 'false'
;
$response->headers->set(static::SMAXAGE_BACKUP, $backup);
$response->setTtl(
$response->headers->has($this->ttlHeader)
? $response->headers->get($this->ttlHeader)
: 0
$response->headers->addCacheControlDirective(
's-maxage',
$response->headers->get($this->ttlHeader, 0)
);
}

Expand Down Expand Up @@ -111,7 +110,7 @@ public function cleanResponse(CacheEvent $e)
public static function getSubscribedEvents(): array
{
return [
Events::PRE_STORE => 'useCustomTtl',
Events::POST_FORWARD => 'useCustomTtl',
Events::POST_HANDLE => 'cleanResponse',
];
}
Expand Down
10 changes: 10 additions & 0 deletions src/SymfonyCache/EventDispatchingHttpCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,16 @@ protected function invalidate(Request $request, $catch = false): Response
return parent::invalidate($request, $catch);
}

protected function forward(Request $request, bool $catch = false, ?Response $entry = null): Response
{
// do not abort early, if $entry is set this is a validation request
$this->dispatch(Events::PRE_FORWARD, $request, $entry);

$response = parent::forward($request, $catch, $entry);

return $this->dispatch(Events::POST_FORWARD, $request, $response);
}

/**
* Dispatch an event if there are any listeners.
*
Expand Down
4 changes: 4 additions & 0 deletions src/SymfonyCache/Events.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ final class Events

public const POST_HANDLE = 'fos_http_cache.post_handle';

public const PRE_FORWARD = 'fos_http_cache.pre_forward';

public const POST_FORWARD = 'fos_http_cache.post_forward';

public const PRE_INVALIDATE = 'fos_http_cache.pre_invalidate';

public const PRE_STORE = 'fos_http_cache.pre_store';
Expand Down
7 changes: 4 additions & 3 deletions tests/Functional/Symfony/EventDispatchingHttpCacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,14 @@ public function testEventListeners()

$httpKernel = \Mockery::mock(HttpKernelInterface::class)
->shouldReceive('handle')
->withArgs([$request, HttpKernelInterface::MASTER_REQUEST, true])
->andReturn($expectedResponse)
->getMock();
$store = \Mockery::mock(StoreInterface::class)
->shouldReceive('lookup')->andReturn(null)->times(1)
->shouldReceive('write')->times(1)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when i only add this, the test starts failing because write is never called.

->shouldReceive('unlock')->times(1)
// need to declare the cleanup function explicitly to avoid issue between register_shutdown_function and mockery
->shouldReceive('cleanup')
->atMost(1)
->shouldReceive('cleanup')->atMost(1)
->getMock();
$kernel = new AppCache($httpKernel, $store);
$kernel->addSubscriber(new CustomTtlListener());
Expand Down
Loading