1 通过用例建模进行需求分析
1.1 什么是用例?
用例的核心概念首先它是一个业务过程,经过逻辑整理抽象出来的一个业务过程,这是用例的实质。什么业务过程?在待开发软件所处的业务领域内完成特定业务任务的一系列活动就是业务过程。
1.2 用例建模的基本步骤
第一步:从需求表述中找出用例,往往是动名词短语表示的抽象用例
第二步:描述用例开始和结束的状态,用TUCBW和TUCEW表示的高层用例
第三步:对用例按照子系统或不同的方面进行分类,描述用例与用例、用例与参与者之间的上下文关系,并画出用例图
第四步:进一步逐一分析用例与参与者之间的详细交互过程,完成一个两列表格将参与者和待开发软件系统之间从用例开始到用例结束的所有交互步骤都列举出来扩展用例
1.3 根据上述用例的概念以及用例建模的基本步骤进行分析
抽象用例:局域网内与远程服务器建立连接
高层用例:
扩展用例:参与者的所有交互步骤
1.4 用例图
2 逆向工程
从VScode Remote部署架构来说,基本的组件如图所示。其中 vscode remoteAgent 这个进程,与 vscode remoteExtensionHost 这个进程,都是不开源的。所以我们就开源的 vscode 界面部分来看一下这个 vscode remote 是如何实现的。
2.1 构造workbench的参数
一个 workbench 就是我们所看见的 vscode 整个界面。构造它需要的参数有
interface IWorkbenchConstructionOptions {
/**
* The remote authority is the IP:PORT from where the workbench is served
* from. It is for example being used for the websocket connections as address.
*/
readonly remoteAuthority?: string;
/**
* The connection token to send to the server.
*/
readonly connectionToken?: string;
/**
* An endpoint to serve iframe content ("webview") from. This is required
* to provide full security isolation from the workbench host.
*/
readonly webviewEndpoint?: string;
/**
* A handler for opening workspaces and providing the initial workspace.
*/
readonly workspaceProvider?: IWorkspaceProvider;
/**
* The user data provider is used to handle user specific application
* state like settings, keybindings, UI state (e.g. opened editors) and snippets.
*/
userDataProvider?: IFileSystemProvider;
/**
* A factory for web sockets.
*/
readonly webSocketFactory?: IWebSocketFactory;
/**
* A provider for resource URIs.
*/
readonly resourceUriProvider?: IResourceUriProvider;
/**
* The credentials provider to store and retrieve secrets.
*/
readonly credentialsProvider?: ICredentialsProvider;
/**
* Add static extensions that cannot be uninstalled but only be disabled.
*/
readonly staticExtensions?: ReadonlyArray<IStaticExtension>;
/**
* Support for URL callbacks.
*/
readonly urlCallbackProvider?: IURLCallbackProvider;
/**
* Support for update reporting.
*/
readonly updateProvider?: IUpdateProvider;
/**
* Support adding additional properties to telemetry.
*/
readonly resolveCommonTelemetryProperties?: ICommontTelemetryPropertiesResolver;
/**
* Resolves an external uri before it is opened.
*/
readonly resolveExternalUri?: IExternalUriResolver;
/**
* Current logging level. Default is `LogLevel.Info`.
*/
readonly logLevel?: LogLevel;
/**
* Whether to enable the smoke test driver.
*/
readonly driver?: boolean;
}
其中 remoteAuthority 标记了当前 workbench 是运行在什么 remote 环境中。
2.2 构造workbench
// Remote
const remoteAuthorityResolverService = new RemoteAuthorityResolverService(this.configuration.resourceUriProvider);
serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService);
// Remote Agent
const remoteAgentService = this._register(new RemoteAgentService(this.configuration.webSocketFactory, environmentService, productService, remoteAuthorityResolverService, signService, logService));
serviceCollection.set(IRemoteAgentService, remoteAgentService);
// Files
const fileService = this._register(new FileService(logService));
serviceCollection.set(IFileService, fileService);
this.registerFileSystemProviders(environmentService, fileService, remoteAgentService, logService, logsPath);
分出了三条支线剧情:
remoteAuthorityResolverService
remoteAgentService
fileSystemProvider
2.3 remoteAuthorityResolverService设置全局变量RemoteAuthorities
这里通过全局变量 RemoteAuthorities,影响了其他地方的 Uri 的解析。
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 } };
}
2.4 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
2.5 RemoteFileSystemProvider
// --- forwarding calls
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]);
}
这里的连接都是用 webSocketFactory 这个最初从 IWorkbenchConstructionOptions 上传进来的参数控制的。所以 remote 的关键,还是怎么用 websocket 和 remoteAgent 连上。这个地方连上了,vscode 就可以在这个连接上转发一切,从文件系统调用,到插件的消息。
2.6 服务端
我们只能看到 vscode remote 客户端的部分。websocket 对端的 remoteAgent 是没有提供开源代码的。我们可以自己脑补一下,一个类似冰河木马的进程,在用 websocket 把你的机器暴露出去。在 vscode 的开源部分,并没有提供对这个 socket 的加密机制,而是选择由插件去处理如何加密。
2.7 总结
通过逆向工程对本作业用例进行分析可知,首先最重要的是VScode界面workbench参数之一webSocketFactory控制websocket和remoteAgent进行通信;三条支线剧情其中之一remoteAuthorityResolverService通过被类RemoteExtensionHostClient调用与remoteExtensionHost进行连接,从而间接操作远端及其上运行的插件进程;类RemoteFileSystemProvider将FileSystemProvider转成IPC调用,实现文件系统的读写等完成该用例的实现。
学号:SA22225258