-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support remote port forwarding (#13439)
* basics for dev-container support Signed-off-by: Jonah Iden <[email protected]> * basic creating and connecting to container working Signed-off-by: Jonah Iden <[email protected]> * open workspace when opening container Signed-off-by: Jonah Iden <[email protected]> * save and reuse last USed container per workspace Signed-off-by: Jonah Iden <[email protected]> * restart container if running Signed-off-by: Jonah Iden <[email protected]> * better container creation extension features Signed-off-by: Jonah Iden <[email protected]> * added dockerfile support Signed-off-by: Jonah Iden <[email protected]> * rebuild container if devcontainer.json has been changed since last use Signed-off-by: Jonah Iden <[email protected]> * fix build Signed-off-by: Jonah Iden <[email protected]> * fixed checking if container needs rebuild Signed-off-by: Jonah Iden <[email protected]> * working port forwarding via exec instance Signed-off-by: Jonah Iden <[email protected]> * review changes Signed-off-by: Jonah Iden <[email protected]> * fix import Signed-off-by: Jonah Iden <[email protected]> * smaller fixes and added support for multiple devcontainer configuration files Signed-off-by: Jonah Iden <[email protected]> * basic output window for devcontainer build Signed-off-by: Jonah Iden <[email protected]> * smaller review changes and nicer dockerfile.json detection code Signed-off-by: Jonah Iden <[email protected]> * fixed build and docuemented implemented devcontainer.json properties Signed-off-by: Jonah Iden <[email protected]> * Fix unneeded URI conversion (#13415) * Fix quickpick problems found in IDE testing (#13451) Fixes #13450, #13449 contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder <[email protected]> * Fix rending of quickpick buttons (#13342) Ensure that the Theia specific wrapper for the MonacoQuickPickItem properly forwards assignments of the "buttons" property to the wrapped item. Fixes #13076 Contributed on behalf of STMicroelectronics * electron: allow accessing the metrics endpoint for performance analysis (#13380) By default, when running Theia in Electron, all endpoints are protected by the ElectronTokenValidator. This patch allows accessing the '/metrics' endpoint without a token, which enables us to collect metrics for performance analysis. For this, ElectronTokenValidator is extended to allow access to the metrics endpoint. All other endpoints are still protected. Contributed on behalf of STMicroelectronics Signed-off-by: Olaf Lessenich <[email protected]> * fixed renaming and moving of open notebooks (#13467) * fixed renameing of open notebooks Signed-off-by: Jonah Iden <[email protected]> * fixed moving of notebook editors to other areas Signed-off-by: Jonah Iden <[email protected]> --------- Signed-off-by: Jonah Iden <[email protected]> * [playwright] Update documentation Since a recent enhancement/refactoring of @theia/playwright, to permit using it in Theia Electron applications, the way to load an application has changed. This commit is an attempt to update the examples that are part of the documentation. I validated the changes in the "theia-playwright-template" repository, and so I have adapted the sample code to that repo's linting rules (using single quotes instead of double). It's possible that other things have changed, that I have not yet encountered, but this should be a good step forward, at least for those just getting started integrating playwright to test their Theia-based app. Signed-off-by: Marc Dumais <[email protected]> * basics for dev-container support Signed-off-by: Jonah Iden <[email protected]> * basic creating and connecting to container working Signed-off-by: Jonah Iden <[email protected]> * added dockerfile support Signed-off-by: Jonah Iden <[email protected]> * added port forwarding inlcuding ui Signed-off-by: Jonah Iden <[email protected]> * basic port/address validation Signed-off-by: Jonah Iden <[email protected]> * fixed allready forwarded port checking Signed-off-by: Jonah Iden <[email protected]> * rebase fixes Signed-off-by: Jonah Iden <[email protected]> * removed unused file Signed-off-by: Jonah Iden <[email protected]> * review changes Signed-off-by: Jonah Iden <[email protected]> * fixed widget focus and message margin Signed-off-by: Jonah Iden <[email protected]> * default port binding now shows as 0.0.0.0 Signed-off-by: Jonah Iden <[email protected]> --------- Signed-off-by: Jonah Iden <[email protected]> Signed-off-by: Thomas Mäder <[email protected]> Signed-off-by: Olaf Lessenich <[email protected]> Signed-off-by: Marc Dumais <[email protected]> Co-authored-by: Alexander Taran <[email protected]> Co-authored-by: Thomas Mäder <[email protected]> Co-authored-by: Tobias Ortmayr <[email protected]> Co-authored-by: Olaf Lessenich <[email protected]> Co-authored-by: Marc Dumais <[email protected]>
- Loading branch information
1 parent
c2b0704
commit 1a03381
Showing
11 changed files
with
420 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
packages/remote/src/electron-browser/port-forwarding/port-forwading-contribution.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// ***************************************************************************** | ||
// Copyright (C) 2024 TypeFox and others. | ||
// | ||
// This program and the accompanying materials are made available under the | ||
// terms of the Eclipse Public License v. 2.0 which is available at | ||
// http://www.eclipse.org/legal/epl-2.0. | ||
// | ||
// This Source Code may also be made available under the following Secondary | ||
// Licenses when the conditions for such availability set forth in the Eclipse | ||
// Public License v. 2.0 are satisfied: GNU General Public License, version 2 | ||
// with the GNU Classpath Exception which is available at | ||
// https://www.gnu.org/software/classpath/license.html. | ||
// | ||
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 | ||
// ***************************************************************************** | ||
|
||
import { nls } from '@theia/core'; | ||
import { AbstractViewContribution } from '@theia/core/lib/browser'; | ||
import { injectable } from '@theia/core/shared/inversify'; | ||
import { PortForwardingWidget, PORT_FORWARDING_WIDGET_ID } from './port-forwarding-widget'; | ||
|
||
@injectable() | ||
export class PortForwardingContribution extends AbstractViewContribution<PortForwardingWidget> { | ||
constructor() { | ||
super({ | ||
widgetId: PORT_FORWARDING_WIDGET_ID, | ||
widgetName: nls.localizeByDefault('Ports'), | ||
defaultWidgetOptions: { | ||
area: 'bottom' | ||
} | ||
}); | ||
} | ||
} |
84 changes: 84 additions & 0 deletions
84
packages/remote/src/electron-browser/port-forwarding/port-forwarding-service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// ***************************************************************************** | ||
// Copyright (C) 2024 TypeFox and others. | ||
// | ||
// This program and the accompanying materials are made available under the | ||
// terms of the Eclipse Public License v. 2.0 which is available at | ||
// http://www.eclipse.org/legal/epl-2.0. | ||
// | ||
// This Source Code may also be made available under the following Secondary | ||
// Licenses when the conditions for such availability set forth in the Eclipse | ||
// Public License v. 2.0 are satisfied: GNU General Public License, version 2 | ||
// with the GNU Classpath Exception which is available at | ||
// https://www.gnu.org/software/classpath/license.html. | ||
// | ||
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 | ||
// ***************************************************************************** | ||
|
||
import { Emitter } from '@theia/core'; | ||
import { inject, injectable } from '@theia/core/shared/inversify'; | ||
import { RemotePortForwardingProvider } from '../../electron-common/remote-port-forwarding-provider'; | ||
|
||
export interface ForwardedPort { | ||
localPort?: number; | ||
address?: string; | ||
origin?: string; | ||
editing: boolean; | ||
} | ||
|
||
@injectable() | ||
export class PortForwardingService { | ||
|
||
@inject(RemotePortForwardingProvider) | ||
readonly provider: RemotePortForwardingProvider; | ||
|
||
protected readonly onDidChangePortsEmitter = new Emitter<void>(); | ||
readonly onDidChangePorts = this.onDidChangePortsEmitter.event; | ||
|
||
forwardedPorts: ForwardedPort[] = []; | ||
|
||
forwardNewPort(origin?: string): ForwardedPort { | ||
const index = this.forwardedPorts.push({ editing: true, origin }); | ||
return this.forwardedPorts[index - 1]; | ||
} | ||
|
||
updatePort(port: ForwardedPort, newAdress: string): void { | ||
const connectionPort = new URLSearchParams(location.search).get('port'); | ||
if (!connectionPort) { | ||
// if there is no open remote connection we can't forward a port | ||
return; | ||
} | ||
|
||
const parts = newAdress.split(':'); | ||
if (parts.length === 2) { | ||
port.address = parts[0]; | ||
port.localPort = parseInt(parts[1]); | ||
} else { | ||
port.localPort = parseInt(parts[0]); | ||
} | ||
|
||
port.editing = false; | ||
|
||
this.provider.forwardPort(parseInt(connectionPort), { port: port.localPort!, address: port.address }); | ||
this.onDidChangePortsEmitter.fire(); | ||
} | ||
|
||
removePort(port: ForwardedPort): void { | ||
const index = this.forwardedPorts.indexOf(port); | ||
if (index !== -1) { | ||
this.forwardedPorts.splice(index, 1); | ||
this.provider.portRemoved({ port: port.localPort! }); | ||
this.onDidChangePortsEmitter.fire(); | ||
} | ||
} | ||
|
||
isValidAddress(address: string): boolean { | ||
const match = address.match(/^(.*:)?\d+$/); | ||
if (!match) { | ||
return false; | ||
} | ||
|
||
const port = parseInt(address.includes(':') ? address.split(':')[1] : address); | ||
|
||
return !this.forwardedPorts.some(p => p.localPort === port); | ||
} | ||
} |
140 changes: 140 additions & 0 deletions
140
packages/remote/src/electron-browser/port-forwarding/port-forwarding-widget.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
// ***************************************************************************** | ||
// Copyright (C) 2024 TypeFox and others. | ||
// | ||
// This program and the accompanying materials are made available under the | ||
// terms of the Eclipse Public License v. 2.0 which is available at | ||
// http://www.eclipse.org/legal/epl-2.0. | ||
// | ||
// This Source Code may also be made available under the following Secondary | ||
// Licenses when the conditions for such availability set forth in the Eclipse | ||
// Public License v. 2.0 are satisfied: GNU General Public License, version 2 | ||
// with the GNU Classpath Exception which is available at | ||
// https://www.gnu.org/software/classpath/license.html. | ||
// | ||
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 | ||
// ***************************************************************************** | ||
|
||
import * as React from '@theia/core/shared/react'; | ||
import { ReactNode } from '@theia/core/shared/react'; | ||
import { OpenerService, ReactWidget } from '@theia/core/lib/browser'; | ||
import { nls, URI } from '@theia/core'; | ||
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; | ||
import { ForwardedPort, PortForwardingService } from './port-forwarding-service'; | ||
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service'; | ||
|
||
export const PORT_FORWARDING_WIDGET_ID = 'port-forwarding-widget'; | ||
|
||
@injectable() | ||
export class PortForwardingWidget extends ReactWidget { | ||
|
||
@inject(PortForwardingService) | ||
protected readonly portForwardingService: PortForwardingService; | ||
|
||
@inject(OpenerService) | ||
protected readonly openerService: OpenerService; | ||
|
||
@inject(ClipboardService) | ||
protected readonly clipboardService: ClipboardService; | ||
|
||
@postConstruct() | ||
protected init(): void { | ||
this.id = PORT_FORWARDING_WIDGET_ID; | ||
this.node.tabIndex = -1; | ||
this.title.label = nls.localizeByDefault('Ports'); | ||
this.title.caption = this.title.label; | ||
this.title.closable = true; | ||
this.update(); | ||
|
||
this.portForwardingService.onDidChangePorts(() => this.update()); | ||
} | ||
|
||
protected render(): ReactNode { | ||
if (this.portForwardingService.forwardedPorts.length === 0) { | ||
return <div> | ||
<p style={{ marginLeft: 'calc(var(--theia-ui-padding) * 2)' }}> | ||
{nls.localizeByDefault('No forwarded ports. Forward a port to access your locally running services over the internet.\n[Forward a Port]({0})').split('\n')[0]} | ||
</p> | ||
{this.renderForwardPortButton()} | ||
</div>; | ||
} | ||
|
||
return <div> | ||
<table className='port-table'> | ||
<thead> | ||
<tr> | ||
<th className='port-table-header'>{nls.localizeByDefault('Port')}</th> | ||
<th className='port-table-header'>{nls.localizeByDefault('Address')}</th> | ||
<th className='port-table-header'>{nls.localizeByDefault('Running Process')}</th> | ||
<th className='port-table-header'>{nls.localizeByDefault('Origin')}</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{this.portForwardingService.forwardedPorts.map(port => ( | ||
<tr key={port.localPort ?? 'editing'}> | ||
{this.renderPortColumn(port)} | ||
{this.renderAddressColumn(port)} | ||
<td></td> | ||
<td>{port.origin ? nls.localizeByDefault(port.origin) : ''}</td> | ||
</tr> | ||
))} | ||
{!this.portForwardingService.forwardedPorts.some(port => port.editing) && <tr><td>{this.renderForwardPortButton()}</td></tr>} | ||
</tbody> | ||
</table> | ||
</div>; | ||
} | ||
|
||
protected renderForwardPortButton(): ReactNode { | ||
return <button className='theia-button' onClick={() => { | ||
this.portForwardingService.forwardNewPort('User Forwarded'); | ||
this.update(); | ||
} | ||
}>{nls.localizeByDefault('Forward a Port')}</button>; | ||
} | ||
|
||
protected renderAddressColumn(port: ForwardedPort): ReactNode { | ||
const address = `${port.address ?? '0.0.0.0'}:${port.localPort}`; | ||
return <td> | ||
<div className='button-cell'> | ||
<span style={{ flexGrow: 1 }} className='forwarded-address' onClick={async e => { | ||
if (e.ctrlKey) { | ||
const uri = new URI(`http://${address}`); | ||
(await this.openerService.getOpener(uri)).open(uri); | ||
} | ||
}} title={nls.localizeByDefault('Follow link') + ' (ctrl/cmd + click)'}> | ||
{port.localPort ? address : ''} | ||
</span> | ||
{ | ||
port.localPort && | ||
<span className='codicon codicon-clippy action-label' title={nls.localizeByDefault('Copy Local Address')} onClick={() => { | ||
this.clipboardService.writeText(address); | ||
}}></span> | ||
} | ||
</div> | ||
</td>; | ||
} | ||
|
||
protected renderPortColumn(port: ForwardedPort): ReactNode { | ||
return port.editing ? | ||
<td><PortEditingInput port={port} service={this.portForwardingService} /></td> : | ||
<td> | ||
<div className='button-cell'> | ||
<span style={{ flexGrow: 1 }}>{port.localPort}</span> | ||
<span className='codicon codicon-close action-label' title={nls.localizeByDefault('Stop Forwarding Port')} onClick={() => { | ||
this.portForwardingService.removePort(port); | ||
this.update(); | ||
}}></span> | ||
</div> | ||
</td>; | ||
} | ||
|
||
} | ||
|
||
function PortEditingInput({ port, service }: { port: ForwardedPort, service: PortForwardingService }): React.JSX.Element { | ||
const [error, setError] = React.useState(false); | ||
return <input className={`theia-input forward-port-button${error ? ' port-edit-input-error' : ''}`} port-edit-input-error={error} | ||
autoFocus defaultValue={port.address ? `${port.address}:${port.localPort}` : port.localPort ?? ''} | ||
placeholder={nls.localizeByDefault('Port number or address (eg. 3000 or 10.10.10.10:2000).')} | ||
onKeyDown={e => e.key === 'Enter' && !error && service.updatePort(port, e.currentTarget.value)} | ||
onKeyUp={e => setError(!service.isValidAddress(e.currentTarget.value))}></input>; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
packages/remote/src/electron-browser/style/port-forwarding-widget.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/******************************************************************************** | ||
* Copyright (C) 2024 TypeFox and others. | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Eclipse Public License v. 2.0 which is available at | ||
* http://www.eclipse.org/legal/epl-2.0. | ||
* | ||
* This Source Code may also be made available under the following Secondary | ||
* Licenses when the conditions for such availability set forth in the Eclipse | ||
* Public License v. 2.0 are satisfied: GNU General Public License, version 2 | ||
* with the GNU Classpath Exception which is available at | ||
* https://www.gnu.org/software/classpath/license.html. | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 | ||
********************************************************************************/ | ||
|
||
.port-table { | ||
width: 100%; | ||
margin: calc(var(--theia-ui-padding) * 2); | ||
table-layout: fixed; | ||
} | ||
|
||
.port-table-header { | ||
text-align: left; | ||
} | ||
|
||
.forward-port-button { | ||
margin-left: 0; | ||
width: 100%; | ||
} | ||
|
||
.button-cell { | ||
display: flex; | ||
padding-right: var(--theia-ui-padding); | ||
} | ||
|
||
.forwarded-address:hover { | ||
cursor: pointer; | ||
text-decoration: underline; | ||
} | ||
|
||
.port-edit-input-error { | ||
outline-color: var(--theia-inputValidation-errorBorder); | ||
} |
Oops, something went wrong.