Skip to content

Commit a0f60cc

Browse files
authored
Merge pull request #11 from clue-labs/compat
Documentation for Connector concepts (TCP/TLS, timeouts, DNS resolution)
2 parents a3a51b2 + 01013ff commit a0f60cc

6 files changed

+168
-44
lines changed

README.md

Lines changed: 133 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ Async HTTP CONNECT proxy connector, use any TCP/IP protocol through an HTTP prox
99
* [ConnectorInterface](#connectorinterface)
1010
* [connect()](#connect)
1111
* [ProxyConnector](#proxyconnector)
12+
* [Plain TCP connections](#plain-tcp-connections)
13+
* [Secure TLS connections](#secure-tls-connections)
14+
* [Connection timeout](#connection-timeout)
15+
* [DNS resolution](#dns-resolution)
16+
* [Advanced secure proxy connections](#advanced-secure-proxy-connections)
1217
* [Install](#install)
1318
* [Tests](#tests)
1419
* [License](#license)
@@ -21,11 +26,15 @@ secure HTTPS request to google.com through a local HTTP proxy server:
2126

2227
```php
2328
$loop = React\EventLoop\Factory::create();
24-
$connector = new TcpConnector($loop);
25-
$proxy = new ProxyConnector('127.0.0.1:8080', $connector);
26-
$ssl = new SecureConnector($proxy, $loop);
2729

28-
$ssl->connect('google.com:443')->then(function (ConnectionInterface $stream) {
30+
$proxy = new ProxyConnector('127.0.0.1:8080', new Connector($loop));
31+
$connector = new Connector($loop, array(
32+
'tcp' => $proxy,
33+
'timeout' => 3.0,
34+
'dns' => false
35+
));
36+
37+
$connector->connect('tls://google.com:443')->then(function (ConnectionInterface $stream) {
2938
$stream->write("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n");
3039
$stream->on('data', function ($chunk) {
3140
echo $chunk;
@@ -91,15 +100,15 @@ Its constructor simply accepts an HTTP proxy URL and a connector used to connect
91100
to the proxy server address:
92101

93102
```php
94-
$connector = new TcpConnector($loop);
103+
$connector = new Connector($loop);
95104
$proxy = new ProxyConnector('127.0.0.1:8080', $connector);
96105
```
97106

98107
The proxy URL may or may not contain a scheme and port definition. The default
99108
port will be `80` for HTTP (or `443` for HTTPS), but many common HTTP proxy
100109
servers use custom ports.
101110
In its most simple form, the given connector will be a
102-
[`TcpConnector`](https://github.com/reactphp/socket#tcpconnector) if you
111+
[`\React\Socket\Connector`](https://github.com/reactphp/socket#connector) if you
103112
want to connect to a given IP address as above.
104113

105114
This is the main class in this package.
@@ -114,14 +123,36 @@ higher-level component:
114123
+ $client = new SomeClient($proxy);
115124
```
116125

126+
#### Plain TCP connections
127+
117128
This is most frequently used to issue HTTPS requests to your destination.
118129
However, this is actually performed on a higher protocol layer and this
119-
connector is actually inherently a general-purpose plain TCP/IP connector:
130+
connector is actually inherently a general-purpose plain TCP/IP connector.
131+
132+
The `ProxyConnector` implements the [`ConnectorInterface`](#connectorinterface) and
133+
hence provides a single public method, the [`connect()`](#connect) method.
120134

121135
```php
122136
$proxy = new ProxyConnector('127.0.0.1:8080', $connector);
123137

124-
$proxy->connect('smtp.googlemail.com:587')->then(function (ConnectionInterface $stream) {
138+
$proxy->connect('tcp://smtp.googlemail.com:587')->then(function (ConnectionInterface $stream) {
139+
$stream->write("EHLO local\r\n");
140+
$stream->on('data', function ($chunk) use ($stream) {
141+
echo $chunk;
142+
});
143+
});
144+
```
145+
146+
You can either use the `ProxyConnector` directly or you may want to wrap this connector
147+
in React's [`Connector`](https://github.com/reactphp/socket#connector):
148+
149+
```php
150+
$connector = new Connector($loop, array(
151+
'tcp' => $proxy,
152+
'dns' => false
153+
));
154+
155+
$connector->connect('tcp://smtp.googlemail.com:587')->then(function (ConnectionInterface $stream) {
125156
$stream->write("EHLO local\r\n");
126157
$stream->on('data', function ($chunk) use ($stream) {
127158
echo $chunk;
@@ -132,23 +163,112 @@ $proxy->connect('smtp.googlemail.com:587')->then(function (ConnectionInterface $
132163
Note that HTTP CONNECT proxies often restrict which ports one may connect to.
133164
Many (public) proxy servers do in fact limit this to HTTPS (443) only.
134165

166+
#### Secure TLS connections
167+
135168
If you want to establish a TLS connection (such as HTTPS) between you and
136-
your destination, you may want to wrap this connector in a
137-
[`SecureConnector`](https://github.com/reactphp/socket#secureconnector)
138-
instance:
169+
your destination, you may want to wrap this connector in React's
170+
[`Connector`](https://github.com/reactphp/socket#connector) or the low-level
171+
[`SecureConnector`](https://github.com/reactphp/socket#secureconnector):
139172

140173
```php
141174
$proxy = new ProxyConnector('127.0.0.1:8080', $connector);
142-
$ssl = new SecureConnector($proxy, $loop);
175+
$connector = new Connector($loop, array(
176+
'tcp' => $proxy,
177+
'dns' => false
178+
));
143179

144-
$ssl->connect('smtp.googlemail.com:465')->then(function (ConnectionInterface $stream) {
180+
$connector->connect('tls://smtp.googlemail.com:465')->then(function (ConnectionInterface $stream) {
145181
$stream->write("EHLO local\r\n");
146182
$stream->on('data', function ($chunk) use ($stream) {
147183
echo $chunk;
148184
});
149185
});
150186
```
151187

188+
> Also note how secure TLS connections are in fact entirely handled outside of
189+
this HTTP CONNECT client implementation.
190+
191+
#### Connection timeout
192+
193+
By default, the `ProxyConnector` does not implement any timeouts for establishing remote
194+
connections.
195+
Your underlying operating system may impose limits on pending and/or idle TCP/IP
196+
connections, anywhere in a range of a few minutes to several hours.
197+
198+
Many use cases require more control over the timeout and likely values much
199+
smaller, usually in the range of a few seconds only.
200+
201+
You can use React's [`Connector`](https://github.com/reactphp/socket#connector)
202+
or the low-level
203+
[`TimeoutConnector`](https://github.com/reactphp/socket#timeoutconnector)
204+
to decorate any given `ConnectorInterface` instance.
205+
It provides the same `connect()` method, but will automatically reject the
206+
underlying connection attempt if it takes too long:
207+
208+
```php
209+
$connector = new Connector($loop, array(
210+
'tcp' => $proxy,
211+
'dns' => false,
212+
'timeout' => 3.0
213+
));
214+
215+
$connector->connect('tcp://google.com:80')->then(function ($stream) {
216+
// connection succeeded within 3.0 seconds
217+
});
218+
```
219+
220+
See also any of the [examples](examples).
221+
222+
> Also note how connection timeout is in fact entirely handled outside of this
223+
HTTP CONNECT client implementation.
224+
225+
#### DNS resolution
226+
227+
By default, the `ProxyConnector` does not perform any DNS resolution at all and simply
228+
forwards any hostname you're trying to connect to the remote proxy server.
229+
The remote proxy server is thus responsible for looking up any hostnames via DNS
230+
(this default mode is thus called *remote DNS resolution*).
231+
232+
As an alternative, you can also send the destination IP to the remote proxy
233+
server.
234+
In this mode you either have to stick to using IPs only (which is ofen unfeasable)
235+
or perform any DNS lookups locally and only transmit the resolved destination IPs
236+
(this mode is thus called *local DNS resolution*).
237+
238+
The default *remote DNS resolution* is useful if your local `ProxyConnector` either can
239+
not resolve target hostnames because it has no direct access to the internet or
240+
if it should not resolve target hostnames because its outgoing DNS traffic might
241+
be intercepted.
242+
243+
As noted above, the `ProxyConnector` defaults to using remote DNS resolution.
244+
However, wrapping the `ProxyConnector` in React's
245+
[`Connector`](https://github.com/reactphp/socket#connector) actually
246+
performs local DNS resolution unless explicitly defined otherwise.
247+
Given that remote DNS resolution is assumed to be the preferred mode, all
248+
other examples explicitly disable DNS resoltion like this:
249+
250+
```php
251+
$connector = new Connector($loop, array(
252+
'tcp' => $proxy,
253+
'dns' => false
254+
));
255+
```
256+
257+
If you want to explicitly use *local DNS resolution*, you can use the following code:
258+
259+
```php
260+
// set up Connector which uses Google's public DNS (8.8.8.8)
261+
$connector = Connector($loop, array(
262+
'tcp' => $proxy,
263+
'dns' => '8.8.8.8'
264+
));
265+
```
266+
267+
> Also note how local DNS resolution is in fact entirely handled outside of this
268+
HTTP CONNECT client implementation.
269+
270+
#### Advanced secure proxy connections
271+
152272
Note that communication between the client and the proxy is usually via an
153273
unencrypted, plain TCP/IP HTTP connection. Note that this is the most common
154274
setup, because you can still establish a TLS connection between you and the

examples/01-proxy-https.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
// The proxy can be given as first argument and defaults to localhost:8080 otherwise.
55

66
use Clue\React\HttpProxy\ProxyConnector;
7-
use React\Socket\TcpConnector;
8-
use React\Socket\SecureConnector;
7+
use React\Socket\Connector;
98
use React\Socket\ConnectionInterface;
109

1110
require __DIR__ . '/../vendor/autoload.php';
@@ -14,11 +13,14 @@
1413

1514
$loop = React\EventLoop\Factory::create();
1615

17-
$connector = new TcpConnector($loop);
18-
$proxy = new ProxyConnector($url, $connector);
19-
$ssl = new SecureConnector($proxy, $loop);
16+
$proxy = new ProxyConnector($url, new Connector($loop));
17+
$connector = new Connector($loop, array(
18+
'tcp' => $proxy,
19+
'timeout' => 3.0,
20+
'dns' => false
21+
));
2022

21-
$ssl->connect('google.com:443')->then(function (ConnectionInterface $stream) {
23+
$connector->connect('tls://google.com:443')->then(function (ConnectionInterface $stream) {
2224
$stream->write("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n");
2325
$stream->on('data', function ($chunk) {
2426
echo $chunk;

examples/02-optional-proxy-https.php

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,26 @@
88
// network protocol otherwise.
99

1010
use Clue\React\HttpProxy\ProxyConnector;
11-
use React\Socket\TcpConnector;
12-
use React\Socket\SecureConnector;
13-
use React\Socket\DnsConnector;
14-
use React\Dns\Resolver\Factory;
11+
use React\Socket\Connector;
1512
use React\Socket\ConnectionInterface;
1613

1714
require __DIR__ . '/../vendor/autoload.php';
1815

1916
$loop = React\EventLoop\Factory::create();
2017

21-
$tcp = new TcpConnector($loop);
22-
$dnsFactory = new Factory();
23-
$resolver = $dnsFactory->create('8.8.8.8', $loop);
24-
$dns = new DnsConnector($tcp, $resolver);
18+
$connector = new Connector($loop);
2519

2620
// first argument given? use this as the proxy URL
2721
if (isset($argv[1])) {
28-
$proxy = new ProxyConnector($argv[1], $dns);
29-
$connector = new SecureConnector($proxy, $loop);
30-
} else {
31-
$connector = new SecureConnector($dns, $loop);
22+
$proxy = new ProxyConnector($argv[1], $connector);
23+
$connector = new Connector($loop, array(
24+
'tcp' => $proxy,
25+
'timeout' => 3.0,
26+
'dns' => false
27+
));
3228
}
3329

34-
$connector->connect('google.com:443')->then(function (ConnectionInterface $stream) {
30+
$connector->connect('tls://google.com:443')->then(function (ConnectionInterface $stream) {
3531
$stream->write("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n");
3632
$stream->on('data', function ($chunk) {
3733
echo $chunk;

examples/11-proxy-smtp.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// Please note that MANY public proxies do not allow SMTP connections, YMMV.
66

77
use Clue\React\HttpProxy\ProxyConnector;
8-
use React\Socket\TcpConnector;
8+
use React\Socket\Connector;
99
use React\Socket\ConnectionInterface;
1010

1111
require __DIR__ . '/../vendor/autoload.php';
@@ -14,10 +14,14 @@
1414

1515
$loop = React\EventLoop\Factory::create();
1616

17-
$connector = new TcpConnector($loop);
18-
$proxy = new ProxyConnector($url, $connector);
17+
$proxy = new ProxyConnector($url, new Connector($loop));
18+
$connector = new Connector($loop, array(
19+
'tcp' => $proxy,
20+
'timeout' => 3.0,
21+
'dns' => false
22+
));
1923

20-
$proxy->connect('smtp.googlemail.com:587')->then(function (ConnectionInterface $stream) {
24+
$connector->connect('tcp://smtp.googlemail.com:587')->then(function (ConnectionInterface $stream) {
2125
$stream->write("EHLO local\r\n");
2226
$stream->on('data', function ($chunk) use ($stream) {
2327
echo $chunk;

examples/12-proxy-smtps.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
// Please note that MANY public proxies do not allow SMTP connections, YMMV.
99

1010
use Clue\React\HttpProxy\ProxyConnector;
11-
use React\Socket\TcpConnector;
12-
use React\Socket\SecureConnector;
11+
use React\Socket\Connector;
1312
use React\Socket\ConnectionInterface;
1413

1514
require __DIR__ . '/../vendor/autoload.php';
@@ -18,11 +17,14 @@
1817

1918
$loop = React\EventLoop\Factory::create();
2019

21-
$connector = new TcpConnector($loop);
22-
$proxy = new ProxyConnector($url, $connector);
23-
$ssl = new SecureConnector($proxy, $loop);
20+
$proxy = new ProxyConnector($url, new Connector($loop));
21+
$connector = new Connector($loop, array(
22+
'tcp' => $proxy,
23+
'timeout' => 3.0,
24+
'dns' => false
25+
));
2426

25-
$ssl->connect('smtp.googlemail.com:465')->then(function (ConnectionInterface $stream) {
27+
$connector->connect('tls://smtp.googlemail.com:465')->then(function (ConnectionInterface $stream) {
2628
$stream->write("EHLO local\r\n");
2729
$stream->on('data', function ($chunk) use ($stream) {
2830
echo $chunk;

src/ProxyConnector.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ class ProxyConnector implements ConnectorInterface
4949
* port definition. The default port will be `80` for HTTP (or `443` for
5050
* HTTPS), but many common HTTP proxy servers use custom ports.
5151
* @param ConnectorInterface $connector In its most simple form, the given
52-
* connector will be a TcpConnector if you want to connect to a given IP
53-
* address.
52+
* connector will be a \React\Socket\Connector if you want to connect to
53+
* a given IP address.
5454
* @throws InvalidArgumentException if the proxy URL is invalid
5555
*/
5656
public function __construct($proxyUrl, ConnectorInterface $connector)

0 commit comments

Comments
 (0)