1 | /*
|
2 | * Copyright 2019 gRPC authors.
|
3 | *
|
4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
5 | * you may not use this file except in compliance with the License.
|
6 | * You may obtain a copy of the License at
|
7 | *
|
8 | * http://www.apache.org/licenses/LICENSE-2.0
|
9 | *
|
10 | * Unless required by applicable law or agreed to in writing, software
|
11 | * distributed under the License is distributed on an "AS IS" BASIS,
|
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 | * See the License for the specific language governing permissions and
|
14 | * limitations under the License.
|
15 | *
|
16 | */
|
17 |
|
18 | import { MethodConfig, ServiceConfig } from './service-config';
|
19 | import { StatusObject } from './call-interface';
|
20 | import { SubchannelAddress } from './subchannel-address';
|
21 | import { GrpcUri, uriToString } from './uri-parser';
|
22 | import { ChannelOptions } from './channel-options';
|
23 | import { Metadata } from './metadata';
|
24 | import { Status } from './constants';
|
25 | import { Filter, FilterFactory } from './filter';
|
26 |
|
27 | export interface CallConfig {
|
28 | methodConfig: MethodConfig;
|
29 | onCommitted?: () => void;
|
30 | pickInformation: { [key: string]: string };
|
31 | status: Status;
|
32 | dynamicFilterFactories: FilterFactory<Filter>[];
|
33 | }
|
34 |
|
35 | /**
|
36 | * Selects a configuration for a method given the name and metadata. Defined in
|
37 | * https://github.com/grpc/proposal/blob/master/A31-xds-timeout-support-and-config-selector.md#new-functionality-in-grpc
|
38 | */
|
39 | export interface ConfigSelector {
|
40 | (methodName: string, metadata: Metadata): CallConfig;
|
41 | }
|
42 |
|
43 | /**
|
44 | * A listener object passed to the resolver's constructor that provides name
|
45 | * resolution updates back to the resolver's owner.
|
46 | */
|
47 | export interface ResolverListener {
|
48 | /**
|
49 | * Called whenever the resolver has new name resolution results to report
|
50 | * @param addressList The new list of backend addresses
|
51 | * @param serviceConfig The new service configuration corresponding to the
|
52 | * `addressList`. Will be `null` if no service configuration was
|
53 | * retrieved or if the service configuration was invalid
|
54 | * @param serviceConfigError If non-`null`, indicates that the retrieved
|
55 | * service configuration was invalid
|
56 | */
|
57 | onSuccessfulResolution(
|
58 | addressList: SubchannelAddress[],
|
59 | serviceConfig: ServiceConfig | null,
|
60 | serviceConfigError: StatusObject | null,
|
61 | configSelector: ConfigSelector | null,
|
62 | attributes: { [key: string]: unknown }
|
63 | ): void;
|
64 | /**
|
65 | * Called whenever a name resolution attempt fails.
|
66 | * @param error Describes how resolution failed
|
67 | */
|
68 | onError(error: StatusObject): void;
|
69 | }
|
70 |
|
71 | /**
|
72 | * A resolver class that handles one or more of the name syntax schemes defined
|
73 | * in the [gRPC Name Resolution document](https://github.com/grpc/grpc/blob/master/doc/naming.md)
|
74 | */
|
75 | export interface Resolver {
|
76 | /**
|
77 | * Indicates that the caller wants new name resolution data. Calling this
|
78 | * function may eventually result in calling one of the `ResolverListener`
|
79 | * functions, but that is not guaranteed. Those functions will never be
|
80 | * called synchronously with the constructor or updateResolution.
|
81 | */
|
82 | updateResolution(): void;
|
83 |
|
84 | /**
|
85 | * Destroy the resolver. Should be called when the owning channel shuts down.
|
86 | */
|
87 | destroy(): void;
|
88 | }
|
89 |
|
90 | export interface ResolverConstructor {
|
91 | new (
|
92 | target: GrpcUri,
|
93 | listener: ResolverListener,
|
94 | channelOptions: ChannelOptions
|
95 | ): Resolver;
|
96 | /**
|
97 | * Get the default authority for a target. This loosely corresponds to that
|
98 | * target's hostname. Throws an error if this resolver class cannot parse the
|
99 | * `target`.
|
100 | * @param target
|
101 | */
|
102 | getDefaultAuthority(target: GrpcUri): string;
|
103 | }
|
104 |
|
105 | const registeredResolvers: { [scheme: string]: ResolverConstructor } = {};
|
106 | let defaultScheme: string | null = null;
|
107 |
|
108 | /**
|
109 | * Register a resolver class to handle target names prefixed with the `prefix`
|
110 | * string. This prefix should correspond to a URI scheme name listed in the
|
111 | * [gRPC Name Resolution document](https://github.com/grpc/grpc/blob/master/doc/naming.md)
|
112 | * @param prefix
|
113 | * @param resolverClass
|
114 | */
|
115 | export function registerResolver(
|
116 | scheme: string,
|
117 | resolverClass: ResolverConstructor
|
118 | ) {
|
119 | registeredResolvers[scheme] = resolverClass;
|
120 | }
|
121 |
|
122 | /**
|
123 | * Register a default resolver to handle target names that do not start with
|
124 | * any registered prefix.
|
125 | * @param resolverClass
|
126 | */
|
127 | export function registerDefaultScheme(scheme: string) {
|
128 | defaultScheme = scheme;
|
129 | }
|
130 |
|
131 | /**
|
132 | * Create a name resolver for the specified target, if possible. Throws an
|
133 | * error if no such name resolver can be created.
|
134 | * @param target
|
135 | * @param listener
|
136 | */
|
137 | export function createResolver(
|
138 | target: GrpcUri,
|
139 | listener: ResolverListener,
|
140 | options: ChannelOptions
|
141 | ): Resolver {
|
142 | if (target.scheme !== undefined && target.scheme in registeredResolvers) {
|
143 | return new registeredResolvers[target.scheme](target, listener, options);
|
144 | } else {
|
145 | throw new Error(
|
146 | `No resolver could be created for target ${uriToString(target)}`
|
147 | );
|
148 | }
|
149 | }
|
150 |
|
151 | /**
|
152 | * Get the default authority for the specified target, if possible. Throws an
|
153 | * error if no registered name resolver can parse that target string.
|
154 | * @param target
|
155 | */
|
156 | export function getDefaultAuthority(target: GrpcUri): string {
|
157 | if (target.scheme !== undefined && target.scheme in registeredResolvers) {
|
158 | return registeredResolvers[target.scheme].getDefaultAuthority(target);
|
159 | } else {
|
160 | throw new Error(`Invalid target ${uriToString(target)}`);
|
161 | }
|
162 | }
|
163 |
|
164 | export function mapUriDefaultScheme(target: GrpcUri): GrpcUri | null {
|
165 | if (target.scheme === undefined || !(target.scheme in registeredResolvers)) {
|
166 | if (defaultScheme !== null) {
|
167 | return {
|
168 | scheme: defaultScheme,
|
169 | authority: undefined,
|
170 | path: uriToString(target),
|
171 | };
|
172 | } else {
|
173 | return null;
|
174 | }
|
175 | }
|
176 | return target;
|
177 | }
|