abstractRemoteAgentService.ts 8.1 KB
Newer Older
A
Alex Dima 已提交
1 2 3 4 5 6 7 8 9 10
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import * as nls from 'vs/nls';
import { Disposable } from 'vs/base/common/lifecycle';
import { IChannel, IServerChannel, getDelayedChannel } from 'vs/base/parts/ipc/common/ipc';
import { Client } from 'vs/base/parts/ipc/common/ipc.net';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
A
Alex Dima 已提交
11
import { connectRemoteAgentManagement, IConnectionOptions, ISocketFactory, PersistenConnectionEvent } from 'vs/platform/remote/common/remoteAgentConnection';
A
Alex Dima 已提交
12
import { IRemoteAgentConnection, IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
A
Tweaks  
Alex Dima 已提交
13
import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError } from 'vs/platform/remote/common/remoteAuthorityResolver';
A
Alex Dima 已提交
14 15 16 17 18 19
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { RemoteAgentConnectionContext, IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment';
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions';
import { Registry } from 'vs/platform/registry/common/platform';
import { RemoteExtensionEnvironmentChannelClient } from 'vs/workbench/services/remote/common/remoteAgentEnvironmentChannel';
import { INotificationService } from 'vs/platform/notification/common/notification';
20
import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics';
A
Alex Dima 已提交
21
import { Emitter } from 'vs/base/common/event';
I
isidor 已提交
22
import { ISignService } from 'vs/platform/sign/common/sign';
23
import { ILogService } from 'vs/platform/log/common/log';
24
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
25
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
A
Alex Dima 已提交
26

27
export abstract class AbstractRemoteAgentService extends Disposable implements IRemoteAgentService {
A
Alex Dima 已提交
28

29
	declare readonly _serviceBrand: undefined;
A
Alex Dima 已提交
30

31
	public readonly socketFactory: ISocketFactory;
A
Alex Dima 已提交
32 33 34
	private _environment: Promise<IRemoteAgentEnvironment | null> | null;

	constructor(
35
		socketFactory: ISocketFactory,
36 37
		@IEnvironmentService protected readonly _environmentService: IEnvironmentService,
		@IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService
A
Alex Dima 已提交
38 39
	) {
		super();
40
		this.socketFactory = socketFactory;
A
Alex Dima 已提交
41
		this._environment = null;
A
Alex Dima 已提交
42 43 44 45
	}

	abstract getConnection(): IRemoteAgentConnection | null;

A
Alex Dima 已提交
46 47 48 49 50
	getEnvironment(): Promise<IRemoteAgentEnvironment | null> {
		return this.getRawEnvironment().then(undefined, () => null);
	}

	getRawEnvironment(): Promise<IRemoteAgentEnvironment | null> {
A
Alex Dima 已提交
51
		if (!this._environment) {
52
			this._environment = this._withChannel(
53
				async (channel, connection) => {
54
					const env = await RemoteExtensionEnvironmentChannelClient.getEnvironmentData(channel, connection.remoteAuthority);
55 56 57
					this._remoteAuthorityResolverService._setAuthorityConnectionToken(connection.remoteAuthority, env.connectionToken);
					return env;
				},
58 59
				null
			);
A
Alex Dima 已提交
60
		}
A
Alex Dima 已提交
61
		return this._environment;
A
Alex Dima 已提交
62
	}
R
Rachel Macfarlane 已提交
63

64 65 66 67 68 69 70
	scanExtensions(skipExtensions: ExtensionIdentifier[] = []): Promise<IExtensionDescription[]> {
		return this._withChannel(
			(channel, connection) => RemoteExtensionEnvironmentChannelClient.scanExtensions(channel, connection.remoteAuthority, this._environmentService.extensionDevelopmentLocationURI, skipExtensions),
			[]
		).then(undefined, () => []);
	}

R
Rachel Macfarlane 已提交
71
	getDiagnosticInfo(options: IDiagnosticInfoOptions): Promise<IDiagnosticInfo | undefined> {
72 73 74 75
		return this._withChannel(
			channel => RemoteExtensionEnvironmentChannelClient.getDiagnosticInfo(channel, options),
			undefined
		);
R
Rob Lourens 已提交
76 77 78
	}

	disableTelemetry(): Promise<void> {
79 80 81 82
		return this._withChannel(
			channel => RemoteExtensionEnvironmentChannelClient.disableTelemetry(channel),
			undefined
		);
R
Rachel Macfarlane 已提交
83
	}
84 85

	logTelemetry(eventName: string, data: ITelemetryData): Promise<void> {
86 87 88 89
		return this._withChannel(
			channel => RemoteExtensionEnvironmentChannelClient.logTelemetry(channel, eventName, data),
			undefined
		);
90 91 92
	}

	flushTelemetry(): Promise<void> {
93 94 95 96 97 98 99
		return this._withChannel(
			channel => RemoteExtensionEnvironmentChannelClient.flushTelemetry(channel),
			undefined
		);
	}

	private _withChannel<R>(callback: (channel: IChannel, connection: IRemoteAgentConnection) => Promise<R>, fallback: R): Promise<R> {
100
		const connection = this.getConnection();
101 102
		if (!connection) {
			return Promise.resolve(fallback);
103
		}
104
		return connection.withChannel('remoteextensionsenvironment', (channel) => callback(channel, connection));
105
	}
A
Alex Dima 已提交
106 107 108 109
}

export class RemoteAgentConnection extends Disposable implements IRemoteAgentConnection {

A
Alex Dima 已提交
110 111 112
	private readonly _onReconnecting = this._register(new Emitter<void>());
	public readonly onReconnecting = this._onReconnecting.event;

A
Alex Dima 已提交
113 114 115
	private readonly _onDidStateChange = this._register(new Emitter<PersistenConnectionEvent>());
	public readonly onDidStateChange = this._onDidStateChange.event;

A
Alex Dima 已提交
116 117 118 119 120
	readonly remoteAuthority: string;
	private _connection: Promise<Client<RemoteAgentConnectionContext>> | null;

	constructor(
		remoteAuthority: string,
A
Alex Dima 已提交
121
		private readonly _commit: string | undefined,
A
Alex Dima 已提交
122
		private readonly _socketFactory: ISocketFactory,
I
isidor 已提交
123
		private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService,
124 125
		private readonly _signService: ISignService,
		private readonly _logService: ILogService
A
Alex Dima 已提交
126 127 128 129 130 131 132 133 134 135
	) {
		super();
		this.remoteAuthority = remoteAuthority;
		this._connection = null;
	}

	getChannel<T extends IChannel>(channelName: string): T {
		return <T>getDelayedChannel(this._getOrCreateConnection().then(c => c.getChannel(channelName)));
	}

136 137 138 139 140 141
	withChannel<T extends IChannel, R>(channelName: string, callback: (channel: T) => Promise<R>): Promise<R> {
		const channel = this.getChannel<T>(channelName);
		const result = callback(channel);
		return result;
	}

A
Alex Dima 已提交
142 143 144 145 146 147 148 149 150 151 152 153
	registerChannel<T extends IServerChannel<RemoteAgentConnectionContext>>(channelName: string, channel: T): void {
		this._getOrCreateConnection().then(client => client.registerChannel(channelName, channel));
	}

	private _getOrCreateConnection(): Promise<Client<RemoteAgentConnectionContext>> {
		if (!this._connection) {
			this._connection = this._createConnection();
		}
		return this._connection;
	}

	private async _createConnection(): Promise<Client<RemoteAgentConnectionContext>> {
A
Alex Dima 已提交
154
		let firstCall = true;
A
Alex Dima 已提交
155 156
		const options: IConnectionOptions = {
			commit: this._commit,
A
Alex Dima 已提交
157
			socketFactory: this._socketFactory,
A
Alex Dima 已提交
158 159
			addressProvider: {
				getAddress: async () => {
A
Alex Dima 已提交
160 161 162 163 164
					if (firstCall) {
						firstCall = false;
					} else {
						this._onReconnecting.fire(undefined);
					}
165 166
					const { authority } = await this._remoteAuthorityResolverService.resolveAuthority(this.remoteAuthority);
					return { host: authority.host, port: authority.port };
A
Alex Dima 已提交
167
				}
I
isidor 已提交
168
			},
169 170
			signService: this._signService,
			logService: this._logService
A
Alex Dima 已提交
171
		};
A
Alex Dima 已提交
172 173
		const connection = this._register(await connectRemoteAgentManagement(options, this.remoteAuthority, `renderer`));
		this._register(connection.onDidStateChange(e => this._onDidStateChange.fire(e)));
A
Alex Dima 已提交
174 175 176 177 178 179 180 181 182 183 184
		return connection.client;
	}
}

class RemoteConnectionFailureNotificationContribution implements IWorkbenchContribution {

	constructor(
		@IRemoteAgentService remoteAgentService: IRemoteAgentService,
		@INotificationService notificationService: INotificationService,
	) {
		// Let's cover the case where connecting to fetch the remote extension info fails
A
Alex Dima 已提交
185
		remoteAgentService.getRawEnvironment()
A
Tweaks  
Alex Dima 已提交
186
			.then(undefined, err => {
187
				if (!RemoteAuthorityResolverError.isHandled(err)) {
A
Tweaks  
Alex Dima 已提交
188 189 190
					notificationService.error(nls.localize('connectionError', "Failed to connect to the remote extension host server (Error: {0})", err ? err.message : ''));
				}
			});
A
Alex Dima 已提交
191 192 193 194 195 196
	}

}

const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench);
workbenchRegistry.registerWorkbenchContribution(RemoteConnectionFailureNotificationContribution, LifecyclePhase.Ready);