mirror of
https://github.com/lush2020/edgetunnel.git
synced 2026-03-21 08:52:43 +08:00
delete custom code for bypass
This commit is contained in:
48
.github/workflows/build-client.yml
vendored
48
.github/workflows/build-client.yml
vendored
@@ -1,48 +0,0 @@
|
|||||||
name: build client
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Use Node.js 18
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 18.x
|
|
||||||
cache: 'npm'
|
|
||||||
- run: npm ci
|
|
||||||
- name: pkg edge tunnel client
|
|
||||||
run: npx nx run edge-bypass-client:pkg
|
|
||||||
- name: copy config file
|
|
||||||
run: cp dist/apps/edge-bypass-client/assets/config.json dist/pkg/edge-tunnel-client
|
|
||||||
- name: linux arm64 package
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: edgetunnel-linux-arm64
|
|
||||||
path: |
|
|
||||||
dist/pkg/edge-tunnel-client/edgetunnel-linux-arm64
|
|
||||||
dist/pkg/edge-tunnel-client/config.json
|
|
||||||
- name: linux x64 package
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: edgetunnel-linux-x64
|
|
||||||
path: |
|
|
||||||
dist/pkg/edge-tunnel-client/edgetunnel-linux-x64
|
|
||||||
dist/pkg/edge-tunnel-client/config.json
|
|
||||||
- name: macos x64 package
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: edgetunnel-macos-x64
|
|
||||||
path: |
|
|
||||||
dist/pkg/edge-tunnel-client/edgetunnel-macos-x64
|
|
||||||
dist/pkg/edge-tunnel-client/config.json
|
|
||||||
- name: win x64 package
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: edgetunnel-win-x64
|
|
||||||
path: |
|
|
||||||
dist/pkg/edge-tunnel-client/edgetunnel-win-x64.exe
|
|
||||||
dist/pkg/edge-tunnel-client/config.json
|
|
||||||
37
.github/workflows/release.yml
vendored
37
.github/workflows/release.yml
vendored
@@ -1,37 +0,0 @@
|
|||||||
name: release
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Use Node.js 18
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 18.x
|
|
||||||
cache: 'npm'
|
|
||||||
- run: npm ci
|
|
||||||
- name: pkg edge tunnel client
|
|
||||||
run: npx nx run edge-bypass-client:pkg
|
|
||||||
- name: copy config file
|
|
||||||
run: cp dist/apps/edge-bypass-client/assets/config.json dist/pkg/edge-tunnel-client
|
|
||||||
- name: zip edgetunnel-linux-arm64
|
|
||||||
run: zip -j edgetunnel-linux-arm64-${{github.run_number}}.zip dist/pkg/edge-tunnel-client/edgetunnel-linux-arm64 dist/pkg/edge-tunnel-client/config.json
|
|
||||||
- name: zip edgetunnel-linux-x64
|
|
||||||
run: zip -j edgetunnel-linux-x64-${{github.run_number}}.zip dist/pkg/edge-tunnel-client/edgetunnel-linux-x64 dist/pkg/edge-tunnel-client/config.json
|
|
||||||
- name: zip edgetunnel-macos-x64
|
|
||||||
run: zip -j edgetunnel-macos-x64-${{github.run_number}}.zip dist/pkg/edge-tunnel-client/edgetunnel-macos-x64 dist/pkg/edge-tunnel-client/config.json
|
|
||||||
- name: zip edgetunnel-win-x64
|
|
||||||
run: zip -j edgetunnel-win-x64-${{github.run_number}}.zip dist/pkg/edge-tunnel-client/edgetunnel-win-x64.exe dist/pkg/edge-tunnel-client/config.json
|
|
||||||
- name: Upload release files
|
|
||||||
uses: svenstaro/upload-release-action@v2
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
with:
|
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
file_glob: true
|
|
||||||
file: edgetunnel*.zip
|
|
||||||
tag: ${{ github.ref }}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "deno-bypass",
|
|
||||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
||||||
"sourceRoot": "apps/deno-bypass/src",
|
|
||||||
"projectType": "application",
|
|
||||||
"targets": {
|
|
||||||
"run": {
|
|
||||||
"executor": "nx:run-commands",
|
|
||||||
"options": {
|
|
||||||
"command": "deno run --allow-net --allow-env --allow-write apps/deno-bypass/src/bypass.ts "
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"serve": {
|
|
||||||
"executor": "nx:run-commands",
|
|
||||||
"options": {
|
|
||||||
"command": "deno run --allow-net --allow-write --allow-env --watch apps/deno-bypass/src/bypass.ts "
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
import { serve } from 'https://deno.land/std@0.167.0/http/server.ts';
|
|
||||||
import { buildRawHttp500, isVaildateReq } from './helper.ts';
|
|
||||||
const userID = Deno.env.get('UUID');
|
|
||||||
|
|
||||||
const handler = async (request: Request): Promise<Response> => {
|
|
||||||
console.log('--------start--------');
|
|
||||||
try {
|
|
||||||
const headers = request.headers;
|
|
||||||
const serverAddress = headers.get('x-host') || '';
|
|
||||||
const remotePort = headers.get('x-port') || 443;
|
|
||||||
const isHttp = headers.get('x-http') === 'true';
|
|
||||||
const uuid = request.headers.get('x-uuid');
|
|
||||||
|
|
||||||
if (!serverAddress || !remotePort || !userID) {
|
|
||||||
return new Response(
|
|
||||||
`Version 0.0.1-2022/12/04!!
|
|
||||||
${userID ? 'has UUID env' : 'no UUID env'}
|
|
||||||
感谢 deno deploy 严肃对待 web standard。支持 HTTP request & response streaming。
|
|
||||||
`,
|
|
||||||
{
|
|
||||||
status: 200,
|
|
||||||
headers: {},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (uuid !== userID) {
|
|
||||||
return new Response(buildRawHttp500('Do not send right UUID!'), {
|
|
||||||
status: 403,
|
|
||||||
headers: {},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isVaildateReq(request)) {
|
|
||||||
return new Response(
|
|
||||||
buildRawHttp500(
|
|
||||||
'request is not vaild due to lcoalip or request body is null'
|
|
||||||
),
|
|
||||||
{
|
|
||||||
status: 500,
|
|
||||||
headers: {},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
console.log(
|
|
||||||
`want to proxy to server address ${serverAddress}, and port ${remotePort}`
|
|
||||||
);
|
|
||||||
|
|
||||||
const connection = await Deno.connect({
|
|
||||||
port: Number(remotePort),
|
|
||||||
hostname: serverAddress,
|
|
||||||
});
|
|
||||||
|
|
||||||
// const proxyResp = request.body?.pipeThrough(connection);
|
|
||||||
// 1. request.body readablestream end casue socket to be end, this will casue socket send FIN package early
|
|
||||||
// and casue deno can't get TCP pcakge.
|
|
||||||
// 2. current soluction for this, let proxy client wait for few ms and then end readablestream
|
|
||||||
// 3. this is only inpact HTTP proxy not https
|
|
||||||
let readablestreamRsp = connection.readable;
|
|
||||||
if (isHttp) {
|
|
||||||
// if is http, we need wait for request read, or we can warpper into async function
|
|
||||||
for await (let chunk of request.body || []) {
|
|
||||||
// console.log(new TextDecoder().decode(chunk));
|
|
||||||
connection.write(chunk);
|
|
||||||
}
|
|
||||||
readablestreamRsp = connection.readable;
|
|
||||||
} else {
|
|
||||||
readablestreamRsp = request.body!.pipeThrough(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Response(readablestreamRsp, {
|
|
||||||
status: 200,
|
|
||||||
headers: {},
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return new Response(buildRawHttp500('has error'), {
|
|
||||||
status: 500,
|
|
||||||
headers: {},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
serve(handler, { port: 8888, hostname: '[::]' });
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
const EOL = '\n';
|
|
||||||
function isPrivateIP(ip: string) {
|
|
||||||
if (ip === 'localhost') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const parts = ip.split('.');
|
|
||||||
return (
|
|
||||||
parts[0] === '10' ||
|
|
||||||
(parts[0] === '172' &&
|
|
||||||
parseInt(parts[1], 10) >= 16 &&
|
|
||||||
parseInt(parts[1], 10) <= 31) ||
|
|
||||||
(parts[0] === '192' && parts[1] === '168')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildRawHttp500(message: string) {
|
|
||||||
const body = new TextEncoder().encode(`${message}`);
|
|
||||||
return new ReadableStream({
|
|
||||||
start(controller) {
|
|
||||||
controller.enqueue(
|
|
||||||
new TextEncoder().encode(`HTTP/1.1 500 Internal Server Error${EOL}`)
|
|
||||||
);
|
|
||||||
controller.enqueue(
|
|
||||||
new TextEncoder().encode(`content-length: ${body.length}${EOL}`)
|
|
||||||
);
|
|
||||||
controller.enqueue(
|
|
||||||
new TextEncoder().encode(
|
|
||||||
`content-type: text/plain;charset=UTF-8${EOL}${EOL}`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
controller.enqueue(body);
|
|
||||||
controller.close();
|
|
||||||
},
|
|
||||||
cancel() {},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function isVaildateReq(request: Request) {
|
|
||||||
const serverAddress = request.headers.get('x-host') || '';
|
|
||||||
let isVaild = true;
|
|
||||||
if (isPrivateIP(serverAddress) || !request.body) {
|
|
||||||
console.log('lcoal ip or request.body is null');
|
|
||||||
isVaild = false;
|
|
||||||
}
|
|
||||||
return isVaild;
|
|
||||||
}
|
|
||||||
|
|
||||||
export { isPrivateIP, buildRawHttp500, isVaildateReq };
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
import { serve } from 'https://deno.land/std@0.167.0/http/server.ts';
|
|
||||||
|
|
||||||
const userID = Deno.env.get('UUID') || '***REMOVED******';
|
|
||||||
|
|
||||||
const handler = async (request: Request): Promise<Response> => {
|
|
||||||
const headers = request.headers;
|
|
||||||
const serverAddress = headers.get('x-host') || '';
|
|
||||||
const remotePort = headers.get('x-port') || 443;
|
|
||||||
const uuid = headers.get('x-uuid');
|
|
||||||
|
|
||||||
if (!serverAddress || !remotePort || !userID) {
|
|
||||||
return new Response(
|
|
||||||
`Version 0.0.1-2022/12/04!!
|
|
||||||
${userID ? 'has UUID env' : 'no UUID env'}
|
|
||||||
感谢 deno deploy 严肃对待 web standard。支持 HTTP request & response streaming。
|
|
||||||
`,
|
|
||||||
{
|
|
||||||
status: 200,
|
|
||||||
headers: {},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
console.log(
|
|
||||||
`want to proxy to server address ${serverAddress}, and port ${remotePort}`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (uuid !== userID) {
|
|
||||||
return new Response('Do not send right UUID!', {
|
|
||||||
status: 403,
|
|
||||||
headers: {},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const connection = await Deno.connect({
|
|
||||||
port: Number(remotePort),
|
|
||||||
hostname: serverAddress,
|
|
||||||
});
|
|
||||||
const result = request.body?.pipeThrough(
|
|
||||||
new TransformStream({
|
|
||||||
transform(chunk, controller) {
|
|
||||||
console.log('-- transform--');
|
|
||||||
controller.enqueue(chunk);
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
const proxyResp = result?.pipeThrough(connection);
|
|
||||||
for await (let chunk of connection.readable) {
|
|
||||||
console.log('-------', new TextDecoder().decode(chunk));
|
|
||||||
}
|
|
||||||
// let timer: number | undefined = undefined;
|
|
||||||
// const body = new ReadableStream({
|
|
||||||
// start(controller) {
|
|
||||||
// timer = setInterval(() => {
|
|
||||||
// const message = `It is ${new Date().toISOString()}\n`;
|
|
||||||
// controller.enqueue(new TextEncoder().encode(message));
|
|
||||||
// }, 1000);
|
|
||||||
// },
|
|
||||||
// pull(chunk) {},
|
|
||||||
// cancel() {
|
|
||||||
// if (timer !== undefined) {
|
|
||||||
// clearInterval(timer);
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
return new Response('111', {
|
|
||||||
status: 200,
|
|
||||||
headers: {},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
serve(handler, { port: 8080, hostname: '0.0.0.0' });
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
// (async () => {
|
|
||||||
// try {
|
|
||||||
// for await (let chunk of request.body || []) {
|
|
||||||
// // console.log(new TextDecoder().decode(chunk));
|
|
||||||
// connection.write(chunk);
|
|
||||||
// }
|
|
||||||
// } catch (error) {
|
|
||||||
// console.log(error);
|
|
||||||
// return new Response('has error', {
|
|
||||||
// status: 500,
|
|
||||||
// headers: {},
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// })();
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import { serve } from 'https://deno.land/std@0.167.0/http/server.ts';
|
|
||||||
|
|
||||||
console.log('Current Deno version', Deno.version.deno);
|
|
||||||
|
|
||||||
const handler = async (request: Request): Promise<Response> => {
|
|
||||||
const connection = await Deno.connect({
|
|
||||||
port: 80,
|
|
||||||
hostname: 'www.baidu.com',
|
|
||||||
});
|
|
||||||
const body2 = new ReadableStream({
|
|
||||||
start(controller) {
|
|
||||||
controller.enqueue(new TextEncoder().encode('GET / HTTP/1.1\r\n'));
|
|
||||||
controller.enqueue(new TextEncoder().encode('Host: www.baidu.com\r\n'));
|
|
||||||
controller.enqueue(
|
|
||||||
new TextEncoder().encode('User-Agent: curl/7.83.1\r\n')
|
|
||||||
);
|
|
||||||
controller.enqueue(new TextEncoder().encode('Accept: */*\r\n\r\n'));
|
|
||||||
controller.close();
|
|
||||||
},
|
|
||||||
cancel() {},
|
|
||||||
});
|
|
||||||
|
|
||||||
// for await (const chunk of body2) {
|
|
||||||
// console.log('11', new TextDecoder().decode(chunk));
|
|
||||||
// }
|
|
||||||
const proxyResp = body2?.pipeThrough(connection);
|
|
||||||
|
|
||||||
for await (const chunk of proxyResp) {
|
|
||||||
console.log('11', new TextDecoder().decode(chunk));
|
|
||||||
}
|
|
||||||
return new Response('111', {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
'x-ray': 'xxxx',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
serve(handler, { port: 8080, hostname: '0.0.0.0' });
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": ["../../.eslintrc.json"],
|
|
||||||
"ignorePatterns": ["!**/*"],
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
|
||||||
"rules": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": ["*.ts", "*.tsx"],
|
|
||||||
"rules": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": ["*.js", "*.jsx"],
|
|
||||||
"rules": {}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
export default {
|
|
||||||
displayName: 'edge-bypass-client',
|
|
||||||
preset: '../../jest.preset.js',
|
|
||||||
globals: {
|
|
||||||
'ts-jest': {
|
|
||||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
testEnvironment: 'node',
|
|
||||||
transform: {
|
|
||||||
'^.+\\.[tj]s$': 'ts-jest',
|
|
||||||
},
|
|
||||||
moduleFileExtensions: ['ts', 'js', 'html'],
|
|
||||||
coverageDirectory: '../../coverage/apps/edge-bypass-client',
|
|
||||||
};
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "edge-bypass-client",
|
|
||||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
||||||
"sourceRoot": "apps/edge-bypass-client/src",
|
|
||||||
"projectType": "application",
|
|
||||||
"targets": {
|
|
||||||
"build": {
|
|
||||||
"executor": "@nrwl/webpack:webpack",
|
|
||||||
"outputs": ["{options.outputPath}"],
|
|
||||||
"options": {
|
|
||||||
"target": "node",
|
|
||||||
"compiler": "tsc",
|
|
||||||
"outputPath": "dist/apps/edge-bypass-client",
|
|
||||||
"main": "apps/edge-bypass-client/src/main.ts",
|
|
||||||
"tsConfig": "apps/edge-bypass-client/tsconfig.app.json",
|
|
||||||
"assets": ["apps/edge-bypass-client/src/assets"]
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"optimization": true,
|
|
||||||
"extractLicenses": true,
|
|
||||||
"inspect": false,
|
|
||||||
"fileReplacements": [
|
|
||||||
{
|
|
||||||
"replace": "apps/edge-bypass-client/src/environments/environment.ts",
|
|
||||||
"with": "apps/edge-bypass-client/src/environments/environment.prod.ts"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"serve": {
|
|
||||||
"executor": "@nrwl/js:node",
|
|
||||||
"options": {
|
|
||||||
"buildTarget": "edge-bypass-client:build",
|
|
||||||
"args": [
|
|
||||||
"run",
|
|
||||||
"--config",
|
|
||||||
"./apps/edge-bypass-client/src/assets/config-local.json"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"buildTarget": "edge-bypass-client:build:production"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pkg": {
|
|
||||||
"executor": "nx:run-commands",
|
|
||||||
"options": {
|
|
||||||
"command": "npx pkg dist/apps/edge-bypass-client/main.js --targets linux,linux-arm64,macos,win --compress GZip --output dist/pkg/edge-tunnel-client/edgetunnel"
|
|
||||||
},
|
|
||||||
"dependsOn": ["build"]
|
|
||||||
},
|
|
||||||
"pkg-local": {
|
|
||||||
"executor": "nx:run-commands",
|
|
||||||
"options": {
|
|
||||||
"command": "npx pkg dist/apps/edge-bypass-client/main.js --targets win --compress GZip --output dist/pkg/edge-tunnel-client/edgetunnel"
|
|
||||||
},
|
|
||||||
"dependsOn": ["build"]
|
|
||||||
},
|
|
||||||
"lint": {
|
|
||||||
"executor": "@nrwl/linter:eslint",
|
|
||||||
"outputs": ["{options.outputFile}"],
|
|
||||||
"options": {
|
|
||||||
"lintFilePatterns": ["apps/edge-bypass-client/**/*.ts"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"test": {
|
|
||||||
"executor": "@nrwl/jest:jest",
|
|
||||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
|
||||||
"options": {
|
|
||||||
"jestConfig": "apps/edge-bypass-client/jest.config.ts",
|
|
||||||
"passWithNoTests": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"port": "",
|
|
||||||
"address": "",
|
|
||||||
"uuid": "",
|
|
||||||
"logLevel": "DEBUG"
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export const environment = {
|
|
||||||
production: true,
|
|
||||||
};
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export const environment = {
|
|
||||||
production: false,
|
|
||||||
};
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
import { Command } from 'commander';
|
|
||||||
import { writeFileSync, existsSync, readFileSync } from 'fs';
|
|
||||||
import { exit } from 'node:process';
|
|
||||||
import { env } from 'process';
|
|
||||||
let config: {
|
|
||||||
port: string;
|
|
||||||
address: string;
|
|
||||||
uuid: string;
|
|
||||||
logLevel: string;
|
|
||||||
} = null;
|
|
||||||
const program = new Command();
|
|
||||||
program.option('-v').action((options) => {
|
|
||||||
console.log(options);
|
|
||||||
exit();
|
|
||||||
});
|
|
||||||
program
|
|
||||||
.command('run')
|
|
||||||
.description('launch local http proxy for edge pass')
|
|
||||||
.option(
|
|
||||||
'--config <config>',
|
|
||||||
'address of remote proxy, etc https://***.deno.dev/'
|
|
||||||
)
|
|
||||||
.option(
|
|
||||||
'--address <address>',
|
|
||||||
'address of remote proxy, etc https://***.deno.dev/'
|
|
||||||
)
|
|
||||||
.option('--port <port>', 'local port of http proxy proxy')
|
|
||||||
.option('--uuid <uuid>', 'uuid')
|
|
||||||
.option('--save', 'if this is pass, will save to config.json')
|
|
||||||
.action((options) => {
|
|
||||||
console.log(__dirname);
|
|
||||||
console.log(process.cwd());
|
|
||||||
if (options.config) {
|
|
||||||
if (existsSync(options.config)) {
|
|
||||||
const content = readFileSync(options.config, {
|
|
||||||
encoding: 'utf-8',
|
|
||||||
});
|
|
||||||
config = JSON.parse(content);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
console.error('config not exsit!');
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
config = options;
|
|
||||||
if (config.address && config.port && config.uuid) {
|
|
||||||
if (options.save) {
|
|
||||||
writeFileSync('./config.json', JSON.stringify(options), {
|
|
||||||
encoding: 'utf-8',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error('need pass all args!');
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
program.parse();
|
|
||||||
env.NODE_ENV = env.NODE_ENV || 'production';
|
|
||||||
|
|
||||||
export { config };
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
import { IncomingMessage } from 'http';
|
|
||||||
import * as os from 'os';
|
|
||||||
import * as url from 'node:url';
|
|
||||||
import { config } from './cmd';
|
|
||||||
|
|
||||||
async function* concatStreams(readables: any[]) {
|
|
||||||
for (const readable of readables) {
|
|
||||||
for await (const chunk of readable) {
|
|
||||||
yield chunk;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST http://zizi.press/test11.ttt?test1=66 HTTP/1.1
|
|
||||||
// Host: zizi.press
|
|
||||||
// User-Agent: curl/7.83.1
|
|
||||||
// Connection: Keep-Alive
|
|
||||||
// Content-Type: application/json
|
|
||||||
// Accept: application/json
|
|
||||||
// Content-Length: 16
|
|
||||||
|
|
||||||
// {"tool": "curl"}
|
|
||||||
|
|
||||||
//-------------------------------------
|
|
||||||
// GET http://zizi.press/test11.ttt?test1=66 HTTP/1.1
|
|
||||||
// Host: zizi.press
|
|
||||||
// User-Agent: curl/7.83.1
|
|
||||||
// Accept: */*
|
|
||||||
// Connection: Keep-Alive
|
|
||||||
|
|
||||||
function rawHTTPHeader(req: IncomingMessage) {
|
|
||||||
const reqUrl = url.parse(req.url);
|
|
||||||
const headers = Object.entries(req.headers)
|
|
||||||
.map(([key, value]) => {
|
|
||||||
return `${key}:${value}`;
|
|
||||||
})
|
|
||||||
.join(os.EOL);
|
|
||||||
const raw = `${req.method} ${reqUrl.path} HTTP/${req.httpVersion}${os.EOL}${headers}${os.EOL}${os.EOL}`;
|
|
||||||
return raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
function rawHTTPPackage(req: IncomingMessage) {
|
|
||||||
const rawHttpHeader = rawHTTPHeader(req);
|
|
||||||
return concatStreams([[rawHttpHeader], req]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function* deplay(ms) {
|
|
||||||
yield await new Promise((res, reject) => {
|
|
||||||
setTimeout(() => res(''), ms);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// delay few ms for
|
|
||||||
// // request.body readablestream end casue socket to be end, this will casue socket send FIN package early
|
|
||||||
// and casue deno can't get TCP pcakge.
|
|
||||||
function rawHTTPPackageWithDelay(req: IncomingMessage) {
|
|
||||||
const rawHttpHeader = rawHTTPHeader(req);
|
|
||||||
return concatStreams([[rawHttpHeader], req, deplay(500)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function errorHandler() {
|
|
||||||
process
|
|
||||||
.on('unhandledRejection', (reason, p) => {
|
|
||||||
console.error(reason, 'Unhandled Rejection at Promise', p);
|
|
||||||
})
|
|
||||||
.on('uncaughtException', (err) => {
|
|
||||||
console.error(err, 'Uncaught Exception thrown');
|
|
||||||
// should exit node.js, but anyway
|
|
||||||
// process.exit(1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loghelper(...args) {
|
|
||||||
let logs = args;
|
|
||||||
if (config.logLevel?.toUpperCase() !== 'DEBUG') {
|
|
||||||
logs = args.map((item) => {
|
|
||||||
if (item instanceof Error) {
|
|
||||||
return `${item.message}`;
|
|
||||||
} else {
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
console.log('', ...logs);
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
concatStreams,
|
|
||||||
rawHTTPPackage,
|
|
||||||
rawHTTPHeader,
|
|
||||||
rawHTTPPackageWithDelay,
|
|
||||||
errorHandler,
|
|
||||||
loghelper,
|
|
||||||
};
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
import { createServer } from 'node:http';
|
|
||||||
import { pipeline, Readable } from 'node:stream';
|
|
||||||
import { config } from './lib/cmd';
|
|
||||||
import * as url from 'node:url';
|
|
||||||
import * as undici from 'undici';
|
|
||||||
import {
|
|
||||||
concatStreams,
|
|
||||||
rawHTTPPackage,
|
|
||||||
errorHandler,
|
|
||||||
loghelper,
|
|
||||||
} from './lib/helper';
|
|
||||||
|
|
||||||
errorHandler();
|
|
||||||
|
|
||||||
const isLocal = process.env.DEBUG === 'true';
|
|
||||||
const httpProxyServer = createServer(async (req, resp) => {
|
|
||||||
const reqUrl = url.parse(req.url);
|
|
||||||
const clientSocketLoggerInfo = `[proxy to ${req.url}](http)`;
|
|
||||||
try {
|
|
||||||
loghelper(`${clientSocketLoggerInfo} Client use HTTP/${req.httpVersion}`);
|
|
||||||
// make call to edge http server
|
|
||||||
// 1. forward all package remote, socket over http body
|
|
||||||
const fromClientReq = Readable.from(rawHTTPPackage(req)).on(
|
|
||||||
'error',
|
|
||||||
(error) => {
|
|
||||||
loghelper(
|
|
||||||
`${clientSocketLoggerInfo} client socket to remote http body has error`,
|
|
||||||
error
|
|
||||||
);
|
|
||||||
req.destroy();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const { body, headers, statusCode, trailers } = await undici.request(
|
|
||||||
config.address,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'x-host': reqUrl.hostname,
|
|
||||||
'x-port': reqUrl.port || '80',
|
|
||||||
'x-uuid': config.uuid,
|
|
||||||
'x-http': 'true',
|
|
||||||
},
|
|
||||||
method: 'POST',
|
|
||||||
body: fromClientReq,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
loghelper(
|
|
||||||
`${clientSocketLoggerInfo} remote server return ${statusCode} Connected To Proxy`
|
|
||||||
);
|
|
||||||
// 2. forward remote reponse body to clientSocket
|
|
||||||
for await (const chunk of body) {
|
|
||||||
if (isLocal) {
|
|
||||||
loghelper(chunk.toString());
|
|
||||||
}
|
|
||||||
req.socket.write(chunk);
|
|
||||||
// resp.write(chunk);
|
|
||||||
}
|
|
||||||
// resp.end();
|
|
||||||
body.on('error', (err) => {
|
|
||||||
loghelper(
|
|
||||||
`${clientSocketLoggerInfo} remote server response body has error`,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
req.destroy();
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
req.destroy();
|
|
||||||
req.socket?.end();
|
|
||||||
loghelper(`${clientSocketLoggerInfo} has error `, error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// handle https website
|
|
||||||
httpProxyServer.on('connect', async (req, clientSocket, head) => {
|
|
||||||
const reqUrl = url.parse('https://' + req.url);
|
|
||||||
const clientSocketLoggerInfo = `[proxy to ${req.url}]`;
|
|
||||||
let fromClientSocket = null;
|
|
||||||
try {
|
|
||||||
loghelper(
|
|
||||||
`${clientSocketLoggerInfo} Client use HTTP/${
|
|
||||||
req.httpVersion
|
|
||||||
} Connected To Proxy, head on connect is ${head.toString() || 'empty'}`
|
|
||||||
);
|
|
||||||
// We need only the data once, the starting packet, per http proxy spec
|
|
||||||
clientSocket.write(
|
|
||||||
`HTTP/${req.httpVersion} 200 Connection Established\r\n\r\n`
|
|
||||||
);
|
|
||||||
|
|
||||||
// loghelper(config);
|
|
||||||
// make call to edge http server
|
|
||||||
// 1. forward all package remote, socket over http body
|
|
||||||
fromClientSocket = Readable.from(concatStreams([head, clientSocket]));
|
|
||||||
const { body, headers, statusCode, trailers } = await undici.request(
|
|
||||||
config.address,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'x-host': reqUrl.hostname,
|
|
||||||
'x-port': reqUrl.port,
|
|
||||||
'x-uuid': config.uuid,
|
|
||||||
// "Content-Type": "text/plain",
|
|
||||||
},
|
|
||||||
method: 'POST',
|
|
||||||
body: fromClientSocket,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
fromClientSocket.on('error', (error) => {
|
|
||||||
loghelper(
|
|
||||||
`${clientSocketLoggerInfo} client socket to remote http body has error`,
|
|
||||||
error
|
|
||||||
);
|
|
||||||
//
|
|
||||||
fromClientSocket.push(null);
|
|
||||||
clientSocket.destroy();
|
|
||||||
});
|
|
||||||
loghelper(`${clientSocketLoggerInfo} remote server return ${statusCode}`);
|
|
||||||
// 2. forward remote reponse body to clientSocket
|
|
||||||
for await (const chunk of body) {
|
|
||||||
clientSocket.write(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
body.on('error', (err) => {
|
|
||||||
loghelper(
|
|
||||||
`${clientSocketLoggerInfo} remote response body has error`,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
fromClientSocket.push(null);
|
|
||||||
clientSocket.destroy();
|
|
||||||
});
|
|
||||||
clientSocket.on('error', (e) => {
|
|
||||||
body?.destroy();
|
|
||||||
fromClientSocket.push(null);
|
|
||||||
clientSocket.end();
|
|
||||||
loghelper(`${clientSocketLoggerInfo} clientSocket has error: ` + e);
|
|
||||||
});
|
|
||||||
clientSocket.on('end', () => {
|
|
||||||
loghelper(`${clientSocketLoggerInfo} has done and end.`);
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
fromClientSocket?.push(null);
|
|
||||||
clientSocket.end();
|
|
||||||
loghelper(`${clientSocketLoggerInfo} has error `, error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
httpProxyServer.on('error', (err) => {
|
|
||||||
loghelper('SERVER ERROR', err);
|
|
||||||
});
|
|
||||||
httpProxyServer.on('clientError', (err, clientSocket) => {
|
|
||||||
loghelper('client error: ' + err);
|
|
||||||
clientSocket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
httpProxyServer.on('close', () => {
|
|
||||||
loghelper('Server close');
|
|
||||||
});
|
|
||||||
|
|
||||||
httpProxyServer.listen(Number(config.port), () => {
|
|
||||||
loghelper('Server runnig at http://localhost:' + config.port);
|
|
||||||
});
|
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
import { createServer } from 'node:http';
|
|
||||||
import { pipeline, Readable } from 'node:stream';
|
|
||||||
import { config } from '../lib/cmd';
|
|
||||||
import * as url from 'node:url';
|
|
||||||
import * as undici from 'undici';
|
|
||||||
import {
|
|
||||||
concatStreams,
|
|
||||||
rawHTTPHeader,
|
|
||||||
rawHTTPPackage,
|
|
||||||
rawHTTPPackageWithDelay,
|
|
||||||
} from '../lib/helper';
|
|
||||||
|
|
||||||
const isLocal = process.env.env === 'LOCAL';
|
|
||||||
const httpProxyServer = createServer(async (req, resp) => {
|
|
||||||
const reqUrl = url.parse(req.url || '');
|
|
||||||
const clientSocketLoggerInfo = `[proxy to ${req.url}](http)`;
|
|
||||||
try {
|
|
||||||
console.log(`${clientSocketLoggerInfo} Client use HTTP/${req.httpVersion}`);
|
|
||||||
|
|
||||||
// for await (const chunk of req.socket) {
|
|
||||||
// console.log(chunk.toString());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// make call to edge http server
|
|
||||||
// 1. forward all package remote, socket over http body
|
|
||||||
const { body, headers, statusCode, trailers } = await undici.request(
|
|
||||||
config.address,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'x-host': reqUrl.hostname,
|
|
||||||
'x-port': reqUrl.port || '80',
|
|
||||||
'x-uuid': config.uuid,
|
|
||||||
'x-http': 'true',
|
|
||||||
} as any,
|
|
||||||
method: 'POST',
|
|
||||||
// append few ms for body
|
|
||||||
// body: Readable.from(rawHTTPPackageWithDelay(req)),
|
|
||||||
body: Readable.from(rawHTTPPackage(req)),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
`${clientSocketLoggerInfo} remote server return ${statusCode} Connected To Proxy`
|
|
||||||
);
|
|
||||||
// 2. forward remote reponse body to clientSocket
|
|
||||||
for await (const chunk of body) {
|
|
||||||
if (isLocal) {
|
|
||||||
console.log(chunk.toString());
|
|
||||||
}
|
|
||||||
req.socket.write(chunk);
|
|
||||||
}
|
|
||||||
body.on('error', (err) => {
|
|
||||||
console.log(
|
|
||||||
`${clientSocketLoggerInfo} remote server response body has error`,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
});
|
|
||||||
// issue with pipeline
|
|
||||||
// https://stackoverflow.com/questions/55959479/error-err-stream-premature-close-premature-close-in-node-pipeline-stream
|
|
||||||
// pipeline(body, req.socket, (error) => {
|
|
||||||
// console.log(
|
|
||||||
// `${clientSocketLoggerInfo} remote server to clientSocket has error: ` +
|
|
||||||
// error
|
|
||||||
// );
|
|
||||||
// req.socket.end();
|
|
||||||
// req.socket.destroy();
|
|
||||||
// });
|
|
||||||
} catch (error) {
|
|
||||||
req.socket.end();
|
|
||||||
req.socket.destroy();
|
|
||||||
console.log(`${clientSocketLoggerInfo} has error `, error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// handle https website
|
|
||||||
httpProxyServer.on('connect', async (req, clientSocket, head) => {
|
|
||||||
const reqUrl = url.parse('https://' + req.url);
|
|
||||||
const clientSocketLoggerInfo = `[proxy to ${req.url}]`;
|
|
||||||
try {
|
|
||||||
console.log(
|
|
||||||
`${clientSocketLoggerInfo} Client use HTTP/${
|
|
||||||
req.httpVersion
|
|
||||||
} Connected To Proxy, head on connect is ${head.toString() || 'empty'}`
|
|
||||||
);
|
|
||||||
// We need only the data once, the starting packet, per http proxy spec
|
|
||||||
clientSocket.write(
|
|
||||||
`HTTP/${req.httpVersion} 200 Connection Established\r\n\r\n`
|
|
||||||
);
|
|
||||||
|
|
||||||
// console.log(config);
|
|
||||||
// make call to edge http server
|
|
||||||
// 1. forward all package remote, socket over http body
|
|
||||||
const { body, headers, statusCode, trailers } = await undici.request(
|
|
||||||
config.address,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'x-host': reqUrl.hostname,
|
|
||||||
'x-port': reqUrl.port,
|
|
||||||
'x-uuid': config.uuid,
|
|
||||||
// "Content-Type": "text/plain",
|
|
||||||
} as any,
|
|
||||||
method: 'POST',
|
|
||||||
body: Readable.from(concatStreams([head, clientSocket])),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
console.log(`${clientSocketLoggerInfo} remote server return ${statusCode}`);
|
|
||||||
// 2. forward remote reponse body to clientSocket
|
|
||||||
// 2. forward remote reponse body to clientSocket
|
|
||||||
for await (const chunk of body) {
|
|
||||||
clientSocket.write(chunk);
|
|
||||||
}
|
|
||||||
body.on('error', (err) => {
|
|
||||||
console.log(`${clientSocketLoggerInfo} body error`, err);
|
|
||||||
});
|
|
||||||
// pipeline(body, clientSocket, (error) => {
|
|
||||||
// console.log(
|
|
||||||
// `${clientSocketLoggerInfo} remote server to clientSocket has error: `,
|
|
||||||
// error
|
|
||||||
// );
|
|
||||||
// body?.destroy();
|
|
||||||
// clientSocket.destroy();
|
|
||||||
// });
|
|
||||||
clientSocket.on('error', (e) => {
|
|
||||||
body?.destroy();
|
|
||||||
clientSocket.destroy();
|
|
||||||
console.log(`${clientSocketLoggerInfo} clientSocket has error: ` + e);
|
|
||||||
});
|
|
||||||
clientSocket.on('end', () => {
|
|
||||||
console.log(`${clientSocketLoggerInfo} has done and end.`);
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
clientSocket.destroy();
|
|
||||||
console.log(`${clientSocketLoggerInfo} has error `, error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
httpProxyServer.on('error', (err) => {
|
|
||||||
console.log('SERVER ERROR');
|
|
||||||
console.log(err);
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
httpProxyServer.on('clientError', (err, clientSocket) => {
|
|
||||||
console.log('client error: ' + err);
|
|
||||||
clientSocket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
httpProxyServer.on('close', () => {
|
|
||||||
console.log('Server close');
|
|
||||||
});
|
|
||||||
|
|
||||||
httpProxyServer.listen(Number(config.port), () => {
|
|
||||||
console.log('Server runnig at http://localhost:' + config.port);
|
|
||||||
});
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import * as undici from 'undici';
|
|
||||||
import { pipeline, Readable, Writable } from 'node:stream';
|
|
||||||
|
|
||||||
pipeline(
|
|
||||||
new Readable({
|
|
||||||
read() {
|
|
||||||
this.push(Buffer.from('undici'));
|
|
||||||
this.push(null);
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
undici.pipeline(
|
|
||||||
'http://localhost:1082',
|
|
||||||
{
|
|
||||||
method: 'POST',
|
|
||||||
},
|
|
||||||
({ statusCode, headers, body }) => {
|
|
||||||
console.log(`response received ${statusCode}`);
|
|
||||||
console.log('headers', headers);
|
|
||||||
console.log('headers', body);
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
),
|
|
||||||
// new Writable({
|
|
||||||
// write(chunk) {
|
|
||||||
// console.log(chunk.toString());
|
|
||||||
// },
|
|
||||||
// }),
|
|
||||||
(error) => {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "../../dist/out-tsc",
|
|
||||||
"module": "commonjs",
|
|
||||||
"types": ["node"]
|
|
||||||
},
|
|
||||||
"exclude": [
|
|
||||||
"jest.config.ts",
|
|
||||||
"**/*.spec.ts",
|
|
||||||
"**/*.test.ts",
|
|
||||||
"**/test-demo/*.ts"
|
|
||||||
],
|
|
||||||
"include": ["**/*.ts"]
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"module": "NodeNext",
|
|
||||||
"target": "ES2022"
|
|
||||||
},
|
|
||||||
"files": [],
|
|
||||||
"include": [],
|
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.app.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.spec.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "../../dist/out-tsc",
|
|
||||||
"module": "commonjs",
|
|
||||||
"types": ["jest", "node"]
|
|
||||||
},
|
|
||||||
"include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"]
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user