151 lines
4.8 KiB
TypeScript
151 lines
4.8 KiB
TypeScript
/**
|
|
* Mobile Proxy Test — Bun Reverse Proxy Server
|
|
*
|
|
* Sits on port 3000 (what ngrok tunnels) and:
|
|
* GET / -> serves our clean mobile viewer (view.html)
|
|
* Everything else -> reverse-proxied to ws-scrcpy on port 8000
|
|
*
|
|
* WebSocket upgrades are proxied transparently so the ws-scrcpy
|
|
* player running inside our iframe can talk to the scrcpy server.
|
|
*/
|
|
|
|
const WS_SCRCPY_ORIGIN = "http://localhost:8000";
|
|
const PROXY_PORT = 3000;
|
|
|
|
const viewHtml = await Bun.file(
|
|
new URL("./view.html", import.meta.url).pathname,
|
|
).text();
|
|
|
|
const server = Bun.serve({
|
|
port: PROXY_PORT,
|
|
hostname: "0.0.0.0",
|
|
|
|
async fetch(req, server) {
|
|
const url = new URL(req.url);
|
|
|
|
// Serve our clean mobile viewer at root only
|
|
// (the iframe loads ws-scrcpy's own page via /?scrcpy=1)
|
|
if (
|
|
(url.pathname === "/" || url.pathname === "/index.html") &&
|
|
!url.searchParams.has("scrcpy")
|
|
) {
|
|
return new Response(viewHtml, {
|
|
headers: { "Content-Type": "text/html; charset=utf-8" },
|
|
});
|
|
}
|
|
|
|
// Upgrade WebSocket connections — proxy to ws-scrcpy
|
|
if (req.headers.get("upgrade")?.toLowerCase() === "websocket") {
|
|
const targetUrl = `${WS_SCRCPY_ORIGIN.replace("http", "ws")}${url.pathname}${url.search}`;
|
|
|
|
// Use Bun's WebSocket upgrade to establish a client-side WS to ws-scrcpy
|
|
// and bridge the two connections
|
|
const success = server.upgrade(req, {
|
|
data: { targetUrl },
|
|
});
|
|
|
|
if (success) return undefined;
|
|
return new Response("WebSocket upgrade failed", { status: 400 });
|
|
}
|
|
|
|
// Proxy all other HTTP requests to ws-scrcpy
|
|
const targetUrl = `${WS_SCRCPY_ORIGIN}${url.pathname}${url.search}`;
|
|
|
|
try {
|
|
const proxyRes = await fetch(targetUrl, {
|
|
method: req.method,
|
|
headers: req.headers,
|
|
body:
|
|
req.method !== "GET" && req.method !== "HEAD"
|
|
? req.body
|
|
: undefined,
|
|
});
|
|
|
|
// Clone response with CORS headers stripped / adjusted
|
|
const headers = new Headers(proxyRes.headers);
|
|
headers.delete("content-encoding"); // Bun handles this
|
|
|
|
return new Response(proxyRes.body, {
|
|
status: proxyRes.status,
|
|
statusText: proxyRes.statusText,
|
|
headers,
|
|
});
|
|
} catch (err) {
|
|
console.error(`Proxy error for ${url.pathname}:`, err);
|
|
return new Response(
|
|
"ws-scrcpy not reachable — is it running on port 8000?",
|
|
{
|
|
status: 502,
|
|
},
|
|
);
|
|
}
|
|
},
|
|
|
|
websocket: {
|
|
async open(ws) {
|
|
const { targetUrl } = ws.data as { targetUrl: string };
|
|
|
|
// Open a WebSocket connection to ws-scrcpy
|
|
const upstream = new WebSocket(targetUrl);
|
|
|
|
// Store upstream reference on ws data for cleanup
|
|
(ws.data as any).upstream = upstream;
|
|
|
|
upstream.binaryType = "arraybuffer";
|
|
|
|
upstream.onopen = () => {
|
|
// Connection established, nothing extra needed
|
|
};
|
|
|
|
upstream.onmessage = (event) => {
|
|
try {
|
|
if (event.data instanceof ArrayBuffer) {
|
|
ws.sendBinary(new Uint8Array(event.data));
|
|
} else {
|
|
ws.sendText(event.data);
|
|
}
|
|
} catch {
|
|
// Client disconnected
|
|
}
|
|
};
|
|
|
|
upstream.onclose = () => {
|
|
ws.close();
|
|
};
|
|
|
|
upstream.onerror = (err) => {
|
|
console.error("Upstream WS error:", err);
|
|
ws.close();
|
|
};
|
|
},
|
|
|
|
message(ws, message) {
|
|
const upstream = (ws.data as any).upstream as WebSocket | undefined;
|
|
if (!upstream || upstream.readyState !== WebSocket.OPEN) return;
|
|
|
|
try {
|
|
if (typeof message === "string") {
|
|
upstream.send(message);
|
|
} else {
|
|
upstream.send(message);
|
|
}
|
|
} catch {
|
|
// Upstream disconnected
|
|
}
|
|
},
|
|
|
|
close(ws) {
|
|
const upstream = (ws.data as any).upstream as WebSocket | undefined;
|
|
if (upstream && upstream.readyState === WebSocket.OPEN) {
|
|
upstream.close();
|
|
}
|
|
},
|
|
},
|
|
});
|
|
|
|
console.log(`\n Mobile Proxy running on http://localhost:${server.port}`);
|
|
console.log(` Proxying to ws-scrcpy at ${WS_SCRCPY_ORIGIN}`);
|
|
console.log(
|
|
`\n Point ngrok at port ${server.port}, then open the ngrok URL on the user's phone.\n`,
|
|
);
|