UNPKG

18.8 kBJavaScriptView Raw
1"use strict";
2/*
3 * Copyright 2019 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18Object.defineProperty(exports, "__esModule", { value: true });
19exports.Client = void 0;
20const call_1 = require("./call");
21const channel_1 = require("./channel");
22const connectivity_state_1 = require("./connectivity-state");
23const constants_1 = require("./constants");
24const metadata_1 = require("./metadata");
25const client_interceptors_1 = require("./client-interceptors");
26const CHANNEL_SYMBOL = Symbol();
27const INTERCEPTOR_SYMBOL = Symbol();
28const INTERCEPTOR_PROVIDER_SYMBOL = Symbol();
29const CALL_INVOCATION_TRANSFORMER_SYMBOL = Symbol();
30function isFunction(arg) {
31 return typeof arg === 'function';
32}
33function getErrorStackString(error) {
34 return error.stack.split('\n').slice(1).join('\n');
35}
36/**
37 * A generic gRPC client. Primarily useful as a base class for all generated
38 * clients.
39 */
40class Client {
41 constructor(address, credentials, options = {}) {
42 var _a, _b;
43 options = Object.assign({}, options);
44 this[INTERCEPTOR_SYMBOL] = (_a = options.interceptors) !== null && _a !== void 0 ? _a : [];
45 delete options.interceptors;
46 this[INTERCEPTOR_PROVIDER_SYMBOL] = (_b = options.interceptor_providers) !== null && _b !== void 0 ? _b : [];
47 delete options.interceptor_providers;
48 if (this[INTERCEPTOR_SYMBOL].length > 0 &&
49 this[INTERCEPTOR_PROVIDER_SYMBOL].length > 0) {
50 throw new Error('Both interceptors and interceptor_providers were passed as options ' +
51 'to the client constructor. Only one of these is allowed.');
52 }
53 this[CALL_INVOCATION_TRANSFORMER_SYMBOL] =
54 options.callInvocationTransformer;
55 delete options.callInvocationTransformer;
56 if (options.channelOverride) {
57 this[CHANNEL_SYMBOL] = options.channelOverride;
58 }
59 else if (options.channelFactoryOverride) {
60 const channelFactoryOverride = options.channelFactoryOverride;
61 delete options.channelFactoryOverride;
62 this[CHANNEL_SYMBOL] = channelFactoryOverride(address, credentials, options);
63 }
64 else {
65 this[CHANNEL_SYMBOL] = new channel_1.ChannelImplementation(address, credentials, options);
66 }
67 }
68 close() {
69 this[CHANNEL_SYMBOL].close();
70 }
71 getChannel() {
72 return this[CHANNEL_SYMBOL];
73 }
74 waitForReady(deadline, callback) {
75 const checkState = (err) => {
76 if (err) {
77 callback(new Error('Failed to connect before the deadline'));
78 return;
79 }
80 let newState;
81 try {
82 newState = this[CHANNEL_SYMBOL].getConnectivityState(true);
83 }
84 catch (e) {
85 callback(new Error('The channel has been closed'));
86 return;
87 }
88 if (newState === connectivity_state_1.ConnectivityState.READY) {
89 callback();
90 }
91 else {
92 try {
93 this[CHANNEL_SYMBOL].watchConnectivityState(newState, deadline, checkState);
94 }
95 catch (e) {
96 callback(new Error('The channel has been closed'));
97 }
98 }
99 };
100 setImmediate(checkState);
101 }
102 checkOptionalUnaryResponseArguments(arg1, arg2, arg3) {
103 if (isFunction(arg1)) {
104 return { metadata: new metadata_1.Metadata(), options: {}, callback: arg1 };
105 }
106 else if (isFunction(arg2)) {
107 if (arg1 instanceof metadata_1.Metadata) {
108 return { metadata: arg1, options: {}, callback: arg2 };
109 }
110 else {
111 return { metadata: new metadata_1.Metadata(), options: arg1, callback: arg2 };
112 }
113 }
114 else {
115 if (!(arg1 instanceof metadata_1.Metadata &&
116 arg2 instanceof Object &&
117 isFunction(arg3))) {
118 throw new Error('Incorrect arguments passed');
119 }
120 return { metadata: arg1, options: arg2, callback: arg3 };
121 }
122 }
123 makeUnaryRequest(method, serialize, deserialize, argument, metadata, options, callback) {
124 var _a, _b;
125 const checkedArguments = this.checkOptionalUnaryResponseArguments(metadata, options, callback);
126 const methodDefinition = {
127 path: method,
128 requestStream: false,
129 responseStream: false,
130 requestSerialize: serialize,
131 responseDeserialize: deserialize,
132 };
133 let callProperties = {
134 argument: argument,
135 metadata: checkedArguments.metadata,
136 call: new call_1.ClientUnaryCallImpl(),
137 channel: this[CHANNEL_SYMBOL],
138 methodDefinition: methodDefinition,
139 callOptions: checkedArguments.options,
140 callback: checkedArguments.callback,
141 };
142 if (this[CALL_INVOCATION_TRANSFORMER_SYMBOL]) {
143 callProperties = this[CALL_INVOCATION_TRANSFORMER_SYMBOL](callProperties);
144 }
145 const emitter = callProperties.call;
146 const interceptorArgs = {
147 clientInterceptors: this[INTERCEPTOR_SYMBOL],
148 clientInterceptorProviders: this[INTERCEPTOR_PROVIDER_SYMBOL],
149 callInterceptors: (_a = callProperties.callOptions.interceptors) !== null && _a !== void 0 ? _a : [],
150 callInterceptorProviders: (_b = callProperties.callOptions.interceptor_providers) !== null && _b !== void 0 ? _b : [],
151 };
152 const call = client_interceptors_1.getInterceptingCall(interceptorArgs, callProperties.methodDefinition, callProperties.callOptions, callProperties.channel);
153 /* This needs to happen before the emitter is used. Unfortunately we can't
154 * enforce this with the type system. We need to construct this emitter
155 * before calling the CallInvocationTransformer, and we need to create the
156 * call after that. */
157 emitter.call = call;
158 if (callProperties.callOptions.credentials) {
159 call.setCredentials(callProperties.callOptions.credentials);
160 }
161 let responseMessage = null;
162 let receivedStatus = false;
163 const callerStackError = new Error();
164 call.start(callProperties.metadata, {
165 onReceiveMetadata: (metadata) => {
166 emitter.emit('metadata', metadata);
167 },
168 // eslint-disable-next-line @typescript-eslint/no-explicit-any
169 onReceiveMessage(message) {
170 if (responseMessage !== null) {
171 call.cancelWithStatus(constants_1.Status.INTERNAL, 'Too many responses received');
172 }
173 responseMessage = message;
174 },
175 onReceiveStatus(status) {
176 if (receivedStatus) {
177 return;
178 }
179 receivedStatus = true;
180 if (status.code === constants_1.Status.OK) {
181 if (responseMessage === null) {
182 const callerStack = getErrorStackString(callerStackError);
183 callProperties.callback(call_1.callErrorFromStatus({
184 code: constants_1.Status.INTERNAL,
185 details: 'No message received',
186 metadata: status.metadata
187 }, callerStack));
188 }
189 else {
190 callProperties.callback(null, responseMessage);
191 }
192 }
193 else {
194 const callerStack = getErrorStackString(callerStackError);
195 callProperties.callback(call_1.callErrorFromStatus(status, callerStack));
196 }
197 emitter.emit('status', status);
198 },
199 });
200 call.sendMessage(argument);
201 call.halfClose();
202 return emitter;
203 }
204 makeClientStreamRequest(method, serialize, deserialize, metadata, options, callback) {
205 var _a, _b;
206 const checkedArguments = this.checkOptionalUnaryResponseArguments(metadata, options, callback);
207 const methodDefinition = {
208 path: method,
209 requestStream: true,
210 responseStream: false,
211 requestSerialize: serialize,
212 responseDeserialize: deserialize,
213 };
214 let callProperties = {
215 metadata: checkedArguments.metadata,
216 call: new call_1.ClientWritableStreamImpl(serialize),
217 channel: this[CHANNEL_SYMBOL],
218 methodDefinition: methodDefinition,
219 callOptions: checkedArguments.options,
220 callback: checkedArguments.callback,
221 };
222 if (this[CALL_INVOCATION_TRANSFORMER_SYMBOL]) {
223 callProperties = this[CALL_INVOCATION_TRANSFORMER_SYMBOL](callProperties);
224 }
225 const emitter = callProperties.call;
226 const interceptorArgs = {
227 clientInterceptors: this[INTERCEPTOR_SYMBOL],
228 clientInterceptorProviders: this[INTERCEPTOR_PROVIDER_SYMBOL],
229 callInterceptors: (_a = callProperties.callOptions.interceptors) !== null && _a !== void 0 ? _a : [],
230 callInterceptorProviders: (_b = callProperties.callOptions.interceptor_providers) !== null && _b !== void 0 ? _b : [],
231 };
232 const call = client_interceptors_1.getInterceptingCall(interceptorArgs, callProperties.methodDefinition, callProperties.callOptions, callProperties.channel);
233 /* This needs to happen before the emitter is used. Unfortunately we can't
234 * enforce this with the type system. We need to construct this emitter
235 * before calling the CallInvocationTransformer, and we need to create the
236 * call after that. */
237 emitter.call = call;
238 if (callProperties.callOptions.credentials) {
239 call.setCredentials(callProperties.callOptions.credentials);
240 }
241 let responseMessage = null;
242 let receivedStatus = false;
243 const callerStackError = new Error();
244 call.start(callProperties.metadata, {
245 onReceiveMetadata: (metadata) => {
246 emitter.emit('metadata', metadata);
247 },
248 // eslint-disable-next-line @typescript-eslint/no-explicit-any
249 onReceiveMessage(message) {
250 if (responseMessage !== null) {
251 call.cancelWithStatus(constants_1.Status.INTERNAL, 'Too many responses received');
252 }
253 responseMessage = message;
254 },
255 onReceiveStatus(status) {
256 if (receivedStatus) {
257 return;
258 }
259 receivedStatus = true;
260 if (status.code === constants_1.Status.OK) {
261 if (responseMessage === null) {
262 const callerStack = getErrorStackString(callerStackError);
263 callProperties.callback(call_1.callErrorFromStatus({
264 code: constants_1.Status.INTERNAL,
265 details: 'No message received',
266 metadata: status.metadata
267 }, callerStack));
268 }
269 else {
270 callProperties.callback(null, responseMessage);
271 }
272 }
273 else {
274 const callerStack = getErrorStackString(callerStackError);
275 callProperties.callback(call_1.callErrorFromStatus(status, callerStack));
276 }
277 emitter.emit('status', status);
278 },
279 });
280 return emitter;
281 }
282 checkMetadataAndOptions(arg1, arg2) {
283 let metadata;
284 let options;
285 if (arg1 instanceof metadata_1.Metadata) {
286 metadata = arg1;
287 if (arg2) {
288 options = arg2;
289 }
290 else {
291 options = {};
292 }
293 }
294 else {
295 if (arg1) {
296 options = arg1;
297 }
298 else {
299 options = {};
300 }
301 metadata = new metadata_1.Metadata();
302 }
303 return { metadata, options };
304 }
305 makeServerStreamRequest(method, serialize, deserialize, argument, metadata, options) {
306 var _a, _b;
307 const checkedArguments = this.checkMetadataAndOptions(metadata, options);
308 const methodDefinition = {
309 path: method,
310 requestStream: false,
311 responseStream: true,
312 requestSerialize: serialize,
313 responseDeserialize: deserialize,
314 };
315 let callProperties = {
316 argument: argument,
317 metadata: checkedArguments.metadata,
318 call: new call_1.ClientReadableStreamImpl(deserialize),
319 channel: this[CHANNEL_SYMBOL],
320 methodDefinition: methodDefinition,
321 callOptions: checkedArguments.options,
322 };
323 if (this[CALL_INVOCATION_TRANSFORMER_SYMBOL]) {
324 callProperties = this[CALL_INVOCATION_TRANSFORMER_SYMBOL](callProperties);
325 }
326 const stream = callProperties.call;
327 const interceptorArgs = {
328 clientInterceptors: this[INTERCEPTOR_SYMBOL],
329 clientInterceptorProviders: this[INTERCEPTOR_PROVIDER_SYMBOL],
330 callInterceptors: (_a = callProperties.callOptions.interceptors) !== null && _a !== void 0 ? _a : [],
331 callInterceptorProviders: (_b = callProperties.callOptions.interceptor_providers) !== null && _b !== void 0 ? _b : [],
332 };
333 const call = client_interceptors_1.getInterceptingCall(interceptorArgs, callProperties.methodDefinition, callProperties.callOptions, callProperties.channel);
334 /* This needs to happen before the emitter is used. Unfortunately we can't
335 * enforce this with the type system. We need to construct this emitter
336 * before calling the CallInvocationTransformer, and we need to create the
337 * call after that. */
338 stream.call = call;
339 if (callProperties.callOptions.credentials) {
340 call.setCredentials(callProperties.callOptions.credentials);
341 }
342 let receivedStatus = false;
343 const callerStackError = new Error();
344 call.start(callProperties.metadata, {
345 onReceiveMetadata(metadata) {
346 stream.emit('metadata', metadata);
347 },
348 // eslint-disable-next-line @typescript-eslint/no-explicit-any
349 onReceiveMessage(message) {
350 stream.push(message);
351 },
352 onReceiveStatus(status) {
353 if (receivedStatus) {
354 return;
355 }
356 receivedStatus = true;
357 stream.push(null);
358 if (status.code !== constants_1.Status.OK) {
359 const callerStack = getErrorStackString(callerStackError);
360 stream.emit('error', call_1.callErrorFromStatus(status, callerStack));
361 }
362 stream.emit('status', status);
363 },
364 });
365 call.sendMessage(argument);
366 call.halfClose();
367 return stream;
368 }
369 makeBidiStreamRequest(method, serialize, deserialize, metadata, options) {
370 var _a, _b;
371 const checkedArguments = this.checkMetadataAndOptions(metadata, options);
372 const methodDefinition = {
373 path: method,
374 requestStream: true,
375 responseStream: true,
376 requestSerialize: serialize,
377 responseDeserialize: deserialize,
378 };
379 let callProperties = {
380 metadata: checkedArguments.metadata,
381 call: new call_1.ClientDuplexStreamImpl(serialize, deserialize),
382 channel: this[CHANNEL_SYMBOL],
383 methodDefinition: methodDefinition,
384 callOptions: checkedArguments.options,
385 };
386 if (this[CALL_INVOCATION_TRANSFORMER_SYMBOL]) {
387 callProperties = this[CALL_INVOCATION_TRANSFORMER_SYMBOL](callProperties);
388 }
389 const stream = callProperties.call;
390 const interceptorArgs = {
391 clientInterceptors: this[INTERCEPTOR_SYMBOL],
392 clientInterceptorProviders: this[INTERCEPTOR_PROVIDER_SYMBOL],
393 callInterceptors: (_a = callProperties.callOptions.interceptors) !== null && _a !== void 0 ? _a : [],
394 callInterceptorProviders: (_b = callProperties.callOptions.interceptor_providers) !== null && _b !== void 0 ? _b : [],
395 };
396 const call = client_interceptors_1.getInterceptingCall(interceptorArgs, callProperties.methodDefinition, callProperties.callOptions, callProperties.channel);
397 /* This needs to happen before the emitter is used. Unfortunately we can't
398 * enforce this with the type system. We need to construct this emitter
399 * before calling the CallInvocationTransformer, and we need to create the
400 * call after that. */
401 stream.call = call;
402 if (callProperties.callOptions.credentials) {
403 call.setCredentials(callProperties.callOptions.credentials);
404 }
405 let receivedStatus = false;
406 const callerStackError = new Error();
407 call.start(callProperties.metadata, {
408 onReceiveMetadata(metadata) {
409 stream.emit('metadata', metadata);
410 },
411 onReceiveMessage(message) {
412 stream.push(message);
413 },
414 onReceiveStatus(status) {
415 if (receivedStatus) {
416 return;
417 }
418 receivedStatus = true;
419 stream.push(null);
420 if (status.code !== constants_1.Status.OK) {
421 const callerStack = getErrorStackString(callerStackError);
422 stream.emit('error', call_1.callErrorFromStatus(status, callerStack));
423 }
424 stream.emit('status', status);
425 },
426 });
427 return stream;
428 }
429}
430exports.Client = Client;
431//# sourceMappingURL=client.js.map
\No newline at end of file