mirror of
https://github.com/lush2020/edgetunnel.git
synced 2026-03-24 00:48:39 +08:00
Refactor deno (#107)
* refactor deno * refactor deno * add deno bunled * add eamodio.gitlens --------- Co-authored-by: zizifn3 <75520940+zizifn3@users.noreply.github.com>
This commit is contained in:
5
.vscode/extensions.json
vendored
5
.vscode/extensions.json
vendored
@@ -4,6 +4,9 @@
|
|||||||
"esbenp.prettier-vscode",
|
"esbenp.prettier-vscode",
|
||||||
"dbaeumer.vscode-eslint",
|
"dbaeumer.vscode-eslint",
|
||||||
"firsttris.vscode-jest-runner",
|
"firsttris.vscode-jest-runner",
|
||||||
"denoland.vscode-deno"
|
"denoland.vscode-deno",
|
||||||
|
"GitHub.copilot",
|
||||||
|
"GitHub.copilot-labs",
|
||||||
|
"eamodio.gitlens"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
975
apps/deno-vless/deno-bunled.js
Normal file
975
apps/deno-vless/deno-bunled.js
Normal file
@@ -0,0 +1,975 @@
|
|||||||
|
// deno-fmt-ignore-file
|
||||||
|
// deno-lint-ignore-file
|
||||||
|
// This code was bundled using `deno bundle` and it's not recommended to edit it manually
|
||||||
|
|
||||||
|
function deferred() {
|
||||||
|
let methods;
|
||||||
|
let state = "pending";
|
||||||
|
const promise = new Promise((resolve, reject)=>{
|
||||||
|
methods = {
|
||||||
|
async resolve (value) {
|
||||||
|
await value;
|
||||||
|
state = "fulfilled";
|
||||||
|
resolve(value);
|
||||||
|
},
|
||||||
|
reject (reason) {
|
||||||
|
state = "rejected";
|
||||||
|
reject(reason);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
Object.defineProperty(promise, "state", {
|
||||||
|
get: ()=>state
|
||||||
|
});
|
||||||
|
return Object.assign(promise, methods);
|
||||||
|
}
|
||||||
|
function delay(ms, options = {}) {
|
||||||
|
const { signal , persistent } = options;
|
||||||
|
if (signal?.aborted) {
|
||||||
|
return Promise.reject(new DOMException("Delay was aborted.", "AbortError"));
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject)=>{
|
||||||
|
const abort = ()=>{
|
||||||
|
clearTimeout(i);
|
||||||
|
reject(new DOMException("Delay was aborted.", "AbortError"));
|
||||||
|
};
|
||||||
|
const done = ()=>{
|
||||||
|
signal?.removeEventListener("abort", abort);
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
const i = setTimeout(done, ms);
|
||||||
|
signal?.addEventListener("abort", abort, {
|
||||||
|
once: true
|
||||||
|
});
|
||||||
|
if (persistent === false) {
|
||||||
|
try {
|
||||||
|
Deno.unrefTimer(i);
|
||||||
|
} catch (error) {
|
||||||
|
if (!(error instanceof ReferenceError)) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
console.error("`persistent` option is only available in Deno");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
class MuxAsyncIterator {
|
||||||
|
#iteratorCount = 0;
|
||||||
|
#yields = [];
|
||||||
|
#throws = [];
|
||||||
|
#signal = deferred();
|
||||||
|
add(iterable) {
|
||||||
|
++this.#iteratorCount;
|
||||||
|
this.#callIteratorNext(iterable[Symbol.asyncIterator]());
|
||||||
|
}
|
||||||
|
async #callIteratorNext(iterator) {
|
||||||
|
try {
|
||||||
|
const { value , done } = await iterator.next();
|
||||||
|
if (done) {
|
||||||
|
--this.#iteratorCount;
|
||||||
|
} else {
|
||||||
|
this.#yields.push({
|
||||||
|
iterator,
|
||||||
|
value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.#throws.push(e);
|
||||||
|
}
|
||||||
|
this.#signal.resolve();
|
||||||
|
}
|
||||||
|
async *iterate() {
|
||||||
|
while(this.#iteratorCount > 0){
|
||||||
|
await this.#signal;
|
||||||
|
for(let i = 0; i < this.#yields.length; i++){
|
||||||
|
const { iterator , value } = this.#yields[i];
|
||||||
|
yield value;
|
||||||
|
this.#callIteratorNext(iterator);
|
||||||
|
}
|
||||||
|
if (this.#throws.length) {
|
||||||
|
for (const e of this.#throws){
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
this.#throws.length = 0;
|
||||||
|
}
|
||||||
|
this.#yields.length = 0;
|
||||||
|
this.#signal = deferred();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[Symbol.asyncIterator]() {
|
||||||
|
return this.iterate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const ERROR_SERVER_CLOSED = "Server closed";
|
||||||
|
const INITIAL_ACCEPT_BACKOFF_DELAY = 5;
|
||||||
|
const MAX_ACCEPT_BACKOFF_DELAY = 1000;
|
||||||
|
class Server {
|
||||||
|
#port;
|
||||||
|
#host;
|
||||||
|
#handler;
|
||||||
|
#closed = false;
|
||||||
|
#listeners = new Set();
|
||||||
|
#acceptBackoffDelayAbortController = new AbortController();
|
||||||
|
#httpConnections = new Set();
|
||||||
|
#onError;
|
||||||
|
constructor(serverInit){
|
||||||
|
this.#port = serverInit.port;
|
||||||
|
this.#host = serverInit.hostname;
|
||||||
|
this.#handler = serverInit.handler;
|
||||||
|
this.#onError = serverInit.onError ?? function(error) {
|
||||||
|
console.error(error);
|
||||||
|
return new Response("Internal Server Error", {
|
||||||
|
status: 500
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
async serve(listener) {
|
||||||
|
if (this.#closed) {
|
||||||
|
throw new Deno.errors.Http(ERROR_SERVER_CLOSED);
|
||||||
|
}
|
||||||
|
this.#trackListener(listener);
|
||||||
|
try {
|
||||||
|
return await this.#accept(listener);
|
||||||
|
} finally{
|
||||||
|
this.#untrackListener(listener);
|
||||||
|
try {
|
||||||
|
listener.close();
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async listenAndServe() {
|
||||||
|
if (this.#closed) {
|
||||||
|
throw new Deno.errors.Http(ERROR_SERVER_CLOSED);
|
||||||
|
}
|
||||||
|
const listener = Deno.listen({
|
||||||
|
port: this.#port ?? 80,
|
||||||
|
hostname: this.#host ?? "0.0.0.0",
|
||||||
|
transport: "tcp"
|
||||||
|
});
|
||||||
|
return await this.serve(listener);
|
||||||
|
}
|
||||||
|
async listenAndServeTls(certFile, keyFile) {
|
||||||
|
if (this.#closed) {
|
||||||
|
throw new Deno.errors.Http(ERROR_SERVER_CLOSED);
|
||||||
|
}
|
||||||
|
const listener = Deno.listenTls({
|
||||||
|
port: this.#port ?? 443,
|
||||||
|
hostname: this.#host ?? "0.0.0.0",
|
||||||
|
certFile,
|
||||||
|
keyFile,
|
||||||
|
transport: "tcp"
|
||||||
|
});
|
||||||
|
return await this.serve(listener);
|
||||||
|
}
|
||||||
|
close() {
|
||||||
|
if (this.#closed) {
|
||||||
|
throw new Deno.errors.Http(ERROR_SERVER_CLOSED);
|
||||||
|
}
|
||||||
|
this.#closed = true;
|
||||||
|
for (const listener of this.#listeners){
|
||||||
|
try {
|
||||||
|
listener.close();
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
this.#listeners.clear();
|
||||||
|
this.#acceptBackoffDelayAbortController.abort();
|
||||||
|
for (const httpConn of this.#httpConnections){
|
||||||
|
this.#closeHttpConn(httpConn);
|
||||||
|
}
|
||||||
|
this.#httpConnections.clear();
|
||||||
|
}
|
||||||
|
get closed() {
|
||||||
|
return this.#closed;
|
||||||
|
}
|
||||||
|
get addrs() {
|
||||||
|
return Array.from(this.#listeners).map((listener)=>listener.addr);
|
||||||
|
}
|
||||||
|
async #respond(requestEvent, connInfo) {
|
||||||
|
let response;
|
||||||
|
try {
|
||||||
|
response = await this.#handler(requestEvent.request, connInfo);
|
||||||
|
if (response.bodyUsed && response.body !== null) {
|
||||||
|
throw new TypeError("Response body already consumed.");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
response = await this.#onError(error);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await requestEvent.respondWith(response);
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
async #serveHttp(httpConn, connInfo1) {
|
||||||
|
while(!this.#closed){
|
||||||
|
let requestEvent;
|
||||||
|
try {
|
||||||
|
requestEvent = await httpConn.nextRequest();
|
||||||
|
} catch {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (requestEvent === null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.#respond(requestEvent, connInfo1);
|
||||||
|
}
|
||||||
|
this.#closeHttpConn(httpConn);
|
||||||
|
}
|
||||||
|
async #accept(listener) {
|
||||||
|
let acceptBackoffDelay;
|
||||||
|
while(!this.#closed){
|
||||||
|
let conn;
|
||||||
|
try {
|
||||||
|
conn = await listener.accept();
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Deno.errors.BadResource || error instanceof Deno.errors.InvalidData || error instanceof Deno.errors.UnexpectedEof || error instanceof Deno.errors.ConnectionReset || error instanceof Deno.errors.NotConnected) {
|
||||||
|
if (!acceptBackoffDelay) {
|
||||||
|
acceptBackoffDelay = INITIAL_ACCEPT_BACKOFF_DELAY;
|
||||||
|
} else {
|
||||||
|
acceptBackoffDelay *= 2;
|
||||||
|
}
|
||||||
|
if (acceptBackoffDelay >= 1000) {
|
||||||
|
acceptBackoffDelay = MAX_ACCEPT_BACKOFF_DELAY;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await delay(acceptBackoffDelay, {
|
||||||
|
signal: this.#acceptBackoffDelayAbortController.signal
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
if (!(err instanceof DOMException && err.name === "AbortError")) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
acceptBackoffDelay = undefined;
|
||||||
|
let httpConn;
|
||||||
|
try {
|
||||||
|
httpConn = Deno.serveHttp(conn);
|
||||||
|
} catch {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this.#trackHttpConnection(httpConn);
|
||||||
|
const connInfo = {
|
||||||
|
localAddr: conn.localAddr,
|
||||||
|
remoteAddr: conn.remoteAddr
|
||||||
|
};
|
||||||
|
this.#serveHttp(httpConn, connInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#closeHttpConn(httpConn1) {
|
||||||
|
this.#untrackHttpConnection(httpConn1);
|
||||||
|
try {
|
||||||
|
httpConn1.close();
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
#trackListener(listener1) {
|
||||||
|
this.#listeners.add(listener1);
|
||||||
|
}
|
||||||
|
#untrackListener(listener2) {
|
||||||
|
this.#listeners.delete(listener2);
|
||||||
|
}
|
||||||
|
#trackHttpConnection(httpConn2) {
|
||||||
|
this.#httpConnections.add(httpConn2);
|
||||||
|
}
|
||||||
|
#untrackHttpConnection(httpConn3) {
|
||||||
|
this.#httpConnections.delete(httpConn3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function hostnameForDisplay(hostname) {
|
||||||
|
return hostname === "0.0.0.0" ? "localhost" : hostname;
|
||||||
|
}
|
||||||
|
async function serve(handler, options = {}) {
|
||||||
|
let port = options.port ?? 8000;
|
||||||
|
const hostname = options.hostname ?? "0.0.0.0";
|
||||||
|
const server = new Server({
|
||||||
|
port,
|
||||||
|
hostname,
|
||||||
|
handler,
|
||||||
|
onError: options.onError
|
||||||
|
});
|
||||||
|
options?.signal?.addEventListener("abort", ()=>server.close(), {
|
||||||
|
once: true
|
||||||
|
});
|
||||||
|
const s = server.listenAndServe();
|
||||||
|
port = server.addrs[0].port;
|
||||||
|
if ("onListen" in options) {
|
||||||
|
options.onListen?.({
|
||||||
|
port,
|
||||||
|
hostname
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log(`Listening on http://${hostnameForDisplay(hostname)}:${port}/`);
|
||||||
|
}
|
||||||
|
return await s;
|
||||||
|
}
|
||||||
|
new Uint8Array(16);
|
||||||
|
var REGEX = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;
|
||||||
|
function validate(uuid) {
|
||||||
|
return typeof uuid === 'string' && REGEX.test(uuid);
|
||||||
|
}
|
||||||
|
const byteToHex = [];
|
||||||
|
for(let i = 0; i < 256; ++i){
|
||||||
|
byteToHex.push((i + 0x100).toString(16).slice(1));
|
||||||
|
}
|
||||||
|
function unsafeStringify(arr, offset = 0) {
|
||||||
|
return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
|
||||||
|
}
|
||||||
|
function stringify(arr, offset = 0) {
|
||||||
|
const uuid = unsafeStringify(arr, offset);
|
||||||
|
if (!validate(uuid)) {
|
||||||
|
throw TypeError('Stringified UUID is invalid');
|
||||||
|
}
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
function parse(uuid) {
|
||||||
|
if (!validate(uuid)) {
|
||||||
|
throw TypeError('Invalid UUID');
|
||||||
|
}
|
||||||
|
let v;
|
||||||
|
const arr = new Uint8Array(16);
|
||||||
|
arr[0] = (v = parseInt(uuid.slice(0, 8), 16)) >>> 24;
|
||||||
|
arr[1] = v >>> 16 & 0xff;
|
||||||
|
arr[2] = v >>> 8 & 0xff;
|
||||||
|
arr[3] = v & 0xff;
|
||||||
|
arr[4] = (v = parseInt(uuid.slice(9, 13), 16)) >>> 8;
|
||||||
|
arr[5] = v & 0xff;
|
||||||
|
arr[6] = (v = parseInt(uuid.slice(14, 18), 16)) >>> 8;
|
||||||
|
arr[7] = v & 0xff;
|
||||||
|
arr[8] = (v = parseInt(uuid.slice(19, 23), 16)) >>> 8;
|
||||||
|
arr[9] = v & 0xff;
|
||||||
|
arr[10] = (v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000 & 0xff;
|
||||||
|
arr[11] = v / 0x100000000 & 0xff;
|
||||||
|
arr[12] = v >>> 24 & 0xff;
|
||||||
|
arr[13] = v >>> 16 & 0xff;
|
||||||
|
arr[14] = v >>> 8 & 0xff;
|
||||||
|
arr[15] = v & 0xff;
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
function stringToBytes(str) {
|
||||||
|
str = unescape(encodeURIComponent(str));
|
||||||
|
const bytes = [];
|
||||||
|
for(let i = 0; i < str.length; ++i){
|
||||||
|
bytes.push(str.charCodeAt(i));
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
const DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
|
||||||
|
const URL1 = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';
|
||||||
|
function v35(name, version, hashfunc) {
|
||||||
|
function generateUUID(value, namespace, buf, offset) {
|
||||||
|
var _namespace;
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
value = stringToBytes(value);
|
||||||
|
}
|
||||||
|
if (typeof namespace === 'string') {
|
||||||
|
namespace = parse(namespace);
|
||||||
|
}
|
||||||
|
if (((_namespace = namespace) === null || _namespace === void 0 ? void 0 : _namespace.length) !== 16) {
|
||||||
|
throw TypeError('Namespace must be array-like (16 iterable integer values, 0-255)');
|
||||||
|
}
|
||||||
|
let bytes = new Uint8Array(16 + value.length);
|
||||||
|
bytes.set(namespace);
|
||||||
|
bytes.set(value, namespace.length);
|
||||||
|
bytes = hashfunc(bytes);
|
||||||
|
bytes[6] = bytes[6] & 0x0f | version;
|
||||||
|
bytes[8] = bytes[8] & 0x3f | 0x80;
|
||||||
|
if (buf) {
|
||||||
|
offset = offset || 0;
|
||||||
|
for(let i = 0; i < 16; ++i){
|
||||||
|
buf[offset + i] = bytes[i];
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
return unsafeStringify(bytes);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
generateUUID.name = name;
|
||||||
|
} catch (err) {}
|
||||||
|
generateUUID.DNS = DNS;
|
||||||
|
generateUUID.URL = URL1;
|
||||||
|
return generateUUID;
|
||||||
|
}
|
||||||
|
function md5(bytes) {
|
||||||
|
if (typeof bytes === 'string') {
|
||||||
|
const msg = unescape(encodeURIComponent(bytes));
|
||||||
|
bytes = new Uint8Array(msg.length);
|
||||||
|
for(let i = 0; i < msg.length; ++i){
|
||||||
|
bytes[i] = msg.charCodeAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return md5ToHexEncodedArray(wordsToMd5(bytesToWords(bytes), bytes.length * 8));
|
||||||
|
}
|
||||||
|
function md5ToHexEncodedArray(input) {
|
||||||
|
const output = [];
|
||||||
|
const length32 = input.length * 32;
|
||||||
|
const hexTab = '0123456789abcdef';
|
||||||
|
for(let i = 0; i < length32; i += 8){
|
||||||
|
const x = input[i >> 5] >>> i % 32 & 0xff;
|
||||||
|
const hex = parseInt(hexTab.charAt(x >>> 4 & 0x0f) + hexTab.charAt(x & 0x0f), 16);
|
||||||
|
output.push(hex);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
function getOutputLength(inputLength8) {
|
||||||
|
return (inputLength8 + 64 >>> 9 << 4) + 14 + 1;
|
||||||
|
}
|
||||||
|
function wordsToMd5(x, len) {
|
||||||
|
x[len >> 5] |= 0x80 << len % 32;
|
||||||
|
x[getOutputLength(len) - 1] = len;
|
||||||
|
let a = 1732584193;
|
||||||
|
let b = -271733879;
|
||||||
|
let c = -1732584194;
|
||||||
|
let d = 271733878;
|
||||||
|
for(let i = 0; i < x.length; i += 16){
|
||||||
|
const olda = a;
|
||||||
|
const oldb = b;
|
||||||
|
const oldc = c;
|
||||||
|
const oldd = d;
|
||||||
|
a = md5ff(a, b, c, d, x[i], 7, -680876936);
|
||||||
|
d = md5ff(d, a, b, c, x[i + 1], 12, -389564586);
|
||||||
|
c = md5ff(c, d, a, b, x[i + 2], 17, 606105819);
|
||||||
|
b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330);
|
||||||
|
a = md5ff(a, b, c, d, x[i + 4], 7, -176418897);
|
||||||
|
d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426);
|
||||||
|
c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341);
|
||||||
|
b = md5ff(b, c, d, a, x[i + 7], 22, -45705983);
|
||||||
|
a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416);
|
||||||
|
d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417);
|
||||||
|
c = md5ff(c, d, a, b, x[i + 10], 17, -42063);
|
||||||
|
b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162);
|
||||||
|
a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682);
|
||||||
|
d = md5ff(d, a, b, c, x[i + 13], 12, -40341101);
|
||||||
|
c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290);
|
||||||
|
b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329);
|
||||||
|
a = md5gg(a, b, c, d, x[i + 1], 5, -165796510);
|
||||||
|
d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632);
|
||||||
|
c = md5gg(c, d, a, b, x[i + 11], 14, 643717713);
|
||||||
|
b = md5gg(b, c, d, a, x[i], 20, -373897302);
|
||||||
|
a = md5gg(a, b, c, d, x[i + 5], 5, -701558691);
|
||||||
|
d = md5gg(d, a, b, c, x[i + 10], 9, 38016083);
|
||||||
|
c = md5gg(c, d, a, b, x[i + 15], 14, -660478335);
|
||||||
|
b = md5gg(b, c, d, a, x[i + 4], 20, -405537848);
|
||||||
|
a = md5gg(a, b, c, d, x[i + 9], 5, 568446438);
|
||||||
|
d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690);
|
||||||
|
c = md5gg(c, d, a, b, x[i + 3], 14, -187363961);
|
||||||
|
b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501);
|
||||||
|
a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467);
|
||||||
|
d = md5gg(d, a, b, c, x[i + 2], 9, -51403784);
|
||||||
|
c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473);
|
||||||
|
b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734);
|
||||||
|
a = md5hh(a, b, c, d, x[i + 5], 4, -378558);
|
||||||
|
d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463);
|
||||||
|
c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562);
|
||||||
|
b = md5hh(b, c, d, a, x[i + 14], 23, -35309556);
|
||||||
|
a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060);
|
||||||
|
d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353);
|
||||||
|
c = md5hh(c, d, a, b, x[i + 7], 16, -155497632);
|
||||||
|
b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640);
|
||||||
|
a = md5hh(a, b, c, d, x[i + 13], 4, 681279174);
|
||||||
|
d = md5hh(d, a, b, c, x[i], 11, -358537222);
|
||||||
|
c = md5hh(c, d, a, b, x[i + 3], 16, -722521979);
|
||||||
|
b = md5hh(b, c, d, a, x[i + 6], 23, 76029189);
|
||||||
|
a = md5hh(a, b, c, d, x[i + 9], 4, -640364487);
|
||||||
|
d = md5hh(d, a, b, c, x[i + 12], 11, -421815835);
|
||||||
|
c = md5hh(c, d, a, b, x[i + 15], 16, 530742520);
|
||||||
|
b = md5hh(b, c, d, a, x[i + 2], 23, -995338651);
|
||||||
|
a = md5ii(a, b, c, d, x[i], 6, -198630844);
|
||||||
|
d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415);
|
||||||
|
c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905);
|
||||||
|
b = md5ii(b, c, d, a, x[i + 5], 21, -57434055);
|
||||||
|
a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571);
|
||||||
|
d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606);
|
||||||
|
c = md5ii(c, d, a, b, x[i + 10], 15, -1051523);
|
||||||
|
b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799);
|
||||||
|
a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359);
|
||||||
|
d = md5ii(d, a, b, c, x[i + 15], 10, -30611744);
|
||||||
|
c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380);
|
||||||
|
b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649);
|
||||||
|
a = md5ii(a, b, c, d, x[i + 4], 6, -145523070);
|
||||||
|
d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379);
|
||||||
|
c = md5ii(c, d, a, b, x[i + 2], 15, 718787259);
|
||||||
|
b = md5ii(b, c, d, a, x[i + 9], 21, -343485551);
|
||||||
|
a = safeAdd(a, olda);
|
||||||
|
b = safeAdd(b, oldb);
|
||||||
|
c = safeAdd(c, oldc);
|
||||||
|
d = safeAdd(d, oldd);
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c,
|
||||||
|
d
|
||||||
|
];
|
||||||
|
}
|
||||||
|
function bytesToWords(input) {
|
||||||
|
if (input.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const length8 = input.length * 8;
|
||||||
|
const output = new Uint32Array(getOutputLength(length8));
|
||||||
|
for(let i = 0; i < length8; i += 8){
|
||||||
|
output[i >> 5] |= (input[i / 8] & 0xff) << i % 32;
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
function safeAdd(x, y) {
|
||||||
|
const lsw = (x & 0xffff) + (y & 0xffff);
|
||||||
|
const msw = (x >> 16) + (y >> 16) + (lsw >> 16);
|
||||||
|
return msw << 16 | lsw & 0xffff;
|
||||||
|
}
|
||||||
|
function bitRotateLeft(num, cnt) {
|
||||||
|
return num << cnt | num >>> 32 - cnt;
|
||||||
|
}
|
||||||
|
function md5cmn(q, a, b, x, s, t) {
|
||||||
|
return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b);
|
||||||
|
}
|
||||||
|
function md5ff(a, b, c, d, x, s, t) {
|
||||||
|
return md5cmn(b & c | ~b & d, a, b, x, s, t);
|
||||||
|
}
|
||||||
|
function md5gg(a, b, c, d, x, s, t) {
|
||||||
|
return md5cmn(b & d | c & ~d, a, b, x, s, t);
|
||||||
|
}
|
||||||
|
function md5hh(a, b, c, d, x, s, t) {
|
||||||
|
return md5cmn(b ^ c ^ d, a, b, x, s, t);
|
||||||
|
}
|
||||||
|
function md5ii(a, b, c, d, x, s, t) {
|
||||||
|
return md5cmn(c ^ (b | ~d), a, b, x, s, t);
|
||||||
|
}
|
||||||
|
v35('v3', 0x30, md5);
|
||||||
|
typeof crypto !== 'undefined' && crypto.randomUUID && crypto.randomUUID.bind(crypto);
|
||||||
|
function f(s, x, y, z) {
|
||||||
|
switch(s){
|
||||||
|
case 0:
|
||||||
|
return x & y ^ ~x & z;
|
||||||
|
case 1:
|
||||||
|
return x ^ y ^ z;
|
||||||
|
case 2:
|
||||||
|
return x & y ^ x & z ^ y & z;
|
||||||
|
case 3:
|
||||||
|
return x ^ y ^ z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function ROTL(x, n) {
|
||||||
|
return x << n | x >>> 32 - n;
|
||||||
|
}
|
||||||
|
function sha1(bytes) {
|
||||||
|
const K = [
|
||||||
|
0x5a827999,
|
||||||
|
0x6ed9eba1,
|
||||||
|
0x8f1bbcdc,
|
||||||
|
0xca62c1d6
|
||||||
|
];
|
||||||
|
const H = [
|
||||||
|
0x67452301,
|
||||||
|
0xefcdab89,
|
||||||
|
0x98badcfe,
|
||||||
|
0x10325476,
|
||||||
|
0xc3d2e1f0
|
||||||
|
];
|
||||||
|
if (typeof bytes === 'string') {
|
||||||
|
const msg = unescape(encodeURIComponent(bytes));
|
||||||
|
bytes = [];
|
||||||
|
for(let i = 0; i < msg.length; ++i){
|
||||||
|
bytes.push(msg.charCodeAt(i));
|
||||||
|
}
|
||||||
|
} else if (!Array.isArray(bytes)) {
|
||||||
|
bytes = Array.prototype.slice.call(bytes);
|
||||||
|
}
|
||||||
|
bytes.push(0x80);
|
||||||
|
const l = bytes.length / 4 + 2;
|
||||||
|
const N = Math.ceil(l / 16);
|
||||||
|
const M = new Array(N);
|
||||||
|
for(let i = 0; i < N; ++i){
|
||||||
|
const arr = new Uint32Array(16);
|
||||||
|
for(let j = 0; j < 16; ++j){
|
||||||
|
arr[j] = bytes[i * 64 + j * 4] << 24 | bytes[i * 64 + j * 4 + 1] << 16 | bytes[i * 64 + j * 4 + 2] << 8 | bytes[i * 64 + j * 4 + 3];
|
||||||
|
}
|
||||||
|
M[i] = arr;
|
||||||
|
}
|
||||||
|
M[N - 1][14] = (bytes.length - 1) * 8 / Math.pow(2, 32);
|
||||||
|
M[N - 1][14] = Math.floor(M[N - 1][14]);
|
||||||
|
M[N - 1][15] = (bytes.length - 1) * 8 & 0xffffffff;
|
||||||
|
for(let i = 0; i < N; ++i){
|
||||||
|
const W = new Uint32Array(80);
|
||||||
|
for(let t = 0; t < 16; ++t){
|
||||||
|
W[t] = M[i][t];
|
||||||
|
}
|
||||||
|
for(let t = 16; t < 80; ++t){
|
||||||
|
W[t] = ROTL(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1);
|
||||||
|
}
|
||||||
|
let a = H[0];
|
||||||
|
let b = H[1];
|
||||||
|
let c = H[2];
|
||||||
|
let d = H[3];
|
||||||
|
let e = H[4];
|
||||||
|
for(let t = 0; t < 80; ++t){
|
||||||
|
const s = Math.floor(t / 20);
|
||||||
|
const T = ROTL(a, 5) + f(s, b, c, d) + e + K[s] + W[t] >>> 0;
|
||||||
|
e = d;
|
||||||
|
d = c;
|
||||||
|
c = ROTL(b, 30) >>> 0;
|
||||||
|
b = a;
|
||||||
|
a = T;
|
||||||
|
}
|
||||||
|
H[0] = H[0] + a >>> 0;
|
||||||
|
H[1] = H[1] + b >>> 0;
|
||||||
|
H[2] = H[2] + c >>> 0;
|
||||||
|
H[3] = H[3] + d >>> 0;
|
||||||
|
H[4] = H[4] + e >>> 0;
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
H[0] >> 24 & 0xff,
|
||||||
|
H[0] >> 16 & 0xff,
|
||||||
|
H[0] >> 8 & 0xff,
|
||||||
|
H[0] & 0xff,
|
||||||
|
H[1] >> 24 & 0xff,
|
||||||
|
H[1] >> 16 & 0xff,
|
||||||
|
H[1] >> 8 & 0xff,
|
||||||
|
H[1] & 0xff,
|
||||||
|
H[2] >> 24 & 0xff,
|
||||||
|
H[2] >> 16 & 0xff,
|
||||||
|
H[2] >> 8 & 0xff,
|
||||||
|
H[2] & 0xff,
|
||||||
|
H[3] >> 24 & 0xff,
|
||||||
|
H[3] >> 16 & 0xff,
|
||||||
|
H[3] >> 8 & 0xff,
|
||||||
|
H[3] & 0xff,
|
||||||
|
H[4] >> 24 & 0xff,
|
||||||
|
H[4] >> 16 & 0xff,
|
||||||
|
H[4] >> 8 & 0xff,
|
||||||
|
H[4] & 0xff
|
||||||
|
];
|
||||||
|
}
|
||||||
|
v35('v5', 0x50, sha1);
|
||||||
|
async function serveClient(req, basePath) {
|
||||||
|
const url = new URL(req.url);
|
||||||
|
if (url.pathname.startsWith('/assets') || url.pathname.includes(basePath)) {
|
||||||
|
let targetUrl = `https://raw.githubusercontent.com/zizifn/edgetunnel/main/dist/apps/cf-page${url.pathname}`;
|
||||||
|
if (url.pathname.includes(basePath)) {
|
||||||
|
targetUrl = `https://raw.githubusercontent.com/zizifn/edgetunnel/main/dist/apps/cf-page/index.html`;
|
||||||
|
}
|
||||||
|
console.log(targetUrl);
|
||||||
|
const resp = await fetch(targetUrl);
|
||||||
|
const modifiedHeaders = new Headers(resp.headers);
|
||||||
|
modifiedHeaders.delete('content-security-policy');
|
||||||
|
if (url.pathname.endsWith('.js')) {
|
||||||
|
modifiedHeaders.set('content-type', 'application/javascript');
|
||||||
|
} else if (url.pathname.endsWith('.css')) {
|
||||||
|
modifiedHeaders.set('content-type', 'text/css');
|
||||||
|
} else if (url.pathname.includes(basePath)) {
|
||||||
|
modifiedHeaders.set('content-type', 'text/html; charset=utf-8');
|
||||||
|
}
|
||||||
|
return new Response(resp.body, {
|
||||||
|
status: resp.status,
|
||||||
|
headers: modifiedHeaders
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const basicAuth = req.headers.get('Authorization') || '';
|
||||||
|
const authString = basicAuth.split(' ')?.[1] || '';
|
||||||
|
if (atob(authString).includes(basePath)) {
|
||||||
|
console.log('302');
|
||||||
|
return new Response(``, {
|
||||||
|
status: 302,
|
||||||
|
headers: {
|
||||||
|
'content-type': 'text/html; charset=utf-8',
|
||||||
|
Location: `./${basePath}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return new Response(``, {
|
||||||
|
status: 401,
|
||||||
|
headers: {
|
||||||
|
'content-type': 'text/html; charset=utf-8',
|
||||||
|
'WWW-Authenticate': 'Basic'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function delay1(ms) {
|
||||||
|
return new Promise((resolve, rej)=>{
|
||||||
|
setTimeout(resolve, ms);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function makeReadableWebSocketStream(ws, earlyDataHeader, log) {
|
||||||
|
let readableStreamCancel = false;
|
||||||
|
return new ReadableStream({
|
||||||
|
start (controller) {
|
||||||
|
ws.addEventListener('message', async (e)=>{
|
||||||
|
if (readableStreamCancel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const vlessBuffer = e.data;
|
||||||
|
controller.enqueue(vlessBuffer);
|
||||||
|
});
|
||||||
|
ws.addEventListener('error', (e)=>{
|
||||||
|
log('socket has error');
|
||||||
|
readableStreamCancel = true;
|
||||||
|
controller.error(e);
|
||||||
|
});
|
||||||
|
ws.addEventListener('close', ()=>{
|
||||||
|
try {
|
||||||
|
log('webSocket is close');
|
||||||
|
if (readableStreamCancel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
controller.close();
|
||||||
|
} catch (error) {
|
||||||
|
log(`websocketStream can't close DUE to `, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const { earlyData , error } = base64ToArrayBuffer(earlyDataHeader);
|
||||||
|
if (error) {
|
||||||
|
log(`earlyDataHeader has invaild base64`);
|
||||||
|
closeWebSocket(ws);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (earlyData) {
|
||||||
|
controller.enqueue(earlyData);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pull (controller) {},
|
||||||
|
cancel (reason) {
|
||||||
|
log(`websocketStream is cancel DUE to `, reason);
|
||||||
|
if (readableStreamCancel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
readableStreamCancel = true;
|
||||||
|
closeWebSocket(ws);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function base64ToArrayBuffer(base64Str) {
|
||||||
|
if (!base64Str) {
|
||||||
|
return {
|
||||||
|
error: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
base64Str = base64Str.replace(/-/g, '+').replace(/_/g, '/');
|
||||||
|
const decode = atob(base64Str);
|
||||||
|
const arryBuffer = Uint8Array.from(decode, (c)=>c.charCodeAt(0));
|
||||||
|
return {
|
||||||
|
earlyData: arryBuffer.buffer,
|
||||||
|
error: null
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
error
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function closeWebSocket(socket) {
|
||||||
|
if (socket.readyState === socket.OPEN) {
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function processVlessHeader(vlessBuffer, userID) {
|
||||||
|
if (vlessBuffer.byteLength < 24) {
|
||||||
|
return {
|
||||||
|
hasError: true,
|
||||||
|
message: 'invalid data'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const version = new Uint8Array(vlessBuffer.slice(0, 1));
|
||||||
|
let isValidUser = false;
|
||||||
|
let isUDP = false;
|
||||||
|
if (stringify(new Uint8Array(vlessBuffer.slice(1, 17))) === userID) {
|
||||||
|
isValidUser = true;
|
||||||
|
}
|
||||||
|
if (!isValidUser) {
|
||||||
|
return {
|
||||||
|
hasError: true,
|
||||||
|
message: 'invalid user'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const optLength = new Uint8Array(vlessBuffer.slice(17, 18))[0];
|
||||||
|
const command = new Uint8Array(vlessBuffer.slice(18 + optLength, 18 + optLength + 1))[0];
|
||||||
|
if (command === 1) {} else if (command === 2) {
|
||||||
|
isUDP = true;
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
hasError: true,
|
||||||
|
message: `command ${command} is not support, command 01-tcp,02-udp,03-mux`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const portIndex = 18 + optLength + 1;
|
||||||
|
const portBuffer = vlessBuffer.slice(portIndex, portIndex + 2);
|
||||||
|
const portRemote = new DataView(portBuffer).getInt16(0);
|
||||||
|
let addressIndex = portIndex + 2;
|
||||||
|
const addressBuffer = new Uint8Array(vlessBuffer.slice(addressIndex, addressIndex + 1));
|
||||||
|
const addressType = addressBuffer[0];
|
||||||
|
let addressLength = 0;
|
||||||
|
let addressValueIndex = addressIndex + 1;
|
||||||
|
let addressValue = '';
|
||||||
|
switch(addressType){
|
||||||
|
case 1:
|
||||||
|
addressLength = 4;
|
||||||
|
addressValue = new Uint8Array(vlessBuffer.slice(addressValueIndex, addressValueIndex + addressLength)).join('.');
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
addressLength = new Uint8Array(vlessBuffer.slice(addressValueIndex, addressValueIndex + 1))[0];
|
||||||
|
addressValueIndex += 1;
|
||||||
|
addressValue = new TextDecoder().decode(vlessBuffer.slice(addressValueIndex, addressValueIndex + addressLength));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
addressLength = 16;
|
||||||
|
const dataView = new DataView(vlessBuffer.slice(addressValueIndex, addressValueIndex + addressLength));
|
||||||
|
const ipv6 = [];
|
||||||
|
for(let i = 0; i < 8; i++){
|
||||||
|
ipv6.push(dataView.getUint16(i * 2).toString(16));
|
||||||
|
}
|
||||||
|
addressValue = ipv6.join(':');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log(`invild addressType is ${addressType}`);
|
||||||
|
}
|
||||||
|
if (!addressValue) {
|
||||||
|
return {
|
||||||
|
hasError: true,
|
||||||
|
message: `addressValue is empty, addressType is ${addressType}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
hasError: false,
|
||||||
|
addressRemote: addressValue,
|
||||||
|
portRemote,
|
||||||
|
rawDataIndex: addressValueIndex + addressLength,
|
||||||
|
vlessVersion: version,
|
||||||
|
isUDP
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const userID = Deno.env.get('UUID') || '';
|
||||||
|
let isVaildUser = validate(userID);
|
||||||
|
if (!isVaildUser) {
|
||||||
|
console.log('not set valid UUID');
|
||||||
|
}
|
||||||
|
const handler = async (req)=>{
|
||||||
|
if (!isVaildUser) {
|
||||||
|
const index401 = await Deno.readFile(`${Deno.cwd()}/dist/apps/cf-page/401.html`);
|
||||||
|
return new Response(index401, {
|
||||||
|
status: 401,
|
||||||
|
headers: {
|
||||||
|
'content-type': 'text/html; charset=utf-8'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const upgrade = req.headers.get('upgrade') || '';
|
||||||
|
if (upgrade.toLowerCase() != 'websocket') {
|
||||||
|
return await serveClient(req, userID);
|
||||||
|
}
|
||||||
|
const { socket , response } = Deno.upgradeWebSocket(req);
|
||||||
|
socket.addEventListener('open', ()=>{});
|
||||||
|
const earlyDataHeader = req.headers.get('sec-websocket-protocol') || '';
|
||||||
|
processWebSocket({
|
||||||
|
userID,
|
||||||
|
webSocket: socket,
|
||||||
|
earlyDataHeader
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
};
|
||||||
|
async function processWebSocket({ userID , webSocket , earlyDataHeader }) {
|
||||||
|
let address = '';
|
||||||
|
let portWithRandomLog = '';
|
||||||
|
let remoteConnection = null;
|
||||||
|
let remoteConnectionReadyResolve;
|
||||||
|
try {
|
||||||
|
const log = (info, event)=>{
|
||||||
|
console.log(`[${address}:${portWithRandomLog}] ${info}`, event || '');
|
||||||
|
};
|
||||||
|
const readableWebSocketStream = makeReadableWebSocketStream(webSocket, earlyDataHeader, log);
|
||||||
|
let vlessResponseHeader = null;
|
||||||
|
readableWebSocketStream.pipeTo(new WritableStream({
|
||||||
|
async write (chunk, controller) {
|
||||||
|
const vlessBuffer = chunk;
|
||||||
|
if (remoteConnection) {
|
||||||
|
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} `);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
await new Promise((resolve)=>remoteConnectionReadyResolve = resolve);
|
||||||
|
let remoteChunkCount = 0;
|
||||||
|
await remoteConnection.readable.pipeTo(new WritableStream({
|
||||||
|
start () {
|
||||||
|
if (webSocket.readyState === webSocket.OPEN) {
|
||||||
|
webSocket.send(vlessResponseHeader);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async write (chunk, 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++;
|
||||||
|
if (remoteChunkCount < 20) {
|
||||||
|
send2WebSocket();
|
||||||
|
} else if (remoteChunkCount < 120) {
|
||||||
|
await delay1(10);
|
||||||
|
send2WebSocket();
|
||||||
|
} else if (remoteChunkCount < 500) {
|
||||||
|
await delay1(20);
|
||||||
|
send2WebSocket();
|
||||||
|
} else {
|
||||||
|
await delay1(50);
|
||||||
|
send2WebSocket();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
close () {
|
||||||
|
console.log(`[${address}:${portWithRandomLog}] remoteConnection!.readable is close`);
|
||||||
|
},
|
||||||
|
abort (reason) {
|
||||||
|
closeWebSocket(webSocket);
|
||||||
|
console.error(`[${address}:${portWithRandomLog}] remoteConnection!.readable abort`, reason);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[${address}:${portWithRandomLog}] processWebSocket has exception `, error.stack || error);
|
||||||
|
closeWebSocket(webSocket);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
globalThis.addEventListener('beforeunload', (e)=>{
|
||||||
|
console.log('About to exit...');
|
||||||
|
});
|
||||||
|
globalThis.addEventListener('unload', (e)=>{
|
||||||
|
console.log('Exiting');
|
||||||
|
});
|
||||||
|
serve(handler, {
|
||||||
|
port: 8080,
|
||||||
|
hostname: '0.0.0.0'
|
||||||
|
});
|
||||||
@@ -20,6 +20,13 @@
|
|||||||
"watch": true
|
"watch": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"deno-bunled": {
|
||||||
|
"executor": "nx:run-commands",
|
||||||
|
"options": {
|
||||||
|
"cwd": "apps/deno-vless",
|
||||||
|
"command": "deno bundle src/main.ts deno-bunled.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"executor": "@nrwl/deno:test",
|
"executor": "@nrwl/deno:test",
|
||||||
"outputs": ["coverage/apps/deno-vless"],
|
"outputs": ["coverage/apps/deno-vless"],
|
||||||
|
|||||||
@@ -1,18 +1,33 @@
|
|||||||
import {
|
|
||||||
serveDir,
|
|
||||||
serveFile,
|
|
||||||
} from 'https://deno.land/std@0.167.0/http/file_server.ts';
|
|
||||||
async function serveClient(req: Request, basePath: string) {
|
async function serveClient(req: Request, basePath: string) {
|
||||||
const pathname = new URL(req.url).pathname;
|
const url = new URL(req.url)
|
||||||
if (pathname.startsWith('/assets')) {
|
if (url.pathname.startsWith('/assets') || url.pathname.includes(basePath)) {
|
||||||
const resp = await serveDir(req, {
|
// const resp = await serveDir(req, {
|
||||||
fsRoot: `${Deno.cwd()}/dist/apps/cf-page`,
|
// fsRoot: `${Deno.cwd()}/dist/apps/cf-page`,
|
||||||
});
|
// });
|
||||||
resp.headers.set('cache-control', 'public, max-age=2592000');
|
// resp.headers.set('cache-control', 'public, max-age=2592000');
|
||||||
return resp;
|
let targetUrl = `https://raw.githubusercontent.com/zizifn/edgetunnel/main/dist/apps/cf-page${url.pathname}`;
|
||||||
}
|
if(url.pathname.includes(basePath)){
|
||||||
if (pathname.includes(basePath)) {
|
targetUrl = `https://raw.githubusercontent.com/zizifn/edgetunnel/main/dist/apps/cf-page/index.html`;
|
||||||
return await serveFile(req, `${Deno.cwd()}/dist/apps/cf-page/index.html`);
|
}
|
||||||
|
console.log(targetUrl)
|
||||||
|
const resp = await fetch(targetUrl);
|
||||||
|
const modifiedHeaders = new Headers(resp.headers);
|
||||||
|
modifiedHeaders.delete('content-security-policy');
|
||||||
|
if(url.pathname.endsWith('.js')){
|
||||||
|
modifiedHeaders.set('content-type', 'application/javascript');
|
||||||
|
}else if(url.pathname.endsWith('.css')){
|
||||||
|
modifiedHeaders.set('content-type', 'text/css');
|
||||||
|
}else if(url.pathname.includes(basePath)){
|
||||||
|
modifiedHeaders.set('content-type', 'text/html; charset=utf-8');
|
||||||
|
|
||||||
|
}
|
||||||
|
return new Response(
|
||||||
|
resp.body,
|
||||||
|
{
|
||||||
|
status: resp.status,
|
||||||
|
headers: modifiedHeaders
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const basicAuth = req.headers.get('Authorization') || '';
|
const basicAuth = req.headers.get('Authorization') || '';
|
||||||
const authString = basicAuth.split(' ')?.[1] || '';
|
const authString = basicAuth.split(' ')?.[1] || '';
|
||||||
|
|||||||
26
dist/apps/node-vless/3rdpartylicenses.txt
vendored
Normal file
26
dist/apps/node-vless/3rdpartylicenses.txt
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
edge-bypass
|
||||||
|
MIT
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 zizifn
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
vless-js
|
||||||
6344
dist/apps/node-vless/main.js
vendored
6344
dist/apps/node-vless/main.js
vendored
File diff suppressed because one or more lines are too long
2
dist/apps/node-vless/main.js.map
vendored
2
dist/apps/node-vless/main.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -12,6 +12,7 @@
|
|||||||
"node-vless:build": "nx build cf-page --configuration=production && nx build node-vless --configurations=production",
|
"node-vless:build": "nx build cf-page --configuration=production && nx build node-vless --configurations=production",
|
||||||
"node-vless:bunled": "nx build cf-page --configuration=production && nx build node-vless --configurations=production -- --externalDependencies=none",
|
"node-vless:bunled": "nx build cf-page --configuration=production && nx build node-vless --configurations=production -- --externalDependencies=none",
|
||||||
"node-vless:start": "node dist/apps/node-vless/main.js",
|
"node-vless:start": "node dist/apps/node-vless/main.js",
|
||||||
|
"deno-vless:bunled": "nx deno-bunled deno-vless",
|
||||||
"test": "nx test"
|
"test": "nx test"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
|
|||||||
Reference in New Issue
Block a user