diff --git a/packages/grpc-js/src/http_proxy.ts b/packages/grpc-js/src/http_proxy.ts index 2b3fb5d5a..708b30c6e 100644 --- a/packages/grpc-js/src/http_proxy.ts +++ b/packages/grpc-js/src/http_proxy.ts @@ -18,9 +18,11 @@ import { URL } from 'url'; import { log } from './logging'; import { LogVerbosity } from './constants'; +import { getDefaultAuthority } from './resolver'; import { parseTarget } from './resolver-dns'; import { Socket } from 'net'; import * as http from 'http'; +import * as tls from 'tls'; import * as logging from './logging'; import { SubchannelAddress, @@ -157,7 +159,8 @@ export interface ProxyConnectionResult { export function getProxiedConnection( address: SubchannelAddress, - channelOptions: ChannelOptions + channelOptions: ChannelOptions, + connectionOptions: tls.ConnectionOptions ): Promise { if (!('grpc.http_connect_target' in channelOptions)) { return Promise.resolve({}); @@ -202,10 +205,25 @@ export function getProxiedConnection( ' through proxy ' + proxyAddressString ); - resolve({ - socket, - realTarget, - }); + if ('secureContext' in connectionOptions) { + /* The proxy is connecting to a TLS server, so upgrade this socket + * connection to a TLS connection. + * This is a workaround for https://github.com/nodejs/node/issues/32922 + * See https://github.com/grpc/grpc-node/pull/1369 for more info. */ + const cts = tls.connect({ + ...connectionOptions, + host: getDefaultAuthority(realTarget), + socket: socket, + }, () => { + resolve({ socket: cts, realTarget }); + } + ); + } else { + resolve({ + socket, + realTarget, + }); + } } else { log( LogVerbosity.ERROR, diff --git a/packages/grpc-js/src/subchannel.ts b/packages/grpc-js/src/subchannel.ts index e332df36d..414e797d3 100644 --- a/packages/grpc-js/src/subchannel.ts +++ b/packages/grpc-js/src/subchannel.ts @@ -28,6 +28,7 @@ import * as logging from './logging'; import { LogVerbosity } from './constants'; import { getProxiedConnection, ProxyConnectionResult } from './http_proxy'; import * as net from 'net'; +import { ConnectionOptions } from 'tls'; const clientVersion = require('../../package.json').version; @@ -300,7 +301,9 @@ export class Subchannel { connectionOptions.servername = sslTargetNameOverride; } if (proxyConnectionResult.socket) { - connectionOptions.socket = proxyConnectionResult.socket; + connectionOptions.createConnection = (authority, option) => { + return proxyConnectionResult.socket!; + }; } } else { /* In all but the most recent versions of Node, http2.connect does not use @@ -317,6 +320,7 @@ export class Subchannel { } }; } + connectionOptions = Object.assign( connectionOptions, this.subchannelAddress @@ -412,7 +416,37 @@ export class Subchannel { } private startConnectingInternal() { - getProxiedConnection(this.subchannelAddress, this.options).then( + /* Pass connection options through to the proxy so that it's able to + * upgrade it's connection to support tls if needed. + * This is a workaround for https://github.com/nodejs/node/issues/32922 + * See https://github.com/grpc/grpc-node/pull/1369 for more info. */ + const connectionOptions: ConnectionOptions = + this.credentials._getConnectionOptions() || {}; + + if ('secureContext' in connectionOptions) { + connectionOptions.ALPNProtocols = ['h2']; + // If provided, the value of grpc.ssl_target_name_override should be used + // to override the target hostname when checking server identity. + // This option is used for testing only. + if (this.options['grpc.ssl_target_name_override']) { + const sslTargetNameOverride = this.options[ + 'grpc.ssl_target_name_override' + ]!; + connectionOptions.checkServerIdentity = ( + host: string, + cert: PeerCertificate + ): Error | undefined => { + return checkServerIdentity(sslTargetNameOverride, cert); + }; + connectionOptions.servername = sslTargetNameOverride; + } + } + + getProxiedConnection( + this.subchannelAddress, + this.options, + connectionOptions + ).then( (result) => { this.createSession(result); },