@bbk47/toolbox 1.0.7 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/lib/deferred.d.ts +7 -0
  2. package/lib/deferred.d.ts.map +1 -0
  3. package/lib/deferred.js +8 -4
  4. package/lib/deferred.js.map +1 -0
  5. package/lib/encrypt.d.ts +19 -0
  6. package/lib/encrypt.d.ts.map +1 -0
  7. package/lib/encrypt.js +78 -79
  8. package/lib/encrypt.js.map +1 -0
  9. package/lib/index.d.ts +19 -0
  10. package/lib/index.d.ts.map +1 -0
  11. package/lib/index.js +58 -8
  12. package/lib/index.js.map +1 -0
  13. package/lib/logger.d.ts +11 -0
  14. package/lib/logger.d.ts.map +1 -0
  15. package/lib/logger.js +23 -14
  16. package/lib/logger.js.map +1 -0
  17. package/lib/md5.d.ts +2 -0
  18. package/lib/md5.d.ts.map +1 -0
  19. package/lib/md5.js +9 -6
  20. package/lib/md5.js.map +1 -0
  21. package/lib/merge.d.ts +2 -0
  22. package/lib/merge.d.ts.map +1 -0
  23. package/lib/merge.js +7 -8
  24. package/lib/merge.js.map +1 -0
  25. package/lib/proxy.d.ts +6 -0
  26. package/lib/proxy.d.ts.map +1 -0
  27. package/lib/proxy.js +65 -0
  28. package/lib/proxy.js.map +1 -0
  29. package/lib/retry.d.ts +6 -0
  30. package/lib/retry.d.ts.map +1 -0
  31. package/lib/retry.js +11 -13
  32. package/lib/retry.js.map +1 -0
  33. package/lib/server.d.ts +14 -0
  34. package/lib/server.d.ts.map +1 -0
  35. package/lib/server.js +71 -0
  36. package/lib/server.js.map +1 -0
  37. package/lib/socks5.d.ts +8 -0
  38. package/lib/socks5.d.ts.map +1 -0
  39. package/lib/socks5.js +24 -27
  40. package/lib/socks5.js.map +1 -0
  41. package/lib/timeout.d.ts +2 -0
  42. package/lib/timeout.d.ts.map +1 -0
  43. package/lib/timeout.js +7 -0
  44. package/lib/timeout.js.map +1 -0
  45. package/lib/transport/creater.d.ts +14 -0
  46. package/lib/transport/creater.d.ts.map +1 -0
  47. package/lib/transport/creater.js +72 -0
  48. package/lib/transport/creater.js.map +1 -0
  49. package/lib/transport/helper.d.ts +14 -0
  50. package/lib/transport/helper.d.ts.map +1 -0
  51. package/lib/transport/helper.js +63 -0
  52. package/lib/transport/helper.js.map +1 -0
  53. package/lib/transport/index.d.ts +6 -0
  54. package/lib/transport/index.d.ts.map +1 -0
  55. package/lib/transport/index.js +43 -0
  56. package/lib/transport/index.js.map +1 -0
  57. package/lib/transport/transport.d.ts +23 -0
  58. package/lib/transport/transport.d.ts.map +1 -0
  59. package/lib/transport/transport.js +41 -0
  60. package/lib/transport/transport.js.map +1 -0
  61. package/lib/uuid.d.ts +2 -0
  62. package/lib/uuid.d.ts.map +1 -0
  63. package/lib/uuid.js +14 -20
  64. package/lib/uuid.js.map +1 -0
  65. package/package.json +18 -4
  66. package/src/deferred.ts +14 -0
  67. package/src/encrypt.ts +119 -0
  68. package/src/index.ts +29 -0
  69. package/src/logger.ts +59 -0
  70. package/src/md5.ts +5 -0
  71. package/src/merge.ts +16 -0
  72. package/src/proxy.ts +65 -0
  73. package/src/retry.ts +32 -0
  74. package/src/server.ts +77 -0
  75. package/src/socks5.ts +46 -0
  76. package/src/timeout.ts +3 -0
  77. package/src/transport/creater.ts +88 -0
  78. package/src/transport/helper.ts +73 -0
  79. package/src/transport/index.ts +6 -0
  80. package/src/transport/transport.ts +54 -0
  81. package/src/uuid.ts +37 -0
  82. package/.github/workflows/test.yml +0 -30
  83. package/.prettierrc +0 -11
  84. package/test/deferred.js +0 -24
  85. package/test/encrypt.js +0 -25
  86. package/test/md5.js +0 -9
  87. package/test/merge.js +0 -11
  88. package/test/retry.js +0 -61
  89. package/test/socks5.js +0 -42
  90. package/test/uuid.js +0 -11
