diff options
Diffstat (limited to 'vendor/clue/http-proxy-react/README.md')
-rw-r--r-- | vendor/clue/http-proxy-react/README.md | 510 |
1 files changed, 510 insertions, 0 deletions
diff --git a/vendor/clue/http-proxy-react/README.md b/vendor/clue/http-proxy-react/README.md new file mode 100644 index 0000000..b5337c1 --- /dev/null +++ b/vendor/clue/http-proxy-react/README.md @@ -0,0 +1,510 @@ +# clue/reactphp-http-proxy + +[](https://github.com/clue/reactphp-http-proxy/actions) +[](https://packagist.org/packages/clue/http-proxy-react) + +Async HTTP proxy connector, tunnel any TCP/IP-based protocol through an HTTP +CONNECT proxy server, built on top of [ReactPHP](https://reactphp.org/). + +HTTP CONNECT proxy servers (also commonly known as "HTTPS proxy" or "SSL proxy") +are commonly used to tunnel HTTPS traffic through an intermediary ("proxy"), to +conceal the origin address (anonymity) or to circumvent address blocking +(geoblocking). While many (public) HTTP CONNECT proxy servers often limit this +to HTTPS port `443` only, this can technically be used to tunnel any +TCP/IP-based protocol (HTTP, SMTP, IMAP etc.). +This library provides a simple API to create these tunneled connections for you. +Because it implements ReactPHP's standard +[`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface), +it can simply be used in place of a normal connector. +This makes it fairly simple to add HTTP CONNECT proxy support to pretty much any +existing higher-level protocol implementation. + +* **Async execution of connections** - + Send any number of HTTP CONNECT requests in parallel and process their + responses as soon as results come in. + The Promise-based design provides a *sane* interface to working with out of + order responses and possible connection errors. +* **Standard interfaces** - + Allows easy integration with existing higher-level components by implementing + ReactPHP's standard + [`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface). +* **Lightweight, SOLID design** - + Provides a thin abstraction that is [*just good enough*](https://en.wikipedia.org/wiki/Principle_of_good_enough) + and does not get in your way. + Builds on top of well-tested components and well-established concepts instead of reinventing the wheel. +* **Good test coverage** - + Comes with an automated tests suite and is regularly tested against actual proxy servers in the wild. + +**Table of contents** + +* [Support us](#support-us) +* [Quickstart example](#quickstart-example) +* [Usage](#usage) + * [ProxyConnector](#proxyconnector) + * [Plain TCP connections](#plain-tcp-connections) + * [Secure TLS connections](#secure-tls-connections) + * [HTTP requests](#http-requests) + * [Connection timeout](#connection-timeout) + * [DNS resolution](#dns-resolution) + * [Authentication](#authentication) + * [Advanced HTTP headers](#advanced-http-headers) + * [Advanced secure proxy connections](#advanced-secure-proxy-connections) + * [Advanced Unix domain sockets](#advanced-unix-domain-sockets) +* [Install](#install) +* [Tests](#tests) +* [License](#license) +* [More](#more) + +## Support us + +We invest a lot of time developing, maintaining and updating our awesome +open-source projects. You can help us sustain this high-quality of our work by +[becoming a sponsor on GitHub](https://github.com/sponsors/clue). Sponsors get +numerous benefits in return, see our [sponsoring page](https://github.com/sponsors/clue) +for details. + +Let's take these projects to the next level together! 🚀 + +## Quickstart example + +The following example code demonstrates how this library can be used to send a +secure HTTPS request to google.com through a local HTTP proxy server: + +```php +<?php + +require __DIR__ . '/vendor/autoload.php'; + +$proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080'); + +$connector = new React\Socket\Connector(array( + 'tcp' => $proxy, + 'dns' => false +)); + +$browser = new React\Http\Browser($connector); + +$browser->get('https://google.com/')->then(function (Psr\Http\Message\ResponseInterface $response) { + var_dump($response->getHeaders(), (string) $response->getBody()); +}, function (Exception $e) { + echo 'Error: ' . $e->getMessage() . PHP_EOL; +}); +``` + +See also the [examples](examples). + +## Usage + +### ProxyConnector + +The `ProxyConnector` is responsible for creating plain TCP/IP connections to +any destination by using an intermediary HTTP CONNECT proxy. + +``` +[you] -> [proxy] -> [destination] +``` + +Its constructor simply accepts an HTTP proxy URL with the proxy server address: + +```php +$proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080'); +``` + +The proxy URL may or may not contain a scheme and port definition. The default +port will be `80` for HTTP (or `443` for HTTPS), but many common HTTP proxy +servers use custom ports (often the alternative HTTP port `8080`). + +If you need custom connector settings (DNS resolution, TLS parameters, timeouts, +proxy servers etc.), you can explicitly pass a custom instance of the +[`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface): + +```php +$connector = new React\Socket\Connector(array( + 'dns' => '127.0.0.1', + 'tcp' => array( + 'bindto' => '192.168.10.1:0' + ), + 'tls' => array( + 'verify_peer' => false, + 'verify_peer_name' => false + ) +)); + +$proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080', $connector); +``` + +This is the main class in this package. +Because it implements ReactPHP's standard +[`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface), +it can simply be used in place of a normal connector. +Accordingly, it provides only a single public method, the +[`connect()`](https://github.com/reactphp/socket#connect) method. +The `connect(string $uri): PromiseInterface<ConnectionInterface, Exception>` +method can be used to establish a streaming connection. +It returns a [Promise](https://github.com/reactphp/promise) which either +fulfills with a [ConnectionInterface](https://github.com/reactphp/socket#connectioninterface) +on success or rejects with an `Exception` on error. + +This makes it fairly simple to add HTTP CONNECT proxy support to pretty much any +higher-level component: + +```diff +- $acme = new AcmeApi($connector); ++ $proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080', $connector); ++ $acme = new AcmeApi($proxy); +``` + +#### Plain TCP connections + +HTTP CONNECT proxies are most frequently used to issue HTTPS requests to your destination. +However, this is actually performed on a higher protocol layer and this +connector is actually inherently a general-purpose plain TCP/IP connector. +As documented above, you can simply invoke its `connect()` method to establish +a streaming plain TCP/IP connection and use any higher level protocol like so: + +```php +$proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080'); + +$proxy->connect('tcp://smtp.googlemail.com:587')->then(function (React\Socket\ConnectionInterface $connection) { + $connection->write("EHLO local\r\n"); + $connection->on('data', function ($chunk) use ($connection) { + echo $chunk; + }); +}); +``` + +You can either use the `ProxyConnector` directly or you may want to wrap this connector +in ReactPHP's [`Connector`](https://github.com/reactphp/socket#connector): + +```php +$proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080'); + +$connector = new React\Socket\Connector(array( + 'tcp' => $proxy, + 'dns' => false +)); + +$connector->connect('tcp://smtp.googlemail.com:587')->then(function (React\Socket\ConnectionInterface $connection) { + $connection->write("EHLO local\r\n"); + $connection->on('data', function ($chunk) use ($connection) { + echo $chunk; + }); +}); +``` + +Note that HTTP CONNECT proxies often restrict which ports one may connect to. +Many (public) proxy servers do in fact limit this to HTTPS (443) only. + +#### Secure TLS connections + +This class can also be used if you want to establish a secure TLS connection +(formerly known as SSL) between you and your destination, such as when using +secure HTTPS to your destination site. You can simply wrap this connector in +ReactPHP's [`Connector`](https://github.com/reactphp/socket#connector): + +```php +$proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080'); + +$connector = new React\Socket\Connector(array( + 'tcp' => $proxy, + 'dns' => false +)); + +$connector->connect('tls://smtp.googlemail.com:465')->then(function (React\Socket\ConnectionInterface $connection) { + $connection->write("EHLO local\r\n"); + $connection->on('data', function ($chunk) use ($connection) { + echo $chunk; + }); +}); +``` + +> Note how secure TLS connections are in fact entirely handled outside of + this HTTP CONNECT client implementation. + +#### HTTP requests + +This library also allows you to send HTTP requests through an HTTP CONNECT proxy server. + +In order to send HTTP requests, you first have to add a dependency for +[ReactPHP's async HTTP client](https://github.com/reactphp/http#client-usage). +This allows you to send both plain HTTP and TLS-encrypted HTTPS requests like this: + +```php +$proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080'); + +$connector = new React\Socket\Connector(array( + 'tcp' => $proxy, + 'dns' => false +)); + +$browser = new React\Http\Browser($connector); + +$browser->get('https://example.com/')->then(function (Psr\Http\Message\ResponseInterface $response) { + var_dump($response->getHeaders(), (string) $response->getBody()); +}, function (Exception $e) { + echo 'Error: ' . $e->getMessage() . PHP_EOL; +}); +``` + +See also [ReactPHP's HTTP client](https://github.com/reactphp/http#client-usage) +and any of the [examples](examples) for more details. + +#### Connection timeout + +By default, the `ProxyConnector` does not implement any timeouts for establishing remote +connections. +Your underlying operating system may impose limits on pending and/or idle TCP/IP +connections, anywhere in a range of a few minutes to several hours. + +Many use cases require more control over the timeout and likely values much +smaller, usually in the range of a few seconds only. + +You can use ReactPHP's [`Connector`](https://github.com/reactphp/socket#connector) +to decorate any given `ConnectorInterface` instance. +It provides the same `connect()` method, but will automatically reject the +underlying connection attempt if it takes too long: + +```php +$proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080'); + +$connector = new React\Socket\Connector(array( + 'tcp' => $proxy, + 'dns' => false, + 'timeout' => 3.0 +)); + +$connector->connect('tcp://google.com:80')->then(function ($connection) { + // connection succeeded within 3.0 seconds +}); +``` + +See also any of the [examples](examples). + +> Note how the connection timeout is in fact entirely handled outside of this + HTTP CONNECT client implementation. + +#### DNS resolution + +By default, the `ProxyConnector` does not perform any DNS resolution at all and simply +forwards any hostname you're trying to connect to the remote proxy server. +The remote proxy server is thus responsible for looking up any hostnames via DNS +(this default mode is thus called *remote DNS resolution*). + +As an alternative, you can also send the destination IP to the remote proxy +server. +In this mode you either have to stick to using IPs only (which is ofen unfeasable) +or perform any DNS lookups locally and only transmit the resolved destination IPs +(this mode is thus called *local DNS resolution*). + +The default *remote DNS resolution* is useful if your local `ProxyConnector` either can +not resolve target hostnames because it has no direct access to the internet or +if it should not resolve target hostnames because its outgoing DNS traffic might +be intercepted. + +As noted above, the `ProxyConnector` defaults to using remote DNS resolution. +However, wrapping the `ProxyConnector` in ReactPHP's +[`Connector`](https://github.com/reactphp/socket#connector) actually +performs local DNS resolution unless explicitly defined otherwise. +Given that remote DNS resolution is assumed to be the preferred mode, all +other examples explicitly disable DNS resolution like this: + +```php +$proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080'); + +$connector = new React\Socket\Connector(array( + 'tcp' => $proxy, + 'dns' => false +)); +``` + +If you want to explicitly use *local DNS resolution*, you can use the following code: + +```php +$proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080'); + +// set up Connector which uses Google's public DNS (8.8.8.8) +$connector = new React\Socket\Connector(array( + 'tcp' => $proxy, + 'dns' => '8.8.8.8' +)); +``` + +> Note how local DNS resolution is in fact entirely handled outside of this + HTTP CONNECT client implementation. + +#### Authentication + +If your HTTP proxy server requires authentication, you may pass the username and +password as part of the HTTP proxy URL like this: + +```php +$proxy = new Clue\React\HttpProxy\ProxyConnector('alice:password@127.0.0.1:8080'); +``` + +Note that both the username and password must be percent-encoded if they contain +special characters: + +```php +$user = 'he:llo'; +$pass = 'p@ss'; +$url = rawurlencode($user) . ':' . rawurlencode($pass) . '@127.0.0.1:8080'; + +$proxy = new Clue\React\HttpProxy\ProxyConnector($url); +``` + +> The authentication details will be used for basic authentication and will be + transferred in the `Proxy-Authorization` HTTP request header for each + connection attempt. + If the authentication details are missing or not accepted by the remote HTTP + proxy server, it is expected to reject each connection attempt with a + `407` (Proxy Authentication Required) response status code and an exception + error code of `SOCKET_EACCES` (13). + +#### Advanced HTTP headers + +The `ProxyConnector` constructor accepts an optional array of custom request +headers to send in the `CONNECT` request. This can be useful if you're using a +custom proxy setup or authentication scheme if the proxy server does not support +basic [authentication](#authentication) as documented above. This is rarely used +in practice, but may be useful for some more advanced use cases. In this case, +you may simply pass an assoc array of additional request headers like this: + +```php +$proxy = new Clue\React\HttpProxy\ProxyConnector( + '127.0.0.1:8080', + null, + array( + 'Proxy-Authorization' => 'Bearer abc123', + 'User-Agent' => 'ReactPHP' + ) +); +``` + +#### Advanced secure proxy connections + +Note that communication between the client and the proxy is usually via an +unencrypted, plain TCP/IP HTTP connection. Note that this is the most common +setup, because you can still establish a TLS connection between you and the +destination host as above. + +If you want to connect to a (rather rare) HTTPS proxy, you may want use the +`https://` scheme (HTTPS default port 443) to create a secure connection to the proxy: + +```php +$proxy = new Clue\React\HttpProxy\ProxyConnector('https://127.0.0.1:443'); + +$proxy->connect('tcp://smtp.googlemail.com:587'); +``` + +#### Advanced Unix domain sockets + +HTTP CONNECT proxy servers support forwarding TCP/IP based connections and +higher level protocols. +In some advanced cases, it may be useful to let your HTTP CONNECT proxy server +listen on a Unix domain socket (UDS) path instead of a IP:port combination. +For example, this allows you to rely on file system permissions instead of +having to rely on explicit [authentication](#authentication). + +You can simply use the `http+unix://` URI scheme like this: + +```php +$proxy = new Clue\React\HttpProxy\ProxyConnector('http+unix:///tmp/proxy.sock'); + +$proxy->connect('tcp://google.com:80')->then(function (React\Socket\ConnectionInterface $connection) { + // connected… +}); +``` + +Similarly, you can also combine this with [authentication](#authentication) +like this: + +```php +$proxy = new Clue\React\HttpProxy\ProxyConnector('http+unix://alice:password@/tmp/proxy.sock'); +``` + +> Note that Unix domain sockets (UDS) are considered advanced usage and PHP only + has limited support for this. + In particular, enabling [secure TLS](#secure-tls-connections) may not be + supported. + +> Note that the HTTP CONNECT protocol does not support the notion of UDS paths. + The above works reasonably well because UDS is only used for the connection between + client and proxy server and the path will not actually passed over the protocol. + This implies that this does not support connecting to UDS destination paths. + +## Install + +The recommended way to install this library is [through Composer](https://getcomposer.org/). +[New to Composer?](https://getcomposer.org/doc/00-intro.md) + +This project follows [SemVer](https://semver.org/). +This will install the latest supported version: + +```bash +composer require clue/http-proxy-react:^1.8 +``` + +See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. + +This project aims to run on any platform and thus does not require any PHP +extensions and supports running on legacy PHP 5.3 through current PHP 8+ and +HHVM. +It's *highly recommended to use the latest supported PHP version* for this project. + +## Tests + +To run the test suite, you first need to clone this repo and then install all +dependencies [through Composer](https://getcomposer.org/): + +```bash +composer install +``` + +To run the test suite, go to the project root and run: + +```bash +vendor/bin/phpunit +``` + +The test suite contains tests that rely on a working internet connection, +alternatively you can also run it like this: + +```bash +vendor/bin/phpunit --exclude-group internet +``` + +## License + +This project is released under the permissive [MIT license](LICENSE). + +> Did you know that I offer custom development services and issuing invoices for + sponsorships of releases and for contributions? Contact me (@clue) for details. + +## More + +* If you want to learn more about how the + [`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface) + and its usual implementations look like, refer to the documentation of the underlying + [react/socket](https://github.com/reactphp/socket) component. +* If you want to learn more about processing streams of data, refer to the + documentation of the underlying + [react/stream](https://github.com/reactphp/stream) component. +* As an alternative to an HTTP CONNECT proxy, you may also want to look into + using a SOCKS (SOCKS4/SOCKS5) proxy instead. + You may want to use [clue/reactphp-socks](https://github.com/clue/reactphp-socks) + which also provides an implementation of the same + [`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface) + so that supporting either proxy protocol should be fairly trivial. +* As an alternative to an HTTP CONNECT proxy, you may also want to look into + using an SSH proxy (SSH tunnel) instead. + You may want to use [clue/reactphp-ssh-proxy](https://github.com/clue/reactphp-ssh-proxy) + which also provides an implementation of the same + [`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface) + so that supporting either proxy protocol should be fairly trivial. +* If you're dealing with public proxies, you'll likely have to work with mixed + quality and unreliable proxies. You may want to look into using + [clue/reactphp-connection-manager-extra](https://github.com/clue/reactphp-connection-manager-extra) + which allows retrying unreliable ones, implying connection timeouts, + concurrently working with multiple connectors and more. +* If you're looking for an end-user HTTP CONNECT proxy server daemon, you may + want to use [LeProxy](https://leproxy.org/). |