1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | import { isIP, isIPv6 } from 'net';
|
19 |
|
20 | export interface TcpSubchannelAddress {
|
21 | port: number;
|
22 | host: string;
|
23 | }
|
24 |
|
25 | export interface IpcSubchannelAddress {
|
26 | path: string;
|
27 | }
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 | export type SubchannelAddress = TcpSubchannelAddress | IpcSubchannelAddress;
|
36 |
|
37 | export function isTcpSubchannelAddress(
|
38 | address: SubchannelAddress
|
39 | ): address is TcpSubchannelAddress {
|
40 | return 'port' in address;
|
41 | }
|
42 |
|
43 | export function subchannelAddressEqual(
|
44 | address1?: SubchannelAddress,
|
45 | address2?: SubchannelAddress
|
46 | ): boolean {
|
47 | if (!address1 && !address2) {
|
48 | return true;
|
49 | }
|
50 | if (!address1 || !address2) {
|
51 | return false;
|
52 | }
|
53 | if (isTcpSubchannelAddress(address1)) {
|
54 | return (
|
55 | isTcpSubchannelAddress(address2) &&
|
56 | address1.host === address2.host &&
|
57 | address1.port === address2.port
|
58 | );
|
59 | } else {
|
60 | return !isTcpSubchannelAddress(address2) && address1.path === address2.path;
|
61 | }
|
62 | }
|
63 |
|
64 | export function subchannelAddressToString(address: SubchannelAddress): string {
|
65 | if (isTcpSubchannelAddress(address)) {
|
66 | if (isIPv6(address.host)) {
|
67 | return '[' + address.host + ']:' + address.port;
|
68 | } else {
|
69 | return address.host + ':' + address.port;
|
70 | }
|
71 | } else {
|
72 | return address.path;
|
73 | }
|
74 | }
|
75 |
|
76 | const DEFAULT_PORT = 443;
|
77 |
|
78 | export function stringToSubchannelAddress(
|
79 | addressString: string,
|
80 | port?: number
|
81 | ): SubchannelAddress {
|
82 | if (isIP(addressString)) {
|
83 | return {
|
84 | host: addressString,
|
85 | port: port ?? DEFAULT_PORT,
|
86 | };
|
87 | } else {
|
88 | return {
|
89 | path: addressString,
|
90 | };
|
91 | }
|
92 | }
|
93 |
|
94 | export interface Endpoint {
|
95 | addresses: SubchannelAddress[];
|
96 | }
|
97 |
|
98 | export function endpointEqual(endpoint1: Endpoint, endpoint2: Endpoint) {
|
99 | if (endpoint1.addresses.length !== endpoint2.addresses.length) {
|
100 | return false;
|
101 | }
|
102 | for (let i = 0; i < endpoint1.addresses.length; i++) {
|
103 | if (
|
104 | !subchannelAddressEqual(endpoint1.addresses[i], endpoint2.addresses[i])
|
105 | ) {
|
106 | return false;
|
107 | }
|
108 | }
|
109 | return true;
|
110 | }
|
111 |
|
112 | export function endpointToString(endpoint: Endpoint): string {
|
113 | return (
|
114 | '[' + endpoint.addresses.map(subchannelAddressToString).join(', ') + ']'
|
115 | );
|
116 | }
|
117 |
|
118 | export function endpointHasAddress(
|
119 | endpoint: Endpoint,
|
120 | expectedAddress: SubchannelAddress
|
121 | ): boolean {
|
122 | for (const address of endpoint.addresses) {
|
123 | if (subchannelAddressEqual(address, expectedAddress)) {
|
124 | return true;
|
125 | }
|
126 | }
|
127 | return false;
|
128 | }
|
129 |
|
130 | interface EndpointMapEntry<ValueType> {
|
131 | key: Endpoint;
|
132 | value: ValueType;
|
133 | }
|
134 |
|
135 | function endpointEqualUnordered(
|
136 | endpoint1: Endpoint,
|
137 | endpoint2: Endpoint
|
138 | ): boolean {
|
139 | if (endpoint1.addresses.length !== endpoint2.addresses.length) {
|
140 | return false;
|
141 | }
|
142 | for (const address1 of endpoint1.addresses) {
|
143 | let matchFound = false;
|
144 | for (const address2 of endpoint2.addresses) {
|
145 | if (subchannelAddressEqual(address1, address2)) {
|
146 | matchFound = true;
|
147 | break;
|
148 | }
|
149 | }
|
150 | if (!matchFound) {
|
151 | return false;
|
152 | }
|
153 | }
|
154 | return true;
|
155 | }
|
156 |
|
157 | export class EndpointMap<ValueType> {
|
158 | private map: Set<EndpointMapEntry<ValueType>> = new Set();
|
159 |
|
160 | get size() {
|
161 | return this.map.size;
|
162 | }
|
163 |
|
164 | getForSubchannelAddress(address: SubchannelAddress): ValueType | undefined {
|
165 | for (const entry of this.map) {
|
166 | if (endpointHasAddress(entry.key, address)) {
|
167 | return entry.value;
|
168 | }
|
169 | }
|
170 | return undefined;
|
171 | }
|
172 |
|
173 | |
174 |
|
175 |
|
176 |
|
177 | deleteMissing(endpoints: Endpoint[]): ValueType[] {
|
178 | const removedValues: ValueType[] = [];
|
179 | for (const entry of this.map) {
|
180 | let foundEntry = false;
|
181 | for (const endpoint of endpoints) {
|
182 | if (endpointEqualUnordered(endpoint, entry.key)) {
|
183 | foundEntry = true;
|
184 | }
|
185 | }
|
186 | if (!foundEntry) {
|
187 | removedValues.push(entry.value);
|
188 | this.map.delete(entry);
|
189 | }
|
190 | }
|
191 | return removedValues;
|
192 | }
|
193 |
|
194 | get(endpoint: Endpoint): ValueType | undefined {
|
195 | for (const entry of this.map) {
|
196 | if (endpointEqualUnordered(endpoint, entry.key)) {
|
197 | return entry.value;
|
198 | }
|
199 | }
|
200 | return undefined;
|
201 | }
|
202 |
|
203 | set(endpoint: Endpoint, mapEntry: ValueType) {
|
204 | for (const entry of this.map) {
|
205 | if (endpointEqualUnordered(endpoint, entry.key)) {
|
206 | entry.value = mapEntry;
|
207 | return;
|
208 | }
|
209 | }
|
210 | this.map.add({ key: endpoint, value: mapEntry });
|
211 | }
|
212 |
|
213 | delete(endpoint: Endpoint) {
|
214 | for (const entry of this.map) {
|
215 | if (endpointEqualUnordered(endpoint, entry.key)) {
|
216 | this.map.delete(entry);
|
217 | return;
|
218 | }
|
219 | }
|
220 | }
|
221 |
|
222 | has(endpoint: Endpoint): boolean {
|
223 | for (const entry of this.map) {
|
224 | if (endpointEqualUnordered(endpoint, entry.key)) {
|
225 | return true;
|
226 | }
|
227 | }
|
228 | return false;
|
229 | }
|
230 |
|
231 | clear() {
|
232 | this.map.clear();
|
233 | }
|
234 |
|
235 | *keys(): IterableIterator<Endpoint> {
|
236 | for (const entry of this.map) {
|
237 | yield entry.key;
|
238 | }
|
239 | }
|
240 |
|
241 | *values(): IterableIterator<ValueType> {
|
242 | for (const entry of this.map) {
|
243 | yield entry.value;
|
244 | }
|
245 | }
|
246 |
|
247 | *entries(): IterableIterator<[Endpoint, ValueType]> {
|
248 | for (const entry of this.map) {
|
249 | yield [entry.key, entry.value];
|
250 | }
|
251 | }
|
252 | }
|