| 1 |
liveuser |
1 |
# Socket
|
|
|
2 |
|
|
|
3 |
[](https://travis-ci.org/reactphp/socket)
|
|
|
4 |
|
|
|
5 |
Async, streaming plaintext TCP/IP and secure TLS socket server and client
|
|
|
6 |
connections for [ReactPHP](https://reactphp.org/).
|
|
|
7 |
|
|
|
8 |
The socket library provides re-usable interfaces for a socket-layer
|
|
|
9 |
server and client based on the [`EventLoop`](https://github.com/reactphp/event-loop)
|
|
|
10 |
and [`Stream`](https://github.com/reactphp/stream) components.
|
|
|
11 |
Its server component allows you to build networking servers that accept incoming
|
|
|
12 |
connections from networking clients (such as an HTTP server).
|
|
|
13 |
Its client component allows you to build networking clients that establish
|
|
|
14 |
outgoing connections to networking servers (such as an HTTP or database client).
|
|
|
15 |
This library provides async, streaming means for all of this, so you can
|
|
|
16 |
handle multiple concurrent connections without blocking.
|
|
|
17 |
|
|
|
18 |
**Table of Contents**
|
|
|
19 |
|
|
|
20 |
* [Quickstart example](#quickstart-example)
|
|
|
21 |
* [Connection usage](#connection-usage)
|
|
|
22 |
* [ConnectionInterface](#connectioninterface)
|
|
|
23 |
* [getRemoteAddress()](#getremoteaddress)
|
|
|
24 |
* [getLocalAddress()](#getlocaladdress)
|
|
|
25 |
* [Server usage](#server-usage)
|
|
|
26 |
* [ServerInterface](#serverinterface)
|
|
|
27 |
* [connection event](#connection-event)
|
|
|
28 |
* [error event](#error-event)
|
|
|
29 |
* [getAddress()](#getaddress)
|
|
|
30 |
* [pause()](#pause)
|
|
|
31 |
* [resume()](#resume)
|
|
|
32 |
* [close()](#close)
|
|
|
33 |
* [Server](#server)
|
|
|
34 |
* [Advanced server usage](#advanced-server-usage)
|
|
|
35 |
* [TcpServer](#tcpserver)
|
|
|
36 |
* [SecureServer](#secureserver)
|
|
|
37 |
* [UnixServer](#unixserver)
|
|
|
38 |
* [LimitingServer](#limitingserver)
|
|
|
39 |
* [getConnections()](#getconnections)
|
|
|
40 |
* [Client usage](#client-usage)
|
|
|
41 |
* [ConnectorInterface](#connectorinterface)
|
|
|
42 |
* [connect()](#connect)
|
|
|
43 |
* [Connector](#connector)
|
|
|
44 |
* [Advanced client usage](#advanced-client-usage)
|
|
|
45 |
* [TcpConnector](#tcpconnector)
|
|
|
46 |
* [HappyEyeBallsConnector](#happyeyeballsconnector)
|
|
|
47 |
* [DnsConnector](#dnsconnector)
|
|
|
48 |
* [SecureConnector](#secureconnector)
|
|
|
49 |
* [TimeoutConnector](#timeoutconnector)
|
|
|
50 |
* [UnixConnector](#unixconnector)
|
|
|
51 |
* [FixUriConnector](#fixeduriconnector)
|
|
|
52 |
* [Install](#install)
|
|
|
53 |
* [Tests](#tests)
|
|
|
54 |
* [License](#license)
|
|
|
55 |
|
|
|
56 |
## Quickstart example
|
|
|
57 |
|
|
|
58 |
Here is a server that closes the connection if you send it anything:
|
|
|
59 |
|
|
|
60 |
```php
|
|
|
61 |
$loop = React\EventLoop\Factory::create();
|
|
|
62 |
$socket = new React\Socket\Server('127.0.0.1:8080', $loop);
|
|
|
63 |
|
|
|
64 |
$socket->on('connection', function (React\Socket\ConnectionInterface $connection) {
|
|
|
65 |
$connection->write("Hello " . $connection->getRemoteAddress() . "!\n");
|
|
|
66 |
$connection->write("Welcome to this amazing server!\n");
|
|
|
67 |
$connection->write("Here's a tip: don't say anything.\n");
|
|
|
68 |
|
|
|
69 |
$connection->on('data', function ($data) use ($connection) {
|
|
|
70 |
$connection->close();
|
|
|
71 |
});
|
|
|
72 |
});
|
|
|
73 |
|
|
|
74 |
$loop->run();
|
|
|
75 |
```
|
|
|
76 |
|
|
|
77 |
See also the [examples](examples).
|
|
|
78 |
|
|
|
79 |
Here's a client that outputs the output of said server and then attempts to
|
|
|
80 |
send it a string:
|
|
|
81 |
|
|
|
82 |
```php
|
|
|
83 |
$loop = React\EventLoop\Factory::create();
|
|
|
84 |
$connector = new React\Socket\Connector($loop);
|
|
|
85 |
|
|
|
86 |
$connector->connect('127.0.0.1:8080')->then(function (React\Socket\ConnectionInterface $connection) use ($loop) {
|
|
|
87 |
$connection->pipe(new React\Stream\WritableResourceStream(STDOUT, $loop));
|
|
|
88 |
$connection->write("Hello World!\n");
|
|
|
89 |
});
|
|
|
90 |
|
|
|
91 |
$loop->run();
|
|
|
92 |
```
|
|
|
93 |
|
|
|
94 |
## Connection usage
|
|
|
95 |
|
|
|
96 |
### ConnectionInterface
|
|
|
97 |
|
|
|
98 |
The `ConnectionInterface` is used to represent any incoming and outgoing
|
|
|
99 |
connection, such as a normal TCP/IP connection.
|
|
|
100 |
|
|
|
101 |
An incoming or outgoing connection is a duplex stream (both readable and
|
|
|
102 |
writable) that implements React's
|
|
|
103 |
[`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface).
|
|
|
104 |
It contains additional properties for the local and remote address (client IP)
|
|
|
105 |
where this connection has been established to/from.
|
|
|
106 |
|
|
|
107 |
Most commonly, instances implementing this `ConnectionInterface` are emitted
|
|
|
108 |
by all classes implementing the [`ServerInterface`](#serverinterface) and
|
|
|
109 |
used by all classes implementing the [`ConnectorInterface`](#connectorinterface).
|
|
|
110 |
|
|
|
111 |
Because the `ConnectionInterface` implements the underlying
|
|
|
112 |
[`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface)
|
|
|
113 |
you can use any of its events and methods as usual:
|
|
|
114 |
|
|
|
115 |
```php
|
|
|
116 |
$connection->on('data', function ($chunk) {
|
|
|
117 |
echo $chunk;
|
|
|
118 |
});
|
|
|
119 |
|
|
|
120 |
$connection->on('end', function () {
|
|
|
121 |
echo 'ended';
|
|
|
122 |
});
|
|
|
123 |
|
|
|
124 |
$connection->on('error', function (Exception $e) {
|
|
|
125 |
echo 'error: ' . $e->getMessage();
|
|
|
126 |
});
|
|
|
127 |
|
|
|
128 |
$connection->on('close', function () {
|
|
|
129 |
echo 'closed';
|
|
|
130 |
});
|
|
|
131 |
|
|
|
132 |
$connection->write($data);
|
|
|
133 |
$connection->end($data = null);
|
|
|
134 |
$connection->close();
|
|
|
135 |
// …
|
|
|
136 |
```
|
|
|
137 |
|
|
|
138 |
For more details, see the
|
|
|
139 |
[`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface).
|
|
|
140 |
|
|
|
141 |
#### getRemoteAddress()
|
|
|
142 |
|
|
|
143 |
The `getRemoteAddress(): ?string` method returns the full remote address
|
|
|
144 |
(URI) where this connection has been established with.
|
|
|
145 |
|
|
|
146 |
```php
|
|
|
147 |
$address = $connection->getRemoteAddress();
|
|
|
148 |
echo 'Connection with ' . $address . PHP_EOL;
|
|
|
149 |
```
|
|
|
150 |
|
|
|
151 |
If the remote address can not be determined or is unknown at this time (such as
|
|
|
152 |
after the connection has been closed), it MAY return a `NULL` value instead.
|
|
|
153 |
|
|
|
154 |
Otherwise, it will return the full address (URI) as a string value, such
|
|
|
155 |
as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`,
|
|
|
156 |
`unix://example.sock` or `unix:///path/to/example.sock`.
|
|
|
157 |
Note that individual URI components are application specific and depend
|
|
|
158 |
on the underlying transport protocol.
|
|
|
159 |
|
|
|
160 |
If this is a TCP/IP based connection and you only want the remote IP, you may
|
|
|
161 |
use something like this:
|
|
|
162 |
|
|
|
163 |
```php
|
|
|
164 |
$address = $connection->getRemoteAddress();
|
|
|
165 |
$ip = trim(parse_url($address, PHP_URL_HOST), '[]');
|
|
|
166 |
echo 'Connection with ' . $ip . PHP_EOL;
|
|
|
167 |
```
|
|
|
168 |
|
|
|
169 |
#### getLocalAddress()
|
|
|
170 |
|
|
|
171 |
The `getLocalAddress(): ?string` method returns the full local address
|
|
|
172 |
(URI) where this connection has been established with.
|
|
|
173 |
|
|
|
174 |
```php
|
|
|
175 |
$address = $connection->getLocalAddress();
|
|
|
176 |
echo 'Connection with ' . $address . PHP_EOL;
|
|
|
177 |
```
|
|
|
178 |
|
|
|
179 |
If the local address can not be determined or is unknown at this time (such as
|
|
|
180 |
after the connection has been closed), it MAY return a `NULL` value instead.
|
|
|
181 |
|
|
|
182 |
Otherwise, it will return the full address (URI) as a string value, such
|
|
|
183 |
as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`,
|
|
|
184 |
`unix://example.sock` or `unix:///path/to/example.sock`.
|
|
|
185 |
Note that individual URI components are application specific and depend
|
|
|
186 |
on the underlying transport protocol.
|
|
|
187 |
|
|
|
188 |
This method complements the [`getRemoteAddress()`](#getremoteaddress) method,
|
|
|
189 |
so they should not be confused.
|
|
|
190 |
|
|
|
191 |
If your `TcpServer` instance is listening on multiple interfaces (e.g. using
|
|
|
192 |
the address `0.0.0.0`), you can use this method to find out which interface
|
|
|
193 |
actually accepted this connection (such as a public or local interface).
|
|
|
194 |
|
|
|
195 |
If your system has multiple interfaces (e.g. a WAN and a LAN interface),
|
|
|
196 |
you can use this method to find out which interface was actually
|
|
|
197 |
used for this connection.
|
|
|
198 |
|
|
|
199 |
## Server usage
|
|
|
200 |
|
|
|
201 |
### ServerInterface
|
|
|
202 |
|
|
|
203 |
The `ServerInterface` is responsible for providing an interface for accepting
|
|
|
204 |
incoming streaming connections, such as a normal TCP/IP connection.
|
|
|
205 |
|
|
|
206 |
Most higher-level components (such as a HTTP server) accept an instance
|
|
|
207 |
implementing this interface to accept incoming streaming connections.
|
|
|
208 |
This is usually done via dependency injection, so it's fairly simple to actually
|
|
|
209 |
swap this implementation against any other implementation of this interface.
|
|
|
210 |
This means that you SHOULD typehint against this interface instead of a concrete
|
|
|
211 |
implementation of this interface.
|
|
|
212 |
|
|
|
213 |
Besides defining a few methods, this interface also implements the
|
|
|
214 |
[`EventEmitterInterface`](https://github.com/igorw/evenement)
|
|
|
215 |
which allows you to react to certain events.
|
|
|
216 |
|
|
|
217 |
#### connection event
|
|
|
218 |
|
|
|
219 |
The `connection` event will be emitted whenever a new connection has been
|
|
|
220 |
established, i.e. a new client connects to this server socket:
|
|
|
221 |
|
|
|
222 |
```php
|
|
|
223 |
$server->on('connection', function (React\Socket\ConnectionInterface $connection) {
|
|
|
224 |
echo 'new connection' . PHP_EOL;
|
|
|
225 |
});
|
|
|
226 |
```
|
|
|
227 |
|
|
|
228 |
See also the [`ConnectionInterface`](#connectioninterface) for more details
|
|
|
229 |
about handling the incoming connection.
|
|
|
230 |
|
|
|
231 |
#### error event
|
|
|
232 |
|
|
|
233 |
The `error` event will be emitted whenever there's an error accepting a new
|
|
|
234 |
connection from a client.
|
|
|
235 |
|
|
|
236 |
```php
|
|
|
237 |
$server->on('error', function (Exception $e) {
|
|
|
238 |
echo 'error: ' . $e->getMessage() . PHP_EOL;
|
|
|
239 |
});
|
|
|
240 |
```
|
|
|
241 |
|
|
|
242 |
Note that this is not a fatal error event, i.e. the server keeps listening for
|
|
|
243 |
new connections even after this event.
|
|
|
244 |
|
|
|
245 |
|
|
|
246 |
#### getAddress()
|
|
|
247 |
|
|
|
248 |
The `getAddress(): ?string` method can be used to
|
|
|
249 |
return the full address (URI) this server is currently listening on.
|
|
|
250 |
|
|
|
251 |
```php
|
|
|
252 |
$address = $server->getAddress();
|
|
|
253 |
echo 'Server listening on ' . $address . PHP_EOL;
|
|
|
254 |
```
|
|
|
255 |
|
|
|
256 |
If the address can not be determined or is unknown at this time (such as
|
|
|
257 |
after the socket has been closed), it MAY return a `NULL` value instead.
|
|
|
258 |
|
|
|
259 |
Otherwise, it will return the full address (URI) as a string value, such
|
|
|
260 |
as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`
|
|
|
261 |
`unix://example.sock` or `unix:///path/to/example.sock`.
|
|
|
262 |
Note that individual URI components are application specific and depend
|
|
|
263 |
on the underlying transport protocol.
|
|
|
264 |
|
|
|
265 |
If this is a TCP/IP based server and you only want the local port, you may
|
|
|
266 |
use something like this:
|
|
|
267 |
|
|
|
268 |
```php
|
|
|
269 |
$address = $server->getAddress();
|
|
|
270 |
$port = parse_url($address, PHP_URL_PORT);
|
|
|
271 |
echo 'Server listening on port ' . $port . PHP_EOL;
|
|
|
272 |
```
|
|
|
273 |
|
|
|
274 |
#### pause()
|
|
|
275 |
|
|
|
276 |
The `pause(): void` method can be used to
|
|
|
277 |
pause accepting new incoming connections.
|
|
|
278 |
|
|
|
279 |
Removes the socket resource from the EventLoop and thus stop accepting
|
|
|
280 |
new connections. Note that the listening socket stays active and is not
|
|
|
281 |
closed.
|
|
|
282 |
|
|
|
283 |
This means that new incoming connections will stay pending in the
|
|
|
284 |
operating system backlog until its configurable backlog is filled.
|
|
|
285 |
Once the backlog is filled, the operating system may reject further
|
|
|
286 |
incoming connections until the backlog is drained again by resuming
|
|
|
287 |
to accept new connections.
|
|
|
288 |
|
|
|
289 |
Once the server is paused, no futher `connection` events SHOULD
|
|
|
290 |
be emitted.
|
|
|
291 |
|
|
|
292 |
```php
|
|
|
293 |
$server->pause();
|
|
|
294 |
|
|
|
295 |
$server->on('connection', assertShouldNeverCalled());
|
|
|
296 |
```
|
|
|
297 |
|
|
|
298 |
This method is advisory-only, though generally not recommended, the
|
|
|
299 |
server MAY continue emitting `connection` events.
|
|
|
300 |
|
|
|
301 |
Unless otherwise noted, a successfully opened server SHOULD NOT start
|
|
|
302 |
in paused state.
|
|
|
303 |
|
|
|
304 |
You can continue processing events by calling `resume()` again.
|
|
|
305 |
|
|
|
306 |
Note that both methods can be called any number of times, in particular
|
|
|
307 |
calling `pause()` more than once SHOULD NOT have any effect.
|
|
|
308 |
Similarly, calling this after `close()` is a NO-OP.
|
|
|
309 |
|
|
|
310 |
#### resume()
|
|
|
311 |
|
|
|
312 |
The `resume(): void` method can be used to
|
|
|
313 |
resume accepting new incoming connections.
|
|
|
314 |
|
|
|
315 |
Re-attach the socket resource to the EventLoop after a previous `pause()`.
|
|
|
316 |
|
|
|
317 |
```php
|
|
|
318 |
$server->pause();
|
|
|
319 |
|
|
|
320 |
$loop->addTimer(1.0, function () use ($server) {
|
|
|
321 |
$server->resume();
|
|
|
322 |
});
|
|
|
323 |
```
|
|
|
324 |
|
|
|
325 |
Note that both methods can be called any number of times, in particular
|
|
|
326 |
calling `resume()` without a prior `pause()` SHOULD NOT have any effect.
|
|
|
327 |
Similarly, calling this after `close()` is a NO-OP.
|
|
|
328 |
|
|
|
329 |
#### close()
|
|
|
330 |
|
|
|
331 |
The `close(): void` method can be used to
|
|
|
332 |
shut down this listening socket.
|
|
|
333 |
|
|
|
334 |
This will stop listening for new incoming connections on this socket.
|
|
|
335 |
|
|
|
336 |
```php
|
|
|
337 |
echo 'Shutting down server socket' . PHP_EOL;
|
|
|
338 |
$server->close();
|
|
|
339 |
```
|
|
|
340 |
|
|
|
341 |
Calling this method more than once on the same instance is a NO-OP.
|
|
|
342 |
|
|
|
343 |
### Server
|
|
|
344 |
|
|
|
345 |
The `Server` class is the main class in this package that implements the
|
|
|
346 |
[`ServerInterface`](#serverinterface) and allows you to accept incoming
|
|
|
347 |
streaming connections, such as plaintext TCP/IP or secure TLS connection streams.
|
|
|
348 |
Connections can also be accepted on Unix domain sockets.
|
|
|
349 |
|
|
|
350 |
```php
|
|
|
351 |
$server = new React\Socket\Server(8080, $loop);
|
|
|
352 |
```
|
|
|
353 |
|
|
|
354 |
As above, the `$uri` parameter can consist of only a port, in which case the
|
|
|
355 |
server will default to listening on the localhost address `127.0.0.1`,
|
|
|
356 |
which means it will not be reachable from outside of this system.
|
|
|
357 |
|
|
|
358 |
In order to use a random port assignment, you can use the port `0`:
|
|
|
359 |
|
|
|
360 |
```php
|
|
|
361 |
$server = new React\Socket\Server(0, $loop);
|
|
|
362 |
$address = $server->getAddress();
|
|
|
363 |
```
|
|
|
364 |
|
|
|
365 |
In order to change the host the socket is listening on, you can provide an IP
|
|
|
366 |
address through the first parameter provided to the constructor, optionally
|
|
|
367 |
preceded by the `tcp://` scheme:
|
|
|
368 |
|
|
|
369 |
```php
|
|
|
370 |
$server = new React\Socket\Server('192.168.0.1:8080', $loop);
|
|
|
371 |
```
|
|
|
372 |
|
|
|
373 |
If you want to listen on an IPv6 address, you MUST enclose the host in square
|
|
|
374 |
brackets:
|
|
|
375 |
|
|
|
376 |
```php
|
|
|
377 |
$server = new React\Socket\Server('[::1]:8080', $loop);
|
|
|
378 |
```
|
|
|
379 |
|
|
|
380 |
To listen on a Unix domain socket (UDS) path, you MUST prefix the URI with the
|
|
|
381 |
`unix://` scheme:
|
|
|
382 |
|
|
|
383 |
```php
|
|
|
384 |
$server = new React\Socket\Server('unix:///tmp/server.sock', $loop);
|
|
|
385 |
```
|
|
|
386 |
|
|
|
387 |
If the given URI is invalid, does not contain a port, any other scheme or if it
|
|
|
388 |
contains a hostname, it will throw an `InvalidArgumentException`:
|
|
|
389 |
|
|
|
390 |
```php
|
|
|
391 |
// throws InvalidArgumentException due to missing port
|
|
|
392 |
$server = new React\Socket\Server('127.0.0.1', $loop);
|
|
|
393 |
```
|
|
|
394 |
|
|
|
395 |
If the given URI appears to be valid, but listening on it fails (such as if port
|
|
|
396 |
is already in use or port below 1024 may require root access etc.), it will
|
|
|
397 |
throw a `RuntimeException`:
|
|
|
398 |
|
|
|
399 |
```php
|
|
|
400 |
$first = new React\Socket\Server(8080, $loop);
|
|
|
401 |
|
|
|
402 |
// throws RuntimeException because port is already in use
|
|
|
403 |
$second = new React\Socket\Server(8080, $loop);
|
|
|
404 |
```
|
|
|
405 |
|
|
|
406 |
> Note that these error conditions may vary depending on your system and/or
|
|
|
407 |
configuration.
|
|
|
408 |
See the exception message and code for more details about the actual error
|
|
|
409 |
condition.
|
|
|
410 |
|
|
|
411 |
Optionally, you can specify [TCP socket context options](https://www.php.net/manual/en/context.socket.php)
|
|
|
412 |
for the underlying stream socket resource like this:
|
|
|
413 |
|
|
|
414 |
```php
|
|
|
415 |
$server = new React\Socket\Server('[::1]:8080', $loop, array(
|
|
|
416 |
'tcp' => array(
|
|
|
417 |
'backlog' => 200,
|
|
|
418 |
'so_reuseport' => true,
|
|
|
419 |
'ipv6_v6only' => true
|
|
|
420 |
)
|
|
|
421 |
));
|
|
|
422 |
```
|
|
|
423 |
|
|
|
424 |
> Note that available [socket context options](https://www.php.net/manual/en/context.socket.php),
|
|
|
425 |
their defaults and effects of changing these may vary depending on your system
|
|
|
426 |
and/or PHP version.
|
|
|
427 |
Passing unknown context options has no effect.
|
|
|
428 |
The `backlog` context option defaults to `511` unless given explicitly.
|
|
|
429 |
For BC reasons, you can also pass the TCP socket context options as a simple
|
|
|
430 |
array without wrapping this in another array under the `tcp` key.
|
|
|
431 |
|
|
|
432 |
You can start a secure TLS (formerly known as SSL) server by simply prepending
|
|
|
433 |
the `tls://` URI scheme.
|
|
|
434 |
Internally, it will wait for plaintext TCP/IP connections and then performs a
|
|
|
435 |
TLS handshake for each connection.
|
|
|
436 |
It thus requires valid [TLS context options](https://www.php.net/manual/en/context.ssl.php),
|
|
|
437 |
which in its most basic form may look something like this if you're using a
|
|
|
438 |
PEM encoded certificate file:
|
|
|
439 |
|
|
|
440 |
```php
|
|
|
441 |
$server = new React\Socket\Server('tls://127.0.0.1:8080', $loop, array(
|
|
|
442 |
'tls' => array(
|
|
|
443 |
'local_cert' => 'server.pem'
|
|
|
444 |
)
|
|
|
445 |
));
|
|
|
446 |
```
|
|
|
447 |
|
|
|
448 |
> Note that the certificate file will not be loaded on instantiation but when an
|
|
|
449 |
incoming connection initializes its TLS context.
|
|
|
450 |
This implies that any invalid certificate file paths or contents will only cause
|
|
|
451 |
an `error` event at a later time.
|
|
|
452 |
|
|
|
453 |
If your private key is encrypted with a passphrase, you have to specify it
|
|
|
454 |
like this:
|
|
|
455 |
|
|
|
456 |
```php
|
|
|
457 |
$server = new React\Socket\Server('tls://127.0.0.1:8000', $loop, array(
|
|
|
458 |
'tls' => array(
|
|
|
459 |
'local_cert' => 'server.pem',
|
|
|
460 |
'passphrase' => 'secret'
|
|
|
461 |
)
|
|
|
462 |
));
|
|
|
463 |
```
|
|
|
464 |
|
|
|
465 |
By default, this server supports TLSv1.0+ and excludes support for legacy
|
|
|
466 |
SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you
|
|
|
467 |
want to negotiate with the remote side:
|
|
|
468 |
|
|
|
469 |
```php
|
|
|
470 |
$server = new React\Socket\Server('tls://127.0.0.1:8000', $loop, array(
|
|
|
471 |
'tls' => array(
|
|
|
472 |
'local_cert' => 'server.pem',
|
|
|
473 |
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER
|
|
|
474 |
)
|
|
|
475 |
));
|
|
|
476 |
```
|
|
|
477 |
|
|
|
478 |
> Note that available [TLS context options](https://www.php.net/manual/en/context.ssl.php),
|
|
|
479 |
their defaults and effects of changing these may vary depending on your system
|
|
|
480 |
and/or PHP version.
|
|
|
481 |
The outer context array allows you to also use `tcp` (and possibly more)
|
|
|
482 |
context options at the same time.
|
|
|
483 |
Passing unknown context options has no effect.
|
|
|
484 |
If you do not use the `tls://` scheme, then passing `tls` context options
|
|
|
485 |
has no effect.
|
|
|
486 |
|
|
|
487 |
Whenever a client connects, it will emit a `connection` event with a connection
|
|
|
488 |
instance implementing [`ConnectionInterface`](#connectioninterface):
|
|
|
489 |
|
|
|
490 |
```php
|
|
|
491 |
$server->on('connection', function (React\Socket\ConnectionInterface $connection) {
|
|
|
492 |
echo 'Plaintext connection from ' . $connection->getRemoteAddress() . PHP_EOL;
|
|
|
493 |
|
|
|
494 |
$connection->write('hello there!' . PHP_EOL);
|
|
|
495 |
…
|
|
|
496 |
});
|
|
|
497 |
```
|
|
|
498 |
|
|
|
499 |
See also the [`ServerInterface`](#serverinterface) for more details.
|
|
|
500 |
|
|
|
501 |
> Note that the `Server` class is a concrete implementation for TCP/IP sockets.
|
|
|
502 |
If you want to typehint in your higher-level protocol implementation, you SHOULD
|
|
|
503 |
use the generic [`ServerInterface`](#serverinterface) instead.
|
|
|
504 |
|
|
|
505 |
### Advanced server usage
|
|
|
506 |
|
|
|
507 |
#### TcpServer
|
|
|
508 |
|
|
|
509 |
The `TcpServer` class implements the [`ServerInterface`](#serverinterface) and
|
|
|
510 |
is responsible for accepting plaintext TCP/IP connections.
|
|
|
511 |
|
|
|
512 |
```php
|
|
|
513 |
$server = new React\Socket\TcpServer(8080, $loop);
|
|
|
514 |
```
|
|
|
515 |
|
|
|
516 |
As above, the `$uri` parameter can consist of only a port, in which case the
|
|
|
517 |
server will default to listening on the localhost address `127.0.0.1`,
|
|
|
518 |
which means it will not be reachable from outside of this system.
|
|
|
519 |
|
|
|
520 |
In order to use a random port assignment, you can use the port `0`:
|
|
|
521 |
|
|
|
522 |
```php
|
|
|
523 |
$server = new React\Socket\TcpServer(0, $loop);
|
|
|
524 |
$address = $server->getAddress();
|
|
|
525 |
```
|
|
|
526 |
|
|
|
527 |
In order to change the host the socket is listening on, you can provide an IP
|
|
|
528 |
address through the first parameter provided to the constructor, optionally
|
|
|
529 |
preceded by the `tcp://` scheme:
|
|
|
530 |
|
|
|
531 |
```php
|
|
|
532 |
$server = new React\Socket\TcpServer('192.168.0.1:8080', $loop);
|
|
|
533 |
```
|
|
|
534 |
|
|
|
535 |
If you want to listen on an IPv6 address, you MUST enclose the host in square
|
|
|
536 |
brackets:
|
|
|
537 |
|
|
|
538 |
```php
|
|
|
539 |
$server = new React\Socket\TcpServer('[::1]:8080', $loop);
|
|
|
540 |
```
|
|
|
541 |
|
|
|
542 |
If the given URI is invalid, does not contain a port, any other scheme or if it
|
|
|
543 |
contains a hostname, it will throw an `InvalidArgumentException`:
|
|
|
544 |
|
|
|
545 |
```php
|
|
|
546 |
// throws InvalidArgumentException due to missing port
|
|
|
547 |
$server = new React\Socket\TcpServer('127.0.0.1', $loop);
|
|
|
548 |
```
|
|
|
549 |
|
|
|
550 |
If the given URI appears to be valid, but listening on it fails (such as if port
|
|
|
551 |
is already in use or port below 1024 may require root access etc.), it will
|
|
|
552 |
throw a `RuntimeException`:
|
|
|
553 |
|
|
|
554 |
```php
|
|
|
555 |
$first = new React\Socket\TcpServer(8080, $loop);
|
|
|
556 |
|
|
|
557 |
// throws RuntimeException because port is already in use
|
|
|
558 |
$second = new React\Socket\TcpServer(8080, $loop);
|
|
|
559 |
```
|
|
|
560 |
|
|
|
561 |
> Note that these error conditions may vary depending on your system and/or
|
|
|
562 |
configuration.
|
|
|
563 |
See the exception message and code for more details about the actual error
|
|
|
564 |
condition.
|
|
|
565 |
|
|
|
566 |
Optionally, you can specify [socket context options](https://www.php.net/manual/en/context.socket.php)
|
|
|
567 |
for the underlying stream socket resource like this:
|
|
|
568 |
|
|
|
569 |
```php
|
|
|
570 |
$server = new React\Socket\TcpServer('[::1]:8080', $loop, array(
|
|
|
571 |
'backlog' => 200,
|
|
|
572 |
'so_reuseport' => true,
|
|
|
573 |
'ipv6_v6only' => true
|
|
|
574 |
));
|
|
|
575 |
```
|
|
|
576 |
|
|
|
577 |
> Note that available [socket context options](https://www.php.net/manual/en/context.socket.php),
|
|
|
578 |
their defaults and effects of changing these may vary depending on your system
|
|
|
579 |
and/or PHP version.
|
|
|
580 |
Passing unknown context options has no effect.
|
|
|
581 |
The `backlog` context option defaults to `511` unless given explicitly.
|
|
|
582 |
|
|
|
583 |
Whenever a client connects, it will emit a `connection` event with a connection
|
|
|
584 |
instance implementing [`ConnectionInterface`](#connectioninterface):
|
|
|
585 |
|
|
|
586 |
```php
|
|
|
587 |
$server->on('connection', function (React\Socket\ConnectionInterface $connection) {
|
|
|
588 |
echo 'Plaintext connection from ' . $connection->getRemoteAddress() . PHP_EOL;
|
|
|
589 |
|
|
|
590 |
$connection->write('hello there!' . PHP_EOL);
|
|
|
591 |
…
|
|
|
592 |
});
|
|
|
593 |
```
|
|
|
594 |
|
|
|
595 |
See also the [`ServerInterface`](#serverinterface) for more details.
|
|
|
596 |
|
|
|
597 |
#### SecureServer
|
|
|
598 |
|
|
|
599 |
The `SecureServer` class implements the [`ServerInterface`](#serverinterface)
|
|
|
600 |
and is responsible for providing a secure TLS (formerly known as SSL) server.
|
|
|
601 |
|
|
|
602 |
It does so by wrapping a [`TcpServer`](#tcpserver) instance which waits for plaintext
|
|
|
603 |
TCP/IP connections and then performs a TLS handshake for each connection.
|
|
|
604 |
It thus requires valid [TLS context options](https://www.php.net/manual/en/context.ssl.php),
|
|
|
605 |
which in its most basic form may look something like this if you're using a
|
|
|
606 |
PEM encoded certificate file:
|
|
|
607 |
|
|
|
608 |
```php
|
|
|
609 |
$server = new React\Socket\TcpServer(8000, $loop);
|
|
|
610 |
$server = new React\Socket\SecureServer($server, $loop, array(
|
|
|
611 |
'local_cert' => 'server.pem'
|
|
|
612 |
));
|
|
|
613 |
```
|
|
|
614 |
|
|
|
615 |
> Note that the certificate file will not be loaded on instantiation but when an
|
|
|
616 |
incoming connection initializes its TLS context.
|
|
|
617 |
This implies that any invalid certificate file paths or contents will only cause
|
|
|
618 |
an `error` event at a later time.
|
|
|
619 |
|
|
|
620 |
If your private key is encrypted with a passphrase, you have to specify it
|
|
|
621 |
like this:
|
|
|
622 |
|
|
|
623 |
```php
|
|
|
624 |
$server = new React\Socket\TcpServer(8000, $loop);
|
|
|
625 |
$server = new React\Socket\SecureServer($server, $loop, array(
|
|
|
626 |
'local_cert' => 'server.pem',
|
|
|
627 |
'passphrase' => 'secret'
|
|
|
628 |
));
|
|
|
629 |
```
|
|
|
630 |
|
|
|
631 |
By default, this server supports TLSv1.0+ and excludes support for legacy
|
|
|
632 |
SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you
|
|
|
633 |
want to negotiate with the remote side:
|
|
|
634 |
|
|
|
635 |
```php
|
|
|
636 |
$server = new React\Socket\TcpServer(8000, $loop);
|
|
|
637 |
$server = new React\Socket\SecureServer($server, $loop, array(
|
|
|
638 |
'local_cert' => 'server.pem',
|
|
|
639 |
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER
|
|
|
640 |
));
|
|
|
641 |
```
|
|
|
642 |
|
|
|
643 |
> Note that available [TLS context options](https://www.php.net/manual/en/context.ssl.php),
|
|
|
644 |
their defaults and effects of changing these may vary depending on your system
|
|
|
645 |
and/or PHP version.
|
|
|
646 |
Passing unknown context options has no effect.
|
|
|
647 |
|
|
|
648 |
Whenever a client completes the TLS handshake, it will emit a `connection` event
|
|
|
649 |
with a connection instance implementing [`ConnectionInterface`](#connectioninterface):
|
|
|
650 |
|
|
|
651 |
```php
|
|
|
652 |
$server->on('connection', function (React\Socket\ConnectionInterface $connection) {
|
|
|
653 |
echo 'Secure connection from' . $connection->getRemoteAddress() . PHP_EOL;
|
|
|
654 |
|
|
|
655 |
$connection->write('hello there!' . PHP_EOL);
|
|
|
656 |
…
|
|
|
657 |
});
|
|
|
658 |
```
|
|
|
659 |
|
|
|
660 |
Whenever a client fails to perform a successful TLS handshake, it will emit an
|
|
|
661 |
`error` event and then close the underlying TCP/IP connection:
|
|
|
662 |
|
|
|
663 |
```php
|
|
|
664 |
$server->on('error', function (Exception $e) {
|
|
|
665 |
echo 'Error' . $e->getMessage() . PHP_EOL;
|
|
|
666 |
});
|
|
|
667 |
```
|
|
|
668 |
|
|
|
669 |
See also the [`ServerInterface`](#serverinterface) for more details.
|
|
|
670 |
|
|
|
671 |
Note that the `SecureServer` class is a concrete implementation for TLS sockets.
|
|
|
672 |
If you want to typehint in your higher-level protocol implementation, you SHOULD
|
|
|
673 |
use the generic [`ServerInterface`](#serverinterface) instead.
|
|
|
674 |
|
|
|
675 |
> Advanced usage: Despite allowing any `ServerInterface` as first parameter,
|
|
|
676 |
you SHOULD pass a `TcpServer` instance as first parameter, unless you
|
|
|
677 |
know what you're doing.
|
|
|
678 |
Internally, the `SecureServer` has to set the required TLS context options on
|
|
|
679 |
the underlying stream resources.
|
|
|
680 |
These resources are not exposed through any of the interfaces defined in this
|
|
|
681 |
package, but only through the internal `Connection` class.
|
|
|
682 |
The `TcpServer` class is guaranteed to emit connections that implement
|
|
|
683 |
the `ConnectionInterface` and uses the internal `Connection` class in order to
|
|
|
684 |
expose these underlying resources.
|
|
|
685 |
If you use a custom `ServerInterface` and its `connection` event does not
|
|
|
686 |
meet this requirement, the `SecureServer` will emit an `error` event and
|
|
|
687 |
then close the underlying connection.
|
|
|
688 |
|
|
|
689 |
#### UnixServer
|
|
|
690 |
|
|
|
691 |
The `UnixServer` class implements the [`ServerInterface`](#serverinterface) and
|
|
|
692 |
is responsible for accepting connections on Unix domain sockets (UDS).
|
|
|
693 |
|
|
|
694 |
```php
|
|
|
695 |
$server = new React\Socket\UnixServer('/tmp/server.sock', $loop);
|
|
|
696 |
```
|
|
|
697 |
|
|
|
698 |
As above, the `$uri` parameter can consist of only a socket path or socket path
|
|
|
699 |
prefixed by the `unix://` scheme.
|
|
|
700 |
|
|
|
701 |
If the given URI appears to be valid, but listening on it fails (such as if the
|
|
|
702 |
socket is already in use or the file not accessible etc.), it will throw a
|
|
|
703 |
`RuntimeException`:
|
|
|
704 |
|
|
|
705 |
```php
|
|
|
706 |
$first = new React\Socket\UnixServer('/tmp/same.sock', $loop);
|
|
|
707 |
|
|
|
708 |
// throws RuntimeException because socket is already in use
|
|
|
709 |
$second = new React\Socket\UnixServer('/tmp/same.sock', $loop);
|
|
|
710 |
```
|
|
|
711 |
|
|
|
712 |
> Note that these error conditions may vary depending on your system and/or
|
|
|
713 |
configuration.
|
|
|
714 |
In particular, Zend PHP does only report "Unknown error" when the UDS path
|
|
|
715 |
already exists and can not be bound. You may want to check `is_file()` on the
|
|
|
716 |
given UDS path to report a more user-friendly error message in this case.
|
|
|
717 |
See the exception message and code for more details about the actual error
|
|
|
718 |
condition.
|
|
|
719 |
|
|
|
720 |
Whenever a client connects, it will emit a `connection` event with a connection
|
|
|
721 |
instance implementing [`ConnectionInterface`](#connectioninterface):
|
|
|
722 |
|
|
|
723 |
```php
|
|
|
724 |
$server->on('connection', function (React\Socket\ConnectionInterface $connection) {
|
|
|
725 |
echo 'New connection' . PHP_EOL;
|
|
|
726 |
|
|
|
727 |
$connection->write('hello there!' . PHP_EOL);
|
|
|
728 |
…
|
|
|
729 |
});
|
|
|
730 |
```
|
|
|
731 |
|
|
|
732 |
See also the [`ServerInterface`](#serverinterface) for more details.
|
|
|
733 |
|
|
|
734 |
#### LimitingServer
|
|
|
735 |
|
|
|
736 |
The `LimitingServer` decorator wraps a given `ServerInterface` and is responsible
|
|
|
737 |
for limiting and keeping track of open connections to this server instance.
|
|
|
738 |
|
|
|
739 |
Whenever the underlying server emits a `connection` event, it will check its
|
|
|
740 |
limits and then either
|
|
|
741 |
- keep track of this connection by adding it to the list of
|
|
|
742 |
open connections and then forward the `connection` event
|
|
|
743 |
- or reject (close) the connection when its limits are exceeded and will
|
|
|
744 |
forward an `error` event instead.
|
|
|
745 |
|
|
|
746 |
Whenever a connection closes, it will remove this connection from the list of
|
|
|
747 |
open connections.
|
|
|
748 |
|
|
|
749 |
```php
|
|
|
750 |
$server = new React\Socket\LimitingServer($server, 100);
|
|
|
751 |
$server->on('connection', function (React\Socket\ConnectionInterface $connection) {
|
|
|
752 |
$connection->write('hello there!' . PHP_EOL);
|
|
|
753 |
…
|
|
|
754 |
});
|
|
|
755 |
```
|
|
|
756 |
|
|
|
757 |
See also the [second example](examples) for more details.
|
|
|
758 |
|
|
|
759 |
You have to pass a maximum number of open connections to ensure
|
|
|
760 |
the server will automatically reject (close) connections once this limit
|
|
|
761 |
is exceeded. In this case, it will emit an `error` event to inform about
|
|
|
762 |
this and no `connection` event will be emitted.
|
|
|
763 |
|
|
|
764 |
```php
|
|
|
765 |
$server = new React\Socket\LimitingServer($server, 100);
|
|
|
766 |
$server->on('connection', function (React\Socket\ConnectionInterface $connection) {
|
|
|
767 |
$connection->write('hello there!' . PHP_EOL);
|
|
|
768 |
…
|
|
|
769 |
});
|
|
|
770 |
```
|
|
|
771 |
|
|
|
772 |
You MAY pass a `null` limit in order to put no limit on the number of
|
|
|
773 |
open connections and keep accepting new connection until you run out of
|
|
|
774 |
operating system resources (such as open file handles). This may be
|
|
|
775 |
useful if you do not want to take care of applying a limit but still want
|
|
|
776 |
to use the `getConnections()` method.
|
|
|
777 |
|
|
|
778 |
You can optionally configure the server to pause accepting new
|
|
|
779 |
connections once the connection limit is reached. In this case, it will
|
|
|
780 |
pause the underlying server and no longer process any new connections at
|
|
|
781 |
all, thus also no longer closing any excessive connections.
|
|
|
782 |
The underlying operating system is responsible for keeping a backlog of
|
|
|
783 |
pending connections until its limit is reached, at which point it will
|
|
|
784 |
start rejecting further connections.
|
|
|
785 |
Once the server is below the connection limit, it will continue consuming
|
|
|
786 |
connections from the backlog and will process any outstanding data on
|
|
|
787 |
each connection.
|
|
|
788 |
This mode may be useful for some protocols that are designed to wait for
|
|
|
789 |
a response message (such as HTTP), but may be less useful for other
|
|
|
790 |
protocols that demand immediate responses (such as a "welcome" message in
|
|
|
791 |
an interactive chat).
|
|
|
792 |
|
|
|
793 |
```php
|
|
|
794 |
$server = new React\Socket\LimitingServer($server, 100, true);
|
|
|
795 |
$server->on('connection', function (React\Socket\ConnectionInterface $connection) {
|
|
|
796 |
$connection->write('hello there!' . PHP_EOL);
|
|
|
797 |
…
|
|
|
798 |
});
|
|
|
799 |
```
|
|
|
800 |
|
|
|
801 |
##### getConnections()
|
|
|
802 |
|
|
|
803 |
The `getConnections(): ConnectionInterface[]` method can be used to
|
|
|
804 |
return an array with all currently active connections.
|
|
|
805 |
|
|
|
806 |
```php
|
|
|
807 |
foreach ($server->getConnection() as $connection) {
|
|
|
808 |
$connection->write('Hi!');
|
|
|
809 |
}
|
|
|
810 |
```
|
|
|
811 |
|
|
|
812 |
## Client usage
|
|
|
813 |
|
|
|
814 |
### ConnectorInterface
|
|
|
815 |
|
|
|
816 |
The `ConnectorInterface` is responsible for providing an interface for
|
|
|
817 |
establishing streaming connections, such as a normal TCP/IP connection.
|
|
|
818 |
|
|
|
819 |
This is the main interface defined in this package and it is used throughout
|
|
|
820 |
React's vast ecosystem.
|
|
|
821 |
|
|
|
822 |
Most higher-level components (such as HTTP, database or other networking
|
|
|
823 |
service clients) accept an instance implementing this interface to create their
|
|
|
824 |
TCP/IP connection to the underlying networking service.
|
|
|
825 |
This is usually done via dependency injection, so it's fairly simple to actually
|
|
|
826 |
swap this implementation against any other implementation of this interface.
|
|
|
827 |
|
|
|
828 |
The interface only offers a single method:
|
|
|
829 |
|
|
|
830 |
#### connect()
|
|
|
831 |
|
|
|
832 |
The `connect(string $uri): PromiseInterface<ConnectionInterface,Exception>` method
|
|
|
833 |
can be used to create a streaming connection to the given remote address.
|
|
|
834 |
|
|
|
835 |
It returns a [Promise](https://github.com/reactphp/promise) which either
|
|
|
836 |
fulfills with a stream implementing [`ConnectionInterface`](#connectioninterface)
|
|
|
837 |
on success or rejects with an `Exception` if the connection is not successful:
|
|
|
838 |
|
|
|
839 |
```php
|
|
|
840 |
$connector->connect('google.com:443')->then(
|
|
|
841 |
function (React\Socket\ConnectionInterface $connection) {
|
|
|
842 |
// connection successfully established
|
|
|
843 |
},
|
|
|
844 |
function (Exception $error) {
|
|
|
845 |
// failed to connect due to $error
|
|
|
846 |
}
|
|
|
847 |
);
|
|
|
848 |
```
|
|
|
849 |
|
|
|
850 |
See also [`ConnectionInterface`](#connectioninterface) for more details.
|
|
|
851 |
|
|
|
852 |
The returned Promise MUST be implemented in such a way that it can be
|
|
|
853 |
cancelled when it is still pending. Cancelling a pending promise MUST
|
|
|
854 |
reject its value with an `Exception`. It SHOULD clean up any underlying
|
|
|
855 |
resources and references as applicable:
|
|
|
856 |
|
|
|
857 |
```php
|
|
|
858 |
$promise = $connector->connect($uri);
|
|
|
859 |
|
|
|
860 |
$promise->cancel();
|
|
|
861 |
```
|
|
|
862 |
|
|
|
863 |
### Connector
|
|
|
864 |
|
|
|
865 |
The `Connector` class is the main class in this package that implements the
|
|
|
866 |
[`ConnectorInterface`](#connectorinterface) and allows you to create streaming connections.
|
|
|
867 |
|
|
|
868 |
You can use this connector to create any kind of streaming connections, such
|
|
|
869 |
as plaintext TCP/IP, secure TLS or local Unix connection streams.
|
|
|
870 |
|
|
|
871 |
It binds to the main event loop and can be used like this:
|
|
|
872 |
|
|
|
873 |
```php
|
|
|
874 |
$loop = React\EventLoop\Factory::create();
|
|
|
875 |
$connector = new React\Socket\Connector($loop);
|
|
|
876 |
|
|
|
877 |
$connector->connect($uri)->then(function (React\Socket\ConnectionInterface $connection) {
|
|
|
878 |
$connection->write('...');
|
|
|
879 |
$connection->end();
|
|
|
880 |
});
|
|
|
881 |
|
|
|
882 |
$loop->run();
|
|
|
883 |
```
|
|
|
884 |
|
|
|
885 |
In order to create a plaintext TCP/IP connection, you can simply pass a host
|
|
|
886 |
and port combination like this:
|
|
|
887 |
|
|
|
888 |
```php
|
|
|
889 |
$connector->connect('www.google.com:80')->then(function (React\Socket\ConnectionInterface $connection) {
|
|
|
890 |
$connection->write('...');
|
|
|
891 |
$connection->end();
|
|
|
892 |
});
|
|
|
893 |
```
|
|
|
894 |
|
|
|
895 |
> If you do no specify a URI scheme in the destination URI, it will assume
|
|
|
896 |
`tcp://` as a default and establish a plaintext TCP/IP connection.
|
|
|
897 |
Note that TCP/IP connections require a host and port part in the destination
|
|
|
898 |
URI like above, all other URI components are optional.
|
|
|
899 |
|
|
|
900 |
In order to create a secure TLS connection, you can use the `tls://` URI scheme
|
|
|
901 |
like this:
|
|
|
902 |
|
|
|
903 |
```php
|
|
|
904 |
$connector->connect('tls://www.google.com:443')->then(function (React\Socket\ConnectionInterface $connection) {
|
|
|
905 |
$connection->write('...');
|
|
|
906 |
$connection->end();
|
|
|
907 |
});
|
|
|
908 |
```
|
|
|
909 |
|
|
|
910 |
In order to create a local Unix domain socket connection, you can use the
|
|
|
911 |
`unix://` URI scheme like this:
|
|
|
912 |
|
|
|
913 |
```php
|
|
|
914 |
$connector->connect('unix:///tmp/demo.sock')->then(function (React\Socket\ConnectionInterface $connection) {
|
|
|
915 |
$connection->write('...');
|
|
|
916 |
$connection->end();
|
|
|
917 |
});
|
|
|
918 |
```
|
|
|
919 |
|
|
|
920 |
> The [`getRemoteAddress()`](#getremoteaddress) method will return the target
|
|
|
921 |
Unix domain socket (UDS) path as given to the `connect()` method, including
|
|
|
922 |
the `unix://` scheme, for example `unix:///tmp/demo.sock`.
|
|
|
923 |
The [`getLocalAddress()`](#getlocaladdress) method will most likely return a
|
|
|
924 |
`null` value as this value is not applicable to UDS connections here.
|
|
|
925 |
|
|
|
926 |
Under the hood, the `Connector` is implemented as a *higher-level facade*
|
|
|
927 |
for the lower-level connectors implemented in this package. This means it
|
|
|
928 |
also shares all of their features and implementation details.
|
|
|
929 |
If you want to typehint in your higher-level protocol implementation, you SHOULD
|
|
|
930 |
use the generic [`ConnectorInterface`](#connectorinterface) instead.
|
|
|
931 |
|
|
|
932 |
As of `v1.4.0`, the `Connector` class defaults to using the
|
|
|
933 |
[happy eyeballs algorithm](https://en.wikipedia.org/wiki/Happy_Eyeballs) to
|
|
|
934 |
automatically connect over IPv4 or IPv6 when a hostname is given.
|
|
|
935 |
This automatically attempts to connect using both IPv4 and IPv6 at the same time
|
|
|
936 |
(preferring IPv6), thus avoiding the usual problems faced by users with imperfect
|
|
|
937 |
IPv6 connections or setups.
|
|
|
938 |
If you want to revert to the old behavior of only doing an IPv4 lookup and
|
|
|
939 |
only attempt a single IPv4 connection, you can set up the `Connector` like this:
|
|
|
940 |
|
|
|
941 |
```php
|
|
|
942 |
$connector = new React\Socket\Connector($loop, array(
|
|
|
943 |
'happy_eyeballs' => false
|
|
|
944 |
));
|
|
|
945 |
```
|
|
|
946 |
|
|
|
947 |
Similarly, you can also affect the default DNS behavior as follows.
|
|
|
948 |
The `Connector` class will try to detect your system DNS settings (and uses
|
|
|
949 |
Google's public DNS server `8.8.8.8` as a fallback if unable to determine your
|
|
|
950 |
system settings) to resolve all public hostnames into underlying IP addresses by
|
|
|
951 |
default.
|
|
|
952 |
If you explicitly want to use a custom DNS server (such as a local DNS relay or
|
|
|
953 |
a company wide DNS server), you can set up the `Connector` like this:
|
|
|
954 |
|
|
|
955 |
```php
|
|
|
956 |
$connector = new React\Socket\Connector($loop, array(
|
|
|
957 |
'dns' => '127.0.1.1'
|
|
|
958 |
));
|
|
|
959 |
|
|
|
960 |
$connector->connect('localhost:80')->then(function (React\Socket\ConnectionInterface $connection) {
|
|
|
961 |
$connection->write('...');
|
|
|
962 |
$connection->end();
|
|
|
963 |
});
|
|
|
964 |
```
|
|
|
965 |
|
|
|
966 |
If you do not want to use a DNS resolver at all and want to connect to IP
|
|
|
967 |
addresses only, you can also set up your `Connector` like this:
|
|
|
968 |
|
|
|
969 |
```php
|
|
|
970 |
$connector = new React\Socket\Connector($loop, array(
|
|
|
971 |
'dns' => false
|
|
|
972 |
));
|
|
|
973 |
|
|
|
974 |
$connector->connect('127.0.0.1:80')->then(function (React\Socket\ConnectionInterface $connection) {
|
|
|
975 |
$connection->write('...');
|
|
|
976 |
$connection->end();
|
|
|
977 |
});
|
|
|
978 |
```
|
|
|
979 |
|
|
|
980 |
Advanced: If you need a custom DNS `React\Dns\Resolver\ResolverInterface` instance, you
|
|
|
981 |
can also set up your `Connector` like this:
|
|
|
982 |
|
|
|
983 |
```php
|
|
|
984 |
$dnsResolverFactory = new React\Dns\Resolver\Factory();
|
|
|
985 |
$resolver = $dnsResolverFactory->createCached('127.0.1.1', $loop);
|
|
|
986 |
|
|
|
987 |
$connector = new React\Socket\Connector($loop, array(
|
|
|
988 |
'dns' => $resolver
|
|
|
989 |
));
|
|
|
990 |
|
|
|
991 |
$connector->connect('localhost:80')->then(function (React\Socket\ConnectionInterface $connection) {
|
|
|
992 |
$connection->write('...');
|
|
|
993 |
$connection->end();
|
|
|
994 |
});
|
|
|
995 |
```
|
|
|
996 |
|
|
|
997 |
By default, the `tcp://` and `tls://` URI schemes will use timeout value that
|
|
|
998 |
respects your `default_socket_timeout` ini setting (which defaults to 60s).
|
|
|
999 |
If you want a custom timeout value, you can simply pass this like this:
|
|
|
1000 |
|
|
|
1001 |
```php
|
|
|
1002 |
$connector = new React\Socket\Connector($loop, array(
|
|
|
1003 |
'timeout' => 10.0
|
|
|
1004 |
));
|
|
|
1005 |
```
|
|
|
1006 |
|
|
|
1007 |
Similarly, if you do not want to apply a timeout at all and let the operating
|
|
|
1008 |
system handle this, you can pass a boolean flag like this:
|
|
|
1009 |
|
|
|
1010 |
```php
|
|
|
1011 |
$connector = new React\Socket\Connector($loop, array(
|
|
|
1012 |
'timeout' => false
|
|
|
1013 |
));
|
|
|
1014 |
```
|
|
|
1015 |
|
|
|
1016 |
By default, the `Connector` supports the `tcp://`, `tls://` and `unix://`
|
|
|
1017 |
URI schemes. If you want to explicitly prohibit any of these, you can simply
|
|
|
1018 |
pass boolean flags like this:
|
|
|
1019 |
|
|
|
1020 |
```php
|
|
|
1021 |
// only allow secure TLS connections
|
|
|
1022 |
$connector = new React\Socket\Connector($loop, array(
|
|
|
1023 |
'tcp' => false,
|
|
|
1024 |
'tls' => true,
|
|
|
1025 |
'unix' => false,
|
|
|
1026 |
));
|
|
|
1027 |
|
|
|
1028 |
$connector->connect('tls://google.com:443')->then(function (React\Socket\ConnectionInterface $connection) {
|
|
|
1029 |
$connection->write('...');
|
|
|
1030 |
$connection->end();
|
|
|
1031 |
});
|
|
|
1032 |
```
|
|
|
1033 |
|
|
|
1034 |
The `tcp://` and `tls://` also accept additional context options passed to
|
|
|
1035 |
the underlying connectors.
|
|
|
1036 |
If you want to explicitly pass additional context options, you can simply
|
|
|
1037 |
pass arrays of context options like this:
|
|
|
1038 |
|
|
|
1039 |
```php
|
|
|
1040 |
// allow insecure TLS connections
|
|
|
1041 |
$connector = new React\Socket\Connector($loop, array(
|
|
|
1042 |
'tcp' => array(
|
|
|
1043 |
'bindto' => '192.168.0.1:0'
|
|
|
1044 |
),
|
|
|
1045 |
'tls' => array(
|
|
|
1046 |
'verify_peer' => false,
|
|
|
1047 |
'verify_peer_name' => false
|
|
|
1048 |
),
|
|
|
1049 |
));
|
|
|
1050 |
|
|
|
1051 |
$connector->connect('tls://localhost:443')->then(function (React\Socket\ConnectionInterface $connection) {
|
|
|
1052 |
$connection->write('...');
|
|
|
1053 |
$connection->end();
|
|
|
1054 |
});
|
|
|
1055 |
```
|
|
|
1056 |
|
|
|
1057 |
By default, this connector supports TLSv1.0+ and excludes support for legacy
|
|
|
1058 |
SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you
|
|
|
1059 |
want to negotiate with the remote side:
|
|
|
1060 |
|
|
|
1061 |
```php
|
|
|
1062 |
$connector = new React\Socket\Connector($loop, array(
|
|
|
1063 |
'tls' => array(
|
|
|
1064 |
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
|
|
|
1065 |
)
|
|
|
1066 |
));
|
|
|
1067 |
```
|
|
|
1068 |
|
|
|
1069 |
> For more details about context options, please refer to the PHP documentation
|
|
|
1070 |
about [socket context options](https://www.php.net/manual/en/context.socket.php)
|
|
|
1071 |
and [SSL context options](https://www.php.net/manual/en/context.ssl.php).
|
|
|
1072 |
|
|
|
1073 |
Advanced: By default, the `Connector` supports the `tcp://`, `tls://` and
|
|
|
1074 |
`unix://` URI schemes.
|
|
|
1075 |
For this, it sets up the required connector classes automatically.
|
|
|
1076 |
If you want to explicitly pass custom connectors for any of these, you can simply
|
|
|
1077 |
pass an instance implementing the `ConnectorInterface` like this:
|
|
|
1078 |
|
|
|
1079 |
```php
|
|
|
1080 |
$dnsResolverFactory = new React\Dns\Resolver\Factory();
|
|
|
1081 |
$resolver = $dnsResolverFactory->createCached('127.0.1.1', $loop);
|
|
|
1082 |
$tcp = new React\Socket\HappyEyeBallsConnector($loop, new React\Socket\TcpConnector($loop), $resolver);
|
|
|
1083 |
|
|
|
1084 |
$tls = new React\Socket\SecureConnector($tcp, $loop);
|
|
|
1085 |
|
|
|
1086 |
$unix = new React\Socket\UnixConnector($loop);
|
|
|
1087 |
|
|
|
1088 |
$connector = new React\Socket\Connector($loop, array(
|
|
|
1089 |
'tcp' => $tcp,
|
|
|
1090 |
'tls' => $tls,
|
|
|
1091 |
'unix' => $unix,
|
|
|
1092 |
|
|
|
1093 |
'dns' => false,
|
|
|
1094 |
'timeout' => false,
|
|
|
1095 |
));
|
|
|
1096 |
|
|
|
1097 |
$connector->connect('google.com:80')->then(function (React\Socket\ConnectionInterface $connection) {
|
|
|
1098 |
$connection->write('...');
|
|
|
1099 |
$connection->end();
|
|
|
1100 |
});
|
|
|
1101 |
```
|
|
|
1102 |
|
|
|
1103 |
> Internally, the `tcp://` connector will always be wrapped by the DNS resolver,
|
|
|
1104 |
unless you disable DNS like in the above example. In this case, the `tcp://`
|
|
|
1105 |
connector receives the actual hostname instead of only the resolved IP address
|
|
|
1106 |
and is thus responsible for performing the lookup.
|
|
|
1107 |
Internally, the automatically created `tls://` connector will always wrap the
|
|
|
1108 |
underlying `tcp://` connector for establishing the underlying plaintext
|
|
|
1109 |
TCP/IP connection before enabling secure TLS mode. If you want to use a custom
|
|
|
1110 |
underlying `tcp://` connector for secure TLS connections only, you may
|
|
|
1111 |
explicitly pass a `tls://` connector like above instead.
|
|
|
1112 |
Internally, the `tcp://` and `tls://` connectors will always be wrapped by
|
|
|
1113 |
`TimeoutConnector`, unless you disable timeouts like in the above example.
|
|
|
1114 |
|
|
|
1115 |
### Advanced client usage
|
|
|
1116 |
|
|
|
1117 |
#### TcpConnector
|
|
|
1118 |
|
|
|
1119 |
The `TcpConnector` class implements the
|
|
|
1120 |
[`ConnectorInterface`](#connectorinterface) and allows you to create plaintext
|
|
|
1121 |
TCP/IP connections to any IP-port-combination:
|
|
|
1122 |
|
|
|
1123 |
```php
|
|
|
1124 |
$tcpConnector = new React\Socket\TcpConnector($loop);
|
|
|
1125 |
|
|
|
1126 |
$tcpConnector->connect('127.0.0.1:80')->then(function (React\Socket\ConnectionInterface $connection) {
|
|
|
1127 |
$connection->write('...');
|
|
|
1128 |
$connection->end();
|
|
|
1129 |
});
|
|
|
1130 |
|
|
|
1131 |
$loop->run();
|
|
|
1132 |
```
|
|
|
1133 |
|
|
|
1134 |
See also the [examples](examples).
|
|
|
1135 |
|
|
|
1136 |
Pending connection attempts can be cancelled by cancelling its pending promise like so:
|
|
|
1137 |
|
|
|
1138 |
```php
|
|
|
1139 |
$promise = $tcpConnector->connect('127.0.0.1:80');
|
|
|
1140 |
|
|
|
1141 |
$promise->cancel();
|
|
|
1142 |
```
|
|
|
1143 |
|
|
|
1144 |
Calling `cancel()` on a pending promise will close the underlying socket
|
|
|
1145 |
resource, thus cancelling the pending TCP/IP connection, and reject the
|
|
|
1146 |
resulting promise.
|
|
|
1147 |
|
|
|
1148 |
You can optionally pass additional
|
|
|
1149 |
[socket context options](https://www.php.net/manual/en/context.socket.php)
|
|
|
1150 |
to the constructor like this:
|
|
|
1151 |
|
|
|
1152 |
```php
|
|
|
1153 |
$tcpConnector = new React\Socket\TcpConnector($loop, array(
|
|
|
1154 |
'bindto' => '192.168.0.1:0'
|
|
|
1155 |
));
|
|
|
1156 |
```
|
|
|
1157 |
|
|
|
1158 |
Note that this class only allows you to connect to IP-port-combinations.
|
|
|
1159 |
If the given URI is invalid, does not contain a valid IP address and port
|
|
|
1160 |
or contains any other scheme, it will reject with an
|
|
|
1161 |
`InvalidArgumentException`:
|
|
|
1162 |
|
|
|
1163 |
If the given URI appears to be valid, but connecting to it fails (such as if
|
|
|
1164 |
the remote host rejects the connection etc.), it will reject with a
|
|
|
1165 |
`RuntimeException`.
|
|
|
1166 |
|
|
|
1167 |
If you want to connect to hostname-port-combinations, see also the following chapter.
|
|
|
1168 |
|
|
|
1169 |
> Advanced usage: Internally, the `TcpConnector` allocates an empty *context*
|
|
|
1170 |
resource for each stream resource.
|
|
|
1171 |
If the destination URI contains a `hostname` query parameter, its value will
|
|
|
1172 |
be used to set up the TLS peer name.
|
|
|
1173 |
This is used by the `SecureConnector` and `DnsConnector` to verify the peer
|
|
|
1174 |
name and can also be used if you want a custom TLS peer name.
|
|
|
1175 |
|
|
|
1176 |
#### HappyEyeBallsConnector
|
|
|
1177 |
|
|
|
1178 |
The `HappyEyeBallsConnector` class implements the
|
|
|
1179 |
[`ConnectorInterface`](#connectorinterface) and allows you to create plaintext
|
|
|
1180 |
TCP/IP connections to any hostname-port-combination. Internally it implements the
|
|
|
1181 |
happy eyeballs algorithm from [`RFC6555`](https://tools.ietf.org/html/rfc6555) and
|
|
|
1182 |
[`RFC8305`](https://tools.ietf.org/html/rfc8305) to support IPv6 and IPv4 hostnames.
|
|
|
1183 |
|
|
|
1184 |
It does so by decorating a given `TcpConnector` instance so that it first
|
|
|
1185 |
looks up the given domain name via DNS (if applicable) and then establishes the
|
|
|
1186 |
underlying TCP/IP connection to the resolved target IP address.
|
|
|
1187 |
|
|
|
1188 |
Make sure to set up your DNS resolver and underlying TCP connector like this:
|
|
|
1189 |
|
|
|
1190 |
```php
|
|
|
1191 |
$dnsResolverFactory = new React\Dns\Resolver\Factory();
|
|
|
1192 |
$dns = $dnsResolverFactory->createCached('8.8.8.8', $loop);
|
|
|
1193 |
|
|
|
1194 |
$dnsConnector = new React\Socket\HappyEyeBallsConnector($loop, $tcpConnector, $dns);
|
|
|
1195 |
|
|
|
1196 |
$dnsConnector->connect('www.google.com:80')->then(function (React\Socket\ConnectionInterface $connection) {
|
|
|
1197 |
$connection->write('...');
|
|
|
1198 |
$connection->end();
|
|
|
1199 |
});
|
|
|
1200 |
|
|
|
1201 |
$loop->run();
|
|
|
1202 |
```
|
|
|
1203 |
|
|
|
1204 |
See also the [examples](examples).
|
|
|
1205 |
|
|
|
1206 |
Pending connection attempts can be cancelled by cancelling its pending promise like so:
|
|
|
1207 |
|
|
|
1208 |
```php
|
|
|
1209 |
$promise = $dnsConnector->connect('www.google.com:80');
|
|
|
1210 |
|
|
|
1211 |
$promise->cancel();
|
|
|
1212 |
```
|
|
|
1213 |
|
|
|
1214 |
Calling `cancel()` on a pending promise will cancel the underlying DNS lookups
|
|
|
1215 |
and/or the underlying TCP/IP connection(s) and reject the resulting promise.
|
|
|
1216 |
|
|
|
1217 |
|
|
|
1218 |
> Advanced usage: Internally, the `HappyEyeBallsConnector` relies on a `Resolver` to
|
|
|
1219 |
look up the IP addresses for the given hostname.
|
|
|
1220 |
It will then replace the hostname in the destination URI with this IP's and
|
|
|
1221 |
append a `hostname` query parameter and pass this updated URI to the underlying
|
|
|
1222 |
connector.
|
|
|
1223 |
The Happy Eye Balls algorithm describes looking the IPv6 and IPv4 address for
|
|
|
1224 |
the given hostname so this connector sends out two DNS lookups for the A and
|
|
|
1225 |
AAAA records. It then uses all IP addresses (both v6 and v4) and tries to
|
|
|
1226 |
connect to all of them with a 50ms interval in between. Alterating between IPv6
|
|
|
1227 |
and IPv4 addresses. When a connection is established all the other DNS lookups
|
|
|
1228 |
and connection attempts are cancelled.
|
|
|
1229 |
|
|
|
1230 |
#### DnsConnector
|
|
|
1231 |
|
|
|
1232 |
The `DnsConnector` class implements the
|
|
|
1233 |
[`ConnectorInterface`](#connectorinterface) and allows you to create plaintext
|
|
|
1234 |
TCP/IP connections to any hostname-port-combination.
|
|
|
1235 |
|
|
|
1236 |
It does so by decorating a given `TcpConnector` instance so that it first
|
|
|
1237 |
looks up the given domain name via DNS (if applicable) and then establishes the
|
|
|
1238 |
underlying TCP/IP connection to the resolved target IP address.
|
|
|
1239 |
|
|
|
1240 |
Make sure to set up your DNS resolver and underlying TCP connector like this:
|
|
|
1241 |
|
|
|
1242 |
```php
|
|
|
1243 |
$dnsResolverFactory = new React\Dns\Resolver\Factory();
|
|
|
1244 |
$dns = $dnsResolverFactory->createCached('8.8.8.8', $loop);
|
|
|
1245 |
|
|
|
1246 |
$dnsConnector = new React\Socket\DnsConnector($tcpConnector, $dns);
|
|
|
1247 |
|
|
|
1248 |
$dnsConnector->connect('www.google.com:80')->then(function (React\Socket\ConnectionInterface $connection) {
|
|
|
1249 |
$connection->write('...');
|
|
|
1250 |
$connection->end();
|
|
|
1251 |
});
|
|
|
1252 |
|
|
|
1253 |
$loop->run();
|
|
|
1254 |
```
|
|
|
1255 |
|
|
|
1256 |
See also the [examples](examples).
|
|
|
1257 |
|
|
|
1258 |
Pending connection attempts can be cancelled by cancelling its pending promise like so:
|
|
|
1259 |
|
|
|
1260 |
```php
|
|
|
1261 |
$promise = $dnsConnector->connect('www.google.com:80');
|
|
|
1262 |
|
|
|
1263 |
$promise->cancel();
|
|
|
1264 |
```
|
|
|
1265 |
|
|
|
1266 |
Calling `cancel()` on a pending promise will cancel the underlying DNS lookup
|
|
|
1267 |
and/or the underlying TCP/IP connection and reject the resulting promise.
|
|
|
1268 |
|
|
|
1269 |
> Advanced usage: Internally, the `DnsConnector` relies on a `React\Dns\Resolver\ResolverInterface`
|
|
|
1270 |
to look up the IP address for the given hostname.
|
|
|
1271 |
It will then replace the hostname in the destination URI with this IP and
|
|
|
1272 |
append a `hostname` query parameter and pass this updated URI to the underlying
|
|
|
1273 |
connector.
|
|
|
1274 |
The underlying connector is thus responsible for creating a connection to the
|
|
|
1275 |
target IP address, while this query parameter can be used to check the original
|
|
|
1276 |
hostname and is used by the `TcpConnector` to set up the TLS peer name.
|
|
|
1277 |
If a `hostname` is given explicitly, this query parameter will not be modified,
|
|
|
1278 |
which can be useful if you want a custom TLS peer name.
|
|
|
1279 |
|
|
|
1280 |
#### SecureConnector
|
|
|
1281 |
|
|
|
1282 |
The `SecureConnector` class implements the
|
|
|
1283 |
[`ConnectorInterface`](#connectorinterface) and allows you to create secure
|
|
|
1284 |
TLS (formerly known as SSL) connections to any hostname-port-combination.
|
|
|
1285 |
|
|
|
1286 |
It does so by decorating a given `DnsConnector` instance so that it first
|
|
|
1287 |
creates a plaintext TCP/IP connection and then enables TLS encryption on this
|
|
|
1288 |
stream.
|
|
|
1289 |
|
|
|
1290 |
```php
|
|
|
1291 |
$secureConnector = new React\Socket\SecureConnector($dnsConnector, $loop);
|
|
|
1292 |
|
|
|
1293 |
$secureConnector->connect('www.google.com:443')->then(function (React\Socket\ConnectionInterface $connection) {
|
|
|
1294 |
$connection->write("GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n");
|
|
|
1295 |
...
|
|
|
1296 |
});
|
|
|
1297 |
|
|
|
1298 |
$loop->run();
|
|
|
1299 |
```
|
|
|
1300 |
|
|
|
1301 |
See also the [examples](examples).
|
|
|
1302 |
|
|
|
1303 |
Pending connection attempts can be cancelled by cancelling its pending promise like so:
|
|
|
1304 |
|
|
|
1305 |
```php
|
|
|
1306 |
$promise = $secureConnector->connect('www.google.com:443');
|
|
|
1307 |
|
|
|
1308 |
$promise->cancel();
|
|
|
1309 |
```
|
|
|
1310 |
|
|
|
1311 |
Calling `cancel()` on a pending promise will cancel the underlying TCP/IP
|
|
|
1312 |
connection and/or the SSL/TLS negotiation and reject the resulting promise.
|
|
|
1313 |
|
|
|
1314 |
You can optionally pass additional
|
|
|
1315 |
[SSL context options](https://www.php.net/manual/en/context.ssl.php)
|
|
|
1316 |
to the constructor like this:
|
|
|
1317 |
|
|
|
1318 |
```php
|
|
|
1319 |
$secureConnector = new React\Socket\SecureConnector($dnsConnector, $loop, array(
|
|
|
1320 |
'verify_peer' => false,
|
|
|
1321 |
'verify_peer_name' => false
|
|
|
1322 |
));
|
|
|
1323 |
```
|
|
|
1324 |
|
|
|
1325 |
By default, this connector supports TLSv1.0+ and excludes support for legacy
|
|
|
1326 |
SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you
|
|
|
1327 |
want to negotiate with the remote side:
|
|
|
1328 |
|
|
|
1329 |
```php
|
|
|
1330 |
$secureConnector = new React\Socket\SecureConnector($dnsConnector, $loop, array(
|
|
|
1331 |
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
|
|
|
1332 |
));
|
|
|
1333 |
```
|
|
|
1334 |
|
|
|
1335 |
> Advanced usage: Internally, the `SecureConnector` relies on setting up the
|
|
|
1336 |
required *context options* on the underlying stream resource.
|
|
|
1337 |
It should therefor be used with a `TcpConnector` somewhere in the connector
|
|
|
1338 |
stack so that it can allocate an empty *context* resource for each stream
|
|
|
1339 |
resource and verify the peer name.
|
|
|
1340 |
Failing to do so may result in a TLS peer name mismatch error or some hard to
|
|
|
1341 |
trace race conditions, because all stream resources will use a single, shared
|
|
|
1342 |
*default context* resource otherwise.
|
|
|
1343 |
|
|
|
1344 |
#### TimeoutConnector
|
|
|
1345 |
|
|
|
1346 |
The `TimeoutConnector` class implements the
|
|
|
1347 |
[`ConnectorInterface`](#connectorinterface) and allows you to add timeout
|
|
|
1348 |
handling to any existing connector instance.
|
|
|
1349 |
|
|
|
1350 |
It does so by decorating any given [`ConnectorInterface`](#connectorinterface)
|
|
|
1351 |
instance and starting a timer that will automatically reject and abort any
|
|
|
1352 |
underlying connection attempt if it takes too long.
|
|
|
1353 |
|
|
|
1354 |
```php
|
|
|
1355 |
$timeoutConnector = new React\Socket\TimeoutConnector($connector, 3.0, $loop);
|
|
|
1356 |
|
|
|
1357 |
$timeoutConnector->connect('google.com:80')->then(function (React\Socket\ConnectionInterface $connection) {
|
|
|
1358 |
// connection succeeded within 3.0 seconds
|
|
|
1359 |
});
|
|
|
1360 |
```
|
|
|
1361 |
|
|
|
1362 |
See also any of the [examples](examples).
|
|
|
1363 |
|
|
|
1364 |
Pending connection attempts can be cancelled by cancelling its pending promise like so:
|
|
|
1365 |
|
|
|
1366 |
```php
|
|
|
1367 |
$promise = $timeoutConnector->connect('google.com:80');
|
|
|
1368 |
|
|
|
1369 |
$promise->cancel();
|
|
|
1370 |
```
|
|
|
1371 |
|
|
|
1372 |
Calling `cancel()` on a pending promise will cancel the underlying connection
|
|
|
1373 |
attempt, abort the timer and reject the resulting promise.
|
|
|
1374 |
|
|
|
1375 |
#### UnixConnector
|
|
|
1376 |
|
|
|
1377 |
The `UnixConnector` class implements the
|
|
|
1378 |
[`ConnectorInterface`](#connectorinterface) and allows you to connect to
|
|
|
1379 |
Unix domain socket (UDS) paths like this:
|
|
|
1380 |
|
|
|
1381 |
```php
|
|
|
1382 |
$connector = new React\Socket\UnixConnector($loop);
|
|
|
1383 |
|
|
|
1384 |
$connector->connect('/tmp/demo.sock')->then(function (React\Socket\ConnectionInterface $connection) {
|
|
|
1385 |
$connection->write("HELLO\n");
|
|
|
1386 |
});
|
|
|
1387 |
|
|
|
1388 |
$loop->run();
|
|
|
1389 |
```
|
|
|
1390 |
|
|
|
1391 |
Connecting to Unix domain sockets is an atomic operation, i.e. its promise will
|
|
|
1392 |
settle (either resolve or reject) immediately.
|
|
|
1393 |
As such, calling `cancel()` on the resulting promise has no effect.
|
|
|
1394 |
|
|
|
1395 |
> The [`getRemoteAddress()`](#getremoteaddress) method will return the target
|
|
|
1396 |
Unix domain socket (UDS) path as given to the `connect()` method, prepended
|
|
|
1397 |
with the `unix://` scheme, for example `unix:///tmp/demo.sock`.
|
|
|
1398 |
The [`getLocalAddress()`](#getlocaladdress) method will most likely return a
|
|
|
1399 |
`null` value as this value is not applicable to UDS connections here.
|
|
|
1400 |
|
|
|
1401 |
#### FixedUriConnector
|
|
|
1402 |
|
|
|
1403 |
The `FixedUriConnector` class implements the
|
|
|
1404 |
[`ConnectorInterface`](#connectorinterface) and decorates an existing Connector
|
|
|
1405 |
to always use a fixed, preconfigured URI.
|
|
|
1406 |
|
|
|
1407 |
This can be useful for consumers that do not support certain URIs, such as
|
|
|
1408 |
when you want to explicitly connect to a Unix domain socket (UDS) path
|
|
|
1409 |
instead of connecting to a default address assumed by an higher-level API:
|
|
|
1410 |
|
|
|
1411 |
```php
|
|
|
1412 |
$connector = new React\Socket\FixedUriConnector(
|
|
|
1413 |
'unix:///var/run/docker.sock',
|
|
|
1414 |
new React\Socket\UnixConnector($loop)
|
|
|
1415 |
);
|
|
|
1416 |
|
|
|
1417 |
// destination will be ignored, actually connects to Unix domain socket
|
|
|
1418 |
$promise = $connector->connect('localhost:80');
|
|
|
1419 |
```
|
|
|
1420 |
|
|
|
1421 |
## Install
|
|
|
1422 |
|
|
|
1423 |
The recommended way to install this library is [through Composer](https://getcomposer.org).
|
|
|
1424 |
[New to Composer?](https://getcomposer.org/doc/00-intro.md)
|
|
|
1425 |
|
|
|
1426 |
This project follows [SemVer](https://semver.org/).
|
|
|
1427 |
This will install the latest supported version:
|
|
|
1428 |
|
|
|
1429 |
```bash
|
|
|
1430 |
$ composer require react/socket:^1.6
|
|
|
1431 |
```
|
|
|
1432 |
|
|
|
1433 |
See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
|
|
|
1434 |
|
|
|
1435 |
This project aims to run on any platform and thus does not require any PHP
|
|
|
1436 |
extensions and supports running on legacy PHP 5.3 through current PHP 7+ and HHVM.
|
|
|
1437 |
It's *highly recommended to use PHP 7+* for this project, partly due to its vast
|
|
|
1438 |
performance improvements and partly because legacy PHP versions require several
|
|
|
1439 |
workarounds as described below.
|
|
|
1440 |
|
|
|
1441 |
Secure TLS connections received some major upgrades starting with PHP 5.6, with
|
|
|
1442 |
the defaults now being more secure, while older versions required explicit
|
|
|
1443 |
context options.
|
|
|
1444 |
This library does not take responsibility over these context options, so it's
|
|
|
1445 |
up to consumers of this library to take care of setting appropriate context
|
|
|
1446 |
options as described above.
|
|
|
1447 |
|
|
|
1448 |
PHP < 7.3.3 (and PHP < 7.2.15) suffers from a bug where feof() might
|
|
|
1449 |
block with 100% CPU usage on fragmented TLS records.
|
|
|
1450 |
We try to work around this by always consuming the complete receive
|
|
|
1451 |
buffer at once to avoid stale data in TLS buffers. This is known to
|
|
|
1452 |
work around high CPU usage for well-behaving peers, but this may
|
|
|
1453 |
cause very large data chunks for high throughput scenarios. The buggy
|
|
|
1454 |
behavior can still be triggered due to network I/O buffers or
|
|
|
1455 |
malicious peers on affected versions, upgrading is highly recommended.
|
|
|
1456 |
|
|
|
1457 |
PHP < 7.1.4 (and PHP < 7.0.18) suffers from a bug when writing big
|
|
|
1458 |
chunks of data over TLS streams at once.
|
|
|
1459 |
We try to work around this by limiting the write chunk size to 8192
|
|
|
1460 |
bytes for older PHP versions only.
|
|
|
1461 |
This is only a work-around and has a noticable performance penalty on
|
|
|
1462 |
affected versions.
|
|
|
1463 |
|
|
|
1464 |
This project also supports running on HHVM.
|
|
|
1465 |
Note that really old HHVM < 3.8 does not support secure TLS connections, as it
|
|
|
1466 |
lacks the required `stream_socket_enable_crypto()` function.
|
|
|
1467 |
As such, trying to create a secure TLS connections on affected versions will
|
|
|
1468 |
return a rejected promise instead.
|
|
|
1469 |
This issue is also covered by our test suite, which will skip related tests
|
|
|
1470 |
on affected versions.
|
|
|
1471 |
|
|
|
1472 |
## Tests
|
|
|
1473 |
|
|
|
1474 |
To run the test suite, you first need to clone this repo and then install all
|
|
|
1475 |
dependencies [through Composer](https://getcomposer.org):
|
|
|
1476 |
|
|
|
1477 |
```bash
|
|
|
1478 |
$ composer install
|
|
|
1479 |
```
|
|
|
1480 |
|
|
|
1481 |
To run the test suite, go to the project root and run:
|
|
|
1482 |
|
|
|
1483 |
```bash
|
|
|
1484 |
$ php vendor/bin/phpunit
|
|
|
1485 |
```
|
|
|
1486 |
|
|
|
1487 |
The test suite also contains a number of functional integration tests that rely
|
|
|
1488 |
on a stable internet connection.
|
|
|
1489 |
If you do not want to run these, they can simply be skipped like this:
|
|
|
1490 |
|
|
|
1491 |
```bash
|
|
|
1492 |
$ php vendor/bin/phpunit --exclude-group internet
|
|
|
1493 |
```
|
|
|
1494 |
|
|
|
1495 |
## License
|
|
|
1496 |
|
|
|
1497 |
MIT, see [LICENSE file](LICENSE).
|