|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import { isIPv4, isIPv6 } from 'net'; |
|
import { StatusObject } from './call-interface'; |
|
import { ChannelOptions } from './channel-options'; |
|
import { LogVerbosity, Status } from './constants'; |
|
import { Metadata } from './metadata'; |
|
import { registerResolver, Resolver, ResolverListener } from './resolver'; |
|
import { SubchannelAddress } from './subchannel-address'; |
|
import { GrpcUri, splitHostPort, uriToString } from './uri-parser'; |
|
import * as logging from './logging'; |
|
|
|
const TRACER_NAME = 'ip_resolver'; |
|
|
|
function trace(text: string): void { |
|
logging.trace(LogVerbosity.DEBUG, TRACER_NAME, text); |
|
} |
|
|
|
const IPV4_SCHEME = 'ipv4'; |
|
const IPV6_SCHEME = 'ipv6'; |
|
|
|
|
|
|
|
|
|
const DEFAULT_PORT = 443; |
|
|
|
class IpResolver implements Resolver { |
|
private addresses: SubchannelAddress[] = []; |
|
private error: StatusObject | null = null; |
|
private hasReturnedResult = false; |
|
constructor( |
|
target: GrpcUri, |
|
private listener: ResolverListener, |
|
channelOptions: ChannelOptions |
|
) { |
|
trace('Resolver constructed for target ' + uriToString(target)); |
|
const addresses: SubchannelAddress[] = []; |
|
if (!(target.scheme === IPV4_SCHEME || target.scheme === IPV6_SCHEME)) { |
|
this.error = { |
|
code: Status.UNAVAILABLE, |
|
details: `Unrecognized scheme ${target.scheme} in IP resolver`, |
|
metadata: new Metadata(), |
|
}; |
|
return; |
|
} |
|
const pathList = target.path.split(','); |
|
for (const path of pathList) { |
|
const hostPort = splitHostPort(path); |
|
if (hostPort === null) { |
|
this.error = { |
|
code: Status.UNAVAILABLE, |
|
details: `Failed to parse ${target.scheme} address ${path}`, |
|
metadata: new Metadata(), |
|
}; |
|
return; |
|
} |
|
if ( |
|
(target.scheme === IPV4_SCHEME && !isIPv4(hostPort.host)) || |
|
(target.scheme === IPV6_SCHEME && !isIPv6(hostPort.host)) |
|
) { |
|
this.error = { |
|
code: Status.UNAVAILABLE, |
|
details: `Failed to parse ${target.scheme} address ${path}`, |
|
metadata: new Metadata(), |
|
}; |
|
return; |
|
} |
|
addresses.push({ |
|
host: hostPort.host, |
|
port: hostPort.port ?? DEFAULT_PORT, |
|
}); |
|
} |
|
this.addresses = addresses; |
|
trace('Parsed ' + target.scheme + ' address list ' + this.addresses); |
|
} |
|
updateResolution(): void { |
|
if (!this.hasReturnedResult) { |
|
this.hasReturnedResult = true; |
|
process.nextTick(() => { |
|
if (this.error) { |
|
this.listener.onError(this.error); |
|
} else { |
|
this.listener.onSuccessfulResolution( |
|
this.addresses, |
|
null, |
|
null, |
|
null, |
|
{} |
|
); |
|
} |
|
}); |
|
} |
|
} |
|
destroy(): void { |
|
this.hasReturnedResult = false; |
|
} |
|
|
|
static getDefaultAuthority(target: GrpcUri): string { |
|
return target.path.split(',')[0]; |
|
} |
|
} |
|
|
|
export function setup() { |
|
registerResolver(IPV4_SCHEME, IpResolver); |
|
registerResolver(IPV6_SCHEME, IpResolver); |
|
} |
|
|