mirror of
https://github.com/denoland/deno.git
synced 2025-09-26 20:29:11 +00:00
feat(unstable): fetch tunnel proxy
This commit is contained in:
parent
ebcb2fa294
commit
2e9daab3ea
10 changed files with 81 additions and 25 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2106,6 +2106,7 @@ dependencies = [
|
||||||
"deno_core",
|
"deno_core",
|
||||||
"deno_error",
|
"deno_error",
|
||||||
"deno_fs",
|
"deno_fs",
|
||||||
|
"deno_net",
|
||||||
"deno_path_util",
|
"deno_path_util",
|
||||||
"deno_permissions",
|
"deno_permissions",
|
||||||
"deno_tls",
|
"deno_tls",
|
||||||
|
|
3
cli/tsc/dts/lib.deno.ns.d.ts
vendored
3
cli/tsc/dts/lib.deno.ns.d.ts
vendored
|
@ -6387,6 +6387,9 @@ declare namespace Deno {
|
||||||
cid: number;
|
cid: number;
|
||||||
/** The port of the vsock to connect to. */
|
/** The port of the vsock to connect to. */
|
||||||
port: number;
|
port: number;
|
||||||
|
} | {
|
||||||
|
transport: "tunnel";
|
||||||
|
kind: "agent";
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -77,7 +77,8 @@ function createHttpClient(options) {
|
||||||
}
|
}
|
||||||
case "tcp":
|
case "tcp":
|
||||||
case "unix":
|
case "unix":
|
||||||
case "vsock": {
|
case "vsock":
|
||||||
|
case "tunnel": {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
|
|
@ -20,6 +20,7 @@ data-url.workspace = true
|
||||||
deno_core.workspace = true
|
deno_core.workspace = true
|
||||||
deno_error.workspace = true
|
deno_error.workspace = true
|
||||||
deno_fs.workspace = true
|
deno_fs.workspace = true
|
||||||
|
deno_net.workspace = true
|
||||||
deno_path_util.workspace = true
|
deno_path_util.workspace = true
|
||||||
deno_permissions.workspace = true
|
deno_permissions.workspace = true
|
||||||
deno_tls.workspace = true
|
deno_tls.workspace = true
|
||||||
|
|
|
@ -972,6 +972,7 @@ where
|
||||||
let permissions = state.borrow_mut::<FP>();
|
let permissions = state.borrow_mut::<FP>();
|
||||||
permissions.check_net_vsock(*cid, *port, "Deno.createHttpClient()")?;
|
permissions.check_net_vsock(*cid, *port, "Deno.createHttpClient()")?;
|
||||||
}
|
}
|
||||||
|
Proxy::Tunnel { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1144,16 +1145,15 @@ pub fn create_http_client(
|
||||||
}
|
}
|
||||||
intercept
|
intercept
|
||||||
}
|
}
|
||||||
Proxy::Tcp {
|
Proxy::Tcp { hostname, port } => {
|
||||||
hostname: host,
|
let target = proxy::Target::Tcp { hostname, port };
|
||||||
port,
|
|
||||||
} => {
|
|
||||||
let target = proxy::Target::new_tcp(host, port);
|
|
||||||
proxy::Intercept::all(target)
|
proxy::Intercept::all(target)
|
||||||
}
|
}
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
Proxy::Unix { path } => {
|
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)
|
proxy::Intercept::all(target)
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
@ -1166,7 +1166,7 @@ pub fn create_http_client(
|
||||||
target_os = "macos"
|
target_os = "macos"
|
||||||
))]
|
))]
|
||||||
Proxy::Vsock { cid, port } => {
|
Proxy::Vsock { cid, port } => {
|
||||||
let target = proxy::Target::new_vsock(cid, port);
|
let target = proxy::Target::Vsock { cid, port };
|
||||||
proxy::Intercept::all(target)
|
proxy::Intercept::all(target)
|
||||||
}
|
}
|
||||||
#[cfg(not(any(
|
#[cfg(not(any(
|
||||||
|
@ -1177,6 +1177,7 @@ pub fn create_http_client(
|
||||||
Proxy::Vsock { .. } => {
|
Proxy::Vsock { .. } => {
|
||||||
return Err(HttpClientCreateError::VsockProxyNotSupported);
|
return Err(HttpClientCreateError::VsockProxyNotSupported);
|
||||||
}
|
}
|
||||||
|
Proxy::Tunnel { .. } => proxy::Intercept::all(proxy::Target::Tunnel),
|
||||||
};
|
};
|
||||||
proxies.prepend(intercept);
|
proxies.prepend(intercept);
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,6 +128,7 @@ pub(crate) enum Target {
|
||||||
cid: u32,
|
cid: u32,
|
||||||
port: u32,
|
port: u32,
|
||||||
},
|
},
|
||||||
|
Tunnel,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
@ -242,6 +243,9 @@ impl Intercept {
|
||||||
Target::Vsock { .. } => {
|
Target::Vsock { .. } => {
|
||||||
// Auth not supported for Vsock sockets
|
// Auth not supported for Vsock sockets
|
||||||
}
|
}
|
||||||
|
Target::Tunnel => {
|
||||||
|
// Auth not supported for Vsock sockets
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -339,20 +343,6 @@ impl Target {
|
||||||
|
|
||||||
Some(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)]
|
#[derive(Debug)]
|
||||||
|
@ -560,6 +550,8 @@ pub enum Proxied<T> {
|
||||||
/// Forwarded via Vsock socket
|
/// Forwarded via Vsock socket
|
||||||
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
|
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
|
||||||
Vsock(TokioIo<VsockStream>),
|
Vsock(TokioIo<VsockStream>),
|
||||||
|
/// Forwarded through tunnel
|
||||||
|
Tunnel(TokioIo<deno_net::tunnel::TunnelStream>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> Service<Uri> for ProxyConnector<C>
|
impl<C> Service<Uri> for ProxyConnector<C>
|
||||||
|
@ -691,6 +683,15 @@ where
|
||||||
let io = VsockStream::connect(addr).await?;
|
let io = VsockStream::connect(addr).await?;
|
||||||
Ok(Proxied::Vsock(TokioIo::new(io)))
|
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)))
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -808,6 +809,7 @@ where
|
||||||
target_os = "macos"
|
target_os = "macos"
|
||||||
))]
|
))]
|
||||||
Proxied::Vsock(ref mut p) => Pin::new(p).poll_read(cx, buf),
|
Proxied::Vsock(ref mut p) => Pin::new(p).poll_read(cx, buf),
|
||||||
|
Proxied::Tunnel(ref mut p) => Pin::new(p).poll_read(cx, buf),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -835,6 +837,7 @@ where
|
||||||
target_os = "macos"
|
target_os = "macos"
|
||||||
))]
|
))]
|
||||||
Proxied::Vsock(ref mut p) => Pin::new(p).poll_write(cx, buf),
|
Proxied::Vsock(ref mut p) => Pin::new(p).poll_write(cx, buf),
|
||||||
|
Proxied::Tunnel(ref mut p) => Pin::new(p).poll_write(cx, buf),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -856,6 +859,7 @@ where
|
||||||
target_os = "macos"
|
target_os = "macos"
|
||||||
))]
|
))]
|
||||||
Proxied::Vsock(ref mut p) => Pin::new(p).poll_flush(cx),
|
Proxied::Vsock(ref mut p) => Pin::new(p).poll_flush(cx),
|
||||||
|
Proxied::Tunnel(ref mut p) => Pin::new(p).poll_flush(cx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -877,6 +881,7 @@ where
|
||||||
target_os = "macos"
|
target_os = "macos"
|
||||||
))]
|
))]
|
||||||
Proxied::Vsock(ref mut p) => Pin::new(p).poll_shutdown(cx),
|
Proxied::Vsock(ref mut p) => Pin::new(p).poll_shutdown(cx),
|
||||||
|
Proxied::Tunnel(ref mut p) => Pin::new(p).poll_shutdown(cx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -895,6 +900,7 @@ where
|
||||||
target_os = "macos"
|
target_os = "macos"
|
||||||
))]
|
))]
|
||||||
Proxied::Vsock(ref p) => p.is_write_vectored(),
|
Proxied::Vsock(ref p) => p.is_write_vectored(),
|
||||||
|
Proxied::Tunnel(ref p) => p.is_write_vectored(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -921,6 +927,7 @@ where
|
||||||
target_os = "macos"
|
target_os = "macos"
|
||||||
))]
|
))]
|
||||||
Proxied::Vsock(ref mut p) => Pin::new(p).poll_write_vectored(cx, bufs),
|
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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -958,6 +965,7 @@ where
|
||||||
target_os = "macos"
|
target_os = "macos"
|
||||||
))]
|
))]
|
||||||
Proxied::Vsock(_) => Connected::new().proxy(true),
|
Proxied::Vsock(_) => Connected::new().proxy(true),
|
||||||
|
Proxied::Tunnel(_) => Connected::new().proxy(true),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,6 +224,12 @@ impl ServerCertVerifier for NoServerNameVerification {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub enum TunnelKind {
|
||||||
|
Agent,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
#[serde(rename_all = "camelCase", tag = "transport")]
|
#[serde(rename_all = "camelCase", tag = "transport")]
|
||||||
pub enum Proxy {
|
pub enum Proxy {
|
||||||
|
@ -243,6 +249,9 @@ pub enum Proxy {
|
||||||
cid: u32,
|
cid: u32,
|
||||||
port: u32,
|
port: u32,
|
||||||
},
|
},
|
||||||
|
Tunnel {
|
||||||
|
kind: TunnelKind,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Debug, Clone)]
|
#[derive(Deserialize, Default, Debug, Clone)]
|
||||||
|
|
|
@ -1,11 +1,18 @@
|
||||||
let serveAddr;
|
let serveAddr;
|
||||||
|
|
||||||
|
const client = Deno.createHttpClient({
|
||||||
|
proxy: { transport: "tunnel", kind: "agent" },
|
||||||
|
});
|
||||||
|
|
||||||
Deno.serve({
|
Deno.serve({
|
||||||
onListen(addr) {
|
onListen(addr) {
|
||||||
serveAddr = addr;
|
serveAddr = addr;
|
||||||
},
|
},
|
||||||
}, async (req, info) => {
|
}, async (req, info) => {
|
||||||
const headers = Object.fromEntries(req.headers);
|
const headers = Object.fromEntries(req.headers);
|
||||||
|
|
||||||
|
await fetch("http://meow.com", { client });
|
||||||
|
|
||||||
return Response.json({
|
return Response.json({
|
||||||
method: req.method,
|
method: req.method,
|
||||||
url: req.url,
|
url: req.url,
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
You are connected to https://localhost:[WILDLINE]
|
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
|
HTTP/1.1 200 OK
|
||||||
content-type: application/json
|
content-type: application/json
|
||||||
vary: Accept-Encoding
|
vary: Accept-Encoding
|
||||||
|
|
|
@ -43,10 +43,10 @@ for await (const incoming of listener) {
|
||||||
async function handleConnection(incoming: Deno.QuicIncoming) {
|
async function handleConnection(incoming: Deno.QuicIncoming) {
|
||||||
const conn = await incoming.accept();
|
const conn = await incoming.accept();
|
||||||
|
|
||||||
|
const incomingBidi = conn.incomingBidirectionalStreams.getReader();
|
||||||
|
|
||||||
{
|
{
|
||||||
const { value: bi } = await conn.incomingBidirectionalStreams
|
const { value: bi } = await incomingBidi.read();
|
||||||
.getReader()
|
|
||||||
.read();
|
|
||||||
|
|
||||||
const reader = bi.readable.getReader({ mode: "byob" });
|
const reader = bi.readable.getReader({ mode: "byob" });
|
||||||
const version = await readUint32LE(reader);
|
const version = await readUint32LE(reader);
|
||||||
|
@ -105,6 +105,23 @@ async function handleConnection(incoming: Deno.QuicIncoming) {
|
||||||
new TextEncoder().encode(`GET / HTTP/1.1\r\nHost: localhost\r\n\r\n`),
|
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 reader = stream.readable.getReader({ mode: "byob" });
|
||||||
const { value } = await reader.read(new Uint8Array(1024));
|
const { value } = await reader.read(new Uint8Array(1024));
|
||||||
console.log(new TextDecoder().decode(value));
|
console.log(new TextDecoder().decode(value));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue