fix hanging stdio servers (#1200)

Co-authored-by: Johnny Santos <johnnyadsantos@gmail.com>
Co-authored-by: TomasRup <tomasr@wix.com>
This commit is contained in:
Matt 2025-12-01 14:56:48 +00:00 • committed by GitHub
commit ace9b62d3c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -91,7 +91,6 @@ export function getDefaultEnvironment(): Record<string, string> {
*/
export class StdioClientTransport implements Transport {
private _process?: ChildProcess;
private _abortController: AbortController = new AbortController();
private _readBuffer: ReadBuffer = new ReadBuffer();
private _serverParams: StdioServerParameters;
private _stderrStream: PassThrough | null = null;
@ -126,18 +125,11 @@ export class StdioClientTransport implements Transport {
},
stdio: ['pipe', 'pipe', this._serverParams.stderr ?? 'inherit'],
shell: false,
signal: this._abortController.signal,
windowsHide: process.platform === 'win32' && isElectron(),
cwd: this._serverParams.cwd
});
this._process.on('error', error => {
if (error.name === 'AbortError') {
// Expected when close() is called.
this.onclose?.();
return;
}
reject(error);
this.onerror?.(error);
});
@ -210,8 +202,43 @@ export class StdioClientTransport implements Transport {
}
async close(): Promise<void> {
this._abortController.abort();
this._process = undefined;
if (this._process) {
const processToClose = this._process;
this._process = undefined;
const closePromise = new Promise<void>(resolve => {
processToClose.once('close', () => {
resolve();
});
});
try {
processToClose.stdin?.end();
} catch {
// ignore
}
await Promise.race([closePromise, new Promise(resolve => setTimeout(resolve, 2_000).unref())]);
if (processToClose.exitCode === null) {
try {
processToClose.kill('SIGTERM');
} catch {
// ignore
}
await Promise.race([closePromise, new Promise(resolve => setTimeout(resolve, 2_000).unref())]);
}
if (processToClose.exitCode === null) {
try {
processToClose.kill('SIGKILL');
} catch {
// ignore
}
}
}
this._readBuffer.clear();
}