Built motion from commit 6a09e18b.|2.6.11
[motion2.git] / legacy-libs / grpc-cloned / node_modules / @grpc / grpc-js / build / src / channel.js
1 "use strict";
2 Object.defineProperty(exports, "__esModule", { value: true });
3 const events_1 = require("events");
4 const http2 = require("http2");
5 const tls_1 = require("tls");
6 const url = require("url");
7 const call_credentials_filter_1 = require("./call-credentials-filter");
8 const call_stream_1 = require("./call-stream");
9 const channel_options_1 = require("./channel-options");
10 const compression_filter_1 = require("./compression-filter");
11 const constants_1 = require("./constants");
12 const deadline_filter_1 = require("./deadline-filter");
13 const filter_stack_1 = require("./filter-stack");
14 const metadata_status_filter_1 = require("./metadata-status-filter");
15 const subchannel_1 = require("./subchannel");
16 const { version: clientVersion } = require('../../package.json');
17 const MIN_CONNECT_TIMEOUT_MS = 20000;
18 const INITIAL_BACKOFF_MS = 1000;
19 const BACKOFF_MULTIPLIER = 1.6;
20 const MAX_BACKOFF_MS = 120000;
21 const BACKOFF_JITTER = 0.2;
22 const { HTTP2_HEADER_AUTHORITY, HTTP2_HEADER_CONTENT_TYPE, HTTP2_HEADER_METHOD, HTTP2_HEADER_PATH, HTTP2_HEADER_TE, HTTP2_HEADER_USER_AGENT } = http2.constants;
23 var ConnectivityState;
24 (function (ConnectivityState) {
25     ConnectivityState[ConnectivityState["CONNECTING"] = 0] = "CONNECTING";
26     ConnectivityState[ConnectivityState["READY"] = 1] = "READY";
27     ConnectivityState[ConnectivityState["TRANSIENT_FAILURE"] = 2] = "TRANSIENT_FAILURE";
28     ConnectivityState[ConnectivityState["IDLE"] = 3] = "IDLE";
29     ConnectivityState[ConnectivityState["SHUTDOWN"] = 4] = "SHUTDOWN";
30 })(ConnectivityState = exports.ConnectivityState || (exports.ConnectivityState = {}));
31 function uniformRandom(min, max) {
32     return Math.random() * (max - min) + min;
33 }
34 class Http2Channel extends events_1.EventEmitter {
35     constructor(address, credentials, options) {
36         super();
37         this.credentials = credentials;
38         this.options = options;
39         this.connectivityState = ConnectivityState.IDLE;
40         // Helper Promise object only used in the implementation of connect().
41         this.connecting = null;
42         /* For now, we have up to one subchannel, which will exist as long as we are
43          * connecting or trying to connect */
44         this.subChannel = null;
45         this.subChannelConnectCallback = () => { };
46         this.subChannelCloseCallback = () => { };
47         this.currentBackoff = INITIAL_BACKOFF_MS;
48         for (const option in options) {
49             if (options.hasOwnProperty(option)) {
50                 if (!channel_options_1.recognizedOptions.hasOwnProperty(option)) {
51                     console.warn(`Unrecognized channel argument '${option}' will be ignored.`);
52                 }
53             }
54         }
55         if (credentials._isSecure()) {
56             this.target = new url.URL(`https://${address}`);
57         }
58         else {
59             this.target = new url.URL(`http://${address}`);
60         }
61         // TODO(murgatroid99): Add more centralized handling of channel options
62         if (this.options['grpc.default_authority']) {
63             this.defaultAuthority = this.options['grpc.default_authority'];
64         }
65         else {
66             this.defaultAuthority = this.target.host;
67         }
68         this.filterStackFactory = new filter_stack_1.FilterStackFactory([
69             new call_credentials_filter_1.CallCredentialsFilterFactory(this), new deadline_filter_1.DeadlineFilterFactory(this),
70             new metadata_status_filter_1.MetadataStatusFilterFactory(this), new compression_filter_1.CompressionFilterFactory(this)
71         ]);
72         this.currentBackoffDeadline = new Date();
73         /* The only purpose of these lines is to ensure that this.backoffTimerId has
74          * a value of type NodeJS.Timer. */
75         this.backoffTimerId = setTimeout(() => { }, 0);
76         // Build user-agent string.
77         this.userAgent = [
78             options['grpc.primary_user_agent'], `grpc-node-js/${clientVersion}`,
79             options['grpc.secondary_user_agent']
80         ].filter(e => e).join(' '); // remove falsey values first
81     }
82     handleStateChange(oldState, newState) {
83         const now = new Date();
84         switch (newState) {
85             case ConnectivityState.CONNECTING:
86                 if (oldState === ConnectivityState.IDLE) {
87                     this.currentBackoff = INITIAL_BACKOFF_MS;
88                     this.currentBackoffDeadline =
89                         new Date(now.getTime() + INITIAL_BACKOFF_MS);
90                 }
91                 else if (oldState === ConnectivityState.TRANSIENT_FAILURE) {
92                     this.currentBackoff = Math.min(this.currentBackoff * BACKOFF_MULTIPLIER, MAX_BACKOFF_MS);
93                     const jitterMagnitude = BACKOFF_JITTER * this.currentBackoff;
94                     this.currentBackoffDeadline = new Date(now.getTime() + this.currentBackoff +
95                         uniformRandom(-jitterMagnitude, jitterMagnitude));
96                 }
97                 this.startConnecting();
98                 break;
99             case ConnectivityState.READY:
100                 this.emit('connect');
101                 break;
102             case ConnectivityState.TRANSIENT_FAILURE:
103                 this.subChannel = null;
104                 this.backoffTimerId = setTimeout(() => {
105                     this.transitionToState([ConnectivityState.TRANSIENT_FAILURE], ConnectivityState.CONNECTING);
106                 }, this.currentBackoffDeadline.getTime() - now.getTime());
107                 break;
108             case ConnectivityState.IDLE:
109             case ConnectivityState.SHUTDOWN:
110                 if (this.subChannel) {
111                     this.subChannel.close();
112                     this.subChannel.removeListener('connect', this.subChannelConnectCallback);
113                     this.subChannel.removeListener('close', this.subChannelCloseCallback);
114                     this.subChannel = null;
115                     this.emit('shutdown');
116                     clearTimeout(this.backoffTimerId);
117                 }
118                 break;
119             default:
120                 throw new Error('This should never happen');
121         }
122     }
123     // Transition from any of a set of oldStates to a specific newState
124     transitionToState(oldStates, newState) {
125         if (oldStates.indexOf(this.connectivityState) > -1) {
126             const oldState = this.connectivityState;
127             this.connectivityState = newState;
128             this.handleStateChange(oldState, newState);
129             this.emit('connectivityStateChanged', newState);
130         }
131     }
132     startConnecting() {
133         const connectionOptions = this.credentials._getConnectionOptions() || {};
134         if (connectionOptions.secureContext !== null) {
135             // If provided, the value of grpc.ssl_target_name_override should be used
136             // to override the target hostname when checking server identity.
137             // This option is used for testing only.
138             if (this.options['grpc.ssl_target_name_override']) {
139                 const sslTargetNameOverride = this.options['grpc.ssl_target_name_override'];
140                 connectionOptions.checkServerIdentity =
141                     (host, cert) => {
142                         return tls_1.checkServerIdentity(sslTargetNameOverride, cert);
143                     };
144                 connectionOptions.servername = sslTargetNameOverride;
145             }
146         }
147         const subChannel = new subchannel_1.Http2SubChannel(this.target, connectionOptions, this.userAgent, this.options);
148         this.subChannel = subChannel;
149         const now = new Date();
150         const connectionTimeout = Math.max(this.currentBackoffDeadline.getTime() - now.getTime(), MIN_CONNECT_TIMEOUT_MS);
151         const connectionTimerId = setTimeout(() => {
152             // This should trigger the 'close' event, which will send us back to
153             // TRANSIENT_FAILURE
154             subChannel.close();
155         }, connectionTimeout);
156         this.subChannelConnectCallback = () => {
157             // Connection succeeded
158             clearTimeout(connectionTimerId);
159             this.transitionToState([ConnectivityState.CONNECTING], ConnectivityState.READY);
160         };
161         subChannel.once('connect', this.subChannelConnectCallback);
162         this.subChannelCloseCallback = () => {
163             // Connection failed
164             clearTimeout(connectionTimerId);
165             /* TODO(murgatroid99): verify that this works for
166              * CONNECTING->TRANSITIVE_FAILURE see nodejs/node#16645 */
167             this.transitionToState([ConnectivityState.CONNECTING, ConnectivityState.READY], ConnectivityState.TRANSIENT_FAILURE);
168         };
169         subChannel.once('close', this.subChannelCloseCallback);
170     }
171     _startHttp2Stream(authority, methodName, stream, metadata) {
172         const finalMetadata = stream.filterStack.sendMetadata(Promise.resolve(metadata.clone()));
173         Promise.all([finalMetadata, this.connect()])
174             .then(([metadataValue]) => {
175             const headers = metadataValue.toHttp2Headers();
176             headers[HTTP2_HEADER_AUTHORITY] = authority;
177             headers[HTTP2_HEADER_USER_AGENT] = this.userAgent;
178             headers[HTTP2_HEADER_CONTENT_TYPE] = 'application/grpc';
179             headers[HTTP2_HEADER_METHOD] = 'POST';
180             headers[HTTP2_HEADER_PATH] = methodName;
181             headers[HTTP2_HEADER_TE] = 'trailers';
182             if (this.connectivityState === ConnectivityState.READY) {
183                 const subChannel = this.subChannel;
184                 subChannel.startCallStream(metadataValue, stream);
185             }
186             else {
187                 /* In this case, we lost the connection while finalizing
188                  * metadata. That should be very unusual */
189                 setImmediate(() => {
190                     this._startHttp2Stream(authority, methodName, stream, metadata);
191                 });
192             }
193         })
194             .catch((error) => {
195             // We assume the error code isn't 0 (Status.OK)
196             stream.cancelWithStatus(error.code || constants_1.Status.UNKNOWN, `Getting metadata from plugin failed with error: ${error.message}`);
197         });
198     }
199     createCall(method, deadline, host, parentCall, propagateFlags) {
200         if (this.connectivityState === ConnectivityState.SHUTDOWN) {
201             throw new Error('Channel has been shut down');
202         }
203         const finalOptions = {
204             deadline: (deadline === null || deadline === undefined) ? Infinity :
205                 deadline,
206             flags: propagateFlags || 0,
207             host: host || this.defaultAuthority,
208             parentCall: parentCall || null
209         };
210         const stream = new call_stream_1.Http2CallStream(method, this, finalOptions, this.filterStackFactory);
211         return stream;
212     }
213     /**
214      * Attempts to connect, returning a Promise that resolves when the connection
215      * is successful, or rejects if the channel is shut down.
216      */
217     connect() {
218         if (this.connectivityState === ConnectivityState.READY) {
219             return Promise.resolve();
220         }
221         else if (this.connectivityState === ConnectivityState.SHUTDOWN) {
222             return Promise.reject(new Error('Channel has been shut down'));
223         }
224         else {
225             // In effect, this.connecting is only assigned upon the first attempt to
226             // transition from IDLE to CONNECTING, so this condition could have also
227             // been (connectivityState === IDLE).
228             if (!this.connecting) {
229                 this.connecting = new Promise((resolve, reject) => {
230                     this.transitionToState([ConnectivityState.IDLE], ConnectivityState.CONNECTING);
231                     const onConnect = () => {
232                         this.connecting = null;
233                         this.removeListener('shutdown', onShutdown);
234                         resolve();
235                     };
236                     const onShutdown = () => {
237                         this.connecting = null;
238                         this.removeListener('connect', onConnect);
239                         reject(new Error('Channel has been shut down'));
240                     };
241                     this.once('connect', onConnect);
242                     this.once('shutdown', onShutdown);
243                 });
244             }
245             return this.connecting;
246         }
247     }
248     getConnectivityState(tryToConnect) {
249         if (tryToConnect) {
250             this.transitionToState([ConnectivityState.IDLE], ConnectivityState.CONNECTING);
251         }
252         return this.connectivityState;
253     }
254     watchConnectivityState(currentState, deadline, callback) {
255         if (this.connectivityState !== currentState) {
256             /* If the connectivity state is different from the provided currentState,
257              * we assume that a state change has successfully occurred */
258             setImmediate(callback);
259         }
260         else {
261             let deadlineMs = 0;
262             if (deadline instanceof Date) {
263                 deadlineMs = deadline.getTime();
264             }
265             else {
266                 deadlineMs = deadline;
267             }
268             let timeout = deadlineMs - Date.now();
269             if (timeout < 0) {
270                 timeout = 0;
271             }
272             const timeoutId = setTimeout(() => {
273                 this.removeListener('connectivityStateChanged', eventCb);
274                 callback(new Error('Channel state did not change before deadline'));
275             }, timeout);
276             const eventCb = () => {
277                 clearTimeout(timeoutId);
278                 callback();
279             };
280             this.once('connectivityStateChanged', eventCb);
281         }
282     }
283     getTarget() {
284         return this.target.toString();
285     }
286     close() {
287         if (this.connectivityState === ConnectivityState.SHUTDOWN) {
288             throw new Error('Channel has been shut down');
289         }
290         this.transitionToState([
291             ConnectivityState.CONNECTING, ConnectivityState.READY,
292             ConnectivityState.TRANSIENT_FAILURE, ConnectivityState.IDLE
293         ], ConnectivityState.SHUTDOWN);
294     }
295 }
296 exports.Http2Channel = Http2Channel;
297 //# sourceMappingURL=channel.js.map