Wait for secure connectors to be usable before TCP connect

This commit is contained in:
Michael Lumish 2025-02-19 17:22:59 -08:00
parent 1fe3f7406c
commit e883425ef3
2 changed files with 36 additions and 7 deletions

View File

@ -70,6 +70,7 @@ export interface SecureConnectResult {
export interface SecureConnector {
connect(socket: Socket): Promise<SecureConnectResult>;
waitForReady(): Promise<void>;
getCallCredentials(): CallCredentials;
destroy(): void;
}
@ -188,6 +189,9 @@ class InsecureChannelCredentialsImpl extends ChannelCredentials {
secure: false
});
},
waitForReady: () => {
return Promise.resolve();
},
getCallCredentials: () => {
return callCredentials ?? CallCredentials.createEmpty();
},
@ -276,6 +280,9 @@ class SecureConnectorImpl implements SecureConnector {
});
});
}
waitForReady(): Promise<void> {
return Promise.resolve();
}
getCallCredentials(): CallCredentials {
return this.callCredentials;
}
@ -333,17 +340,28 @@ class CertificateProviderChannelCredentialsImpl extends ChannelCredentials {
connect(socket: Socket): Promise<SecureConnectResult> {
return new Promise(async (resolve, reject) => {
const secureContext = await this.parent.getSecureContext();
const secureContext = this.parent.getLatestSecureContext();
if (!secureContext) {
reject(new Error('Failed to load credentials'));
return;
}
if (socket.closed) {
reject(new Error('Socket closed while loading credentials'));
}
const connnectionOptions = getConnectionOptions(secureContext, this.parent.verifyOptions, this.channelTarget, this.options);
const tlsConnectOptions: ConnectionOptions = {
socket: socket,
...connnectionOptions
}
const closeCallback = () => {
reject(new Error('Socket closed'));
};
const errorCallback = (error: Error) => {
reject(error);
}
const tlsSocket = tlsConnect(tlsConnectOptions, () => {
tlsSocket.removeListener('close', closeCallback);
tlsSocket.removeListener('error', errorCallback);
if (!tlsSocket.authorized) {
reject(tlsSocket.authorizationError);
return;
@ -353,12 +371,15 @@ class CertificateProviderChannelCredentialsImpl extends ChannelCredentials {
secure: true
});
});
tlsSocket.on('error', (error: Error) => {
reject(error);
});
tlsSocket.once('close', closeCallback);
tlsSocket.once('error', errorCallback);
});
}
async waitForReady(): Promise<void> {
await this.parent.getSecureContext();
}
getCallCredentials(): CallCredentials {
return this.callCredentials;
}

View File

@ -717,12 +717,19 @@ export class Http2SubchannelConnector implements SubchannelConnector {
return proxiedSocket;
} else {
return new Promise<Socket>((resolve, reject) => {
const closeCallback = () => {
reject(new Error('Socket closed'));
};
const errorCallback = (error: Error) => {
reject(error);
}
const socket = net.connect(address, () => {
socket.removeListener('close', closeCallback);
socket.removeListener('error', errorCallback);
resolve(socket);
});
socket.once('error', (error) => {
reject(error);
});
socket.once('close', closeCallback);
socket.once('error', errorCallback);
});
}
});
@ -740,6 +747,7 @@ export class Http2SubchannelConnector implements SubchannelConnector {
let secureConnectResult: SecureConnectResult | null = null;
const addressString = subchannelAddressToString(address);
try {
await secureConnector.waitForReady();
tcpConnection = await this.tcpConnect(address, options);
this.trace(addressString + ' ' + 'Established TCP connection');
secureConnectResult = await secureConnector.connect(tcpConnection);