Skip to content

Commit 10ef934

Browse files
authored
Merge pull request #8 from clue-labs/connect
Update to SocketClient v0.6+ and use connect($uri)
2 parents 85375c0 + e6b8c03 commit 10ef934

File tree

9 files changed

+107
-62
lines changed

9 files changed

+107
-62
lines changed

README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Async HTTP CONNECT proxy connector, use any TCP/IP protocol through an HTTP prox
77
* [Quickstart example](#quickstart-example)
88
* [Usage](#usage)
99
* [ConnectorInterface](#connectorinterface)
10-
* [create()](#create)
10+
* [connect()](#connect)
1111
* [ProxyConnector](#proxyconnector)
1212
* [Install](#install)
1313
* [Tests](#tests)
@@ -25,7 +25,7 @@ $connector = new TcpConnector($loop);
2525
$proxy = new ProxyConnector('127.0.0.1:8080', $connector);
2626
$ssl = new SecureConnector($proxy, $loop);
2727

28-
$ssl->create('google.com', 443)->then(function (Stream $stream) {
28+
$ssl->connect('google.com:443')->then(function (ConnectionInterface $stream) {
2929
$stream->write("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n");
3030
$stream->on('data', function ($chunk) {
3131
echo $chunk;
@@ -59,17 +59,17 @@ HTTP CONNECT proxy.
5959

6060
The interface only offers a single method:
6161

62-
#### create()
62+
#### connect()
6363

64-
The `create(string $host, int $port): PromiseInterface<Stream, Exception>` method
64+
The `connect(string $uri): PromiseInterface<ConnectionInterface, Exception>` method
6565
can be used to establish a streaming connection.
6666
It returns a [Promise](https://github.com/reactphp/promise) which either
67-
fulfills with a [Stream](https://github.com/reactphp/stream) or
67+
fulfills with a [ConnectionInterface](https://github.com/reactphp/socket-client#connectioninterface) or
6868
rejects with an `Exception`:
6969

7070
```php
71-
$connector->create('google.com', 443)->then(
72-
function (Stream $stream) {
71+
$connector->connect('google.com:443')->then(
72+
function (ConnectionInterface $stream) {
7373
// connection successfully established
7474
},
7575
function (Exception $error) {
@@ -121,7 +121,7 @@ connector is actually inherently a general-purpose plain TCP/IP connector:
121121
```php
122122
$proxy = new ProxyConnector('127.0.0.1:8080', $connector);
123123

124-
$proxy->create('smtp.googlemail.com', 587)->then(function (Stream $stream) {
124+
$proxy->connect('smtp.googlemail.com:587')->then(function (ConnectionInterface $stream) {
125125
$stream->write("EHLO local\r\n");
126126
$stream->on('data', function ($chunk) use ($stream) {
127127
echo $chunk;
@@ -141,7 +141,7 @@ instance:
141141
$proxy = new ProxyConnector('127.0.0.1:8080', $connector);
142142
$ssl = new SecureConnector($proxy, $loop);
143143

144-
$ssl->create('smtp.googlemail.com', 465)->then(function (Stream $stream) {
144+
$ssl->connect('smtp.googlemail.com:465')->then(function (ConnectionInterface $stream) {
145145
$stream->write("EHLO local\r\n");
146146
$stream->on('data', function ($chunk) use ($stream) {
147147
echo $chunk;
@@ -163,7 +163,7 @@ instance to create a secure connection to the proxy:
163163
$ssl = new SecureConnector($connector, $loop);
164164
$proxy = new ProxyConnector('127.0.0.1:443', $ssl);
165165

166-
$proxy->create('smtp.googlemail.com', 587);
166+
$proxy->connect('smtp.googlemail.com:587');
167167
```
168168

169169
## Install

composer.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,13 @@
1818
},
1919
"require": {
2020
"php": ">=5.3",
21-
"react/socket-client": "^0.5 || ^0.4 || ^0.3",
21+
"react/socket-client": "^0.7 || ^0.6",
2222
"react/event-loop": "^0.4 || ^0.3",
23-
"react/stream": "^0.4 || ^0.3",
2423
"react/promise": " ^2.1 || ^1.2",
2524
"ringcentral/psr7": "^1.2"
2625
},
2726
"require-dev": {
2827
"phpunit/phpunit": "^5.0 || ^4.8",
29-
"react/socket-client": "^0.5",
3028
"clue/block-react": "^1.1"
3129
}
3230
}

examples/01-proxy-https.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
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\Stream\Stream;
87
use React\SocketClient\TcpConnector;
98
use React\SocketClient\SecureConnector;
9+
use React\SocketClient\ConnectionInterface;
1010

1111
require __DIR__ . '/../vendor/autoload.php';
1212

@@ -18,7 +18,7 @@
1818
$proxy = new ProxyConnector($url, $connector);
1919
$ssl = new SecureConnector($proxy, $loop);
2020

21-
$ssl->create('google.com', 443)->then(function (Stream $stream) {
21+
$ssl->connect('google.com:443')->then(function (ConnectionInterface $stream) {
2222
$stream->write("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n");
2323
$stream->on('data', function ($chunk) {
2424
echo $chunk;

examples/02-optional-proxy-https.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
// network protocol otherwise.
99

1010
use Clue\React\HttpProxy\ProxyConnector;
11-
use React\Stream\Stream;
1211
use React\SocketClient\TcpConnector;
1312
use React\SocketClient\SecureConnector;
1413
use React\SocketClient\DnsConnector;
1514
use React\Dns\Resolver\Factory;
15+
use React\SocketClient\ConnectionInterface;
1616

1717
require __DIR__ . '/../vendor/autoload.php';
1818

@@ -31,7 +31,7 @@
3131
$connector = new SecureConnector($dns, $loop);
3232
}
3333

34-
$connector->create('google.com', 443)->then(function (Stream $stream) {
34+
$connector->connect('google.com:443')->then(function (ConnectionInterface $stream) {
3535
$stream->write("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n");
3636
$stream->on('data', function ($chunk) {
3737
echo $chunk;

examples/11-proxy-smtp.php

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

77
use Clue\React\HttpProxy\ProxyConnector;
8-
use React\Stream\Stream;
98
use React\SocketClient\TcpConnector;
9+
use React\SocketClient\ConnectionInterface;
1010

1111
require __DIR__ . '/../vendor/autoload.php';
1212

@@ -17,7 +17,7 @@
1717
$connector = new TcpConnector($loop);
1818
$proxy = new ProxyConnector($url, $connector);
1919

20-
$proxy->create('smtp.googlemail.com', 587)->then(function (Stream $stream) {
20+
$proxy->connect('smtp.googlemail.com:587')->then(function (ConnectionInterface $stream) {
2121
$stream->write("EHLO local\r\n");
2222
$stream->on('data', function ($chunk) use ($stream) {
2323
echo $chunk;

examples/12-proxy-smtps.php

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

1010
use Clue\React\HttpProxy\ProxyConnector;
11-
use React\Stream\Stream;
1211
use React\SocketClient\TcpConnector;
1312
use React\SocketClient\SecureConnector;
13+
use React\SocketClient\ConnectionInterface;
1414

1515
require __DIR__ . '/../vendor/autoload.php';
1616

@@ -22,7 +22,7 @@
2222
$proxy = new ProxyConnector($url, $connector);
2323
$ssl = new SecureConnector($proxy, $loop);
2424

25-
$ssl->create('smtp.googlemail.com', 465)->then(function (Stream $stream) {
25+
$ssl->connect('smtp.googlemail.com:465')->then(function (ConnectionInterface $stream) {
2626
$stream->write("EHLO local\r\n");
2727
$stream->on('data', function ($chunk) use ($stream) {
2828
echo $chunk;

src/ProxyConnector.php

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
namespace Clue\React\HttpProxy;
44

55
use React\SocketClient\ConnectorInterface;
6-
use React\Stream\Stream;
76
use Exception;
87
use InvalidArgumentException;
98
use RuntimeException;
109
use RingCentral\Psr7;
1110
use React\Promise\Deferred;
11+
use React\SocketClient\ConnectionInterface;
1212

1313
/**
1414
* A simple Connector that uses an HTTP CONNECT proxy to create plain TCP/IP connections to any destination
@@ -40,8 +40,7 @@
4040
class ProxyConnector implements ConnectorInterface
4141
{
4242
private $connector;
43-
private $proxyHost;
44-
private $proxyPort;
43+
private $proxyUri;
4544

4645
/**
4746
* Instantiate a new ProxyConnector which uses the given $proxyUrl
@@ -61,7 +60,7 @@ public function __construct($proxyUrl, ConnectorInterface $connector)
6160
}
6261

6362
$parts = parse_url($proxyUrl);
64-
if (!$parts || !isset($parts['host'])) {
63+
if (!$parts || !isset($parts['scheme'], $parts['host'])) {
6564
throw new InvalidArgumentException('Invalid proxy URL');
6665
}
6766

@@ -70,13 +69,51 @@ public function __construct($proxyUrl, ConnectorInterface $connector)
7069
}
7170

7271
$this->connector = $connector;
73-
$this->proxyHost = $parts['host'];
74-
$this->proxyPort = $parts['port'];
72+
$this->proxyUri = $parts['host'] . ':' . $parts['port'];
7573
}
7674

77-
public function create($host, $port)
75+
public function connect($uri)
7876
{
79-
return $this->connector->create($this->proxyHost, $this->proxyPort)->then(function (Stream $stream) use ($host, $port) {
77+
if (strpos($uri, '://') === false) {
78+
$uri = 'tcp://' . $uri;
79+
}
80+
81+
$parts = parse_url($uri);
82+
if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') {
83+
return Promise\reject(new InvalidArgumentException('Invalid target URI specified'));
84+
}
85+
86+
$host = trim($parts['host'], '[]');
87+
$port = $parts['port'];
88+
89+
// construct URI to HTTP CONNECT proxy server to connect to
90+
$proxyUri = $this->proxyUri;
91+
92+
// append path from URI if given
93+
if (isset($parts['path'])) {
94+
$proxyUri .= $parts['path'];
95+
}
96+
97+
// parse query args
98+
$args = array();
99+
if (isset($parts['query'])) {
100+
parse_str($parts['query'], $args);
101+
}
102+
103+
// append hostname from URI to query string unless explicitly given
104+
if (!isset($args['hostname'])) {
105+
$args['hostname'] = $parts['host'];
106+
}
107+
108+
// append query string
109+
$proxyUri .= '?' . http_build_query($args, '', '&');;
110+
111+
// append fragment from URI if given
112+
if (isset($parts['fragment'])) {
113+
$proxyUri .= '#' . $parts['fragment'];
114+
}
115+
116+
return $this->connector->connect($proxyUri)->then(function (ConnectionInterface $stream) use ($host, $port) {
80117
$deferred = new Deferred(function ($_, $reject) use ($stream) {
81118
$reject(new RuntimeException('Operation canceled while waiting for response from proxy'));
82119
$stream->close();

tests/FunctionalTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public function testPlainGoogleDoesNotAcceptConnectMethod()
3131
{
3232
$proxy = new ProxyConnector('google.com', $this->dnsConnector);
3333

34-
$promise = $proxy->create('google.com', 80);
34+
$promise = $proxy->connect('google.com:80');
3535

3636
$this->setExpectedException('RuntimeException', 'Method Not Allowed', 405);
3737
Block\await($promise, $this->loop, 3.0);
@@ -46,7 +46,7 @@ public function testSecureGoogleDoesNotAcceptConnectMethod()
4646
$secure = new SecureConnector($this->dnsConnector, $this->loop);
4747
$proxy = new ProxyConnector('google.com:443', $secure);
4848

49-
$promise = $proxy->create('google.com', 80);
49+
$promise = $proxy->connect('google.com:80');
5050

5151
$this->setExpectedException('RuntimeException', 'Method Not Allowed', 405);
5252
Block\await($promise, $this->loop, 3.0);
@@ -56,7 +56,7 @@ public function testSecureGoogleDoesNotAcceptPlainStream()
5656
{
5757
$proxy = new ProxyConnector('google.com:443', $this->dnsConnector);
5858

59-
$promise = $proxy->create('google.com', 80);
59+
$promise = $proxy->connect('google.com:80');
6060

6161
$this->setExpectedException('RuntimeException', 'Connection to proxy lost');
6262
Block\await($promise, $this->loop, 3.0);

0 commit comments

Comments
 (0)