UNPKG

29.1 kBJavaScriptView Raw
1"use strict";
2/*
3 * Copyright 2023 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.Http2SubchannelConnector = void 0;
20const http2 = require("http2");
21const tls_1 = require("tls");
22const channelz_1 = require("./channelz");
23const constants_1 = require("./constants");
24const http_proxy_1 = require("./http_proxy");
25const logging = require("./logging");
26const resolver_1 = require("./resolver");
27const subchannel_address_1 = require("./subchannel-address");
28const uri_parser_1 = require("./uri-parser");
29const net = require("net");
30const subchannel_call_1 = require("./subchannel-call");
31const call_number_1 = require("./call-number");
32const TRACER_NAME = 'transport';
33const FLOW_CONTROL_TRACER_NAME = 'transport_flowctrl';
34const clientVersion = require('../../package.json').version;
35const { HTTP2_HEADER_AUTHORITY, HTTP2_HEADER_CONTENT_TYPE, HTTP2_HEADER_METHOD, HTTP2_HEADER_PATH, HTTP2_HEADER_TE, HTTP2_HEADER_USER_AGENT, } = http2.constants;
36const KEEPALIVE_TIMEOUT_MS = 20000;
37const tooManyPingsData = Buffer.from('too_many_pings', 'ascii');
38class Http2Transport {
39 constructor(session, subchannelAddress, options,
40 /**
41 * Name of the remote server, if it is not the same as the subchannel
42 * address, i.e. if connecting through an HTTP CONNECT proxy.
43 */
44 remoteName) {
45 this.session = session;
46 this.remoteName = remoteName;
47 /**
48 * The amount of time in between sending pings
49 */
50 this.keepaliveTimeMs = -1;
51 /**
52 * The amount of time to wait for an acknowledgement after sending a ping
53 */
54 this.keepaliveTimeoutMs = KEEPALIVE_TIMEOUT_MS;
55 /**
56 * Timer reference for timeout that indicates when to send the next ping
57 */
58 this.keepaliveTimerId = null;
59 /**
60 * Indicates that the keepalive timer ran out while there were no active
61 * calls, and a ping should be sent the next time a call starts.
62 */
63 this.pendingSendKeepalivePing = false;
64 /**
65 * Timer reference tracking when the most recent ping will be considered lost
66 */
67 this.keepaliveTimeoutId = null;
68 /**
69 * Indicates whether keepalive pings should be sent without any active calls
70 */
71 this.keepaliveWithoutCalls = false;
72 this.activeCalls = new Set();
73 this.disconnectListeners = [];
74 this.disconnectHandled = false;
75 this.channelzEnabled = true;
76 this.streamTracker = new channelz_1.ChannelzCallTracker();
77 this.keepalivesSent = 0;
78 this.messagesSent = 0;
79 this.messagesReceived = 0;
80 this.lastMessageSentTimestamp = null;
81 this.lastMessageReceivedTimestamp = null;
82 /* Populate subchannelAddressString and channelzRef before doing anything
83 * else, because they are used in the trace methods. */
84 this.subchannelAddressString = (0, subchannel_address_1.subchannelAddressToString)(subchannelAddress);
85 if (options['grpc.enable_channelz'] === 0) {
86 this.channelzEnabled = false;
87 }
88 this.channelzRef = (0, channelz_1.registerChannelzSocket)(this.subchannelAddressString, () => this.getChannelzInfo(), this.channelzEnabled);
89 // Build user-agent string.
90 this.userAgent = [
91 options['grpc.primary_user_agent'],
92 `grpc-node-js/${clientVersion}`,
93 options['grpc.secondary_user_agent'],
94 ]
95 .filter(e => e)
96 .join(' '); // remove falsey values first
97 if ('grpc.keepalive_time_ms' in options) {
98 this.keepaliveTimeMs = options['grpc.keepalive_time_ms'];
99 }
100 if ('grpc.keepalive_timeout_ms' in options) {
101 this.keepaliveTimeoutMs = options['grpc.keepalive_timeout_ms'];
102 }
103 if ('grpc.keepalive_permit_without_calls' in options) {
104 this.keepaliveWithoutCalls =
105 options['grpc.keepalive_permit_without_calls'] === 1;
106 }
107 else {
108 this.keepaliveWithoutCalls = false;
109 }
110 session.once('close', () => {
111 this.trace('session closed');
112 this.stopKeepalivePings();
113 this.handleDisconnect();
114 });
115 session.once('goaway', (errorCode, lastStreamID, opaqueData) => {
116 let tooManyPings = false;
117 /* See the last paragraph of
118 * https://github.com/grpc/proposal/blob/master/A8-client-side-keepalive.md#basic-keepalive */
119 if (errorCode === http2.constants.NGHTTP2_ENHANCE_YOUR_CALM &&
120 opaqueData.equals(tooManyPingsData)) {
121 tooManyPings = true;
122 }
123 this.trace('connection closed by GOAWAY with code ' + errorCode);
124 this.reportDisconnectToOwner(tooManyPings);
125 });
126 session.once('error', error => {
127 /* Do nothing here. Any error should also trigger a close event, which is
128 * where we want to handle that. */
129 this.trace('connection closed with error ' + error.message);
130 });
131 if (logging.isTracerEnabled(TRACER_NAME)) {
132 session.on('remoteSettings', (settings) => {
133 this.trace('new settings received' +
134 (this.session !== session ? ' on the old connection' : '') +
135 ': ' +
136 JSON.stringify(settings));
137 });
138 session.on('localSettings', (settings) => {
139 this.trace('local settings acknowledged by remote' +
140 (this.session !== session ? ' on the old connection' : '') +
141 ': ' +
142 JSON.stringify(settings));
143 });
144 }
145 /* Start the keepalive timer last, because this can trigger trace logs,
146 * which should only happen after everything else is set up. */
147 if (this.keepaliveWithoutCalls) {
148 this.maybeStartKeepalivePingTimer();
149 }
150 }
151 getChannelzInfo() {
152 var _a, _b, _c;
153 const sessionSocket = this.session.socket;
154 const remoteAddress = sessionSocket.remoteAddress
155 ? (0, subchannel_address_1.stringToSubchannelAddress)(sessionSocket.remoteAddress, sessionSocket.remotePort)
156 : null;
157 const localAddress = sessionSocket.localAddress
158 ? (0, subchannel_address_1.stringToSubchannelAddress)(sessionSocket.localAddress, sessionSocket.localPort)
159 : null;
160 let tlsInfo;
161 if (this.session.encrypted) {
162 const tlsSocket = sessionSocket;
163 const cipherInfo = tlsSocket.getCipher();
164 const certificate = tlsSocket.getCertificate();
165 const peerCertificate = tlsSocket.getPeerCertificate();
166 tlsInfo = {
167 cipherSuiteStandardName: (_a = cipherInfo.standardName) !== null && _a !== void 0 ? _a : null,
168 cipherSuiteOtherName: cipherInfo.standardName ? null : cipherInfo.name,
169 localCertificate: certificate && 'raw' in certificate ? certificate.raw : null,
170 remoteCertificate: peerCertificate && 'raw' in peerCertificate
171 ? peerCertificate.raw
172 : null,
173 };
174 }
175 else {
176 tlsInfo = null;
177 }
178 const socketInfo = {
179 remoteAddress: remoteAddress,
180 localAddress: localAddress,
181 security: tlsInfo,
182 remoteName: this.remoteName,
183 streamsStarted: this.streamTracker.callsStarted,
184 streamsSucceeded: this.streamTracker.callsSucceeded,
185 streamsFailed: this.streamTracker.callsFailed,
186 messagesSent: this.messagesSent,
187 messagesReceived: this.messagesReceived,
188 keepAlivesSent: this.keepalivesSent,
189 lastLocalStreamCreatedTimestamp: this.streamTracker.lastCallStartedTimestamp,
190 lastRemoteStreamCreatedTimestamp: null,
191 lastMessageSentTimestamp: this.lastMessageSentTimestamp,
192 lastMessageReceivedTimestamp: this.lastMessageReceivedTimestamp,
193 localFlowControlWindow: (_b = this.session.state.localWindowSize) !== null && _b !== void 0 ? _b : null,
194 remoteFlowControlWindow: (_c = this.session.state.remoteWindowSize) !== null && _c !== void 0 ? _c : null,
195 };
196 return socketInfo;
197 }
198 trace(text) {
199 logging.trace(constants_1.LogVerbosity.DEBUG, TRACER_NAME, '(' +
200 this.channelzRef.id +
201 ') ' +
202 this.subchannelAddressString +
203 ' ' +
204 text);
205 }
206 keepaliveTrace(text) {
207 logging.trace(constants_1.LogVerbosity.DEBUG, 'keepalive', '(' +
208 this.channelzRef.id +
209 ') ' +
210 this.subchannelAddressString +
211 ' ' +
212 text);
213 }
214 flowControlTrace(text) {
215 logging.trace(constants_1.LogVerbosity.DEBUG, FLOW_CONTROL_TRACER_NAME, '(' +
216 this.channelzRef.id +
217 ') ' +
218 this.subchannelAddressString +
219 ' ' +
220 text);
221 }
222 internalsTrace(text) {
223 logging.trace(constants_1.LogVerbosity.DEBUG, 'transport_internals', '(' +
224 this.channelzRef.id +
225 ') ' +
226 this.subchannelAddressString +
227 ' ' +
228 text);
229 }
230 /**
231 * Indicate to the owner of this object that this transport should no longer
232 * be used. That happens if the connection drops, or if the server sends a
233 * GOAWAY.
234 * @param tooManyPings If true, this was triggered by a GOAWAY with data
235 * indicating that the session was closed becaues the client sent too many
236 * pings.
237 * @returns
238 */
239 reportDisconnectToOwner(tooManyPings) {
240 if (this.disconnectHandled) {
241 return;
242 }
243 this.disconnectHandled = true;
244 this.disconnectListeners.forEach(listener => listener(tooManyPings));
245 }
246 /**
247 * Handle connection drops, but not GOAWAYs.
248 */
249 handleDisconnect() {
250 this.reportDisconnectToOwner(false);
251 /* Give calls an event loop cycle to finish naturally before reporting the
252 * disconnnection to them. */
253 setImmediate(() => {
254 for (const call of this.activeCalls) {
255 call.onDisconnect();
256 }
257 });
258 }
259 addDisconnectListener(listener) {
260 this.disconnectListeners.push(listener);
261 }
262 clearKeepaliveTimer() {
263 if (!this.keepaliveTimerId) {
264 return;
265 }
266 clearTimeout(this.keepaliveTimerId);
267 this.keepaliveTimerId = null;
268 }
269 clearKeepaliveTimeout() {
270 if (!this.keepaliveTimeoutId) {
271 return;
272 }
273 clearTimeout(this.keepaliveTimeoutId);
274 this.keepaliveTimeoutId = null;
275 }
276 canSendPing() {
277 return (this.keepaliveTimeMs > 0 &&
278 (this.keepaliveWithoutCalls || this.activeCalls.size > 0));
279 }
280 maybeSendPing() {
281 var _a, _b;
282 this.clearKeepaliveTimer();
283 if (!this.canSendPing()) {
284 this.pendingSendKeepalivePing = true;
285 return;
286 }
287 if (this.channelzEnabled) {
288 this.keepalivesSent += 1;
289 }
290 this.keepaliveTrace('Sending ping with timeout ' + this.keepaliveTimeoutMs + 'ms');
291 if (!this.keepaliveTimeoutId) {
292 this.keepaliveTimeoutId = setTimeout(() => {
293 this.keepaliveTrace('Ping timeout passed without response');
294 this.handleDisconnect();
295 }, this.keepaliveTimeoutMs);
296 (_b = (_a = this.keepaliveTimeoutId).unref) === null || _b === void 0 ? void 0 : _b.call(_a);
297 }
298 try {
299 this.session.ping((err, duration, payload) => {
300 if (err) {
301 this.keepaliveTrace('Ping failed with error ' + err.message);
302 this.handleDisconnect();
303 }
304 this.keepaliveTrace('Received ping response');
305 this.clearKeepaliveTimeout();
306 this.maybeStartKeepalivePingTimer();
307 });
308 }
309 catch (e) {
310 /* If we fail to send a ping, the connection is no longer functional, so
311 * we should discard it. */
312 this.handleDisconnect();
313 }
314 }
315 /**
316 * Starts the keepalive ping timer if appropriate. If the timer already ran
317 * out while there were no active requests, instead send a ping immediately.
318 * If the ping timer is already running or a ping is currently in flight,
319 * instead do nothing and wait for them to resolve.
320 */
321 maybeStartKeepalivePingTimer() {
322 var _a, _b;
323 if (!this.canSendPing()) {
324 return;
325 }
326 if (this.pendingSendKeepalivePing) {
327 this.pendingSendKeepalivePing = false;
328 this.maybeSendPing();
329 }
330 else if (!this.keepaliveTimerId && !this.keepaliveTimeoutId) {
331 this.keepaliveTrace('Starting keepalive timer for ' + this.keepaliveTimeMs + 'ms');
332 this.keepaliveTimerId = (_b = (_a = setTimeout(() => {
333 this.maybeSendPing();
334 }, this.keepaliveTimeMs)).unref) === null || _b === void 0 ? void 0 : _b.call(_a);
335 }
336 /* Otherwise, there is already either a keepalive timer or a ping pending,
337 * wait for those to resolve. */
338 }
339 stopKeepalivePings() {
340 if (this.keepaliveTimerId) {
341 clearTimeout(this.keepaliveTimerId);
342 this.keepaliveTimerId = null;
343 }
344 this.clearKeepaliveTimeout();
345 }
346 removeActiveCall(call) {
347 this.activeCalls.delete(call);
348 if (this.activeCalls.size === 0) {
349 this.session.unref();
350 }
351 }
352 addActiveCall(call) {
353 this.activeCalls.add(call);
354 if (this.activeCalls.size === 1) {
355 this.session.ref();
356 if (!this.keepaliveWithoutCalls) {
357 this.maybeStartKeepalivePingTimer();
358 }
359 }
360 }
361 createCall(metadata, host, method, listener, subchannelCallStatsTracker) {
362 const headers = metadata.toHttp2Headers();
363 headers[HTTP2_HEADER_AUTHORITY] = host;
364 headers[HTTP2_HEADER_USER_AGENT] = this.userAgent;
365 headers[HTTP2_HEADER_CONTENT_TYPE] = 'application/grpc';
366 headers[HTTP2_HEADER_METHOD] = 'POST';
367 headers[HTTP2_HEADER_PATH] = method;
368 headers[HTTP2_HEADER_TE] = 'trailers';
369 let http2Stream;
370 /* In theory, if an error is thrown by session.request because session has
371 * become unusable (e.g. because it has received a goaway), this subchannel
372 * should soon see the corresponding close or goaway event anyway and leave
373 * READY. But we have seen reports that this does not happen
374 * (https://github.com/googleapis/nodejs-firestore/issues/1023#issuecomment-653204096)
375 * so for defense in depth, we just discard the session when we see an
376 * error here.
377 */
378 try {
379 http2Stream = this.session.request(headers);
380 }
381 catch (e) {
382 this.handleDisconnect();
383 throw e;
384 }
385 this.flowControlTrace('local window size: ' +
386 this.session.state.localWindowSize +
387 ' remote window size: ' +
388 this.session.state.remoteWindowSize);
389 this.internalsTrace('session.closed=' +
390 this.session.closed +
391 ' session.destroyed=' +
392 this.session.destroyed +
393 ' session.socket.destroyed=' +
394 this.session.socket.destroyed);
395 let eventTracker;
396 // eslint-disable-next-line prefer-const
397 let call;
398 if (this.channelzEnabled) {
399 this.streamTracker.addCallStarted();
400 eventTracker = {
401 addMessageSent: () => {
402 var _a;
403 this.messagesSent += 1;
404 this.lastMessageSentTimestamp = new Date();
405 (_a = subchannelCallStatsTracker.addMessageSent) === null || _a === void 0 ? void 0 : _a.call(subchannelCallStatsTracker);
406 },
407 addMessageReceived: () => {
408 var _a;
409 this.messagesReceived += 1;
410 this.lastMessageReceivedTimestamp = new Date();
411 (_a = subchannelCallStatsTracker.addMessageReceived) === null || _a === void 0 ? void 0 : _a.call(subchannelCallStatsTracker);
412 },
413 onCallEnd: status => {
414 var _a;
415 (_a = subchannelCallStatsTracker.onCallEnd) === null || _a === void 0 ? void 0 : _a.call(subchannelCallStatsTracker, status);
416 this.removeActiveCall(call);
417 },
418 onStreamEnd: success => {
419 var _a;
420 if (success) {
421 this.streamTracker.addCallSucceeded();
422 }
423 else {
424 this.streamTracker.addCallFailed();
425 }
426 (_a = subchannelCallStatsTracker.onStreamEnd) === null || _a === void 0 ? void 0 : _a.call(subchannelCallStatsTracker, success);
427 },
428 };
429 }
430 else {
431 eventTracker = {
432 addMessageSent: () => {
433 var _a;
434 (_a = subchannelCallStatsTracker.addMessageSent) === null || _a === void 0 ? void 0 : _a.call(subchannelCallStatsTracker);
435 },
436 addMessageReceived: () => {
437 var _a;
438 (_a = subchannelCallStatsTracker.addMessageReceived) === null || _a === void 0 ? void 0 : _a.call(subchannelCallStatsTracker);
439 },
440 onCallEnd: status => {
441 var _a;
442 (_a = subchannelCallStatsTracker.onCallEnd) === null || _a === void 0 ? void 0 : _a.call(subchannelCallStatsTracker, status);
443 this.removeActiveCall(call);
444 },
445 onStreamEnd: success => {
446 var _a;
447 (_a = subchannelCallStatsTracker.onStreamEnd) === null || _a === void 0 ? void 0 : _a.call(subchannelCallStatsTracker, success);
448 },
449 };
450 }
451 call = new subchannel_call_1.Http2SubchannelCall(http2Stream, eventTracker, listener, this, (0, call_number_1.getNextCallNumber)());
452 this.addActiveCall(call);
453 return call;
454 }
455 getChannelzRef() {
456 return this.channelzRef;
457 }
458 getPeerName() {
459 return this.subchannelAddressString;
460 }
461 shutdown() {
462 this.session.close();
463 (0, channelz_1.unregisterChannelzRef)(this.channelzRef);
464 }
465}
466class Http2SubchannelConnector {
467 constructor(channelTarget) {
468 this.channelTarget = channelTarget;
469 this.session = null;
470 this.isShutdown = false;
471 }
472 trace(text) {
473 logging.trace(constants_1.LogVerbosity.DEBUG, TRACER_NAME, (0, uri_parser_1.uriToString)(this.channelTarget) + ' ' + text);
474 }
475 createSession(address, credentials, options, proxyConnectionResult) {
476 if (this.isShutdown) {
477 return Promise.reject();
478 }
479 return new Promise((resolve, reject) => {
480 var _a, _b, _c;
481 let remoteName;
482 if (proxyConnectionResult.realTarget) {
483 remoteName = (0, uri_parser_1.uriToString)(proxyConnectionResult.realTarget);
484 this.trace('creating HTTP/2 session through proxy to ' +
485 (0, uri_parser_1.uriToString)(proxyConnectionResult.realTarget));
486 }
487 else {
488 remoteName = null;
489 this.trace('creating HTTP/2 session to ' + (0, subchannel_address_1.subchannelAddressToString)(address));
490 }
491 const targetAuthority = (0, resolver_1.getDefaultAuthority)((_a = proxyConnectionResult.realTarget) !== null && _a !== void 0 ? _a : this.channelTarget);
492 let connectionOptions = credentials._getConnectionOptions() || {};
493 connectionOptions.maxSendHeaderBlockLength = Number.MAX_SAFE_INTEGER;
494 if ('grpc-node.max_session_memory' in options) {
495 connectionOptions.maxSessionMemory =
496 options['grpc-node.max_session_memory'];
497 }
498 else {
499 /* By default, set a very large max session memory limit, to effectively
500 * disable enforcement of the limit. Some testing indicates that Node's
501 * behavior degrades badly when this limit is reached, so we solve that
502 * by disabling the check entirely. */
503 connectionOptions.maxSessionMemory = Number.MAX_SAFE_INTEGER;
504 }
505 let addressScheme = 'http://';
506 if ('secureContext' in connectionOptions) {
507 addressScheme = 'https://';
508 // If provided, the value of grpc.ssl_target_name_override should be used
509 // to override the target hostname when checking server identity.
510 // This option is used for testing only.
511 if (options['grpc.ssl_target_name_override']) {
512 const sslTargetNameOverride = options['grpc.ssl_target_name_override'];
513 connectionOptions.checkServerIdentity = (host, cert) => {
514 return (0, tls_1.checkServerIdentity)(sslTargetNameOverride, cert);
515 };
516 connectionOptions.servername = sslTargetNameOverride;
517 }
518 else {
519 const authorityHostname = (_c = (_b = (0, uri_parser_1.splitHostPort)(targetAuthority)) === null || _b === void 0 ? void 0 : _b.host) !== null && _c !== void 0 ? _c : 'localhost';
520 // We want to always set servername to support SNI
521 connectionOptions.servername = authorityHostname;
522 }
523 if (proxyConnectionResult.socket) {
524 /* This is part of the workaround for
525 * https://github.com/nodejs/node/issues/32922. Without that bug,
526 * proxyConnectionResult.socket would always be a plaintext socket and
527 * this would say
528 * connectionOptions.socket = proxyConnectionResult.socket; */
529 connectionOptions.createConnection = (authority, option) => {
530 return proxyConnectionResult.socket;
531 };
532 }
533 }
534 else {
535 /* In all but the most recent versions of Node, http2.connect does not use
536 * the options when establishing plaintext connections, so we need to
537 * establish that connection explicitly. */
538 connectionOptions.createConnection = (authority, option) => {
539 if (proxyConnectionResult.socket) {
540 return proxyConnectionResult.socket;
541 }
542 else {
543 /* net.NetConnectOpts is declared in a way that is more restrictive
544 * than what net.connect will actually accept, so we use the type
545 * assertion to work around that. */
546 return net.connect(address);
547 }
548 };
549 }
550 connectionOptions = Object.assign(Object.assign(Object.assign({}, connectionOptions), address), { enableTrace: options['grpc-node.tls_enable_trace'] === 1 });
551 /* http2.connect uses the options here:
552 * https://github.com/nodejs/node/blob/70c32a6d190e2b5d7b9ff9d5b6a459d14e8b7d59/lib/internal/http2/core.js#L3028-L3036
553 * The spread operator overides earlier values with later ones, so any port
554 * or host values in the options will be used rather than any values extracted
555 * from the first argument. In addition, the path overrides the host and port,
556 * as documented for plaintext connections here:
557 * https://nodejs.org/api/net.html#net_socket_connect_options_connectlistener
558 * and for TLS connections here:
559 * https://nodejs.org/api/tls.html#tls_tls_connect_options_callback. In
560 * earlier versions of Node, http2.connect passes these options to
561 * tls.connect but not net.connect, so in the insecure case we still need
562 * to set the createConnection option above to create the connection
563 * explicitly. We cannot do that in the TLS case because http2.connect
564 * passes necessary additional options to tls.connect.
565 * The first argument just needs to be parseable as a URL and the scheme
566 * determines whether the connection will be established over TLS or not.
567 */
568 const session = http2.connect(addressScheme + targetAuthority, connectionOptions);
569 this.session = session;
570 session.unref();
571 session.once('connect', () => {
572 session.removeAllListeners();
573 resolve(new Http2Transport(session, address, options, remoteName));
574 this.session = null;
575 });
576 session.once('close', () => {
577 this.session = null;
578 reject();
579 });
580 session.once('error', error => {
581 this.trace('connection failed with error ' + error.message);
582 });
583 });
584 }
585 connect(address, credentials, options) {
586 var _a, _b;
587 if (this.isShutdown) {
588 return Promise.reject();
589 }
590 /* Pass connection options through to the proxy so that it's able to
591 * upgrade it's connection to support tls if needed.
592 * This is a workaround for https://github.com/nodejs/node/issues/32922
593 * See https://github.com/grpc/grpc-node/pull/1369 for more info. */
594 const connectionOptions = credentials._getConnectionOptions() || {};
595 if ('secureContext' in connectionOptions) {
596 connectionOptions.ALPNProtocols = ['h2'];
597 // If provided, the value of grpc.ssl_target_name_override should be used
598 // to override the target hostname when checking server identity.
599 // This option is used for testing only.
600 if (options['grpc.ssl_target_name_override']) {
601 const sslTargetNameOverride = options['grpc.ssl_target_name_override'];
602 connectionOptions.checkServerIdentity = (host, cert) => {
603 return (0, tls_1.checkServerIdentity)(sslTargetNameOverride, cert);
604 };
605 connectionOptions.servername = sslTargetNameOverride;
606 }
607 else {
608 if ('grpc.http_connect_target' in options) {
609 /* This is more or less how servername will be set in createSession
610 * if a connection is successfully established through the proxy.
611 * If the proxy is not used, these connectionOptions are discarded
612 * anyway */
613 const targetPath = (0, resolver_1.getDefaultAuthority)((_a = (0, uri_parser_1.parseUri)(options['grpc.http_connect_target'])) !== null && _a !== void 0 ? _a : {
614 path: 'localhost',
615 });
616 const hostPort = (0, uri_parser_1.splitHostPort)(targetPath);
617 connectionOptions.servername = (_b = hostPort === null || hostPort === void 0 ? void 0 : hostPort.host) !== null && _b !== void 0 ? _b : targetPath;
618 }
619 }
620 if (options['grpc-node.tls_enable_trace']) {
621 connectionOptions.enableTrace = true;
622 }
623 }
624 return (0, http_proxy_1.getProxiedConnection)(address, options, connectionOptions).then(result => this.createSession(address, credentials, options, result));
625 }
626 shutdown() {
627 var _a;
628 this.isShutdown = true;
629 (_a = this.session) === null || _a === void 0 ? void 0 : _a.close();
630 this.session = null;
631 }
632}
633exports.Http2SubchannelConnector = Http2SubchannelConnector;
634//# sourceMappingURL=transport.js.map
\No newline at end of file