1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | import { isIPv4, isIPv6 } from 'net';
|
18 | import { StatusObject } from './call-interface';
|
19 | import { ChannelOptions } from './channel-options';
|
20 | import { LogVerbosity, Status } from './constants';
|
21 | import { Metadata } from './metadata';
|
22 | import { registerResolver, Resolver, ResolverListener } from './resolver';
|
23 | import { SubchannelAddress } from './subchannel-address';
|
24 | import { GrpcUri, splitHostPort, uriToString } from './uri-parser';
|
25 | import * as logging from './logging';
|
26 |
|
27 | const TRACER_NAME = 'ip_resolver';
|
28 |
|
29 | function trace(text: string): void {
|
30 | logging.trace(LogVerbosity.DEBUG, TRACER_NAME, text);
|
31 | }
|
32 |
|
33 | const IPV4_SCHEME = 'ipv4';
|
34 | const IPV6_SCHEME = 'ipv6';
|
35 |
|
36 |
|
37 |
|
38 |
|
39 | const DEFAULT_PORT = 443;
|
40 |
|
41 | class IpResolver implements Resolver {
|
42 | private addresses: SubchannelAddress[] = [];
|
43 | private error: StatusObject | null = null;
|
44 | constructor(
|
45 | target: GrpcUri,
|
46 | private listener: ResolverListener,
|
47 | channelOptions: ChannelOptions
|
48 | ) {
|
49 | trace('Resolver constructed for target ' + uriToString(target));
|
50 | const addresses: SubchannelAddress[] = [];
|
51 | if (!(target.scheme === IPV4_SCHEME || target.scheme === IPV6_SCHEME)) {
|
52 | this.error = {
|
53 | code: Status.UNAVAILABLE,
|
54 | details: `Unrecognized scheme ${target.scheme} in IP resolver`,
|
55 | metadata: new Metadata(),
|
56 | };
|
57 | return;
|
58 | }
|
59 | const pathList = target.path.split(',');
|
60 | for (const path of pathList) {
|
61 | const hostPort = splitHostPort(path);
|
62 | if (hostPort === null) {
|
63 | this.error = {
|
64 | code: Status.UNAVAILABLE,
|
65 | details: `Failed to parse ${target.scheme} address ${path}`,
|
66 | metadata: new Metadata(),
|
67 | };
|
68 | return;
|
69 | }
|
70 | if (
|
71 | (target.scheme === IPV4_SCHEME && !isIPv4(hostPort.host)) ||
|
72 | (target.scheme === IPV6_SCHEME && !isIPv6(hostPort.host))
|
73 | ) {
|
74 | this.error = {
|
75 | code: Status.UNAVAILABLE,
|
76 | details: `Failed to parse ${target.scheme} address ${path}`,
|
77 | metadata: new Metadata(),
|
78 | };
|
79 | return;
|
80 | }
|
81 | addresses.push({
|
82 | host: hostPort.host,
|
83 | port: hostPort.port ?? DEFAULT_PORT,
|
84 | });
|
85 | }
|
86 | this.addresses = addresses;
|
87 | trace('Parsed ' + target.scheme + ' address list ' + this.addresses);
|
88 | }
|
89 | updateResolution(): void {
|
90 | process.nextTick(() => {
|
91 | if (this.error) {
|
92 | this.listener.onError(this.error);
|
93 | } else {
|
94 | this.listener.onSuccessfulResolution(
|
95 | this.addresses,
|
96 | null,
|
97 | null,
|
98 | null,
|
99 | {}
|
100 | );
|
101 | }
|
102 | });
|
103 | }
|
104 | destroy(): void {
|
105 |
|
106 | }
|
107 |
|
108 | static getDefaultAuthority(target: GrpcUri): string {
|
109 | return target.path.split(',')[0];
|
110 | }
|
111 | }
|
112 |
|
113 | export function setup() {
|
114 | registerResolver(IPV4_SCHEME, IpResolver);
|
115 | registerResolver(IPV6_SCHEME, IpResolver);
|
116 | }
|