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/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']; + } +}