Subversion Repositories php-qbpwcf

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 liveuser 1
<?php
2
 
3
namespace React\Socket;
4
 
5
use React\Dns\Resolver\ResolverInterface;
6
use React\Promise;
7
use React\Promise\CancellablePromiseInterface;
8
 
9
final class DnsConnector implements ConnectorInterface
10
{
11
    private $connector;
12
    private $resolver;
13
 
14
    public function __construct(ConnectorInterface $connector, ResolverInterface $resolver)
15
    {
16
        $this->connector = $connector;
17
        $this->resolver = $resolver;
18
    }
19
 
20
    public function connect($uri)
21
    {
22
        if (\strpos($uri, '://') === false) {
23
            $parts = \parse_url('tcp://' . $uri);
24
            unset($parts['scheme']);
25
        } else {
26
            $parts = \parse_url($uri);
27
        }
28
 
29
        if (!$parts || !isset($parts['host'])) {
30
            return Promise\reject(new \InvalidArgumentException('Given URI "' . $uri . '" is invalid'));
31
        }
32
 
33
        $host = \trim($parts['host'], '[]');
34
        $connector = $this->connector;
35
 
36
        // skip DNS lookup / URI manipulation if this URI already contains an IP
37
        if (false !== \filter_var($host, \FILTER_VALIDATE_IP)) {
38
            return $connector->connect($uri);
39
        }
40
 
41
        $promise = $this->resolver->resolve($host);
42
        $resolved = null;
43
 
44
        return new Promise\Promise(
45
            function ($resolve, $reject) use (&$promise, &$resolved, $uri, $connector, $host, $parts) {
46
                // resolve/reject with result of DNS lookup
47
                $promise->then(function ($ip) use (&$promise, &$resolved, $connector, $host, $parts) {
48
                    $resolved = $ip;
49
                    $uri = '';
50
 
51
                    // prepend original scheme if known
52
                    if (isset($parts['scheme'])) {
53
                        $uri .= $parts['scheme'] . '://';
54
                    }
55
 
56
                    if (\strpos($ip, ':') !== false) {
57
                        // enclose IPv6 addresses in square brackets before appending port
58
                        $uri .= '[' . $ip . ']';
59
                    } else {
60
                        $uri .= $ip;
61
                    }
62
 
63
                    // append original port if known
64
                    if (isset($parts['port'])) {
65
                        $uri .= ':' . $parts['port'];
66
                    }
67
 
68
                    // append orignal path if known
69
                    if (isset($parts['path'])) {
70
                        $uri .= $parts['path'];
71
                    }
72
 
73
                    // append original query if known
74
                    if (isset($parts['query'])) {
75
                        $uri .= '?' . $parts['query'];
76
                    }
77
 
78
                    // append original hostname as query if resolved via DNS and if
79
                    // destination URI does not contain "hostname" query param already
80
                    $args = array();
81
                    \parse_str(isset($parts['query']) ? $parts['query'] : '', $args);
82
                    if ($host !== $ip && !isset($args['hostname'])) {
83
                        $uri .= (isset($parts['query']) ? '&' : '?') . 'hostname=' . \rawurlencode($host);
84
                    }
85
 
86
                    // append original fragment if known
87
                    if (isset($parts['fragment'])) {
88
                        $uri .= '#' . $parts['fragment'];
89
                    }
90
 
91
                    return $promise = $connector->connect($uri);
92
                }, function ($e) use ($uri, $reject) {
93
                    $reject(new \RuntimeException('Connection to ' . $uri .' failed during DNS lookup: ' . $e->getMessage(), 0, $e));
94
                })->then($resolve, $reject);
95
            },
96
            function ($_, $reject) use (&$promise, &$resolved, $uri) {
97
                // cancellation should reject connection attempt
98
                // reject DNS resolution with custom reason, otherwise rely on connection cancellation below
99
                if ($resolved === null) {
100
                    $reject(new \RuntimeException('Connection to ' . $uri . ' cancelled during DNS lookup'));
101
                }
102
 
103
                // (try to) cancel pending DNS lookup / connection attempt
104
                if ($promise instanceof CancellablePromiseInterface) {
105
                    // overwrite callback arguments for PHP7+ only, so they do not show
106
                    // up in the Exception trace and do not cause a possible cyclic reference.
107
                    $_ = $reject = null;
108
 
109
                    $promise->cancel();
110
                    $promise = null;
111
                }
112
            }
113
        );
114
    }
115
}