|
9 | 9 |
|
10 | 10 | class ConnectionManagerSelective implements ConnectorInterface |
11 | 11 | { |
12 | | - const MATCH_ALL = '*'; |
| 12 | + private $managers; |
13 | 13 |
|
14 | | - private $targets = array(); |
15 | | - |
16 | | - public function create($host, $port) |
| 14 | + /** |
| 15 | + * |
| 16 | + * @param ConnectorInterface[] $managers |
| 17 | + */ |
| 18 | + public function __construct(array $managers) |
17 | 19 | { |
18 | | - try { |
19 | | - $cm = $this->getConnectionManagerFor($host, $port); |
20 | | - } |
21 | | - catch (UnderflowException $e) { |
22 | | - return Promise\reject($e); |
23 | | - } |
24 | | - return $cm->create($host, $port); |
25 | | - } |
| 20 | + foreach ($managers as $filter => $manager) { |
| 21 | + $host = $filter; |
| 22 | + $portMin = 0; |
| 23 | + $portMax = 65535; |
26 | 24 |
|
27 | | - public function addConnectionManagerFor($connectionManager, $targetHost=self::MATCH_ALL, $targetPort=self::MATCH_ALL, $priority=0) |
28 | | - { |
29 | | - $this->targets []= array( |
30 | | - 'connectionManager' => $connectionManager, |
31 | | - 'matchHost' => $this->createMatcherHost($targetHost), |
32 | | - 'matchPort' => $this->createMatcherPort($targetPort), |
33 | | - 'host' => $targetHost, |
34 | | - 'port' => $targetPort, |
35 | | - 'priority' => $priority |
36 | | - ); |
37 | | - |
38 | | - // return the key as new entry ID |
39 | | - end($this->targets); |
40 | | - $id = key($this->targets); |
41 | | - |
42 | | - // sort array by priority |
43 | | - $targets =& $this->targets; |
44 | | - uksort($this->targets, function ($a, $b) use ($targets) { |
45 | | - $pa = $targets[$a]['priority']; |
46 | | - $pb = $targets[$b]['priority']; |
47 | | - return ($pa < $pb ? -1 : ($pa > $pb ? 1 : ($a - $b))); |
48 | | - }); |
49 | | - |
50 | | - return $id; |
51 | | - } |
| 25 | + // search colon (either single one OR preceded by "]" due to IPv6) |
| 26 | + $colon = strrpos($host, ':'); |
| 27 | + if ($colon !== false && (strpos($host, ':') === $colon || substr($host, $colon - 1, 1) === ']' )) { |
| 28 | + if (!isset($host[$colon + 1])) { |
| 29 | + throw new InvalidArgumentException('Entry "' . $filter . '" has no port after colon'); |
| 30 | + } |
52 | 31 |
|
53 | | - public function getConnectionManagerEntries() |
54 | | - { |
55 | | - return $this->targets; |
56 | | - } |
| 32 | + $minus = strpos($host, '-', $colon); |
| 33 | + if ($minus === false) { |
| 34 | + $portMin = $portMax = (int)substr($host, $colon + 1); |
57 | 35 |
|
58 | | - public function removeConnectionManagerEntry($id) |
59 | | - { |
60 | | - unset($this->targets[$id]); |
61 | | - } |
| 36 | + if (substr($host, $colon + 1) !== (string)$portMin) { |
| 37 | + throw new InvalidArgumentException('Entry "' . $filter . '" has no valid port after colon'); |
| 38 | + } |
| 39 | + } else { |
| 40 | + $portMin = (int)substr($host, $colon + 1, ($minus - $colon)); |
| 41 | + $portMax = (int)substr($host, $minus + 1); |
62 | 42 |
|
63 | | - public function getConnectionManagerFor($targetHost, $targetPort) |
64 | | - { |
65 | | - foreach ($this->targets as $target) { |
66 | | - if ($target['matchPort']($targetPort) && $target['matchHost']($targetHost)) { |
67 | | - return $target['connectionManager']; |
68 | | - } |
69 | | - } |
70 | | - throw new UnderflowException('No connection manager for given target found'); |
71 | | - } |
| 43 | + if (substr($host, $colon + 1) !== ($portMin . '-' . $portMax)) { |
| 44 | + throw new InvalidArgumentException('Entry "' . $filter . '" has no valid port range after colon'); |
| 45 | + } |
72 | 46 |
|
73 | | - // * |
74 | | - // singlePort |
75 | | - // startPort - targetPort |
76 | | - // port1, port2, port3 |
77 | | - // startPort - targetPort, portAdditional |
78 | | - public function createMatcherPort($pattern) |
79 | | - { |
80 | | - if ($pattern === self::MATCH_ALL) { |
81 | | - return function() { |
82 | | - return true; |
83 | | - }; |
84 | | - } else if (strpos($pattern, ',') !== false) { |
85 | | - $checks = array(); |
86 | | - foreach (explode(',', $pattern) as $part) { |
87 | | - $checks []= $this->createMatcherPort(trim($part)); |
88 | | - } |
89 | | - return function ($port) use ($checks) { |
90 | | - foreach ($checks as $check) { |
91 | | - if ($check($port)) { |
92 | | - return true; |
| 47 | + if ($portMin > $portMax) { |
| 48 | + throw new InvalidArgumentException('Entry "' . $filter . '" has port range mixed up'); |
93 | 49 | } |
94 | 50 | } |
95 | | - return false; |
96 | | - }; |
97 | | - } else if (preg_match('/^(\d+)$/', $pattern, $match)) { |
98 | | - $single = $this->coercePort($match[1]); |
99 | | - return function ($port) use ($single) { |
100 | | - return ($port == $single); |
101 | | - }; |
102 | | - } else if (preg_match('/^(\d+)\s*\-\s*(\d+)$/', $pattern, $match)) { |
103 | | - $start = $this->coercePort($match[1]); |
104 | | - $end = $this->coercePort($match[2]); |
105 | | - if ($start >= $end) { |
106 | | - throw new InvalidArgumentException('Invalid port range given'); |
| 51 | + $host = substr($host, 0, $colon); |
| 52 | + } |
| 53 | + |
| 54 | + if ($host === '') { |
| 55 | + throw new InvalidArgumentException('Entry "' . $filter . '" has an empty host'); |
| 56 | + } |
| 57 | + |
| 58 | + if (!$manager instanceof ConnectorInterface) { |
| 59 | + throw new InvalidArgumentException('Entry "' . $filter . '" is not a valid connector'); |
107 | 60 | } |
108 | | - return function($port) use ($start, $end) { |
109 | | - return ($port >= $start && $port <= $end); |
110 | | - }; |
111 | | - } else { |
112 | | - throw new InvalidArgumentException('Invalid port matcher given'); |
113 | 61 | } |
| 62 | + |
| 63 | + $this->managers = $managers; |
114 | 64 | } |
115 | 65 |
|
116 | | - private function coercePort($port) |
| 66 | + public function create($host, $port) |
117 | 67 | { |
118 | | - // TODO: check 0-65535 |
119 | | - return (int)$port; |
| 68 | + try { |
| 69 | + $connector = $this->getConnectorForTarget($host, $port); |
| 70 | + } catch (UnderflowException $e) { |
| 71 | + return Promise\reject($e); |
| 72 | + } |
| 73 | + return $connector->create($host, $port); |
120 | 74 | } |
121 | 75 |
|
122 | | - // * |
123 | | - // targetHostname |
124 | | - // targetIp |
125 | | - // targetHostname, otherTargetHostname, anotherTargetHostname |
126 | | - // TODO: targetIp/netmaskNum |
127 | | - // TODO: targetIp/netmaskIp |
128 | | - public function createMatcherHost($pattern) |
| 76 | + private function getConnectorForTarget($targetHost, $targetPort) |
129 | 77 | { |
130 | | - if ($pattern === self::MATCH_ALL) { |
131 | | - return function() { |
132 | | - return true; |
133 | | - }; |
134 | | - } else if (strpos($pattern, ',') !== false) { |
135 | | - $checks = array(); |
136 | | - foreach (explode(',', $pattern) as $part) { |
137 | | - $checks []= $this->createMatcherHost(trim($part)); |
138 | | - } |
139 | | - return function ($host) use ($checks) { |
140 | | - foreach ($checks as $check) { |
141 | | - if ($check($host)) { |
142 | | - return true; |
143 | | - } |
| 78 | + foreach ($this->managers as $host => $connector) { |
| 79 | + $portMin = 0; |
| 80 | + $portMax = 65535; |
| 81 | + |
| 82 | + // search colon (either single one OR preceded by "]" due to IPv6) |
| 83 | + $colon = strrpos($host, ':'); |
| 84 | + if ($colon !== false && (strpos($host, ':') === $colon || substr($host, $colon - 1, 1) === ']' )) { |
| 85 | + $minus = strpos($host, '-', $colon); |
| 86 | + if ($minus === false) { |
| 87 | + $portMin = $portMax = (int)substr($host, $colon + 1); |
| 88 | + } else { |
| 89 | + $portMin = (int)substr($host, $colon + 1, ($minus - $colon)); |
| 90 | + $portMax = (int)substr($host, $minus + 1); |
144 | 91 | } |
145 | | - return false; |
146 | | - }; |
147 | | - } else if (is_string($pattern)) { |
148 | | - $pattern = strtolower($pattern); |
149 | | - return function($target) use ($pattern) { |
150 | | - return fnmatch($pattern, strtolower($target)); |
151 | | - }; |
152 | | - } else { |
153 | | - throw new InvalidArgumentException('Invalid host matcher given'); |
| 92 | + $host = trim(substr($host, 0, $colon), '[]'); |
| 93 | + } |
| 94 | + |
| 95 | + if ($targetPort >= $portMin && $targetPort <= $portMax && fnmatch($host, $targetHost)) { |
| 96 | + return $connector; |
| 97 | + } |
154 | 98 | } |
| 99 | + |
| 100 | + throw new UnderflowException('No connector for given target found'); |
155 | 101 | } |
156 | 102 | } |
0 commit comments