js-tcp-tunnel 1.0.2 → 1.1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-tcp-tunnel",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "js tcp tunnel",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -24,10 +24,10 @@
24
24
  },
25
25
  "homepage": "https://github.com/yuanliwei/js-tunnel#readme",
26
26
  "devDependencies": {
27
- "@types/node": "^22.9.4",
28
- "koa": "^2.15.3",
29
- "koa-router": "^13.0.1",
27
+ "@koa/router": "^14.0.0",
28
+ "@types/node": "^24.2.1",
29
+ "koa": "^3.0.1",
30
30
  "log4js": "^6.9.1",
31
- "ws": "^8.18.0"
31
+ "ws": "^8.18.3"
32
32
  }
33
33
  }
package/src/lib.js CHANGED
@@ -3,10 +3,12 @@ import net from 'node:net'
3
3
  import { Readable, Writable } from 'node:stream'
4
4
  import http from 'node:http'
5
5
  import https from 'node:https'
6
+ import { ReadableStream, TransformStream, WritableStream } from 'node:stream/web'
6
7
 
7
8
  /**
8
9
  * @import {WebSocketServer} from 'ws'
9
- * @import Router from 'koa-router'
10
+ * @import Router from '@koa/router'
11
+ * @import {SocketChannel, TCP_TUNNEL_DATA, TUNNEL_TCP_CLIENT_HELPER, TUNNEL_TCP_DATA_CONNECT, TUNNEL_TCP_DATA_LISTEN, TUNNEL_TCP_DATA_PINGPONG, TUNNEL_TCP_SERVER_HELPER, TunnelTcpClientHelperParam, TunnelTcpServerHelperParam} from './types.js'
10
12
  */
11
13
 
12
14
  const DEBUG_TUNNEL_TCP = false
@@ -53,14 +55,6 @@ export function formatNumber(num) {
53
55
  return Number(num.toFixed(2))
54
56
  }
55
57
 
56
- /**
57
- * @typedef {{
58
- * promise: Promise<any>;
59
- * resolve: (value: any | null) => void;
60
- * reject: (reason: any | null) => void;
61
- * }} PromiseResolvers
62
- */
63
-
64
58
  export function Promise_withResolvers() {
65
59
  /** @type{(value?:object)=>void} */
66
60
  let resolve = null
@@ -74,8 +68,8 @@ export function Promise_withResolvers() {
74
68
  }
75
69
 
76
70
  /**
77
- * @param {Promise<[CryptoKey,Uint8Array]>} key_iv
78
- * @returns {TransformStream<Uint8Array, Uint8Array>}
71
+ * @param {Promise<[CryptoKey,Uint8Array<ArrayBuffer>]>} key_iv
72
+ * @returns {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>}
79
73
  */
80
74
  export function createEncodeStream(key_iv) {
81
75
  let key = null
@@ -92,8 +86,8 @@ export function createEncodeStream(key_iv) {
92
86
  }
93
87
 
94
88
  /**
95
- * @param {Promise<[CryptoKey,Uint8Array]>} key_iv
96
- * @returns {TransformStream<Uint8Array, Uint8Array>}
89
+ * @param {Promise<[CryptoKey,Uint8Array<ArrayBuffer>]>} key_iv
90
+ * @returns {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>}
97
91
  */
98
92
  export function createDecodeStream(key_iv) {
99
93
  let key = null
@@ -116,10 +110,10 @@ export function createDecodeStream(key_iv) {
116
110
  const HEADER_CHECK = 0xb1f7705f
117
111
 
118
112
  /**
119
- * @param {Uint8Array[]} queue
113
+ * @param {Uint8Array<ArrayBuffer>[]} queue
120
114
  * @param {CryptoKey} key
121
- * @param {Uint8Array} iv
122
- * @returns {Promise<Uint8Array>}
115
+ * @param {Uint8Array<ArrayBuffer>} iv
116
+ * @returns {Promise<Uint8Array<ArrayBuffer>>}
123
117
  */
124
118
  export async function buildBufferData(queue, key, iv) {
125
119
  let buffers = []
@@ -138,11 +132,11 @@ export async function buildBufferData(queue, key, iv) {
138
132
  /**
139
133
  * @param {Uint8Array<ArrayBuffer>} buffer
140
134
  * @param {CryptoKey} key
141
- * @param {Uint8Array} iv
142
- * @returns {Promise<[Uint8Array[],Uint8Array<ArrayBuffer>]>}
135
+ * @param {Uint8Array<ArrayBuffer>} iv
136
+ * @returns {Promise<[Uint8Array<ArrayBuffer>[],Uint8Array<ArrayBuffer>]>}
143
137
  */
144
138
  export async function parseBufferData(buffer, key, iv) {
145
- /** @type{Uint8Array[]} */
139
+ /** @type{Uint8Array<ArrayBuffer>[]} */
146
140
  let queue = []
147
141
  let offset = 0
148
142
  let remain = new Uint8Array(0)
@@ -303,17 +297,17 @@ let tcpTunnelDataSend = 0
303
297
  */
304
298
  export function printTcpTunnelData(data) {
305
299
  return `id: ${`${data.srcId}:${data.dstId}`.padEnd(10)} channel: ${`${data.srcChannel}:${data.dstChannel}`.padEnd(10)} ${{
306
- 0xa9b398d5: 'TUNNEL_TCP_TYPE_INIT ',
307
- 0xe41957d3: 'TUNNEL_TCP_TYPE_LISTEN ',
308
- 0x20993e38: 'TUNNEL_TCP_TYPE_ONLISTEN ',
309
- 0x11d949f8: 'TUNNEL_TCP_TYPE_CONNECT ',
310
- 0x377b2181: 'TUNNEL_TCP_TYPE_ONCONNECT ',
311
- 0x48678f39: 'TUNNEL_TCP_TYPE_DATA ',
312
- 0x8117f762: 'TUNNEL_TCP_TYPE_ERROR ',
313
- 0x72fd6470: 'TUNNEL_TCP_TYPE_CLOSE ',
314
- 0x4768e1ba: 'TUNNEL_TCP_TYPE_PING ',
315
- 0x106f43fb: 'TUNNEL_TCP_TYPE_PONG ',
316
- 0xc5870539: 'TUNNEL_TCP_TYPE_ACK ',
300
+ [TUNNEL_TCP_TYPE_INIT]: 'TUNNEL_TCP_TYPE_INIT ',
301
+ [TUNNEL_TCP_TYPE_LISTEN]: 'TUNNEL_TCP_TYPE_LISTEN ',
302
+ [TUNNEL_TCP_TYPE_ONLISTEN]: 'TUNNEL_TCP_TYPE_ONLISTEN ',
303
+ [TUNNEL_TCP_TYPE_CONNECT]: 'TUNNEL_TCP_TYPE_CONNECT ',
304
+ [TUNNEL_TCP_TYPE_ONCONNECT]: 'TUNNEL_TCP_TYPE_ONCONNECT ',
305
+ [TUNNEL_TCP_TYPE_DATA]: 'TUNNEL_TCP_TYPE_DATA ',
306
+ [TUNNEL_TCP_TYPE_ERROR]: 'TUNNEL_TCP_TYPE_ERROR ',
307
+ [TUNNEL_TCP_TYPE_CLOSE]: 'TUNNEL_TCP_TYPE_CLOSE ',
308
+ [TUNNEL_TCP_TYPE_PING]: 'TUNNEL_TCP_TYPE_PING ',
309
+ [TUNNEL_TCP_TYPE_PONG]: 'TUNNEL_TCP_TYPE_PONG ',
310
+ [TUNNEL_TCP_TYPE_ACK]: 'TUNNEL_TCP_TYPE_ACK ',
317
311
  }[data.type]} recv: ${(tcpTunnelDataRecv)} send: ${(tcpTunnelDataSend)} size:${data.buffer.length}`
318
312
  }
319
313
 
@@ -321,7 +315,7 @@ export function printTcpTunnelData(data) {
321
315
  *
322
316
  * @param {string} password
323
317
  * @param {number} iterations
324
- * @returns {Promise<[CryptoKey,Uint8Array]>}
318
+ * @returns {Promise<[CryptoKey,Uint8Array<ArrayBuffer>]>}
325
319
  */
326
320
  export async function buildKeyIv(password, iterations) {
327
321
  if (!TUNNEL_TCP_WITH_CRYPTO) return [null, null]
@@ -357,9 +351,9 @@ export async function buildKeyIv(password, iterations) {
357
351
 
358
352
  /**
359
353
  *
360
- * @param {Uint8Array} data
354
+ * @param {Uint8Array<ArrayBuffer>} data
361
355
  * @param {CryptoKey} key
362
- * @param {Uint8Array} iv
356
+ * @param {Uint8Array<ArrayBuffer>} iv
363
357
  * @returns
364
358
  */
365
359
  export async function encrypt(data, key, iv) {
@@ -372,9 +366,9 @@ export async function encrypt(data, key, iv) {
372
366
  }
373
367
 
374
368
  /**
375
- * @param {Uint8Array} data
369
+ * @param {Uint8Array<ArrayBuffer>} data
376
370
  * @param {CryptoKey} key
377
- * @param {Uint8Array} iv
371
+ * @param {Uint8Array<ArrayBuffer>} iv
378
372
  * @returns
379
373
  */
380
374
  export async function decrypt(data, key, iv) {
@@ -428,83 +422,6 @@ export const TUNNEL_TCP_TYPE_PING = 0x4768e1ba
428
422
  export const TUNNEL_TCP_TYPE_PONG = 0x106f43fb
429
423
  export const TUNNEL_TCP_TYPE_ACK = 0xc5870539
430
424
 
431
- /**
432
- * @typedef {TUNNEL_TCP_TYPE_INIT
433
- * |TUNNEL_TCP_TYPE_LISTEN
434
- * |TUNNEL_TCP_TYPE_ONLISTEN
435
- * |TUNNEL_TCP_TYPE_CONNECT
436
- * |TUNNEL_TCP_TYPE_ONCONNECT
437
- * |TUNNEL_TCP_TYPE_DATA
438
- * |TUNNEL_TCP_TYPE_ERROR
439
- * |TUNNEL_TCP_TYPE_CLOSE
440
- * |TUNNEL_TCP_TYPE_PING
441
- * |TUNNEL_TCP_TYPE_PONG
442
- * |TUNNEL_TCP_TYPE_ACK} TUNNEL_TCP_TYPE
443
- */
444
-
445
- /**
446
- * @typedef {{
447
- * type:TUNNEL_TCP_TYPE;
448
- * srcId:number;
449
- * dstId:number;
450
- * srcChannel:number;
451
- * dstChannel:number;
452
- * buffer:Uint8Array;
453
- * }} TCP_TUNNEL_DATA
454
- */
455
-
456
- /**
457
- * @typedef {{
458
- * key:string;
459
- * }} TUNNEL_TCP_DATA_LISTEN
460
- */
461
-
462
- /**
463
- * @typedef {{
464
- * key:string;
465
- * }} TUNNEL_TCP_DATA_CONNECT
466
- */
467
-
468
- /**
469
- * @typedef {{
470
- * time:number;
471
- * }} TUNNEL_TCP_DATA_PINGPONG
472
- */
473
-
474
- /**
475
- * @typedef {{
476
- * id:number;
477
- * encodeWriter:WritableStreamDefaultWriter<Uint8Array>;
478
- * }} TUNNEL_TCP_SERVER
479
- */
480
-
481
- /**
482
- * @typedef {{
483
- * readable:ReadableStream<Uint8Array>;
484
- * writable:WritableStream<Uint8Array>;
485
- * reader:ReadableStreamDefaultReader<Uint8Array>;
486
- * writer:WritableStreamDefaultWriter<Uint8Array>;
487
- * dstId:number;
488
- * }} TUNNEL_TCP_SERVER_HELPER
489
- * @typedef {{
490
- * readable:ReadableStream<Uint8Array>;
491
- * writable:WritableStream<Uint8Array>;
492
- * reader:ReadableStreamDefaultReader<Uint8Array>;
493
- * writer:WritableStreamDefaultWriter<Uint8Array>;
494
- * listen:(param:{
495
- * clientKey?:string;
496
- * tunnelKey:string;
497
- * host?:string;
498
- * port:number;
499
- * })=>Promise<void>;
500
- * connect:(param:{
501
- * clientKey?:string;
502
- * tunnelKey: string;
503
- * port: number;
504
- * })=>Promise<void>;
505
- * }} TUNNEL_TCP_CLIENT_HELPER
506
- */
507
-
508
425
  /**
509
426
  * @param {TCP_TUNNEL_DATA} box
510
427
  */
@@ -520,7 +437,7 @@ export function buildTcpTunnelData(box) {
520
437
  }
521
438
 
522
439
  /**
523
- * @param {Uint8Array} buffer
440
+ * @param {Uint8Array<ArrayBuffer>} buffer
524
441
  */
525
442
  export function parseTcpTunnelData(buffer) {
526
443
  let offset = 0
@@ -617,25 +534,43 @@ export function pipeSocketDataWithChannel(channelMap, channelId, encodeWriter) {
617
534
  let signal = Promise_withResolvers()
618
535
  signal.resolve()
619
536
  let sendPackSize = 0
620
- let recvPackSize = 0
537
+ let remoteRecvPackSize = 0
621
538
  channel.notify = (size) => {
622
- recvPackSize = size
539
+ remoteRecvPackSize = size
623
540
  signal.resolve()
624
541
  }
625
542
  let [clientKey, clientIv] = channel.key_iv
626
543
  let bufferedTransform = createTimeBufferedTransformStream(50)
544
+ let backPressureTimer = null
627
545
  Readable.toWeb(socket).pipeThrough(bufferedTransform).pipeTo(new WritableStream({
546
+ /**
547
+ * @param {Uint8Array<ArrayBuffer>} chunk
548
+ */
628
549
  async write(chunk) {
629
550
  const buffer = await encrypt(chunk, clientKey, clientIv)
630
- let bufferPackSize = sendPackSize - recvPackSize
551
+ let bufferPackSize = sendPackSize - remoteRecvPackSize
552
+ if (DEBUG_TUNNEL_TCP) {
553
+ console.warn('bufferPackSize:', bufferPackSize)
554
+ }
631
555
  if (bufferPackSize > 10) {
632
556
  signal.resolve()
633
557
  signal = Promise_withResolvers()
558
+ const s = signal
559
+ backPressureTimer = setTimeout(() => {
560
+ s.resolve()
561
+ console.error('pipeSocketDataWithChannel timeout close channel')
562
+ sendPackSize = 0
563
+ this.close()
564
+ }, 10_000).unref()
634
565
  if (DEBUG_TUNNEL_TCP) {
635
- console.info('stop wait signal', ' sendPackSize:', sendPackSize, ' recvPackSize:', recvPackSize, ' bufferPackSize:', bufferPackSize)
566
+ console.info('stop wait signal', ' sendPackSize:', sendPackSize, ' recvPackSize:', remoteRecvPackSize, ' bufferPackSize:', bufferPackSize)
636
567
  }
637
568
  }
638
569
  await signal.promise
570
+ if (backPressureTimer) {
571
+ clearTimeout(backPressureTimer)
572
+ backPressureTimer = null
573
+ }
639
574
  await encodeWriter.write(buildTcpTunnelData({
640
575
  type: TUNNEL_TCP_TYPE_DATA,
641
576
  srcId: channel.srcId,
@@ -647,7 +582,7 @@ export function pipeSocketDataWithChannel(channelMap, channelId, encodeWriter) {
647
582
  sendPackSize++
648
583
  },
649
584
  async close() {
650
- await encodeWriter.write(buildTcpTunnelData({
585
+ encodeWriter.write(buildTcpTunnelData({
651
586
  type: TUNNEL_TCP_TYPE_CLOSE,
652
587
  srcId: channel.srcId,
653
588
  srcChannel: channel.srcChannel,
@@ -656,6 +591,7 @@ export function pipeSocketDataWithChannel(channelMap, channelId, encodeWriter) {
656
591
  buffer: new Uint8Array(0),
657
592
  })).catch((err) => { console.error('web stream write error', err.message) })
658
593
  channelMap.delete(channelId)
594
+ socket.destroy()
659
595
  }
660
596
  })).catch((err) => {
661
597
  console.error('web stream error', err.message)
@@ -694,8 +630,8 @@ function natTunnelData(param, data) {
694
630
 
695
631
  /**
696
632
  * @param {TunnelTcpServerHelperParam} param
697
- * @param {WritableStreamDefaultWriter<Uint8Array<ArrayBufferLike>>} encodeWriter
698
- * @param {Uint8Array<ArrayBufferLike>} chunk
633
+ * @param {WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>} encodeWriter
634
+ * @param {Uint8Array<ArrayBuffer>} chunk
699
635
  */
700
636
  async function dispatchServerBufferData(param, encodeWriter, chunk) {
701
637
  let data = parseTcpTunnelData(chunk)
@@ -741,10 +677,10 @@ async function dispatchServerBufferData(param, encodeWriter, chunk) {
741
677
  /**
742
678
  * @param {TunnelTcpClientHelperParam} param
743
679
  * @param {{ (): Promise<void>; }} setup
744
- * @param {Map<string,{host:string;port:number;key_iv:[CryptoKey, Uint8Array]}>} listenKeyParamMap
680
+ * @param {Map<string,{host:string;port:number;key_iv:[CryptoKey, Uint8Array<ArrayBuffer>]}>} listenKeyParamMap
745
681
  * @param {Map<number, SocketChannel>} channelMap
746
- * @param {WritableStreamDefaultWriter<Uint8Array<ArrayBufferLike>>} encodeWriter
747
- * @param {Uint8Array<ArrayBufferLike>} buffer
682
+ * @param {WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>} encodeWriter
683
+ * @param {Uint8Array<ArrayBuffer>} buffer
748
684
  */
749
685
  async function dispatchClientBufferData(param, setup, listenKeyParamMap, channelMap, encodeWriter, buffer) {
750
686
  let data = parseTcpTunnelData(buffer)
@@ -846,7 +782,7 @@ async function dispatchClientBufferData(param, setup, listenKeyParamMap, channel
846
782
  let channel = channelMap.get(channelId)
847
783
  if (channel) {
848
784
  channelMap.delete(channelId)
849
- channel.socket.end()
785
+ channel.socket.destroy()
850
786
  }
851
787
  }
852
788
  if (data.type == TUNNEL_TCP_TYPE_ERROR) {
@@ -923,39 +859,6 @@ export async function closeRemoteChannel(encodeWriter, data) {
923
859
  })).catch((err) => { console.error('closeRemoteChannel stream write error', err.message) })
924
860
  }
925
861
 
926
- /**
927
- * @typedef {{
928
- * writer:WritableStreamDefaultWriter<Uint8Array>;
929
- * socket:net.Socket;
930
- * srcId:number;
931
- * dstId:number;
932
- * srcChannel:number;
933
- * dstChannel:number;
934
- * notify:(size:number)=>void;
935
- * recvPackSize:number;
936
- * key_iv:[CryptoKey, Uint8Array];
937
- * }} SocketChannel
938
- */
939
-
940
-
941
- /**
942
- * @typedef {{
943
- * uniqueId:number;
944
- * listenMap:Map<string,number>;
945
- * dstMap:Map<number,TUNNEL_TCP_SERVER>;
946
- * serverKey?:string;
947
- * }} TunnelTcpServerHelperParam
948
- */
949
-
950
- /**
951
- * @typedef {{
952
- * signal:AbortSignal;
953
- * serverKey?:string;
954
- * uniqueId:number;
955
- * clientDataId:number;
956
- * }} TunnelTcpClientHelperParam
957
- */
958
-
959
862
  /**
960
863
  * @param {TunnelTcpServerHelperParam} param
961
864
  */
@@ -1010,7 +913,7 @@ export function createTunnelTcpClientHelper(param) {
1010
913
  /** @type{Map<number,SocketChannel>} */
1011
914
  let channelMap = new Map()
1012
915
 
1013
- /** @type{Map<string,{host:string;port:number;key_iv:[CryptoKey, Uint8Array]}>} */
916
+ /** @type{Map<string,{host:string;port:number;key_iv:[CryptoKey, Uint8Array<ArrayBuffer>]}>} */
1014
917
  let listenKeyParamMap = new Map()
1015
918
 
1016
919
  let server_key_iv = buildKeyIv(param.serverKey, 10)
@@ -1037,6 +940,9 @@ export function createTunnelTcpClientHelper(param) {
1037
940
  }
1038
941
 
1039
942
  decode.readable.pipeTo(new WritableStream({
943
+ /**
944
+ * @param {Uint8Array<ArrayBuffer>} buffer
945
+ */
1040
946
  async write(buffer) {
1041
947
  try {
1042
948
  await dispatchClientBufferData(param, setup, listenKeyParamMap, channelMap, encodeWriter, buffer)
@@ -1144,7 +1050,7 @@ export function createTunnelTcpClientHelper(param) {
1144
1050
  }
1145
1051
 
1146
1052
  /** @type{TUNNEL_TCP_CLIENT_HELPER} */
1147
- let helper = { readable: encode.readable, writable: decode.writable, reader: null, writer: null, listen, connect }
1053
+ let helper = { readable: encode.readable, writable: decode.writable, reader: null, writer: null, listen, connect, param: outParam }
1148
1054
  return helper
1149
1055
 
1150
1056
  }
@@ -1467,6 +1373,9 @@ export function createTunnelTcpClientHttp(param) {
1467
1373
  let socketWriter = null
1468
1374
  let bufferedTransform = createTimeBufferedTransformStream(50)
1469
1375
  helper.readable.pipeThrough(bufferedTransform).pipeTo(new WritableStream({
1376
+ /**
1377
+ * @param {Uint8Array<ArrayBufferLike>} chunk
1378
+ */
1470
1379
  async write(chunk) {
1471
1380
  while (!param.signal.aborted && socketWriter == null) {
1472
1381
  await signal.promise
@@ -1521,6 +1430,11 @@ export function createTunnelTcpClientHttp(param) {
1521
1430
  console.error('createConnectionHttpx res close ')
1522
1431
  promise.resolve()
1523
1432
  })
1433
+ setTimeout(() => {
1434
+ if (helper.param.clientDataId < 1) {
1435
+ res.destroy(new Error('init clientId failed!'))
1436
+ }
1437
+ }, 10_000).unref()
1524
1438
  })
1525
1439
  Readable.fromWeb(transform.readable).pipe(req)
1526
1440
  req.flushHeaders()
package/src/lib.test.js CHANGED
@@ -5,7 +5,7 @@ import net from 'net'
5
5
  import http from 'http'
6
6
  import { Readable, Transform } from 'node:stream'
7
7
  import Koa from 'koa'
8
- import Router from 'koa-router'
8
+ import Router from '@koa/router'
9
9
  import { WebSocketServer } from 'ws'
10
10
  import log4js from 'log4js'
11
11
 
@@ -62,7 +62,7 @@ test('tunnel-tcp-socket-test', async () => {
62
62
  console.error(err.message)
63
63
  })
64
64
 
65
- let rec = null
65
+ let rec = ''
66
66
 
67
67
  await new Promise((resolve) => {
68
68
  socket.on('connect', async () => {
@@ -86,7 +86,8 @@ test('tunnel-tcp-socket-test', async () => {
86
86
  socketServer.close()
87
87
  })
88
88
 
89
- strictEqual(rec, 'qwertyuiop - 99')
89
+ let from = rec.length - 'qwertyuiop - 99'.length
90
+ strictEqual(rec.substring(from), 'qwertyuiop - 99')
90
91
 
91
92
  })
92
93
  console.info('over!')
@@ -508,4 +509,144 @@ test('test-connect', { skip: SKIP_MANAUAL_TEST }, async () => {
508
509
 
509
510
  await sleep(10000_000)
510
511
  })
512
+ })
513
+
514
+ test('terminal-server', async () => {
515
+ // node --test-name-pattern="^terminal-server$" src/lib.test.js
516
+ using stack = new DisposableStack()
517
+ const ac = new AbortController()
518
+ stack.adopt(ac, () => { ac.abort() })
519
+ let transformSize = 0
520
+
521
+ async function createEchoServer() {
522
+ console.info('创建socket服务')
523
+ let socketServer = net.createServer((socket) => {
524
+ socket.pipe(new Transform({
525
+ transform(chunk, _, callback) {
526
+ // console.info('transform chunk', chunk.length)
527
+ transformSize += chunk.length
528
+ this.push("echo::::")
529
+ this.push(chunk)
530
+ callback()
531
+ }
532
+ })).pipe(socket).on('error', (err) => console.error(err.message))
533
+ }).listen(9036)
534
+ await sleep(100)
535
+ socketServer.on('error', (err) => {
536
+ console.error(err.message)
537
+ })
538
+ ac.signal.addEventListener('abort', () => {
539
+ socketServer.close()
540
+ })
541
+ }
542
+
543
+ /**
544
+ *
545
+ * @param {AbortController} ac
546
+ */
547
+ async function createDispatchServer(ac) {
548
+ console.info('创建转发服务 http')
549
+ let app = new Koa()
550
+ let router = new Router()
551
+ createTunnelTcpServerKoaRouter({
552
+ signal: ac.signal,
553
+ router: router,
554
+ path: '/tunnel/0f7c5b2c9080eaa9e4d6139126daac04',
555
+ serverKey: '2934c57f790f9e99a52a121802df231c',
556
+ })
557
+ app.use(router.routes())
558
+ app.use(router.allowedMethods())
559
+ app.onerror = (err) => console.info(err.message)
560
+ let koaServer = http.createServer(app.callback())
561
+ koaServer.listen(9035)
562
+ koaServer.on('error', (e) => { console.error(e.message) })
563
+ ac.signal.addEventListener('abort', () => {
564
+ console.info('close koa server')
565
+ koaServer.close((err) => {
566
+ console.info('on server close result ', err)
567
+ })
568
+ koaServer.closeAllConnections()
569
+ })
570
+ await sleep(1000)
571
+ }
572
+
573
+ async function createListenClient() {
574
+ console.info('创建监听服务 http')
575
+ let connection1 = createTunnelTcpClientHttp({
576
+ url: 'http://127.0.0.1:9035/tunnel/0f7c5b2c9080eaa9e4d6139126daac04',
577
+ signal: ac.signal,
578
+ serverKey: '2934c57f790f9e99a52a121802df231c',
579
+ })
580
+ connection1.listen({
581
+ clientKey: 'mmm',
582
+ tunnelKey: 'b0f5014acad2060d6bd3730a1721c97f',
583
+ host: '127.0.0.1',
584
+ port: 9036,
585
+ })
586
+ await sleep(100)
587
+ }
588
+
589
+ async function createConnectClient() {
590
+ console.info('创建连接服务 http 1')
591
+ let connection2 = createTunnelTcpClientHttp({
592
+ url: 'http://127.0.0.1:9035/tunnel/0f7c5b2c9080eaa9e4d6139126daac04',
593
+ signal: ac.signal,
594
+ serverKey: '2934c57f790f9e99a52a121802df231c',
595
+ })
596
+ connection2.connect({
597
+ clientKey: 'mmm',
598
+ tunnelKey: 'b0f5014acad2060d6bd3730a1721c97f',
599
+ port: 9037,
600
+ })
601
+
602
+ await sleep(1000)
603
+ }
604
+
605
+ let socketCreateCount = 0
606
+ let recvCount = 0
607
+ async function startSendRecvTest() {
608
+ socketCreateCount++
609
+ console.info('tcp透传测试 start')
610
+ let socket1 = net.createConnection({ host: '127.0.0.1', port: 9037 })
611
+ socket1.on('error', (err) => { console.error(err.message) })
612
+ ac.signal.addEventListener('abort', () => { socket1.destroy() })
613
+ socket1.on('connect', async () => {
614
+ socket1.on('data', (chunk) => {
615
+ console.info('receive chunk ', Uint8Array_toString(chunk))
616
+ recvCount++
617
+ })
618
+ while (!ac.signal.aborted && !socket1.closed) {
619
+ await sleep(1000)
620
+ socket1.write(`iiiiiii>>>>>>> ${new Date().toLocaleString()}`)
621
+ }
622
+ console.info('finished socket.')
623
+ setTimeout(() => {
624
+ startSendRecvTest()
625
+ }, 1)
626
+ })
627
+ }
628
+
629
+ await createEchoServer()
630
+ let ac2 = new AbortController()
631
+ stack.adopt(0, () => ac2.abort())
632
+ await createDispatchServer(ac2)
633
+ await createListenClient()
634
+ await createConnectClient()
635
+ await startSendRecvTest()
636
+
637
+ await sleep(5000)
638
+ ac2.abort()
639
+ console.info('stop dispatch server')
640
+ await sleep(5_000)
641
+ ac2 = new AbortController()
642
+ stack.adopt(0, () => ac2.abort())
643
+ console.info('recreate dispatch server')
644
+ await createDispatchServer(ac2)
645
+
646
+ await sleep(20_000)
647
+
648
+ strictEqual(socketCreateCount, 2)
649
+ strictEqual(recvCount, 14)
650
+
651
+ console.info('over!')
511
652
  })
package/src/types.ts ADDED
@@ -0,0 +1,114 @@
1
+ import net from 'node:net'
2
+ import { WritableStream, ReadableStream, ReadableStreamDefaultReader, WritableStreamDefaultWriter } from "stream/web"
3
+
4
+
5
+ export type PromiseResolvers = {
6
+ promise: Promise<any>
7
+ resolve: (value: any | null) => void
8
+ reject: (reason: any | null) => void
9
+ }
10
+
11
+ export type TUNNEL_TCP_TYPE_INIT = 0xa9b398d5 // 链接建立后返回id
12
+ export type TUNNEL_TCP_TYPE_LISTEN = 0xe41957d3
13
+ export type TUNNEL_TCP_TYPE_ONLISTEN = 0x20993e38
14
+ export type TUNNEL_TCP_TYPE_CONNECT = 0x11d949f8
15
+ export type TUNNEL_TCP_TYPE_ONCONNECT = 0x377b2181
16
+ export type TUNNEL_TCP_TYPE_DATA = 0x48678f39
17
+ export type TUNNEL_TCP_TYPE_ERROR = 0x8117f762
18
+ export type TUNNEL_TCP_TYPE_CLOSE = 0x72fd6470
19
+ export type TUNNEL_TCP_TYPE_PING = 0x4768e1ba
20
+ export type TUNNEL_TCP_TYPE_PONG = 0x106f43fb
21
+ export type TUNNEL_TCP_TYPE_ACK = 0xc5870539
22
+
23
+ export type TUNNEL_TCP_TYPE = TUNNEL_TCP_TYPE_INIT |
24
+ TUNNEL_TCP_TYPE_LISTEN |
25
+ TUNNEL_TCP_TYPE_ONLISTEN |
26
+ TUNNEL_TCP_TYPE_CONNECT |
27
+ TUNNEL_TCP_TYPE_ONCONNECT |
28
+ TUNNEL_TCP_TYPE_DATA |
29
+ TUNNEL_TCP_TYPE_ERROR |
30
+ TUNNEL_TCP_TYPE_CLOSE |
31
+ TUNNEL_TCP_TYPE_PING |
32
+ TUNNEL_TCP_TYPE_PONG |
33
+ TUNNEL_TCP_TYPE_ACK
34
+
35
+ export type TCP_TUNNEL_DATA = {
36
+ type: TUNNEL_TCP_TYPE
37
+ srcId: number
38
+ dstId: number
39
+ srcChannel: number
40
+ dstChannel: number
41
+ buffer: Uint8Array<ArrayBuffer>
42
+ }
43
+
44
+ export type TUNNEL_TCP_DATA_LISTEN = {
45
+ key: string
46
+ }
47
+
48
+ export type TUNNEL_TCP_DATA_CONNECT = {
49
+ key: string
50
+ }
51
+
52
+ export type TUNNEL_TCP_DATA_PINGPONG = {
53
+ time: number
54
+ }
55
+
56
+ export type TUNNEL_TCP_SERVER = {
57
+ id: number
58
+ encodeWriter: WritableStreamDefaultWriter<Uint8Array>
59
+ }
60
+
61
+ export type TUNNEL_TCP_SERVER_HELPER = {
62
+ readable: ReadableStream<Uint8Array<ArrayBuffer>>
63
+ writable: WritableStream<Uint8Array<ArrayBuffer>>
64
+ reader: ReadableStreamDefaultReader<Uint8Array<ArrayBuffer>>
65
+ writer: WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>
66
+ dstId: number
67
+ }
68
+
69
+ export type TUNNEL_TCP_CLIENT_HELPER = {
70
+ readable: ReadableStream<Uint8Array<ArrayBuffer>>
71
+ writable: WritableStream<Uint8Array<ArrayBuffer>>
72
+ reader: ReadableStreamDefaultReader<Uint8Array<ArrayBuffer>>
73
+ writer: WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>
74
+ param: TunnelTcpClientHelperParam
75
+ listen: (param: {
76
+ clientKey?: string
77
+ tunnelKey: string
78
+ host?: string
79
+ port: number
80
+ }) => Promise<void>
81
+ connect: (param: {
82
+ clientKey?: string
83
+ tunnelKey: string
84
+ port: number
85
+ }) => Promise<void>
86
+ }
87
+
88
+
89
+ export type SocketChannel = {
90
+ writer: WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>
91
+ socket: net.Socket
92
+ srcId: number
93
+ dstId: number
94
+ srcChannel: number
95
+ dstChannel: number
96
+ notify: (size: number) => void
97
+ recvPackSize: number
98
+ key_iv: [CryptoKey, Uint8Array<ArrayBuffer>]
99
+ }
100
+
101
+
102
+ export type TunnelTcpServerHelperParam = {
103
+ uniqueId: number
104
+ listenMap: Map<string, number>
105
+ dstMap: Map<number, TUNNEL_TCP_SERVER>
106
+ serverKey?: string
107
+ }
108
+
109
+ export type TunnelTcpClientHelperParam = {
110
+ signal: AbortSignal
111
+ serverKey?: string
112
+ uniqueId: number
113
+ clientDataId: number
114
+ }