diff --git a/src/ProxyConnector.php b/src/ProxyConnector.php index 69eb5ee..dfb5490 100644 --- a/src/ProxyConnector.php +++ b/src/ProxyConnector.php @@ -107,8 +107,7 @@ public function connect($uri) return Promise\reject(new InvalidArgumentException('Invalid target URI specified')); } - $host = trim($parts['host'], '[]'); - $port = $parts['port']; + $target = $parts['host'] . ':' . $parts['port']; // construct URI to HTTP CONNECT proxy server to connect to $proxyUri = $this->proxyUri; @@ -126,11 +125,11 @@ public function connect($uri) // append hostname from URI to query string unless explicitly given if (!isset($args['hostname'])) { - $args['hostname'] = $parts['host']; + $args['hostname'] = trim($parts['host'], '[]'); } // append query string - $proxyUri .= '?' . http_build_query($args, '', '&');; + $proxyUri .= '?' . http_build_query($args, '', '&'); // append fragment from URI if given if (isset($parts['fragment'])) { @@ -139,7 +138,7 @@ public function connect($uri) $auth = $this->proxyAuth; - return $this->connector->connect($proxyUri)->then(function (ConnectionInterface $stream) use ($host, $port, $auth) { + return $this->connector->connect($proxyUri)->then(function (ConnectionInterface $stream) use ($target, $auth) { $deferred = new Deferred(function ($_, $reject) use ($stream) { $reject(new RuntimeException('Connection canceled while waiting for response from proxy (ECONNABORTED)', defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103)); $stream->close(); @@ -199,7 +198,7 @@ public function connect($uri) $deferred->reject(new RuntimeException('Connection to proxy lost while waiting for response (ECONNRESET)', defined('SOCKET_ECONNRESET') ? SOCKET_ECONNRESET : 104)); }); - $stream->write("CONNECT " . $host . ":" . $port . " HTTP/1.1\r\nHost: " . $host . ":" . $port . "\r\n" . $auth . "\r\n"); + $stream->write("CONNECT " . $target . " HTTP/1.1\r\nHost: " . $target . "\r\n" . $auth . "\r\n"); return $deferred->promise()->then(function (ConnectionInterface $stream) use ($fn) { // Stop buffering when connection has been established. diff --git a/tests/ProxyConnectorTest.php b/tests/ProxyConnectorTest.php index 13d6e42..32b7d66 100644 --- a/tests/ProxyConnectorTest.php +++ b/tests/ProxyConnectorTest.php @@ -69,6 +69,36 @@ public function testCreatesConnectionToHttpPortAndObeysExplicitHostname() $proxy->connect('google.com:80?hostname=www.google.com'); } + public function testCreatesConnectionToIpv4Address() + { + $promise = new Promise(function () { }); + $this->connector->expects($this->once())->method('connect')->with('tcp://proxy.example.com:80?hostname=127.0.0.1')->willReturn($promise); + + $proxy = new ProxyConnector('proxy.example.com', $this->connector); + + $proxy->connect('127.0.0.1:80'); + } + + public function testCreatesConnectionToIpv6Address() + { + $promise = new Promise(function () { }); + $this->connector->expects($this->once())->method('connect')->with('tcp://proxy.example.com:80?hostname=%3A%3A1')->willReturn($promise); + + $proxy = new ProxyConnector('proxy.example.com', $this->connector); + + $proxy->connect('[::1]:80'); + } + + public function testCreatesConnectionToIpv4AddressOverIpv6Proxy() + { + $promise = new Promise(function () { }); + $this->connector->expects($this->once())->method('connect')->with('tcp://[::1]:80?hostname=127.0.0.1')->willReturn($promise); + + $proxy = new ProxyConnector('[::1]:80', $this->connector); + + $proxy->connect('127.0.0.1:80'); + } + public function testCreatesConnectionToHttpsPort() { $promise = new Promise(function () { }); @@ -116,6 +146,19 @@ public function testWillWriteToOpenConnection() $proxy->connect('google.com:80'); } + public function testWillWriteIpv6HostToOpenConnection() + { + $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock(); + $stream->expects($this->once())->method('write')->with("CONNECT [::1]:80 HTTP/1.1\r\nHost: [::1]:80\r\n\r\n"); + + $promise = \React\Promise\resolve($stream); + $this->connector->expects($this->once())->method('connect')->willReturn($promise); + + $proxy = new ProxyConnector('proxy.example.com', $this->connector); + + $proxy->connect('[::1]:80'); + } + public function testWillProxyAuthorizationHeaderIfProxyUriContainsAuthentication() { $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock();