From 8fe8b9692112468810266595641b0ecc21245b95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Sun, 3 Jan 2021 00:27:32 +0100 Subject: [PATCH] [Messenger] Added RouterContextMiddleware --- .../FrameworkExtension.php | 5 ++ .../Resources/config/messenger.php | 6 ++ src/Symfony/Component/Messenger/CHANGELOG.md | 1 + .../Middleware/RouterContextMiddleware.php | 72 +++++++++++++++++ .../Messenger/Stamp/RouterContextStamp.php | 79 +++++++++++++++++++ .../RouterContextMiddlewareTest.php | 63 +++++++++++++++ src/Symfony/Component/Messenger/composer.json | 1 + 7 files changed, 227 insertions(+) create mode 100644 src/Symfony/Component/Messenger/Middleware/RouterContextMiddleware.php create mode 100644 src/Symfony/Component/Messenger/Stamp/RouterContextStamp.php create mode 100644 src/Symfony/Component/Messenger/Tests/Middleware/RouterContextMiddlewareTest.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 872b467318781..12478fadd5d8e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -97,6 +97,7 @@ use Symfony\Component\Messenger\Handler\MessageHandlerInterface; use Symfony\Component\Messenger\MessageBus; use Symfony\Component\Messenger\MessageBusInterface; +use Symfony\Component\Messenger\Middleware\RouterContextMiddleware; use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface; use Symfony\Component\Messenger\Transport\TransportFactoryInterface; use Symfony\Component\Messenger\Transport\TransportInterface; @@ -944,9 +945,13 @@ private function registerRouterConfiguration(array $config, ContainerBuilder $co if (!$this->isConfigEnabled($container, $config)) { $container->removeDefinition('console.command.router_debug'); $container->removeDefinition('console.command.router_match'); + $container->removeDefinition('messenger.middleware.router_context'); return; } + if (!class_exists(RouterContextMiddleware::class)) { + $container->removeDefinition('messenger.middleware.router_context'); + } $loader->load('routing.php'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.php index d3b9bbd681ed8..a7d993d47e316 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.php @@ -26,6 +26,7 @@ use Symfony\Component\Messenger\Middleware\FailedMessageProcessingMiddleware; use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware; use Symfony\Component\Messenger\Middleware\RejectRedeliveredMessageMiddleware; +use Symfony\Component\Messenger\Middleware\RouterContextMiddleware; use Symfony\Component\Messenger\Middleware\SendMessageMiddleware; use Symfony\Component\Messenger\Middleware\TraceableMiddleware; use Symfony\Component\Messenger\Middleware\ValidationMiddleware; @@ -100,6 +101,11 @@ service('debug.stopwatch'), ]) + ->set('messenger.middleware.router_context', RouterContextMiddleware::class) + ->args([ + service('router'), + ]) + // Discovery ->set('messenger.receiver_locator') ->args([ diff --git a/src/Symfony/Component/Messenger/CHANGELOG.md b/src/Symfony/Component/Messenger/CHANGELOG.md index 4cdf02d8d254c..381e88c4b8783 100644 --- a/src/Symfony/Component/Messenger/CHANGELOG.md +++ b/src/Symfony/Component/Messenger/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 5.3 --- + * Add the `RouterContextMiddleware` to restore the original router context when handling a message * `InMemoryTransport` can perform message serialization through dsn `in-memory://?serialize=true`. * Added `queues` option to `Worker` to only fetch messages from a specific queue from a receiver implementing `QueueReceiverInterface`. diff --git a/src/Symfony/Component/Messenger/Middleware/RouterContextMiddleware.php b/src/Symfony/Component/Messenger/Middleware/RouterContextMiddleware.php new file mode 100644 index 0000000000000..35a381870b858 --- /dev/null +++ b/src/Symfony/Component/Messenger/Middleware/RouterContextMiddleware.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Middleware; + +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Stamp\ConsumedByWorkerStamp; +use Symfony\Component\Messenger\Stamp\RouterContextStamp; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\RequestContextAwareInterface; + +/** + * Restore the Router context when processing the message. + * + * @author Jérémy Derussé + */ +class RouterContextMiddleware implements MiddlewareInterface +{ + private $router; + + public function __construct(RequestContextAwareInterface $router) + { + $this->router = $router; + } + + public function handle(Envelope $envelope, StackInterface $stack): Envelope + { + if (!$envelope->last(ConsumedByWorkerStamp::class) || !$contextStamp = $envelope->last(RouterContextStamp::class)) { + $context = $this->router->getContext(); + $envelope = $envelope->with(new RouterContextStamp( + $context->getBaseUrl(), + $context->getMethod(), + $context->getHost(), + $context->getScheme(), + $context->getHttpPort(), + $context->getHttpsPort(), + $context->getPathInfo(), + $context->getQueryString() + )); + + return $stack->next()->handle($envelope, $stack); + } + + $currentContext = $this->router->getContext(); + + /* @var RouterContextStamp $contextStamp */ + $this->router->setContext(new RequestContext( + $contextStamp->getBaseUrl(), + $contextStamp->getMethod(), + $contextStamp->getHost(), + $contextStamp->getScheme(), + $contextStamp->getHttpPort(), + $contextStamp->getHttpsPort(), + $contextStamp->getPathInfo(), + $contextStamp->getQueryString() + )); + + try { + return $stack->next()->handle($envelope, $stack); + } finally { + $this->router->setContext($currentContext); + } + } +} diff --git a/src/Symfony/Component/Messenger/Stamp/RouterContextStamp.php b/src/Symfony/Component/Messenger/Stamp/RouterContextStamp.php new file mode 100644 index 0000000000000..4906f502533b7 --- /dev/null +++ b/src/Symfony/Component/Messenger/Stamp/RouterContextStamp.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Stamp; + +/** + * @author Jérémy Derussé + */ +class RouterContextStamp implements StampInterface +{ + private $baseUrl; + private $method; + private $host; + private $scheme; + private $httpPort; + private $httpsPort; + private $pathInfo; + private $queryString; + + public function __construct(string $baseUrl, string $method, string $host, string $scheme, int $httpPort, int $httpsPort, string $pathInfo, string $queryString) + { + $this->baseUrl = $baseUrl; + $this->method = $method; + $this->host = $host; + $this->scheme = $scheme; + $this->httpPort = $httpPort; + $this->httpsPort = $httpsPort; + $this->pathInfo = $pathInfo; + $this->queryString = $queryString; + } + + public function getBaseUrl(): string + { + return $this->baseUrl; + } + + public function getMethod(): string + { + return $this->method; + } + + public function getHost(): string + { + return $this->host; + } + + public function getScheme(): string + { + return $this->scheme; + } + + public function getHttpPort(): int + { + return $this->httpPort; + } + + public function getHttpsPort(): int + { + return $this->httpsPort; + } + + public function getPathInfo(): string + { + return $this->pathInfo; + } + + public function getQueryString(): string + { + return $this->queryString; + } +} diff --git a/src/Symfony/Component/Messenger/Tests/Middleware/RouterContextMiddlewareTest.php b/src/Symfony/Component/Messenger/Tests/Middleware/RouterContextMiddlewareTest.php new file mode 100644 index 0000000000000..a183cd03fb6ab --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Middleware/RouterContextMiddlewareTest.php @@ -0,0 +1,63 @@ +createMock(RequestContextAwareInterface::class); + $router + ->expects($this->once()) + ->method('getContext') + ->willReturn($context); + + $middleware = new RouterContextMiddleware($router); + + $envelope = new Envelope(new \stdClass()); + $envelope = $middleware->handle($envelope, $this->getStackMock()); + + $this->assertNotNull($stamp = $envelope->last(RouterContextStamp::class)); + $this->assertSame('symfony.com', $stamp->getHost()); + } + + public function testMiddlewareRestoreContext() + { + $router = $this->createMock(RequestContextAwareInterface::class); + $originalContext = new RequestContext(); + + $router + ->expects($this->once()) + ->method('getContext') + ->willReturn($originalContext); + + $router + ->expects($this->exactly(2)) + ->method('setContext') + ->withConsecutive( + [$this->callback(function ($context) { + $this->assertSame('symfony.com', $context->getHost()); + + return true; + })], + [$originalContext] + ); + + $middleware = new RouterContextMiddleware($router); + $envelope = new Envelope(new \stdClass(), [ + new ConsumedByWorkerStamp(), + new RouterContextStamp('', 'GET', 'symfony.com', 'https', 80, 443, '/', ''), + ]); + $middleware->handle($envelope, $this->getStackMock()); + } +} diff --git a/src/Symfony/Component/Messenger/composer.json b/src/Symfony/Component/Messenger/composer.json index 704583c0df4ce..0a5593cc7c694 100644 --- a/src/Symfony/Component/Messenger/composer.json +++ b/src/Symfony/Component/Messenger/composer.json @@ -32,6 +32,7 @@ "symfony/http-kernel": "^4.4|^5.0", "symfony/process": "^4.4|^5.0", "symfony/property-access": "^4.4|^5.0", + "symfony/routing": "^4.4|^5.0", "symfony/serializer": "^4.4|^5.0", "symfony/service-contracts": "^1.1|^2", "symfony/stopwatch": "^4.4|^5.0",