From 1ede4e085eeaf9307223fc0c8eb48bf26713cc8b Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Mon, 21 Jan 2019 23:13:25 +0100 Subject: [PATCH 1/3] Add always seekable body plugin --- spec/Plugin/AlwaysSeekableBodyPluginSpec.php | 98 ++++++++++++++++++++ src/Plugin/AlwaysSeekableBodyPlugin.php | 65 +++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 spec/Plugin/AlwaysSeekableBodyPluginSpec.php create mode 100644 src/Plugin/AlwaysSeekableBodyPlugin.php diff --git a/spec/Plugin/AlwaysSeekableBodyPluginSpec.php b/spec/Plugin/AlwaysSeekableBodyPluginSpec.php new file mode 100644 index 0000000..cf1ac65 --- /dev/null +++ b/spec/Plugin/AlwaysSeekableBodyPluginSpec.php @@ -0,0 +1,98 @@ +shouldHaveType(AlwaysSeekableBodyPlugin::class); + } + + public function it_is_a_plugin() + { + $this->shouldImplement(Plugin::class); + } + + public function it_decorate_response_body_if_not_seekable(RequestInterface $request, ResponseInterface $response, StreamInterface $responseStream, StreamInterface $requestStream) + { + $next = function () use ($response) { + return new HttpFulfilledPromise($response->getWrappedObject()); + }; + + $request->getBody()->shouldBeCalled()->willReturn($requestStream); + $requestStream->isSeekable()->shouldBeCalled()->willReturn(true); + + $response->getBody()->shouldBeCalled()->willReturn($responseStream); + $responseStream->isSeekable()->shouldBeCalled()->willReturn(false); + $responseStream->getSize()->willReturn(null); + + $response->withBody(Argument::type(BufferedStream::class))->shouldBeCalled()->willReturn($response); + + $this->handleRequest($request, $next, function () {}); + } + + public function it_does_not_decorate_response_body_if_seekable(RequestInterface $request, ResponseInterface $response, StreamInterface $responseStream, StreamInterface $requestStream) + { + $next = function () use ($response) { + return new HttpFulfilledPromise($response->getWrappedObject()); + }; + + $request->getBody()->shouldBeCalled()->willReturn($requestStream); + $requestStream->isSeekable()->shouldBeCalled()->willReturn(true); + + $response->getBody()->shouldBeCalled()->willReturn($responseStream); + $responseStream->isSeekable()->shouldBeCalled()->willReturn(true); + $responseStream->getSize()->willReturn(null); + + $response->withBody(Argument::type(BufferedStream::class))->shouldNotBeCalled(); + + $this->handleRequest($request, $next, function () {}); + } + + public function it_decorate_request_body_if_not_seekable(RequestInterface $request, ResponseInterface $response, StreamInterface $responseStream, StreamInterface $requestStream) + { + $next = function () use ($response) { + return new HttpFulfilledPromise($response->getWrappedObject()); + }; + + $response->getBody()->willReturn($responseStream); + $responseStream->isSeekable()->willReturn(true); + + $request->getBody()->shouldBeCalled()->willReturn($requestStream); + $requestStream->isSeekable()->shouldBeCalled()->willReturn(false); + $requestStream->getSize()->willReturn(null); + + $request->withBody(Argument::type(BufferedStream::class))->shouldBeCalled(); + + $this->handleRequest($request, $next, function () {}); + } + + public function it_does_not_decorate_request_body_if_seekable(RequestInterface $request, ResponseInterface $response, StreamInterface $responseStream, StreamInterface $requestStream) + { + $next = function () use ($response) { + return new HttpFulfilledPromise($response->getWrappedObject()); + }; + + $response->getBody()->willReturn($responseStream); + $responseStream->isSeekable()->willReturn(true); + + $request->getBody()->shouldBeCalled()->willReturn($requestStream); + $requestStream->isSeekable()->shouldBeCalled()->willReturn(true); + $requestStream->getSize()->willReturn(null); + + $request->withBody(Argument::type(BufferedStream::class))->shouldNotBeCalled(); + + $this->handleRequest($request, $next, function () {}); + } +} diff --git a/src/Plugin/AlwaysSeekableBodyPlugin.php b/src/Plugin/AlwaysSeekableBodyPlugin.php new file mode 100644 index 0000000..2af3c61 --- /dev/null +++ b/src/Plugin/AlwaysSeekableBodyPlugin.php @@ -0,0 +1,65 @@ + + */ +final class AlwaysSeekableBodyPlugin implements Plugin +{ + private $useFileBuffer; + + private $memoryBufferSize; + + /** + * @param array $config { + * + * @var bool $use_file_buffer Whether this plugin should use a file as a buffer if the stream is too big, defaults to true + * @var int $memory_buffer_size Max memory size in bytes to use for the buffer before it use a file, defaults to 2097152 (2 mb) + * } + */ + public function __construct(array $config = []) + { + $resolver = new OptionsResolver(); + $resolver->setDefaults([ + 'use_file_buffer' => true, + 'memory_buffer_size' => 2097152 + ]); + $resolver->setAllowedTypes('use_file_buffer', 'bool'); + $resolver->setAllowedTypes('memory_buffer_size', 'int'); + + $options = $resolver->resolve($config); + + $this->useFileBuffer = $options['use_file_buffer']; + $this->memoryBufferSize = $options['memory_buffer_size']; + } + + /** + * {@inheritdoc} + */ + public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise + { + if (!$request->getBody()->isSeekable()) { + $request = $request->withBody(new BufferedStream($request->getBody(), $this->useFileBuffer, $this->memoryBufferSize)); + } + + return $next($request)->then(function (ResponseInterface $response) { + if ($response->getBody()->isSeekable()) { + return $response; + } + + return $response->withBody(new BufferedStream($response->getBody(), $this->useFileBuffer, $this->memoryBufferSize)); + }); + } +} From b013db50d654bab0208e9ba4a134b0670cfd0250 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Mon, 21 Jan 2019 23:17:36 +0100 Subject: [PATCH 2/3] Fix cs --- src/Plugin/AlwaysSeekableBodyPlugin.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Plugin/AlwaysSeekableBodyPlugin.php b/src/Plugin/AlwaysSeekableBodyPlugin.php index 2af3c61..57d9d4c 100644 --- a/src/Plugin/AlwaysSeekableBodyPlugin.php +++ b/src/Plugin/AlwaysSeekableBodyPlugin.php @@ -12,7 +12,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver; /** - * Decorate the body of the request and the response if it's not seekable by using Http\Message\Stream\BufferedStream + * Decorate the body of the request and the response if it's not seekable by using Http\Message\Stream\BufferedStream. * * @author Joel Wurtz */ @@ -34,7 +34,7 @@ public function __construct(array $config = []) $resolver = new OptionsResolver(); $resolver->setDefaults([ 'use_file_buffer' => true, - 'memory_buffer_size' => 2097152 + 'memory_buffer_size' => 2097152, ]); $resolver->setAllowedTypes('use_file_buffer', 'bool'); $resolver->setAllowedTypes('memory_buffer_size', 'int'); From a3c421cb79fb15da67c09241298fa4cfaec14476 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Tue, 29 Jan 2019 15:59:34 +0100 Subject: [PATCH 3/3] Split in two separate plugin (to handle request / response at different time) --- spec/Plugin/AlwaysSeekableBodyPluginSpec.php | 98 ------------------- spec/Plugin/RequestSeekableBodyPluginSpec.php | 46 +++++++++ .../Plugin/ResponseSeekableBodyPluginSpec.php | 56 +++++++++++ src/Plugin/AlwaysSeekableBodyPlugin.php | 65 ------------ src/Plugin/RequestSeekableBodyPlugin.php | 29 ++++++ src/Plugin/ResponseSeekableBodyPlugin.php | 32 ++++++ src/Plugin/SeekableBodyPlugin.php | 41 ++++++++ 7 files changed, 204 insertions(+), 163 deletions(-) delete mode 100644 spec/Plugin/AlwaysSeekableBodyPluginSpec.php create mode 100644 spec/Plugin/RequestSeekableBodyPluginSpec.php create mode 100644 spec/Plugin/ResponseSeekableBodyPluginSpec.php delete mode 100644 src/Plugin/AlwaysSeekableBodyPlugin.php create mode 100644 src/Plugin/RequestSeekableBodyPlugin.php create mode 100644 src/Plugin/ResponseSeekableBodyPlugin.php create mode 100644 src/Plugin/SeekableBodyPlugin.php diff --git a/spec/Plugin/AlwaysSeekableBodyPluginSpec.php b/spec/Plugin/AlwaysSeekableBodyPluginSpec.php deleted file mode 100644 index cf1ac65..0000000 --- a/spec/Plugin/AlwaysSeekableBodyPluginSpec.php +++ /dev/null @@ -1,98 +0,0 @@ -shouldHaveType(AlwaysSeekableBodyPlugin::class); - } - - public function it_is_a_plugin() - { - $this->shouldImplement(Plugin::class); - } - - public function it_decorate_response_body_if_not_seekable(RequestInterface $request, ResponseInterface $response, StreamInterface $responseStream, StreamInterface $requestStream) - { - $next = function () use ($response) { - return new HttpFulfilledPromise($response->getWrappedObject()); - }; - - $request->getBody()->shouldBeCalled()->willReturn($requestStream); - $requestStream->isSeekable()->shouldBeCalled()->willReturn(true); - - $response->getBody()->shouldBeCalled()->willReturn($responseStream); - $responseStream->isSeekable()->shouldBeCalled()->willReturn(false); - $responseStream->getSize()->willReturn(null); - - $response->withBody(Argument::type(BufferedStream::class))->shouldBeCalled()->willReturn($response); - - $this->handleRequest($request, $next, function () {}); - } - - public function it_does_not_decorate_response_body_if_seekable(RequestInterface $request, ResponseInterface $response, StreamInterface $responseStream, StreamInterface $requestStream) - { - $next = function () use ($response) { - return new HttpFulfilledPromise($response->getWrappedObject()); - }; - - $request->getBody()->shouldBeCalled()->willReturn($requestStream); - $requestStream->isSeekable()->shouldBeCalled()->willReturn(true); - - $response->getBody()->shouldBeCalled()->willReturn($responseStream); - $responseStream->isSeekable()->shouldBeCalled()->willReturn(true); - $responseStream->getSize()->willReturn(null); - - $response->withBody(Argument::type(BufferedStream::class))->shouldNotBeCalled(); - - $this->handleRequest($request, $next, function () {}); - } - - public function it_decorate_request_body_if_not_seekable(RequestInterface $request, ResponseInterface $response, StreamInterface $responseStream, StreamInterface $requestStream) - { - $next = function () use ($response) { - return new HttpFulfilledPromise($response->getWrappedObject()); - }; - - $response->getBody()->willReturn($responseStream); - $responseStream->isSeekable()->willReturn(true); - - $request->getBody()->shouldBeCalled()->willReturn($requestStream); - $requestStream->isSeekable()->shouldBeCalled()->willReturn(false); - $requestStream->getSize()->willReturn(null); - - $request->withBody(Argument::type(BufferedStream::class))->shouldBeCalled(); - - $this->handleRequest($request, $next, function () {}); - } - - public function it_does_not_decorate_request_body_if_seekable(RequestInterface $request, ResponseInterface $response, StreamInterface $responseStream, StreamInterface $requestStream) - { - $next = function () use ($response) { - return new HttpFulfilledPromise($response->getWrappedObject()); - }; - - $response->getBody()->willReturn($responseStream); - $responseStream->isSeekable()->willReturn(true); - - $request->getBody()->shouldBeCalled()->willReturn($requestStream); - $requestStream->isSeekable()->shouldBeCalled()->willReturn(true); - $requestStream->getSize()->willReturn(null); - - $request->withBody(Argument::type(BufferedStream::class))->shouldNotBeCalled(); - - $this->handleRequest($request, $next, function () {}); - } -} diff --git a/spec/Plugin/RequestSeekableBodyPluginSpec.php b/spec/Plugin/RequestSeekableBodyPluginSpec.php new file mode 100644 index 0000000..fbb5530 --- /dev/null +++ b/spec/Plugin/RequestSeekableBodyPluginSpec.php @@ -0,0 +1,46 @@ +shouldHaveType(RequestSeekableBodyPlugin::class); + } + + public function it_is_a_plugin() + { + $this->shouldImplement(Plugin::class); + } + + public function it_decorate_request_body_if_not_seekable(RequestInterface $request, StreamInterface $requestStream) + { + $request->getBody()->shouldBeCalled()->willReturn($requestStream); + $requestStream->isSeekable()->shouldBeCalled()->willReturn(false); + $requestStream->getSize()->willReturn(null); + + $request->withBody(Argument::type(BufferedStream::class))->shouldBeCalled()->willReturn($request); + + $this->handleRequest($request, PluginStub::next(), function () {}); + } + + public function it_does_not_decorate_request_body_if_seekable(RequestInterface $request, StreamInterface $requestStream) + { + $request->getBody()->shouldBeCalled()->willReturn($requestStream); + $requestStream->isSeekable()->shouldBeCalled()->willReturn(true); + $requestStream->getSize()->willReturn(null); + + $request->withBody(Argument::type(BufferedStream::class))->shouldNotBeCalled(); + + $this->handleRequest($request, PluginStub::next(), function () {}); + } +} diff --git a/spec/Plugin/ResponseSeekableBodyPluginSpec.php b/spec/Plugin/ResponseSeekableBodyPluginSpec.php new file mode 100644 index 0000000..4f75027 --- /dev/null +++ b/spec/Plugin/ResponseSeekableBodyPluginSpec.php @@ -0,0 +1,56 @@ +shouldHaveType(ResponseSeekableBodyPlugin::class); + } + + public function it_is_a_plugin() + { + $this->shouldImplement(Plugin::class); + } + + public function it_decorate_response_body_if_not_seekable(ResponseInterface $response, StreamInterface $responseStream) + { + $next = function () use ($response) { + return new HttpFulfilledPromise($response->getWrappedObject()); + }; + + $response->getBody()->shouldBeCalled()->willReturn($responseStream); + $responseStream->isSeekable()->shouldBeCalled()->willReturn(false); + $responseStream->getSize()->willReturn(null); + + $response->withBody(Argument::type(BufferedStream::class))->shouldBeCalled()->willReturn($response); + + $this->handleRequest(new Request('GET', '/'), $next, function () {}); + } + + public function it_does_not_decorate_response_body_if_seekable(ResponseInterface $response, StreamInterface $responseStream) + { + $next = function () use ($response) { + return new HttpFulfilledPromise($response->getWrappedObject()); + }; + + $response->getBody()->shouldBeCalled()->willReturn($responseStream); + $responseStream->isSeekable()->shouldBeCalled()->willReturn(true); + $responseStream->getSize()->willReturn(null); + + $response->withBody(Argument::type(BufferedStream::class))->shouldNotBeCalled(); + + $this->handleRequest(new Request('GET', '/'), $next, function () {}); + } +} diff --git a/src/Plugin/AlwaysSeekableBodyPlugin.php b/src/Plugin/AlwaysSeekableBodyPlugin.php deleted file mode 100644 index 57d9d4c..0000000 --- a/src/Plugin/AlwaysSeekableBodyPlugin.php +++ /dev/null @@ -1,65 +0,0 @@ - - */ -final class AlwaysSeekableBodyPlugin implements Plugin -{ - private $useFileBuffer; - - private $memoryBufferSize; - - /** - * @param array $config { - * - * @var bool $use_file_buffer Whether this plugin should use a file as a buffer if the stream is too big, defaults to true - * @var int $memory_buffer_size Max memory size in bytes to use for the buffer before it use a file, defaults to 2097152 (2 mb) - * } - */ - public function __construct(array $config = []) - { - $resolver = new OptionsResolver(); - $resolver->setDefaults([ - 'use_file_buffer' => true, - 'memory_buffer_size' => 2097152, - ]); - $resolver->setAllowedTypes('use_file_buffer', 'bool'); - $resolver->setAllowedTypes('memory_buffer_size', 'int'); - - $options = $resolver->resolve($config); - - $this->useFileBuffer = $options['use_file_buffer']; - $this->memoryBufferSize = $options['memory_buffer_size']; - } - - /** - * {@inheritdoc} - */ - public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise - { - if (!$request->getBody()->isSeekable()) { - $request = $request->withBody(new BufferedStream($request->getBody(), $this->useFileBuffer, $this->memoryBufferSize)); - } - - return $next($request)->then(function (ResponseInterface $response) { - if ($response->getBody()->isSeekable()) { - return $response; - } - - return $response->withBody(new BufferedStream($response->getBody(), $this->useFileBuffer, $this->memoryBufferSize)); - }); - } -} diff --git a/src/Plugin/RequestSeekableBodyPlugin.php b/src/Plugin/RequestSeekableBodyPlugin.php new file mode 100644 index 0000000..1b6c528 --- /dev/null +++ b/src/Plugin/RequestSeekableBodyPlugin.php @@ -0,0 +1,29 @@ + + */ +final class RequestSeekableBodyPlugin extends SeekableBodyPlugin +{ + /** + * {@inheritdoc} + */ + public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise + { + if (!$request->getBody()->isSeekable()) { + $request = $request->withBody(new BufferedStream($request->getBody(), $this->useFileBuffer, $this->memoryBufferSize)); + } + + return $next($request); + } +} diff --git a/src/Plugin/ResponseSeekableBodyPlugin.php b/src/Plugin/ResponseSeekableBodyPlugin.php new file mode 100644 index 0000000..6f941b6 --- /dev/null +++ b/src/Plugin/ResponseSeekableBodyPlugin.php @@ -0,0 +1,32 @@ + + */ +final class ResponseSeekableBodyPlugin extends SeekableBodyPlugin +{ + /** + * {@inheritdoc} + */ + public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise + { + return $next($request)->then(function (ResponseInterface $response) { + if ($response->getBody()->isSeekable()) { + return $response; + } + + return $response->withBody(new BufferedStream($response->getBody(), $this->useFileBuffer, $this->memoryBufferSize)); + }); + } +} diff --git a/src/Plugin/SeekableBodyPlugin.php b/src/Plugin/SeekableBodyPlugin.php new file mode 100644 index 0000000..3b37b1c --- /dev/null +++ b/src/Plugin/SeekableBodyPlugin.php @@ -0,0 +1,41 @@ +setDefaults([ + 'use_file_buffer' => true, + 'memory_buffer_size' => 2097152, + ]); + $resolver->setAllowedTypes('use_file_buffer', 'bool'); + $resolver->setAllowedTypes('memory_buffer_size', 'int'); + + $options = $resolver->resolve($config); + + $this->useFileBuffer = $options['use_file_buffer']; + $this->memoryBufferSize = $options['memory_buffer_size']; + } +}