diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a7b201..dccc2f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## 2.4.0 (unreleased) + +### Added + +- `strict` option to `RedirectPlugin` to allow preserving the request method on redirections with status 300, 301 and 302. + ## 2.3.0 - 2020-07-21 ### Fixed @@ -112,7 +118,7 @@ ## 1.7.0 - 2017-11-30 -### Added +### Added - Symfony 4 support @@ -132,12 +138,12 @@ ### Changed -- The `RetryPlugin` does now wait between retries. To disable/change this feature you must write something like: - +- The `RetryPlugin` does now wait between retries. To disable/change this feature you must write something like: + ```php -$plugin = new RetryPlugin(['delay' => function(RequestInterface $request, Exception $e, $retries) { - return 0; -}); +$plugin = new RetryPlugin(['delay' => function(RequestInterface $request, Exception $e, $retries) { + return 0; +}); ``` ### Deprecated diff --git a/spec/Plugin/RedirectPluginSpec.php b/spec/Plugin/RedirectPluginSpec.php index 3342f60..475d458 100644 --- a/spec/Plugin/RedirectPluginSpec.php +++ b/spec/Plugin/RedirectPluginSpec.php @@ -286,11 +286,56 @@ public function it_switch_method_for_302( $request->withUri($uriRedirect)->willReturn($modifiedRequest); $modifiedRequest->getUri()->willReturn($uriRedirect); + $uriRedirect->__toString()->willReturn('/redirect'); + $modifiedRequest->getMethod()->willReturn('POST'); + $modifiedRequest->withMethod('GET')->shouldBeCalled()->willReturn($modifiedRequest); + + $next = function (RequestInterface $receivedRequest) use ($request, $responseRedirect) { + if (Argument::is($request->getWrappedObject())->scoreArgument($receivedRequest)) { + return new HttpFulfilledPromise($responseRedirect->getWrappedObject()); + } + }; + + $first = function (RequestInterface $receivedRequest) use ($modifiedRequest, $promise) { + if (Argument::is($modifiedRequest->getWrappedObject())->scoreArgument($receivedRequest)) { + return $promise->getWrappedObject(); + } + }; + + $promise->getState()->willReturn(Promise::FULFILLED); + $promise->wait()->shouldBeCalled()->willReturn($finalResponse); + + $this->handleRequest($request, $next, $first); + } + + public function it_does_not_switch_method_for_302_with_strict_option( + UriInterface $uri, + UriInterface $uriRedirect, + RequestInterface $request, + ResponseInterface $responseRedirect, + RequestInterface $modifiedRequest, + ResponseInterface $finalResponse, + Promise $promise + ) { + $this->beConstructedWith(['strict' => true]); + + $request->getUri()->willReturn($uri); + $uri->__toString()->willReturn('/original'); + $responseRedirect->getStatusCode()->willReturn(302); + $responseRedirect->hasHeader('Location')->willReturn(true); + $responseRedirect->getHeaderLine('Location')->willReturn('/redirect'); + + $request->getUri()->willReturn($uri); + $uri->withPath('/redirect')->willReturn($uriRedirect); + $uriRedirect->withFragment('')->willReturn($uriRedirect); + $uriRedirect->withQuery('')->willReturn($uriRedirect); + + $request->withUri($uriRedirect)->willReturn($modifiedRequest); $modifiedRequest->getUri()->willReturn($uriRedirect); $uriRedirect->__toString()->willReturn('/redirect'); $modifiedRequest->getMethod()->willReturn('POST'); - $modifiedRequest->withMethod('GET')->willReturn($modifiedRequest); + $modifiedRequest->withMethod('GET')->shouldNotBeCalled(); $next = function (RequestInterface $receivedRequest) use ($request, $responseRedirect) { if (Argument::is($request->getWrappedObject())->scoreArgument($receivedRequest)) { diff --git a/src/Plugin/RedirectPlugin.php b/src/Plugin/RedirectPlugin.php index 061f35b..488b3aa 100644 --- a/src/Plugin/RedirectPlugin.php +++ b/src/Plugin/RedirectPlugin.php @@ -105,7 +105,8 @@ final class RedirectPlugin implements Plugin * @param array $config { * * @var bool|string[] $preserve_header True keeps all headers, false remove all of them, an array is interpreted as a list of header names to keep - * @var bool $use_default_for_multiple Whether the location header must be directly used for a multiple redirection status code (300). + * @var bool $use_default_for_multiple Whether the location header must be directly used for a multiple redirection status code (300) + * @var bool $strict When true, redirect codes 300, 301, 302 will not modify request method and body. * } */ public function __construct(array $config = []) @@ -114,9 +115,11 @@ public function __construct(array $config = []) $resolver->setDefaults([ 'preserve_header' => true, 'use_default_for_multiple' => true, + 'strict' => false, ]); $resolver->setAllowedTypes('preserve_header', ['bool', 'array']); $resolver->setAllowedTypes('use_default_for_multiple', 'bool'); + $resolver->setAllowedTypes('strict', 'bool'); $resolver->setNormalizer('preserve_header', function (OptionsResolver $resolver, $value) { if (is_bool($value) && false === $value) { return []; @@ -128,6 +131,12 @@ public function __construct(array $config = []) $this->preserveHeader = $options['preserve_header']; $this->useDefaultForMultiple = $options['use_default_for_multiple']; + + if ($options['strict']) { + $this->redirectCodes[300]['switch'] = false; + $this->redirectCodes[301]['switch'] = false; + $this->redirectCodes[302]['switch'] = false; + } } /**