remote cli: improve error handling over the pipe

This commit is contained in:
Martin Aeschlimann 2022-02-02 14:21:57 +01:00
parent e6a6c81bd1
commit 5abc4e0071
No known key found for this signature in database
GPG key ID: 2609A01E695523E3
2 changed files with 78 additions and 73 deletions

View file

@ -105,7 +105,7 @@ export function main(desc: ProductDescription, args: string[]): void {
options['openExternal'] = { type: 'boolean' };
}
const errorReporter : ErrorReporter = {
const errorReporter: ErrorReporter = {
onMultipleValues: (id: string, usedValue: string) => {
console.error(`Option ${id} can only be defined once. Using value ${usedValue}.`);
},
@ -261,6 +261,8 @@ export function main(desc: ProductDescription, args: string[]): void {
type: 'status'
}, verbose).then((res: string) => {
console.log(res);
}).catch(e => {
console.error('Error when requesting status:', e);
});
return;
}
@ -274,6 +276,8 @@ export function main(desc: ProductDescription, args: string[]): void {
force: parsedArgs['force']
}, verbose).then((res: string) => {
console.log(res);
}).catch(e => {
console.error('Error when invoking the extension management command:', e);
});
return;
}
@ -298,7 +302,9 @@ export function main(desc: ProductDescription, args: string[]): void {
forceNewWindow: parsedArgs['new-window'],
waitMarkerFilePath,
remoteAuthority: remote
}, verbose);
}, verbose).catch(e => {
console.error('Error when invoking the open command:', e);
});
if (waitMarkerFilePath) {
waitForFileDeleted(waitMarkerFilePath);
@ -329,7 +335,9 @@ function openInBrowser(args: string[], verbose: boolean) {
sendToPipe({
type: 'openExternal',
uris
}, verbose);
}, verbose).catch(e => {
console.error('Error when invoking the open external command:', e);
});
}
}
@ -337,7 +345,7 @@ function sendToPipe(args: PipeCommand, verbose: boolean): Promise<any> {
if (verbose) {
console.log(JSON.stringify(args, null, ' '));
}
return new Promise<string>(resolve => {
return new Promise<string>((resolve, reject) => {
const message = JSON.stringify(args);
if (!cliPipe) {
console.log('Message ' + message);
@ -348,10 +356,19 @@ function sendToPipe(args: PipeCommand, verbose: boolean): Promise<any> {
const opts: _http.RequestOptions = {
socketPath: cliPipe,
path: '/',
method: 'POST'
method: 'POST',
headers: {
'content-type': 'application/json',
'accept': 'application/json'
}
};
const req = _http.request(opts, res => {
if (res.headers['content-type'] !== 'application/json') {
reject('Error in response: Invalid content type: Expected \'application/json\', is: ' + res.headers['content-type']);
return;
}
const chunks: string[] = [];
res.setEncoding('utf8');
res.on('data', chunk => {
@ -359,7 +376,17 @@ function sendToPipe(args: PipeCommand, verbose: boolean): Promise<any> {
});
res.on('error', (err) => fatal('Error in response.', err));
res.on('end', () => {
resolve(chunks.join(''));
const content = chunks.join('');
try {
const obj = JSON.parse(content);
if (res.statusCode === 200) {
resolve(obj);
} else {
reject(obj);
}
} catch (e) {
reject('Error in response: Unable to parse response as JSON: ' + content);
}
});
});

View file

@ -79,39 +79,45 @@ export class CLIServerBase {
}
private onRequest(req: http.IncomingMessage, res: http.ServerResponse): void {
const sendResponse = (statusCode: number, returnObj: any) => {
res.writeHead(statusCode, { 'content-type': 'application/json' });
res.end(JSON.stringify(returnObj), (err?: any) => err && this.logService.error(err));
};
const chunks: string[] = [];
req.setEncoding('utf8');
req.on('data', (d: string) => chunks.push(d));
req.on('end', () => {
const data: PipeCommand | any = JSON.parse(chunks.join(''));
switch (data.type) {
case 'open':
this.open(data, res);
break;
case 'openExternal':
this.openExternal(data, res);
break;
case 'status':
this.getStatus(data, res);
break;
case 'extensionManagement':
this.manageExtensions(data, res)
.catch(this.logService.error);
break;
default:
res.writeHead(404);
res.write(`Unknown message type: ${data.type}`, err => {
if (err) {
this.logService.error(err);
}
});
res.end();
break;
req.on('end', async () => {
try {
const data: PipeCommand | any = JSON.parse(chunks.join(''));
let returnObj;
switch (data.type) {
case 'open':
returnObj = await this.open(data);
break;
case 'openExternal':
returnObj = await this.openExternal(data);
break;
case 'status':
returnObj = await this.getStatus(data);
break;
case 'extensionManagement':
returnObj = await this.manageExtensions(data);
break;
default:
sendResponse(404, `Unknown message type: ${data.type}`);
break;
}
sendResponse(200, returnObj);
} catch (e) {
const message = e instanceof Error ? e.message : JSON.stringify(e);
sendResponse(500, message);
this.logService.error('Error while processing pipe request', e);
}
});
}
private open(data: OpenCommandPipeArgs, res: http.ServerResponse) {
private async open(data: OpenCommandPipeArgs): Promise<string> {
const { fileURIs, folderURIs, forceNewWindow, diffMode, addMode, forceReuseWindow, gotoLineMode, waitMarkerFilePath, remoteAuthority } = data;
const urisToOpen: IWindowOpenable[] = [];
if (Array.isArray(folderURIs)) {
@ -141,58 +147,30 @@ export class CLIServerBase {
const windowOpenArgs: IOpenWindowOptions = { forceNewWindow, diffMode, addMode, gotoLineMode, forceReuseWindow, preferNewWindow, waitMarkerFileURI, remoteAuthority };
this._commands.executeCommand('_remoteCLI.windowOpen', urisToOpen, windowOpenArgs);
res.writeHead(200);
res.end();
return '';
}
private async openExternal(data: OpenExternalCommandPipeArgs, res: http.ServerResponse) {
private async openExternal(data: OpenExternalCommandPipeArgs): Promise<any> {
for (const uriString of data.uris) {
const uri = URI.parse(uriString);
const urioOpen = uri.scheme === 'file' ? uri : uriString; // workaround for #112577
await this._commands.executeCommand('_remoteCLI.openExternal', urioOpen);
}
res.writeHead(200);
res.end();
}
private async manageExtensions(data: ExtensionManagementPipeArgs, res: http.ServerResponse) {
try {
const toExtOrVSIX = (inputs: string[] | undefined) => inputs?.map(input => /\.vsix$/i.test(input) ? URI.parse(input) : input);
const commandArgs = {
list: data.list,
install: toExtOrVSIX(data.install),
uninstall: toExtOrVSIX(data.uninstall),
force: data.force
};
const output = await this._commands.executeCommand('_remoteCLI.manageExtensions', commandArgs);
res.writeHead(200);
res.write(output);
} catch (err) {
res.writeHead(500);
res.write(String(err), err => {
if (err) {
this.logService.error(err);
}
});
}
res.end();
private async manageExtensions(data: ExtensionManagementPipeArgs): Promise<any> {
const toExtOrVSIX = (inputs: string[] | undefined) => inputs?.map(input => /\.vsix$/i.test(input) ? URI.parse(input) : input);
const commandArgs = {
list: data.list,
install: toExtOrVSIX(data.install),
uninstall: toExtOrVSIX(data.uninstall),
force: data.force
};
return await this._commands.executeCommand('_remoteCLI.manageExtensions', commandArgs);
}
private async getStatus(data: StatusPipeArgs, res: http.ServerResponse) {
try {
const status = await this._commands.executeCommand('_remoteCLI.getSystemStatus');
res.writeHead(200);
res.write(status);
res.end();
} catch (err) {
res.writeHead(500);
res.write(String(err), err => {
if (err) {
this.logService.error(err);
}
});
res.end();
}
private async getStatus(data: StatusPipeArgs) {
return await this._commands.executeCommand('_remoteCLI.getSystemStatus');
}
dispose(): void {