diff --git a/CHANGELOG.md b/CHANGELOG.md index 0365ac7..2a39ccf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,12 @@ ## 1.3.0 - Unreleased +### Added - Add HttpClientPool client to leverage load balancing and fallback mechanism [see the documentation](http://docs.php-http.org/en/latest/components/client-common.html) for more details +### Changed +- RedirectPlugin: use the full URL instead of the URI to properly keep track of redirects + ## 1.2.1 - 2016-07-26 ### Changed diff --git a/spec/Plugin/RedirectPluginSpec.php b/spec/Plugin/RedirectPluginSpec.php index 4310da2..7f2df64 100644 --- a/spec/Plugin/RedirectPluginSpec.php +++ b/spec/Plugin/RedirectPluginSpec.php @@ -36,17 +36,19 @@ function it_redirects_on_302( $responseRedirect->hasHeader('Location')->willReturn(true); $responseRedirect->getHeaderLine('Location')->willReturn('/redirect'); - $request->getRequestTarget()->willReturn('/original'); $request->getUri()->willReturn($uri); $request->withUri($uriRedirect)->willReturn($modifiedRequest); + $uri->__toString()->willReturn('/original'); $uri->withPath('/redirect')->willReturn($uriRedirect); $uriRedirect->withFragment('')->willReturn($uriRedirect); $uriRedirect->withQuery('')->willReturn($uriRedirect); + $uriRedirect->__toString()->willReturn('/redirect'); - $modifiedRequest->getRequestTarget()->willReturn('/redirect'); + $modifiedRequest->getUri()->willReturn($uriRedirect); $modifiedRequest->getMethod()->willReturn('GET'); + $next = function (RequestInterface $receivedRequest) use($request, $responseRedirect) { if (Argument::is($request->getWrappedObject())->scoreArgument($receivedRequest)) { return new FulfilledPromise($responseRedirect->getWrappedObject()); @@ -67,7 +69,7 @@ function it_redirects_on_302( $finalPromise->wait()->shouldReturn($finalResponse); } - function it_use_storage_on_301(UriInterface $uriRedirect, RequestInterface $request, RequestInterface $modifiedRequest) + function it_use_storage_on_301(UriInterface $uri, UriInterface $uriRedirect, RequestInterface $request, RequestInterface $modifiedRequest) { $this->beAnInstanceOf('spec\Http\Client\Common\Plugin\RedirectPluginStub'); $this->beConstructedWith($uriRedirect, '/original', '301'); @@ -76,12 +78,15 @@ function it_use_storage_on_301(UriInterface $uriRedirect, RequestInterface $requ throw new \Exception('Must not be called'); }; - $request->getRequestTarget()->willReturn('/original'); + $request->getUri()->willReturn($uri); + $uri->__toString()->willReturn('/original'); $request->withUri($uriRedirect)->willReturn($modifiedRequest); - $modifiedRequest->getRequestTarget()->willReturn('/redirect'); + $modifiedRequest->getUri()->willReturn($uriRedirect); $modifiedRequest->getMethod()->willReturn('GET'); + $uriRedirect->__toString()->willReturn('/redirect'); + $this->handleRequest($request, $next, function () {}); } @@ -98,8 +103,8 @@ function it_stores_a_301( $this->beAnInstanceOf('spec\Http\Client\Common\Plugin\RedirectPluginStub'); $this->beConstructedWith($uriRedirect, '', '301'); - $request->getRequestTarget()->willReturn('/301-url'); $request->getUri()->willReturn($uri); + $uri->__toString()->willReturn('/301-url'); $responseRedirect->getStatusCode()->willReturn('301'); $responseRedirect->hasHeader('Location')->willReturn(true); @@ -111,9 +116,11 @@ function it_stores_a_301( $request->withUri($uriRedirect)->willReturn($modifiedRequest); - $modifiedRequest->getRequestTarget()->willReturn('/redirect'); + $modifiedRequest->getUri()->willReturn($uriRedirect); $modifiedRequest->getMethod()->willReturn('GET'); + $uriRedirect->__toString()->willReturn('/redirect'); + $next = function (RequestInterface $receivedRequest) use($request, $responseRedirect) { if (Argument::is($request->getWrappedObject())->scoreArgument($receivedRequest)) { return new FulfilledPromise($responseRedirect->getWrappedObject()); @@ -142,7 +149,8 @@ function it_replace_full_url( ResponseInterface $finalResponse, Promise $promise ) { - $request->getRequestTarget()->willReturn('/original'); + $request->getUri()->willReturn($uri); + $uri->__toString()->willReturn('/original'); $responseRedirect->getStatusCode()->willReturn('302'); $responseRedirect->hasHeader('Location')->willReturn(true); @@ -158,9 +166,11 @@ function it_replace_full_url( $request->withUri($uriRedirect)->willReturn($modifiedRequest); - $modifiedRequest->getRequestTarget()->willReturn('/redirect'); + $modifiedRequest->getUri()->willReturn($uriRedirect); $modifiedRequest->getMethod()->willReturn('GET'); + $uriRedirect->__toString()->willReturn('/redirect'); + $next = function (RequestInterface $receivedRequest) use($request, $responseRedirect) { if (Argument::is($request->getWrappedObject())->scoreArgument($receivedRequest)) { return new FulfilledPromise($responseRedirect->getWrappedObject()); @@ -179,7 +189,7 @@ function it_replace_full_url( $this->handleRequest($request, $next, $first); } - function it_throws_http_exception_on_no_location(RequestInterface $request, ResponseInterface $responseRedirect) + function it_throws_http_exception_on_no_location(RequestInterface $request, UriInterface $uri, ResponseInterface $responseRedirect) { $next = function (RequestInterface $receivedRequest) use($request, $responseRedirect) { if (Argument::is($request->getWrappedObject())->scoreArgument($receivedRequest)) { @@ -187,7 +197,8 @@ function it_throws_http_exception_on_no_location(RequestInterface $request, Resp } }; - $request->getRequestTarget()->willReturn('/original'); + $request->getUri()->willReturn($uri); + $uri->__toString()->willReturn('/original'); $responseRedirect->getStatusCode()->willReturn('302'); $responseRedirect->hasHeader('Location')->willReturn(false); @@ -196,7 +207,7 @@ function it_throws_http_exception_on_no_location(RequestInterface $request, Resp $promise->shouldThrow('Http\Client\Exception\HttpException')->duringWait(); } - function it_throws_http_exception_on_invalid_location(RequestInterface $request, ResponseInterface $responseRedirect) + function it_throws_http_exception_on_invalid_location(RequestInterface $request, UriInterface $uri, ResponseInterface $responseRedirect) { $next = function (RequestInterface $receivedRequest) use($request, $responseRedirect) { if (Argument::is($request->getWrappedObject())->scoreArgument($receivedRequest)) { @@ -204,7 +215,8 @@ function it_throws_http_exception_on_invalid_location(RequestInterface $request, } }; - $request->getRequestTarget()->willReturn('/original'); + $request->getUri()->willReturn($uri); + $uri->__toString()->willReturn('/original'); $responseRedirect->getHeaderLine('Location')->willReturn('scheme:///invalid'); $responseRedirect->getStatusCode()->willReturn('302'); @@ -256,7 +268,8 @@ function it_switch_method_for_302( ResponseInterface $finalResponse, Promise $promise ) { - $request->getRequestTarget()->willReturn('/original'); + $request->getUri()->willReturn($uri); + $uri->__toString()->willReturn('/original'); $responseRedirect->getStatusCode()->willReturn('302'); $responseRedirect->hasHeader('Location')->willReturn(true); @@ -270,7 +283,8 @@ function it_switch_method_for_302( $request->withUri($uriRedirect)->willReturn($modifiedRequest); $modifiedRequest->getUri()->willReturn($uriRedirect); - $modifiedRequest->getRequestTarget()->willReturn('/redirect'); + $modifiedRequest->getUri()->willReturn($uriRedirect); + $uriRedirect->__toString()->willReturn('/redirect'); $modifiedRequest->getMethod()->willReturn('POST'); $modifiedRequest->withMethod('GET')->willReturn($modifiedRequest); @@ -303,7 +317,8 @@ function it_clears_headers( ) { $this->beConstructedWith(['preserve_header' => ['Accept']]); - $request->getRequestTarget()->willReturn('/original'); + $request->getUri()->willReturn($uri); + $uri->__toString()->willReturn('/original'); $responseRedirect->getStatusCode()->willReturn('302'); $responseRedirect->hasHeader('Location')->willReturn(true); @@ -316,7 +331,8 @@ function it_clears_headers( $request->withUri($uriRedirect)->willReturn($modifiedRequest); - $modifiedRequest->getRequestTarget()->willReturn('/redirect'); + $modifiedRequest->getUri()->willReturn($uriRedirect); + $uriRedirect->__toString()->willReturn('/redirect'); $modifiedRequest->getMethod()->willReturn('GET'); $modifiedRequest->getHeaders()->willReturn(['Accept' => 'value', 'Cookie' => 'value']); $modifiedRequest->withoutHeader('Cookie')->willReturn($modifiedRequest); @@ -347,8 +363,8 @@ function it_throws_circular_redirection_exception(UriInterface $uri, UriInterfac $this->beAnInstanceOf('spec\Http\Client\Common\Plugin\RedirectPluginStubCircular'); $this->beConstructedWith(spl_object_hash((object)$first)); - $request->getRequestTarget()->willReturn('/original'); $request->getUri()->willReturn($uri); + $uri->__toString()->willReturn('/original'); $responseRedirect->getStatusCode()->willReturn('302'); $responseRedirect->hasHeader('Location')->willReturn(true); @@ -360,7 +376,7 @@ function it_throws_circular_redirection_exception(UriInterface $uri, UriInterfac $request->withUri($uriRedirect)->willReturn($modifiedRequest); $modifiedRequest->getUri()->willReturn($uriRedirect); - $modifiedRequest->getRequestTarget()->willReturn('/redirect'); + $uriRedirect->__toString()->willReturn('/redirect'); $modifiedRequest->getMethod()->willReturn('GET'); $next = function (RequestInterface $receivedRequest) use($request, $responseRedirect) { @@ -373,6 +389,53 @@ function it_throws_circular_redirection_exception(UriInterface $uri, UriInterfac $promise->shouldReturnAnInstanceOf('Http\Promise\RejectedPromise'); $promise->shouldThrow('Http\Client\Common\Exception\CircularRedirectionException')->duringWait(); } + + function it_redirects_http_to_https( + UriInterface $uri, + UriInterface $uriRedirect, + RequestInterface $request, + ResponseInterface $responseRedirect, + RequestInterface $modifiedRequest, + ResponseInterface $finalResponse, + Promise $promise + ) { + $responseRedirect->getStatusCode()->willReturn('302'); + $responseRedirect->hasHeader('Location')->willReturn(true); + $responseRedirect->getHeaderLine('Location')->willReturn('https://my-site.com/original'); + + $request->getUri()->willReturn($uri); + $request->withUri($uriRedirect)->willReturn($modifiedRequest); + $uri->__toString()->willReturn('http://my-site.com/original'); + + $uri->withScheme('https')->willReturn($uriRedirect); + $uriRedirect->withHost('my-site.com')->willReturn($uriRedirect); + $uriRedirect->withPath('/original')->willReturn($uriRedirect); + $uriRedirect->withFragment('')->willReturn($uriRedirect); + $uriRedirect->withQuery('')->willReturn($uriRedirect); + $uriRedirect->__toString()->willReturn('https://my-site.com/original'); + + $modifiedRequest->getUri()->willReturn($uriRedirect); + $modifiedRequest->getMethod()->willReturn('GET'); + + $next = function (RequestInterface $receivedRequest) use($request, $responseRedirect) { + if (Argument::is($request->getWrappedObject())->scoreArgument($receivedRequest)) { + return new FulfilledPromise($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); + + $finalPromise = $this->handleRequest($request, $next, $first); + $finalPromise->shouldReturnAnInstanceOf('Http\Promise\FulfilledPromise'); + $finalPromise->wait()->shouldReturn($finalResponse); + } } class RedirectPluginStub extends RedirectPlugin diff --git a/src/Plugin/RedirectPlugin.php b/src/Plugin/RedirectPlugin.php index d85d979..d2f442e 100644 --- a/src/Plugin/RedirectPlugin.php +++ b/src/Plugin/RedirectPlugin.php @@ -134,9 +134,9 @@ public function __construct(array $config = []) public function handleRequest(RequestInterface $request, callable $next, callable $first) { // Check in storage - if (array_key_exists($request->getRequestTarget(), $this->redirectStorage)) { - $uri = $this->redirectStorage[$request->getRequestTarget()]['uri']; - $statusCode = $this->redirectStorage[$request->getRequestTarget()]['status']; + if (array_key_exists((string) $request->getUri(), $this->redirectStorage)) { + $uri = $this->redirectStorage[(string) $request->getUri()]['uri']; + $statusCode = $this->redirectStorage[(string) $request->getUri()]['status']; $redirectRequest = $this->buildRedirectRequest($request, $uri, $statusCode); return $first($redirectRequest); @@ -157,14 +157,14 @@ public function handleRequest(RequestInterface $request, callable $next, callabl $this->circularDetection[$chainIdentifier] = []; } - $this->circularDetection[$chainIdentifier][] = $request->getRequestTarget(); + $this->circularDetection[$chainIdentifier][] = (string) $request->getUri(); - if (in_array($redirectRequest->getRequestTarget(), $this->circularDetection[$chainIdentifier])) { + if (in_array((string) $redirectRequest->getUri(), $this->circularDetection[$chainIdentifier])) { throw new CircularRedirectionException('Circular redirection detected', $request, $response); } if ($this->redirectCodes[$statusCode]['permanent']) { - $this->redirectStorage[$request->getRequestTarget()] = [ + $this->redirectStorage[(string) $request->getUri()] = [ 'uri' => $uri, 'status' => $statusCode, ];