1: <?php
2:
3: namespace React\Socket;
4:
5: use React\Dns\Resolver\Resolver;
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, Resolver $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: return $this
37: ->resolveHostname($host)
38: ->then(function ($ip) use ($connector, $host, $parts) {
39: $uri = '';
40:
41:
42: if (isset($parts['scheme'])) {
43: $uri .= $parts['scheme'] . '://';
44: }
45:
46: if (strpos($ip, ':') !== false) {
47:
48: $uri .= '[' . $ip . ']';
49: } else {
50: $uri .= $ip;
51: }
52:
53:
54: if (isset($parts['port'])) {
55: $uri .= ':' . $parts['port'];
56: }
57:
58:
59: if (isset($parts['path'])) {
60: $uri .= $parts['path'];
61: }
62:
63:
64: if (isset($parts['query'])) {
65: $uri .= '?' . $parts['query'];
66: }
67:
68:
69:
70: $args = array();
71: parse_str(isset($parts['query']) ? $parts['query'] : '', $args);
72: if ($host !== $ip && !isset($args['hostname'])) {
73: $uri .= (isset($parts['query']) ? '&' : '?') . 'hostname=' . rawurlencode($host);
74: }
75:
76:
77: if (isset($parts['fragment'])) {
78: $uri .= '#' . $parts['fragment'];
79: }
80:
81: return $connector->connect($uri);
82: });
83: }
84:
85: private function resolveHostname($host)
86: {
87: if (false !== filter_var($host, FILTER_VALIDATE_IP)) {
88: return Promise\resolve($host);
89: }
90:
91: $promise = $this->resolver->resolve($host);
92:
93: return new Promise\Promise(
94: function ($resolve, $reject) use ($promise) {
95:
96: $promise->then($resolve, $reject);
97: },
98: function ($_, $reject) use ($promise) {
99:
100: $reject(new \RuntimeException('Connection attempt cancelled during DNS lookup'));
101:
102:
103: if ($promise instanceof CancellablePromiseInterface) {
104: $promise->cancel();
105: }
106: }
107: );
108: }
109: }
110: