Skip to content

Commit cf7c46f

Browse files
authored
Merge pull request #170 from clue-labs/dns-errors
Improve DNS error messages and cancellation forwarding after DNS lookup
2 parents b59f8f1 + e14f7bd commit cf7c46f

File tree

3 files changed

+330
-66
lines changed

3 files changed

+330
-66
lines changed

src/DnsConnector.php

Lines changed: 61 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -40,71 +40,76 @@ public function connect($uri)
4040
return $connector->connect($uri);
4141
}
4242

43-
return $this
44-
->resolveHostname($host)
45-
->then(function ($ip) use ($connector, $host, $parts) {
46-
$uri = '';
47-
48-
// prepend original scheme if known
49-
if (isset($parts['scheme'])) {
50-
$uri .= $parts['scheme'] . '://';
51-
}
52-
53-
if (strpos($ip, ':') !== false) {
54-
// enclose IPv6 addresses in square brackets before appending port
55-
$uri .= '[' . $ip . ']';
56-
} else {
57-
$uri .= $ip;
58-
}
59-
60-
// append original port if known
61-
if (isset($parts['port'])) {
62-
$uri .= ':' . $parts['port'];
63-
}
64-
65-
// append orignal path if known
66-
if (isset($parts['path'])) {
67-
$uri .= $parts['path'];
68-
}
69-
70-
// append original query if known
71-
if (isset($parts['query'])) {
72-
$uri .= '?' . $parts['query'];
73-
}
74-
75-
// append original hostname as query if resolved via DNS and if
76-
// destination URI does not contain "hostname" query param already
77-
$args = array();
78-
parse_str(isset($parts['query']) ? $parts['query'] : '', $args);
79-
if ($host !== $ip && !isset($args['hostname'])) {
80-
$uri .= (isset($parts['query']) ? '&' : '?') . 'hostname=' . rawurlencode($host);
81-
}
82-
83-
// append original fragment if known
84-
if (isset($parts['fragment'])) {
85-
$uri .= '#' . $parts['fragment'];
86-
}
87-
88-
return $connector->connect($uri);
89-
});
90-
}
91-
92-
private function resolveHostname($host)
93-
{
9443
$promise = $this->resolver->resolve($host);
44+
$resolved = null;
9545

9646
return new Promise\Promise(
97-
function ($resolve, $reject) use ($promise) {
47+
function ($resolve, $reject) use (&$promise, &$resolved, $uri, $connector, $host, $parts) {
9848
// resolve/reject with result of DNS lookup
99-
$promise->then($resolve, $reject);
49+
$promise->then(function ($ip) use (&$promise, &$resolved, $connector, $host, $parts) {
50+
$resolved = $ip;
51+
$uri = '';
52+
53+
// prepend original scheme if known
54+
if (isset($parts['scheme'])) {
55+
$uri .= $parts['scheme'] . '://';
56+
}
57+
58+
if (strpos($ip, ':') !== false) {
59+
// enclose IPv6 addresses in square brackets before appending port
60+
$uri .= '[' . $ip . ']';
61+
} else {
62+
$uri .= $ip;
63+
}
64+
65+
// append original port if known
66+
if (isset($parts['port'])) {
67+
$uri .= ':' . $parts['port'];
68+
}
69+
70+
// append orignal path if known
71+
if (isset($parts['path'])) {
72+
$uri .= $parts['path'];
73+
}
74+
75+
// append original query if known
76+
if (isset($parts['query'])) {
77+
$uri .= '?' . $parts['query'];
78+
}
79+
80+
// append original hostname as query if resolved via DNS and if
81+
// destination URI does not contain "hostname" query param already
82+
$args = array();
83+
parse_str(isset($parts['query']) ? $parts['query'] : '', $args);
84+
if ($host !== $ip && !isset($args['hostname'])) {
85+
$uri .= (isset($parts['query']) ? '&' : '?') . 'hostname=' . rawurlencode($host);
86+
}
87+
88+
// append original fragment if known
89+
if (isset($parts['fragment'])) {
90+
$uri .= '#' . $parts['fragment'];
91+
}
92+
93+
return $promise = $connector->connect($uri);
94+
}, function ($e) use ($uri, $reject) {
95+
$reject(new RuntimeException('Connection to ' . $uri .' failed during DNS lookup: ' . $e->getMessage(), 0, $e));
96+
})->then($resolve, $reject);
10097
},
101-
function ($_, $reject) use ($promise) {
98+
function ($_, $reject) use (&$promise, &$resolved, $uri) {
10299
// cancellation should reject connection attempt
103-
$reject(new RuntimeException('Connection attempt cancelled during DNS lookup'));
100+
// reject DNS resolution with custom reason, otherwise rely on connection cancellation below
101+
if ($resolved === null) {
102+
$reject(new RuntimeException('Connection to ' . $uri . ' cancelled during DNS lookup'));
103+
}
104104

105-
// (try to) cancel pending DNS lookup
105+
// (try to) cancel pending DNS lookup / connection attempt
106106
if ($promise instanceof CancellablePromiseInterface) {
107+
// overwrite callback arguments for PHP7+ only, so they do not show
108+
// up in the Exception trace and do not cause a possible cyclic reference.
109+
$_ = $reject = null;
110+
107111
$promise->cancel();
112+
$promise = null;
108113
}
109114
}
110115
);

0 commit comments

Comments
 (0)