add cf worker poc

This commit is contained in:
zizifn
2023-05-17 12:05:40 +08:00
committed by zizifn
parent 5a2e475652
commit d56791c5d4
17 changed files with 455 additions and 3 deletions

View File

@@ -0,0 +1,18 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}

View File

@@ -0,0 +1,11 @@
# cf-worker-vless
This library was generated with [Nx](https://nx.dev).
## Building
Run `nx build cf-worker-vless` to build the library.
## Running unit tests
Run `nx test cf-worker-vless` to execute the unit tests via [Jest](https://jestjs.io).

View File

@@ -0,0 +1,10 @@
/* eslint-disable */
export default {
displayName: 'cf-worker-vless',
preset: '../../jest.preset.js',
transform: {
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../coverage/libs/cf-worker-vless',
};

View File

@@ -0,0 +1,5 @@
{
"name": "@edge-bypass/cf-worker-vless",
"version": "0.0.1",
"type": "commonjs"
}

View File

@@ -0,0 +1,40 @@
{
"name": "cf-worker-vless",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/cf-worker-vless/src",
"projectType": "library",
"targets": {
"build": {
"executor": "@nx/js:tsc",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/libs/cf-worker-vless",
"main": "libs/cf-worker-vless/src/index.ts",
"tsConfig": "libs/cf-worker-vless/tsconfig.lib.json",
"assets": ["libs/cf-worker-vless/*.md"]
}
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["libs/cf-worker-vless/**/*.ts"]
}
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "libs/cf-worker-vless/jest.config.ts",
"passWithNoTests": true
},
"configurations": {
"ci": {
"ci": true,
"codeCoverage": true
}
}
}
},
"tags": []
}

View File

@@ -0,0 +1,140 @@
import {
makeReadableWebSocketStream,
processVlessHeader,
vlessJs,
} from 'vless-js';
import { connect } from 'cloudflare:sockets';
import { Buffer } from 'node:buffer';
import { validate } from 'uuid';
interface Env {
UUID: string;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
let address = '';
let portWithRandomLog = '';
const userID = env.UUID;
const log = (info: string, event?: any) => {
console.log(`[${address}:${portWithRandomLog}] ${info}`, event || '');
};
const upgradeHeader = request.headers.get('Upgrade');
if (!upgradeHeader || upgradeHeader !== 'websocket') {
return new Response(`Expected Upgrade: websocket--uuid--${userID}`, {
status: 426,
});
}
const webSocketPair = new WebSocketPair();
const [client, webSocket] = Object.values(webSocketPair);
console.log(WebSocket.READY_STATE_OPEN);
const earlyDataHeader = request.headers.get('sec-websocket-protocol') || '';
let remoteSocket: TransformStream = null;
webSocket.accept();
webSocket.addEventListener('message', async (event) => {
if (remoteSocket) {
const writer = remoteSocket.writable.getWriter();
await writer.write(event.data);
writer.releaseLock();
return;
}
const vlessBuffer: ArrayBuffer = event.data as ArrayBuffer;
const {
hasError,
message,
portRemote,
addressRemote,
rawDataIndex,
vlessVersion,
isUDP,
} = processVlessHeader(vlessBuffer, userID);
address = addressRemote || '';
portWithRandomLog = `${portRemote}--${Math.random()} ${
isUDP ? 'udp ' : 'tcp '
} `;
log(`connecting`);
if (hasError) {
webSocket.close(); // server close will not casuse worker throw error
}
const vlessResponseHeader = new Uint8Array([vlessVersion![0], 0]);
const rawClientData = vlessBuffer.slice(rawDataIndex!);
remoteSocket = connect({
hostname: addressRemote,
port: portRemote,
});
log(`connected`);
const writer = remoteSocket.writable.getWriter();
await writer.write(rawClientData); // first write, nomal is tls client hello
writer.releaseLock();
remoteSocket.readable
.pipeTo(
new WritableStream({
start() {
if (webSocket.readyState === WebSocket.READY_STATE_OPEN) {
webSocket.send(vlessResponseHeader!);
}
},
async write(chunk: Uint8Array, controller) {
if (webSocket.readyState === WebSocket.READY_STATE_OPEN) {
webSocket.send(chunk);
} else {
controller.error(
'webSocket.readyState is not open, maybe close'
);
}
},
close() {
console.log(
`[${address}:${portWithRandomLog}] remoteConnection!.readable is close`
);
},
abort(reason) {
console.error(
`[${address}:${portWithRandomLog}] remoteConnection!.readable abort`,
reason
);
},
})
)
.catch((error) => {
console.error(
`[${address}:${portWithRandomLog}] processWebSocket has exception `,
error.stack || error
);
safeCloseWebSocket(webSocket);
});
// end
});
webSocket.addEventListener('close', async (event) => {
console.log('-------------close-----------------', event);
});
webSocket.addEventListener('error', () => {
console.log('-------------error-----------------');
});
return new Response(null, {
status: 101,
webSocket: client,
});
},
};
function safeCloseWebSocket(ws: WebSocket) {
try {
if (ws.readyState === WebSocket.READY_STATE_OPEN) {
ws.close();
}
} catch (error) {
console.error('safeCloseWebSocket error', error);
}
}

View File

@@ -0,0 +1,36 @@
import { connect } from 'cloudflare:sockets';
interface Env {
UUID: string;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
console.log('start fetch');
const socket = connect({
hostname: 'neverssl.com',
port: 80,
});
const writer = socket.writable.getWriter();
const encoder = new TextEncoder();
const encoded = encoder.encode(
'GET / HTTP/1.1\r\nHost: neverssl.com\r\n\r\n'
);
await writer.write(encoded);
const reader = socket.readable.getReader();
const decoder = new TextDecoder();
let response = '';
while (true) {
const res = await reader.read();
if (res.done) {
console.log('Stream done, socket connection has been closed.');
break;
}
response += decoder.decode(res.value);
}
return new Response(response);
},
};

View File

@@ -0,0 +1,11 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"lib": ["esnext"],
"types": ["@cloudflare/workers-types"],
"paths": {
"vless-js": ["../vless-js/src/index.ts"]
}
}
}

View File

@@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"declaration": true,
"types": ["node"]
},
"include": ["src/**/*.ts"],
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
}

View File

@@ -0,0 +1,14 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": [
"jest.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}