mirror of
https://github.com/lush2020/edgetunnel.git
synced 2026-03-24 17:18:25 +08:00
@@ -6,16 +6,13 @@ async function serveClient(req: Request, basePath: string) {
|
||||
const pathname = new URL(req.url).pathname;
|
||||
if (pathname.startsWith('/assets')) {
|
||||
const resp = await serveDir(req, {
|
||||
fsRoot: `${Deno.cwd()}/apps/deno-vless/src/client`,
|
||||
fsRoot: `${Deno.cwd()}/dist/apps/cf-page`,
|
||||
});
|
||||
resp.headers.set('cache-control', 'public, max-age=2592000');
|
||||
return resp;
|
||||
}
|
||||
if (pathname.includes(basePath)) {
|
||||
return await serveFile(
|
||||
req,
|
||||
`${Deno.cwd()}/apps/deno-vless/src/client/index.html`
|
||||
);
|
||||
return await serveFile(req, `${Deno.cwd()}/dist/apps/cf-page/index.html`);
|
||||
}
|
||||
const basicAuth = req.headers.get('Authorization') || '';
|
||||
const authString = basicAuth.split(' ')?.[1] || '';
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,20 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Edge Tunnel VLESS Deno</title>
|
||||
<base href="/" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<script type="module" crossorigin src="/assets/index.0f81511d.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index.74140327.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,16 +0,0 @@
|
||||
import { serve } from 'https://deno.land/std@0.170.0/http/server.ts';
|
||||
|
||||
const handler = async (req: Request) => {
|
||||
console.log('start');
|
||||
|
||||
const connect = await Deno.connect({
|
||||
port: 443,
|
||||
hostname: '2606:4700:0000:0000:0000:0000:6810:7c60',
|
||||
});
|
||||
|
||||
console.log(connect.remoteAddr);
|
||||
return new Response('hello', {
|
||||
status: 200,
|
||||
});
|
||||
};
|
||||
serve(handler, { port: 8081, hostname: '0.0.0.0' });
|
||||
@@ -1,33 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>401 - UUID Not Valid</title>
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
<h1 style="color: red;">Not set valid UUID in Environment Variables.</h1>
|
||||
<h2>Please use tool to generate and <span style="color: red;">remember</span> UUID or use this one <span
|
||||
style="color: blue;" id="uuidSpan"></span>
|
||||
</h2>
|
||||
<h3> You must use same UUID for login this page after config valid UUID Environment Variables
|
||||
</h3>
|
||||
<h2>Please refer to <a
|
||||
href="https://github.com/zizifn/edgetunnel/blob/main/doc/edge-tunnel-deno.md#%E6%B5%81%E7%A8%8B%E6%BC%94%E7%A4%BA">deno
|
||||
deploy guide</a>
|
||||
</h2>
|
||||
|
||||
<h3>Or maybe check below <a
|
||||
href="https://raw.githubusercontent.com/zizifn/edgetunnel/main/doc/deno-deploy2.gif">GIF</a> </h3>
|
||||
<img src="https://raw.githubusercontent.com/zizifn/edgetunnel/main/doc/deno-deploy2.gif" alt="guide" srcset="">
|
||||
<script>
|
||||
let uuid = URL.createObjectURL(new Blob([])).substr(-36);
|
||||
document.getElementById('uuidSpan').textContent = uuid
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,14 +0,0 @@
|
||||
import { StrictMode } from 'react';
|
||||
import * as ReactDOM from 'react-dom/client';
|
||||
// import App from './app/app';
|
||||
import { EdgeApp } from 'edge-ui';
|
||||
import './styles.css';
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById('root') as HTMLElement
|
||||
);
|
||||
root.render(
|
||||
<StrictMode>
|
||||
<EdgeApp />
|
||||
</StrictMode>
|
||||
);
|
||||
@@ -1,8 +1,12 @@
|
||||
import { serve } from 'https://deno.land/std@0.170.0/http/server.ts';
|
||||
import * as uuid from 'https://jspm.dev/uuid';
|
||||
import * as lodash from 'https://jspm.dev/lodash-es';
|
||||
import { serveClient } from './deno/client.ts';
|
||||
import { processWebSocket } from '../../../libs/vless-js/src/lib/vless-js.ts';
|
||||
import { serveClient } from './client.ts';
|
||||
import {
|
||||
closeWebSocket,
|
||||
delay,
|
||||
makeReadableWebSocketStream,
|
||||
processVlessHeader,
|
||||
} from 'vless-js';
|
||||
|
||||
const userID = Deno.env.get('UUID') || '';
|
||||
let isVaildUser = uuid.validate(userID);
|
||||
@@ -13,7 +17,7 @@ if (!isVaildUser) {
|
||||
const handler = async (req: Request): Promise<Response> => {
|
||||
if (!isVaildUser) {
|
||||
const index401 = await Deno.readFile(
|
||||
`${Deno.cwd()}/apps/deno-vless/src/deno/401.html`
|
||||
`${Deno.cwd()}/dist/apps/cf-page/401.html`
|
||||
);
|
||||
return new Response(index401, {
|
||||
status: 401,
|
||||
@@ -32,20 +36,194 @@ const handler = async (req: Request): Promise<Response> => {
|
||||
// let test: Deno.TcpConn | null = null;
|
||||
// test!.writable.abort();
|
||||
//
|
||||
const earlyDataHeader = req.headers.get('sec-websocket-protocol') || '';
|
||||
|
||||
processWebSocket({
|
||||
userID,
|
||||
webSocket: socket,
|
||||
rawTCPFactory: (port: number, hostname: string) => {
|
||||
return Deno.connect({
|
||||
port,
|
||||
hostname,
|
||||
});
|
||||
},
|
||||
libs: { uuid, lodash },
|
||||
earlyDataHeader,
|
||||
// rawTCPFactory: (port: number, hostname: string) => {
|
||||
// return Deno.connect({
|
||||
// port,
|
||||
// hostname,
|
||||
// });
|
||||
// },
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
async function processWebSocket({
|
||||
userID,
|
||||
webSocket,
|
||||
earlyDataHeader,
|
||||
}: // libs: { uuid, lodash },
|
||||
{
|
||||
userID: string;
|
||||
webSocket: WebSocket;
|
||||
earlyDataHeader: string;
|
||||
// rawTCPFactory: (port: number, hostname: string) => Promise<any>;
|
||||
// libs: { uuid: any; lodash: any };
|
||||
}) {
|
||||
let address = '';
|
||||
let portWithRandomLog = '';
|
||||
let remoteConnection: {
|
||||
readable: any;
|
||||
writable: any;
|
||||
write: (arg0: Uint8Array) => any;
|
||||
close: () => void;
|
||||
} | null = null;
|
||||
let remoteConnectionReadyResolve: Function;
|
||||
try {
|
||||
const log = (info: string, event?: any) => {
|
||||
console.log(`[${address}:${portWithRandomLog}] ${info}`, event || '');
|
||||
};
|
||||
const readableWebSocketStream = makeReadableWebSocketStream(
|
||||
webSocket,
|
||||
earlyDataHeader,
|
||||
log
|
||||
);
|
||||
let vlessResponseHeader: Uint8Array | null = null;
|
||||
|
||||
// ws --> remote
|
||||
readableWebSocketStream
|
||||
.pipeTo(
|
||||
new WritableStream({
|
||||
async write(chunk, controller) {
|
||||
const vlessBuffer = chunk;
|
||||
if (remoteConnection) {
|
||||
const number = await remoteConnection.write(
|
||||
new Uint8Array(vlessBuffer)
|
||||
);
|
||||
return;
|
||||
}
|
||||
const {
|
||||
hasError,
|
||||
message,
|
||||
portRemote,
|
||||
addressRemote,
|
||||
rawDataIndex,
|
||||
vlessVersion,
|
||||
isUDP,
|
||||
} = processVlessHeader(vlessBuffer, userID);
|
||||
address = addressRemote || '';
|
||||
portWithRandomLog = `${portRemote}--${Math.random()}`;
|
||||
if (isUDP) {
|
||||
console.log('udp');
|
||||
controller.error(
|
||||
`[${address}:${portWithRandomLog}] command udp is not support `
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (hasError) {
|
||||
controller.error(`[${address}:${portWithRandomLog}] ${message} `);
|
||||
}
|
||||
// const addressType = requestAddr >> 4;
|
||||
// const addressLength = requestAddr & 0x0f;
|
||||
console.log(`[${address}:${portWithRandomLog}] connecting`);
|
||||
remoteConnection = await Deno.connect({
|
||||
port: portRemote!,
|
||||
hostname: address,
|
||||
});
|
||||
vlessResponseHeader = new Uint8Array([vlessVersion![0], 0]);
|
||||
const rawClientData = vlessBuffer.slice(rawDataIndex!);
|
||||
await remoteConnection!.write(new Uint8Array(rawClientData));
|
||||
remoteConnectionReadyResolve(remoteConnection);
|
||||
},
|
||||
close() {
|
||||
console.log(
|
||||
`[${address}:${portWithRandomLog}] readableWebSocketStream is close`
|
||||
);
|
||||
},
|
||||
abort(reason) {
|
||||
console.log(
|
||||
`[${address}:${portWithRandomLog}] readableWebSocketStream is abort`,
|
||||
JSON.stringify(reason)
|
||||
);
|
||||
},
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
`[${address}:${portWithRandomLog}] readableWebSocketStream pipeto has exception`,
|
||||
error.stack || error
|
||||
);
|
||||
// error is cancel readable stream anyway, no need close websocket in here
|
||||
// closeWebSocket(webSocket);
|
||||
// close remote conn
|
||||
// remoteConnection?.close();
|
||||
});
|
||||
await new Promise((resolve) => (remoteConnectionReadyResolve = resolve));
|
||||
let remoteChunkCount = 0;
|
||||
let totoal = 0;
|
||||
// remote --> ws
|
||||
await remoteConnection!.readable.pipeTo(
|
||||
new WritableStream({
|
||||
start() {
|
||||
if (webSocket.readyState === webSocket.OPEN) {
|
||||
webSocket.send(vlessResponseHeader!);
|
||||
}
|
||||
},
|
||||
async write(chunk: Uint8Array, controller) {
|
||||
function send2WebSocket() {
|
||||
if (webSocket.readyState !== webSocket.OPEN) {
|
||||
controller.error(
|
||||
`can't accept data from remoteConnection!.readable when client webSocket is close early`
|
||||
);
|
||||
return;
|
||||
}
|
||||
webSocket.send(chunk);
|
||||
}
|
||||
|
||||
remoteChunkCount++;
|
||||
//#region
|
||||
// console.log(
|
||||
// `${(totoal +=
|
||||
// chunk.length)}, count: ${remoteChunkCount.toString()}, ${
|
||||
// chunk.length
|
||||
// }`
|
||||
// );
|
||||
// https://github.com/zizifn/edgetunnel/issues/87, hack for this issue, maybe websocket sent too many small chunk,
|
||||
// casue v2ray client can't process https://github.com/denoland/deno/issues/17332
|
||||
// limit X number count / bandwith, due to deno can't read bufferedAmount in deno,
|
||||
// this is deno bug and this will not need in nodejs version
|
||||
//#endregion
|
||||
if (remoteChunkCount < 20) {
|
||||
send2WebSocket();
|
||||
} else if (remoteChunkCount < 120) {
|
||||
await delay(10); // 64kb * 100 = 6m/s
|
||||
send2WebSocket();
|
||||
} else if (remoteChunkCount < 500) {
|
||||
await delay(20); // (64kb * 1000/20) = 3m/s
|
||||
send2WebSocket();
|
||||
} else {
|
||||
await delay(50); // (64kb * 1000/50) /s
|
||||
send2WebSocket();
|
||||
}
|
||||
},
|
||||
close() {
|
||||
console.log(
|
||||
`[${address}:${portWithRandomLog}] remoteConnection!.readable is close`
|
||||
);
|
||||
},
|
||||
abort(reason) {
|
||||
closeWebSocket(webSocket);
|
||||
console.error(
|
||||
`[${address}:${portWithRandomLog}] remoteConnection!.readable abort`,
|
||||
reason
|
||||
);
|
||||
},
|
||||
})
|
||||
);
|
||||
} catch (error: any) {
|
||||
console.error(
|
||||
`[${address}:${portWithRandomLog}] processWebSocket has exception `,
|
||||
error.stack || error
|
||||
);
|
||||
closeWebSocket(webSocket);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
globalThis.addEventListener('beforeunload', (e) => {
|
||||
console.log('About to exit...');
|
||||
});
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
@tailwind components;
|
||||
@tailwind base;
|
||||
@tailwind utilities;
|
||||
Reference in New Issue
Block a user