From 9569f20fc6014c4ff2bbd4c053e226eeb2e0a0a9 Mon Sep 17 00:00:00 2001 From: Michael Lumish Date: Thu, 30 Jan 2025 14:03:06 -0800 Subject: [PATCH] grpc-js: Connect with https scheme when using TLS --- packages/grpc-js/src/channel-credentials.ts | 28 +++++++++++++++------ packages/grpc-js/src/experimental.ts | 2 +- packages/grpc-js/src/transport.ts | 17 +++++++------ 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/packages/grpc-js/src/channel-credentials.ts b/packages/grpc-js/src/channel-credentials.ts index 13c9c5520..c608ebe36 100644 --- a/packages/grpc-js/src/channel-credentials.ts +++ b/packages/grpc-js/src/channel-credentials.ts @@ -63,8 +63,13 @@ export interface VerifyOptions { rejectUnauthorized?: boolean; } +export interface SecureConnectResult { + socket: Socket; + secure: boolean; +} + export interface SecureConnector { - connect(socket: Socket): Promise; + connect(socket: Socket): Promise; getCallCredentials(): CallCredentials; destroy(): void; } @@ -178,7 +183,10 @@ class InsecureChannelCredentialsImpl extends ChannelCredentials { _createSecureConnector(channelTarget: GrpcUri, options: ChannelOptions, callCredentials?: CallCredentials): SecureConnector { return { connect(socket) { - return Promise.resolve(socket); + return Promise.resolve({ + socket, + secure: false + }); }, getCallCredentials: () => { return callCredentials ?? CallCredentials.createEmpty(); @@ -247,14 +255,17 @@ function getConnectionOptions(secureContext: SecureContext, verifyOptions: Verif class SecureConnectorImpl implements SecureConnector { constructor(private connectionOptions: ConnectionOptions, private callCredentials: CallCredentials) { } - connect(socket: Socket): Promise { + connect(socket: Socket): Promise { const tlsConnectOptions: ConnectionOptions = { socket: socket, ...this.connectionOptions }; - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const tlsSocket = tlsConnect(tlsConnectOptions, () => { - resolve(tlsSocket) + resolve({ + socket: tlsSocket, + secure: true + }) }); tlsSocket.on('error', (error: Error) => { reject(error); @@ -316,7 +327,7 @@ class CertificateProviderChannelCredentialsImpl extends ChannelCredentials { private static SecureConnectorImpl = class implements SecureConnector { constructor(private parent: CertificateProviderChannelCredentialsImpl, private channelTarget: GrpcUri, private options: ChannelOptions, private callCredentials: CallCredentials) {} - connect(socket: Socket): Promise { + connect(socket: Socket): Promise { return new Promise(async (resolve, reject) => { const secureContext = await this.parent.getSecureContext(); if (!secureContext) { @@ -329,7 +340,10 @@ class CertificateProviderChannelCredentialsImpl extends ChannelCredentials { ...connnectionOptions } const tlsSocket = tlsConnect(tlsConnectOptions, () => { - resolve(tlsSocket) + resolve({ + socket: tlsSocket, + secure: true + }); }); tlsSocket.on('error', (error: Error) => { reject(error); diff --git a/packages/grpc-js/src/experimental.ts b/packages/grpc-js/src/experimental.ts index b74728cd0..a381e1d9b 100644 --- a/packages/grpc-js/src/experimental.ts +++ b/packages/grpc-js/src/experimental.ts @@ -63,5 +63,5 @@ export { FileWatcherCertificateProvider, FileWatcherCertificateProviderConfig } from './certificate-provider'; -export { createCertificateProviderChannelCredentials, SecureConnector } from './channel-credentials'; +export { createCertificateProviderChannelCredentials, SecureConnector, SecureConnectResult } from './channel-credentials'; export { SUBCHANNEL_ARGS_EXCLUDE_KEY_PREFIX } from './internal-channel'; diff --git a/packages/grpc-js/src/transport.ts b/packages/grpc-js/src/transport.ts index 6677cf4cf..3c2223e91 100644 --- a/packages/grpc-js/src/transport.ts +++ b/packages/grpc-js/src/transport.ts @@ -21,7 +21,7 @@ import { TLSSocket, } from 'tls'; import { PartialStatusObject } from './call-interface'; -import { SecureConnector } from './channel-credentials'; +import { SecureConnector, SecureConnectResult } from './channel-credentials'; import { ChannelOptions } from './channel-options'; import { ChannelzCallTracker, @@ -651,7 +651,7 @@ export class Http2SubchannelConnector implements SubchannelConnector { } private createSession( - underlyingConnection: Socket, + secureConnectResult: SecureConnectResult, address: SubchannelAddress, options: ChannelOptions ): Promise { @@ -669,10 +669,11 @@ export class Http2SubchannelConnector implements SubchannelConnector { remoteName = uriToString(parsedTarget); } } + const scheme = secureConnectResult.secure ? 'https' : 'http'; const targetPath = getDefaultAuthority(realTarget); - const session = http2.connect(`http://${targetPath}`, { + const session = http2.connect(`${scheme}://${targetPath}`, { createConnection: (authority, option) => { - return underlyingConnection; + return secureConnectResult.socket; }, settings: { initialWindowSize: @@ -736,14 +737,14 @@ export class Http2SubchannelConnector implements SubchannelConnector { return Promise.reject(); } let tcpConnection: net.Socket | null = null; - let secureConnection: net.Socket | null = null; + let secureConnectResult: SecureConnectResult | null = null; try { tcpConnection = await this.tcpConnect(address, options); - secureConnection = await secureConnector.connect(tcpConnection); - return this.createSession(secureConnection, address, options); + secureConnectResult = await secureConnector.connect(tcpConnection); + return this.createSession(secureConnectResult, address, options); } catch (e) { tcpConnection?.destroy(); - secureConnection?.destroy(); + secureConnectResult?.socket.destroy(); throw e; } }