add Ws 0rtt into node and deno (#103)

add ws 0rtt to deno and nodejs
This commit is contained in:
zizifn
2023-03-05 16:20:04 +08:00
committed by GitHub
parent c4f3cf8390
commit c2d655774f
45 changed files with 1406 additions and 553 deletions

View File

@@ -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

View File

@@ -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>

View File

@@ -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' });

View File

@@ -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>

View File

@@ -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>
);

View File

@@ -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...');
});

View File

@@ -1,3 +0,0 @@
@tailwind components;
@tailwind base;
@tailwind utilities;