Skip to content

Commit 94a9243

Browse files
mbonneaudavidwdan
authored andcommittedSep 12, 2017
Update libraries for more recent react work (#13)
* Library updates * Update README.md and add a test * Import some classes
1 parent d87f61c commit 94a9243

9 files changed

+122
-52
lines changed
 

‎CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# 2.0.0
2+
3+
- Updated react libraries (http/http-client)
4+
- Changed API to allow passing of `Connector`
5+
16
# 1.0.2
27

38
- End the request not the response when dispose is called ([b77c5118](https://github.com/RxPHP/RxWebsocket/commit/b77c5118c14d34e034b19383974337aec05d787a))

‎README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ $server->subscribe(function (\Rx\Websocket\MessageSubject $cs) {
4343

4444
#### Server that dumps everything to the console
4545
```php
46-
$server = new \Rx\Websocket\Server('127.0.0.1', 9191);
46+
$server = new \Rx\Websocket\Server('tcp://127.0.0.1:9191');
4747

4848
$server->subscribe(function (\Rx\Websocket\MessageSubject $cs) {
4949
$cs->subscribe(function ($message) {

‎composer.json

+7-2
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,14 @@
2727
"Rx\\Websocket\\": "src/"
2828
}
2929
},
30+
"autoload-dev": {
31+
"psr-4": {
32+
"Rx\\Websocket\\Test\\": "tests/"
33+
}
34+
},
3035
"require": {
31-
"react/http": "^0.4.1",
32-
"react/http-client": "^0.4.10",
36+
"react/http": "^0.7.3",
37+
"react/http-client": "^0.5.3",
3338
"voryx/event-loop": "^2.0",
3439
"ratchet/rfc6455": "^0.2.2",
3540
"reactivex/rxphp": "^2.0"

‎src/Client.php

+26-11
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
namespace Rx\Websocket;
44

5+
use GuzzleHttp\Psr7\Request as Psr7Request;
6+
use GuzzleHttp\Psr7\Response as Psr7Response;
57
use GuzzleHttp\Psr7\Uri;
68
use Ratchet\RFC6455\Handshake\ClientNegotiator;
7-
use React\Dns\Resolver\Factory;
8-
use React\Dns\Resolver\Resolver;
99
use React\EventLoop\LoopInterface;
10+
use React\HttpClient\Client as HttpClient;
1011
use React\HttpClient\Request;
1112
use React\HttpClient\Response;
13+
use React\Socket\ConnectorInterface;
1214
use Rx\Disposable\CallbackDisposable;
1315
use Rx\DisposableInterface;
1416
use Rx\Observable;
@@ -22,25 +24,37 @@ class Client extends Observable
2224
private $useMessageObject;
2325
private $subProtocols;
2426
private $loop;
25-
private $dnsResolver;
27+
private $connector;
2628

27-
public function __construct(string $url, bool $useMessageObject = false, array $subProtocols = [], LoopInterface $loop = null, Resolver $dnsResolver = null)
29+
public function __construct(string $url, bool $useMessageObject = false, array $subProtocols = [], LoopInterface $loop = null, ConnectorInterface $connector = null)
2830
{
31+
$parsedUrl = parse_url($url);
32+
if (!isset($parsedUrl['scheme']) || !in_array($parsedUrl['scheme'], ['wss', 'ws'])) {
33+
throw new \InvalidArgumentException('url must use either ws or wss scheme');
34+
}
35+
36+
if ($parsedUrl['scheme'] === 'wss') {
37+
$url = 'https://' . substr($url, 6);
38+
}
39+
40+
if ($parsedUrl['scheme'] === 'ws') {
41+
$url = 'http://' . substr($url, 5);
42+
}
43+
2944
$this->url = $url;
3045
$this->useMessageObject = $useMessageObject;
3146
$this->subProtocols = $subProtocols;
3247
$this->loop = $loop ?: \EventLoop\getLoop();
33-
$this->dnsResolver = $dnsResolver ?: (new Factory())->createCached('8.8.8.8', $this->loop);
48+
$this->connector = $connector;
3449
}
3550

3651
public function _subscribe(ObserverInterface $clientObserver): DisposableInterface
3752
{
38-
$factory = new \React\HttpClient\Factory();
39-
$client = $factory->create($this->loop, $this->dnsResolver);
53+
$client = new HttpClient($this->loop, $this->connector);
4054

4155
$cNegotiator = new ClientNegotiator();
4256

43-
/** @var \GuzzleHttp\Psr7\Request $nRequest */
57+
/** @var Psr7Request $nRequest */
4458
$nRequest = $cNegotiator->generateRequest(new Uri($this->url));
4559

4660
if (!empty($this->subProtocols)) {
@@ -67,14 +81,14 @@ public function _subscribe(ObserverInterface $clientObserver): DisposableInterfa
6781
throw new \Exception('Unexpected response code ' . $response->getCode());
6882
}
6983

70-
$psr7Response = new \GuzzleHttp\Psr7\Response(
84+
$psr7Response = new Psr7Response(
7185
$response->getCode(),
7286
$response->getHeaders(),
7387
null,
7488
$response->getVersion()
7589
);
7690

77-
$psr7Request = new \GuzzleHttp\Psr7\Request('GET', $this->url, $flatHeaders);
91+
$psr7Request = new Psr7Request('GET', $this->url, $flatHeaders);
7892

7993
if (!$cNegotiator->validateResponse($psr7Request, $psr7Response)) {
8094
throw new \Exception('Invalid response');
@@ -127,7 +141,8 @@ function () use ($request) {
127141
));
128142
});
129143

130-
$request->writeHead();
144+
// empty write to force connection and header send
145+
$request->write('');
131146

132147
return new CallbackDisposable(function () use ($request) {
133148
$request->close();

‎src/Server.php

+45-35
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@
22

33
namespace Rx\Websocket;
44

5-
use GuzzleHttp\Psr7\Uri;
5+
use GuzzleHttp\Psr7\Request;
6+
use Psr\Http\Message\ServerRequestInterface;
67
use Ratchet\RFC6455\Handshake\RequestVerifier;
78
use Ratchet\RFC6455\Handshake\ServerNegotiator;
89
use React\EventLoop\LoopInterface;
9-
use React\Http\Request;
1010
use React\Http\Response;
11+
use React\Http\Server as HttpServer;
12+
use React\Socket\Server as SocketServer;
13+
use React\Stream\CompositeStream;
14+
use React\Stream\ReadableStreamInterface;
15+
use React\Stream\ThroughStream;
1116
use Rx\Disposable\CallbackDisposable;
1217
use Rx\DisposableInterface;
1318
use Rx\Observable;
@@ -23,53 +28,56 @@ class Server extends Observable
2328
private $subProtocols;
2429
private $loop;
2530

26-
public function __construct(string $bindAddress, int $port, bool $useMessageObject = false, array $subProtocols = [], LoopInterface $loop = null)
31+
public function __construct(string $bindAddressOrPort, bool $useMessageObject = false, array $subProtocols = [], LoopInterface $loop = null)
2732
{
28-
$this->bindAddress = $bindAddress;
29-
$this->port = $port;
33+
$this->bindAddress = $bindAddressOrPort;
3034
$this->useMessageObject = $useMessageObject;
3135
$this->subProtocols = $subProtocols;
3236
$this->loop = $loop ?: \EventLoop\getLoop();
3337
}
3438

3539
public function _subscribe(ObserverInterface $observer): DisposableInterface
3640
{
37-
$socket = new \React\Socket\Server($this->loop);
41+
$socket = new SocketServer($this->bindAddress, $this->loop);
3842

3943
$negotiator = new ServerNegotiator(new RequestVerifier());
4044
if (!empty($this->subProtocols)) {
4145
$negotiator->setSupportedSubProtocols($this->subProtocols);
4246
}
4347

44-
$http = new \React\Http\Server($socket);
45-
$http->on('request', function (Request $request, Response $response) use ($negotiator, $observer, &$outStream) {
46-
$uri = new Uri($request->getPath());
47-
if (count($request->getQuery()) > 0) {
48-
$uri = $uri->withQuery(\GuzzleHttp\Psr7\build_query($request->getQuery()));
49-
}
48+
$http = new HttpServer(function (ServerRequestInterface $request) use ($negotiator, $observer) {
49+
$uri = $request->getUri();
5050

51-
$psrRequest = new \GuzzleHttp\Psr7\Request(
51+
$psrRequest = new Request(
5252
$request->getMethod(),
5353
$uri,
5454
$request->getHeaders()
5555
);
5656

5757
// cram the remote address into the header in our own X- header so
5858
// the user will have access to it
59-
$psrRequest = $psrRequest->withAddedHeader('X-RxWebsocket-Remote-Address', $request->remoteAddress);
59+
$psrRequest = $psrRequest->withAddedHeader('X-RxWebsocket-Remote-Address', $request->getServerParams()['REMOTE_ADDR'] ?? '');
6060

6161
$negotiatorResponse = $negotiator->handshake($psrRequest);
6262

63-
$response->writeHead(
63+
/** @var ReadableStreamInterface $requestStream */
64+
$requestStream = new ThroughStream();
65+
$responseStream = new ThroughStream();
66+
67+
$response = new Response(
6468
$negotiatorResponse->getStatusCode(),
6569
array_merge(
66-
$negotiatorResponse->getHeaders(),
67-
['Content-Length' => '0']
70+
$negotiatorResponse->getHeaders()
71+
),
72+
new CompositeStream(
73+
$responseStream,
74+
$requestStream
6875
)
6976
);
7077

78+
7179
if ($negotiatorResponse->getStatusCode() !== 101) {
72-
$response->end();
80+
$responseStream->end();
7381
return;
7482
}
7583

@@ -78,38 +86,38 @@ public function _subscribe(ObserverInterface $observer): DisposableInterface
7886
$subProtocol = $negotiatorResponse->getHeader('Sec-WebSocket-Protocol')[0];
7987
}
8088

81-
$connection = new MessageSubject(
89+
$messageSubject = new MessageSubject(
8290
new AnonymousObservable(
83-
function (ObserverInterface $observer) use ($request) {
84-
$request->on('data', function ($data) use ($observer) {
91+
function (ObserverInterface $observer) use ($requestStream) {
92+
$requestStream->on('data', function ($data) use ($observer) {
8593
$observer->onNext($data);
8694
});
87-
$request->on('error', function ($error) use ($observer) {
95+
$requestStream->on('error', function ($error) use ($observer) {
8896
$observer->onError($error);
8997
});
90-
$request->on('close', function () use ($observer) {
98+
$requestStream->on('close', function () use ($observer) {
9199
$observer->onCompleted();
92100
});
93-
$request->on('end', function () use ($observer) {
101+
$requestStream->on('end', function () use ($observer) {
94102
$observer->onCompleted();
95103
});
96104

97105
return new CallbackDisposable(
98-
function () use ($request) {
99-
$request->close();
106+
function () use ($requestStream) {
107+
$requestStream->close();
100108
}
101109
);
102110
}
103111
),
104112
new CallbackObserver(
105-
function ($x) use ($response) {
106-
$response->write($x);
113+
function ($x) use ($responseStream) {
114+
$responseStream->write($x);
107115
},
108-
function ($error) use ($response) {
109-
$response->close();
116+
function ($error) use ($responseStream) {
117+
$responseStream->close();
110118
},
111-
function () use ($response) {
112-
$response->end();
119+
function () use ($responseStream) {
120+
$responseStream->end();
113121
}
114122
),
115123
false,
@@ -119,13 +127,15 @@ function () use ($response) {
119127
$negotiatorResponse
120128
);
121129

122-
$observer->onNext($connection);
130+
$observer->onNext($messageSubject);
131+
132+
return $response;
123133
});
124134

125-
$socket->listen($this->port, $this->bindAddress);
135+
$http->listen($socket);
126136

127137
return new CallbackDisposable(function () use ($socket) {
128-
$socket->shutdown();
138+
$socket->close();
129139
});
130140
}
131141
}

‎test/ClientTest.php

+36
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@
33
namespace Rx\Websocket\Test;
44

55
use React\EventLoop\Factory;
6+
use Rx\Websocket\Client;
7+
use Rx\Websocket\MessageSubject;
8+
use Rx\Websocket\Server;
69

710
class ClientTest extends \PHPUnit_Framework_TestCase
811
{
912
public function testErrorBeforeRequest()
1013
{
1114
$loop = Factory::create();
1215

16+
// expecting connection error
1317
$client = new \Rx\Websocket\Client('ws://127.0.0.1:12340/', false, [], $loop);
1418

1519
$errored = false;
@@ -25,4 +29,36 @@ function ($err) use (&$errored) {
2529

2630
$this->assertTrue($errored);
2731
}
32+
33+
public function testRequestEndOnDispose()
34+
{
35+
$this->markTestSkipped();
36+
$loop = Factory::create();
37+
38+
$server = new Server('tcp://127.0.0.1:1234', false, [], $loop);
39+
$serverDisp = $server->subscribe(function (MessageSubject $ms) {
40+
$ms->map('strrev')->subscribe($ms);
41+
});
42+
43+
$value = null;
44+
45+
$client = new Client('ws://127.0.0.1:1234/', false, [], $loop);
46+
$client
47+
->subscribe(function (MessageSubject $ms) use ($serverDisp) {
48+
$ms->onNext('Hello');
49+
$ms
50+
->finally(function () use ($serverDisp) {
51+
$serverDisp->dispose();
52+
})
53+
->take(1)
54+
->subscribe(function ($x) use (&$value) {
55+
$this->assertNull($value);
56+
$value = $x;
57+
});
58+
});
59+
60+
$loop->run();
61+
62+
$this->assertEquals('olleH', $value);
63+
}
2864
}

‎test/ab/run_ab_tests.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ php clientRunner.php
66

77
sleep 2
88

9-
php testServer.php 60 &
9+
php testServer.php 600 &
1010
sleep 3
1111
wstest -m fuzzingclient -s fuzzingclient.json
1212
sleep 12

‎test/ab/testServer.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
$timerObservable = Observable::timer(1000 * $argv[1]);
1212
}
1313

14-
$server = new \Rx\Websocket\Server("127.0.0.1", 9001, true);
14+
$server = new \Rx\Websocket\Server("tcp://127.0.0.1:9001", true);
1515

1616
$server
1717
->takeUntil($timerObservable)

‎test/bootstrap.php

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
foreach ($files as $file) {
1414
if (file_exists($file)) {
1515
$loader = require $file;
16-
$loader->addPsr4('Rx\\Websocket\\Test\\', __DIR__);
1716
break;
1817
}
1918
}

0 commit comments

Comments
 (0)
Please sign in to comment.