UNPKG

17.9 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.getInterceptingCall = exports.InterceptingCall = exports.RequesterBuilder = exports.ListenerBuilder = exports.InterceptorConfigurationError = void 0;
20const metadata_1 = require("./metadata");
21const call_stream_1 = require("./call-stream");
22const constants_1 = require("./constants");
23/**
24 * Error class associated with passing both interceptors and interceptor
25 * providers to a client constructor or as call options.
26 */
27class InterceptorConfigurationError extends Error {
28 constructor(message) {
29 super(message);
30 this.name = 'InterceptorConfigurationError';
31 Error.captureStackTrace(this, InterceptorConfigurationError);
32 }
33}
34exports.InterceptorConfigurationError = InterceptorConfigurationError;
35class ListenerBuilder {
36 constructor() {
37 this.metadata = undefined;
38 this.message = undefined;
39 this.status = undefined;
40 }
41 withOnReceiveMetadata(onReceiveMetadata) {
42 this.metadata = onReceiveMetadata;
43 return this;
44 }
45 withOnReceiveMessage(onReceiveMessage) {
46 this.message = onReceiveMessage;
47 return this;
48 }
49 withOnReceiveStatus(onReceiveStatus) {
50 this.status = onReceiveStatus;
51 return this;
52 }
53 build() {
54 return {
55 onReceiveMetadata: this.metadata,
56 onReceiveMessage: this.message,
57 onReceiveStatus: this.status,
58 };
59 }
60}
61exports.ListenerBuilder = ListenerBuilder;
62class RequesterBuilder {
63 constructor() {
64 this.start = undefined;
65 this.message = undefined;
66 this.halfClose = undefined;
67 this.cancel = undefined;
68 }
69 withStart(start) {
70 this.start = start;
71 return this;
72 }
73 withSendMessage(sendMessage) {
74 this.message = sendMessage;
75 return this;
76 }
77 withHalfClose(halfClose) {
78 this.halfClose = halfClose;
79 return this;
80 }
81 withCancel(cancel) {
82 this.cancel = cancel;
83 return this;
84 }
85 build() {
86 return {
87 start: this.start,
88 sendMessage: this.message,
89 halfClose: this.halfClose,
90 cancel: this.cancel,
91 };
92 }
93}
94exports.RequesterBuilder = RequesterBuilder;
95/**
96 * A Listener with a default pass-through implementation of each method. Used
97 * for filling out Listeners with some methods omitted.
98 */
99const defaultListener = {
100 onReceiveMetadata: (metadata, next) => {
101 next(metadata);
102 },
103 onReceiveMessage: (message, next) => {
104 next(message);
105 },
106 onReceiveStatus: (status, next) => {
107 next(status);
108 },
109};
110/**
111 * A Requester with a default pass-through implementation of each method. Used
112 * for filling out Requesters with some methods omitted.
113 */
114const defaultRequester = {
115 start: (metadata, listener, next) => {
116 next(metadata, listener);
117 },
118 sendMessage: (message, next) => {
119 next(message);
120 },
121 halfClose: (next) => {
122 next();
123 },
124 cancel: (next) => {
125 next();
126 },
127};
128class InterceptingCall {
129 constructor(nextCall, requester) {
130 var _a, _b, _c, _d;
131 this.nextCall = nextCall;
132 /**
133 * Indicates that metadata has been passed to the requester's start
134 * method but it has not been passed to the corresponding next callback
135 */
136 this.processingMetadata = false;
137 /**
138 * Message context for a pending message that is waiting for
139 */
140 this.pendingMessageContext = null;
141 /**
142 * Indicates that a message has been passed to the requester's sendMessage
143 * method but it has not been passed to the corresponding next callback
144 */
145 this.processingMessage = false;
146 /**
147 * Indicates that a status was received but could not be propagated because
148 * a message was still being processed.
149 */
150 this.pendingHalfClose = false;
151 if (requester) {
152 this.requester = {
153 start: (_a = requester.start) !== null && _a !== void 0 ? _a : defaultRequester.start,
154 sendMessage: (_b = requester.sendMessage) !== null && _b !== void 0 ? _b : defaultRequester.sendMessage,
155 halfClose: (_c = requester.halfClose) !== null && _c !== void 0 ? _c : defaultRequester.halfClose,
156 cancel: (_d = requester.cancel) !== null && _d !== void 0 ? _d : defaultRequester.cancel,
157 };
158 }
159 else {
160 this.requester = defaultRequester;
161 }
162 }
163 cancelWithStatus(status, details) {
164 this.requester.cancel(() => {
165 this.nextCall.cancelWithStatus(status, details);
166 });
167 }
168 getPeer() {
169 return this.nextCall.getPeer();
170 }
171 processPendingMessage() {
172 if (this.pendingMessageContext) {
173 this.nextCall.sendMessageWithContext(this.pendingMessageContext, this.pendingMessage);
174 this.pendingMessageContext = null;
175 this.pendingMessage = null;
176 }
177 }
178 processPendingHalfClose() {
179 if (this.pendingHalfClose) {
180 this.nextCall.halfClose();
181 }
182 }
183 start(metadata, interceptingListener) {
184 var _a, _b, _c, _d, _e, _f;
185 const fullInterceptingListener = {
186 onReceiveMetadata: (_b = (_a = interceptingListener === null || interceptingListener === void 0 ? void 0 : interceptingListener.onReceiveMetadata) === null || _a === void 0 ? void 0 : _a.bind(interceptingListener)) !== null && _b !== void 0 ? _b : ((metadata) => { }),
187 onReceiveMessage: (_d = (_c = interceptingListener === null || interceptingListener === void 0 ? void 0 : interceptingListener.onReceiveMessage) === null || _c === void 0 ? void 0 : _c.bind(interceptingListener)) !== null && _d !== void 0 ? _d : ((message) => { }),
188 onReceiveStatus: (_f = (_e = interceptingListener === null || interceptingListener === void 0 ? void 0 : interceptingListener.onReceiveStatus) === null || _e === void 0 ? void 0 : _e.bind(interceptingListener)) !== null && _f !== void 0 ? _f : ((status) => { }),
189 };
190 this.processingMetadata = true;
191 this.requester.start(metadata, fullInterceptingListener, (md, listener) => {
192 var _a, _b, _c;
193 this.processingMetadata = false;
194 let finalInterceptingListener;
195 if (call_stream_1.isInterceptingListener(listener)) {
196 finalInterceptingListener = listener;
197 }
198 else {
199 const fullListener = {
200 onReceiveMetadata: (_a = listener.onReceiveMetadata) !== null && _a !== void 0 ? _a : defaultListener.onReceiveMetadata,
201 onReceiveMessage: (_b = listener.onReceiveMessage) !== null && _b !== void 0 ? _b : defaultListener.onReceiveMessage,
202 onReceiveStatus: (_c = listener.onReceiveStatus) !== null && _c !== void 0 ? _c : defaultListener.onReceiveStatus,
203 };
204 finalInterceptingListener = new call_stream_1.InterceptingListenerImpl(fullListener, fullInterceptingListener);
205 }
206 this.nextCall.start(md, finalInterceptingListener);
207 this.processPendingMessage();
208 this.processPendingHalfClose();
209 });
210 }
211 // eslint-disable-next-line @typescript-eslint/no-explicit-any
212 sendMessageWithContext(context, message) {
213 this.processingMessage = true;
214 this.requester.sendMessage(message, (finalMessage) => {
215 this.processingMessage = false;
216 if (this.processingMetadata) {
217 this.pendingMessageContext = context;
218 this.pendingMessage = message;
219 }
220 else {
221 this.nextCall.sendMessageWithContext(context, finalMessage);
222 this.processPendingHalfClose();
223 }
224 });
225 }
226 // eslint-disable-next-line @typescript-eslint/no-explicit-any
227 sendMessage(message) {
228 this.sendMessageWithContext({}, message);
229 }
230 startRead() {
231 this.nextCall.startRead();
232 }
233 halfClose() {
234 this.requester.halfClose(() => {
235 if (this.processingMetadata || this.processingMessage) {
236 this.pendingHalfClose = true;
237 }
238 else {
239 this.nextCall.halfClose();
240 }
241 });
242 }
243 setCredentials(credentials) {
244 this.nextCall.setCredentials(credentials);
245 }
246}
247exports.InterceptingCall = InterceptingCall;
248function getCall(channel, path, options) {
249 var _a, _b;
250 const deadline = (_a = options.deadline) !== null && _a !== void 0 ? _a : Infinity;
251 const host = options.host;
252 const parent = (_b = options.parent) !== null && _b !== void 0 ? _b : null;
253 const propagateFlags = options.propagate_flags;
254 const credentials = options.credentials;
255 const call = channel.createCall(path, deadline, host, parent, propagateFlags);
256 if (credentials) {
257 call.setCredentials(credentials);
258 }
259 return call;
260}
261/**
262 * InterceptingCall implementation that directly owns the underlying Call
263 * object and handles serialization and deseraizliation.
264 */
265class BaseInterceptingCall {
266 constructor(call,
267 // eslint-disable-next-line @typescript-eslint/no-explicit-any
268 methodDefinition) {
269 this.call = call;
270 this.methodDefinition = methodDefinition;
271 }
272 cancelWithStatus(status, details) {
273 this.call.cancelWithStatus(status, details);
274 }
275 getPeer() {
276 return this.call.getPeer();
277 }
278 setCredentials(credentials) {
279 this.call.setCredentials(credentials);
280 }
281 // eslint-disable-next-line @typescript-eslint/no-explicit-any
282 sendMessageWithContext(context, message) {
283 let serialized;
284 try {
285 serialized = this.methodDefinition.requestSerialize(message);
286 }
287 catch (e) {
288 this.call.cancelWithStatus(constants_1.Status.INTERNAL, `Request message serialization failure: ${e.message}`);
289 return;
290 }
291 this.call.sendMessageWithContext(context, serialized);
292 }
293 // eslint-disable-next-line @typescript-eslint/no-explicit-any
294 sendMessage(message) {
295 this.sendMessageWithContext({}, message);
296 }
297 start(metadata, interceptingListener) {
298 let readError = null;
299 this.call.start(metadata, {
300 onReceiveMetadata: (metadata) => {
301 var _a;
302 (_a = interceptingListener === null || interceptingListener === void 0 ? void 0 : interceptingListener.onReceiveMetadata) === null || _a === void 0 ? void 0 : _a.call(interceptingListener, metadata);
303 },
304 onReceiveMessage: (message) => {
305 var _a;
306 // eslint-disable-next-line @typescript-eslint/no-explicit-any
307 let deserialized;
308 try {
309 deserialized = this.methodDefinition.responseDeserialize(message);
310 }
311 catch (e) {
312 readError = {
313 code: constants_1.Status.INTERNAL,
314 details: `Response message parsing error: ${e.message}`,
315 metadata: new metadata_1.Metadata(),
316 };
317 this.call.cancelWithStatus(readError.code, readError.details);
318 return;
319 }
320 (_a = interceptingListener === null || interceptingListener === void 0 ? void 0 : interceptingListener.onReceiveMessage) === null || _a === void 0 ? void 0 : _a.call(interceptingListener, deserialized);
321 },
322 onReceiveStatus: (status) => {
323 var _a, _b;
324 if (readError) {
325 (_a = interceptingListener === null || interceptingListener === void 0 ? void 0 : interceptingListener.onReceiveStatus) === null || _a === void 0 ? void 0 : _a.call(interceptingListener, readError);
326 }
327 else {
328 (_b = interceptingListener === null || interceptingListener === void 0 ? void 0 : interceptingListener.onReceiveStatus) === null || _b === void 0 ? void 0 : _b.call(interceptingListener, status);
329 }
330 },
331 });
332 }
333 startRead() {
334 this.call.startRead();
335 }
336 halfClose() {
337 this.call.halfClose();
338 }
339}
340/**
341 * BaseInterceptingCall with special-cased behavior for methods with unary
342 * responses.
343 */
344class BaseUnaryInterceptingCall extends BaseInterceptingCall {
345 // eslint-disable-next-line @typescript-eslint/no-explicit-any
346 constructor(call, methodDefinition) {
347 super(call, methodDefinition);
348 }
349 start(metadata, listener) {
350 var _a, _b;
351 let receivedMessage = false;
352 const wrapperListener = {
353 onReceiveMetadata: (_b = (_a = listener === null || listener === void 0 ? void 0 : listener.onReceiveMetadata) === null || _a === void 0 ? void 0 : _a.bind(listener)) !== null && _b !== void 0 ? _b : ((metadata) => { }),
354 // eslint-disable-next-line @typescript-eslint/no-explicit-any
355 onReceiveMessage: (message) => {
356 var _a;
357 receivedMessage = true;
358 (_a = listener === null || listener === void 0 ? void 0 : listener.onReceiveMessage) === null || _a === void 0 ? void 0 : _a.call(listener, message);
359 },
360 onReceiveStatus: (status) => {
361 var _a, _b;
362 if (!receivedMessage) {
363 (_a = listener === null || listener === void 0 ? void 0 : listener.onReceiveMessage) === null || _a === void 0 ? void 0 : _a.call(listener, null);
364 }
365 (_b = listener === null || listener === void 0 ? void 0 : listener.onReceiveStatus) === null || _b === void 0 ? void 0 : _b.call(listener, status);
366 },
367 };
368 super.start(metadata, wrapperListener);
369 this.call.startRead();
370 }
371}
372/**
373 * BaseInterceptingCall with special-cased behavior for methods with streaming
374 * responses.
375 */
376class BaseStreamingInterceptingCall extends BaseInterceptingCall {
377}
378function getBottomInterceptingCall(channel, options,
379// eslint-disable-next-line @typescript-eslint/no-explicit-any
380methodDefinition) {
381 const call = getCall(channel, methodDefinition.path, options);
382 if (methodDefinition.responseStream) {
383 return new BaseStreamingInterceptingCall(call, methodDefinition);
384 }
385 else {
386 return new BaseUnaryInterceptingCall(call, methodDefinition);
387 }
388}
389function getInterceptingCall(interceptorArgs,
390// eslint-disable-next-line @typescript-eslint/no-explicit-any
391methodDefinition, options, channel) {
392 if (interceptorArgs.clientInterceptors.length > 0 &&
393 interceptorArgs.clientInterceptorProviders.length > 0) {
394 throw new InterceptorConfigurationError('Both interceptors and interceptor_providers were passed as options ' +
395 'to the client constructor. Only one of these is allowed.');
396 }
397 if (interceptorArgs.callInterceptors.length > 0 &&
398 interceptorArgs.callInterceptorProviders.length > 0) {
399 throw new InterceptorConfigurationError('Both interceptors and interceptor_providers were passed as call ' +
400 'options. Only one of these is allowed.');
401 }
402 let interceptors = [];
403 // Interceptors passed to the call override interceptors passed to the client constructor
404 if (interceptorArgs.callInterceptors.length > 0 ||
405 interceptorArgs.callInterceptorProviders.length > 0) {
406 interceptors = []
407 .concat(interceptorArgs.callInterceptors, interceptorArgs.callInterceptorProviders.map((provider) => provider(methodDefinition)))
408 .filter((interceptor) => interceptor);
409 // Filter out falsy values when providers return nothing
410 }
411 else {
412 interceptors = []
413 .concat(interceptorArgs.clientInterceptors, interceptorArgs.clientInterceptorProviders.map((provider) => provider(methodDefinition)))
414 .filter((interceptor) => interceptor);
415 // Filter out falsy values when providers return nothing
416 }
417 const interceptorOptions = Object.assign({}, options, {
418 method_definition: methodDefinition,
419 });
420 /* For each interceptor in the list, the nextCall function passed to it is
421 * based on the next interceptor in the list, using a nextCall function
422 * constructed with the following interceptor in the list, and so on. The
423 * initialValue, which is effectively at the end of the list, is a nextCall
424 * function that invokes getBottomInterceptingCall, the result of which
425 * handles (de)serialization and also gets the underlying call from the
426 * channel. */
427 const getCall = interceptors.reduceRight((nextCall, nextInterceptor) => {
428 return (currentOptions) => nextInterceptor(currentOptions, nextCall);
429 }, (finalOptions) => getBottomInterceptingCall(channel, finalOptions, methodDefinition));
430 return getCall(interceptorOptions);
431}
432exports.getInterceptingCall = getInterceptingCall;
433//# sourceMappingURL=client-interceptors.js.map
\No newline at end of file