1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | Object.defineProperty(exports, "__esModule", { value: true });
|
19 | exports.ChannelImplementation = void 0;
|
20 | const call_stream_1 = require("./call-stream");
|
21 | const channel_credentials_1 = require("./channel-credentials");
|
22 | const resolving_load_balancer_1 = require("./resolving-load-balancer");
|
23 | const subchannel_pool_1 = require("./subchannel-pool");
|
24 | const picker_1 = require("./picker");
|
25 | const constants_1 = require("./constants");
|
26 | const filter_stack_1 = require("./filter-stack");
|
27 | const call_credentials_filter_1 = require("./call-credentials-filter");
|
28 | const deadline_filter_1 = require("./deadline-filter");
|
29 | const compression_filter_1 = require("./compression-filter");
|
30 | const resolver_1 = require("./resolver");
|
31 | const logging_1 = require("./logging");
|
32 | const max_message_size_filter_1 = require("./max-message-size-filter");
|
33 | const http_proxy_1 = require("./http_proxy");
|
34 | const uri_parser_1 = require("./uri-parser");
|
35 | const connectivity_state_1 = require("./connectivity-state");
|
36 | const channelz_1 = require("./channelz");
|
37 |
|
38 |
|
39 |
|
40 | const MAX_TIMEOUT_TIME = 2147483647;
|
41 | let nextCallNumber = 0;
|
42 | function getNewCallNumber() {
|
43 | const callNumber = nextCallNumber;
|
44 | nextCallNumber += 1;
|
45 | if (nextCallNumber >= Number.MAX_SAFE_INTEGER) {
|
46 | nextCallNumber = 0;
|
47 | }
|
48 | return callNumber;
|
49 | }
|
50 | const INAPPROPRIATE_CONTROL_PLANE_CODES = [
|
51 | constants_1.Status.OK,
|
52 | constants_1.Status.INVALID_ARGUMENT,
|
53 | constants_1.Status.NOT_FOUND,
|
54 | constants_1.Status.ALREADY_EXISTS,
|
55 | constants_1.Status.FAILED_PRECONDITION,
|
56 | constants_1.Status.ABORTED,
|
57 | constants_1.Status.OUT_OF_RANGE,
|
58 | constants_1.Status.DATA_LOSS
|
59 | ];
|
60 | function restrictControlPlaneStatusCode(code, details) {
|
61 | if (INAPPROPRIATE_CONTROL_PLANE_CODES.includes(code)) {
|
62 | return {
|
63 | code: constants_1.Status.INTERNAL,
|
64 | details: `Invalid status from control plane: ${code} ${constants_1.Status[code]} ${details}`
|
65 | };
|
66 | }
|
67 | else {
|
68 | return { code, details };
|
69 | }
|
70 | }
|
71 | class ChannelImplementation {
|
72 | constructor(target, credentials, options) {
|
73 | var _a, _b, _c, _d;
|
74 | this.credentials = credentials;
|
75 | this.options = options;
|
76 | this.connectivityState = connectivity_state_1.ConnectivityState.IDLE;
|
77 | this.currentPicker = new picker_1.UnavailablePicker();
|
78 | |
79 |
|
80 |
|
81 |
|
82 | this.configSelectionQueue = [];
|
83 | this.pickQueue = [];
|
84 | this.connectivityStateWatchers = [];
|
85 | this.configSelector = null;
|
86 | |
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 | this.currentResolutionError = null;
|
94 |
|
95 | this.channelzEnabled = true;
|
96 | this.callTracker = new channelz_1.ChannelzCallTracker();
|
97 | this.childrenTracker = new channelz_1.ChannelzChildrenTracker();
|
98 | if (typeof target !== 'string') {
|
99 | throw new TypeError('Channel target must be a string');
|
100 | }
|
101 | if (!(credentials instanceof channel_credentials_1.ChannelCredentials)) {
|
102 | throw new TypeError('Channel credentials must be a ChannelCredentials object');
|
103 | }
|
104 | if (options) {
|
105 | if (typeof options !== 'object') {
|
106 | throw new TypeError('Channel options must be an object');
|
107 | }
|
108 | }
|
109 | this.originalTarget = target;
|
110 | const originalTargetUri = uri_parser_1.parseUri(target);
|
111 | if (originalTargetUri === null) {
|
112 | throw new Error(`Could not parse target name "${target}"`);
|
113 | }
|
114 | |
115 |
|
116 | const defaultSchemeMapResult = resolver_1.mapUriDefaultScheme(originalTargetUri);
|
117 | if (defaultSchemeMapResult === null) {
|
118 | throw new Error(`Could not find a default scheme for target name "${target}"`);
|
119 | }
|
120 | this.callRefTimer = setInterval(() => { }, MAX_TIMEOUT_TIME);
|
121 | (_b = (_a = this.callRefTimer).unref) === null || _b === void 0 ? void 0 : _b.call(_a);
|
122 | if (this.options['grpc.enable_channelz'] === 0) {
|
123 | this.channelzEnabled = false;
|
124 | }
|
125 | this.channelzTrace = new channelz_1.ChannelzTrace();
|
126 | this.channelzRef = channelz_1.registerChannelzChannel(target, () => this.getChannelzInfo(), this.channelzEnabled);
|
127 | if (this.channelzEnabled) {
|
128 | this.channelzTrace.addTrace('CT_INFO', 'Channel created');
|
129 | }
|
130 | if (this.options['grpc.default_authority']) {
|
131 | this.defaultAuthority = this.options['grpc.default_authority'];
|
132 | }
|
133 | else {
|
134 | this.defaultAuthority = resolver_1.getDefaultAuthority(defaultSchemeMapResult);
|
135 | }
|
136 | const proxyMapResult = http_proxy_1.mapProxyName(defaultSchemeMapResult, options);
|
137 | this.target = proxyMapResult.target;
|
138 | this.options = Object.assign({}, this.options, proxyMapResult.extraOptions);
|
139 | |
140 |
|
141 | this.subchannelPool = subchannel_pool_1.getSubchannelPool(((_c = options['grpc.use_local_subchannel_pool']) !== null && _c !== void 0 ? _c : 0) === 0);
|
142 | const channelControlHelper = {
|
143 | createSubchannel: (subchannelAddress, subchannelArgs) => {
|
144 | const subchannel = this.subchannelPool.getOrCreateSubchannel(this.target, subchannelAddress, Object.assign({}, this.options, subchannelArgs), this.credentials);
|
145 | if (this.channelzEnabled) {
|
146 | this.channelzTrace.addTrace('CT_INFO', 'Created subchannel or used existing subchannel', subchannel.getChannelzRef());
|
147 | }
|
148 | return subchannel;
|
149 | },
|
150 | updateState: (connectivityState, picker) => {
|
151 | this.currentPicker = picker;
|
152 | const queueCopy = this.pickQueue.slice();
|
153 | this.pickQueue = [];
|
154 | this.callRefTimerUnref();
|
155 | for (const { callStream, callMetadata, callConfig, dynamicFilters } of queueCopy) {
|
156 | this.tryPick(callStream, callMetadata, callConfig, dynamicFilters);
|
157 | }
|
158 | this.updateState(connectivityState);
|
159 | },
|
160 | requestReresolution: () => {
|
161 |
|
162 | throw new Error('Resolving load balancer should never call requestReresolution');
|
163 | },
|
164 | addChannelzChild: (child) => {
|
165 | if (this.channelzEnabled) {
|
166 | this.childrenTracker.refChild(child);
|
167 | }
|
168 | },
|
169 | removeChannelzChild: (child) => {
|
170 | if (this.channelzEnabled) {
|
171 | this.childrenTracker.unrefChild(child);
|
172 | }
|
173 | }
|
174 | };
|
175 | this.resolvingLoadBalancer = new resolving_load_balancer_1.ResolvingLoadBalancer(this.target, channelControlHelper, options, (configSelector) => {
|
176 | if (this.channelzEnabled) {
|
177 | this.channelzTrace.addTrace('CT_INFO', 'Address resolution succeeded');
|
178 | }
|
179 | this.configSelector = configSelector;
|
180 | this.currentResolutionError = null;
|
181 | |
182 |
|
183 | process.nextTick(() => {
|
184 | const localQueue = this.configSelectionQueue;
|
185 | this.configSelectionQueue = [];
|
186 | this.callRefTimerUnref();
|
187 | for (const { callStream, callMetadata } of localQueue) {
|
188 | this.tryGetConfig(callStream, callMetadata);
|
189 | }
|
190 | this.configSelectionQueue = [];
|
191 | });
|
192 | }, (status) => {
|
193 | if (this.channelzEnabled) {
|
194 | this.channelzTrace.addTrace('CT_WARNING', 'Address resolution failed with code ' + status.code + ' and details "' + status.details + '"');
|
195 | }
|
196 | if (this.configSelectionQueue.length > 0) {
|
197 | this.trace('Name resolution failed with calls queued for config selection');
|
198 | }
|
199 | if (this.configSelector === null) {
|
200 | this.currentResolutionError = Object.assign(Object.assign({}, restrictControlPlaneStatusCode(status.code, status.details)), { metadata: status.metadata });
|
201 | }
|
202 | const localQueue = this.configSelectionQueue;
|
203 | this.configSelectionQueue = [];
|
204 | this.callRefTimerUnref();
|
205 | for (const { callStream, callMetadata } of localQueue) {
|
206 | if (callMetadata.getOptions().waitForReady) {
|
207 | this.callRefTimerRef();
|
208 | this.configSelectionQueue.push({ callStream, callMetadata });
|
209 | }
|
210 | else {
|
211 | callStream.cancelWithStatus(status.code, status.details);
|
212 | }
|
213 | }
|
214 | });
|
215 | this.filterStackFactory = new filter_stack_1.FilterStackFactory([
|
216 | new call_credentials_filter_1.CallCredentialsFilterFactory(this),
|
217 | new deadline_filter_1.DeadlineFilterFactory(this),
|
218 | new max_message_size_filter_1.MaxMessageSizeFilterFactory(this.options),
|
219 | new compression_filter_1.CompressionFilterFactory(this, this.options),
|
220 | ]);
|
221 | this.trace('Channel constructed with options ' + JSON.stringify(options, undefined, 2));
|
222 | const error = new Error();
|
223 | logging_1.trace(constants_1.LogVerbosity.DEBUG, 'channel_stacktrace', '(' + this.channelzRef.id + ') ' + 'Channel constructed \n' + ((_d = error.stack) === null || _d === void 0 ? void 0 : _d.substring(error.stack.indexOf('\n') + 1)));
|
224 | }
|
225 | getChannelzInfo() {
|
226 | return {
|
227 | target: this.originalTarget,
|
228 | state: this.connectivityState,
|
229 | trace: this.channelzTrace,
|
230 | callTracker: this.callTracker,
|
231 | children: this.childrenTracker.getChildLists()
|
232 | };
|
233 | }
|
234 | trace(text, verbosityOverride) {
|
235 | logging_1.trace(verbosityOverride !== null && verbosityOverride !== void 0 ? verbosityOverride : constants_1.LogVerbosity.DEBUG, 'channel', '(' + this.channelzRef.id + ') ' + uri_parser_1.uriToString(this.target) + ' ' + text);
|
236 | }
|
237 | callRefTimerRef() {
|
238 | var _a, _b, _c, _d;
|
239 |
|
240 | if (!((_b = (_a = this.callRefTimer).hasRef) === null || _b === void 0 ? void 0 : _b.call(_a))) {
|
241 | this.trace('callRefTimer.ref | configSelectionQueue.length=' +
|
242 | this.configSelectionQueue.length +
|
243 | ' pickQueue.length=' +
|
244 | this.pickQueue.length);
|
245 | (_d = (_c = this.callRefTimer).ref) === null || _d === void 0 ? void 0 : _d.call(_c);
|
246 | }
|
247 | }
|
248 | callRefTimerUnref() {
|
249 | var _a, _b;
|
250 |
|
251 | if (!this.callRefTimer.hasRef || this.callRefTimer.hasRef()) {
|
252 | this.trace('callRefTimer.unref | configSelectionQueue.length=' +
|
253 | this.configSelectionQueue.length +
|
254 | ' pickQueue.length=' +
|
255 | this.pickQueue.length);
|
256 | (_b = (_a = this.callRefTimer).unref) === null || _b === void 0 ? void 0 : _b.call(_a);
|
257 | }
|
258 | }
|
259 | pushPick(callStream, callMetadata, callConfig, dynamicFilters) {
|
260 | this.pickQueue.push({ callStream, callMetadata, callConfig, dynamicFilters });
|
261 | this.callRefTimerRef();
|
262 | }
|
263 | |
264 |
|
265 |
|
266 |
|
267 |
|
268 |
|
269 |
|
270 | tryPick(callStream, callMetadata, callConfig, dynamicFilters) {
|
271 | var _a, _b;
|
272 | const pickResult = this.currentPicker.pick({
|
273 | metadata: callMetadata,
|
274 | extraPickInfo: callConfig.pickInformation,
|
275 | });
|
276 | const subchannelString = pickResult.subchannel ?
|
277 | '(' + pickResult.subchannel.getChannelzRef().id + ') ' + pickResult.subchannel.getAddress() :
|
278 | '' + pickResult.subchannel;
|
279 | this.trace('Pick result for call [' +
|
280 | callStream.getCallNumber() +
|
281 | ']: ' +
|
282 | picker_1.PickResultType[pickResult.pickResultType] +
|
283 | ' subchannel: ' +
|
284 | subchannelString +
|
285 | ' status: ' + ((_a = pickResult.status) === null || _a === void 0 ? void 0 : _a.code) +
|
286 | ' ' + ((_b = pickResult.status) === null || _b === void 0 ? void 0 : _b.details));
|
287 | switch (pickResult.pickResultType) {
|
288 | case picker_1.PickResultType.COMPLETE:
|
289 | if (pickResult.subchannel === null) {
|
290 | callStream.cancelWithStatus(constants_1.Status.UNAVAILABLE, 'Request dropped by load balancing policy');
|
291 |
|
292 | }
|
293 | else {
|
294 | |
295 |
|
296 |
|
297 | if (pickResult.subchannel.getConnectivityState() !==
|
298 | connectivity_state_1.ConnectivityState.READY) {
|
299 | logging_1.log(constants_1.LogVerbosity.ERROR, 'Error: COMPLETE pick result subchannel ' +
|
300 | subchannelString +
|
301 | ' has state ' +
|
302 | connectivity_state_1.ConnectivityState[pickResult.subchannel.getConnectivityState()]);
|
303 | this.pushPick(callStream, callMetadata, callConfig, dynamicFilters);
|
304 | break;
|
305 | }
|
306 | |
307 |
|
308 |
|
309 | callStream.filterStack
|
310 | .sendMetadata(Promise.resolve(callMetadata.clone()))
|
311 | .then((finalMetadata) => {
|
312 | var _a, _b, _c;
|
313 | const subchannelState = pickResult.subchannel.getConnectivityState();
|
314 | if (subchannelState === connectivity_state_1.ConnectivityState.READY) {
|
315 | try {
|
316 | const pickExtraFilters = pickResult.extraFilterFactories.map(factory => factory.createFilter(callStream));
|
317 | (_a = pickResult.subchannel) === null || _a === void 0 ? void 0 : _a.getRealSubchannel().startCallStream(finalMetadata, callStream, [...dynamicFilters, ...pickExtraFilters]);
|
318 | |
319 |
|
320 | (_b = callConfig.onCommitted) === null || _b === void 0 ? void 0 : _b.call(callConfig);
|
321 | (_c = pickResult.onCallStarted) === null || _c === void 0 ? void 0 : _c.call(pickResult);
|
322 | }
|
323 | catch (error) {
|
324 | const errorCode = error.code;
|
325 | if (errorCode === 'ERR_HTTP2_GOAWAY_SESSION' ||
|
326 | errorCode === 'ERR_HTTP2_INVALID_SESSION') {
|
327 | |
328 |
|
329 |
|
330 |
|
331 |
|
332 |
|
333 |
|
334 |
|
335 |
|
336 |
|
337 |
|
338 |
|
339 |
|
340 | this.trace('Failed to start call on picked subchannel ' +
|
341 | subchannelString +
|
342 | ' with error ' +
|
343 | error.message +
|
344 | '. Retrying pick', constants_1.LogVerbosity.INFO);
|
345 | this.tryPick(callStream, callMetadata, callConfig, dynamicFilters);
|
346 | }
|
347 | else {
|
348 | this.trace('Failed to start call on picked subchanel ' +
|
349 | subchannelString +
|
350 | ' with error ' +
|
351 | error.message +
|
352 | '. Ending call', constants_1.LogVerbosity.INFO);
|
353 | callStream.cancelWithStatus(constants_1.Status.INTERNAL, `Failed to start HTTP/2 stream with error: ${error.message}`);
|
354 | }
|
355 | }
|
356 | }
|
357 | else {
|
358 | |
359 |
|
360 | this.trace('Picked subchannel ' +
|
361 | subchannelString +
|
362 | ' has state ' +
|
363 | connectivity_state_1.ConnectivityState[subchannelState] +
|
364 | ' after metadata filters. Retrying pick', constants_1.LogVerbosity.INFO);
|
365 | this.tryPick(callStream, callMetadata, callConfig, dynamicFilters);
|
366 | }
|
367 | }, (error) => {
|
368 |
|
369 | const { code, details } = restrictControlPlaneStatusCode(typeof error.code === 'number' ? error.code : constants_1.Status.UNKNOWN, `Getting metadata from plugin failed with error: ${error.message}`);
|
370 | callStream.cancelWithStatus(code, details);
|
371 | });
|
372 | }
|
373 | break;
|
374 | case picker_1.PickResultType.QUEUE:
|
375 | this.pushPick(callStream, callMetadata, callConfig, dynamicFilters);
|
376 | break;
|
377 | case picker_1.PickResultType.TRANSIENT_FAILURE:
|
378 | if (callMetadata.getOptions().waitForReady) {
|
379 | this.pushPick(callStream, callMetadata, callConfig, dynamicFilters);
|
380 | }
|
381 | else {
|
382 | const { code, details } = restrictControlPlaneStatusCode(pickResult.status.code, pickResult.status.details);
|
383 | callStream.cancelWithStatus(code, details);
|
384 | }
|
385 | break;
|
386 | case picker_1.PickResultType.DROP:
|
387 | const { code, details } = restrictControlPlaneStatusCode(pickResult.status.code, pickResult.status.details);
|
388 | callStream.cancelWithStatus(code, details);
|
389 | break;
|
390 | default:
|
391 | throw new Error(`Invalid state: unknown pickResultType ${pickResult.pickResultType}`);
|
392 | }
|
393 | }
|
394 | removeConnectivityStateWatcher(watcherObject) {
|
395 | const watcherIndex = this.connectivityStateWatchers.findIndex((value) => value === watcherObject);
|
396 | if (watcherIndex >= 0) {
|
397 | this.connectivityStateWatchers.splice(watcherIndex, 1);
|
398 | }
|
399 | }
|
400 | updateState(newState) {
|
401 | logging_1.trace(constants_1.LogVerbosity.DEBUG, 'connectivity_state', '(' + this.channelzRef.id + ') ' +
|
402 | uri_parser_1.uriToString(this.target) +
|
403 | ' ' +
|
404 | connectivity_state_1.ConnectivityState[this.connectivityState] +
|
405 | ' -> ' +
|
406 | connectivity_state_1.ConnectivityState[newState]);
|
407 | if (this.channelzEnabled) {
|
408 | this.channelzTrace.addTrace('CT_INFO', connectivity_state_1.ConnectivityState[this.connectivityState] + ' -> ' + connectivity_state_1.ConnectivityState[newState]);
|
409 | }
|
410 | this.connectivityState = newState;
|
411 | const watchersCopy = this.connectivityStateWatchers.slice();
|
412 | for (const watcherObject of watchersCopy) {
|
413 | if (newState !== watcherObject.currentState) {
|
414 | if (watcherObject.timer) {
|
415 | clearTimeout(watcherObject.timer);
|
416 | }
|
417 | this.removeConnectivityStateWatcher(watcherObject);
|
418 | watcherObject.callback();
|
419 | }
|
420 | }
|
421 | if (newState !== connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE) {
|
422 | this.currentResolutionError = null;
|
423 | }
|
424 | }
|
425 | tryGetConfig(stream, metadata) {
|
426 | if (stream.getStatus() !== null) {
|
427 | |
428 |
|
429 | return;
|
430 | }
|
431 | if (this.configSelector === null) {
|
432 | |
433 |
|
434 |
|
435 |
|
436 | this.resolvingLoadBalancer.exitIdle();
|
437 | if (this.currentResolutionError && !metadata.getOptions().waitForReady) {
|
438 | stream.cancelWithStatus(this.currentResolutionError.code, this.currentResolutionError.details);
|
439 | }
|
440 | else {
|
441 | this.configSelectionQueue.push({
|
442 | callStream: stream,
|
443 | callMetadata: metadata,
|
444 | });
|
445 | this.callRefTimerRef();
|
446 | }
|
447 | }
|
448 | else {
|
449 | const callConfig = this.configSelector(stream.getMethod(), metadata);
|
450 | if (callConfig.status === constants_1.Status.OK) {
|
451 | if (callConfig.methodConfig.timeout) {
|
452 | const deadline = new Date();
|
453 | deadline.setSeconds(deadline.getSeconds() + callConfig.methodConfig.timeout.seconds);
|
454 | deadline.setMilliseconds(deadline.getMilliseconds() +
|
455 | callConfig.methodConfig.timeout.nanos / 1000000);
|
456 | stream.setConfigDeadline(deadline);
|
457 |
|
458 | stream.filterStack.refresh();
|
459 | }
|
460 | if (callConfig.dynamicFilterFactories.length > 0) {
|
461 | |
462 |
|
463 |
|
464 |
|
465 |
|
466 |
|
467 |
|
468 |
|
469 |
|
470 |
|
471 |
|
472 |
|
473 | const dynamicFilterStackFactory = new filter_stack_1.FilterStackFactory(callConfig.dynamicFilterFactories);
|
474 | const dynamicFilterStack = dynamicFilterStackFactory.createFilter(stream);
|
475 | dynamicFilterStack.sendMetadata(Promise.resolve(metadata)).then(filteredMetadata => {
|
476 | this.tryPick(stream, filteredMetadata, callConfig, dynamicFilterStack.getFilters());
|
477 | });
|
478 | }
|
479 | else {
|
480 | this.tryPick(stream, metadata, callConfig, []);
|
481 | }
|
482 | }
|
483 | else {
|
484 | const { code, details } = restrictControlPlaneStatusCode(callConfig.status, 'Failed to route call to method ' + stream.getMethod());
|
485 | stream.cancelWithStatus(code, details);
|
486 | }
|
487 | }
|
488 | }
|
489 | _startCallStream(stream, metadata) {
|
490 | this.tryGetConfig(stream, metadata.clone());
|
491 | }
|
492 | close() {
|
493 | this.resolvingLoadBalancer.destroy();
|
494 | this.updateState(connectivity_state_1.ConnectivityState.SHUTDOWN);
|
495 | clearInterval(this.callRefTimer);
|
496 | if (this.channelzEnabled) {
|
497 | channelz_1.unregisterChannelzRef(this.channelzRef);
|
498 | }
|
499 | this.subchannelPool.unrefUnusedSubchannels();
|
500 | }
|
501 | getTarget() {
|
502 | return uri_parser_1.uriToString(this.target);
|
503 | }
|
504 | getConnectivityState(tryToConnect) {
|
505 | const connectivityState = this.connectivityState;
|
506 | if (tryToConnect) {
|
507 | this.resolvingLoadBalancer.exitIdle();
|
508 | }
|
509 | return connectivityState;
|
510 | }
|
511 | watchConnectivityState(currentState, deadline, callback) {
|
512 | if (this.connectivityState === connectivity_state_1.ConnectivityState.SHUTDOWN) {
|
513 | throw new Error('Channel has been shut down');
|
514 | }
|
515 | let timer = null;
|
516 | if (deadline !== Infinity) {
|
517 | const deadlineDate = deadline instanceof Date ? deadline : new Date(deadline);
|
518 | const now = new Date();
|
519 | if (deadline === -Infinity || deadlineDate <= now) {
|
520 | process.nextTick(callback, new Error('Deadline passed without connectivity state change'));
|
521 | return;
|
522 | }
|
523 | timer = setTimeout(() => {
|
524 | this.removeConnectivityStateWatcher(watcherObject);
|
525 | callback(new Error('Deadline passed without connectivity state change'));
|
526 | }, deadlineDate.getTime() - now.getTime());
|
527 | }
|
528 | const watcherObject = {
|
529 | currentState,
|
530 | callback,
|
531 | timer,
|
532 | };
|
533 | this.connectivityStateWatchers.push(watcherObject);
|
534 | }
|
535 | |
536 |
|
537 |
|
538 |
|
539 |
|
540 | getChannelzRef() {
|
541 | return this.channelzRef;
|
542 | }
|
543 | createCall(method, deadline, host, parentCall, propagateFlags) {
|
544 | if (typeof method !== 'string') {
|
545 | throw new TypeError('Channel#createCall: method must be a string');
|
546 | }
|
547 | if (!(typeof deadline === 'number' || deadline instanceof Date)) {
|
548 | throw new TypeError('Channel#createCall: deadline must be a number or Date');
|
549 | }
|
550 | if (this.connectivityState === connectivity_state_1.ConnectivityState.SHUTDOWN) {
|
551 | throw new Error('Channel has been shut down');
|
552 | }
|
553 | const callNumber = getNewCallNumber();
|
554 | this.trace('createCall [' +
|
555 | callNumber +
|
556 | '] method="' +
|
557 | method +
|
558 | '", deadline=' +
|
559 | deadline);
|
560 | const finalOptions = {
|
561 | deadline: deadline,
|
562 | flags: propagateFlags !== null && propagateFlags !== void 0 ? propagateFlags : constants_1.Propagate.DEFAULTS,
|
563 | host: host !== null && host !== void 0 ? host : this.defaultAuthority,
|
564 | parentCall: parentCall,
|
565 | };
|
566 | const stream = new call_stream_1.Http2CallStream(method, this, finalOptions, this.filterStackFactory, this.credentials._getCallCredentials(), callNumber);
|
567 | if (this.channelzEnabled) {
|
568 | this.callTracker.addCallStarted();
|
569 | stream.addStatusWatcher(status => {
|
570 | if (status.code === constants_1.Status.OK) {
|
571 | this.callTracker.addCallSucceeded();
|
572 | }
|
573 | else {
|
574 | this.callTracker.addCallFailed();
|
575 | }
|
576 | });
|
577 | }
|
578 | return stream;
|
579 | }
|
580 | }
|
581 | exports.ChannelImplementation = ChannelImplementation;
|
582 |
|
\ | No newline at end of file |