@microsoft/dev-tunnels-ssh-tcp
Version:
SSH TCP extensions library for Dev Tunnels
148 lines • 6.26 kB
JavaScript
"use strict";
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
Object.defineProperty(exports, "__esModule", { value: true });
exports.ForwardedPortsCollection = void 0;
const vscode_jsonrpc_1 = require("vscode-jsonrpc");
const forwardedPortEventArgs_1 = require("./forwardedPortEventArgs");
/**
* Tracks the list of ports that are currently being forwarded between the SSH client and server,
* along with the set of channel connections for each forwarded port.
*
* Ports forwarded in either direction (client->server or server->client) are tracked in separate
* collections. Typically within a session the forwarding is done only in one direction, though
* the protocol supports bi-directional forwarding.
*
* @see PortForwardingService.RemoteForwardedPorts
* @see PortForwardingService.LocalForwardedPorts
*/
class ForwardedPortsCollection {
constructor() {
/**
* Maintains a mapping from port keys to port objects and channels for the port.
*
* The ForwardedPort string representation is used as the keys.
*/
this.portChannelMap = new Map();
this.portAddedEmitter = new vscode_jsonrpc_1.Emitter();
/** Event raised when a port is added to the collection. */
this.onPortAdded = this.portAddedEmitter.event;
this.portUpdatedEmitter = new vscode_jsonrpc_1.Emitter();
/**
* Event raised when a port in the collection is updated. "Updating" a port doesn't
* change anything at the SSH protocol level, but the application may use this event
* as a signal to update or refresh its state for the forwarded port.
*/
this.onPortUpdated = this.portUpdatedEmitter.event;
this.portRemovedEmitter = new vscode_jsonrpc_1.Emitter();
/** Event raised when a port is removed from the collection. */
this.onPortRemoved = this.portRemovedEmitter.event;
this.portChannelAddedEmitter = new vscode_jsonrpc_1.Emitter();
/** Event raised when a channel is added to the collection. */
this.onPortChannelAdded = this.portChannelAddedEmitter.event;
this.portChannelRemovedEmitter = new vscode_jsonrpc_1.Emitter();
/** Event raised when a channel is removed from the collection. */
this.onPortChannelRemoved = this.portChannelRemovedEmitter.event;
}
/** Gets the number of ports in the collection. */
get size() {
return this.portChannelMap.size;
}
/** Checks whether a port is in the collection. */
has(port) {
return this.portChannelMap.has(port.toString());
}
/** Lists all the ports in the collection. */
*values() {
for (const [port, channels] of this.portChannelMap.values()) {
yield port;
}
}
/** Iterates over all the ports in the collection. */
[Symbol.iterator]() {
return this.values();
}
/** Lists all the ports in the collection. */
*entries() {
for (const [port, channels] of this.portChannelMap.values()) {
yield [port, port];
}
}
/**
* Lists all the ports in the collection.
* (In a set, the keys are the same as the values.)
*/
keys() {
return this.values();
}
/** Iterates over all the ports in the collection, invoking a callback function on each. */
forEach(callbackfn, thisArg) {
for (const [port, channels] of this.portChannelMap.values()) {
callbackfn.apply(thisArg, [port, port, this]);
}
}
getChannels(port) {
const portAndChannels = this.portChannelMap.get(port.toString());
if (!portAndChannels) {
throw new Error(`Port ${port} is not in the collection.`);
}
return portAndChannels[1];
}
/** Finds the first port in the collection that matches a predicate. */
find(predicate) {
for (const port of this.values()) {
if (predicate(port)) {
return port;
}
}
return undefined;
}
/* @internal */
addOrUpdatePort(port) {
if (this.has(port)) {
this.portUpdatedEmitter.fire(new forwardedPortEventArgs_1.ForwardedPortEventArgs(port));
}
this.portChannelMap.set(port.toString(), [port, []]);
this.portAddedEmitter.fire(new forwardedPortEventArgs_1.ForwardedPortEventArgs(port));
}
/* @internal */
removePort(port) {
if (!this.has(port)) {
return false;
}
this.portChannelMap.delete(port.toString());
this.portRemovedEmitter.fire(new forwardedPortEventArgs_1.ForwardedPortEventArgs(port));
return true;
}
/* @internal */
addChannel(port, channel) {
const portAndChannels = this.portChannelMap.get(port.toString());
if (!portAndChannels) {
throw new Error(`Port ${port} is not in the collection.`);
}
const portChannels = portAndChannels[1];
if (portChannels.find((c) => c.channelId === channel.channelId)) {
throw new Error(`Channel ${channel.channelId} is already in the collection for port ${port}`);
}
portChannels.push(channel);
channel.onClosed(() => this.tryRemoveChannel(port, channel));
this.portChannelAddedEmitter.fire(new forwardedPortEventArgs_1.ForwardedPortChannelEventArgs(port, channel));
}
tryRemoveChannel(port, channel) {
const portAndChannels = this.portChannelMap.get(port.toString());
if (portAndChannels) {
const portChannels = portAndChannels[1];
const index = portChannels.findIndex((c) => c.channelId === channel.channelId);
if (index >= 0) {
portChannels.splice(index, 1);
this.portChannelRemovedEmitter.fire(new forwardedPortEventArgs_1.ForwardedPortChannelEventArgs(port, channel));
}
}
}
toString() {
return [...this].join(', ');
}
}
exports.ForwardedPortsCollection = ForwardedPortsCollection;
//# sourceMappingURL=forwardedPortsCollection.js.map