Skip to content

Commit 6b84e6a

Browse files
committed
Initial commit
0 parents  commit 6b84e6a

14 files changed

+868
-0
lines changed

Diff for: .gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/vendor/
2+
/composer.lock

Diff for: .travis.yml

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
language: php
2+
3+
php:
4+
- 5.3
5+
- 5.4
6+
- 5.5
7+
- 5.6
8+
- 7
9+
- hhvm
10+
11+
sudo: false
12+
13+
install:
14+
- composer install --no-interaction
15+
16+
script:
17+
- phpunit --coverage-text

Diff for: LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2016 Christian Lück
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is furnished
10+
to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

Diff for: README.md

+202
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
# clue/http-proxy-react [![Build Status](https://travis-ci.org/clue/php-http-proxy-react.svg?branch=master)](https://travis-ci.org/clue/php-http-proxy-react)
2+
3+
Async HTTP CONNECT proxy connector, use any TCP/IP protocol through an HTTP proxy server, built on top of React PHP.
4+
5+
**Table of contents**
6+
7+
* [Quickstart example](#quickstart-example)
8+
* [Usage](#usage)
9+
* [ConnectorInterface](#connectorinterface)
10+
* [create()](#create)
11+
* [ProxyConnector](#proxyconnector)
12+
* [Install](#install)
13+
* [License](#license)
14+
* [More](#more)
15+
16+
### Quickstart example
17+
18+
The following example code demonstrates how this library can be used to send a
19+
secure HTTPS request to google.com through a local HTTP proxy server:
20+
21+
```php
22+
$loop = React\EventLoop\Factory::create();
23+
$connector = new TcpConnector($loop);
24+
$proxy = new ProxyConnector('127.0.0.1:8080', $connector);
25+
$ssl = new SecureConnector($proxy, $loop);
26+
27+
$ssl->create('google.com', 443)->then(function (Stream $stream) {
28+
$stream->write("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n");
29+
$stream->on('data', function ($chunk) {
30+
echo $chunk;
31+
});
32+
}, 'printf');
33+
34+
$loop->run();
35+
```
36+
37+
See also the [examples](examples).
38+
39+
## Usage
40+
41+
### ConnectorInterface
42+
43+
The `ConnectorInterface` is responsible for providing an interface for
44+
establishing streaming connections, such as a normal TCP/IP connection.
45+
46+
In order to use this library, you should understand how this integrates with its
47+
ecosystem.
48+
This base interface is actually defined in React's
49+
[SocketClient component](https://github.com/reactphp/socket-client) and used
50+
throughout React's ecosystem.
51+
52+
Most higher-level components (such as HTTP, database or other networking
53+
service clients) accept an instance implementing this interface to create their
54+
TCP/IP connection to the underlying networking service.
55+
This is usually done via dependency injection, so its fairly simple to actually
56+
swap this implementation against this library in order to connect through an
57+
HTTP CONNECT proxy.
58+
59+
The interface only offers a single method:
60+
61+
#### create()
62+
63+
The `create(string $host, int $port): PromiseInterface<Stream>` method
64+
can be used to establish a streaming connection.
65+
It returns a [Promise](https://github.com/reactphp/promise) which either
66+
fulfills with a [Stream](https://github.com/reactphp/stream) or
67+
rejects with an `Exception`:
68+
69+
```php
70+
$connector->create('google.com', 443)->then(
71+
function (Stream $stream) {
72+
// connection successfully established
73+
},
74+
function (Exception $error) {
75+
// failed to connect due to $error
76+
}
77+
);
78+
```
79+
80+
### ProxyConnector
81+
82+
The `ProxyConnector` is responsible for creating plain TCP/IP connections to
83+
any destination by using an intermediary HTTP CONNECT proxy.
84+
85+
```
86+
[you] -> [proxy] -> [destination]
87+
```
88+
89+
Its constructor simply accepts an HTTP proxy URL and a connector used to connect
90+
to the proxy server address:
91+
92+
```php
93+
$connector = new TcpConnector($loop);
94+
$proxy = new ProxyConnector('127.0.0.1:8080', $connector);
95+
```
96+
97+
The proxy URL may or may not contain a scheme and port definition. The default
98+
port will be `80` for HTTP (or `443` for HTTPS), but many common HTTP proxy
99+
servers use custom ports.
100+
In its most simple form, the given connector will be a
101+
[`TcpConnector`](https://github.com/reactphp/socket-client#tcpconnector) if you
102+
want to connect to a given IP address as above.
103+
104+
This is the main class in this package.
105+
Because it implements the the [`ConnectorInterface`](#connectorinterface), it
106+
can simply be used in place of a normal connector.
107+
This makes it fairly simple to add HTTP CONNECT proxy support to pretty much any
108+
higher-level component:
109+
110+
```diff
111+
- $client = new SomeClient($connector);
112+
+ $proxy = new ProxyConnector('127.0.0.1:8080', $connector);
113+
+ $client = new SomeClient($proxy);
114+
```
115+
116+
This is most frequently used to issue HTTPS requests to your destination.
117+
However, this is actually performed on a higher protocol layer and this
118+
connector is actually inherently a general-purpose plain TCP/IP connector:
119+
120+
```php
121+
$proxy = new ProxyConnector('127.0.0.1:8080', $connector);
122+
123+
$proxy->create('smtp.googlemail.com', 587)->then(function (Stream $stream) {
124+
$stream->write("EHLO local\r\n");
125+
$stream->on('data', function ($chunk) use ($stream) {
126+
echo $chunk;
127+
});
128+
});
129+
```
130+
131+
Note that HTTP CONNECT proxies often restrict which ports one may connect to.
132+
Many (public) proxy servers do in fact limit this to HTTPS (443) only.
133+
134+
If you want to establish a TLS connection (such as HTTPS) between you and
135+
your destination, you may want to wrap this connector in a
136+
[`SecureConnector`](https://github.com/reactphp/socket-client#secureconnector)
137+
instance:
138+
139+
```php
140+
$proxy = new ProxyConnector('127.0.0.1:8080', $connector);
141+
$ssl = new SecureConnector($proxy, $loop);
142+
143+
$ssl->create('smtp.googlemail.com', 465)->then(function (Stream $stream) {
144+
$stream->write("EHLO local\r\n");
145+
$stream->on('data', function ($chunk) use ($stream) {
146+
echo $chunk;
147+
});
148+
});
149+
```
150+
151+
Note that communication between the client and the proxy is usually via an
152+
unencrypted, plain TCP/IP HTTP connection. Note that this is the most common
153+
setup, because you can still establish a TLS connection between you and the
154+
destination host as above.
155+
156+
If you want to connect to a (rather rare) HTTPS proxy, you may want use its
157+
HTTPS port (443) and use a
158+
[`SecureConnector`](https://github.com/reactphp/socket-client#secureconnector)
159+
instance to create a secure connection to the proxy:
160+
161+
```php
162+
$ssl = new SecureConnector($connector, $loop);
163+
$proxy = new ProxyConnector('127.0.0.1:443', $ssl);
164+
165+
$proxy->create('smtp.googlemail.com', 587);
166+
```
167+
168+
## Install
169+
170+
The recommended way to install this library is [through Composer](http://getcomposer.org).
171+
[New to Composer?](http://getcomposer.org/doc/00-intro.md)
172+
173+
This will install the latest supported version:
174+
175+
```bash
176+
$ composer require clue/http-proxy-react:dev-master
177+
```
178+
179+
## License
180+
181+
MIT
182+
183+
## More
184+
185+
* If you want to learn more about processing streams of data, refer to the
186+
documentation of the underlying
187+
[react/stream](https://github.com/reactphp/stream) component.
188+
* If you want to learn more about how the
189+
[`ConnectorInterface`](#connectorinterface) and its usual implementations look
190+
like, refer to the documentation of the underlying
191+
[react/socket-client](https://github.com/reactphp/socket-client) component.
192+
* As an alternative to an HTTP CONNECT proxy, you may also want to look into
193+
using a SOCKS (SOCKS4/SOCKS5) proxy instead.
194+
You may want to use [clue/socks-react](https://github.com/clue/php-socks-react)
195+
which also provides an implementation of the
196+
[`ConnectorInterface`](#connectorinterface) so that supporting either proxy
197+
protocol should be fairly trivial.
198+
* If you're dealing with publish proxies, you'll likely have to work with mixed
199+
quality and unreliable proxies. You may want to look into using
200+
[clue/connection-manager-extra](https://github.com/clue/php-connection-manager-extra)
201+
which allows retrying unreliable ones, implying connection timeouts,
202+
concurrently working with multiple connectors and more.

Diff for: composer.json

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "clue/http-proxy-react",
3+
"description": "Async HTTP CONNECT proxy connector, use any TCP/IP protocol through an HTTP proxy server, built on top of React PHP",
4+
"keywords": ["HTTP", "CONNECT", "proxy", "ReactPHP", "async"],
5+
"homepage": "https://github.com/clue/php-http-proxy-react",
6+
"license": "MIT",
7+
"authors": [
8+
{
9+
"name": "Christian Lück",
10+
"email": "[email protected]"
11+
}
12+
],
13+
"autoload": {
14+
"psr-4": { "Clue\\React\\HttpProxy\\": "src/" }
15+
},
16+
"autoload-dev": {
17+
"psr-4": { "Tests\\Clue\\React\\HttpProxy\\": "tests/" }
18+
},
19+
"require": {
20+
"php": ">=5.3",
21+
"react/socket-client": "^0.5 || ^0.4 || ^0.3",
22+
"react/event-loop": "^0.4 || ^0.3",
23+
"react/stream": "^0.4 || ^0.3",
24+
"react/promise": " ^2.1 || ^1.2",
25+
"ringcentral/psr7": "^1.2"
26+
},
27+
"require-dev": {
28+
"react/socket-client": "^0.5",
29+
"clue/block-react": "^1.1"
30+
}
31+
}

Diff for: examples/01-proxy-https.php

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
// A simple example which requests https://google.com/ through an HTTP CONNECT proxy.
4+
// The proxy can be given as first argument and defaults to localhost:8080 otherwise.
5+
6+
use Clue\React\HttpProxy\ProxyConnector;
7+
use React\Stream\Stream;
8+
use React\SocketClient\TcpConnector;
9+
use React\SocketClient\SecureConnector;
10+
11+
require __DIR__ . '/../vendor/autoload.php';
12+
13+
$url = isset($argv[1]) ? $argv[1] : '127.0.0.1:8080';
14+
15+
$loop = React\EventLoop\Factory::create();
16+
17+
$connector = new TcpConnector($loop);
18+
$proxy = new ProxyConnector($url, $connector);
19+
$ssl = new SecureConnector($proxy, $loop);
20+
21+
$ssl->create('google.com', 443)->then(function (Stream $stream) {
22+
$stream->write("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n");
23+
$stream->on('data', function ($chunk) {
24+
echo $chunk;
25+
});
26+
}, 'printf');
27+
28+
$loop->run();

Diff for: examples/02-optional-proxy-https.php

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
// A simple example which requests https://google.com/ either directly or through
4+
// an HTTP CONNECT proxy.
5+
// The Proxy can be given as first argument or does not use a proxy otherwise.
6+
// This example highlights how changing from direct connection to using a proxy
7+
// actually adds very little complexity and does not mess with your actual
8+
// network protocol otherwise.
9+
10+
use Clue\React\HttpProxy\ProxyConnector;
11+
use React\Stream\Stream;
12+
use React\SocketClient\TcpConnector;
13+
use React\SocketClient\SecureConnector;
14+
use React\SocketClient\DnsConnector;
15+
use React\Dns\Resolver\Factory;
16+
17+
require __DIR__ . '/../vendor/autoload.php';
18+
19+
$loop = React\EventLoop\Factory::create();
20+
21+
$tcp = new TcpConnector($loop);
22+
$dnsFactory = new Factory();
23+
$resolver = $dnsFactory->create('8.8.8.8', $loop);
24+
$dns = new DnsConnector($tcp, $resolver);
25+
26+
// first argument given? use this as the proxy URL
27+
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);
32+
}
33+
34+
$connector->create('google.com', 443)->then(function (Stream $stream) {
35+
$stream->write("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n");
36+
$stream->on('data', function ($chunk) {
37+
echo $chunk;
38+
});
39+
}, 'printf');
40+
41+
$loop->run();

Diff for: examples/11-proxy-smtp.php

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
// A simple example which uses a plain SMTP connection to Googlemail through a HTTP CONNECT proxy.
4+
// Proxy can be given as first argument and defaults to localhost:8080 otherwise.
5+
// Please note that MANY public proxies do not allow SMTP connections, YMMV.
6+
7+
use Clue\React\HttpProxy\ProxyConnector;
8+
use React\Stream\Stream;
9+
use React\SocketClient\TcpConnector;
10+
11+
require __DIR__ . '/../vendor/autoload.php';
12+
13+
$url = isset($argv[1]) ? $argv[1] : '127.0.0.1:8080';
14+
15+
$loop = React\EventLoop\Factory::create();
16+
17+
$connector = new TcpConnector($loop);
18+
$proxy = new ProxyConnector($url, $connector);
19+
20+
$proxy->create('smtp.googlemail.com', 587)->then(function (Stream $stream) {
21+
$stream->write("EHLO local\r\n");
22+
$stream->on('data', function ($chunk) use ($stream) {
23+
echo $chunk;
24+
$stream->write("QUIT\r\n");
25+
});
26+
}, 'printf');
27+
28+
$loop->run();

0 commit comments

Comments
 (0)