Skip to content

Commit

Permalink
Wait for secure connectors to be usable before TCP connect
Browse files Browse the repository at this point in the history
  • Loading branch information
murgatroid99 committed Feb 20, 2025
1 parent 1fe3f74 commit e883425
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 7 deletions.
29 changes: 25 additions & 4 deletions packages/grpc-js/src/channel-credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export interface SecureConnectResult {

export interface SecureConnector {
connect(socket: Socket): Promise<SecureConnectResult>;
waitForReady(): Promise<void>;
getCallCredentials(): CallCredentials;
destroy(): void;
}
Expand Down Expand Up @@ -188,6 +189,9 @@ class InsecureChannelCredentialsImpl extends ChannelCredentials {
secure: false
});
},
waitForReady: () => {
return Promise.resolve();
},
getCallCredentials: () => {
return callCredentials ?? CallCredentials.createEmpty();
},
Expand Down Expand Up @@ -276,6 +280,9 @@ class SecureConnectorImpl implements SecureConnector {
});
});
}
waitForReady(): Promise<void> {
return Promise.resolve();
}
getCallCredentials(): CallCredentials {
return this.callCredentials;
}
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down
14 changes: 11 additions & 3 deletions packages/grpc-js/src/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
}
});
Expand All @@ -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);
Expand Down

0 comments on commit e883425

Please sign in to comment.