Skip to content

Commit 5414f98

Browse files
committed
Preserve all components from URI when passing through
1 parent 89ae4f6 commit 5414f98

8 files changed

+140
-13
lines changed

src/DnsConnector.php

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,58 @@ public function __construct(ConnectorInterface $connector, Resolver $resolver)
2020

2121
public function connect($uri)
2222
{
23-
$that = $this;
23+
if (strpos($uri, '://') === false) {
24+
$parts = parse_url('tcp://' . $uri);
25+
unset($parts['scheme']);
26+
} else {
27+
$parts = parse_url($uri);
28+
}
2429

25-
$parts = parse_url('tcp://' . $uri);
26-
if (!$parts || !isset($parts['host'], $parts['port'])) {
30+
if (!$parts || !isset($parts['host'])) {
2731
return Promise\reject(new \InvalidArgumentException('Given URI "' . $uri . '" is invalid'));
2832
}
2933

34+
$that = $this;
3035
$host = trim($parts['host'], '[]');
3136

3237
return $this
3338
->resolveHostname($host)
3439
->then(function ($ip) use ($that, $parts) {
40+
$uri = '';
41+
42+
// prepend original scheme if known
43+
if (isset($parts['scheme'])) {
44+
$uri .= $parts['scheme'] . '://';
45+
}
46+
3547
if (strpos($ip, ':') !== false) {
3648
// enclose IPv6 addresses in square brackets before appending port
37-
$ip = '[' . $ip . ']';
49+
$uri .= '[' . $ip . ']';
50+
} else {
51+
$uri .= $ip;
52+
}
53+
54+
// append original port if known
55+
if (isset($parts['port'])) {
56+
$uri .= ':' . $parts['port'];
57+
}
58+
59+
// append orignal path if known
60+
if (isset($parts['path'])) {
61+
$uri .= $parts['path'];
62+
}
63+
64+
// append original query if known
65+
if (isset($parts['query'])) {
66+
$uri .= '?' . $parts['query'];
67+
}
68+
69+
// append original fragment if known
70+
if (isset($parts['fragment'])) {
71+
$uri .= '#' . $parts['fragment'];
3872
}
3973

40-
return $that->connectTcp(
41-
$ip . ':' . $parts['port']
42-
);
74+
return $that->connectTcp($uri);
4375
});
4476
}
4577

src/SecureConnector.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,17 @@ public function connect($uri)
2626
return Promise\reject(new \BadMethodCallException('Encryption not supported on your platform (HHVM < 3.8?)'));
2727
}
2828

29-
$host = trim(parse_url('tcp://' . $uri, PHP_URL_HOST), '[]');
29+
if (strpos($uri, '://') === false) {
30+
$uri = 'tls://' . $uri;
31+
}
32+
33+
$parts = parse_url($uri);
34+
if (!$parts || !isset($parts['host']) || $parts['scheme'] !== 'tls') {
35+
return Promise\reject(new \InvalidArgumentException('Given URI "' . $uri . '" is invalid'));
36+
}
37+
38+
$uri = str_replace('tls://', '', $uri);
39+
$host = trim($parts['host'], '[]');
3040

