diff --git a/extensions/vscode-test-resolver/package.json b/extensions/vscode-test-resolver/package.json index 6ae381672b0..167df92db2d 100644 --- a/extensions/vscode-test-resolver/package.json +++ b/extensions/vscode-test-resolver/package.json @@ -79,6 +79,11 @@ "description": "If set, the resolver will fail. Use ths setting for testing the failure of a resolver.", "type": "boolean", "default": false + }, + "testresolver.pause": { + "description": "If set, connection is paused", + "type": "boolean", + "default": false } } } diff --git a/extensions/vscode-test-resolver/src/extension.ts b/extensions/vscode-test-resolver/src/extension.ts index 91bcd03c9a0..1c2236ddc58 100644 --- a/extensions/vscode-test-resolver/src/extension.ts +++ b/extensions/vscode-test-resolver/src/extension.ts @@ -8,9 +8,9 @@ import * as cp from 'child_process'; import * as path from 'path'; import * as fs from 'fs'; import * as os from 'os'; +import * as net from 'net'; import { downloadAndUnzipVSCodeServer } from './download'; -let startPromise: Thenable | undefined = void 0; let extHostProcess: cp.ChildProcess | undefined; const enum CharCode { Backspace = 8, @@ -22,7 +22,7 @@ let outputChannel: vscode.OutputChannel; export function activate(context: vscode.ExtensionContext) { function doResolve(_authority: string, progress: vscode.Progress<{ message?: string; increment?: number }>): Promise { - return new Promise(async (res, rej) => { + const serverPromise = new Promise(async (res, rej) => { progress.report({ message: 'Starting Test Resolver' }); outputChannel = vscode.window.createOutputChannel('TestResolver'); @@ -60,7 +60,7 @@ export function activate(context: vscode.ExtensionContext) { } } } - const delay = vscode.workspace.getConfiguration('testresolver').get('startupDelay'); + const delay = getConfiguration('startupDelay'); if (typeof delay === 'number') { let remaining = Math.ceil(delay); outputChannel.append(`Delaying startup by ${remaining} seconds (configured by "testresolver.startupDelay").`); @@ -71,7 +71,7 @@ export function activate(context: vscode.ExtensionContext) { } } - if (vscode.workspace.getConfiguration('testresolver').get('startupError') === true) { + if (getConfiguration('startupError') === true) { processError('Test Resolver failed for testing purposes (configured by "testresolver.startupError").'); return; } @@ -114,18 +114,99 @@ export function activate(context: vscode.ExtensionContext) { } }); }); + return serverPromise.then(serverAddr => { + return new Promise(async (res, _rej) => { + const proxyServer = net.createServer(proxySocket => { + outputChannel.appendLine(`Proxy connection accepted`); + let remoteReady = true, localReady = true; + const remoteSocket = net.createConnection({ port: serverAddr.port }); + + let isDisconnected = getConfiguration('pause') === true; + vscode.workspace.onDidChangeConfiguration(_ => { + let newIsDisconnected = getConfiguration('pause') === true; + if (isDisconnected !== newIsDisconnected) { + outputChannel.appendLine(`Connection state: ${newIsDisconnected ? 'open' : 'paused'}`); + isDisconnected = newIsDisconnected; + if (!isDisconnected) { + outputChannel.appendLine(`Resume remote and proxy sockets.`); + if (remoteSocket.isPaused() && localReady) { + remoteSocket.resume(); + } + if (proxySocket.isPaused() && remoteReady) { + proxySocket.resume(); + } + } else { + outputChannel.appendLine(`Pausing remote and proxy sockets.`); + if (!remoteSocket.isPaused()) { + remoteSocket.pause(); + } + if (!proxySocket.isPaused()) { + proxySocket.pause(); + } + } + } + }); + + proxySocket.on('data', (data) => { + remoteReady = remoteSocket.write(data); + if (!remoteReady) { + proxySocket.pause(); + } + }); + remoteSocket.on('data', (data) => { + localReady = proxySocket.write(data); + if (!localReady) { + remoteSocket.pause(); + } + }); + proxySocket.on('drain', () => { + localReady = true; + if (!isDisconnected) { + remoteSocket.resume(); + } + }); + remoteSocket.on('drain', () => { + remoteReady = true; + if (!isDisconnected) { + proxySocket.resume(); + } + }); + proxySocket.on('close', () => { + outputChannel.appendLine(`Proxy socket closed, closing remote socket.`); + remoteSocket.end(); + }); + remoteSocket.on('close', () => { + outputChannel.appendLine(`Remote socket closed, closing proxy socket.`); + proxySocket.end(); + }); + context.subscriptions.push({ + dispose: () => { + proxySocket.end(); + remoteSocket.end(); + } + }); + }); + proxyServer.listen(0, () => { + const port = (proxyServer.address()).port; + outputChannel.appendLine(`Going through proxy at port ${port}`); + res({ host: '127.0.0.1', port }); + }); + context.subscriptions.push({ + dispose: () => { + proxyServer.close(); + } + }); + }); + }); } vscode.workspace.registerRemoteAuthorityResolver('test', { resolve(_authority: string): Thenable { - if (!startPromise) { - startPromise = vscode.window.withProgress({ - location: vscode.ProgressLocation.Notification, - title: 'Open TestResolver Remote ([details](command:remote-testresolver.showLog))', - cancellable: false - }, (progress) => doResolve(_authority, progress)); - } - return startPromise; + return vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: 'Open TestResolver Remote ([details](command:remote-testresolver.showLog))', + cancellable: false + }, (progress) => doResolve(_authority, progress)); } }); @@ -196,3 +277,7 @@ function sleep(ms: number): Promise { setTimeout(resolve, ms); }); } + +function getConfiguration(id: string): T | undefined { + return vscode.workspace.getConfiguration('testresolver').get(id); +}