From 68be9756c311cb70abc512b115dc83932bbce6d5 Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Tue, 12 Nov 2024 11:57:50 -0400 Subject: [PATCH 1/4] Add in `must-revalidate` cache control directive. Something of a trade off between access control responsiveness and server processing load. --- iiif_presentation_api.services.yml | 5 +++ src/Controller/V3/ManifestController.php | 7 ++- src/EventSubscriber/RevalidationDirective.php | 45 +++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 src/EventSubscriber/RevalidationDirective.php diff --git a/iiif_presentation_api.services.yml b/iiif_presentation_api.services.yml index 82c9e63..35cc516 100644 --- a/iiif_presentation_api.services.yml +++ b/iiif_presentation_api.services.yml @@ -54,3 +54,8 @@ services: class: Drupal\iiif_presentation_api\FieldMapper tags: - { name: iiif_presentation_api_mapper, base: iiif_presentation_api_map, version: v3 } + + iiif_presentation_api.revalidation_directive_subscriber: + class: Drupal\iiif_presentation_api\EventSubscriber\RevalidationDirective + tags: + - { name: event_subscriber } diff --git a/src/Controller/V3/ManifestController.php b/src/Controller/V3/ManifestController.php index 50d9422..b3226f3 100644 --- a/src/Controller/V3/ManifestController.php +++ b/src/Controller/V3/ManifestController.php @@ -8,6 +8,7 @@ use Drupal\Core\Render\RenderContext; use Drupal\Core\Render\RendererInterface; use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\iiif_presentation_api\EventSubscriber\RevalidationDirective; use Drupal\serialization\Normalizer\CacheableNormalizerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Serializer\SerializerInterface; @@ -63,7 +64,7 @@ public function build(string $parameter_name, RouteMatchInterface $route_match) CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY => $cache_meta, ]; $serialized = $this->serializer->serialize($_entity, 'iiif-p-v3', $context); - return (new CacheableJsonResponse( + $response = (new CacheableJsonResponse( $serialized, 200, [ @@ -73,6 +74,10 @@ public function build(string $parameter_name, RouteMatchInterface $route_match) ], TRUE ))->addCacheableDependency($cache_meta); + + $response->headers->set(RevalidationDirective::HEADER, TRUE); + + return $response; }); if (!$context->isEmpty()) { diff --git a/src/EventSubscriber/RevalidationDirective.php b/src/EventSubscriber/RevalidationDirective.php new file mode 100644 index 0000000..1795374 --- /dev/null +++ b/src/EventSubscriber/RevalidationDirective.php @@ -0,0 +1,45 @@ + [['doRevalidation', -10]], + ]; + } + + /** + * Event callback; add in 'must_revalidate' for our controller response. + * + * @param \Symfony\Component\HttpKernel\Event\ResponseEvent $event + * The response on which to act. + */ + public function doRevalidation(ResponseEvent $event) : void { + $response = $event->getResponse(); + + if (!$response->headers->has(static::HEADER)) { + return; + } + + $response->headers->remove(static::HEADER); + + if ($response->isCacheable()) { + $response->setCache(['must_revalidate' => TRUE]); + } + } + +} From cda08e3fe9e0b88f58618fcf40a209dbaf6e6bac Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Tue, 12 Nov 2024 12:02:47 -0400 Subject: [PATCH 2/4] Pre-existing coding standards thing. --- src/EventSubscriber/V3/BaseImageBodyEventSubscriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EventSubscriber/V3/BaseImageBodyEventSubscriber.php b/src/EventSubscriber/V3/BaseImageBodyEventSubscriber.php index 64f01de..379eee1 100644 --- a/src/EventSubscriber/V3/BaseImageBodyEventSubscriber.php +++ b/src/EventSubscriber/V3/BaseImageBodyEventSubscriber.php @@ -16,7 +16,7 @@ class BaseImageBodyEventSubscriber implements EventSubscriberInterface { * Constructor. */ public function __construct( - protected PluginManagerInterface $idPluginManager + protected PluginManagerInterface $idPluginManager, ) { } From 4d87ea8a4cb5bb12f925ff260f8daf03b801f23d Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Tue, 12 Nov 2024 13:15:57 -0400 Subject: [PATCH 3/4] Use the appropriate cache-control directive. Counter-intuitively, `must-revalidate` only takes effect after a response becomes stale, while `no-cache` allows the storage/use of cached values after revalidating them, independent of staleness. --- src/EventSubscriber/RevalidationDirective.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/EventSubscriber/RevalidationDirective.php b/src/EventSubscriber/RevalidationDirective.php index 1795374..c247618 100644 --- a/src/EventSubscriber/RevalidationDirective.php +++ b/src/EventSubscriber/RevalidationDirective.php @@ -7,7 +7,10 @@ use Symfony\Component\HttpKernel\KernelEvents; /** - * Add 'must_revalidate' to IIIF-P manifests, for access control responsiveness. + * Add 'Cache-Control: no-cache' to IIIF-P manifests. + * + * So access control actions should take effect sooner, at the expense of more + * load on the server during general use. */ class RevalidationDirective implements EventSubscriberInterface { @@ -38,7 +41,7 @@ public function doRevalidation(ResponseEvent $event) : void { $response->headers->remove(static::HEADER); if ($response->isCacheable()) { - $response->setCache(['must_revalidate' => TRUE]); + $response->setCache(['no_cache' => TRUE]); } } From 2288350a964bf6566dcbb83156deca6717f10762 Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Tue, 12 Nov 2024 13:26:20 -0400 Subject: [PATCH 4/4] Add some in-line docs. --- src/EventSubscriber/RevalidationDirective.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/EventSubscriber/RevalidationDirective.php b/src/EventSubscriber/RevalidationDirective.php index c247618..d8db033 100644 --- a/src/EventSubscriber/RevalidationDirective.php +++ b/src/EventSubscriber/RevalidationDirective.php @@ -9,8 +9,13 @@ /** * Add 'Cache-Control: no-cache' to IIIF-P manifests. * - * So access control actions should take effect sooner, at the expense of more - * load on the server during general use. + * This is done such that access control actions should take effect sooner, at + * the expense of more load on the server during general use. + * + * Note: This is explicitly weighted/prioritized such that it should run _after_ + * Drupal's `FinishResponseSubscriber` runs to add its base cache headers. + * + * @see \Drupal\Core\EventSubscriber\FinishResponseSubscriber::getSubscribedEvents() */ class RevalidationDirective implements EventSubscriberInterface {