UNPKG

10.3 kBJavaScriptView Raw
1"use strict";
2/*
3 * Copyright 2022 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.ResolvingCall = void 0;
20const constants_1 = require("./constants");
21const deadline_1 = require("./deadline");
22const metadata_1 = require("./metadata");
23const logging = require("./logging");
24const control_plane_status_1 = require("./control-plane-status");
25const TRACER_NAME = 'resolving_call';
26class ResolvingCall {
27 constructor(channel, method, options, filterStackFactory, credentials, callNumber) {
28 this.channel = channel;
29 this.method = method;
30 this.filterStackFactory = filterStackFactory;
31 this.credentials = credentials;
32 this.callNumber = callNumber;
33 this.child = null;
34 this.readPending = false;
35 this.pendingMessage = null;
36 this.pendingHalfClose = false;
37 this.ended = false;
38 this.readFilterPending = false;
39 this.writeFilterPending = false;
40 this.pendingChildStatus = null;
41 this.metadata = null;
42 this.listener = null;
43 this.statusWatchers = [];
44 this.deadlineTimer = setTimeout(() => { }, 0);
45 this.filterStack = null;
46 this.deadline = options.deadline;
47 this.host = options.host;
48 if (options.parentCall) {
49 if (options.flags & constants_1.Propagate.CANCELLATION) {
50 options.parentCall.on('cancelled', () => {
51 this.cancelWithStatus(constants_1.Status.CANCELLED, 'Cancelled by parent call');
52 });
53 }
54 if (options.flags & constants_1.Propagate.DEADLINE) {
55 this.trace('Propagating deadline from parent: ' + options.parentCall.getDeadline());
56 this.deadline = deadline_1.minDeadline(this.deadline, options.parentCall.getDeadline());
57 }
58 }
59 this.trace('Created');
60 this.runDeadlineTimer();
61 }
62 trace(text) {
63 logging.trace(constants_1.LogVerbosity.DEBUG, TRACER_NAME, '[' + this.callNumber + '] ' + text);
64 }
65 runDeadlineTimer() {
66 clearTimeout(this.deadlineTimer);
67 this.trace('Deadline: ' + this.deadline);
68 if (this.deadline !== Infinity) {
69 const timeout = deadline_1.getRelativeTimeout(this.deadline);
70 this.trace('Deadline will be reached in ' + timeout + 'ms');
71 const handleDeadline = () => {
72 this.cancelWithStatus(constants_1.Status.DEADLINE_EXCEEDED, 'Deadline exceeded');
73 };
74 if (timeout <= 0) {
75 process.nextTick(handleDeadline);
76 }
77 else {
78 this.deadlineTimer = setTimeout(handleDeadline, timeout);
79 }
80 }
81 }
82 outputStatus(status) {
83 if (!this.ended) {
84 this.ended = true;
85 if (!this.filterStack) {
86 this.filterStack = this.filterStackFactory.createFilter();
87 }
88 const filteredStatus = this.filterStack.receiveTrailers(status);
89 this.trace('ended with status: code=' + filteredStatus.code + ' details="' + filteredStatus.details + '"');
90 this.statusWatchers.forEach(watcher => watcher(filteredStatus));
91 process.nextTick(() => {
92 var _a;
93 (_a = this.listener) === null || _a === void 0 ? void 0 : _a.onReceiveStatus(filteredStatus);
94 });
95 }
96 }
97 sendMessageOnChild(context, message) {
98 if (!this.child) {
99 throw new Error('sendMessageonChild called with child not populated');
100 }
101 const child = this.child;
102 this.writeFilterPending = true;
103 this.filterStack.sendMessage(Promise.resolve({ message: message, flags: context.flags })).then((filteredMessage) => {
104 this.writeFilterPending = false;
105 child.sendMessageWithContext(context, filteredMessage.message);
106 if (this.pendingHalfClose) {
107 child.halfClose();
108 }
109 }, (status) => {
110 this.cancelWithStatus(status.code, status.details);
111 });
112 }
113 getConfig() {
114 if (this.ended) {
115 return;
116 }
117 if (!this.metadata || !this.listener) {
118 throw new Error('getConfig called before start');
119 }
120 const configResult = this.channel.getConfig(this.method, this.metadata);
121 if (configResult.type === 'NONE') {
122 this.channel.queueCallForConfig(this);
123 return;
124 }
125 else if (configResult.type === 'ERROR') {
126 if (this.metadata.getOptions().waitForReady) {
127 this.channel.queueCallForConfig(this);
128 }
129 else {
130 this.outputStatus(configResult.error);
131 }
132 return;
133 }
134 // configResult.type === 'SUCCESS'
135 const config = configResult.config;
136 if (config.status !== constants_1.Status.OK) {
137 const { code, details } = control_plane_status_1.restrictControlPlaneStatusCode(config.status, 'Failed to route call to method ' + this.method);
138 this.outputStatus({
139 code: code,
140 details: details,
141 metadata: new metadata_1.Metadata()
142 });
143 return;
144 }
145 if (config.methodConfig.timeout) {
146 const configDeadline = new Date();
147 configDeadline.setSeconds(configDeadline.getSeconds() + config.methodConfig.timeout.seconds);
148 configDeadline.setMilliseconds(configDeadline.getMilliseconds() +
149 config.methodConfig.timeout.nanos / 1000000);
150 this.deadline = deadline_1.minDeadline(this.deadline, configDeadline);
151 }
152 this.filterStackFactory.push(config.dynamicFilterFactories);
153 this.filterStack = this.filterStackFactory.createFilter();
154 this.filterStack.sendMetadata(Promise.resolve(this.metadata)).then(filteredMetadata => {
155 this.child = this.channel.createInnerCall(config, this.method, this.host, this.credentials, this.deadline);
156 this.child.start(filteredMetadata, {
157 onReceiveMetadata: metadata => {
158 this.listener.onReceiveMetadata(this.filterStack.receiveMetadata(metadata));
159 },
160 onReceiveMessage: message => {
161 this.readFilterPending = true;
162 this.filterStack.receiveMessage(message).then(filteredMesssage => {
163 this.readFilterPending = false;
164 this.listener.onReceiveMessage(filteredMesssage);
165 if (this.pendingChildStatus) {
166 this.outputStatus(this.pendingChildStatus);
167 }
168 }, (status) => {
169 this.cancelWithStatus(status.code, status.details);
170 });
171 },
172 onReceiveStatus: status => {
173 if (this.readFilterPending) {
174 this.pendingChildStatus = status;
175 }
176 else {
177 this.outputStatus(status);
178 }
179 }
180 });
181 if (this.readPending) {
182 this.child.startRead();
183 }
184 if (this.pendingMessage) {
185 this.sendMessageOnChild(this.pendingMessage.context, this.pendingMessage.message);
186 }
187 else if (this.pendingHalfClose) {
188 this.child.halfClose();
189 }
190 }, (status) => {
191 this.outputStatus(status);
192 });
193 }
194 reportResolverError(status) {
195 var _a;
196 if ((_a = this.metadata) === null || _a === void 0 ? void 0 : _a.getOptions().waitForReady) {
197 this.channel.queueCallForConfig(this);
198 }
199 else {
200 this.outputStatus(status);
201 }
202 }
203 cancelWithStatus(status, details) {
204 var _a;
205 this.trace('cancelWithStatus code: ' + status + ' details: "' + details + '"');
206 (_a = this.child) === null || _a === void 0 ? void 0 : _a.cancelWithStatus(status, details);
207 this.outputStatus({ code: status, details: details, metadata: new metadata_1.Metadata() });
208 }
209 getPeer() {
210 var _a, _b;
211 return (_b = (_a = this.child) === null || _a === void 0 ? void 0 : _a.getPeer()) !== null && _b !== void 0 ? _b : this.channel.getTarget();
212 }
213 start(metadata, listener) {
214 this.trace('start called');
215 this.metadata = metadata.clone();
216 this.listener = listener;
217 this.getConfig();
218 }
219 sendMessageWithContext(context, message) {
220 this.trace('write() called with message of length ' + message.length);
221 if (this.child) {
222 this.sendMessageOnChild(context, message);
223 }
224 else {
225 this.pendingMessage = { context, message };
226 }
227 }
228 startRead() {
229 this.trace('startRead called');
230 if (this.child) {
231 this.child.startRead();
232 }
233 else {
234 this.readPending = true;
235 }
236 }
237 halfClose() {
238 this.trace('halfClose called');
239 if (this.child && !this.writeFilterPending) {
240 this.child.halfClose();
241 }
242 else {
243 this.pendingHalfClose = true;
244 }
245 }
246 setCredentials(credentials) {
247 this.credentials = this.credentials.compose(credentials);
248 }
249 addStatusWatcher(watcher) {
250 this.statusWatchers.push(watcher);
251 }
252 getCallNumber() {
253 return this.callNumber;
254 }
255}
256exports.ResolvingCall = ResolvingCall;
257//# sourceMappingURL=resolving-call.js.map
\No newline at end of file