mirror of
https://github.com/Microsoft/vscode
synced 2024-10-02 17:32:41 +00:00
forwarding: make https work for port forwarding (#213943)
Closes https://github.com/microsoft/vscode/issues/201465
This commit is contained in:
parent
b82c3b9087
commit
54dd0ecc65
|
@ -2,7 +2,7 @@
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
use super::protocol::{self, PortPrivacy};
|
use super::protocol::{self, PortPrivacy, PortProtocol};
|
||||||
use crate::auth;
|
use crate::auth;
|
||||||
use crate::constants::{IS_INTERACTIVE_CLI, PROTOCOL_VERSION_TAG, TUNNEL_SERVICE_USER_AGENT};
|
use crate::constants::{IS_INTERACTIVE_CLI, PROTOCOL_VERSION_TAG, TUNNEL_SERVICE_USER_AGENT};
|
||||||
use crate::state::{LauncherPaths, PersistedState};
|
use crate::state::{LauncherPaths, PersistedState};
|
||||||
|
@ -221,8 +221,11 @@ impl ActiveTunnel {
|
||||||
&self,
|
&self,
|
||||||
port_number: u16,
|
port_number: u16,
|
||||||
privacy: PortPrivacy,
|
privacy: PortPrivacy,
|
||||||
|
protocol: PortProtocol,
|
||||||
) -> Result<(), AnyError> {
|
) -> Result<(), AnyError> {
|
||||||
self.manager.add_port_tcp(port_number, privacy).await?;
|
self.manager
|
||||||
|
.add_port_tcp(port_number, privacy, protocol)
|
||||||
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -972,13 +975,14 @@ impl ActiveTunnelManager {
|
||||||
&self,
|
&self,
|
||||||
port_number: u16,
|
port_number: u16,
|
||||||
privacy: PortPrivacy,
|
privacy: PortPrivacy,
|
||||||
|
protocol: PortProtocol,
|
||||||
) -> Result<(), WrappedError> {
|
) -> Result<(), WrappedError> {
|
||||||
self.relay
|
self.relay
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.add_port(&TunnelPort {
|
.add_port(&TunnelPort {
|
||||||
port_number,
|
port_number,
|
||||||
protocol: Some(TUNNEL_PROTOCOL_AUTO.to_owned()),
|
protocol: Some(protocol.to_contract_str().to_string()),
|
||||||
access_control: Some(privacy_to_tunnel_acl(privacy)),
|
access_control: Some(privacy_to_tunnel_acl(privacy)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
|
|
|
@ -27,7 +27,7 @@ use super::{
|
||||||
protocol::{
|
protocol::{
|
||||||
self,
|
self,
|
||||||
forward_singleton::{PortList, SetPortsResponse},
|
forward_singleton::{PortList, SetPortsResponse},
|
||||||
PortPrivacy,
|
PortPrivacy, PortProtocol,
|
||||||
},
|
},
|
||||||
shutdown_signal::ShutdownSignal,
|
shutdown_signal::ShutdownSignal,
|
||||||
};
|
};
|
||||||
|
@ -71,8 +71,13 @@ impl PortCount {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct PortMapRec {
|
||||||
|
count: PortCount,
|
||||||
|
protocol: PortProtocol,
|
||||||
|
}
|
||||||
|
|
||||||
type PortMap = HashMap<u16, PortCount>;
|
type PortMap = HashMap<u16, PortMapRec>;
|
||||||
|
|
||||||
/// The PortForwardingHandle is given out to multiple consumers to allow
|
/// The PortForwardingHandle is given out to multiple consumers to allow
|
||||||
/// them to set_ports that they want to be forwarded.
|
/// them to set_ports that they want to be forwarded.
|
||||||
|
@ -99,8 +104,8 @@ impl PortForwardingSender {
|
||||||
for p in current.iter() {
|
for p in current.iter() {
|
||||||
if !ports.contains(p) {
|
if !ports.contains(p) {
|
||||||
let n = v.get_mut(&p.number).expect("expected port in map");
|
let n = v.get_mut(&p.number).expect("expected port in map");
|
||||||
n[p.privacy] -= 1;
|
n.count[p.privacy] -= 1;
|
||||||
if n.is_empty() {
|
if n.count.is_empty() {
|
||||||
v.remove(&p.number);
|
v.remove(&p.number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,12 +115,19 @@ impl PortForwardingSender {
|
||||||
if !current.contains(p) {
|
if !current.contains(p) {
|
||||||
match v.get_mut(&p.number) {
|
match v.get_mut(&p.number) {
|
||||||
Some(n) => {
|
Some(n) => {
|
||||||
n[p.privacy] += 1;
|
n.count[p.privacy] += 1;
|
||||||
|
n.protocol = p.protocol;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let mut pc = PortCount::default();
|
let mut count = PortCount::default();
|
||||||
pc[p.privacy] += 1;
|
count[p.privacy] += 1;
|
||||||
v.insert(p.number, pc);
|
v.insert(
|
||||||
|
p.number,
|
||||||
|
PortMapRec {
|
||||||
|
count,
|
||||||
|
protocol: p.protocol,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -164,22 +176,34 @@ impl PortForwardingReceiver {
|
||||||
while self.receiver.changed().await.is_ok() {
|
while self.receiver.changed().await.is_ok() {
|
||||||
let next = self.receiver.borrow().clone();
|
let next = self.receiver.borrow().clone();
|
||||||
|
|
||||||
for (port, count) in current.iter() {
|
for (port, rec) in current.iter() {
|
||||||
let privacy = count.primary_privacy();
|
let privacy = rec.count.primary_privacy();
|
||||||
if !matches!(next.get(port), Some(n) if n.primary_privacy() == privacy) {
|
if !matches!(next.get(port), Some(n) if n.count.primary_privacy() == privacy) {
|
||||||
match tunnel.remove_port(*port).await {
|
match tunnel.remove_port(*port).await {
|
||||||
Ok(_) => info!(log, "stopped forwarding port {} at {:?}", *port, privacy),
|
Ok(_) => info!(
|
||||||
Err(e) => error!(log, "failed to stop forwarding port {}: {}", port, e),
|
log,
|
||||||
|
"stopped forwarding {} port {} at {:?}", rec.protocol, *port, privacy
|
||||||
|
),
|
||||||
|
Err(e) => error!(
|
||||||
|
log,
|
||||||
|
"failed to stop forwarding {} port {}: {}", rec.protocol, port, e
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (port, count) in next.iter() {
|
for (port, rec) in next.iter() {
|
||||||
let privacy = count.primary_privacy();
|
let privacy = rec.count.primary_privacy();
|
||||||
if !matches!(current.get(port), Some(n) if n.primary_privacy() == privacy) {
|
if !matches!(current.get(port), Some(n) if n.count.primary_privacy() == privacy) {
|
||||||
match tunnel.add_port_tcp(*port, privacy).await {
|
match tunnel.add_port_tcp(*port, privacy, rec.protocol).await {
|
||||||
Ok(_) => info!(log, "forwarding port {} at {:?}", port, privacy),
|
Ok(_) => info!(
|
||||||
Err(e) => error!(log, "failed to forward port {}: {}", port, e),
|
log,
|
||||||
|
"forwarding {} port {} at {:?}", rec.protocol, port, privacy
|
||||||
|
),
|
||||||
|
Err(e) => error!(
|
||||||
|
log,
|
||||||
|
"failed to forward {} port {}: {}", rec.protocol, port, e
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,10 @@ use crate::{
|
||||||
util::errors::{AnyError, CannotForwardControlPort, ServerHasClosed},
|
util::errors::{AnyError, CannotForwardControlPort, ServerHasClosed},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{dev_tunnels::ActiveTunnel, protocol::PortPrivacy};
|
use super::{
|
||||||
|
dev_tunnels::ActiveTunnel,
|
||||||
|
protocol::{PortPrivacy, PortProtocol},
|
||||||
|
};
|
||||||
|
|
||||||
pub enum PortForwardingRec {
|
pub enum PortForwardingRec {
|
||||||
Forward(u16, PortPrivacy, oneshot::Sender<Result<String, AnyError>>),
|
Forward(u16, PortPrivacy, oneshot::Sender<Result<String, AnyError>>),
|
||||||
|
@ -89,7 +92,9 @@ impl PortForwardingProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.forwarded.contains(&port) {
|
if !self.forwarded.contains(&port) {
|
||||||
tunnel.add_port_tcp(port, privacy).await?;
|
tunnel
|
||||||
|
.add_port_tcp(port, privacy, PortProtocol::Auto)
|
||||||
|
.await?;
|
||||||
self.forwarded.insert(port);
|
self.forwarded.insert(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -299,10 +299,40 @@ pub enum PortPrivacy {
|
||||||
Private,
|
Private,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, PartialEq, Copy, Eq, Clone, Debug)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum PortProtocol {
|
||||||
|
Auto,
|
||||||
|
Http,
|
||||||
|
Https,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for PortProtocol {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.to_contract_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PortProtocol {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Auto
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PortProtocol {
|
||||||
|
pub fn to_contract_str(&self) -> &'static str {
|
||||||
|
match *self {
|
||||||
|
Self::Auto => tunnels::contracts::TUNNEL_PROTOCOL_AUTO,
|
||||||
|
Self::Http => tunnels::contracts::TUNNEL_PROTOCOL_HTTP,
|
||||||
|
Self::Https => tunnels::contracts::TUNNEL_PROTOCOL_HTTPS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub mod forward_singleton {
|
pub mod forward_singleton {
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::PortPrivacy;
|
use super::{PortPrivacy, PortProtocol};
|
||||||
|
|
||||||
pub const METHOD_SET_PORTS: &str = "set_ports";
|
pub const METHOD_SET_PORTS: &str = "set_ports";
|
||||||
|
|
||||||
|
@ -310,6 +340,7 @@ pub mod forward_singleton {
|
||||||
pub struct PortRec {
|
pub struct PortRec {
|
||||||
pub number: u16,
|
pub number: u16,
|
||||||
pub privacy: PortPrivacy,
|
pub privacy: PortPrivacy,
|
||||||
|
pub protocol: PortProtocol,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type PortList = Vec<PortRec>;
|
pub type PortList = Vec<PortRec>;
|
||||||
|
|
|
@ -37,6 +37,7 @@ class Tunnel implements vscode.Tunnel {
|
||||||
constructor(
|
constructor(
|
||||||
public readonly remoteAddress: { port: number; host: string },
|
public readonly remoteAddress: { port: number; host: string },
|
||||||
public readonly privacy: TunnelPrivacyId,
|
public readonly privacy: TunnelPrivacyId,
|
||||||
|
public readonly protocol: 'http' | 'https',
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public setPortFormat(formatString: string) {
|
public setPortFormat(formatString: string) {
|
||||||
|
@ -82,7 +83,7 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||||
{
|
{
|
||||||
tunnelFeatures: {
|
tunnelFeatures: {
|
||||||
elevation: false,
|
elevation: false,
|
||||||
protocol: false,
|
protocol: true,
|
||||||
privacyOptions: [
|
privacyOptions: [
|
||||||
{ themeIcon: 'globe', id: TunnelPrivacyId.Public, label: vscode.l10n.t('Public') },
|
{ themeIcon: 'globe', id: TunnelPrivacyId.Public, label: vscode.l10n.t('Public') },
|
||||||
{ themeIcon: 'lock', id: TunnelPrivacyId.Private, label: vscode.l10n.t('Private') },
|
{ themeIcon: 'lock', id: TunnelPrivacyId.Private, label: vscode.l10n.t('Private') },
|
||||||
|
@ -152,6 +153,7 @@ class TunnelProvider implements vscode.TunnelProvider {
|
||||||
const tunnel = new Tunnel(
|
const tunnel = new Tunnel(
|
||||||
tunnelOptions.remoteAddress,
|
tunnelOptions.remoteAddress,
|
||||||
(tunnelOptions.privacy as TunnelPrivacyId) || TunnelPrivacyId.Private,
|
(tunnelOptions.privacy as TunnelPrivacyId) || TunnelPrivacyId.Private,
|
||||||
|
tunnelOptions.protocol === 'https' ? 'https' : 'http',
|
||||||
);
|
);
|
||||||
|
|
||||||
this.tunnels.add(tunnel);
|
this.tunnels.add(tunnel);
|
||||||
|
@ -238,7 +240,7 @@ class TunnelProvider implements vscode.TunnelProvider {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ports = [...this.tunnels].map(t => ({ number: t.remoteAddress.port, privacy: t.privacy }));
|
const ports = [...this.tunnels].map(t => ({ number: t.remoteAddress.port, privacy: t.privacy, protocol: t.protocol }));
|
||||||
this.state.process.stdin.write(`${JSON.stringify(ports)}\n`);
|
this.state.process.stdin.write(`${JSON.stringify(ports)}\n`);
|
||||||
|
|
||||||
if (ports.length === 0 && !this.state.cleanupTimeout) {
|
if (ports.length === 0 && !this.state.cleanupTimeout) {
|
||||||
|
|
Loading…
Reference in a new issue