* chaneg deno code

* enhance logic

* chaneg to remove eol

* add cient code

* remove end call

* enhance logger

* add doc
This commit is contained in:
zizifn
2022-12-07 01:23:20 +08:00
committed by GitHub
parent 56f931dc06
commit d34bdb1907
5 changed files with 92 additions and 42 deletions

View File

@@ -2,7 +2,7 @@
一个无比简单安全,基于 edge 的 tunnel 。 一个无比简单安全,基于 edge 的 tunnel 。
**v2ray-heroku 由于 heroku 取消免费,项目已经死了。 这是新的项目。** **v2ray-heroku 由于 heroku 取消免费,项目已经死了。如果你还需要 v2ray-heroku 请访问 [yaolin-0/v2ray-heroku](https://github.com/yaolin-0/v2ray-heroku)**
> 项目正在开发,基本可用,会有 bug。。 > 项目正在开发,基本可用,会有 bug。。
> **请定期按照 github 的提示,只同步到自己的项目。只需要在乎下图红框的提示,其他提示不要点击**。 > **请定期按照 github 的提示,只同步到自己的项目。只需要在乎下图红框的提示,其他提示不要点击**。

View File

@@ -1,5 +1,6 @@
{ {
"port": "", "port": "",
"address": "", "address": "",
"uuid": "" "uuid": "",
"logLevel": "DEBUG"
} }

View File

@@ -1,11 +1,12 @@
import { Command } from 'commander'; import { Command } from 'commander';
import { writeFileSync, existsSync, readFileSync } from 'fs'; import { writeFileSync, existsSync, readFileSync } from 'fs';
import { exit } from 'node:process'; import { exit } from 'node:process';
import { env } from 'process';
let config: { let config: {
port: string; port: string;
address: string; address: string;
uuid: string; uuid: string;
config: string; logLevel: string;
} = null; } = null;
const program = new Command(); const program = new Command();
program.option('-v').action((options) => { program.option('-v').action((options) => {
@@ -54,5 +55,6 @@ program
} }
}); });
program.parse(); program.parse();
env.NODE_ENV = env.NODE_ENV || 'production';
export { config }; export { config };

View File

@@ -1,6 +1,7 @@
import { IncomingMessage } from 'http'; import { IncomingMessage } from 'http';
import * as os from 'os'; import * as os from 'os';
import * as url from 'node:url'; import * as url from 'node:url';
import { config } from './cmd';
async function* concatStreams(readables: any[]) { async function* concatStreams(readables: any[]) {
for (const readable of readables) { for (const readable of readables) {
@@ -57,9 +58,37 @@ function rawHTTPPackageWithDelay(req: IncomingMessage) {
return concatStreams([[rawHttpHeader], req, deplay(500)]); 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 { export {
concatStreams, concatStreams,
rawHTTPPackage, rawHTTPPackage,
rawHTTPHeader, rawHTTPHeader,
rawHTTPPackageWithDelay, rawHTTPPackageWithDelay,
errorHandler,
loghelper,
}; };

View File

@@ -3,21 +3,33 @@ import { pipeline, Readable } from 'node:stream';
import { config } from './lib/cmd'; import { config } from './lib/cmd';
import * as url from 'node:url'; import * as url from 'node:url';
import * as undici from 'undici'; import * as undici from 'undici';
import { concatStreams, rawHTTPPackage } from './lib/helper'; import {
concatStreams,
rawHTTPPackage,
errorHandler,
loghelper,
} from './lib/helper';
const isLocal = process.env.LOCAL === 'true'; errorHandler();
const isLocal = process.env.DEBUG === 'true';
const httpProxyServer = createServer(async (req, resp) => { const httpProxyServer = createServer(async (req, resp) => {
const reqUrl = url.parse(req.url); const reqUrl = url.parse(req.url);
const clientSocketLoggerInfo = `[proxy to ${req.url}](http)`; const clientSocketLoggerInfo = `[proxy to ${req.url}](http)`;
try { try {
console.log(`${clientSocketLoggerInfo} Client use HTTP/${req.httpVersion}`); loghelper(`${clientSocketLoggerInfo} Client use HTTP/${req.httpVersion}`);
// for await (const chunk of req.socket) {
// console.log(chunk.toString());
// }
// make call to edge http server // make call to edge http server
// 1. forward all package remote, socket over http body // 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( const { body, headers, statusCode, trailers } = await undici.request(
config.address, config.address,
{ {
@@ -28,30 +40,32 @@ const httpProxyServer = createServer(async (req, resp) => {
'x-http': 'true', 'x-http': 'true',
}, },
method: 'POST', method: 'POST',
body: Readable.from(rawHTTPPackage(req)), body: fromClientReq,
} }
); );
console.log( loghelper(
`${clientSocketLoggerInfo} remote server return ${statusCode} Connected To Proxy` `${clientSocketLoggerInfo} remote server return ${statusCode} Connected To Proxy`
); );
// 2. forward remote reponse body to clientSocket // 2. forward remote reponse body to clientSocket
for await (const chunk of body) { for await (const chunk of body) {
if (isLocal) { if (isLocal) {
console.log(chunk.toString()); loghelper(chunk.toString());
} }
req.socket.write(chunk); req.socket.write(chunk);
// resp.write(chunk);
} }
// resp.end();
body.on('error', (err) => { body.on('error', (err) => {
console.log( loghelper(
`${clientSocketLoggerInfo} remote server response body has error`, `${clientSocketLoggerInfo} remote server response body has error`,
err err
); );
req.socket.destroy(); req.destroy();
}); });
} catch (error) { } catch (error) {
req.destroy();
req.socket?.end(); req.socket?.end();
req.socket?.destroy(); loghelper(`${clientSocketLoggerInfo} has error `, error);
console.log(`${clientSocketLoggerInfo} has error `, error);
} }
}); });
@@ -59,8 +73,9 @@ const httpProxyServer = createServer(async (req, resp) => {
httpProxyServer.on('connect', async (req, clientSocket, head) => { httpProxyServer.on('connect', async (req, clientSocket, head) => {
const reqUrl = url.parse('https://' + req.url); const reqUrl = url.parse('https://' + req.url);
const clientSocketLoggerInfo = `[proxy to ${req.url}]`; const clientSocketLoggerInfo = `[proxy to ${req.url}]`;
let fromClientSocket = null;
try { try {
console.log( loghelper(
`${clientSocketLoggerInfo} Client use HTTP/${ `${clientSocketLoggerInfo} Client use HTTP/${
req.httpVersion req.httpVersion
} Connected To Proxy, head on connect is ${head.toString() || 'empty'}` } Connected To Proxy, head on connect is ${head.toString() || 'empty'}`
@@ -70,18 +85,10 @@ httpProxyServer.on('connect', async (req, clientSocket, head) => {
`HTTP/${req.httpVersion} 200 Connection Established\r\n\r\n` `HTTP/${req.httpVersion} 200 Connection Established\r\n\r\n`
); );
// console.log(config); // loghelper(config);
// make call to edge http server // make call to edge http server
// 1. forward all package remote, socket over http body // 1. forward all package remote, socket over http body
const fromClientSocket = Readable.from( fromClientSocket = Readable.from(concatStreams([head, clientSocket]));
concatStreams([head, clientSocket])
).on('error', (error) => {
console.log(
`${clientSocketLoggerInfo} client socket to remote http body has error`,
error
);
clientSocket.destroy();
});
const { body, headers, statusCode, trailers } = await undici.request( const { body, headers, statusCode, trailers } = await undici.request(
config.address, config.address,
{ {
@@ -95,46 +102,57 @@ httpProxyServer.on('connect', async (req, clientSocket, head) => {
body: fromClientSocket, body: fromClientSocket,
} }
); );
console.log(`${clientSocketLoggerInfo} remote server return ${statusCode}`); 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 // 2. forward remote reponse body to clientSocket
for await (const chunk of body) { for await (const chunk of body) {
clientSocket.write(chunk); clientSocket.write(chunk);
} }
body.on('error', (err) => { body.on('error', (err) => {
console.log( loghelper(
`${clientSocketLoggerInfo} remote response body has error`, `${clientSocketLoggerInfo} remote response body has error`,
err err
); );
fromClientSocket.push(null);
clientSocket.destroy(); clientSocket.destroy();
}); });
clientSocket.on('error', (e) => { clientSocket.on('error', (e) => {
body?.destroy(); body?.destroy();
clientSocket.destroy(); fromClientSocket.push(null);
console.log(`${clientSocketLoggerInfo} clientSocket has error: ` + e); clientSocket.end();
loghelper(`${clientSocketLoggerInfo} clientSocket has error: ` + e);
}); });
clientSocket.on('end', () => { clientSocket.on('end', () => {
console.log(`${clientSocketLoggerInfo} has done and end.`); loghelper(`${clientSocketLoggerInfo} has done and end.`);
}); });
} catch (error) { } catch (error) {
clientSocket.destroy(); fromClientSocket?.push(null);
console.log(`${clientSocketLoggerInfo} has error `, error); clientSocket.end();
loghelper(`${clientSocketLoggerInfo} has error `, error);
} }
}); });
httpProxyServer.on('error', (err) => { httpProxyServer.on('error', (err) => {
console.log('SERVER ERROR'); loghelper('SERVER ERROR', err);
console.log(err);
throw err;
}); });
httpProxyServer.on('clientError', (err, clientSocket) => { httpProxyServer.on('clientError', (err, clientSocket) => {
console.log('client error: ' + err); loghelper('client error: ' + err);
clientSocket.end('HTTP/1.1 400 Bad Request\r\n\r\n'); clientSocket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
}); });
httpProxyServer.on('close', () => { httpProxyServer.on('close', () => {
console.log('Server close'); loghelper('Server close');
}); });
httpProxyServer.listen(Number(config.port), () => { httpProxyServer.listen(Number(config.port), () => {
console.log('Server runnig at http://localhost:' + config.port); loghelper('Server runnig at http://localhost:' + config.port);
}); });