grpc-js: Connect with https scheme when using TLS

This commit is contained in:
Michael Lumish 2025-01-30 14:03:06 -08:00
parent 023b0862f0
commit 9569f20fc6
3 changed files with 31 additions and 16 deletions

View File

@ -63,8 +63,13 @@ export interface VerifyOptions {
rejectUnauthorized?: boolean;
}
export interface SecureConnectResult {
socket: Socket;
secure: boolean;
}
export interface SecureConnector {
connect(socket: Socket): Promise<Socket>;
connect(socket: Socket): Promise<SecureConnectResult>;
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<Socket> {
connect(socket: Socket): Promise<SecureConnectResult> {
const tlsConnectOptions: ConnectionOptions = {
socket: socket,
...this.connectionOptions
};
return new Promise<Socket>((resolve, reject) => {
return new Promise<SecureConnectResult>((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<Socket> {
connect(socket: Socket): Promise<SecureConnectResult> {
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);

View File

@ -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';

View File

@ -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<Http2Transport> {
@ -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;
}
}