diff --git a/composer.json b/composer.json index 321a2c26..a55648d7 100644 --- a/composer.json +++ b/composer.json @@ -7,10 +7,13 @@ }, "require": { "php": "^5.6 || ^7.0 || ^8.0", - "guzzlehttp/guzzle": "^6.0 || ^7.0", + "psr/http-client-implementation": "^1.0", + "psr/http-factory-implementation": "^1.0", "paragonie/random_compat": "^1 || ^2 || ^9.99" }, "require-dev": { + "guzzlehttp/guzzle": "^7.3", + "guzzlehttp/psr7": "^2.0@RC", "mockery/mockery": "^1.3", "php-parallel-lint/php-parallel-lint": "^1.2", "phpunit/phpunit": "^5.7 || ^6.0 || ^9.3", diff --git a/src/Provider/AbstractProvider.php b/src/Provider/AbstractProvider.php index d1679998..1b4138dd 100644 --- a/src/Provider/AbstractProvider.php +++ b/src/Provider/AbstractProvider.php @@ -14,9 +14,6 @@ namespace League\OAuth2\Client\Provider; -use GuzzleHttp\Client as HttpClient; -use GuzzleHttp\ClientInterface as HttpClientInterface; -use GuzzleHttp\Exception\BadResponseException; use League\OAuth2\Client\Grant\AbstractGrant; use League\OAuth2\Client\Grant\GrantFactory; use League\OAuth2\Client\OptionProvider\OptionProviderInterface; @@ -28,6 +25,8 @@ use League\OAuth2\Client\Tool\GuardedPropertyTrait; use League\OAuth2\Client\Tool\QueryBuilderTrait; use League\OAuth2\Client\Tool\RequestFactory; +use Psr\Http\Client\ClientExceptionInterface; +use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use UnexpectedValueException; @@ -89,7 +88,7 @@ abstract class AbstractProvider protected $requestFactory; /** - * @var HttpClientInterface + * @var ClientInterface */ protected $httpClient; @@ -126,11 +125,7 @@ public function __construct(array $options = [], array $collaborators = []) $this->setRequestFactory($collaborators['requestFactory']); if (empty($collaborators['httpClient'])) { - $client_options = $this->getAllowedClientOptions($options); - - $collaborators['httpClient'] = new HttpClient( - array_intersect_key($options, array_flip($client_options)) - ); + throw new \LogicException('Must specify client'); } $this->setHttpClient($collaborators['httpClient']); @@ -209,10 +204,10 @@ public function getRequestFactory() /** * Sets the HTTP client instance. * - * @param HttpClientInterface $client + * @param ClientInterface $client * @return self */ - public function setHttpClient(HttpClientInterface $client) + public function setHttpClient(ClientInterface $client) { $this->httpClient = $client; @@ -222,7 +217,7 @@ public function setHttpClient(HttpClientInterface $client) /** * Returns the HTTP client instance. * - * @return HttpClientInterface + * @return ClientInterface */ public function getHttpClient() { @@ -600,12 +595,14 @@ protected function createRequest($method, $url, $token, array $options) * WARNING: This method does not attempt to catch exceptions caused by HTTP * errors! It is recommended to wrap this method in a try/catch block. * - * @param RequestInterface $request + * @param RequestInterface $request + * * @return ResponseInterface + * @throws \Psr\Http\Client\ClientExceptionInterface */ public function getResponse(RequestInterface $request) { - return $this->getHttpClient()->send($request); + return $this->getHttpClient()->sendRequest($request); } /** @@ -619,8 +616,8 @@ public function getParsedResponse(RequestInterface $request) { try { $response = $this->getResponse($request); - } catch (BadResponseException $e) { - $response = $e->getResponse(); + } catch (ClientExceptionInterface $e) { + throw $e; } $parsed = $this->parseResponse($response); diff --git a/src/Tool/ProviderRedirectTrait.php b/src/Tool/ProviderRedirectTrait.php deleted file mode 100644 index f81b511f..00000000 --- a/src/Tool/ProviderRedirectTrait.php +++ /dev/null @@ -1,122 +0,0 @@ -redirectLimit) { - $attempts++; - $response = $this->getHttpClient()->send($request, [ - 'allow_redirects' => false - ]); - - if ($this->isRedirect($response)) { - $redirectUrl = new Uri($response->getHeader('Location')[0]); - $request = $request->withUri($redirectUrl); - } else { - break; - } - } - - return $response; - } - - /** - * Returns the HTTP client instance. - * - * @return GuzzleHttp\ClientInterface - */ - abstract public function getHttpClient(); - - /** - * Retrieves current redirect limit. - * - * @return integer - */ - public function getRedirectLimit() - { - return $this->redirectLimit; - } - - /** - * Determines if a given response is a redirect. - * - * @param ResponseInterface $response - * - * @return boolean - */ - protected function isRedirect(ResponseInterface $response) - { - $statusCode = $response->getStatusCode(); - - return $statusCode > 300 && $statusCode < 400 && $response->hasHeader('Location'); - } - - /** - * Sends a request instance and returns a response instance. - * - * WARNING: This method does not attempt to catch exceptions caused by HTTP - * errors! It is recommended to wrap this method in a try/catch block. - * - * @param RequestInterface $request - * @return ResponseInterface - */ - public function getResponse(RequestInterface $request) - { - try { - $response = $this->followRequestRedirects($request); - } catch (BadResponseException $e) { - $response = $e->getResponse(); - } - - return $response; - } - - /** - * Updates the redirect limit. - * - * @param integer $limit - * @return League\OAuth2\Client\Provider\AbstractProvider - * @throws InvalidArgumentException - */ - public function setRedirectLimit($limit) - { - if (!is_int($limit)) { - throw new InvalidArgumentException('redirectLimit must be an integer.'); - } - - if ($limit < 1) { - throw new InvalidArgumentException('redirectLimit must be greater than or equal to one.'); - } - - $this->redirectLimit = $limit; - - return $this; - } -} diff --git a/src/Tool/RequestFactory.php b/src/Tool/RequestFactory.php index 1af43429..9e76bd54 100644 --- a/src/Tool/RequestFactory.php +++ b/src/Tool/RequestFactory.php @@ -14,7 +14,9 @@ namespace League\OAuth2\Client\Tool; -use GuzzleHttp\Psr7\Request; +use Psr\Http\Message\RequestFactoryInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\StreamFactoryInterface; /** * Used to produce PSR-7 Request instances. @@ -23,6 +25,16 @@ */ class RequestFactory { + /** + * @var RequestFactoryInterface + */ + protected $requestFactory; + + /** + * @var StreamFactoryInterface + */ + protected $streamFactory; + /** * Creates a PSR-7 Request instance. * @@ -32,7 +44,7 @@ class RequestFactory * @param string|resource|StreamInterface $body Message body. * @param string $version HTTP protocol version. * - * @return Request + * @return RequestInterface */ public function getRequest( $method, @@ -41,7 +53,15 @@ public function getRequest( $body = null, $version = '1.1' ) { - return new Request($method, $uri, $headers, $body, $version); + $request = $this->requestFactory->createRequest($method, $uri) + ->withProtocolVersion($version) + ->withBody($this->streamFactory->createStream($body)); + + foreach ($headers as $name => $value) { + $request = $request->withHeader($name, $value); + } + + return $request; } /** @@ -70,7 +90,7 @@ protected function parseOptions(array $options) * @param null|string $uri * @param array $options * - * @return Request + * @return RequestInterface */ public function getRequestWithOptions($method, $uri, array $options = []) { diff --git a/test/src/Tool/ProviderRedirectTraitTest.php b/test/src/Tool/ProviderRedirectTraitTest.php deleted file mode 100644 index e761cd9b..00000000 --- a/test/src/Tool/ProviderRedirectTraitTest.php +++ /dev/null @@ -1,160 +0,0 @@ -httpClient; - } - - public function setHttpClient(ClientInterface $httpClient) - { - $this->httpClient = $httpClient; - - return $this; - } - - public function testRedirectLimitDefault() - { - $this->assertEquals(2, $this->getRedirectLimit()); - } - - public function testSetRedirectLimit() - { - $redirectLimit = rand(3, 5); - $this->setRedirectLimit($redirectLimit); - $this->assertEquals($redirectLimit, $this->getRedirectLimit()); - } - - public function testSetRedirectLimitThrowsExceptionWhenNonNumericProvided() - { - $redirectLimit = 'florp'; - - $this->expectException(InvalidArgumentException::class); - - $this->setRedirectLimit($redirectLimit); - } - - public function testSetRedirectLimitThrowsExceptionWhenZeroProvided() - { - $redirectLimit = 0; - - $this->expectException(InvalidArgumentException::class); - - $this->setRedirectLimit($redirectLimit); - } - - public function testSetRedirectLimitThrowsExceptionWhenNegativeIntegerProvided() - { - $redirectLimit = -10; - - $this->expectException(InvalidArgumentException::class); - - $this->setRedirectLimit($redirectLimit); - } - - public function testClientLimitsRedirectResponse() - { - $redirectLimit = rand(3, 5); - $status = rand(301,399); - $redirectUrl = uniqid(); - - $request = Mockery::mock(RequestInterface::class); - $request - ->shouldReceive('withUri') - ->andReturnSelf(); - - $response = Mockery::mock(ResponseInterface::class, [ - 'getStatusCode' => $status, - ]); - $response - ->shouldReceive('hasHeader') - ->with('Location') - ->andReturnTrue(); - $response - ->shouldReceive('getHeader') - ->with('Location') - ->andReturn([$redirectUrl]); - - $client = Mockery::mock(ClientInterface::class); - $client - ->shouldReceive('send') - ->times($redirectLimit) - ->andReturn($response); - - $this->setHttpClient($client)->setRedirectLimit($redirectLimit); - $finalResponse = $this->getResponse($request); - - $this->assertInstanceOf(ResponseInterface::class, $finalResponse); - } - - public function testClientLimitsRedirectLoopWhenRedirectNotDetected() - { - $redirectLimit = rand(3, 5); - $status = 200; - - $request = Mockery::mock(RequestInterface::class); - $request - ->shouldReceive('withUri') - ->andReturnSelf(); - - $response = Mockery::mock(ResponseInterface::class, [ - 'getStatusCode' => $status, - ]); - $response - ->shouldReceive('hasHeader') - ->with('Location') - ->andReturnTrue(); - - $client = Mockery::mock(ClientInterface::class); - $client - ->shouldReceive('send') - ->once() - ->andReturn($response); - - $this->setHttpClient($client)->setRedirectLimit($redirectLimit); - $finalResponse = $this->getResponse($request); - - $this->assertInstanceOf(ResponseInterface::class, $finalResponse); - } - - public function testClientErrorReturnsResponse() - { - $status = rand(400, 500); - $result = ['foo' => 'bar']; - - $request = Mockery::mock(RequestInterface::class); - $request - ->shouldReceive('withUri') - ->andReturnSelf(); - - $response = Mockery::mock(ResponseInterface::class, [ - 'getStatusCode' => $status, - ]); - - $exception = new BadResponseException('test exception', $request, $response); - - $client = Mockery::mock(ClientInterface::class); - $client - ->shouldReceive('send') - ->andThrow($exception); - - $this->setHttpClient($client); - $finalResponse = $this->getResponse($request); - - $this->assertInstanceOf(ResponseInterface::class, $finalResponse); - } -}