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 http_cache of issue 1246 #1368

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
229 changes: 229 additions & 0 deletions src/EventListener/FinalCacheHeadersListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
<?php

/*
* This file is part of Contao.
*
* Copyright (c) 2005-2018 Leo Feyer
*
* @contribute Johannes Pichler <j.pichler@webpixels.at>
*
* @description This Listener handles the new SessionListener handling from Symfony, what results into a bug
* in Contao, because the creation of the Cache Files is Broken.
*
* The listener is registered with a priority of -2038, results in beeing executed just before the
* "Terminal42\HeaderReplay\EventListener\HeaderReplayListener::onKernelRequest()" listener
* with index -2048
*
* @license LGPL-3.0+
*/

namespace Contao\CoreBundle\EventListener;

use Contao\CoreBundle\Controller\FrontendController;
use Contao\CoreBundle\Framework\ContaoFrameworkInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;

class FinalCacheHeadersListener
{

/**
* @var ContaoFrameworkInterface
*/
private $framework;

private $container;


/**
* Constructor.
*
* @param ContaoFrameworkInterface $framework
*/
public function __construct(ContaoFrameworkInterface $framework, ContainerInterface $container)
{
$this->framework = $framework;
$this->container = $container;


}

/**
* Placeholder in case we need the request object as well in the future
* @param GetResponseEvent $event
*/
public function onKernelRequest(GetResponseEvent $event)
{
// do nothing, just a placeholder
}

/**
* Adds the Contao headers to the Symfony response again after Session destroyed it.
*
* @description added new onKernelResponseListener, to fix issue 1246 with broken cache according to
* symfony/symfony 3.4.4
*
* @param FilterResponseEvent $event
*/
public function onKernelResponse(FilterResponseEvent $event)
{
// added new onKernelResponseListener, to fix issue 1246

if (!$this->framework->isInitialized()) {
return;
}


$request = $event->getRequest();
$response = $event->getResponse();

$controllerAttribute = $request->get("_controller");
$insertTagAttribute = $request->get("insertTag");
$formatAttribute = $request->get("_format");


// we distinguish between rendering of insert tags (sub requests) and page rendering (master request)
if (


// AND checks, if request is for an insert tag
$controllerAttribute == "contao.controller.insert_tags:renderAction"

// AND checks, if an insert tag attribute is given in the request
&& $insertTagAttribute

) {
// FIST CASE


// check for format html, because we only want to cache html content
if ($formatAttribute !== "html") {
return;
}

$withoutBraces = str_replace(['{{', '}}'], '',$insertTagAttribute );
$flags = explode('|', $withoutBraces);
$tag = array_shift($flags);
$elements = explode('::', $tag);

// a request for rendering insert tags
// exclude certain elements from being cached
if (
$elements[0] == 'date'
|| $elements[0] == 'ua'
|| $elements[0] == 'post'
|| $elements[0] == 'file'
|| $elements[1] == 'back'
|| $elements[1] == 'referer'
|| $elements[0] == 'request_token'
|| $elements[0] == 'toggle_view'
|| strncmp($elements[0], 'cache_', 6) === 0
|| in_array('uncached', $flags)
|| in_array('refresh', $flags)
) {
$response->setPrivate();
} else {
$this->setCacheHeaders($request, $response);
}

} else if (
// SECOND CASE


// checks, if request is a master request
$event->isMasterRequest()

// AND if the controller ist the FrontendController with its static index action
// what should be "Contao\CoreBundle\Controller\FrontendController::indexAction"
&& $controllerAttribute == FrontendController::class . "::indexAction"
) {
// second case, a master request for rendering a page
$this->setCacheHeaders($request, $response);

}


}

/**
* Set the cache headers according to the page settings.
*
* @param Request $request The request object
* @param Response $response The response object
* @return Response The response object
*/
private function setCacheHeaders(Request $request, Response $response)
{
/** @var $objPage \PageModel */
global $objPage;

if (!$objPage) {
return $response;
}

if (($objPage->cache === false || $objPage->cache === 0) && ($objPage->clientCache === false || $objPage->clientCache === 0)) {
return $response->setPrivate();
}

// Do not cache the response if a user is logged in or the page is protected
// TODO: Add support for proxies so they can vary on member context
if (FE_USER_LOGGED_IN === true || BE_USER_LOGGED_IN === true || $objPage->protected || $this->hasAuthenticatedBackendUser($request)) {
return $response->setPrivate();
}

if ($objPage->clientCache > 0) {
$response->setMaxAge($objPage->clientCache);
}

if ($objPage->cache > 0) {
$response->setSharedMaxAge($objPage->cache);
}


$response->isNotModified($request);

return $response;
}

/**
* Checks if there is an authenticated back end user.
*
* @param Request $request
*
* @return bool
*/
private function hasAuthenticatedBackendUser(Request $request)
{
if (!$request->cookies->has('BE_USER_AUTH')) {
return false;
}

$sessionHash = $this->getSessionHash('BE_USER_AUTH');

return $request->cookies->get('BE_USER_AUTH') === $sessionHash;
}

/**
* Return the session hash
*
* @param string $strCookie The cookie name
*
* @return string The session hash
*/
public function getSessionHash($strCookie)
{
$container = $this->container;
$strHash = $container->get('session')->getId();

if (!$container->getParameter('contao.security.disable_ip_check')) {
$strHash .= \Environment::get('ip');
}

$strHash .= $strCookie;

return sha1($strHash);
}
}
9 changes: 9 additions & 0 deletions src/Resources/config/listener.yml
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,12 @@ services:
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
- { name: kernel.event_listener, event: kernel.response, method: onKernelResponse }

