Skip to content

Commit 1bf5685

Browse files
authored
Merge pull request reactphp-legacy#74 from clue-labs/uri
Use `connect($uri)` instead of `create($host, $port)`
2 parents a322101 + 035b373 commit 1bf5685

15 files changed

+238
-101
lines changed

README.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,16 @@ swap this implementation against any other implementation of this interface.
4444

4545
The interface only offers a single method:
4646

47-
#### create()
47+
#### connect()
4848

49-
The `create(string $host, int $port): PromiseInterface<Stream, Exception>` method
49+
The `connect(string $uri): PromiseInterface<Stream, Exception>` method
5050
can be used to establish a streaming connection.
5151
It returns a [Promise](https://github.com/reactphp/promise) which either
5252
fulfills with a [Stream](https://github.com/reactphp/stream) or
5353
rejects with an `Exception`:
5454

5555
```php
56-
$connector->create('google.com', 443)->then(
56+
$connector->connect('google.com:443')->then(
5757
function (Stream $stream) {
5858
// connection successfully established
5959
},
@@ -69,7 +69,7 @@ reject its value with an `Exception`. It SHOULD clean up any underlying
6969
resources and references as applicable:
7070

7171
```php
72-
$promise = $connector->create($host, $port);
72+
$promise = $connector->connect($uri);
7373

7474
$promise->cancel();
7575
```
@@ -83,7 +83,7 @@ TCP/IP connections to any IP-port-combination:
8383
```php
8484
$tcpConnector = new React\SocketClient\TcpConnector($loop);
8585

86-
$tcpConnector->create('127.0.0.1', 80)->then(function (React\Stream\Stream $stream) {
86+
$tcpConnector->connect('127.0.0.1:80')->then(function (React\Stream\Stream $stream) {
8787
$stream->write('...');
8888
$stream->end();
8989
});
@@ -96,7 +96,7 @@ See also the [first example](examples).
9696
Pending connection attempts can be cancelled by cancelling its pending promise like so:
9797

9898
```php
99-
$promise = $tcpConnector->create($host, $port);
99+
$promise = $tcpConnector->connect('127.0.0.1:80');
100100

101101
$promise->cancel();
102102
```
@@ -132,11 +132,11 @@ Make sure to set up your DNS resolver and underlying TCP connector like this:
132132

133133
```php
134134
$dnsResolverFactory = new React\Dns\Resolver\Factory();
135-
$dns = $dnsResolverFactory->createCached('8.8.8.8', $loop);
135+
$dns = $dnsResolverFactory->connectCached('8.8.8.8', $loop);
136136

137137
$dnsConnector = new React\SocketClient\DnsConnector($tcpConnector, $dns);
138138

139-
$dnsConnector->create('www.google.com', 80)->then(function (React\Stream\Stream $stream) {
139+
$dnsConnector->connect('www.google.com:80')->then(function (React\Stream\Stream $stream) {
140140
$stream->write('...');
141141
$stream->end();
142142
});
@@ -149,7 +149,7 @@ See also the [first example](examples).
149149
Pending connection attempts can be cancelled by cancelling its pending promise like so:
150150

151151
```php
152-
$promise = $dnsConnector->create($host, $port);
152+
$promise = $dnsConnector->connect('www.google.com:80');
153153

154154
$promise->cancel();
155155
```
@@ -164,7 +164,7 @@ set up like this:
164164
```php
165165
$connector = new React\SocketClient\Connector($loop, $dns);
166166

167-
$connector->create('www.google.com', 80)->then($callback);
167+
$connector->connect('www.google.com:80')->then($callback);
168168
```
169169

170170
### Async SSL/TLS connections
@@ -180,7 +180,7 @@ stream.
180180
```php
181181
$secureConnector = new React\SocketClient\SecureConnector($dnsConnector, $loop);
182182

183-
$secureConnector->create('www.google.com', 443)->then(function (React\Stream\Stream $stream) {
183+
$secureConnector->connect('www.google.com:443')->then(function (React\Stream\Stream $stream) {
184184
$stream->write("GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n");
185185
...
186186
});
@@ -193,7 +193,7 @@ See also the [second example](examples).
193193
Pending connection attempts can be cancelled by cancelling its pending promise like so:
194194

195195
```php
196-
$promise = $secureConnector->create($host, $port);
196+
$promise = $secureConnector->connect('www.google.com:443');
197197

198198
$promise->cancel();
199199
```
@@ -233,7 +233,7 @@ underlying connection attempt if it takes too long.
233233
```php
234234
$timeoutConnector = new React\SocketClient\TimeoutConnector($connector, 3.0, $loop);
235235

236-
$timeoutConnector->create('google.com', 80)->then(function (React\Stream\Stream $stream) {
236+
$timeoutConnector->connect('google.com:80')->then(function (React\Stream\Stream $stream) {
237237
// connection succeeded within 3.0 seconds
238238
});
239239
```
@@ -243,7 +243,7 @@ See also any of the [examples](examples).
243243
Pending connection attempts can be cancelled by cancelling its pending promise like so:
244244

245245
```php
246-
$promise = $timeoutConnector->create($host, $port);
246+
$promise = $timeoutConnector->connect('google.com:80');
247247

248248
$promise->cancel();
249249
```
@@ -260,7 +260,7 @@ Unix domain socket (UDS) paths like this:
260260
```php
261261
$connector = new React\SocketClient\UnixConnector($loop);
262262

263-
$connector->create('/tmp/demo.sock')->then(function (React\Stream\Stream $stream) {
263+
$connector->connect('/tmp/demo.sock')->then(function (React\Stream\Stream $stream) {
264264
$stream->write("HELLO\n");
265265
});
266266

src/Connector.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ public function __construct(LoopInterface $loop, Resolver $resolver)
2424
$this->connector = new DnsConnector(new TcpConnector($loop), $resolver);
2525
}
2626

27-
public function create($host, $port)
27+
public function connect($uri)
2828
{
29-
return $this->connector->create($host, $port);
29+
return $this->connector->connect($uri);
3030
}
3131
}

src/ConnectorInterface.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* This is usually done via dependency injection, so it's fairly simple to actually
1616
* swap this implementation against any other implementation of this interface.
1717
*
18-
* The interface only offers a single `create()` method.
18+
* The interface only offers a single `connect()` method.
1919
*/
2020
interface ConnectorInterface
2121
{
@@ -30,9 +30,8 @@ interface ConnectorInterface
3030
* reject its value with an Exception. It SHOULD clean up any underlying
3131
* resources and references as applicable.
3232
*
33-
* @param string $host
34-
* @param int $port
33+
* @param string $uri
3534
* @return React\Promise\PromiseInterface resolves with a Stream on success or rejects with an Exception on error
3635
*/
37-
public function create($host, $port);
36+
public function connect($uri);
3837
}

src/DnsConnector.php

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,60 @@ public function __construct(ConnectorInterface $connector, Resolver $resolver)
1818
$this->resolver = $resolver;
1919
}
2020

21-
public function create($host, $port)
21+
public function connect($uri)
2222
{
23+
if (strpos($uri, '://') === false) {
24+
$parts = parse_url('tcp://' . $uri);
25+
unset($parts['scheme']);
26+
} else {
27+
$parts = parse_url($uri);
28+
}
29+
30+
if (!$parts || !isset($parts['host'])) {
31+
return Promise\reject(new \InvalidArgumentException('Given URI "' . $uri . '" is invalid'));
32+
}
33+
2334
$that = $this;
35+
$host = trim($parts['host'], '[]');
2436

2537
return $this
2638
->resolveHostname($host)
27-
->then(function ($ip) use ($that, $port) {
28-
return $that->connect($ip, $port);
39+
->then(function ($ip) use ($that, $parts) {
40+
$uri = '';
41+
42+
// prepend original scheme if known
43+
if (isset($parts['scheme'])) {
44+
$uri .= $parts['scheme'] . '://';
45+
}
46+
47+
if (strpos($ip, ':') !== false) {
48+
// enclose IPv6 addresses in square brackets before appending port
49+
$uri .= '[' . $ip . ']';
50+
} else {
51+
$uri .= $ip;
52+
}
53+
54+
// append original port if known
55+
if (isset($parts['port'])) {
56+
$uri .= ':' . $parts['port'];
57+
}
58+
59+
// append orignal path if known
60+
if (isset($parts['path'])) {
61+
$uri .= $parts['path'];
62+
}
63+
64+
// append original query if known
65+
if (isset($parts['query'])) {
66+
$uri .= '?' . $parts['query'];
67+
}
68+
69+
// append original fragment if known
70+
if (isset($parts['fragment'])) {
71+
$uri .= '#' . $parts['fragment'];
72+
}
73+
74+
return $that->connectTcp($uri);
2975
});
3076
}
3177

@@ -55,9 +101,9 @@ function ($_, $reject) use ($promise) {
55101
}
56102

57103
/** @internal */
58-
public function connect($ip, $port)
104+
public function connectTcp($uri)
59105
{
60-
$promise = $this->connector->create($ip, $port);
106+
$promise = $this->connector->connect($uri);
61107

62108
return new Promise\Promise(
63109
function ($resolve, $reject) use ($promise) {

src/SecureConnector.php

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,24 @@ public function __construct(ConnectorInterface $connector, LoopInterface $loop,
2020
$this->context = $context;
2121
}
2222

23-
public function create($host, $port)
23+
public function connect($uri)
2424
{
2525
if (!function_exists('stream_socket_enable_crypto')) {
2626
return Promise\reject(new \BadMethodCallException('Encryption not supported on your platform (HHVM < 3.8?)'));
2727
}
2828

29+
if (strpos($uri, '://') === false) {
30+
$uri = 'tls://' . $uri;
31+
}
32+
33+
$parts = parse_url($uri);
34+
if (!$parts || !isset($parts['host']) || $parts['scheme'] !== 'tls') {
35+
return Promise\reject(new \InvalidArgumentException('Given URI "' . $uri . '" is invalid'));
36+
}
37+
38+
$uri = str_replace('tls://', '', $uri);
39+
$host = trim($parts['host'], '[]');
40+
2941
$context = $this->context + array(
3042
'SNI_enabled' => true,
3143
'peer_name' => $host
@@ -40,7 +52,7 @@ public function create($host, $port)
4052
}
4153

4254
$encryption = $this->streamEncryption;
43-
return $this->connect($host, $port)->then(function (Stream $stream) use ($context, $encryption) {
55+
return $this->connectTcp($uri)->then(function (Stream $stream) use ($context, $encryption) {
4456
// (unencrypted) TCP/IP connection succeeded
4557

4658
// set required SSL/TLS context options
@@ -57,9 +69,9 @@ public function create($host, $port)
5769
});
5870
}
5971

60-
private function connect($host, $port)
72+
private function connectTcp($uri)
6173
{
62-
$promise = $this->connector->create($host, $port);
74+
$promise = $this->connector->connect($uri);
6375

6476
return new Promise\Promise(
6577
function ($resolve, $reject) use ($promise) {

src/TcpConnector.php

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,24 @@ public function __construct(LoopInterface $loop, array $context = array())
1919
$this->context = $context;
2020
}
2121

22-
public function create($ip, $port)
22+
public function connect($uri)
2323
{
24-
if (false === filter_var($ip, FILTER_VALIDATE_IP)) {
25-
return Promise\reject(new \InvalidArgumentException('Given parameter "' . $ip . '" is not a valid IP'));
24+
if (strpos($uri, '://') === false) {
25+
$uri = 'tcp://' . $uri;
26+
}
27+
28+
$parts = parse_url($uri);
29+
if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') {
30+
return Promise\reject(new \InvalidArgumentException('Given URI "' . $uri . '" is invalid'));
2631
}
2732

28-
$url = $this->getSocketUrl($ip, $port);
33+
$ip = trim($parts['host'], '[]');
34+
if (false === filter_var($ip, FILTER_VALIDATE_IP)) {
35+
return Promise\reject(new \InvalidArgumentException('Given URI "' . $ip . '" does not contain a valid host IP'));
36+
}
2937

3038
$socket = @stream_socket_client(
31-
$url,
39+
$uri,
3240
$errno,
3341
$errstr,
3442
0,
@@ -38,7 +46,7 @@ public function create($ip, $port)
3846

3947
if (false === $socket) {
4048
return Promise\reject(new \RuntimeException(
41-
sprintf("Connection to %s:%d failed: %s", $ip, $port, $errstr),
49+
sprintf("Connection to %s failed: %s", $uri, $errstr),
4250
$errno
4351
));
4452
}
@@ -90,13 +98,4 @@ public function handleConnectedSocket($socket)
9098
{
9199
return new Stream($socket, $this->loop);
92100
}
93-
94-
private function getSocketUrl($ip, $port)
95-
{
96-
if (strpos($ip, ':') !== false) {
97-
// enclose IPv6 addresses in square brackets before appending port
98-
$ip = '[' . $ip . ']';
99-
}
100-
return sprintf('tcp://%s:%s', $ip, $port);
101-
}
102101
}

src/TimeoutConnector.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ public function __construct(ConnectorInterface $connector, $timeout, LoopInterfa
2222
$this->loop = $loop;
2323
}
2424

25-
public function create($host, $port)
25+
public function connect($uri)
2626
{
27-
$promise = $this->connector->create($host, $port);
27+
$promise = $this->connector->connect($uri);
2828

2929
return Timer\timeout(new Promise(
3030
function ($resolve, $reject) use ($promise) {

src/UnixConnector.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,15 @@ public function __construct(LoopInterface $loop)
2323
$this->loop = $loop;
2424
}
2525

26-
public function create($path, $unusedPort = 0)
26+
public function connect($path)
2727
{
28-
$resource = @stream_socket_client('unix://' . $path, $errno, $errstr, 1.0);
28+
if (strpos($path, '://') === false) {
29+
$path = 'unix://' . $path;
30+
} elseif (substr($path, 0, 7) !== 'unix://') {
31+
return Promise\reject(new \InvalidArgumentException('Given URI "' . $path . '" is invalid'));
32+
}
33+
34+
$resource = @stream_socket_client($path, $errno, $errstr, 1.0);
2935

3036
if (!$resource) {
3137
return Promise\reject(new RuntimeException('Unable to connect to unix domain socket "' . $path . '": ' . $errstr, $errno));

0 commit comments

Comments
 (0)