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 <stephane@hlrd.me>
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy