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,72 @@
{
"log": {
"loglevel": "debug"
},
"inbounds": [
{
"listen": "0.0.0.0",
"port": "4080",
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true,
"ip": "0.0.0.0"
}
},
{
"listen": "0.0.0.0",
"port": "4081",
"protocol": "http"
}
],
"dns": {
"servers": ["8.8.8.8"]
},
"outbounds": [
{
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "127.0.0.1",
"port": 8787,
"users": [
{
"id": "1a403b79-039b-4dc2-9b45-1ad13197b99a",
"encryption": "none",
"level": 0
}
]
}
]
},
"streamSettings": {
"network": "ws"
// "wsSettings": {
// "path": "/node-vless"
// }
// "security": "tls"
},
"tag": "zizi-ws"
},
{
"protocol": "freedom",
"tag": "direct"
}
],
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
// {
// "type": "field",
// "ip": ["8.8.8.8"],
// "outboundTag": "zizi-ws"
// },
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "zizi-ws"
}
]
}
}

View File

@@ -0,0 +1,74 @@
{
"log": {
"loglevel": "debug"
},
"inbounds": [
{
"listen": "0.0.0.0",
"port": "4080",
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true,
"ip": "0.0.0.0"
}
},
{
"listen": "0.0.0.0",
"port": "4081",
"protocol": "http"
}
],
"dns": {
"servers": ["8.8.8.8"]
},
"outbounds": [
{
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "192.203.230.111",
"port": 80,
"users": [
{
"id": "1a403b79-039b-4dc2-9b45-1ad13197b99a",
"encryption": "none",
"level": 0
}
]
}
]
},
"streamSettings": {
"network": "ws",
"wsSettings": {
"headers": {
"Host": "vless-dev.121107.xyz"
}
}
// "security": "tls"
},
"tag": "zizi-ws"
},
{
"protocol": "freedom",
"tag": "direct"
}
],
"routing": {
"domainStrategy": "AsIs",
"rules": [
// {
// "type": "field",
// "ip": ["8.8.8.8"],
// "outboundTag": "zizi-ws"
// },
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "zizi-ws"
}
]
}
}

View File

@@ -2,7 +2,7 @@
// @ts-ignore
import { connect } from 'cloudflare:sockets';
export const onRequest: PagesFunction<Env> = async (context) => {
export const onRequest: PagesFunction<any> = async (context) => {
console.log('start fetch');
const socket = connect({
hostname: 'neverssl.com',

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"
]
}

View File

@@ -3,12 +3,21 @@ export function vlessJs(): string {
return 'vless-js';
}
const WS_READY_STATE_OPEN = 1;
export function delay(ms: number) {
return new Promise((resolve, rej) => {
setTimeout(resolve, ms);
});
}
/**
* we need make sure read websocket message in order
* @param ws
* @param earlyDataHeader
* @param log
* @returns
*/
export function makeReadableWebSocketStream(
ws: WebSocket | any,
earlyDataHeader: string,
@@ -90,7 +99,7 @@ function base64ToArrayBuffer(base64Str: string) {
export function safeCloseWebSocket(socket: WebSocket | any) {
try {
if (socket.readyState === socket.OPEN) {
if (socket.readyState === WS_READY_STATE_OPEN) {
socket.close();
}
} catch (error) {

View File

@@ -13,7 +13,8 @@
"node-vless:bunled": "nx build cf-page --configuration=production && nx build node-vless --skip-nx-cache --configurations=production -- --externalDependencies=none",
"node-vless:start": "node dist/apps/node-vless/main.js",
"deno-vless:bunled": "nx deno-bunled deno-vless",
"test": "nx test"
"test": "nx test",
"cf-worker": "wrangler dev ./libs/cf-worker-vless/src/index.ts --experimental-local"
},
"private": true,
"dependencies": {

View File

@@ -15,6 +15,7 @@
"skipDefaultLibCheck": true,
"baseUrl": ".",
"paths": {
"@edge-bypass/cf-worker-vless": ["libs/cf-worker-vless/src/index.ts"],
"edge-ui": ["libs/edge-ui/src/index.ts"],
"vless-js": ["libs/vless-js/src/index.ts"]
}