Skip to content
This repository has been archived by the owner on Jan 17, 2022. It is now read-only.

Commit

Permalink
feat(response-processor): hiding Swoole StreamedResponse support from…
Browse files Browse the repository at this point in the history
… Symfony (#509)

Co-authored-by: k911 <konradobal@gmail.com>
  • Loading branch information
Martin Fris and k911 committed May 5, 2021
1 parent 8793695 commit ebe8077
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 69 deletions.
Expand Up @@ -5,9 +5,9 @@
namespace K911\Swoole\Bridge\Symfony\Bundle\DependencyInjection\CompilerPass;

use K911\Swoole\Bridge\Symfony\HttpFoundation\StreamedResponseListener;
use K911\Swoole\Bridge\Symfony\HttpFoundation\StreamedResponseProcessor;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

/**
Expand All @@ -17,18 +17,24 @@ final class StreamedResponseListenerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
$oldListenerDefinition = null;
$definitionId = 'streamed_response_listener';
$oldDefinitionId = \sprintf('%s.original', $definitionId);

if ($container->hasDefinition('streamed_response_listener')) {
$definition = $container->getDefinition('streamed_response_listener');
$definition
->setClass(StreamedResponseListener::class)
->setAutowired(true)
;
} else {
$definition = $container
->autowire('streamed_response_listener', StreamedResponseListener::class)
->setAutoconfigured(true)
;
$oldListenerDefinition = $container->getDefinition('streamed_response_listener');
$oldListenerDefinition->clearTag('kernel.event_subscriber');
$container->setDefinition($oldDefinitionId, $oldListenerDefinition);
}
$definition->setArgument(1, new Reference(StreamedResponseProcessor::class));

$newDefinition = new Definition(StreamedResponseListener::class);
$newDefinition->setAutoconfigured(true);
$newDefinition->addTag('kernel.event_subscriber');

if (null !== $oldListenerDefinition) {
$newDefinition->setArgument('$delegate', new Reference($oldDefinitionId));
}

$container->setDefinition($definitionId, $newDefinition);
}
}
7 changes: 6 additions & 1 deletion src/Bridge/Symfony/Bundle/Resources/config/services.yaml
Expand Up @@ -19,7 +19,11 @@ services:

K911\Swoole\Bridge\Symfony\HttpFoundation\SetRequestRuntimeConfiguration:

K911\Swoole\Bridge\Symfony\HttpFoundation\SwooleRequestResponseContextManager:
K911\Swoole\Bridge\Symfony\HttpFoundation\ResponseProcessorInjectorInterface: '@K911\Swoole\Bridge\Symfony\HttpFoundation\ResponseProcessorInjector'

K911\Swoole\Bridge\Symfony\HttpFoundation\ResponseProcessorInjector:
arguments:
$responseProcessor: '@response_processor.headers_and_cookies.streamed'

K911\Swoole\Bridge\Symfony\HttpFoundation\RequestFactoryInterface:
class: K911\Swoole\Bridge\Symfony\HttpFoundation\RequestFactory
Expand All @@ -29,6 +33,7 @@ services:

K911\Swoole\Bridge\Symfony\HttpFoundation\NoOpStreamedResponseProcessor:
decorates: K911\Swoole\Bridge\Symfony\HttpFoundation\ResponseProcessorInterface
decoration_priority: -100
arguments:
- '@K911\Swoole\Bridge\Symfony\HttpFoundation\NoOpStreamedResponseProcessor.inner'

Expand Down
31 changes: 31 additions & 0 deletions src/Bridge/Symfony/HttpFoundation/ResponseProcessorInjector.php
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Bridge\Symfony\HttpFoundation;

use Swoole\Http\Response as SwooleResponse;
use Symfony\Component\HttpFoundation\Request as HttpFoundationRequest;
use Symfony\Component\HttpFoundation\Response as HttpFoundationResponse;

final class ResponseProcessorInjector implements ResponseProcessorInjectorInterface
{
private $responseProcessor;

public function __construct(ResponseProcessorInterface $responseProcessor)
{
$this->responseProcessor = $responseProcessor;
}

public function injectProcessor(
HttpFoundationRequest $request,
SwooleResponse $swooleResponse
): void {
$request->attributes->set(
self::ATTR_KEY_RESPONSE_PROCESSOR,
function (HttpFoundationResponse $httpFoundationResponse) use ($swooleResponse): void {
$this->responseProcessor->process($httpFoundationResponse, $swooleResponse);
}
);
}
}
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Bridge\Symfony\HttpFoundation;

use Swoole\Http\Response as SwooleResponse;
use Symfony\Component\HttpFoundation\Request as HttpFoundationRequest;

interface ResponseProcessorInjectorInterface
{
public const ATTR_KEY_RESPONSE_PROCESSOR = 'swoole_streamed_response_processor';

public function injectProcessor(HttpFoundationRequest $request, SwooleResponse $swooleResponse): void;
}
33 changes: 21 additions & 12 deletions src/Bridge/Symfony/HttpFoundation/StreamedResponseListener.php
Expand Up @@ -7,19 +7,16 @@
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\EventListener\StreamedResponseListener as HttpFoundationStreamedResponseListener;
use Symfony\Component\HttpKernel\KernelEvents;

class StreamedResponseListener implements EventSubscriberInterface
{
private $contextManager;
private $responseProcessor;

public function __construct(
SwooleRequestResponseContextManager $contextManager,
ResponseProcessorInterface $responseProcessor
) {
$this->responseProcessor = $responseProcessor;
$this->contextManager = $contextManager;
private $delegate;

public function __construct(?HttpFoundationStreamedResponseListener $delegate = null)
{
$this->delegate = $delegate;
}

public function onKernelResponse(ResponseEvent $event): void
Expand All @@ -29,10 +26,22 @@ public function onKernelResponse(ResponseEvent $event): void
}

$response = $event->getResponse();
if ($response instanceof StreamedResponse) {
$swooleResponse = $this->contextManager->findResponse($event->getRequest());
$this->responseProcessor->process($response, $swooleResponse);
$attributes = $event->getRequest()->attributes;

if ($response instanceof StreamedResponse &&
$attributes->has(ResponseProcessorInjectorInterface::ATTR_KEY_RESPONSE_PROCESSOR)
) {
$processor = $attributes->get(ResponseProcessorInjectorInterface::ATTR_KEY_RESPONSE_PROCESSOR);
$processor($response);

return;
}

if (null === $this->delegate) {
return;
}

$this->delegate->onKernelResponse($event);
}

public static function getSubscribedEvents()
Expand Down

This file was deleted.

10 changes: 5 additions & 5 deletions src/Bridge/Symfony/HttpKernel/HttpKernelRequestHandler.php
Expand Up @@ -5,8 +5,8 @@
namespace K911\Swoole\Bridge\Symfony\HttpKernel;

use K911\Swoole\Bridge\Symfony\HttpFoundation\RequestFactoryInterface;
use K911\Swoole\Bridge\Symfony\HttpFoundation\ResponseProcessorInjectorInterface;
use K911\Swoole\Bridge\Symfony\HttpFoundation\ResponseProcessorInterface;
use K911\Swoole\Bridge\Symfony\HttpFoundation\SwooleRequestResponseContextManager;
use K911\Swoole\Server\RequestHandler\RequestHandlerInterface;
use K911\Swoole\Server\Runtime\BootableInterface;
use Swoole\Http\Request as SwooleRequest;
Expand All @@ -16,21 +16,21 @@

final class HttpKernelRequestHandler implements RequestHandlerInterface, BootableInterface
{
private $contextManager;
private $processorInjector;
private $kernel;
private $requestFactory;
private $responseProcessor;

public function __construct(
KernelInterface $kernel,
RequestFactoryInterface $requestFactory,
SwooleRequestResponseContextManager $contextManager,
ResponseProcessorInjectorInterface $processorInjector,
ResponseProcessorInterface $responseProcessor
) {
$this->kernel = $kernel;
$this->requestFactory = $requestFactory;
$this->responseProcessor = $responseProcessor;
$this->contextManager = $contextManager;
$this->processorInjector = $processorInjector;
}

/**
Expand All @@ -49,7 +49,7 @@ public function boot(array $runtimeConfiguration = []): void
public function handle(SwooleRequest $request, SwooleResponse $response): void
{
$httpFoundationRequest = $this->requestFactory->make($request);
$this->contextManager->attachRequestResponseAttributes($httpFoundationRequest, $request, $response);
$this->processorInjector->injectProcessor($httpFoundationRequest, $response);
$httpFoundationResponse = $this->kernel->handle($httpFoundationRequest);
$this->responseProcessor->process($httpFoundationResponse, $response);

Expand Down
31 changes: 31 additions & 0 deletions tests/Feature/SymfonyHttpRequestContainsRequestUriTest.php
Expand Up @@ -44,4 +44,35 @@ public function testWhetherCurrentSymfonyHttpRequestContainsRequestUri(): void

$serverRun->stop();
}

/*
* Test whether current Symfony's Request->getRequestUri() is working
* @see https://github.com/k911/swoole-bundle/issues/268
*/
public function testWhetherCurrentSymfonyHttpRequestContainsRequestUriInStreamedResponse(): void
{
$serverRun = $this->createConsoleProcess([
'swoole:server:run',
'--host=localhost',
'--port=9999',
]);

$serverRun->setTimeout(10);
$serverRun->start();

$this->runAsCoroutineAndWait(function (): void {
$client = HttpClient::fromDomain('localhost', 9999, false);
$this->assertTrue($client->connect());

$uri = '/http/request/streamed-uri?test1=1&test2=test3';
$response = $client->send('/http/request/streamed-uri?test1=1&test2=test3')['response'];

$this->assertSame(200, $response['statusCode']);
$this->assertSame([
'requestUri' => $uri,
], $response['body']);
});

$serverRun->stop();
}
}
Expand Up @@ -6,6 +6,7 @@

