8000 [HttpClient] Async HTTPlug client · symfony/symfony@0cbe120 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0cbe120

Browse files
Nyholmnicolas-grekas
authored andcommitted
[HttpClient] Async HTTPlug client
1 parent 732c034 commit 0cbe120

File tree

4 files changed

+385
-33
lines changed

4 files changed

+385
-33
lines changed

src/Symfony/Component/HttpClient/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ CHANGELOG
55
-----
66

77
* added `StreamWrapper`
8-
* added `HttplugClient`
8+
* added `HttplugClient` with support for sync and async requests
99
* added `max_duration` option
1010
* added support for NTLM authentication
1111
* added `$response->toStream()` to cast responses to regular PHP streams

src/Symfony/Component/HttpClient/HttplugClient.php

Lines changed: 88 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,74 +11,120 @@
1111

1212
namespace Symfony\Component\HttpClient;
1313

14-
use Http\Client\Exception\NetworkException;
1514
use Http\Client\Exception\RequestException;
16-
use Http\Client\HttpClient;
15+
use Http\Client\HttpAsyncClient;
16+
use Http\Client\HttpClient as HttplugInterface;
1717
use Http\Message\RequestFactory;
1818
use Http\Message\StreamFactory;
1919
use Http\Message\UriFactory;
20-
use Psr\Http\Client\ClientInterface;
21-
use Psr\Http\Client\NetworkExceptionInterface;
22-
use Psr\Http\Client\RequestExceptionInterface;
20+
use Http\Promise\Promise;
21+
use Nyholm\Psr7\Factory\Psr17Factory;
22+
use Nyholm\Psr7\Request;
23+
use Nyholm\Psr7\Uri;
24+
use Psr\Http\Message\RequestFactoryInterface;
2325
use Psr\Http\Message\RequestInterface;
2426
use Psr\Http\Message\ResponseFactoryInterface;
2527
use Psr\Http\Message\ResponseInterface;
2628
use Psr\Http\Message\StreamFactoryInterface;
2729
use Psr\Http\Message\StreamInterface;
30+
use Psr\Http\Message\UriFactoryInterface;
2831
use Psr\Http\Message\UriInterface;
32+
use Symfony\Component\HttpClient\Response\HttplugPromise;
33+
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
2934
use Symfony\Contracts\HttpClient\HttpClientInterface;
3035

31-
if (!interface_exists(HttpClient::class)) {
36+
if (!interface_exists(HttplugInterface::class)) {
3237
throw new \LogicException('You cannot use "Symfony\Component\HttpClient\HttplugClient" as the "php-http/httplug" package is not installed. Try running "composer require php-http/httplug".');
3338
}
3439

35-
if (!interface_exists(ClientInterface::class)) {
36-
throw new \LogicException('You cannot use "Symfony\Component\HttpClient\HttplugClient" as the "psr/http-client" package is not installed. Try running "composer require psr/http-client".');
37-
}
38-
3940
if (!interface_exists(RequestFactory::class)) {
4041
throw new \LogicException('You cannot use "Symfony\Component\HttpClient\HttplugClient" as the "php-http/message-factory" package is not installed. Try running "composer require nyholm/psr7".');
4142
}
4243

4344
/**
4445
* An adapter to turn a Symfony HttpClientInterface into an Httplug client.
4546
*
46-
* Run "composer require psr/http-client" to install the base ClientInterface. Run
47-
* "composer require nyholm/psr7" to install an efficient implementation of response
47+
* Run "composer require nyholm/psr7" to install an efficient implementation of response
4848
* and stream factories with flex-provided autowiring aliases.
4949
*
5050
* @author Nicolas Grekas <p@tchwork.com>
5151
*/
52-
final class HttplugClient implements HttpClient, RequestFactory, StreamFactory, UriFactory
52+
final class HttplugClient implements HttplugInterface, HttpAsyncClient, RequestFactory, StreamFactory, UriFactory
5353
{
5454
private $client;
55+
private $responseFactory;
56+
private $streamFactory;
5557

5658
public function __construct(HttpClientInterface $client = null, ResponseFactoryInterface $responseFactory = null, StreamFactoryInterface $streamFactory = null)
5759
{
58-
$this->client = new Psr18Client($client, $responseFactory, $streamFactory);
60+
$this->client = $client ?? HttpClient::create();
61+
$this->responseFactory = $responseFactory;
62+
$this->streamFactory = $streamFactory ?? ($responseFactory instanceof StreamFactoryInterface ? $responseFactory : null);
63+
64+
if (null !== $this->responseFactory && null !== $this->streamFactory) {
65+
return;
66+
}
67+
68+
if (!class_exists(Psr17Factory::class)) {
69+
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\HttplugClient" as no PSR-17 factories have been provided. Try running "composer require nyholm/psr7".');
70+
}
71+
72+
$psr17Factory = new Psr17Factory();
73+
$this->responseFactory = $this->responseFactory ?? $psr17Factory;
74+
$this->streamFactory = $this->streamFactory ?? $psr17Factory;
5975
}
6076

6177
/**
6278
* {@inheritdoc}
6379
*/
6480
public function sendRequest(RequestInterface $request): ResponseInterface
81+
{
82+
return $this->sendAsyncRequest($request)->wait();
83+
}
84+
85+
/**
86+
* {@inheritdoc}
87+
*
88+
* @return HttplugPromise
89+
*/
90+
public function sendAsyncRequest(RequestInterface $request): Promise
6591
{
6692
try {
67-
return $this->client->sendRequest($request);
68-
} catch (RequestExceptionInterface $e) {
93+
$body = $request->getBody();
94+
95+
if ($body->isSeekable()) {
96+
$body->seek(0);
97+
}
98+
99+
$response = $this->client->request($request->getMethod(), (string) $request->getUri(), [
100+
'headers' => $request->getHeaders(),
101+
'body' => $body->getContents(),
102+
'http_version' => '1.0' === $request->getProtocolVersion() ? '1.0' : null,
103+
'buffer' => true, // Make sure the response body can be read many times
104+
]);
105+
} catch (\InvalidArgumentException $e) {
69106
throw new RequestException($e->getMessage(), $request, $e);
70-
} catch (NetworkExceptionInterface $e) {
71-
throw new NetworkException($e->getMessage(), $request, $e);
107+
} catch (TransportExceptionInterface $e) {
108+
$response = new NetworkException($e->getMessage(), $request, $e);
72109
}
110+
111+
return new HttplugPromise($request, $response, $this->client, $this->responseFactory, $this->streamFactory);
73112
}
74113

75114
/**
76115
* {@inheritdoc}
77116
*/
78117
public function createRequest($method, $uri, array $headers = [], $body = null, $protocolVersion = '1.1'): RequestInterface
79118
{
80-
$request = $this->client
81-
->createRequest($method, $uri)
119+
if ($this->responseFactory instanceof RequestFactoryInterface) {
120+
$request = $this->responseFactory->createRequest($method, $uri);
121+
} elseif (!class_exists(Request::class)) {
122+
throw new \LogicException(sprintf('You cannot use "%s()" as the "nyholm/psr7" package is not installed. Try running "composer require nyholm/psr7".', __METHOD__));
123+
} else {
124+
$request = new Request($method, $uri);
125+
}
126+
127+
$request = $request
82128
->withProtocolVersion($protocolVersion)
83129
->withBody($this->createStream($body))
84130
;
@@ -100,27 +146,37 @@ public function createStream($body = null): StreamInterface
100146
}
101147

102148
if (\is_string($body ?? '')) {
103-
$body = $this->client->createStream($body ?? '');
104-
105-
if ($body->isSeekable()) {
106-
$body->seek(0);
107-
}
108-
109-
return $body;
149+
$stream = $this->streamFactory->createStream($body ?? '');
150+
} elseif (\is_resource($body)) {
151+
$stream = $this->streamFactory->createStreamFromResource($body);
152+
} else {
153+
throw new \InvalidArgumentException(sprintf('%s() expects string, resource or StreamInterface, %s given.', __METHOD__, \gettype($body)));
110154
}
111155

112-
if (\is_resource($body)) {
113-
return $this->client->createStreamFromResource($body);
156+
if ($stream->isSeekable()) {
157+
$stream->seek(0);
114158
}
115159

116-
throw new \InvalidArgumentException(sprintf('%s() expects string, resource or StreamInterface, %s given.', __METHOD__, \gettype($body)));
160+
return $stream;
117161
}
118162

119163
/**
120164
* {@inheritdoc}
121165
*/
122-
public function createUri($uri = ''): UriInterface
166+
public function createUri($uri): UriInterface
123167
{
124-
return $uri instanceof UriInterface ? $uri : $this->client->createUri($uri);
168+
if ($uri instanceof UriInterface) {
169+
return $uri;
170+
}
171+
172+
if ($this->responseFactory instanceof UriFactoryInterface) {
173+
return $this->responseFactory->createUri($uri);
174+
}
175+
176+
if (!class_exists(Uri::class)) {
177+
throw new \LogicException(sprintf('You cannot use "%s()" as the "nyholm/psr7" package is not installed. Try running "composer require nyholm/psr7".', __METHOD__));
178+
}
179+
180+
return new Uri($uri);
125181
}
126182
}

0 commit comments

Comments
 (0)
0