contao.listener.final_cache_headers:
class: Contao\CoreBundle\EventListener\FinalCacheHeadersListener
arguments:
- "@contao.framework"
- "@service_container"
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: -2038 }
- { name: kernel.event_listener, event: kernel.response, method: onKernelResponse, priority: -1023 }
52 changes: 18 additions & 34 deletions src/Resources/contao/classes/FrontendTemplate.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ public function getResponse($blnCheckRequest=false)
$response->setVary(array('Contao-Page-Layout'), false);
$response->headers->set('Contao-Page-Layout', $objPage->isMobile ? 'mobile' : 'desktop');

return $this->setCacheHeaders($response);
// removed setting cache headers here, to fix issue 1246
return $response;
}


Expand Down Expand Up @@ -254,6 +255,9 @@ public static function addToUrl($strRequest, $blnIgnoreParams=false, $arrUnset=a
/**
* Check whether there is an authenticated back end user
*
* @deprecated Deprecated since Contao 4.4.14
* Is now used in RestoreCacheHeaders Listener.
*
* @return boolean True if there is an authenticated back end user
*/
public function hasAuthenticatedBackendUser()
Expand Down Expand Up @@ -362,40 +366,20 @@ public function getCustomSections($strKey=null)
}


/**
* Set the cache headers according to the page settings.
*
* @param Response $response The response object
*
* @return Response The response object
*/
private function setCacheHeaders(Response $response)
{
/** @var $objPage \PageModel */
global $objPage;
/**
* Set the cache headers according to the page settings.
*
* @param Response $response The response object
*
* @deprecated Deprecated since Contao 4.4.14
* Will be set in the RestoreCacheHeaders Listener.
* @return Response The response object
*/
private function setCacheHeaders(Response $response)
{
return $response;
}

if (($objPage->cache === false || $objPage->cache === 0) && ($objPage->clientCache === false || $objPage->clientCache === 0))
{
return $response->setPrivate();
}

// Do not cache the response if a user is logged in or the page is protected
// TODO: Add support for proxies so they can vary on member context
if (FE_USER_LOGGED_IN === true || BE_USER_LOGGED_IN === true || $objPage->protected || $this->hasAuthenticatedBackendUser())
{
return $response->setPrivate();
}

if ($objPage->clientCache > 0)
{
$response->setMaxAge($objPage->clientCache);
}

if ($objPage->cache > 0)
{
$response->setSharedMaxAge($objPage->cache);
}

return $response;
}
}