提交 0a9ba2ca 编写于 作者: M Matt Bierner

Extract port mapping to class so it can be reused

Also removes telemetry on which extensions were using localhost resources as it is no longer required
上级 a9b3a096
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import * as modes from 'vs/editor/common/modes';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
export class WebviewPortMappingManager extends Disposable {
private readonly _tunnels = new Map<number, Promise<RemoteTunnel>>();
constructor(
private readonly extensionLocation: URI | undefined,
private readonly mappings: () => ReadonlyArray<modes.IWebviewPortMapping>,
private readonly tunnelService: ITunnelService
) {
super();
}
public async getRedirect(url: string): Promise<string | undefined> {
const uri = URI.parse(url);
if (uri.scheme !== 'http' && uri.scheme !== 'https') {
return undefined;
}
const localhostMatch = /^localhost:(\d+)$/.exec(uri.authority);
if (!localhostMatch) {
return undefined;
}
const port = +localhostMatch[1];
for (const mapping of this.mappings()) {
if (mapping.webviewPort === port) {
if (this.extensionLocation && this.extensionLocation.scheme === REMOTE_HOST_SCHEME) {
const tunnel = await this.getOrCreateTunnel(mapping.extensionHostPort);
if (tunnel) {
return url.replace(
new RegExp(`^${uri.scheme}://localhost:${mapping.webviewPort}/`),
`${uri.scheme}://localhost:${tunnel.tunnelLocalPort}/`);
}
}
if (mapping.webviewPort !== mapping.extensionHostPort) {
return url.replace(
new RegExp(`^${uri.scheme}://localhost:${mapping.webviewPort}/`),
`${uri.scheme}://localhost:${mapping.extensionHostPort}/`);
}
}
}
return undefined;
}
dispose() {
super.dispose();
for (const tunnel of this._tunnels.values()) {
tunnel.then(tunnel => tunnel.dispose());
}
this._tunnels.clear();
}
private getOrCreateTunnel(remotePort: number): Promise<RemoteTunnel> | undefined {
const existing = this._tunnels.get(remotePort);
if (existing) {
return existing;
}
const tunnel = this.tunnelService.openTunnel(remotePort);
if (tunnel) {
this._tunnels.set(remotePort, tunnel);
}
return tunnel;
}
}
\ No newline at end of file
......@@ -13,19 +13,17 @@ import { endsWith } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import * as modes from 'vs/editor/common/modes';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IFileService } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ITunnelService } from 'vs/platform/remote/common/tunnel';
import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService';
import { WebviewPortMappingManager } from 'vs/workbench/contrib/webview/common/portMapping';
import { getWebviewThemeData } from 'vs/workbench/contrib/webview/common/themeing';
import { Webview, WebviewContentOptions, WebviewOptions, WebviewResourceScheme } from 'vs/workbench/contrib/webview/common/webview';
import { registerFileProtocol } from 'vs/workbench/contrib/webview/electron-browser/webviewProtocols';
import { areWebviewInputOptionsEqual } from '../browser/webviewEditorService';
import { WebviewFindWidget } from '../browser/webviewFindWidget';
import { getWebviewThemeData } from 'vs/workbench/contrib/webview/common/themeing';
export interface WebviewPortMapping {
readonly port: number;
......@@ -141,88 +139,22 @@ class WebviewProtocolProvider extends Disposable {
class WebviewPortMappingProvider extends Disposable {
private readonly _tunnels = new Map<number, Promise<RemoteTunnel>>();
private readonly _manager: WebviewPortMappingManager;
constructor(
session: WebviewSession,
extensionLocation: URI | undefined,
mappings: () => ReadonlyArray<modes.IWebviewPortMapping>,
private readonly tunnelService: ITunnelService,
extensionId: ExtensionIdentifier | undefined,
@ITelemetryService telemetryService: ITelemetryService
tunnelService: ITunnelService,
) {
super();
this._manager = this._register(new WebviewPortMappingManager(extensionLocation, mappings, tunnelService));
let hasLogged = false;
session.onBeforeRequest(async (details) => {
const uri = URI.parse(details.url);
if (uri.scheme !== 'http' && uri.scheme !== 'https') {
return undefined;
}
const localhostMatch = /^localhost:(\d+)$/.exec(uri.authority);
if (localhostMatch) {
if (!hasLogged && extensionId) {
hasLogged = true;
/* __GDPR__
"webview.accessLocalhost" : {
"extension" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
telemetryService.publicLog('webview.accessLocalhost', { extension: extensionId.value });
}
const port = +localhostMatch[1];
for (const mapping of mappings()) {
if (mapping.webviewPort === port) {
if (extensionLocation && extensionLocation.scheme === REMOTE_HOST_SCHEME) {
const tunnel = await this.getOrCreateTunnel(mapping.extensionHostPort);
if (tunnel) {
return {
redirectURL: details.url.replace(
new RegExp(`^${uri.scheme}://localhost:${mapping.webviewPort}/`),
`${uri.scheme}://localhost:${tunnel.tunnelLocalPort}/`)
};
}
}
if (mapping.webviewPort !== mapping.extensionHostPort) {
return {
redirectURL: details.url.replace(
new RegExp(`^${uri.scheme}://localhost:${mapping.webviewPort}/`),
`${uri.scheme}://localhost:${mapping.extensionHostPort}/`)
};
}
}
}
}
return undefined;
session.onBeforeRequest(async details => {
const redirect = await this._manager.getRedirect(details.url);
return redirect ? { redirectURL: redirect } : undefined;
});
}
dispose() {
super.dispose();
for (const tunnel of this._tunnels.values()) {
tunnel.then(tunnel => tunnel.dispose());
}
this._tunnels.clear();
}
private getOrCreateTunnel(remotePort: number): Promise<RemoteTunnel> | undefined {
const existing = this._tunnels.get(remotePort);
if (existing) {
return existing;
}
const tunnel = this.tunnelService.openTunnel(remotePort);
if (tunnel) {
this._tunnels.set(remotePort, tunnel);
}
return tunnel;
}
}
class SvgBlocker extends Disposable {
......@@ -370,10 +302,8 @@ export class WebviewElement extends Disposable implements Webview {
contentOptions: WebviewContentOptions,
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@IEnvironmentService environmentService: IEnvironmentService,
@IFileService fileService: IFileService,
@ITunnelService tunnelService: ITunnelService,
@ITelemetryService telemetryService: ITelemetryService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
) {
super();
......@@ -420,8 +350,6 @@ export class WebviewElement extends Disposable implements Webview {
_options.extension ? _options.extension.location : undefined,
() => (this.content.options.portMappings || []),
tunnelService,
_options.extension ? _options.extension.id : undefined,
telemetryService
));
if (!this._options.allowSvgs) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册