Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[HttpClient] Add Psr18Client - aka a PSR-18 adapter
- Loading branch information
1 parent
de0ad70
commit d9622bf
Showing
4 changed files
with
190 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\HttpClient; | ||
|
||
use Psr\Http\Client\ClientInterface; | ||
use Psr\Http\Client\NetworkExceptionInterface; | ||
use Psr\Http\Client\RequestExceptionInterface; | ||
use Psr\Http\Message\RequestInterface; | ||
use Psr\Http\Message\ResponseFactoryInterface; | ||
use Psr\Http\Message\ResponseInterface; | ||
use Psr\Http\Message\StreamFactoryInterface; | ||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; | ||
use Symfony\Contracts\HttpClient\HttpClientInterface; | ||
|
||
/** | ||
* An adapter to turn a Symfony HttpClientInterface into a PSR-18 ClientInterface. | ||
* | ||
* Run "composer require psr/http-client" to install the base ClientInterface. Run | ||
* "composer require nyholm/psr7" to install an efficient implementation of response | ||
* and stream factories with flex-provided autowiring aliases. | ||
* | ||
* @author Nicolas Grekas <p@tchwork.com> | ||
*/ | ||
final class Psr18Client implements ClientInterface | ||
{ | ||
private $client; | ||
private $responseFactory; | ||
private $streamFactory; | ||
|
||
public function __construct(HttpClientInterface $client, ResponseFactoryInterface $responseFactory, StreamFactoryInterface $streamFactory) | ||
{ | ||
$this->client = $client; | ||
$this->responseFactory = $responseFactory; | ||
$this->streamFactory = $streamFactory; | ||
} | ||
|
||
public function sendRequest(RequestInterface $request): ResponseInterface | ||
{ | ||
try { | ||
$response = $this->client->request($request->getMethod(), (string) $request->getUri(), [ | ||
'headers' => $request->getHeaders(), | ||
'body' => (string) $request->getBody(), | ||
'http_version' => '1.0' === $request->getProtocolVersion() ? 1.0 : null, | ||
]); | ||
|
||
$psrResponse = $this->responseFactory->createResponse($response->getStatusCode()); | ||
|
||
foreach ($response->getHeaders() as $name => $values) { | ||
foreach ($values as $value) { | ||
$psrResponse = $psrResponse->withAddedHeader($name, $value); | ||
} | ||
} | ||
|
||
return $psrResponse->withBody($this->streamFactory->createStream($response->getContent())); | ||
} catch (TransportExceptionInterface $e) { | ||
if ($e instanceof \InvalidArgumentException) { | ||
throw new Psr18RequestException($e, $request); | ||
} | ||
|
||
throw new Psr18NetworkException($e, $request); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* @internal | ||
*/ | ||
trait Psr18ExceptionTrait | ||
{ | ||
private $request; | ||
|
||
public function __construct(TransportExceptionInterface $e, RequestInterface $request) | ||
{ | ||
parent::__construct($e->getMessage(), 0, $e); | ||
$this->request = $request; | ||
} | ||
|
||
public function getRequest(): RequestInterface | ||
{ | ||
return $this->request; | ||
} | ||
} | ||
|
||
/** | ||
* @internal | ||
*/ | ||
class Psr18NetworkException extends \RuntimeException implements NetworkExceptionInterface | ||
{ | ||
use Psr18ExceptionTrait; | ||
} | ||
|
||
/** | ||
* @internal | ||
*/ | ||
class Psr18RequestException extends \InvalidArgumentException implements RequestExceptionInterface | ||
{ | ||
use Psr18ExceptionTrait; | ||
} |
77 changes: 77 additions & 0 deletions
77
src/Symfony/Component/HttpClient/Tests/Psr18ClientTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\HttpClient\Tests; | ||
|
||
use Nyholm\Psr7\Factory\Psr17Factory; | ||
use PHPUnit\Framework\TestCase; | ||
use Symfony\Component\HttpClient\NativeHttpClient; | ||
use Symfony\Component\HttpClient\Psr18Client; | ||
use Symfony\Component\HttpClient\Psr18NetworkException; | ||
use Symfony\Component\HttpClient\Psr18RequestException; | ||
use Symfony\Contracts\HttpClient\Test\TestHttpServer; | ||
|
||
class Psr18ClientTest extends TestCase | ||
{ | ||
private static $server; | ||
|
||
public static function setUpBeforeClass() | ||
{ | ||
TestHttpServer::start(); | ||
} | ||
|
||
public function testSendRequest() | ||
{ | ||
$factory = new Psr17Factory(); | ||
$client = new Psr18Client(new NativeHttpClient(), $factory, $factory); | ||
|
||
$response = $client->sendRequest($factory->createRequest('GET', 'http://localhost:8057')); | ||
|
||
$this->assertSame(200, $response->getStatusCode()); | ||
$this->assertSame('application/json', $response->getHeaderLine('content-type')); | ||
|
||
$body = json_decode((string) $response->getBody(), true); | ||
|
||
$this->assertSame('HTTP/1.1', $body['SERVER_PROTOCOL']); | ||
} | ||
|
||
public function testPostRequest() | ||
{ | ||
$factory = new Psr17Factory(); | ||
$client = new Psr18Client(new NativeHttpClient(), $factory, $factory); | ||
|
||
$request = $factory->createRequest('POST', 'http://localhost:8057/post') | ||
->withBody($factory->createStream('foo=0123456789')); | ||
|
||
$response = $client->sendRequest($request); | ||
$body = json_decode((string) $response->getBody(), true); | ||
|
||
$this->assertSame(['foo' => '0123456789', 'REQUEST_METHOD' => 'POST'], $body); | ||
} | ||
|
||
public function testNetworkException() | ||
{ | ||
$factory = new Psr17Factory(); | ||
$client = new Psr18Client(new NativeHttpClient(), $factory, $factory); | ||
|
||
$this->expectException(Psr18NetworkException::class); | ||
$client->sendRequest($factory->createRequest('GET', 'http://localhost:8058')); | ||
} | ||
|
||
public function testRequestException() | ||
{ | ||
$factory = new Psr17Factory(); | ||
$client = new Psr18Client(new NativeHttpClient(), $factory, $factory); | ||
|
||
$this->expectException(Psr18RequestException::class); | ||
$client->sendRequest($factory->createRequest('BAD.METHOD', 'http://localhost:8057')); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters