Skip to content

Commit

Permalink
[cli] wait for extension host to activate cli server
Browse files Browse the repository at this point in the history
  • Loading branch information
akosyakov committed Jun 28, 2021
1 parent 19c309e commit 6b591c2
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 40 deletions.
6 changes: 3 additions & 3 deletions extensions/gitpod/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -952,9 +952,9 @@ export async function activate(context: vscode.ExtensionContext) {
const vscodeIpcHookCli = process.env['VSCODE_IPC_HOOK_CLI'];
if (vscodeIpcHookCli) {
function updateIpcHookCli(): void {
if (vscode.window.state.focused) {
codeServerConnection.sendNotification('setActiveCliIpcHook', vscodeIpcHookCli!);
}
codeServerConnection.sendNotification('setActiveCliIpcHook',
vscode.window.state.focused ? vscodeIpcHookCli : undefined
);
}
updateIpcHookCli();
context.subscriptions.push(vscode.window.onDidChangeWindowState(() => updateIpcHookCli()));
Expand Down
58 changes: 33 additions & 25 deletions src/vs/gitpod/node/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,32 +178,40 @@ async function sendCommand(command: PipeCommand): Promise<string> {
port = Number(process.env.GITPOD_THEIA_PORT);
}
const http = await import('http');
return new Promise<string>((resolve, reject) => {
const req = http.request({
hostname: 'localhost',
port,
protocol: 'http:',
path: '/cli',
method: 'POST',
timeout: 5000
}, res => {
const chunks: string[] = [];
res.setEncoding('utf8');
res.on('data', d => chunks.push(d));
res.on('end', () => {
const result = chunks.join('');
if (res.statusCode !== 200) {
reject(new Error(`Bad status code: ${res.statusCode}: ${result}`));
} else {
resolve(result);
}
while (true) {
try {
return await new Promise<string>((resolve, reject) => {
const req = http.request({
hostname: 'localhost',
port,
protocol: 'http:',
path: '/cli',
method: 'POST'
}, res => {
const chunks: string[] = [];
res.setEncoding('utf8');
res.on('data', d => chunks.push(d));
res.on('end', () => {
const result = chunks.join('');
if (res.statusCode !== 200) {
reject(new Error(`Bad status code: ${res.statusCode}: ${result}`));
} else {
resolve(result);
}
});
});
req.on('error', err => reject(err));
req.write(JSON.stringify(command));
req.end();
});
});

req.on('error', err => reject(err));
req.write(JSON.stringify(command));
req.end();
});
} catch (e) {
// Code Server is not running yet, let's try again
if (e.code !== 'ECONNREFUSED') {
throw e;
}
await new Promise(resolve => setTimeout(resolve, 500));
}
}
}

function eventuallyExit(code: number): void {
Expand Down
6 changes: 4 additions & 2 deletions src/vs/gitpod/node/server-extension-host-connection.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ export type setActiveCliIpcHookMethod = 'setActiveCliIpcHook';
export interface ServerExtensionHostConnection {
sendRequest(method: validateExtensionsMethod, param: ValidateExtensionsParam, token: rpc.CancellationToken): Promise<ValidateExtensionsResult>;
onRequest(method: validateExtensionsMethod, handler: (param: ValidateExtensionsParam, token: rpc.CancellationToken) => Promise<ValidateExtensionsResult>): void
sendNotification(method: setActiveCliIpcHookMethod, cliIpcHook: string): void;
onNotification(method: setActiveCliIpcHookMethod, handler: (cliIpcHook: string) => void): void;
sendNotification(method: setActiveCliIpcHookMethod, cliIpcHook: string | undefined): void;
onNotification(method: setActiveCliIpcHookMethod, handler: (cliIpcHook: string | undefined) => void): void;
listen(): void;
dispose(): void;
onClose: rpc.Event<void>;
onDispose: rpc.Event<void>;
}
52 changes: 42 additions & 10 deletions src/vs/gitpod/node/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,20 @@ async function main(): Promise<void> {

const clients = new Map<string, Client>();
let activeCliIpcHook: string | undefined;
const didChangeActiveCliIpcHookEmitter = new Emitter<void>();
function withActiveCliIpcHook(cb: (activeCliIpcHook: string) => void): IDisposable {
if (activeCliIpcHook) {
cb(activeCliIpcHook);
return { dispose: () => { } };
}
const listener = didChangeActiveCliIpcHookEmitter.event(() => {
if (activeCliIpcHook) {
listener.dispose();
cb(activeCliIpcHook);
}
});
return listener;
}

const server = http.createServer(async (req, res) => {
if (!req.url) {
Expand All @@ -707,18 +721,18 @@ async function main(): Promise<void> {
}

if (pathname === '/cli') {
if (!activeCliIpcHook) {
return serveError(req, res, 404, 'Not found.');
}
if (req.method !== 'POST') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
return res.end(activeCliIpcHook);
}
req.pipe(http.request({
socketPath: activeCliIpcHook,
method: req.method,
headers: req.headers
}, res2 => res2.pipe(res)));
const listener = withActiveCliIpcHook(activeCliIpcHook =>
req.pipe(http.request({
socketPath: activeCliIpcHook,
method: req.method,
headers: req.headers
}, res2 => res2.pipe(res)))
);
req.on('close', () => listener.dispose());
return;
}

Expand Down Expand Up @@ -1025,9 +1039,27 @@ async function main(): Promise<void> {
warn: msg => logService.warn(msg)
}
) as any as ServerExtensionHostConnection;
extensionHostConnection.onNotification('setActiveCliIpcHook', (cliIpcHook: string) => {
activeCliIpcHook = cliIpcHook;

let extensionHostCliIpcHook: string | undefined;
function cleanActiveCliIpcHook(): void {
if (!activeCliIpcHook || activeCliIpcHook !== extensionHostCliIpcHook) {
return;
}
activeCliIpcHook = undefined;
didChangeActiveCliIpcHookEmitter.fire(undefined);
}
extensionHostConnection.onClose(cleanActiveCliIpcHook);
extensionHostConnection.onDispose(cleanActiveCliIpcHook);
extensionHostConnection.onNotification('setActiveCliIpcHook', (cliIpcHook: string | undefined) => {
if (!cliIpcHook) {
cleanActiveCliIpcHook();
} else if (activeCliIpcHook !== cliIpcHook) {
activeCliIpcHook = cliIpcHook;
didChangeActiveCliIpcHookEmitter.fire(undefined);
}
extensionHostCliIpcHook = cliIpcHook;
});

extensionHostConnection.onRequest('validateExtensions', async (param, token) => {
const links = new Set<string>();
const extensions = new Set<string>();
Expand Down

0 comments on commit 6b591c2

Please sign in to comment.