Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions cli/tsc/dts/lib.deno.ns.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6387,6 +6387,9 @@ declare namespace Deno {
cid: number;
/** The port of the vsock to connect to. */
port: number;
} | {
transport: "tunnel";
kind: "agent";
};

/**
Expand Down
3 changes: 2 additions & 1 deletion ext/fetch/22_http_client.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ function createHttpClient(options) {
}
case "tcp":
case "unix":
case "vsock": {
case "vsock":
case "tunnel": {
break;
}
default: {
Expand Down
1 change: 1 addition & 0 deletions ext/fetch/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ data-url.workspace = true
deno_core.workspace = true
deno_error.workspace = true
deno_fs.workspace = true
deno_net.workspace = true
deno_path_util.workspace = true
deno_permissions.workspace = true
deno_tls.workspace = true
Expand Down
15 changes: 8 additions & 7 deletions ext/fetch/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,7 @@ where
let permissions = state.borrow_mut::<FP>();
permissions.check_net_vsock(*cid, *port, "Deno.createHttpClient()")?;
}
Proxy::Tunnel { .. } => {}
}
}

Expand Down Expand Up @@ -1144,16 +1145,15 @@ pub fn create_http_client(
}
intercept
}
Proxy::Tcp {
hostname: host,
port,
} => {
let target = proxy::Target::new_tcp(host, port);
Proxy::Tcp { hostname, port } => {
let target = proxy::Target::Tcp { hostname, port };
proxy::Intercept::all(target)
}
#[cfg(not(windows))]
Proxy::Unix { path } => {
let target = proxy::Target::new_unix(PathBuf::from(path));
let target = proxy::Target::Unix {
path: PathBuf::from(path),
};
proxy::Intercept::all(target)
}
#[cfg(windows)]
Expand All @@ -1166,7 +1166,7 @@ pub fn create_http_client(
target_os = "macos"
))]
Proxy::Vsock { cid, port } => {
let target = proxy::Target::new_vsock(cid, port);
let target = proxy::Target::Vsock { cid, port };
proxy::Intercept::all(target)
}
#[cfg(not(any(
Expand All @@ -1177,6 +1177,7 @@ pub fn create_http_client(
Proxy::Vsock { .. } => {
return Err(HttpClientCreateError::VsockProxyNotSupported);
}
Proxy::Tunnel { .. } => proxy::Intercept::all(proxy::Target::Tunnel),
};
proxies.prepend(intercept);
}
Expand Down
36 changes: 22 additions & 14 deletions ext/fetch/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ pub(crate) enum Target {
cid: u32,
port: u32,
},
Tunnel,
}

#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -242,6 +243,9 @@ impl Intercept {
Target::Vsock { .. } => {
// Auth not supported for Vsock sockets
}
Target::Tunnel => {
// Auth not supported for Vsock sockets
}
}
}
}
Expand Down Expand Up @@ -339,20 +343,6 @@ impl Target {

Some(target)
}

pub(crate) fn new_tcp(hostname: String, port: u16) -> Self {
Target::Tcp { hostname, port }
}

#[cfg(not(windows))]
pub(crate) fn new_unix(path: PathBuf) -> Self {
Target::Unix { path }
}

#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
pub(crate) fn new_vsock(cid: u32, port: u32) -> Self {
Target::Vsock { cid, port }
}
}

#[derive(Debug)]
Expand Down Expand Up @@ -560,6 +550,8 @@ pub enum Proxied<T> {
/// Forwarded via Vsock socket
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
Vsock(TokioIo<VsockStream>),
/// Forwarded through tunnel
Tunnel(TokioIo<deno_net::tunnel::TunnelStream>),
}

impl<C> Service<Uri> for ProxyConnector<C>
Expand Down Expand Up @@ -691,6 +683,15 @@ where
let io = VsockStream::connect(addr).await?;
Ok(Proxied::Vsock(TokioIo::new(io)))
}),
Target::Tunnel => Box::pin(async move {
let Some(tunnel) = deno_net::tunnel::get_tunnel() else {
return Err("tunnel is not connected".into());
};

let stream = tunnel.create_agent_stream().await?;

Ok(Proxied::Tunnel(TokioIo::new(stream)))
}),
};
}

Expand Down Expand Up @@ -808,6 +809,7 @@ where
target_os = "macos"
))]
Proxied::Vsock(ref mut p) => Pin::new(p).poll_read(cx, buf),
Proxied::Tunnel(ref mut p) => Pin::new(p).poll_read(cx, buf),
}
}
}
Expand Down Expand Up @@ -835,6 +837,7 @@ where
target_os = "macos"
))]
Proxied::Vsock(ref mut p) => Pin::new(p).poll_write(cx, buf),
Proxied::Tunnel(ref mut p) => Pin::new(p).poll_write(cx, buf),
}
}

Expand All @@ -856,6 +859,7 @@ where
target_os = "macos"
))]
Proxied::Vsock(ref mut p) => Pin::new(p).poll_flush(cx),
Proxied::Tunnel(ref mut p) => Pin::new(p).poll_flush(cx),
}
}

Expand All @@ -877,6 +881,7 @@ where
target_os = "macos"
))]
Proxied::Vsock(ref mut p) => Pin::new(p).poll_shutdown(cx),
Proxied::Tunnel(ref mut p) => Pin::new(p).poll_shutdown(cx),
}
}

Expand All @@ -895,6 +900,7 @@ where
target_os = "macos"
))]
Proxied::Vsock(ref p) => p.is_write_vectored(),
Proxied::Tunnel(ref p) => p.is_write_vectored(),
}
}

Expand All @@ -921,6 +927,7 @@ where
target_os = "macos"
))]
Proxied::Vsock(ref mut p) => Pin::new(p).poll_write_vectored(cx, bufs),
Proxied::Tunnel(ref mut p) => Pin::new(p).poll_write_vectored(cx, bufs),
}
}
}
Expand Down Expand Up @@ -958,6 +965,7 @@ where
target_os = "macos"
))]
Proxied::Vsock(_) => Connected::new().proxy(true),
Proxied::Tunnel(_) => Connected::new().proxy(true),
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions ext/tls/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,12 @@ impl ServerCertVerifier for NoServerNameVerification {
}
}

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub enum TunnelKind {
Agent,
}

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase", tag = "transport")]
pub enum Proxy {
Expand All @@ -243,6 +249,9 @@ pub enum Proxy {
cid: u32,
port: u32,
},
Tunnel {
kind: TunnelKind,
},
}

#[derive(Deserialize, Default, Debug, Clone)]
Expand Down
7 changes: 7 additions & 0 deletions tests/specs/run/tunnel/client.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
let serveAddr;

const client = Deno.createHttpClient({
proxy: { transport: "tunnel", kind: "agent" },
});

Deno.serve({
onListen(addr) {
serveAddr = addr;
},
}, async (req, info) => {
const headers = Object.fromEntries(req.headers);

await fetch("http://meow.com", { client });

return Response.json({
method: req.method,
url: req.url,
Expand Down
8 changes: 8 additions & 0 deletions tests/specs/run/tunnel/test.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
You are connected to https://localhost:[WILDLINE]
GET http://meow.com/ HTTP/1.1
accept: */*
accept-language: *
user-agent: Deno/[WILDCARD]
accept-encoding: gzip,br
host: meow.com


HTTP/1.1 200 OK
content-type: application/json
vary: Accept-Encoding
Expand Down
23 changes: 20 additions & 3 deletions tests/specs/run/tunnel/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ for await (const incoming of listener) {
async function handleConnection(incoming: Deno.QuicIncoming) {
const conn = await incoming.accept();

const incomingBidi = conn.incomingBidirectionalStreams.getReader();

{
const { value: bi } = await conn.incomingBidirectionalStreams
.getReader()
.read();
const { value: bi } = await incomingBidi.read();

const reader = bi.readable.getReader({ mode: "byob" });
const version = await readUint32LE(reader);
Expand Down Expand Up @@ -105,6 +105,23 @@ async function handleConnection(incoming: Deno.QuicIncoming) {
new TextEncoder().encode(`GET / HTTP/1.1\r\nHost: localhost\r\n\r\n`),
);

{
const { value: agentStream } = await incomingBidi.read();
const reader = agentStream.readable.getReader({ mode: "byob" });
const agentHeader = await readStreamHeader(reader);
if (agentHeader.headerType !== "Agent") {
conn.close({ closeCode: 1, reason: "expected Agent" });
return;
}
const { value } = await reader.read(new Uint8Array(1024));
console.log(new TextDecoder().decode(value));
await agentStream.writable.getWriter().write(
new TextEncoder().encode(
`HTTP/1.1 201 No Content\r\nConnection: close\r\n\r\n`,
),
);
}

const reader = stream.readable.getReader({ mode: "byob" });
const { value } = await reader.read(new Uint8Array(1024));
console.log(new TextDecoder().decode(value));
Expand Down
Loading