diff --git a/README.md b/README.md index f8fc9cc..de08786 100644 --- a/README.md +++ b/README.md @@ -84,9 +84,7 @@ Once [installed](#install), you can use the following code to create a connectio to google.com via a local SOCKS proxy server: ```php -$loop = React\EventLoop\Factory::create(); -$connector = new React\Socket\Connector($loop); -$proxy = new Clue\React\Socks\Client('127.0.0.1:1080', $connector); +$proxy = new Clue\React\Socks\Client('127.0.0.1:1080'); $proxy->connect('tcp://www.google.com:80')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write("GET / HTTP/1.0\r\n\r\n"); @@ -95,8 +93,6 @@ $proxy->connect('tcp://www.google.com:80')->then(function (React\Socket\Connecti echo $chunk; }); }); - -$loop->run(); ``` If you're not already running any other [SOCKS proxy server](#servers), @@ -104,16 +100,12 @@ you can use the following code to create a SOCKS proxy server listening for connections on `localhost:1080`: ```php -$loop = React\EventLoop\Factory::create(); - // start a new SOCKS proxy server -$server = new Clue\React\Socks\Server($loop); +$server = new Clue\React\Socks\Server(); // listen on localhost:1080 -$socket = new React\Socket\Server('127.0.0.1:1080', $loop); +$socket = new React\Socket\Server('127.0.0.1:1080'); $server->listen($socket); - -$loop->run(); ``` See also the [examples](examples). @@ -123,22 +115,17 @@ See also the [examples](examples). ### Client The `Client` is responsible for communication with your SOCKS server instance. -Its constructor simply accepts an SOCKS proxy URI and a connector used to -connect to the SOCKS proxy server address. -In its most simple form, you can simply pass ReactPHP's -[`Connector`](https://github.com/reactphp/socket#connector) -like this: +Its constructor simply accepts a SOCKS proxy URI with the SOCKS proxy server address: ```php -$connector = new React\Socket\Connector($loop); -$proxy = new Clue\React\Socks\Client('127.0.0.1:1080', $connector); +$proxy = new Clue\React\Socks\Client('127.0.0.1:1080'); ``` You can omit the port if you're using the default SOCKS port 1080: ```php -$proxy = new Clue\React\Socks\Client('127.0.0.1', $connector); +$proxy = new Clue\React\Socks\Client('127.0.0.1'); ``` If you need custom connector settings (DNS resolution, TLS parameters, timeouts, @@ -146,7 +133,7 @@ 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($loop, array( +$connector = new React\Socket\Connector(null, array( 'dns' => '127.0.0.1', 'tcp' => array( 'bindto' => '192.168.10.1:0' @@ -167,6 +154,7 @@ 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 SOCKS proxy support to pretty much any higher-level component: @@ -185,10 +173,7 @@ 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\Socks\Client( - '127.0.0.1:1080', - new React\Socket\Connector($loop) -); +$proxy = new Clue\React\Socks\Client('127.0.0.1:1080'); $proxy->connect('tcp://www.google.com:80')->then(function (React\Socket\ConnectionInterface $connection) { echo 'connected to www.google.com:80'; @@ -204,12 +189,9 @@ You can either use the `Client` directly or you may want to wrap this connector in ReactPHP's [`Connector`](https://github.com/reactphp/socket#connector): ```php -$proxy = new Clue\React\Socks\Client( - '127.0.0.1:1080', - new React\Socket\Connector($loop) -); +$proxy = new Clue\React\Socks\Client('127.0.0.1:1080'); -$connector = new React\Socket\Connector($loop, array( +$connector = new React\Socket\Connector(null, array( 'tcp' => $proxy, 'dns' => false )); @@ -246,21 +228,16 @@ the resulting promise. 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) or the -low-level [`SecureConnector`](https://github.com/reactphp/socket#secureconnector): +ReactPHP's [`Connector`](https://github.com/reactphp/socket#connector): ```php -$proxy = new Clue\React\Socks\Client( - '127.0.0.1:1080', - new React\Socket\Connector($loop) -); +$proxy = new Clue\React\Socks\Client('127.0.0.1:1080'); -$connector = new React\Socket\Connector($loop, array( +$connector = new React\Socket\Connector(null, array( 'tcp' => $proxy, 'dns' => false )); -// now create an SSL encrypted connection (notice the $ssl instead of $tcp) $connector->connect('tls://www.google.com:443')->then(function (React\Socket\ConnectionInterface $connection) { // proceed with just the plain text data // everything is encrypted/decrypted automatically @@ -275,10 +252,6 @@ $connector->connect('tls://www.google.com:443')->then(function (React\Socket\Con See also the [second example](examples). -If you use the low-level `SecureConnector`, then the `tls://` scheme can also -be omitted. -Passing any other scheme will reject the promise. - Pending connection attempts can be cancelled by canceling its pending promise as usual. @@ -290,7 +263,7 @@ You can optionally pass additional to the constructor like this: ```php -$connector = new React\Socket\Connector($loop, array( +$connector = new React\Socket\Connector(null, array( 'tcp' => $proxy, 'tls' => array( 'verify_peer' => false, @@ -310,17 +283,14 @@ In order to send HTTP requests, you first have to add a dependency for This allows you to send both plain HTTP and TLS-encrypted HTTPS requests like this: ```php -$proxy = new Clue\React\Socks\Client( - '127.0.0.1:1080', - new React\Socket\Connector($loop) -); +$proxy = new Clue\React\Socks\Client('127.0.0.1:1080'); -$connector = new React\Socket\Connector($loop, array( +$connector = new React\Socket\Connector(null, array( 'tcp' => $proxy, 'dns' => false )); -$browser = new React\Http\Browser($loop, $connector); +$browser = new React\Http\Browser(null, $connector); $browser->get('https://example.com/')->then(function (Psr\Http\Message\ResponseInterface $response) { var_dump($response->getHeaders(), (string) $response->getBody()); @@ -408,16 +378,16 @@ URI scheme acts as an alias for the default `socks://` URI scheme. ```php // all three forms are equivalent -$proxy = new Clue\React\Socks\Client('127.0.0.1', $connector); -$proxy = new Clue\React\Socks\Client('socks://127.0.0.1', $connector); -$proxy = new Clue\React\Socks\Client('socks5://127.0.0.1', $connector); +$proxy = new Clue\React\Socks\Client('127.0.0.1'); +$proxy = new Clue\React\Socks\Client('socks://127.0.0.1'); +$proxy = new Clue\React\Socks\Client('socks5://127.0.0.1'); ``` If want to explicitly set the protocol version to SOCKS4(a), you can use the URI scheme `socks4://` as part of the SOCKS URI: ```php -$proxy = new Clue\React\Socks\Client('socks4://127.0.0.1', $connector); +$proxy = new Clue\React\Socks\Client('socks4://127.0.0.1'); ``` #### DNS resolution @@ -449,12 +419,9 @@ 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\Socks\Client( - '127.0.0.1:1080', - new React\Socket\Connector($loop) -); +$proxy = new Clue\React\Socks\Client('127.0.0.1:1080'); -$connector = new React\Socket\Connector($loop, array( +$connector = new React\Socket\Connector(null, array( 'tcp' => $proxy, 'dns' => false )); @@ -464,13 +431,10 @@ If you want to explicitly use *local DNS resolution* (such as when explicitly using SOCKS4), you can use the following code: ```php -$proxy = new Clue\React\Socks\Client( - '127.0.0.1:1080', - new React\Socket\Connector($loop) -); +$proxy = new Clue\React\Socks\Client('127.0.0.1:1080'); // set up Connector which uses Google's public DNS (8.8.8.8) -$connector = new React\Socket\Connector($loop, array( +$connector = new React\Socket\Connector(null, array( 'tcp' => $proxy, 'dns' => '8.8.8.8' )); @@ -502,7 +466,7 @@ so this methods should not be used on a network where you have to worry about ea You can simply pass the authentication information as part of the SOCKS URI: ```php -$proxy = new Clue\React\Socks\Client('username:password@127.0.0.1', $connector); +$proxy = new Clue\React\Socks\Client('username:password@127.0.0.1'); ``` Note that both the username and password must be percent-encoded if they contain @@ -511,11 +475,9 @@ special characters: ```php $user = 'he:llo'; $pass = 'p@ss'; +$url = rawurlencode($user) . ':' . rawurlencode($pass) . '@127.0.0.1'; -$proxy = new Clue\React\Socks\Client( - rawurlencode($user) . ':' . rawurlencode($pass) . '@127.0.0.1', - $connector -); +$proxy = new Clue\React\Socks\Client($url); ``` > The authentication details will be transmitted in cleartext to the SOCKS proxy @@ -530,7 +492,7 @@ version 5 and complains if you have explicitly set anything else: ```php // throws InvalidArgumentException -new Clue\React\Socks\Client('socks4://user:pass@127.0.0.1', $connector); +new Clue\React\Socks\Client('socks4://user:pass@127.0.0.1'); ``` #### Proxy chaining @@ -561,16 +523,10 @@ SOCKS connector from another SOCKS client like this: // which in turn then uses MiddlemanSocksServer. // this creates a TCP/IP connection to MiddlemanSocksServer, which then connects // to TargetSocksServer, which then connects to the TargetHost -$middle = new Clue\React\Socks\Client( - '127.0.0.1:1080', - new React\Socket\Connector($loop) -); -$target = new Clue\React\Socks\Client( - 'example.com:1080', - $middle -); - -$connector = new React\Socket\Connector($loop, array( +$middle = new Clue\React\Socks\Client('127.0.0.1:1080'); +$target = new Clue\React\Socks\Client('example.com:1080', $middle); + +$connector = new React\Socket\Connector(null, array( 'tcp' => $target, 'dns' => false )); @@ -609,19 +565,14 @@ 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) -or the low-level -[`TimeoutConnector`](https://github.com/reactphp/socket#timeoutconnector) 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\Socks\Client( - '127.0.0.1:1080', - new React\Socket\Connector($loop) -); +$proxy = new Clue\React\Socks\Client('127.0.0.1:1080'); -$connector = new React\Socket\Connector($loop, array( +$connector = new React\Socket\Connector(null, array( 'tcp' => $proxy, 'dns' => false, 'timeout' => 3.0 @@ -664,15 +615,9 @@ You can use the `sockss://` URI scheme or use an explicit [SOCKS protocol version](#protocol-version) like this: ```php -$proxy = new Clue\React\Socks\Client( - 'sockss://127.0.0.1:1080', - new React\Socket\Connector($loop) -); - -$proxy = new Clue\React\Socks\Client( - 'socks4s://127.0.0.1:1080', - new React\Socket\Connector($loop) -); +$proxy = new Clue\React\Socks\Client('sockss://127.0.0.1:1080'); + +$proxy = new Clue\React\Socks\Client('socks4s://127.0.0.1:1080'); ``` See also [example 32](examples). @@ -681,10 +626,7 @@ Similarly, you can also combine this with [authentication](#authentication) like this: ```php -$proxy = new Clue\React\Socks\Client( - 'sockss://user:pass@127.0.0.1:1080', - new React\Socket\Connector($loop) -); +$proxy = new Clue\React\Socks\Client('sockss://user:pass@127.0.0.1:1080'); ``` > Note that for most use cases, [secure TLS connections](#secure-tls-connections) @@ -716,25 +658,16 @@ You can use the `socks+unix://` URI scheme or use an explicit [SOCKS protocol version](#protocol-version) like this: ```php -$proxy = new Clue\React\Socks\Client( - 'socks+unix:///tmp/proxy.sock' - new React\Socket\Connector($loop) -); - -$proxy = new Clue\React\Socks\Client( - 'socks4+unix:///tmp/proxy.sock', - new React\Socket\Connector($loop) -); +$proxy = new Clue\React\Socks\Client('socks+unix:///tmp/proxy.sock'); + +$proxy = new Clue\React\Socks\Client('socks4+unix:///tmp/proxy.sock'); ``` Similarly, you can also combine this with [authentication](#authentication) like this: ```php -$proxy = new Clue\React\Socks\Client( - 'socks+unix://user:pass@/tmp/proxy.sock', - new React\Socket\Connector($loop) -); +$proxy = new Clue\React\Socks\Client('socks+unix://user:pass@/tmp/proxy.sock'); ``` > Note that Unix domain sockets (UDS) are considered advanced usage and PHP only @@ -753,21 +686,22 @@ $proxy = new Clue\React\Socks\Client( The `Server` is responsible for accepting incoming communication from SOCKS clients and forwarding the requested connection to the target host. It supports the SOCKS5 and SOCKS4(a) protocol versions by default. -It also registers everything with the main [`EventLoop`](https://github.com/reactphp/event-loop#usage) -and an underlying TCP/IP socket server like this: +You can start listening on an underlying TCP/IP socket server like this: ```php -$loop = React\EventLoop\Factory::create(); - -$server = new Clue\React\Socks\Server($loop); +$server = new Clue\React\Socks\Server(); // listen on localhost:$port -$socket = new React\Socket\Server($port, $loop); +$socket = new React\Socket\Server($port); $server->listen($socket); - -$loop->run(); ``` +This class takes an optional `LoopInterface|null $loop` parameter that can be used to +pass the event loop instance to use for this object. You can use a `null` value +here in order to use the [default loop](https://github.com/reactphp/event-loop#loop). +This value SHOULD NOT be given unless you're sure you want to explicitly use a +given event loop instance. + Additionally, the `Server` constructor accepts optional parameters to explicitly configure the [connector](#server-connector) to use and to require [authentication](#server-authentication). For more details, read on... @@ -783,14 +717,14 @@ 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($loop, array( +$connector = new React\Socket\Connector(null, array( 'dns' => '127.0.0.1', 'tcp' => array( 'bindto' => '192.168.10.1:0' ) )); -$server = new Clue\React\Socks\Server($loop, $connector); +$server = new Clue\React\Socks\Server(null, $connector); ``` If you want to forward the outgoing connection through another SOCKS proxy, you @@ -823,7 +757,7 @@ If you only want to accept static authentication details, you can simply pass an additional assoc array with your authentication details to the `Server` like this: ```php -$server = new Clue\React\Socks\Server($loop, null, array( +$server = new Clue\React\Socks\Server(null, null, array( 'tom' => 'password', 'admin' => 'root' )); @@ -835,7 +769,7 @@ If you want more control over authentication, you can pass an authenticator function that should return a `bool` value like this synchronous example: ```php -$server = new Clue\React\Socks\Server($loop, null, function ($user, $pass, $remote) { +$server = new Clue\React\Socks\Server(null, null, function ($user, $pass, $remote) { // $remote is a full URI à la socks://user:pass@192.168.1.1:1234 // or sockss://user:pass@192.168.1.1:1234 for SOCKS over TLS // or may be null when remote is unknown (SOCKS over Unix Domain Sockets) @@ -856,7 +790,7 @@ from the authenticator function that will fulfill with a `bool` value like this async example: ```php -$server = new Clue\React\Socks\Server($loop, null, function ($user, $pass) use ($db) { +$server = new Clue\React\Socks\Server(null, null, function ($user, $pass) use ($db) { // pseudo-code: query database for given authentication details return $db->query( 'SELECT 1 FROM users WHERE name = ? AND password = ?', @@ -893,20 +827,15 @@ In order to connect through another SOCKS server, you can simply use the You can create a SOCKS `Client` instance like this: ```php -$loop = React\EventLoop\Factory::create(); - // set next SOCKS server example.com:1080 as target -$connector = new React\Socket\Connector($loop); -$proxy = new Clue\React\Socks\Client('user:pass@example.com:1080', $connector); +$proxy = new Clue\React\Socks\Client('user:pass@example.com:1080'); // start a new server which forwards all connections to the other SOCKS server -$server = new Clue\React\Socks\Server($loop, $proxy); +$server = new Clue\React\Socks\Server(null, $proxy); // listen on localhost:1080 -$socket = new React\Socket\Server('127.0.0.1:1080', $loop); +$socket = new React\Socket\Server('127.0.0.1:1080'); $server->listen($socket); - -$loop->run(); ``` See also [example #21](examples). @@ -947,19 +876,15 @@ details. You can simply start your listening socket on the `tls://` URI scheme like this: ```php -$loop = React\EventLoop\Factory::create(); - -$server = new Clue\React\Socks\Server($loop); +$server = new Clue\React\Socks\Server(); // listen on tls://127.0.0.1:1080 with the given server certificate -$socket = new React\Socket\Server('tls://127.0.0.1:1080', $loop, array( +$socket = new React\Socket\Server('tls://127.0.0.1:1080', null, array( 'tls' => array( 'local_cert' => __DIR__ . '/localhost.pem', ) )); $server->listen($socket); - -$loop->run(); ``` See also [example 31](examples). @@ -986,15 +911,11 @@ having to rely on explicit [authentication](#server-authentication). You can simply start your listening socket on the `unix://` URI scheme like this: ```php -$loop = React\EventLoop\Factory::create(); - -$server = new Clue\React\Socks\Server($loop); +$server = new Clue\React\Socks\Server(); // listen on /tmp/proxy.sock -$socket = new React\Socket\Server('unix:///tmp/proxy.sock', $loop); +$socket = new React\Socket\Server('unix:///tmp/proxy.sock'); $server->listen($socket); - -$loop->run(); ``` > Note that Unix domain sockets (UDS) are considered advanced usage and that @@ -1037,10 +958,7 @@ $ ssh -D 1080 example.com Now you can simply use this SSH SOCKS server like this: ```PHP -$proxy = new Clue\React\Socks\Client( - '127.0.0.1:1080', - new React\Socket\Connector($loop) -); +$proxy = new Clue\React\Socks\Client('127.0.0.1:1080'); $proxy->connect('tcp://www.google.com:80')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write("GET / HTTP/1.0\r\n\r\n"); @@ -1064,10 +982,7 @@ $ ssh -D/tmp/proxy.sock example.com Now you can simply use this SSH SOCKS server like this: ```PHP -$proxy = new Clue\React\Socks\Client( - 'socks+unix:///tmp/proxy.sock', - new React\Socket\Connector($loop) -); +$proxy = new Clue\React\Socks\Client('socks+unix:///tmp/proxy.sock'); $proxy->connect('tcp://www.google.com:80')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write("GET / HTTP/1.0\r\n\r\n"); @@ -1092,10 +1007,7 @@ It presents a SOCKS5 and SOCKS4(a) interface on TCP port 9050 by default which allows you to tunnel any traffic through the anonymity network: ```php -$proxy = new Clue\React\Socks\Client( - '127.0.0.1:9050', - new React\Socket\Connector($loop) -); +$proxy = new Clue\React\Socks\Client('127.0.0.1:9050'); $proxy->connect('tcp://www.google.com:80')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write("GET / HTTP/1.0\r\n\r\n"); diff --git a/composer.json b/composer.json index 38af58b..a6c36b5 100644 --- a/composer.json +++ b/composer.json @@ -10,22 +10,22 @@ "email": "christian@clue.engineering" } ], - "autoload": { - "psr-4": {"Clue\\React\\Socks\\": "src/"} - }, - "autoload-dev": { - "psr-4": { "Clue\\Tests\\React\\Socks\\": "tests/" } - }, "require": { "php": ">=5.3", "react/promise": "^2.1 || ^1.2", - "react/socket": "^1.1" + "react/socket": "^1.8" }, "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3", - "react/http": "^1.0", + "clue/block-react": "^1.1", "clue/connection-manager-extra": "^1.0 || ^0.7", - "clue/block-react": "^1.1" + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35", + "react/event-loop": "^1.2", + "react/http": "^1.4" + }, + "autoload": { + "psr-4": { "Clue\\React\\Socks\\": "src/" } + }, + "autoload-dev": { + "psr-4": { "Clue\\Tests\\React\\Socks\\": "tests/" } } } diff --git a/examples/01-https-request.php b/examples/01-https-request.php index 557d221..320fb4a 100644 --- a/examples/01-https-request.php +++ b/examples/01-https-request.php @@ -21,24 +21,18 @@ $url = 'localhost:1080'; } -$loop = React\EventLoop\Factory::create(); -$proxy = new Clue\React\Socks\Client( - $url, - new React\Socket\Connector($loop) -); +$proxy = new Clue\React\Socks\Client($url); -$connector = new React\Socket\Connector($loop, array( +$connector = new React\Socket\Connector(null, array( 'tcp' => $proxy, 'timeout' => 3.0, 'dns' => false )); -$browser = new React\Http\Browser($loop, $connector); +$browser = new React\Http\Browser(null, $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; }); - -$loop->run(); diff --git a/examples/02-optional-proxy-https-request.php b/examples/02-optional-proxy-https-request.php index 64822e3..e6a5f1a 100644 --- a/examples/02-optional-proxy-https-request.php +++ b/examples/02-optional-proxy-https-request.php @@ -15,28 +15,22 @@ require __DIR__ . '/../vendor/autoload.php'; -$loop = React\EventLoop\Factory::create(); - $connector = null; $url = getenv('socks_proxy'); if ($url !== false) { - $proxy = new Clue\React\Socks\Client( - $url, - new React\Socket\Connector($loop) - ); - $connector = new React\Socket\Connector($loop, array( + $proxy = new Clue\React\Socks\Client($url); + + $connector = new React\Socket\Connector(null, array( 'tcp' => $proxy, 'timeout' => 3.0, 'dns' => false )); } -$browser = new React\Http\Browser($loop, $connector); +$browser = new React\Http\Browser(null, $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; }); - -$loop->run(); diff --git a/examples/11-proxy-raw-http-protocol.php b/examples/11-proxy-raw-http-protocol.php index cb347e0..03c93cb 100644 --- a/examples/11-proxy-raw-http-protocol.php +++ b/examples/11-proxy-raw-http-protocol.php @@ -24,13 +24,9 @@ $url = 'localhost:1080'; } -$loop = React\EventLoop\Factory::create(); +$proxy = new Clue\React\Socks\Client($url); -$proxy = new Clue\React\Socks\Client( - $url, - new React\Socket\Connector($loop) -); -$connector = new React\Socket\Connector($loop, array( +$connector = new React\Socket\Connector(null, array( 'tcp' => $proxy, 'timeout' => 3.0, 'dns' => false @@ -47,5 +43,3 @@ }, function (Exception $e) { echo 'Error: ' . $e->getMessage() . PHP_EOL; }); - -$loop->run(); diff --git a/examples/12-optional-proxy-raw-http-protocol.php b/examples/12-optional-proxy-raw-http-protocol.php index 80a30a7..f090d67 100644 --- a/examples/12-optional-proxy-raw-http-protocol.php +++ b/examples/12-optional-proxy-raw-http-protocol.php @@ -22,18 +22,17 @@ require __DIR__ . '/../vendor/autoload.php'; -$loop = React\EventLoop\Factory::create(); - -$connector = new React\Socket\Connector($loop); - $url = getenv('socks_proxy'); if ($url !== false) { - $proxy = new Clue\React\Socks\Client($url, $connector); - $connector = new React\Socket\Connector($loop, array( + $proxy = new Clue\React\Socks\Client($url); + + $connector = new React\Socket\Connector(null, array( 'tcp' => $proxy, 'timeout' => 3.0, 'dns' => false )); +} else { + $connector = new React\Socket\Connector(); } echo 'Demo SOCKS client connecting to SOCKS server ' . $url . PHP_EOL; @@ -47,5 +46,3 @@ }, function (Exception $e) { echo 'Error: ' . $e->getMessage() . PHP_EOL; }); - -$loop->run(); diff --git a/examples/13-proxy-raw-https-protocol.php b/examples/13-proxy-raw-https-protocol.php index 5629ed8..8be3046 100644 --- a/examples/13-proxy-raw-https-protocol.php +++ b/examples/13-proxy-raw-https-protocol.php @@ -24,13 +24,9 @@ $url = 'localhost:1080'; } -$loop = React\EventLoop\Factory::create(); +$proxy = new Clue\React\Socks\Client($url); -$proxy = new Clue\React\Socks\Client( - $url, - new React\Socket\Connector($loop) -); -$connector = new React\Socket\Connector($loop, array( +$connector = new React\Socket\Connector(null, array( 'tcp' => $proxy, 'timeout' => 3.0, 'dns' => false @@ -47,5 +43,3 @@ }, function (Exception $e) { echo 'Error: ' . $e->getMessage() . PHP_EOL; }); - -$loop->run(); diff --git a/examples/14-optional-proxy-raw-https-protocol.php b/examples/14-optional-proxy-raw-https-protocol.php index cf88e24..67cbf67 100644 --- a/examples/14-optional-proxy-raw-https-protocol.php +++ b/examples/14-optional-proxy-raw-https-protocol.php @@ -22,18 +22,17 @@ require __DIR__ . '/../vendor/autoload.php'; -$loop = React\EventLoop\Factory::create(); - -$connector = new React\Socket\Connector($loop); - $url = getenv('socks_proxy'); if ($url !== false) { - $proxy = new Clue\React\Socks\Client($url, $connector); - $connector = new React\Socket\Connector($loop, array( + $proxy = new Clue\React\Socks\Client($url); + + $connector = new React\Socket\Connector(null, array( 'tcp' => $proxy, 'timeout' => 3.0, 'dns' => false )); +} else { + $connector = new React\Socket\Connector(); } echo 'Demo SOCKS client connecting to SOCKS server ' . $url . PHP_EOL; @@ -47,5 +46,3 @@ }, function (Exception $e) { echo 'Error: ' . $e->getMessage() . PHP_EOL; }); - -$loop->run(); diff --git a/examples/15-proxy-chaining.php b/examples/15-proxy-chaining.php index 529bb27..4661c49 100644 --- a/examples/15-proxy-chaining.php +++ b/examples/15-proxy-chaining.php @@ -3,7 +3,7 @@ // A more advanced example which requests http://www.google.com/ through a chain of SOCKS proxy servers. // The proxy servers can be given as arguments. // -// Not already running a SOCKS proxy server? See also example #21 or try this: +// Not already running a SOCKS proxy server? See also example #21 or try this: // $ ssh -D 1080 localhost // // For illustration purposes only. If you want to send HTTP requests in a real @@ -22,17 +22,15 @@ // Alternatively, you can also hard-code this value like this: //$path = array('127.0.0.1:9051', '127.0.0.1:9052', '127.0.0.1:9053'); -$loop = React\EventLoop\Factory::create(); - // set next SOCKS server chain via p1 -> p2 -> p3 -> destination -$connector = new React\Socket\Connector($loop); +$connector = new React\Socket\Connector(); foreach ($path as $proxy) { $connector = new Clue\React\Socks\Client($proxy, $connector); } // please note how the client uses p3 (not p1!), which in turn then uses the complete chain // this creates a TCP/IP connection to p1, which then connects to p2, then to p3, which then connects to the target -$connector = new React\Socket\Connector($loop, array( +$connector = new React\Socket\Connector(null, array( 'tcp' => $connector, 'timeout' => 3.0, 'dns' => false @@ -49,5 +47,3 @@ }, function (Exception $e) { echo 'Error: ' . $e->getMessage() . PHP_EOL; }); - -$loop->run(); diff --git a/examples/16-local-dns.php b/examples/16-local-dns.php index 92f992d..d967dc2 100644 --- a/examples/16-local-dns.php +++ b/examples/16-local-dns.php @@ -13,20 +13,16 @@ $url = isset($argv[1]) ? $argv[1] : '127.0.0.1:1080'; -$loop = React\EventLoop\Factory::create(); +$proxy = new Clue\React\Socks\Client($url); // set up DNS server to use (Google's public DNS) -$proxy = new Clue\React\Socks\Client( - $url, - new React\Socket\Connector($loop) -); -$connector = new React\Socket\Connector($loop, array( +$connector = new React\Socket\Connector(null, array( 'tcp' => $proxy, 'timeout' => 3.0, 'dns' => '8.8.8.8' )); -echo 'Demo SOCKS client connecting to SOCKS server ' . $proxy . PHP_EOL; +echo 'Demo SOCKS client connecting to SOCKS server ' . $url . PHP_EOL; $connector->connect('tls://www.google.com:443')->then(function (React\Socket\ConnectionInterface $connection) { echo 'connected' . PHP_EOL; @@ -37,5 +33,3 @@ }, function (Exception $e) { echo 'Error: ' . $e->getMessage() . PHP_EOL; }); - -$loop->run(); diff --git a/examples/21-server.php b/examples/21-server.php index 1d5802c..c5f4bb5 100644 --- a/examples/21-server.php +++ b/examples/21-server.php @@ -7,15 +7,11 @@ require __DIR__ . '/../vendor/autoload.php'; -$loop = React\EventLoop\Factory::create(); - // start a new SOCKS proxy server -$server = new Clue\React\Socks\Server($loop); +$server = new Clue\React\Socks\Server(); // listen on 127.0.0.1:1080 or first argument -$socket = new React\Socket\Server(isset($argv[1]) ? $argv[1] : '127.0.0.1:1080', $loop); +$socket = new React\Socket\Server(isset($argv[1]) ? $argv[1] : '127.0.0.1:1080'); $server->listen($socket); echo 'SOCKS server listening on ' . $socket->getAddress() . PHP_EOL; - -$loop->run(); diff --git a/examples/22-server-with-password.php b/examples/22-server-with-password.php index eec40ce..2313274 100644 --- a/examples/22-server-with-password.php +++ b/examples/22-server-with-password.php @@ -13,19 +13,15 @@ require __DIR__ . '/../vendor/autoload.php'; -$loop = React\EventLoop\Factory::create(); - // start a new SOCKS proxy server // require authentication and hence make this a SOCKS5-only server -$server = new Clue\React\Socks\Server($loop, null, array( +$server = new Clue\React\Socks\Server(null, null, array( 'tom' => 'god', 'user' => 'p@ssw0rd' )); // listen on 127.0.0.1:1080 or first argument -$socket = new React\Socket\Server(isset($argv[1]) ? $argv[1] : '127.0.0.1:1080', $loop); +$socket = new React\Socket\Server(isset($argv[1]) ? $argv[1] : '127.0.0.1:1080'); $server->listen($socket); echo 'SOCKS5 server requiring authentication listening on ' . $socket->getAddress() . PHP_EOL; - -$loop->run(); diff --git a/examples/23-server-blacklist.php b/examples/23-server-blacklist.php index 70d8f0f..818b0c9 100644 --- a/examples/23-server-blacklist.php +++ b/examples/23-server-blacklist.php @@ -10,13 +10,11 @@ require __DIR__ . '/../vendor/autoload.php'; -$loop = React\EventLoop\Factory::create(); - // create a connector that rejects the connection $reject = new ConnectionManager\Extra\ConnectionManagerReject(); // create an actual connector that establishes real connections -$permit = new React\Socket\Connector($loop); +$permit = new React\Socket\Connector(); // this connector selectively picks one of the the attached connectors depending on the target address // reject youtube.com and unencrypted HTTP for google.com @@ -28,12 +26,10 @@ )); // start a new SOCKS proxy server using our connection manager for outgoing connections -$server = new Clue\React\Socks\Server($loop, $connector); +$server = new Clue\React\Socks\Server(null, $connector); // listen on 127.0.0.1:1080 or first argument -$socket = new React\Socket\Server(isset($argv[1]) ? $argv[1] : '127.0.0.1:1080', $loop); +$socket = new React\Socket\Server(isset($argv[1]) ? $argv[1] : '127.0.0.1:1080'); $server->listen($socket); echo 'SOCKS server listening on ' . $socket->getAddress() . PHP_EOL; - -$loop->run(); diff --git a/examples/31-server-proxy-chaining.php b/examples/31-server-proxy-chaining.php index 05902a7..7c3602b 100644 --- a/examples/31-server-proxy-chaining.php +++ b/examples/31-server-proxy-chaining.php @@ -22,22 +22,18 @@ //$listen = '127.0.0.1:9050'; //$path = array('127.0.0.1:9051', '127.0.0.1:9052', '127.0.0.1:9053'); -$loop = React\EventLoop\Factory::create(); - // set next SOCKS server chain -> p1 -> p2 -> p3 -> destination -$connector = new React\Socket\Connector($loop); +$connector = new React\Socket\Connector(); foreach ($path as $proxy) { $connector = new Clue\React\Socks\Client($proxy, $connector); } // start a new SOCKS proxy server which forwards all connections to the other SOCKS server -$server = new Clue\React\Socks\Server($loop, $connector); +$server = new Clue\React\Socks\Server(null, $connector); // listen on 127.0.0.1:1080 or first argument -$socket = new React\Socket\Server($listen, $loop); +$socket = new React\Socket\Server($listen); $server->listen($socket); echo 'SOCKS server listening on ' . $socket->getAddress() . PHP_EOL; echo 'Forwarding via: ' . implode(' -> ', $path) . PHP_EOL; - -$loop->run(); diff --git a/examples/32-server-proxy-chaining-from-random-pool.php b/examples/32-server-proxy-chaining-from-random-pool.php index f2e7352..a4138ed 100644 --- a/examples/32-server-proxy-chaining-from-random-pool.php +++ b/examples/32-server-proxy-chaining-from-random-pool.php @@ -22,11 +22,9 @@ //$listen = '127.0.0.1:9050'; //$pool = array('127.0.0.1:9051', '127.0.0.1:9052', '127.0.0.1:9053'); -$loop = React\EventLoop\Factory::create(); - // forward to socks server listening on 127.0.0.1:9051-9053 // this connector randomly picks one of the the attached connectors from the pool -$connector = new React\Socket\Connector($loop); +$connector = new React\Socket\Connector(); $proxies = array(); foreach ($pool as $proxy) { $proxies []= new Clue\React\Socks\Client($proxy, $connector); @@ -34,13 +32,11 @@ $connector = new ConnectionManager\Extra\Multiple\ConnectionManagerRandom($proxies); // start the SOCKS proxy server using our connection manager for outgoing connections -$server = new Clue\React\Socks\Server($loop, $socket, $connector); +$server = new Clue\React\Socks\Server(null, $connector); // listen on 127.0.0.1:1080 or first argument -$socket = new React\Socket\Server($listen, $loop); +$socket = new React\Socket\Server($listen); $server->listen($socket); echo 'SOCKS server listening on ' . $socket->getAddress() . PHP_EOL; echo 'Randomly picking from: ' . implode(', ', $pool) . PHP_EOL; - -$loop->run(); diff --git a/examples/41-server-secure.php b/examples/41-server-secure.php index d6d2cad..b518ec1 100644 --- a/examples/41-server-secure.php +++ b/examples/41-server-secure.php @@ -7,17 +7,14 @@ require __DIR__ . '/../vendor/autoload.php'; -$loop = React\EventLoop\Factory::create(); - // start a new SOCKS proxy server -$server = new Clue\React\Socks\Server($loop); +$server = new Clue\React\Socks\Server(); // listen on tls://127.0.0.1:1080 or first argument $listen = isset($argv[1]) ? $argv[1] : '127.0.0.1:1080'; -$socket = new React\Socket\Server('tls://' . $listen, $loop, array('tls' => array( +$socket = new React\Socket\Server('tls://' . $listen, null, array('tls' => array( 'local_cert' => __DIR__ . '/localhost.pem', ))); +$server->listen($socket); echo 'SOCKS over TLS server listening on ' . str_replace('tls:', 'sockss:', $socket->getAddress()) . PHP_EOL; - -$loop->run(); diff --git a/examples/42-http-secure.php b/examples/42-http-secure.php index d123750..ac81126 100644 --- a/examples/42-http-secure.php +++ b/examples/42-http-secure.php @@ -12,16 +12,14 @@ $url = isset($argv[1]) ? $argv[1] : '127.0.0.1:1080'; -$loop = React\EventLoop\Factory::create(); - -$proxy = new Clue\React\Socks\Client( - 'sockss://' . $url, - new React\Socket\Connector($loop, array('tls' => array( - 'verify_peer' => false, - 'verify_peer_name' => false - ))) -); -$connector = new React\Socket\Connector($loop, array( +$connector = new React\Socket\Connector(null, array('tls' => array( + 'verify_peer' => false, + 'verify_peer_name' => false +))); + +$proxy = new Clue\React\Socks\Client($url, $connector); + +$connector = new React\Socket\Connector(null, array( 'tcp' => $proxy, 'timeout' => 3.0, 'dns' => false @@ -38,5 +36,3 @@ }, function (Exception $e) { echo 'Error: ' . $e->getMessage() . PHP_EOL; }); - -$loop->run(); diff --git a/src/Client.php b/src/Client.php index a317f18..5350b54 100644 --- a/src/Client.php +++ b/src/Client.php @@ -6,16 +6,16 @@ use React\Promise\PromiseInterface; use React\Promise\Deferred; use React\Socket\ConnectionInterface; +use React\Socket\Connector; use React\Socket\ConnectorInterface; use React\Socket\FixedUriConnector; -use \Exception; -use \InvalidArgumentException; +use Exception; +use InvalidArgumentException; use RuntimeException; final class Client implements ConnectorInterface { /** - * * @var ConnectorInterface */ private $connector; @@ -26,7 +26,12 @@ final class Client implements ConnectorInterface private $auth = null; - public function __construct($socksUri, ConnectorInterface $connector) + /** + * @param string $socksUri + * @param ?ConnectorInterface $connector + * @throws InvalidArgumentException + */ + public function __construct($socksUri, ConnectorInterface $connector = null) { // support `sockss://` scheme for SOCKS over TLS // support `socks+unix://` scheme for Unix domain socket (UDS) paths @@ -37,7 +42,7 @@ public function __construct($socksUri, ConnectorInterface $connector) // connector uses appropriate transport scheme and explicit host given $connector = new FixedUriConnector( ($match[2] === 's' ? 'tls://' : 'unix://') . $match[4], - $connector + $connector ?: new Connector() ); } @@ -49,7 +54,7 @@ public function __construct($socksUri, ConnectorInterface $connector) // parse URI into individual parts $parts = parse_url($socksUri); if (!$parts || !isset($parts['scheme'], $parts['host'])) { - throw new \InvalidArgumentException('Invalid SOCKS server URI "' . $socksUri . '"'); + throw new InvalidArgumentException('Invalid SOCKS server URI "' . $socksUri . '"'); } // assume default port @@ -71,7 +76,7 @@ public function __construct($socksUri, ConnectorInterface $connector) $this->setProtocolVersionFromScheme($parts['scheme']); $this->socksUri = $parts['host'] . ':' . $parts['port']; - $this->connector = $connector; + $this->connector = $connector ?: new Connector(); } private function setProtocolVersionFromScheme($scheme) diff --git a/src/Server.php b/src/Server.php index 0db0bca..00e92a0 100644 --- a/src/Server.php +++ b/src/Server.php @@ -7,6 +7,7 @@ use React\Socket\ConnectorInterface; use React\Socket\Connector; use React\Socket\ConnectionInterface; +use React\EventLoop\Loop; use React\EventLoop\LoopInterface; use \UnexpectedValueException; use \InvalidArgumentException; @@ -33,8 +34,10 @@ final class Server /** @internal */ const ERROR_ADDRESS_UNSUPPORTED = 0x08; + /** @var LoopInterface */ private $loop; + /** @var ConnectorInterface */ private $connector; /** @@ -43,16 +46,19 @@ final class Server private $auth; /** - * @param LoopInterface $loop - * @param null|ConnectorInterface $connector - * @param null|array|callable $auth + * + * This class takes an optional `LoopInterface|null $loop` parameter that can be used to + * pass the event loop instance to use for this object. You can use a `null` value + * here in order to use the [default loop](https://github.com/reactphp/event-loop#loop). + * This value SHOULD NOT be given unless you're sure you want to explicitly use a + * given event loop instance. + * + * @param ?LoopInterface $loop + * @param ?ConnectorInterface $connector + * @param null|array|callable $auth */ - public function __construct(LoopInterface $loop, ConnectorInterface $connector = null, $auth = null) + public function __construct(LoopInterface $loop = null, ConnectorInterface $connector = null, $auth = null) { - if ($connector === null) { - $connector = new Connector($loop); - } - if (\is_array($auth)) { // wrap authentication array in authentication callback $this->auth = function ($username, $password) use ($auth) { @@ -71,8 +77,8 @@ public function __construct(LoopInterface $loop, ConnectorInterface $connector = throw new \InvalidArgumentException('Invalid authenticator given'); } - $this->loop = $loop; - $this->connector = $connector; + $this->loop = $loop ?: Loop::get(); + $this->connector = $connector ?: new Connector($this->loop); } /** diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 5ddeebe..d581a94 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -26,6 +26,17 @@ public function setUpMocks() $this->client = new Client('127.0.0.1:1080', $this->connector); } + public function testConstructWithoutConnectorAssignsConnectorAutomatically() + { + $proxy = new Client('127.0.0.1:1080'); + + $ref = new \ReflectionProperty($proxy, 'connector'); + $ref->setAccessible(true); + $connector = $ref->getValue($proxy); + + $this->assertInstanceOf('React\Socket\ConnectorInterface', $connector); + } + public function testCtorAcceptsUriWithHostAndPort() { $client = new Client('127.0.0.1:9050', $this->connector); diff --git a/tests/ServerTest.php b/tests/ServerTest.php index 643415b..6f71410 100644 --- a/tests/ServerTest.php +++ b/tests/ServerTest.php @@ -28,6 +28,17 @@ public function setUpServer() $this->server = new Server($this->loop, $this->connector); } + public function testConstructWithoutLoopAssignsLoopAutomatically() + { + $server = new Server(); + + $ref = new \ReflectionProperty($server, 'loop'); + $ref->setAccessible(true); + $loop = $ref->getValue($server); + + $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); + } + /** * @doesNotPerformAssertions */