diff --git a/extensions/vscode-test-resolver/package.json b/extensions/vscode-test-resolver/package.json index 495d15fc7ba..d3368339ee7 100644 --- a/extensions/vscode-test-resolver/package.json +++ b/extensions/vscode-test-resolver/package.json @@ -19,7 +19,8 @@ "onCommand:vscode-testresolver.newWindow", "onCommand:vscode-testresolver.newWindowWithError", "onCommand:vscode-testresolver.showLog", - "onCommand:vscode-testresolver.openTunnel" + "onCommand:vscode-testresolver.openTunnel", + "onCommand:vscode-testresolver.startRemoteServer" ], "main": "./out/extension", "devDependencies": { @@ -55,12 +56,27 @@ "command": "vscode-testresolver.killServerAndTriggerHandledError" }, { - "title": "Open Tunnel 100", + "title": "Open Tunnel...", "category": "Remote-TestResolver", "command": "vscode-testresolver.openTunnel" + }, + { + "title": "Open Remote Server...", + "category": "Remote-TestResolver", + "command": "vscode-testresolver.startRemoteServer" } ], "menus": { + "commandPalette": [ + { + "command": "vscode-testresolver.openTunnel", + "when": "remoteName == test" + }, + { + "command": "vscode-testresolver.startRemoteServer", + "when": "remoteName == test" + } + ], "statusBar/windowIndicator": [ { "command": "vscode-testresolver.newWindow", @@ -76,6 +92,16 @@ "command": "vscode-testresolver.newWindow", "when": "remoteName == test", "group": "1_remote_testresolver_open@1" + }, + { + "command": "vscode-testresolver.openTunnel", + "when": "remoteName == test", + "group": "1_remote_testresolver_open@4" + }, + { + "command": "vscode-testresolver.startRemoteServer", + "when": "remoteName == test", + "group": "1_remote_testresolver_open@5" } ] }, diff --git a/extensions/vscode-test-resolver/src/extension.ts b/extensions/vscode-test-resolver/src/extension.ts index 2c241aa19a4..b1d9bdef1a3 100644 --- a/extensions/vscode-test-resolver/src/extension.ts +++ b/extensions/vscode-test-resolver/src/extension.ts @@ -9,6 +9,7 @@ import * as path from 'path'; import * as fs from 'fs'; import * as os from 'os'; import * as net from 'net'; +import * as http from 'http'; import { downloadAndUnzipVSCodeServer } from './download'; import { terminateProcess } from './util/processes'; @@ -245,14 +246,35 @@ export function activate(context: vscode.ExtensionContext) { outputChannel.show(); } })); - context.subscriptions.push(vscode.commands.registerCommand('vscode-testresolver.openTunnel', () => { - vscode.workspace.openTunnel({ - remoteAddress: { - host: 'localhost', - port: 100 - }, - localAddressPort: 100 + + context.subscriptions.push(vscode.commands.registerCommand('vscode-testresolver.openTunnel', async () => { + const result = await vscode.window.showInputBox({ + prompt: 'Enter the remote port for the tunnel', + value: '5000', + validateInput: input => /^[\d]+$/.test(input) ? undefined : 'Not a valid number' }); + if (result) { + const port = Number.parseInt(result); + vscode.workspace.openTunnel({ + remoteAddress: { + host: 'localhost', + port: port + }, + localAddressPort: port + 1 + }); + } + + })); + context.subscriptions.push(vscode.commands.registerCommand('vscode-testresolver.startRemoteServer', async () => { + const result = await vscode.window.showInputBox({ + prompt: 'Enter the port for the remote server', + value: '5000', + validateInput: input => /^[\d]+$/.test(input) ? undefined : 'Not a valid number' + }); + if (result) { + runHTTPTestServer(Number.parseInt(result)); + } + })); vscode.commands.executeCommand('setContext', 'forwardedPortsViewEnabled', true); } @@ -316,9 +338,14 @@ function getConfiguration(id: string): T | undefined { return vscode.workspace.getConfiguration('testresolver').get(id); } +const remoteServers: number[] = []; + function tunnelFactory(tunnelOptions: vscode.TunnelOptions): Promise | undefined { + outputChannel.appendLine(`Tunnel factory request: Remote ${tunnelOptions.remoteAddress.port} -> local ${tunnelOptions.localAddressPort}`); + let remotePort = tunnelOptions.remoteAddress.port; - if (remotePort === 100) { + // only forward ports from our own 'remote' servers + if (remoteServers.includes(remotePort)) { return createTunnelService(); } return undefined; @@ -344,14 +371,13 @@ function tunnelFactory(tunnelOptions: vscode.TunnelOptions): Promise { return new Promise((res, _rej) => { const proxyServer = net.createServer(proxySocket => { - outputChannel.appendLine(`Connection accepted`); const remoteSocket = net.createConnection({ host: tunnelOptions.remoteAddress.host, port: tunnelOptions.remoteAddress.port }); remoteSocket.pipe(proxySocket); proxySocket.pipe(remoteSocket); }); proxyServer.listen(tunnelOptions.localAddressPort === undefined ? 0 : tunnelOptions.localAddressPort, () => { const localPort = (proxyServer.address()).port; - console.log(`New test resolver tunnel service: Remote ${tunnelOptions.remoteAddress.port} -> local ${localPort}`); + outputChannel.appendLine(`New test resolver tunnel service: Remote ${tunnelOptions.remoteAddress.port} -> local ${localPort}`); const tunnel = newTunnel({ host: 'localhost', port: localPort }); tunnel.onDidDispose(() => proxyServer.close()); res(tunnel); @@ -359,3 +385,24 @@ function tunnelFactory(tunnelOptions: vscode.TunnelOptions): Promise { + res.writeHead(200); + res.end(`Hello, World from test server running on port ${port}!`); + }); + remoteServers.push(port); + server.listen(port); + const message = `Opened HTTP server on http://localhost:${port}`; + console.log(message); + outputChannel.appendLine(message); + return { + dispose: () => { + server.close(); + const index = remoteServers.indexOf(port); + if (index !== -1) { + remoteServers.splice(index, 1); + } + } + }; +}