Commit 6a09a57c authored by catch's avatar catch
Browse files

Issue #2595695 by Wim Leers, alexpott, mfb, Crell, cilefen, dawehner: 4xx...

Issue #2595695 by Wim Leers, alexpott, mfb, Crell, cilefen, dawehner: 4xx handling using subrequests: no longer able to vary by URL
parent 37ddcca0
...@@ -1144,7 +1144,7 @@ services: ...@@ -1144,7 +1144,7 @@ services:
class: Drupal\Core\EventSubscriber\DefaultExceptionHtmlSubscriber class: Drupal\Core\EventSubscriber\DefaultExceptionHtmlSubscriber
tags: tags:
- { name: event_subscriber } - { name: event_subscriber }
arguments: ['@http_kernel', '@logger.channel.php', '@redirect.destination'] arguments: ['@http_kernel', '@logger.channel.php', '@redirect.destination', '@router.no_access_checks']
exception.default: exception.default:
class: Drupal\Core\EventSubscriber\DefaultExceptionSubscriber class: Drupal\Core\EventSubscriber\DefaultExceptionSubscriber
tags: tags:
...@@ -1164,7 +1164,7 @@ services: ...@@ -1164,7 +1164,7 @@ services:
class: Drupal\Core\EventSubscriber\CustomPageExceptionHtmlSubscriber class: Drupal\Core\EventSubscriber\CustomPageExceptionHtmlSubscriber
tags: tags:
- { name: event_subscriber } - { name: event_subscriber }
arguments: ['@config.factory', '@path.alias_manager', '@http_kernel', '@logger.channel.php', '@redirect.destination'] arguments: ['@config.factory', '@http_kernel', '@logger.channel.php', '@redirect.destination', '@router.no_access_checks']
exception.fast_404_html: exception.fast_404_html:
class: Drupal\Core\EventSubscriber\Fast404ExceptionHtmlSubscriber class: Drupal\Core\EventSubscriber\Fast404ExceptionHtmlSubscriber
tags: tags:
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
/** /**
* Exception subscriber for handling core custom HTML error pages. * Exception subscriber for handling core custom HTML error pages.
...@@ -27,31 +28,23 @@ class CustomPageExceptionHtmlSubscriber extends DefaultExceptionHtmlSubscriber { ...@@ -27,31 +28,23 @@ class CustomPageExceptionHtmlSubscriber extends DefaultExceptionHtmlSubscriber {
*/ */
protected $configFactory; protected $configFactory;
/**
* The page alias manager.
*
* @var \Drupal\Core\Path\AliasManagerInterface
*/
protected $aliasManager;
/** /**
* Constructs a new CustomPageExceptionHtmlSubscriber. * Constructs a new CustomPageExceptionHtmlSubscriber.
* *
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The configuration factory. * The configuration factory.
* @param \Drupal\Core\Path\AliasManagerInterface $alias_manager
* The alias manager service.
* @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel * @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
* The HTTP Kernel service. * The HTTP Kernel service.
* @param \Psr\Log\LoggerInterface $logger * @param \Psr\Log\LoggerInterface $logger
* The logger service. * The logger service.
* @param \Drupal\Core\Routing\RedirectDestinationInterface $redirect_destination * @param \Drupal\Core\Routing\RedirectDestinationInterface $redirect_destination
* The redirect destination service. * The redirect destination service.
* @param \Symfony\Component\Routing\Matcher\UrlMatcherInterface $access_unaware_router
* A router implementation which does not check access.
*/ */
public function __construct(ConfigFactoryInterface $config_factory, AliasManagerInterface $alias_manager, HttpKernelInterface $http_kernel, LoggerInterface $logger, RedirectDestinationInterface $redirect_destination) { public function __construct(ConfigFactoryInterface $config_factory, HttpKernelInterface $http_kernel, LoggerInterface $logger, RedirectDestinationInterface $redirect_destination, UrlMatcherInterface $access_unaware_router) {
parent::__construct($http_kernel, $logger, $redirect_destination); parent::__construct($http_kernel, $logger, $redirect_destination, $access_unaware_router);
$this->configFactory = $config_factory; $this->configFactory = $config_factory;
$this->aliasManager = $alias_manager;
} }
/** /**
...@@ -65,16 +58,20 @@ protected static function getPriority() { ...@@ -65,16 +58,20 @@ protected static function getPriority() {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function on403(GetResponseForExceptionEvent $event) { public function on403(GetResponseForExceptionEvent $event) {
$path = $this->aliasManager->getPathByAlias($this->configFactory->get('system.site')->get('page.403')); $custom_403_path = $this->configFactory->get('system.site')->get('page.403');
$this->makeSubrequest($event, trim($path, '/'), Response::HTTP_FORBIDDEN); if (!empty($custom_403_path)) {
$this->makeSubrequest($event, $custom_403_path, Response::HTTP_FORBIDDEN);
}
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function on404(GetResponseForExceptionEvent $event) { public function on404(GetResponseForExceptionEvent $event) {
$path = $this->aliasManager->getPathByAlias($this->configFactory->get('system.site')->get('page.404')); $custom_404_path = $this->configFactory->get('system.site')->get('page.404');
$this->makeSubrequest($event, trim($path, '/'), Response::HTTP_NOT_FOUND); if (!empty($custom_404_path)) {
$this->makeSubrequest($event, $custom_404_path, Response::HTTP_NOT_FOUND);
}
} }
} }
...@@ -7,16 +7,14 @@ ...@@ -7,16 +7,14 @@
namespace Drupal\Core\EventSubscriber; namespace Drupal\Core\EventSubscriber;
use Drupal\Core\Routing\AccessAwareRouterInterface;
use Drupal\Core\Routing\RedirectDestinationInterface; use Drupal\Core\Routing\RedirectDestinationInterface;
use Drupal\Core\Url;
use Drupal\Core\Utility\Error; use Drupal\Core\Utility\Error;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
/** /**
* Exception subscriber for handling core default HTML error pages. * Exception subscriber for handling core default HTML error pages.
...@@ -44,6 +42,13 @@ class DefaultExceptionHtmlSubscriber extends HttpExceptionSubscriberBase { ...@@ -44,6 +42,13 @@ class DefaultExceptionHtmlSubscriber extends HttpExceptionSubscriberBase {
*/ */
protected $redirectDestination; protected $redirectDestination;
/**
* A router implementation which does not check access.
*
* @var \Symfony\Component\Routing\Matcher\UrlMatcherInterface
*/
protected $accessUnawareRouter;
/** /**
* Constructs a new DefaultExceptionHtmlSubscriber. * Constructs a new DefaultExceptionHtmlSubscriber.
* *
...@@ -53,11 +58,14 @@ class DefaultExceptionHtmlSubscriber extends HttpExceptionSubscriberBase { ...@@ -53,11 +58,14 @@ class DefaultExceptionHtmlSubscriber extends HttpExceptionSubscriberBase {
* The logger service. * The logger service.
* @param \Drupal\Core\Routing\RedirectDestinationInterface $redirect_destination * @param \Drupal\Core\Routing\RedirectDestinationInterface $redirect_destination
* The redirect destination service. * The redirect destination service.
* @param \Symfony\Component\Routing\Matcher\UrlMatcherInterface $access_unaware_router
* A router implementation which does not check access.
*/ */
public function __construct(HttpKernelInterface $http_kernel, LoggerInterface $logger, RedirectDestinationInterface $redirect_destination) { public function __construct(HttpKernelInterface $http_kernel, LoggerInterface $logger, RedirectDestinationInterface $redirect_destination, UrlMatcherInterface $access_unaware_router) {
$this->httpKernel = $http_kernel; $this->httpKernel = $http_kernel;
$this->logger = $logger; $this->logger = $logger;
$this->redirectDestination = $redirect_destination; $this->redirectDestination = $redirect_destination;
$this->accessUnawareRouter = $access_unaware_router;
} }
/** /**
...@@ -83,7 +91,7 @@ protected function getHandledFormats() { ...@@ -83,7 +91,7 @@ protected function getHandledFormats() {
* The event to process. * The event to process.
*/ */
public function on401(GetResponseForExceptionEvent $event) { public function on401(GetResponseForExceptionEvent $event) {
$this->makeSubrequest($event, Url::fromRoute('system.401')->toString(), Response::HTTP_UNAUTHORIZED); $this->makeSubrequest($event, '/system/401', Response::HTTP_UNAUTHORIZED);
} }
/** /**
...@@ -93,7 +101,7 @@ public function on401(GetResponseForExceptionEvent $event) { ...@@ -93,7 +101,7 @@ public function on401(GetResponseForExceptionEvent $event) {
* The event to process. * The event to process.
*/ */
public function on403(GetResponseForExceptionEvent $event) { public function on403(GetResponseForExceptionEvent $event) {
$this->makeSubrequest($event, Url::fromRoute('system.403')->toString(), Response::HTTP_FORBIDDEN); $this->makeSubrequest($event, '/system/403', Response::HTTP_FORBIDDEN);
} }
/** /**
...@@ -103,7 +111,7 @@ public function on403(GetResponseForExceptionEvent $event) { ...@@ -103,7 +111,7 @@ public function on403(GetResponseForExceptionEvent $event) {
* The event to process. * The event to process.
*/ */
public function on404(GetResponseForExceptionEvent $event) { public function on404(GetResponseForExceptionEvent $event) {
$this->makeSubrequest($event, Url::fromRoute('system.404')->toString(), Response::HTTP_NOT_FOUND); $this->makeSubrequest($event, '/system/404', Response::HTTP_NOT_FOUND);
} }
/** /**
...@@ -120,54 +128,46 @@ protected function makeSubrequest(GetResponseForExceptionEvent $event, $url, $st ...@@ -120,54 +128,46 @@ protected function makeSubrequest(GetResponseForExceptionEvent $event, $url, $st
$request = $event->getRequest(); $request = $event->getRequest();
$exception = $event->getException(); $exception = $event->getException();
if (!($url && $url[0] == '/')) { try {
$url = $request->getBasePath() . '/' . $url; // Reuse the exact same request (so keep the same URL, keep the access
} // result, the exception, et cetera) but override the routing information.
// This means that aside from routing, this is identical to the master
$current_url = $request->getBasePath() . $request->getPathInfo(); // request. This allows us to generate a response that is executed on
// behalf of the master request, i.e. for the original URL. This is what
if ($url != $request->getBasePath() . '/' && $url != $current_url) { // allows us to e.g. generate a 404 response for the original URL; if we
if ($request->getMethod() === 'POST') { // would execute a subrequest with the 404 route's URL, then it'd be
$sub_request = Request::create($url, 'POST', $this->redirectDestination->getAsArray() + ['_exception_statuscode' => $status_code] + $request->request->all(), $request->cookies->all(), [], $request->server->all()); // generated for *that* URL, not the *original* URL.
} $sub_request = clone $request;
else { $sub_request->attributes->add($this->accessUnawareRouter->match($url));
$sub_request = Request::create($url, 'GET', $request->query->all() + $this->redirectDestination->getAsArray() + ['_exception_statuscode' => $status_code], $request->cookies->all(), [], $request->server->all());
// Add to query (GET) or request (POST) parameters:
// - 'destination' (to ensure e.g. the login form in a 403 response
// redirects to the original URL)
// - '_exception_statuscode'
$parameters = $sub_request->isMethod('GET') ? $sub_request->query : $sub_request->request;
$parameters->add($this->redirectDestination->getAsArray() + ['_exception_statuscode' => $status_code]);
$response = $this->httpKernel->handle($sub_request, HttpKernelInterface::SUB_REQUEST);
// Only 2xx responses should have their status code overridden; any
// other status code should be passed on: redirects (3xx), error (5xx)…
// @see https://www.drupal.org/node/2603788#comment-10504916
if ($response->isSuccessful()) {
$response->setStatusCode($status_code);
} }
try { // Persist any special HTTP headers that were set on the exception.
// Persist the 'exception' attribute to the subrequest. if ($exception instanceof HttpExceptionInterface) {
$sub_request->attributes->set('exception', $request->attributes->get('exception')); $response->headers->add($exception->getHeaders());
// Persist the access result attribute to the subrequest, so that the
// error page inherits the access result of the master request.
$sub_request->attributes->set(AccessAwareRouterInterface::ACCESS_RESULT, $request->attributes->get(AccessAwareRouterInterface::ACCESS_RESULT));
// Carry over the session to the subrequest.
if ($session = $request->getSession()) {
$sub_request->setSession($session);
}
$response = $this->httpKernel->handle($sub_request, HttpKernelInterface::SUB_REQUEST);
// Only 2xx responses should have their status code overridden; any
// other status code should be passed on: redirects (3xx), error (5xx)…
// @see https://www.drupal.org/node/2603788#comment-10504916
if ($response->isSuccessful()) {
$response->setStatusCode($status_code);
}
// Persist any special HTTP headers that were set on the exception.
if ($exception instanceof HttpExceptionInterface) {
$response->headers->add($exception->getHeaders());
}
$event->setResponse($response);
}
catch (\Exception $e) {
// If an error happened in the subrequest we can't do much else. Instead,
// just log it. The DefaultExceptionSubscriber will catch the original
// exception and handle it normally.
$error = Error::decodeException($e);
$this->logger->log($error['severity_level'], '%type: @message in %function (line %line of %file).', $error);
} }
$event->setResponse($response);
}
catch (\Exception $e) {
// If an error happened in the subrequest we can't do much else. Instead,
// just log it. The DefaultExceptionSubscriber will catch the original
// exception and handle it normally.
$error = Error::decodeException($e);
$this->logger->log($error['severity_level'], '%type: @message in %function (line %line of %file).', $error);
} }
} }
......
...@@ -74,7 +74,7 @@ public function testDestination() { ...@@ -74,7 +74,7 @@ public function testDestination() {
// Make sure that 404 pages do not populate $_GET['destination'] with // Make sure that 404 pages do not populate $_GET['destination'] with
// external URLs. // external URLs.
\Drupal::configFactory()->getEditable('system.site')->set('page.404', 'system-test/get-destination')->save(); \Drupal::configFactory()->getEditable('system.site')->set('page.404', '/system-test/get-destination')->save();
$this->drupalGet('http://example.com', ['external' => FALSE]); $this->drupalGet('http://example.com', ['external' => FALSE]);
$this->assertResponse(404); $this->assertResponse(404);
$this->assertIdentical(Url::fromRoute('<front>')->toString(), $this->getRawContent(), 'External URL is not allowed on 404 pages.'); $this->assertIdentical(Url::fromRoute('<front>')->toString(), $this->getRawContent(), 'External URL is not allowed on 404 pages.');
......
...@@ -31,6 +31,7 @@ protected function setUp() { ...@@ -31,6 +31,7 @@ protected function setUp() {
parent::setUp(); parent::setUp();
$this->installSchema('system', ['router']); $this->installSchema('system', ['router']);
$this->installEntitySchema('date_format');
\Drupal::service('router.builder')->rebuild(); \Drupal::service('router.builder')->rebuild();
} }
...@@ -98,6 +99,42 @@ public function testHtml404() { ...@@ -98,6 +99,42 @@ public function testHtml404() {
$this->assertEqual($response->headers->get('Content-type'), 'text/html; charset=UTF-8'); $this->assertEqual($response->headers->get('Content-type'), 'text/html; charset=UTF-8');
} }
/**
* Tests that the exception response is executed in the original context.
*/
public function testExceptionResponseGeneratedForOriginalRequest() {
// Test with 404 path pointing to a route that uses '_controller'.
$response = $this->doTest404Route('/router_test/test25');
$this->assertTrue(strpos($response->getContent(), '/not-found') !== FALSE);
// Test with 404 path pointing to a route that uses '_form'.
$response = $this->doTest404Route('/router_test/test26');
$this->assertTrue(strpos($response->getContent(), '<form class="system-logging-settings"') !== FALSE);
// Test with 404 path pointing to a route that uses '_entity_form'.
$response = $this->doTest404Route('/router_test/test27');
$this->assertTrue(strpos($response->getContent(), '<form class="date-format-add-form date-format-form"') !== FALSE);
}
/**
* Sets the given path to use as the 404 page and triggers a 404.
*
* @param string $path
* @return \Drupal\Core\Render\HtmlResponse
*
* @see \Drupal\system\Tests\Routing\ExceptionHandlingTest::testExceptionResponseGeneratedForOriginalRequest()
*/
protected function doTest404Route($path) {
$this->config('system.site')->set('page.404', $path)->save();
$request = Request::create('/not-found');
$request->setFormat('html', ['text/html']);
/** @var \Symfony\Component\HttpKernel\HttpKernelInterface $kernel */
$kernel = \Drupal::getContainer()->get('http_kernel');
return $kernel->handle($request)->prepare($request);
}
/** /**
* Tests if exception backtraces are properly escaped when output to HTML. * Tests if exception backtraces are properly escaped when output to HTML.
*/ */
......
...@@ -44,6 +44,14 @@ function testAccessDenied() { ...@@ -44,6 +44,14 @@ function testAccessDenied() {
$this->assertText(t('Access denied'), 'Found the default 403 page'); $this->assertText(t('Access denied'), 'Found the default 403 page');
$this->assertResponse(403); $this->assertResponse(403);
// Ensure that users without permission are denied access and have the
// correct path information in drupalSettings.
$this->drupalLogin($this->createUser([]));
$this->drupalGet('admin', ['query' => ['foo' => 'bar']]);
$this->assertEqual($this->drupalSettings['path']['currentPath'], 'admin');
$this->assertEqual($this->drupalSettings['path']['currentPathIsAdmin'], TRUE);
$this->assertEqual($this->drupalSettings['path']['currentQuery'], ['foo' => 'bar']);
$this->drupalLogin($this->adminUser); $this->drupalLogin($this->adminUser);
// Set a custom 404 page without a starting slash. // Set a custom 404 page without a starting slash.
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
use Drupal\Core\PageCache\RequestPolicyInterface; use Drupal\Core\PageCache\RequestPolicyInterface;
use Drupal\Core\PhpStorage\PhpStorageFactory; use Drupal\Core\PhpStorage\PhpStorageFactory;
use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Routing\StackedRouteMatchInterface;
use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Menu\MenuTreeParameters; use Drupal\Core\Menu\MenuTreeParameters;
use Drupal\Core\Extension\ModuleHandler; use Drupal\Core\Extension\ModuleHandler;
...@@ -646,7 +647,9 @@ function system_js_settings_build(&$settings, AttachedAssetsInterface $assets) { ...@@ -646,7 +647,9 @@ function system_js_settings_build(&$settings, AttachedAssetsInterface $assets) {
* as well as theme_token ajax state. * as well as theme_token ajax state.
*/ */
function system_js_settings_alter(&$settings, AttachedAssetsInterface $assets) { function system_js_settings_alter(&$settings, AttachedAssetsInterface $assets) {
$request = \Drupal::request(); // As this is being output in the final response always use the master
// request.
$request = \Drupal::requestStack()->getMasterRequest();
$current_query = $request->query->all(); $current_query = $request->query->all();
// Let output path processors set a prefix. // Let output path processors set a prefix.
...@@ -656,8 +659,12 @@ function system_js_settings_alter(&$settings, AttachedAssetsInterface $assets) { ...@@ -656,8 +659,12 @@ function system_js_settings_alter(&$settings, AttachedAssetsInterface $assets) {
$path_processor->processOutbound('/', $options); $path_processor->processOutbound('/', $options);
$pathPrefix = $options['prefix']; $pathPrefix = $options['prefix'];
$current_path = \Drupal::routeMatch()->getRouteName() ? Url::fromRouteMatch(\Drupal::routeMatch())->getInternalPath() : ''; $route_match = \Drupal::routeMatch();
$current_path_is_admin = \Drupal::service('router.admin_context')->isAdminRoute(); if ($route_match instanceof StackedRouteMatchInterface) {
$route_match = $route_match->getMasterRouteMatch();
}
$current_path = $route_match->getRouteName() ? Url::fromRouteMatch($route_match)->getInternalPath() : '';
$current_path_is_admin = \Drupal::service('router.admin_context')->isAdminRoute($route_match->getRouteObject());
$path_settings = [ $path_settings = [
'baseUrl' => $request->getBaseUrl() . '/', 'baseUrl' => $request->getBaseUrl() . '/',
'pathPrefix' => $pathPrefix, 'pathPrefix' => $pathPrefix,
......
...@@ -155,6 +155,29 @@ router_test.24: ...@@ -155,6 +155,29 @@ router_test.24:
requirements: requirements:
_access: 'TRUE' _access: 'TRUE'
router_test.25:
path: '/router_test/test25'
defaults:
_controller: '\Drupal\router_test\TestControllers::test25'
requirements:
_access: 'TRUE'
router_test.26:
path: '/router_test/test26'
defaults:
_form: 'Drupal\system\Form\LoggingForm'
_title: 'Cron'
requirements:
_access: 'TRUE'
router_test.27:
path: '/router_test/test27'
defaults:
_entity_form: 'date_format.add'
_title: 'Add date format'
requirements:
_access: 'TRUE'
router_test.hierarchy_parent: router_test.hierarchy_parent:
path: '/menu-test/parent' path: '/menu-test/parent'
defaults: defaults:
......
...@@ -106,6 +106,15 @@ public function test24() { ...@@ -106,6 +106,15 @@ public function test24() {
throw new \Exception('Escaped content: <p> <br> <h3>'); throw new \Exception('Escaped content: <p> <br> <h3>');
} }
public function test25() {
return [
'#cache' => [
'url',
],
'#markup' => \Drupal::requestStack()->getCurrentRequest()->getUri(),
];
}
/** /**
* Throws an exception. * Throws an exception.
* *
......
...@@ -35,13 +35,6 @@ class CustomPageExceptionHtmlSubscriberTest extends UnitTestCase { ...@@ -35,13 +35,6 @@ class CustomPageExceptionHtmlSubscriberTest extends UnitTestCase {
*/ */
protected $configFactory; protected $configFactory;
/**
* The mocked alias manager.
*
* @var \Drupal\Core\Path\AliasManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $aliasManager;
/** /**
* The mocked logger. * The mocked logger.
* *
...@@ -70,22 +63,32 @@ class CustomPageExceptionHtmlSubscriberTest extends UnitTestCase { ...@@ -70,22 +63,32 @@ class CustomPageExceptionHtmlSubscriberTest extends UnitTestCase {
*/ */
protected $redirectDestination; protected $redirectDestination;
/**
* The mocked access unaware router.
* @var \Symfony\Component\Routing\Matcher\UrlMatcherInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $accessUnawareRouter;
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function setUp() { protected function setUp() {
$this->configFactory = $this->getConfigFactoryStub(['system.site' => ['page.403' => 'access-denied-page', 'page.404' => 'not-found-page']]); $this->configFactory = $this->getConfigFactoryStub(['system.site' => ['page.403' => '/access-denied-page', 'page.404' => '/not-found-page']]);
$this->aliasManager = $this->getMock('Drupal\Core\Path\AliasManagerInterface');
$this->kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); $this->kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
$this->logger = $this->getMock('Psr\Log\LoggerInterface'); $this->logger = $this->getMock('Psr\Log\LoggerInterface');
$this->redirectDestination = $this->getMock('\Drupal\Core\Routing\RedirectDestinationInterface'); $this->redirectDestination = $this->getMock('\Drupal\Core\Routing\RedirectDestinationInterface');
$this->redirectDestination->expects($this->any()) $this->redirectDestination->expects($this->any())