use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\Routing\Annotation\Route;

final class SymfonyHttpController
Expand All @@ -22,4 +23,21 @@ public function getRequestUri(Request $currentRequest): JsonResponse
{
return new JsonResponse(['requestUri' => $currentRequest->getRequestUri()], 200);
}

/**
* @Route(
* methods={"GET"},
* path="/http/request/streamed-uri"
* )
*/
public function getStreamedRequestUri(Request $currentRequest): StreamedResponse
{
$response = new StreamedResponse(function () use ($currentRequest): void {
$response = ['requestUri' => $currentRequest->getRequestUri()];
echo \json_encode($response);
});
$response->headers->set('Content-Type', 'application/json');

return $response;
}
}
22 changes: 19 additions & 3 deletions tests/Unit/Bridge/Symfony/HttpKernel/HttpKernelHttpDriverTest.php
Expand Up @@ -5,8 +5,8 @@
namespace K911\Swoole\Tests\Unit\Bridge\Symfony\HttpKernel;

use K911\Swoole\Bridge\Symfony\HttpFoundation\RequestFactoryInterface;
use K911\Swoole\Bridge\Symfony\HttpFoundation\ResponseProcessorInjectorInterface;
use K911\Swoole\Bridge\Symfony\HttpFoundation\ResponseProcessorInterface;
use K911\Swoole\Bridge\Symfony\HttpFoundation\SwooleRequestResponseContextManager;
use K911\Swoole\Bridge\Symfony\HttpKernel\HttpKernelRequestHandler;
use PHPUnit\Framework\TestCase;
use Prophecy\Prophecy\ObjectProphecy;
Expand Down Expand Up @@ -35,6 +35,11 @@ class HttpKernelHttpDriverTest extends TestCase
*/
private $requestFactoryProphecy;

/**
* @var ObjectProphecy|ResponseProcessorInjectorInterface
*/
private $responseProcessorInjectorProphecy;

/**
* @var KernelInterface|ObjectProphecy|TerminableInterface
*/
Expand All @@ -44,19 +49,22 @@ protected function setUp(): void
{
$this->kernelProphecy = $this->prophesize(KernelInterface::class);
$this->requestFactoryProphecy = $this->prophesize(RequestFactoryInterface::class);
$this->responseProcessorInjectorProphecy = $this->prophesize(ResponseProcessorInjectorInterface::class);
$this->responseProcessor = $this->prophesize(ResponseProcessorInterface::class);

/** @var KernelInterface $kernelMock */
$kernelMock = $this->kernelProphecy->reveal();
/** @var RequestFactoryInterface $requestFactoryMock */
$requestFactoryMock = $this->requestFactoryProphecy->reveal();
/** @var ResponseProcessorInjectorInterface $responseProcessorInjectorMock */
$responseProcessorInjectorMock = $this->responseProcessorInjectorProphecy->reveal();
/** @var ResponseProcessorInterface $responseProcessorMock */
$responseProcessorMock = $this->responseProcessor->reveal();

$this->httpDriver = new HttpKernelRequestHandler(
$kernelMock,
$requestFactoryMock,
new SwooleRequestResponseContextManager(),
$responseProcessorInjectorMock,
$responseProcessorMock
);
}
Expand All @@ -81,6 +89,9 @@ public function testHandleNonTerminable(): void

