core/modules/big_pipe/big_pipe.services.yml | 5 -- .../big_pipe/src/PageCache/DenyBigPipeResponse.php | 24 ------- .../page_cache/src/StackMiddleware/PageCache.php | 79 +++++++++++++++------- 3 files changed, 56 insertions(+), 52 deletions(-) diff --git a/core/modules/big_pipe/big_pipe.services.yml b/core/modules/big_pipe/big_pipe.services.yml index 82a0c73..5235037 100644 --- a/core/modules/big_pipe/big_pipe.services.yml +++ b/core/modules/big_pipe/big_pipe.services.yml @@ -23,8 +23,3 @@ services: class: Drupal\big_pipe\EventSubscriber\NoBigPipeRouteAlterSubscriber tags: - { name: event_subscriber } - - big_pipe.page_cache: - class: Drupal\big_pipe\PageCache\DenyBigPipeResponse - tags: - - { name: page_cache_response_policy } diff --git a/core/modules/big_pipe/src/PageCache/DenyBigPipeResponse.php b/core/modules/big_pipe/src/PageCache/DenyBigPipeResponse.php deleted file mode 100644 index 9fd5e2c..0000000 --- a/core/modules/big_pipe/src/PageCache/DenyBigPipeResponse.php +++ /dev/null @@ -1,24 +0,0 @@ -getStreamedResponse(); + if ($streamed_response !== FALSE) { + $this->storeResponse($request, $streamed_response); + } + } + } + /** * Sidesteps the page cache and directly forwards a request to the backend. * @@ -205,6 +220,43 @@ protected function fetch(Request $request, $type = self::MASTER_REQUEST, $catch /** @var \Symfony\Component\HttpFoundation\Response $response */ $response = $this->httpKernel->handle($request, $type, $catch); + // Currently it is not possible to cache binary file or streamed responses: + // https://github.com/symfony/symfony/issues/9128#issuecomment-25088678. + // Therefore exclude them, even for subclasses that implement + // CacheableResponseInterface. + if ($response instanceof BinaryFileResponse || $response instanceof StreamedResponse) { + return $response; + } + // The exception: streamed responses that implement + // StreamedResponseInterface: those can be cached by PageCache during the + // 'terminate' phase/event. Already set the 'X-Drupal-Cache' header, so that + // header is present as expected. + // @see ::terminate() + elseif ($response instanceof StreamedResponseInterface) { + // Mark response as a cache miss. + $response->headers->set('X-Drupal-Cache', 'MISS'); + return $response; + } + + // Allow policy rules to further restrict which responses to cache. + if ($this->responsePolicy->check($response, $request) === ResponsePolicyInterface::DENY) { + return $response; + } + + $this->storeResponse($request, $response); + + return $response; + } + + /** + * Stores a response in the page cache. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * A request object. + * @param \Symfony\Component\HttpFoundation\Response $response + * A response object that should be stored in the page cache. + */ + protected function storeResponse(Request $request, Response $response) { // Drupal's primary cache invalidation architecture is cache tags: any // response that varies by a configuration value or data in a content // entity should have cache tags, to allow for instant cache invalidation @@ -227,27 +279,7 @@ protected function fetch(Request $request, $type = self::MASTER_REQUEST, $catch // so by replacing/extending this middleware service or adding another // one. if (!$response instanceof CacheableResponseInterface) { - return $response; - } - - // Mark response as a cache miss. - $response->headers->set('X-Drupal-Cache', 'MISS'); - - // Currently it is not possible to cache binary file or streamed responses: - // https://github.com/symfony/symfony/issues/9128#issuecomment-25088678. - // Therefore exclude them, even for subclasses that implement - // CacheableResponseInterface. - if ($response instanceof BinaryFileResponse || $response instanceof StreamedResponse) { - return $response; - } - - // Allow policy rules to further restrict which responses to cache. - // @todo supporting BigPipe means that we return early here, which means - // that none of the code below runs. Thus: - // - page cache response policies won't work for BigPipe (which I guess can be okay since we need to duplicate that part of the page cache code in BigPipe anyway, because we need to assemble the final non-BigPipe response from it and then store it in Page Cache - // - for the same reason, we need to handle all the other logic below: 4xx responses cache tags + expiration date - if ($this->responsePolicy->check($response, $request) === ResponsePolicyInterface::DENY) { - return $response; + return; } // The response passes all of the above checks, so cache it. @@ -260,7 +292,8 @@ protected function fetch(Request $request, $type = self::MASTER_REQUEST, $catch $expire = ($date > time()) ? $date : Cache::PERMANENT; $this->set($request, $response, $expire, $tags); - return $response; + // Mark response as a cache miss. + $response->headers->set('X-Drupal-Cache', 'MISS'); } /**