From 5ab1806b444843f42765cac23f6ba29583520960 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 25 Sep 2019 17:53:05 -0700 Subject: [PATCH] Add UDS resolver --- packages/grpc-js/src/resolver-uds.ts | 57 ++++++++++++++++++++++++++ packages/grpc-js/src/resolver.ts | 2 + packages/grpc-js/test/test-resolver.ts | 36 ++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 packages/grpc-js/src/resolver-uds.ts diff --git a/packages/grpc-js/src/resolver-uds.ts b/packages/grpc-js/src/resolver-uds.ts new file mode 100644 index 00000000..536a018b --- /dev/null +++ b/packages/grpc-js/src/resolver-uds.ts @@ -0,0 +1,57 @@ +/* + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + Resolver, + ResolverListener, + registerResolver, + registerDefaultResolver, +} from './resolver'; + +function getUdsName(target: string): string { + /* Due to how this resolver is registered, it should only be constructed + * with strings that start with 'unix:'. Other strings may result in + * nonsensical output. If the string starts with 'unix://' that entire + * prefix needs to be ignored */ + if (target.startsWith('unix://')) { + return target.substring(7); + } else { + return target.substring(5); + } +} + +class UdsResolver implements Resolver { + private addresses: string[] = []; + constructor(target: string, private listener: ResolverListener) { + this.addresses = [getUdsName(target)]; + } + updateResolution(): void { + process.nextTick( + this.listener.onSuccessfulResolution, + this.addresses, + null, + null + ); + } + + static getDefaultAuthority(target: string): string { + return 'localhost'; + } +} + +export function setup() { + registerResolver('unix:', UdsResolver); +} diff --git a/packages/grpc-js/src/resolver.ts b/packages/grpc-js/src/resolver.ts index b4c69f3d..512f3c55 100644 --- a/packages/grpc-js/src/resolver.ts +++ b/packages/grpc-js/src/resolver.ts @@ -18,6 +18,7 @@ import { ServiceError } from './call'; import { ServiceConfig } from './service-config'; import * as resolver_dns from './resolver-dns'; +import * as resolver_uds from './resolver-uds'; import { StatusObject } from './call-stream'; /** @@ -137,4 +138,5 @@ export function getDefaultAuthority(target: string): string { export function registerAll() { resolver_dns.setup(); + resolver_uds.setup(); } diff --git a/packages/grpc-js/test/test-resolver.ts b/packages/grpc-js/test/test-resolver.ts index 35c11bf1..1513783b 100644 --- a/packages/grpc-js/test/test-resolver.ts +++ b/packages/grpc-js/test/test-resolver.ts @@ -121,5 +121,41 @@ describe('Name Resolver', () => { const resolver = resolverManager.createResolver(target, listener); resolver.updateResolution(); }); + it('Should handle a relative Unix Domain Socket name', done => { + const target = 'unix:socket'; + const listener: resolverManager.ResolverListener = { + onSuccessfulResolution: ( + addressList: string[], + serviceConfig: ServiceConfig | null, + serviceConfigError: StatusObject | null + ) => { + assert(addressList.includes('socket')); + done(); + }, + onError: (error: StatusObject) => { + done(new Error(`Failed with status ${error.details}`)); + }, + }; + const resolver = resolverManager.createResolver(target, listener); + resolver.updateResolution(); + }); + it('Should handle an absolute Unix Domain Socket name', done => { + const target = 'unix:///tmp/socket'; + const listener: resolverManager.ResolverListener = { + onSuccessfulResolution: ( + addressList: string[], + serviceConfig: ServiceConfig | null, + serviceConfigError: StatusObject | null + ) => { + assert(addressList.includes('/tmp/socket')); + done(); + }, + onError: (error: StatusObject) => { + done(new Error(`Failed with status ${error.details}`)); + }, + }; + const resolver = resolverManager.createResolver(target, listener); + resolver.updateResolution(); + }); }); });