package/src/encrypt.ts ADDED
@@ -0,0 +1,119 @@
1
+ import crypto from 'crypto';
2
+
3
+ type CipherKey = string;
4
+ interface KeyIVCache {
5
+ [key: string]: [Buffer, Buffer];
6
+ }
7
+ interface KeyIV {
8
+ key: Buffer;
9
+ iv: Buffer;
10
+ }
11
+
12
+ const bytes_to_key_results: KeyIVCache = {};
13
+
14
+ const EVP_BytesToKey = function (
15
+ password: Buffer,
16
+ key_len: number,
17
+ iv_len: number
18
+ ): [Buffer, Buffer] {
19
+ const cacheKey = `${password}:${key_len}:${iv_len}`;
20
+ if (bytes_to_key_results[cacheKey]) {
21
+ return bytes_to_key_results[cacheKey];
22
+ }
23
+ const m: Buffer[] = [];
24
+ let i = 0;
25
+ let count = 0;
26
+ while (count < key_len + iv_len) {
27
+ const md5hash = crypto.createHash('md5');
28
+ let data: Buffer = password;
29
+ if (i > 0) {
30
+ data = Buffer.concat([m[i - 1], password]);
31
+ }
32
+ md5hash.update(data);
33
+ const d = md5hash.digest();
34
+ m.push(d);
35
+ count += d.length;
36
+ i += 1;
37
+ }
38
+ const ms = Buffer.concat(m);
39
+ const key = ms.slice(0, key_len);
40
+ const iv = ms.slice(key_len, key_len + iv_len);
41
+ bytes_to_key_results[cacheKey] = [key, iv];
42
+ return [key, iv];
43
+ };
44
+
45
+ export type CipherMethod =
46
+ | 'aes-128-cfb'
47
+ | 'aes-192-cfb'
48
+ | 'aes-256-cfb'
49
+ | 'bf-cfb'
50
+ | 'camellia-128-cfb'
51
+ | 'camellia-192-cfb'
52
+ | 'camellia-256-cfb'
53
+ | 'cast5-cfb'
54
+ | 'des-cfb'
55
+ | 'idea-cfb'
56
+ | 'rc2-cfb'
57
+ | 'rc4'
58
+ | 'seed-cfb';
59
+
60
+ const method_supported: Record<CipherMethod, [number, number]> = {
61
+ 'aes-128-cfb': [16, 16],
62
+ 'aes-192-cfb': [24, 16],
63
+ 'aes-256-cfb': [32, 16],
64
+ 'bf-cfb': [16, 8],
65
+ 'camellia-128-cfb': [16, 16],
66
+ 'camellia-192-cfb': [24, 16],
67
+ 'camellia-256-cfb': [32, 16],
68
+ 'cast5-cfb': [16, 8],
69
+ 'des-cfb': [8, 8],
70
+ 'idea-cfb': [16, 8],
71
+ 'rc2-cfb': [16, 8],
72
+ rc4: [16, 0],
73
+ 'seed-cfb': [16, 16],
74
+ };
75
+
76
+ export class Encryptor {
77
+ key: CipherKey;
78
+ method: CipherMethod;
79
+ _EVP_KEY: Buffer;
80
+ _IV: Buffer;
81
+
82
+ constructor(key: string = 'MVP123', method: CipherMethod = 'aes-256-cfb') {
83
+ this.key = key;
84
+ this.method = method;
85
+ if (!method_supported[this.method]) {
86
+ throw new Error('Not support method!');
87
+ }
88
+ const keyIV = this.getKeyIV(key, method);
89
+ this._EVP_KEY = keyIV.key;
90
+ this._IV = keyIV.iv;
91
+ }
92
+
93
+ get_cipher_len(method: string): [number, number] {
94
+ const m = method.toLowerCase() as CipherMethod;
95
+ return method_supported[m];
96
+ }
97
+
98
+ getKeyIV(password: string, method: string): KeyIV {
99
+ const m = method.toLowerCase() as CipherMethod;
100
+ const passwordBuf = Buffer.from(password, 'ascii');
101
+ const [keyLen, ivLen] = this.get_cipher_len(m);
102
+ const ref = EVP_BytesToKey(passwordBuf, keyLen, ivLen);
103
+ return { key: ref[0], iv: ref[1] };
104
+ }
105
+
106
+ encrypt(buf: Buffer): Buffer {
107
+ const cipher = crypto.createCipheriv(this.method, this._EVP_KEY, this._IV);
108
+ const encrypted = cipher.update(buf);
109
+ const result = cipher.final();
110
+ return Buffer.concat([encrypted, result]);
111
+ }
112
+
113
+ decrypt(buf: Buffer): Buffer {
114
+ const decipher = crypto.createDecipheriv(this.method, this._EVP_KEY, this._IV);
115
+ const decrypted = decipher.update(buf);
116
+ const result = decipher.final();
117
+ return Buffer.concat([decrypted, result]);
118
+ }
119
+ }
package/src/index.ts ADDED
@@ -0,0 +1,29 @@
1
+ export { default as deferred } from './deferred';
2
+ export type { Deferred } from './deferred';
3
+
4
+ export * as encrypt from './encrypt';
5
+ export type { CipherMethod } from './encrypt';
6
+
7
+ export { default as logger } from './logger';
8
+ export type { LogLevel, Logger } from './logger';
9
+
10
+ export { default as md5 } from './md5';
11
+
12
+ export { default as merge } from './merge';
13
+
14
+ export * as proxy from './proxy';
15
+ export type { OnConnect, ConnectCallback } from './proxy';
16
+
17
+ export { default as retry } from './retry';
18
+ export type { RetryOptions } from './retry';
19
+
20
+ export * as server from './server';
21
+
22
+ export * as socks5 from './socks5';
23
+ export type { Socks5AddrInfo } from './socks5';
24
+
25
+ export * as timeout from './timeout';
26
+
27
+ export * as transport from './transport';
28
+
29
+ export { default as uuid } from './uuid';
package/src/logger.ts ADDED
@@ -0,0 +1,59 @@
1
+ export type LogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'log' | 'debug';
2
+
3
+ export interface Logger {
4
+ fatal: (msg: string) => void;
5
+ error: (msg: string) => void;
6
+ warn: (msg: string) => void;
7
+ info: (msg: string) => void;
8
+ log: (msg: string) => void;
9
+ debug: (msg: string) => void;
10
+ }
11
+
12
+ interface LevelConfig {
13
+ piv: number;
14
+ colour: string;
15
+ }
16
+
17
+ const levelVal: Record<LogLevel, LevelConfig> = {
18
+ fatal: { piv: 1, colour: '\x1b[31m%s\x1b[0m' },
19
+ error: { piv: 2, colour: '\x1b[31m%s\x1b[0m' },
20
+ warn: { piv: 3, colour: '\x1b[33m%s\x1b[0m' },
21
+ info: { piv: 4, colour: '\x1b[32m%s\x1b[0m' },
22
+ log: { piv: 5, colour: '\x1b[37m%s\x1b[0m' },
23
+ debug: { piv: 6, colour: '\x1b[35m%s\x1b[0m' },
24
+ };
25
+
26
+ function formatDate(date: Date): string {
27
+ const pad = (n: number) => String(n).padStart(2, '0');
28
+ return `${date.getFullYear()}/${pad(date.getMonth() + 1)}/${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
29
+ }
30
+
31
+ export default function getCustomLogger(
32
+ label: string,
33
+ level: LogLevel,
34
+ _logFile?: string
35
+ ): Logger {
36
+ const noop = () => undefined;
37
+ const setPiv = levelVal[level].piv;
38
+
39
+ function makeLogFn(key: LogLevel): (msg: string) => void {
40
+ if (levelVal[key].piv <= setPiv) {
41
+ const colour = levelVal[key].colour;
42
+ return (msg: string) => {
43
+ const date = formatDate(new Date());
44
+ const prefix = `${date} [${key[0].toUpperCase()}] ${label} `;
45
+ console.log(colour, prefix, msg);
46
+ };
47
+ }
48
+ return noop;
49
+ }
50
+
51
+ return {
52
+ fatal: makeLogFn('fatal'),
53
+ error: makeLogFn('error'),
54
+ warn: makeLogFn('warn'),
55
+ info: makeLogFn('info'),
56
+ log: makeLogFn('log'),
57
+ debug: makeLogFn('debug'),
58
+ };
59
+ }
package/src/md5.ts ADDED
@@ -0,0 +1,5 @@
1
+ import crypto from 'crypto';
2
+
3
+ export default function md5(s: string | number): string {
4
+ return crypto.createHash('md5').update(String(s)).digest('hex');
5
+ }
package/src/merge.ts ADDED
@@ -0,0 +1,16 @@
1
+ export default function merge<T extends Record<string, any>>(
2
+ target: T,
3
+ ...sources: (Record<string, any> | null | undefined)[]
4
+ ): T & Record<string, any> {
5
+ target = target || ({} as T);
6
+ sources.forEach((temp) => {
7
+ if (temp) {
8
+ Object.keys(temp).forEach((key) => {
9
+ if (temp[key] !== null && temp[key] !== undefined) {
10
+ (target as Record<string, any>)[key] = temp[key];
11
+ }
12
+ });
13
+ }
14
+ });
15
+ return target;
16
+ }
package/src/proxy.ts ADDED
@@ -0,0 +1,65 @@
1
+ import net from 'net';
2
+ import { buildSocks5Addr } from './socks5';
3
+
4
+ export type ConnectCallback = (err?: Error | null) => void;
5
+ export type OnConnect = (addr: Buffer, callback: ConnectCallback) => void;
6
+
7
+ export function createSocks5Proxy(cSock: net.Socket, onConnect: OnConnect): void {
8
+ let stage = 'INIT';
9
+ cSock.on('data', function dataListener(data: Buffer) {
10
+ if (stage === 'INIT') {
11
+ cSock.write('\x05\x00');
12
+ stage = 'ADDR';
13
+ return;
14
+ } else if (stage === 'ADDR') {
15
+ onConnect(data.slice(3), (err) => {
16
+ if (!err) {
17
+ stage = 'STREAM';
18
+ cSock.write('\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00');
19
+ cSock.removeAllListeners('data');
20
+ cSock.pause();
21
+ } else {
22
+ cSock.destroy();
23
+ }
24
+ });
25
+ return;
26
+ }
27
+ });
28
+
29
+ cSock.on('error', () => {
30
+ cSock.destroy();
31
+ });
32
+ }
33
+
34
+ export function createConnectProxy(cSock: net.Socket, onConnect: OnConnect): void {
35
+ let stage = 'INIT';
36
+ cSock.on('data', (data: Buffer) => {
37
+ if (stage === 'INIT') {
38
+ const str = data.toString('ascii');
39
+ const lines = str.split(/\s/g);
40
+ if (lines[0] !== 'CONNECT') {
41
+ cSock.destroy();
42
+ return;
43
+ }
44
+ const hoststr = lines[1];
45
+ const reqhost = hoststr.split(':');
46
+ const hostname = reqhost[0];
47
+ const port = Number(reqhost[1]);
48
+ const socksAddrInfo = buildSocks5Addr(hostname, port);
49
+ onConnect(socksAddrInfo, (err) => {
50
+ if (!err) {
51
+ cSock.write(Buffer.from('HTTP/1.1 200 Connection Established\r\n\r\n'));
52
+ stage = 'STREAM';
53
+ cSock.removeAllListeners('data');
54
+ cSock.pause();
55
+ } else {
56
+ cSock.destroy();
57
+ }
58
+ });
59
+ }
60
+ });
61
+
62
+ cSock.on('error', () => {
63
+ cSock.destroy();
64
+ });
65
+ }
package/src/retry.ts ADDED
@@ -0,0 +1,32 @@
1
+ const delay = (time: number): Promise<void> =>
2
+ new Promise((resolve) => setTimeout(resolve, time));
3
+
4
+ export interface RetryOptions {
5
+ times?: number;
6
+ interval?: number;
7
+ }
8
+
9
+ export default function retryPromise<T>(
10
+ fn: () => Promise<T>,
11
+ opts: RetryOptions = {},
12
+ errcall?: (err: Error) => void
13
+ ): Promise<T> {
14
+ const retryCount = opts.times !== undefined ? opts.times : 5;
15
+
16
+ function invoker(idx: number): Promise<T> {
17
+ if (idx >= retryCount) {
18
+ return Promise.reject(
19
+ new Error(`expect retryCount ${retryCount} is attached!`)
20
+ );
21
+ }
22
+ return fn().catch((err: Error) => {
23
+ errcall && errcall(err);
24
+ if (opts.interval) {
25
+ return delay(opts.interval).then(() => invoker(idx + 1));
26
+ } else {
27
+ return invoker(idx + 1);
28
+ }
29
+ });
30
+ }
31
+ return invoker(0);
32
+ }
package/src/server.ts ADDED
@@ -0,0 +1,77 @@
1
+ import net from 'net';
2
+ import http from 'http';
3
+ import tls from 'tls';
4
+ import http2 from 'http2';
5
+ import url from 'url';
6
+ import WebSocket from 'ws';
7
+
8
+ export type HttpHandler = (req: http.IncomingMessage, res: http.ServerResponse) => void;
9
+ export type WsHandler = (ws: WebSocket) => void;
10
+ export type StreamHandler = (stream: net.Socket | http2.ServerHttp2Stream) => void;
11
+ export type ConnHandler = (conn: net.Socket | tls.TLSSocket) => void;
12
+
13
+ export function createWsServer(
14
+ workPath: string,
15
+ handler: WsHandler,
16
+ httpHandler?: HttpHandler
17
+ ): http.Server {
18
+ const server = http.createServer(function (req, res) {
19
+ if (httpHandler) {
20
+ httpHandler(req, res);
21
+ }
22
+ });
23
+ // ws 2.x exposes Server as WebSocket.Server
24
+ const WsServer = (WebSocket as any).Server as typeof WebSocket;
25
+ const wss: any = new (WsServer as any)({ noServer: true, clientTracking: false });
26
+ wss.on('connection', function (wsconn: WebSocket) {
27
+ handler(wsconn);
28
+ });
29
+ server.on('upgrade', function (request, socket, head) {
30
+ const pathname = url.parse(request.url || '').pathname;
31
+ if (pathname === workPath) {
32
+ wss.handleUpgrade(request, socket as net.Socket, head, function done(ws: WebSocket) {
33
+ wss.emit('connection', ws, request);
34
+ });
35
+ } else {
36
+ socket.destroy();
37
+ }
38
+ });
39
+ return server;
40
+ }
41
+
42
+ export function createHttp2Server(
43
+ tlsOpts: tls.TlsOptions,
44
+ workPath: string,
45
+ handler: (stream: http2.ServerHttp2Stream) => void,
46
+ httpHandler?: (req: http2.Http2ServerRequest, res: http2.Http2ServerResponse) => void
47
+ ): http2.Http2SecureServer {
48
+ const http2Opts = Object.assign({ allowHTTP1: true }, tlsOpts);
49
+ const server = http2.createSecureServer(http2Opts, function (req, res) {
50
+ if (httpHandler) {
51
+ httpHandler(req, res);
52
+ }
53
+ });
54
+ server.on('stream', function (stream: http2.ServerHttp2Stream, headers: http2.IncomingHttpHeaders) {
55
+ const path = headers[':path'];
56
+ if (path === workPath) {
57
+ handler(stream);
58
+ } else {
59
+ stream.destroy();
60
+ }
61
+ });
62
+ return server;
63
+ }
64
+
65
+ export function createTcpServer(handler: ConnHandler): net.Server {
66
+ const server = net.createServer(function (conn) {
67
+ handler(conn);
68
+ });
69
+ return server;
70
+ }
71
+
72
+ export function createTlsServer(tlsOpts: tls.TlsOptions, handler: ConnHandler): tls.Server {
73
+ const server = tls.createServer(tlsOpts, function (conn) {
74
+ handler(conn);
75
+ });
76
+ return server;
77
+ }
package/src/socks5.ts ADDED
@@ -0,0 +1,46 @@
1
+ export interface Socks5AddrInfo {
2
+ dstAddr: string;
3
+ dstPort: number;
4
+ }
5
+
6
+ export function parseSocks5Addr(buf: Buffer, offset = 0): Socks5AddrInfo {
7
+ if (offset > 0) {
8
+ buf = buf.slice(offset);
9
+ }
10
+ const type = buf.readUInt8(0);
11
+ const addrData = buf.slice(1);
12
+ let dstAddr = '';
13
+ let dstPort = 0;
14
+
15
+ if (type === 0x01) {
16
+ const IP = addrData.slice(0, -2);
17
+ const PORT = addrData.slice(-2);
18
+ dstAddr = Array.from(IP)
19
+ .map((temp) => Number(temp).toString(10))
20
+ .join('.');
21
+ dstPort = PORT[0] * 256 + PORT[1];
22
+ } else if (type === 0x03) {
23
+ const addrLen = addrData.readUInt8(0);
24
+ const domain = addrData.slice(1, addrLen + 1);
25
+ const port = addrData.slice(addrLen + 1);
26
+ dstAddr = domain.toString();
27
+ dstPort = port[0] * 256 + port[1];
28
+ }
29
+ return { dstAddr, dstPort };
30
+ }
31
+
32
+ export function buildSocks5Addr(hostname: string, port: number): Buffer {
33
+ if (/^(\d+\.){3}\d+$/.test(hostname)) {
34
+ const parts = hostname.split('.').map((p) => Number(p));
35
+ const portBuf = Buffer.from([Math.floor(port / 256), port % 256]);
36
+ const preBuf = Buffer.from([0x01, parts[0], parts[1], parts[2], parts[3]]);
37
+ return Buffer.concat([preBuf, portBuf]);
38
+ } else {
39
+ const preBuf = Buffer.from([0x03, hostname.length]);
40
+ const domainBuf = Buffer.from(hostname);
41
+ const portBuf = Buffer.from([Math.floor(port / 256), port % 256]);
42
+ return Buffer.concat([preBuf, domainBuf, portBuf]);
43
+ }
44
+ }
45
+
46
+ export const parseAddrInfo = parseSocks5Addr;
package/src/timeout.ts ADDED
@@ -0,0 +1,3 @@
1
+ export function timeoutPromise(time: number): Promise<string> {
2
+ return new Promise((resolve) => setTimeout(() => resolve('timeout:' + time), time));
3
+ }
@@ -0,0 +1,88 @@
1
+ import WebSocket from 'ws';
2
+ import net from 'net';
3
+ import http2 from 'http2';
4
+ import tls from 'tls';
5
+ import { Transport, TransportConn } from './transport';
6
+
7
+ export interface TransportParams {
8
+ host: string;
9
+ port: number;
10
+ path?: string;
11
+ secure?: boolean;
12
+ }
13
+
14
+ export function createWebsocketTransport(
15
+ params: TransportParams,
16
+ onOpen: () => void
17
+ ): Transport {
18
+ const tunnelWsUrl = `${params.secure ? 'wss' : 'ws'}://${params.host}:${params.port}${params.path || ''}`;
19
+ const ws = new WebSocket(tunnelWsUrl, {
20
+ perMessageDeflate: false,
21
+ handshakeTimeout: 3000,
22
+ } as any);
23
+ const ts = new Transport({ type: 'ws', conn: ws as unknown as TransportConn });
24
+ ws.on('open', onOpen);
25
+ return ts;
26
+ }
27
+
28
+ export function createHttp2Transport(
29
+ params: TransportParams,
30
+ onOpen: () => void
31
+ ): Transport {
32
+ const http2Url = `https://${params.host}:${params.port}`;
33
+ const client = http2.connect(http2Url, {
34
+ rejectUnauthorized: false,
35
+ requestCert: true,
36
+ });
37
+ const http2stream = client.request({
38
+ ':method': 'POST',
39
+ ':path': params.path || '/',
40
+ 'Content-Type': 'octet-stream',
41
+ });
42
+ http2stream.on('response', onOpen);
43
+ const ts = new Transport({ type: 'h2', conn: http2stream as unknown as TransportConn });
44
+ return ts;
45
+ }
46
+
47
+ export function createTlsTransport(
48
+ params: TransportParams,
49
+ onOpen: () => void
50
+ ): Transport {
51
+ const tlsOpts: tls.ConnectionOptions = {
52
+ rejectUnauthorized: false,
53
+ host: params.host,
54
+ port: params.port,
55
+ };
56
+ const tlsConn = tls.connect(tlsOpts, function () {
57
+ onOpen();
58
+ });
59
+ const ts = new Transport({ type: 'tls', conn: tlsConn as unknown as TransportConn });
60
+ return ts;
61
+ }
62
+
63
+ export function createTcpTransport(
64
+ params: TransportParams,
65
+ onOpen: () => void
66
+ ): Transport {
67
+ const socket = new net.Socket();
68
+ socket.connect(params.port, params.host, function () {
69
+ onOpen();
70
+ });
71
+ return new Transport({ type: 'tcp', conn: socket as unknown as TransportConn });
72
+ }
73
+
74
+ export function createUnixsocketTransport(
75
+ params: TransportParams,
76
+ onOpen: () => void
77
+ ): Transport {
78
+ const socket = new net.Socket();
79
+ socket.connect(params.path || '', function () {
80
+ onOpen();
81
+ });
82
+ const ts = new Transport({ type: 'domainsocket', conn: socket as unknown as TransportConn });
83
+ return ts;
84
+ }
85
+
86
+ export function wrapSocket(type: string, conn: TransportConn): Transport {
87
+ return new Transport({ type: type as any, conn });
88
+ }
@@ -0,0 +1,73 @@
1
+ import WebSocket from 'ws';
2
+ import net from 'net';
3
+ import stream from 'stream';
4
+
5
+ export type DataCallback = (data: Buffer) => void;
6
+ export type ErrorCallback = (err: Error) => void;
7
+ export type CloseCallback = (code?: number) => void;
8
+
9
+ export function bindStreamSocket(
10
+ s: stream.Readable & { on: Function; destroy: Function },
11
+ onData: DataCallback,
12
+ onError: ErrorCallback,
13
+ onClose: CloseCallback
14
+ ): void {
15
+ let buffcache = Buffer.from([]);
16
+ s.on('data', function (data: Buffer) {
17
+ buffcache = Buffer.concat([buffcache, data]);
18
+ while (true) {
19
+ if (buffcache.length <= 2) {
20
+ return;
21
+ }
22
+ const datalen = buffcache[0] * 256 + buffcache[1];
23
+ if (buffcache.length < datalen + 2) {
24
+ return;
25
+ }
26
+ const pack = buffcache.slice(2, datalen + 2);
27
+ buffcache = buffcache.slice(datalen + 2);
28
+ onData(pack);
29
+ }
30
+ });
31
+ s.on('close', function (code?: number) {
32
+ onClose(code);
33
+ });
34
+ s.on('error', function (err: Error) {
35
+ (s as any).destroy();
36
+ onError(err);
37
+ });
38
+ }
39
+
40
+ export function bindWebsocket(
41
+ ws: WebSocket,
42
+ onData: DataCallback,
43
+ onError: ErrorCallback,
44
+ onClose: CloseCallback
45
+ ): void {
46
+ ws.on('message', onData);
47
+ ws.on('close', (code: number) => {
48
+ onClose(code);
49
+ });
50
+ ws.on('error', (err: Error) => {
51
+ ws.close();
52
+ onError(err);
53
+ });
54
+ }
55
+
56
+ export function tcpsocketSend(socket: net.Socket, data: Buffer): void {
57
+ const datalen = data.length;
58
+ if (socket.writable) {
59
+ socket.write(
60
+ Buffer.concat([Buffer.from([datalen >> 8, datalen % 256]), data])
61
+ );
62
+ } else {
63
+ throw new Error('socket cannot writeable!');
64
+ }
65
+ }
66
+
67
+ export function websocketSend(ws: WebSocket, data: Buffer): void {
68
+ if (ws.readyState === WebSocket.OPEN) {
69
+ ws.send(data, { binary: true });
70
+ } else {
71
+ throw new Error('ws socket not open!' + ws.readyState);
72
+ }
73
+ }
@@ -0,0 +1,6 @@
1
+ export { Transport } from './transport';
2
+ export type { TransportType, TransportConn } from './transport';
3
+ export * as creater from './creater';
4
+ export * as helper from './helper';
5
+
6
+ export { wrapSocket } from './creater';
@@ -0,0 +1,54 @@
1
+ import WebSocket from 'ws';
2
+ import net from 'net';
3
+ import { websocketSend, tcpsocketSend, bindWebsocket, bindStreamSocket } from './helper';
4
+ import type { DataCallback, ErrorCallback, CloseCallback } from './helper';
5
+
6
+ export type TransportType = 'ws' | 'h2' | 'tcp' | 'tls' | 'domainsocket';
7
+
8
+ export interface TransportConn {
9
+ close?: () => void;
10
+ destroy?: () => void;
11
+ writable?: boolean;
12
+ readyState?: number;
13
+ send?: Function;
14
+ on: (event: string, listener: Function) => any;
15
+ write?: (data: Buffer) => void;
16
+ }
17
+
18
+ export class Transport {
19
+ type: TransportType;
20
+ conn: TransportConn;
21
+
22
+ constructor(opts: { type: TransportType; conn: TransportConn }) {
23
+ this.type = opts.type;
24
+ this.conn = opts.conn;
25
+ }
26
+
27
+ sendPacket(binarydata: Buffer): void {
28
+ if (this.type === 'ws') {
29
+ websocketSend(this.conn as unknown as WebSocket, binarydata);
30
+ } else {
31
+ tcpsocketSend(this.conn as unknown as net.Socket, binarydata);
32
+ }
33
+ }
34
+
35
+ bindEvents(onData: DataCallback, onError: ErrorCallback, onClose: CloseCallback): void {
36
+ if (this.type === 'ws') {
37
+ bindWebsocket(this.conn as unknown as WebSocket, onData, onError, onClose);
38
+ } else {
39
+ bindStreamSocket(this.conn as any, onData, onError, onClose);
40
+ }
41
+ }
42
+
43
+ close(): void {
44
+ try {
45
+ if (this.type === 'ws' || this.type === 'h2') {
46
+ (this.conn as any).close();
47
+ } else {
48
+ (this.conn as any).destroy();
49
+ }
50
+ } catch (_err) {
51
+ // ignore
52
+ }
53
+ }
54
+ }