3141
$context = $this->context + array(
3242
'SNI_enabled' => true,

src/TcpConnector.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@ public function __construct(LoopInterface $loop, array $context = array())
2121

2222
public function connect($uri)
2323
{
24-
$parts = parse_url('tcp://' . $uri);
25-
if (!$parts || !isset($parts['host'], $parts['port'])) {
24+
if (strpos($uri, '://') === false) {
25+
$uri = 'tcp://' . $uri;
26+
}
27+
28+
$parts = parse_url($uri);
29+
if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') {
2630
return Promise\reject(new \InvalidArgumentException('Given URI "' . $uri . '" is invalid'));
2731
}
28-
$ip = trim($parts['host'], '[]');
2932

33+
$ip = trim($parts['host'], '[]');
3034
if (false === filter_var($ip, FILTER_VALIDATE_IP)) {
3135
return Promise\reject(new \InvalidArgumentException('Given URI "' . $ip . '" does not contain a valid host IP'));
3236
}

src/UnixConnector.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,13 @@ public function __construct(LoopInterface $loop)
2525

2626
public function connect($path)
2727
{
28-
$resource = @stream_socket_client('unix://' . $path, $errno, $errstr, 1.0);
28+
if (strpos($path, '://') === false) {
29+
$path = 'unix://' . $path;
30+
} elseif (substr($path, 0, 7) !== 'unix://') {
31+
return Promise\reject(new \InvalidArgumentException('Given URI "' . $path . '" is invalid'));
32+
}
33+
34+
$resource = @stream_socket_client($path, $errno, $errstr, 1.0);
2935

3036
if (!$resource) {
3137
return Promise\reject(new RuntimeException('Unable to connect to unix domain socket "' . $path . '": ' . $errstr, $errno));

tests/DnsConnectorTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,32 @@ public function testPassThroughResolverIfGivenHost()
3535
$this->connector->connect('google.com:80');
3636
}
3737

38+
public function testPassThroughResolverIfGivenHostWhichResolvesToIpv6()
39+
{
40+
$this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('google.com'))->will($this->returnValue(Promise\resolve('::1')));
41+
$this->tcp->expects($this->once())->method('connect')->with($this->equalTo('[::1]:80'))->will($this->returnValue(Promise\reject()));
42+
43+
$this->connector->connect('google.com:80');
44+
}
45+
46+
public function testPassByResolverIfGivenCompleteUri()
47+
{
48+
$this->resolver->expects($this->never())->method('resolve');
49+
$this->tcp->expects($this->once())->method('connect')->with($this->equalTo('scheme://127.0.0.1:80/path?query#fragment'))->will($this->returnValue(Promise\reject()));
50+
51+
$this->connector->connect('scheme://127.0.0.1:80/path?query#fragment');
52+
}
53+
54+
public function testRejectsImmediatelyIfUriIsInvalid()
55+
{
56+
$this->resolver->expects($this->never())->method('resolve');
57+
$this->tcp->expects($this->never())->method('connect');
58+
59+
$promise = $this->connector->connect('////');
60+
61+
$promise->then($this->expectCallableNever(), $this->expectCallableOnce());
62+
}
63+
3864
public function testSkipConnectionIfDnsFails()
3965
{
4066
$this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('example.invalid'))->will($this->returnValue(Promise\reject()));

tests/SecureConnectorTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,23 @@ public function testConnectionWillWaitForTcpConnection()
3232
$this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
3333
}
3434

35+
public function testConnectionWithCompleteUriWillBePassedThroughExpectForScheme()
36+
{
37+
$pending = new Promise\Promise(function () { });
38+
$this->tcp->expects($this->once())->method('connect')->with($this->equalTo('example.com:80/path?query#fragment'))->will($this->returnValue($pending));
39+
40+
$this->connector->connect('tls://example.com:80/path?query#fragment');
41+
}
42+
43+
public function testConnectionToInvalidSchemeWillReject()
44+
{
45+
$this->tcp->expects($this->never())->method('connect');
46+
47+
$promise = $this->connector->connect('tcp://example.com:80');
48+
49+
$promise->then(null, $this->expectCallableOnce());
50+
}
51+
3552
public function testCancelDuringTcpConnectionCancelsTcpConnection()
3653
{
3754
$pending = new Promise\Promise(function () { }, $this->expectCallableOnce());

tests/TcpConnectorTest.php

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public function connectionToHostnameShouldFailImmediately()
8989
}
9090

9191
/** @test */
92-
public function connectionToInvalidAddressShouldFailImmediately()
92+
public function connectionToInvalidPortShouldFailImmediately()
9393
{
9494
$loop = $this->getMock('React\EventLoop\LoopInterface');
9595

@@ -100,6 +100,32 @@ public function connectionToInvalidAddressShouldFailImmediately()
100100
);
101101
}
102102

103+
/** @test */
104+
public function connectionToInvalidSchemeShouldFailImmediately()
105+
{
106+
$loop = $this->getMock('React\EventLoop\LoopInterface');
107+
108+
$connector = new TcpConnector($loop);
109+
$connector->connect('tls://google.com:443')->then(
110+
$this->expectCallableNever(),
111+
$this->expectCallableOnce()
112+
);
113+
}
114+
115+
/** @test */
116+
public function connectionWithInvalidContextShouldFailImmediately()
117+
{
118+
$this->markTestIncomplete();
119+
120+
$loop = $this->getMock('React\EventLoop\LoopInterface');
121+
122+
$connector = new TcpConnector($loop, array('bindto' => 'invalid.invalid:123456'));
123+
$connector->connect('127.0.0.1:80')->then(
124+
$this->expectCallableNever(),
125+
$this->expectCallableOnce()
126+
);
127+
}
128+
103129
/** @test */
104130
public function cancellingConnectionShouldRejectPromise()
105131
{

tests/UnixConnectorTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ public function testInvalid()
2121
$promise->then(null, $this->expectCallableOnce());
2222
}
2323

24+
public function testInvalidScheme()
25+
{
26+
$promise = $this->connector->connect('tcp://google.com:80');
27+
$promise->then(null, $this->expectCallableOnce());
28+
}
29+
2430
public function testValid()
2531
{
2632
// random unix domain socket path

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy