Commit ba9eff6b authored by alexpott's avatar alexpott

Issue #2929798 by neclimdul, Sut3kh, alexpott, George Bills, mr.baileys,...

Issue #2929798 by neclimdul, Sut3kh, alexpott, George Bills, mr.baileys, mglaman: ActiveLinkResponseFilter breaks any text/html BinaryFileResponse or StreamedResponse for anonymous users
parent 7f44b458
......@@ -76,8 +76,10 @@ public function __construct(AccountInterface $current_user, CurrentPathStack $cu
* The response event.
*/
public function onResponse(FilterResponseEvent $event) {
$response = $event->getResponse();
// Only care about HTML responses.
if (stripos($event->getResponse()->headers->get('Content-Type'), 'text/html') === FALSE) {
if (stripos($response->headers->get('Content-Type'), 'text/html') === FALSE) {
return;
}
......@@ -87,14 +89,20 @@ public function onResponse(FilterResponseEvent $event) {
return;
}
$response = $event->getResponse();
$response->setContent(static::setLinkActiveClass(
$response->getContent(),
ltrim($this->currentPath->getPath(), '/'),
$this->pathMatcher->isFrontPage(),
$this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_URL)->getId(),
$event->getRequest()->query->all()
));
// If content is FALSE, assume the response does not support the
// setContent() method and skip it, for example,
// \Symfony\Component\HttpFoundation\BinaryFileResponse.
$content = $response->getContent();
if ($content !== FALSE) {
$response->setContent(static::setLinkActiveClass(
$content,
ltrim($this->currentPath->getPath(), '/'),
$this->pathMatcher->isFrontPage(),
$this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_URL)
->getId(),
$event->getRequest()->query->all()
));
}
}
/**
......
......@@ -4,8 +4,21 @@
use Drupal\Component\Serialization\Json;
use Drupal\Core\EventSubscriber\ActiveLinkResponseFilter;
use Drupal\Core\Language\LanguageDefault;
use Drupal\Core\Language\LanguageManager;
use Drupal\Core\Path\CurrentPathStack;
use Drupal\Core\Path\PathMatcherInterface;
use Drupal\Core\Session\AnonymousUserSession;
use Drupal\Core\Template\Attribute;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelInterface;
/**
* @coversDefaultClass \Drupal\Core\EventSubscriber\ActiveLinkResponseFilter
......@@ -397,4 +410,90 @@ public function testSetLinkActiveClass($html_markup, $current_path, $is_front, $
$this->assertSame($expected_html_markup, ActiveLinkResponseFilter::setLinkActiveClass($html_markup, $current_path, $is_front, $url_language, $query));
}
/**
* Tests ActiveLinkResponseFilter only affects HTML responses.
*
* @covers ::onResponse
*/
public function testOnlyHtml() {
$session = new AnonymousUserSession();
$language_manager = new LanguageManager(new LanguageDefault([]));
$request_stack = new RequestStack();
$request_stack->push(new Request());
$current_path_stack = new CurrentPathStack($request_stack);
// Make sure path matcher isn't called and we didn't get to the link logic.
$path_matcher = $this->prophesize(PathMatcherInterface::class);
$path_matcher->isFrontPage()->shouldNotBeCalled();
$subscriber = new ActiveLinkResponseFilter(
$session,
$current_path_stack,
$path_matcher->reveal(),
$language_manager
);
// A link that might otherwise be set 'active'.
$content = '<a data-drupal-link-system-path="otherpage">Other page</a>';
// Assert response with non-html content type gets ignored.
$response = new Response();
$response->setContent($content);
$response->headers->get('Content-Type', 'application/json');
$subscriber->onResponse(new FilterResponseEvent(
$this->prophesize(KernelInterface::class)->reveal(),
$request_stack->getCurrentRequest(),
HttpKernelInterface::MASTER_REQUEST,
$response
));
$this->assertSame($response->getContent(), $content);
}
/**
* Tests certain response types ignored by the ActiveLinkResponseFilter.
*
* @covers ::onResponse
*/
public function testSkipCertainResponseTypes() {
$session = new AnonymousUserSession();
$language_manager = new LanguageManager(new LanguageDefault([]));
$request_stack = new RequestStack();
$request_stack->push(new Request());
$current_path_stack = new CurrentPathStack($request_stack);
// Ensure path matcher is not called. This also tests that the
// ActiveLinkResponseFilter ignores the response.
$path_matcher = $this->prophesize(PathMatcherInterface::class);
$path_matcher->isFrontPage()->shouldNotBeCalled();
$subscriber = new ActiveLinkResponseFilter(
$session,
$current_path_stack,
$path_matcher->reveal(),
$language_manager
);
// Test BinaryFileResponse is ignored. Calling setContent() would throw a
// logic exception.
$response = new BinaryFileResponse(__FILE__, 200, ['Content-Type' => 'text/html']);
$subscriber->onResponse(new FilterResponseEvent(
$this->prophesize(KernelInterface::class)->reveal(),
$request_stack->getCurrentRequest(),
HttpKernelInterface::MASTER_REQUEST,
$response
));
// Test StreamedResponse is ignored. Calling setContent() would throw a
// logic exception.
$response = new StreamedResponse(function () {
echo 'Success!';
}, 200, ['Content-Type' => 'text/html']);
$subscriber->onResponse(new FilterResponseEvent(
$this->prophesize(KernelInterface::class)->reveal(),
$request_stack->getCurrentRequest(),
HttpKernelInterface::MASTER_REQUEST,
$response
));
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment