Skip to content

Commit 13d0012

Browse files
committed
Add the React\Http\Client adapter
It rely on react defered logic to handle asyncRequest and is able to mute a PSR7 request to a React Request. Then it take care of react request execution by listening all required events and processing de defered accordingly.
1 parent 94bc741 commit 13d0012

File tree

1 file changed

+151
-0
lines changed

1 file changed

+151
-0
lines changed

src/ReactHttpClient.php

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
<?php
2+
3+
namespace Http\Adapter;
4+
5+
use React\EventLoop\LoopInterface;
6+
use React\Promise\Deferred;
7+
use React\HttpClient\Client;
8+
use React\HttpClient\Request as ReactRequest;
9+
use React\HttpClient\Response as ReactResponse;
10+
11+
use Http\Client\HttpClient;
12+
use Http\Client\HttpAsyncClient;
13+
use Http\Client\Promise;
14+
use Http\Client\Exception\HttpException;
15+
use Http\Client\Exception\RequestException;
16+
use Http\Message\MessageFactory;
17+
18+
use Psr\Http\Message\RequestInterface;
19+
use Psr\Http\Message\StreamInterface;
20+
21+
/**
22+
* Client for the React promise implementation
23+
* @author Stéphane Hulard <[email protected]>
24+
*/
25+
class ReactHttpClient implements HttpClient, HttpAsyncClient
26+
{
27+
/**
28+
* React HTTP client
29+
* @var Client
30+
*/
31+
private $client;
32+
33+
/**
34+
* React event loop
35+
* @var LoopInterface
36+
*/
37+
private $loop;
38+
39+
/**
40+
* Initialize the React client
41+
* @param LoopInterface|null $loop React Event loop
42+
* @param Resolver $resolver React async DNS resolver
43+
*/
44+
public function __construct(
45+
MessageFactory $messageFactory,
46+
LoopInterface $loop = null,
47+
Client $client = null
48+
) {
49+
$this->loop = null === $loop?ReactFactory::buildEventLoop():$loop;
50+
if( null === $client ) {
51+
$this->client = ReactFactory::buildHttpClient($this->loop);
52+
} elseif( null === $loop ) {
53+
throw new \RuntimeException(
54+
"You must give a LoopInterface instance with the Client"
55+
);
56+
}
57+
58+
$this->messageFactory = new ReactMessageFactory($messageFactory);
59+
}
60+
61+
/**
62+
* {@inheritdoc}
63+
*/
64+
public function sendRequest(RequestInterface $request)
65+
{
66+
$promise = $this->sendAsyncRequest($request);
67+
$promise->wait();
68+
69+
if ($promise->getState() == Promise::REJECTED) {
70+
throw $promise->getException();
71+
}
72+
73+
return $promise->getResponse();
74+
}
75+
76+
/**
77+
* {@inheritdoc}
78+
*/
79+
public function sendAsyncRequest(RequestInterface $request)
80+
{
81+
$requestStream = $this->buildReactRequest($request);
82+
$deferred = new Deferred();
83+
84+
$requestStream->on('error', function(\Exception $error) use ($deferred, $request) {
85+
$deferred->reject(new RequestException(
86+
$error->getMessage(),
87+
$request,
88+
$error
89+
));
90+
});
91+
$requestStream->on('response', function(ReactResponse $response = null) use ($deferred, $requestStream, $request) {
92+
$bodyStream = null;
93+
$response->on('data', function($data) use (&$bodyStream) {
94+
if( $data instanceof StreamInterface ) {
95+
$bodyStream = $data;
96+
} else {
97+
$bodyStream->write($data);
98+
}
99+
});
100+
101+
$response->on('end', function(\Exception $error = null) use ($deferred, $request, $response, &$bodyStream) {
102+
$bodyStream->rewind();
103+
$psr7Response = $this->messageFactory->buildResponse(
104+
$response,
105+
$bodyStream
106+
);
107+
if( null !== $error ) {
108+
$deferred->reject(new HttpException(
109+
$error->getMessage(),
110+
$request,
111+
$psr7Response,
112+
$error
113+
));
114+
} else {
115+
$deferred->resolve($psr7Response);
116+
}
117+
});
118+
});
119+
120+
$requestStream->end((string)$request->getBody());
121+
122+
$promise = new ReactPromiseAdapter($deferred->promise());
123+
$promise->setLoop($this->loop);
124+
return $promise;
125+
}
126+
127+
/**
128+
* Build a React request from the PSR7 RequestInterface
129+
* @param RequestInterface $request
130+
* @return ReactRequest
131+
*/
132+
private function buildReactRequest(RequestInterface $request)
133+
{
134+
$headers = [];
135+
foreach( $request->getHeaders() as $name => $value ) {
136+
$headers[$name] = (is_array($value)?$value[0]:$value);
137+
}
138+
if( $request->getBody()->getSize() > 0 ) {
139+
$headers['Content-Length'] = $request->getBody()->getSize();
140+
}
141+
142+
$reactRequest = $this->client->request(
143+
$request->getMethod(),
144+
(string)$request->getUri(),
145+
$headers,
146+
$request->getProtocolVersion()
147+
);
148+
149+
return $reactRequest;
150+
}
151+
}

0 commit comments

Comments
 (0)