提交 216c8476 编写于 作者: A Alex Ross

Watch terminal for ports to forward when remote

Fixes microsoft/vscode-remote-release#3235
上级 69186de4
......@@ -56,8 +56,6 @@ export function extractLocalHostUriMetaDataForPortMapping(uri: URI): { address:
};
}
export abstract class AbstractTunnelService implements ITunnelService {
declare readonly _serviceBrand: undefined;
......
......@@ -56,6 +56,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { RemoteWindowActiveIndicator } from 'vs/workbench/contrib/remote/browser/remoteIndicator';
import { inQuickPickContextKeyValue } from 'vs/workbench/browser/quickaccess';
import { Codicon, registerIcon } from 'vs/base/common/codicons';
import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
export interface HelpInformation {
extensionDescription: IExtensionDescription;
......@@ -481,6 +482,7 @@ export class RemoteViewPaneContainer extends FilterViewPaneContainer implements
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
@ITerminalService private readonly terminalService: ITerminalService
) {
super(VIEWLET_ID, remoteExplorerService.onDidChangeTargetType, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, viewDescriptorService);
this.addConstantViewDescriptors([this.helpPanelDescriptor]);
......@@ -555,7 +557,7 @@ export class RemoteViewPaneContainer extends FilterViewPaneContainer implements
// This context key is set to false in the constructor, but is expected to be changed by resolver extensions to enable the forwarded ports view.
const viewEnabled: boolean = !!forwardedPortsViewEnabled.getValue(this.contextKeyService);
if (this.environmentService.configuration.remoteAuthority && !this.tunnelPanelDescriptor && viewEnabled) {
this.tunnelPanelDescriptor = new TunnelPanelDescriptor(new TunnelViewModel(this.remoteExplorerService), this.environmentService);
this.tunnelPanelDescriptor = new TunnelPanelDescriptor(new TunnelViewModel(this.remoteExplorerService, this.terminalService), this.environmentService);
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
viewsRegistry.registerViews([this.tunnelPanelDescriptor!], this.viewContainer);
}
......
......@@ -42,6 +42,8 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { UrlFinder } from 'vs/workbench/contrib/remote/browser/urlFinder';
export const forwardedPortsViewEnabled = new RawContextKey<boolean>('forwardedPortsViewEnabled', false);
......@@ -72,7 +74,8 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel {
private _candidates: Map<string, { host: string, port: number, detail: string }> = new Map();
constructor(
@IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService) {
@IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService,
@ITerminalService readonly terminalService: ITerminalService) {
super();
this.model = remoteExplorerService.tunnelModel;
this._register(this.model.onForwardPort(() => this._onForwardedPortsChanged.fire()));
......@@ -86,6 +89,11 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel {
remotePort: 0,
description: ''
};
const urlFinder = this._register(new UrlFinder(terminalService));
this._register(urlFinder.onDidMatchLocalUrl(localUrl => {
this.model.forward(localUrl);
}));
}
async groups(): Promise<ITunnelGroup[]> {
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ITerminalInstance, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { Emitter } from 'vs/base/common/event';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
export class UrlFinder extends Disposable {
private static readonly terminalCodesRegex = /(?:\u001B|\u009B)[\[\]()#;?]*(?:(?:(?:[a-zA-Z0-9]*(?:;[a-zA-Z0-9]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[0-9A-PR-TZcf-ntqry=><~]))/g;
/**
* Local server url pattern matching following urls:
* http://localhost:3000/ - commonly used across multiple frameworks
* https://127.0.0.1:5001/ - ASP.NET
* http://:8080 - Beego Golang
* http://0.0.0.0:4000 - Elixir Phoenix
*/
private static readonly localUrlRegex = /\b\w{2,20}:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0|:\d{2,5})[\w\-\.\~:\/\?\#[\]\@!\$&\(\)\*\+\,\;\=]*/gim;
private _onDidMatchLocalUrl: Emitter<{ host: string, port: number }> = new Emitter();
public readonly onDidMatchLocalUrl = this._onDidMatchLocalUrl.event;
private listeners: Map<ITerminalInstance, IDisposable> = new Map();
constructor(terminalService: ITerminalService) {
super();
terminalService.terminalInstances.forEach(instance => {
this.listeners.set(instance, instance.onData(data => {
this.processData(data);
}));
});
this._register(terminalService.onInstanceCreated(instance => {
this.listeners.set(instance, instance.onData(data => {
this.processData(data);
}));
}));
this._register(terminalService.onInstanceDisposed(instance => {
this.listeners.delete(instance);
}));
}
dispose() {
super.dispose();
const listeners = this.listeners.values();
for (const listener of listeners) {
listener.dispose();
}
}
private processData(data: string) {
// strip ANSI terminal codes
data = data.replace(UrlFinder.terminalCodesRegex, '');
const urlMatches = data.match(UrlFinder.localUrlRegex) || [];
urlMatches.forEach((match) => {
// check if valid url
const serverUrl = new URL(match);
if (serverUrl) {
// check if the port is a valid integer value
const port = parseFloat(serverUrl.port!);
if (!isNaN(port) && Number.isInteger(port) && port > 0 && port <= 65535) {
// normalize the host name
let host = serverUrl.hostname;
if (host !== '0.0.0.0') {
host = 'localhost';
}
this._onDidMatchLocalUrl.fire({ port, host });
}
}
});
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册