$this->requestFactoryProphecy->make($swooleRequest)->willReturn($httpFoundationRequest)->shouldBeCalled();
$this->kernelProphecy->handle($httpFoundationRequest)->willReturn($httpFoundationResponse)->shouldBeCalled();
$this->responseProcessorInjectorProphecy->injectProcessor($httpFoundationRequest, $swooleResponse)
->shouldBeCalled()
;
$this->responseProcessor->process($httpFoundationResponse, $swooleResponse)->shouldBeCalled();

$this->httpDriver->handle($swooleRequest, $swooleResponse);
Expand All @@ -101,6 +112,9 @@ public function testHandleTerminable(): void

$this->requestFactoryProphecy->make($swooleRequest)->willReturn($httpFoundationRequest)->shouldBeCalled();
$this->kernelProphecy->handle($httpFoundationRequest)->willReturn($httpFoundationResponse)->shouldBeCalled();
$this->responseProcessorInjectorProphecy->injectProcessor($httpFoundationRequest, $swooleResponse)
->shouldBeCalled()
;
$this->responseProcessor->process($httpFoundationResponse, $swooleResponse)->shouldBeCalled();
$this->kernelProphecy->terminate($httpFoundationRequest, $httpFoundationResponse)->shouldBeCalled();

Expand All @@ -115,13 +129,15 @@ private function setUpTerminableKernel(): void
$kernelMock = $this->kernelProphecy->reveal();
/** @var RequestFactoryInterface $requestFactoryMock */
$requestFactoryMock = $this->requestFactoryProphecy->reveal();
/** @var ResponseProcessorInjectorInterface $responseProcessorInjectorMock */
$responseProcessorInjectorMock = $this->responseProcessorInjectorProphecy->reveal();
/** @var ResponseProcessorInterface $responseProcessorMock */
$responseProcessorMock = $this->responseProcessor->reveal();

$this->httpDriver = new HttpKernelRequestHandler(
$kernelMock,
$requestFactoryMock,
new SwooleRequestResponseContextManager(),
$responseProcessorInjectorMock,
$responseProcessorMock
);
}
Expand Down

0 comments on commit ebe8077

Please sign in to comment.