与 remote 有关的功能,vscode 总共有四条不开源的产品线:
- vscode live share
- vscode remote (container, wsl, ssh)
- visual studio online
- vscode web
实现的功能都是 vscode 编辑的不是本地的文件,而是远程机器上的文件。vscode remote 来说,远程文件则是你指定的通信方式,比如 ssh,连接过去的文件。而 vscode live share 和 visual studio online 这两种方式都不是直连的,而是通过微软在公网上的代理服务器进行的中转。这个也就导致了 live share 在国内用起来并不是特别快。
vscode web 与 vistual studio online 是一样的实现,只是少了走微软的公网代理这个步骤,直接是连了本地暴露的端口。
第一步:构造 workbench 的参数
一个 workbench 就是我们所看见的 vscode 整个界面。构造它需要的参数有:
interface IWorkbenchConstructionOptions {
readonly remoteAuthority?: string;
readonly connectionToken?: string;
readonly webviewEndpoint?: string;
readonly workspaceProvider?: IWorkspaceProvider;
userDataProvider?: IFileSystemProvider;
readonly webSocketFactory?: IWebSocketFactory;
readonly resourceUriProvider?: IResourceUriProvider;
readonly credentialsProvider?: ICredentialsProvider;
readonly staticExtensions?: ReadonlyArray<IStaticExtension>;
readonly urlCallbackProvider?: IURLCallbackProvider;
readonly updateProvider?: IUpdateProvider;
readonly resolveCommonTelemetryProperties?: ICommontTelemetryPropertiesResolver;
readonly resolveExternalUri?: IExternalUriResolver;
readonly logLevel?: LogLevel;
readonly driver?: boolean;
}
- remoteAuthority:远程地址,即从哪里提供工作台的 IP: PORT。例如,用于 WebSocket 连接。connectionToken:要发送到服务器的连接标记。
- webviewEndpoint:帧内容(“webview”)的终端节点,必须提供以完全安全隔离工作台主机。workspaceProvider:打开工作区并提供初始工作区的处理程序。
- userDataProvider:用户数据提供程序用于处理用户特定的应用程序状态,例如设置、键绑定、UI 状态(例如打开的编辑器)和片段。
- webSocketFactory:Web 套接字的工厂。
- resourceUriProvider:资源 URI 的提供程序。
- credentialsProvider:用于存储和检索凭据的凭据提供程序。
- staticExtensions:添加不能卸载但只能禁用的静态扩展。
- urlCallbackProvider:URL 回调支持。
- updateProvider:更新报告支持。
- resolveCommonTelemetryProperties:支持将附加属性添加到遥测中。
- resolveExternalUri:在打开外部 URI 之前解析它。
- logLevel:当前日志级别。默认为 LogLevel.Info。driver:是否启用“ smoke test”驱动程序。
第二步:构造 workbench
其中比较关键的几行
// 远程
const remoteAuthorityResolverService = new RemoteAuthorityResolverService(this.configuration.resourceUriProvider);
serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService);
// 远程代理
const remoteAgentService = this._register(new RemoteAgentService(this.configuration.webSocketFactory, environmentService, productService, remoteAuthorityResolverService, signService, logService));
serviceCollection.set(IRemoteAgentService, remoteAgentService);
// 文件
const fileService = this._register(new FileService(logService));
serviceCollection.set(IFileService, fileService);
this.registerFileSystemProviders(environmentService, fileService, remoteAgentService, logService, logsPath);
这里就分出了三条支线剧情
- remoteAuthorityResolverService
- remoteAgentService
- fileSystemProvider
第三步:remoteAuthorityResolverService 设置全局变量 RemoteAuthorities
resolveAuthority(authority: string): Promise<ResolverResult> {
if (authority.indexOf(':') >= 0) {
const pieces = authority.split(':');
return Promise.resolve(this._createResolvedAuthority(authority, pieces[0], parseInt(pieces[1], 10)));
}
return Promise.resolve(this._createResolvedAuthority(authority, authority, 80));
}
private _createResolvedAuthority(authority: string, host: string, port: number): ResolverResult {
RemoteAuthorities.set(authority, host, port);
return { authority: { authority, host, port } };
}
这里通过全局变量 RemoteAuthorities,影响了其他地方的 Uri 的解析。
第四步 remoteAuthorityResolverService 被谁调用的?
是在 RemoteExtensionHostClient 中调用的。顾名思义,这个类就是去连接 remoteExtensionHost 这个进程的,从而间接操作远端机器上运行的插件进程。
const options: IConnectionOptions = {
commit: this._productService.commit,
socketFactory: this._socketFactory,
addressProvider: {
getAddress: async () => {
const { authority } = await this.remoteAuthorityResolverService.resolveAuthority(this._initDataProvider.remoteAuthority);
return { host: authority.host, port: authority.port };
}
},
signService: this._signService,
logService: this._logService
};
remote 主要就四个功能
- 远程插件执行
- 文件系统读写
- terminal
- port forwarding
这里已经可以找到远程插件执行是怎么过来的了。
第五步:RemoteFileSystemProvider
// 转发调用
stat(resource: URI): Promise<IStat> {
return this.channel.call('stat', [resource]);
}
open(resource: URI, opts: FileOpenOptions): Promise<number> {
return this.channel.call('open', [resource, opts]);
}
close(fd: number): Promise<void> {
return this.channel.call('close', [fd]);
}
还是非常直白的。直接把对 FileSystemProvider 调用转成 IPC 调用,并跑在 REMOTE_FILE_SYSTEM_CHANNEL_NAME 这个 channel 上。
这里的连接都是用 webSocketFactory 这个最初从 IWorkbenchConstructionOptions 上传进来的参数控制的。所以 remote 的关键,还是怎么用 websocket 和 remoteAgent 连上。这个地方连上了,vscode 就可以在这个连接上转发一切,从文件系统调用,到插件的消息。
服务端
我们只能看到 vscode remote 客户端的部分。websocket 对端的 remoteAgent 是没有提供开源代码的。我们可以自己脑补一下,一个类似冰河木马的进程,在用 websocket 把你的机器暴露出去。在 vscode 的开源代码中,并没有提供对这个 socket 的加密机制,而是选择由插件去处理如何加密。
标签:Code,Remote,vscode,authority,readonly,VS,remoteAuthorityResolverService,._,retur From: https://www.cnblogs.com/xusun/p/17443184.html