js-tcp-tunnel 1.0.3 → 1.1.1

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.3",
3
+ "version": "1.1.1",
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
@@ -6,9 +6,10 @@ import https from 'node:https'
6
6
  import { ReadableStream, TransformStream, WritableStream } from 'node:stream/web'
7
7
 
8
8
  /**
9
+ * @import { ReadableStreamDefaultReader, WritableStreamDefaultWriter } from 'node:stream/web'
9
10
  * @import {WebSocketServer} from 'ws'
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'
11
+ * @import Router from '@koa/router'
12
+ * @import {ConnectParam, ListenParam, SocketChannel, TCP_TUNNEL_DATA, TUNNEL_TCP_DATA_CONNECT, TUNNEL_TCP_DATA_LISTEN, TUNNEL_TCP_DATA_PINGPONG, TunnelTcpClientHelperParam, TunnelTcpServerHelperParam} from './types.js'
12
13
  */
13
14
 
14
15
  const DEBUG_TUNNEL_TCP = false
@@ -68,8 +69,8 @@ export function Promise_withResolvers() {
68
69
  }
69
70
 
70
71
  /**
71
- * @param {Promise<[CryptoKey,Uint8Array]>} key_iv
72
- * @returns {TransformStream<Uint8Array, Uint8Array>}
72
+ * @param {Promise<[CryptoKey,Uint8Array<ArrayBuffer>]>} key_iv
73
+ * @returns {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>}
73
74
  */
74
75
  export function createEncodeStream(key_iv) {
75
76
  let key = null
@@ -86,8 +87,8 @@ export function createEncodeStream(key_iv) {
86
87
  }
87
88
 
88
89
  /**
89
- * @param {Promise<[CryptoKey,Uint8Array]>} key_iv
90
- * @returns {TransformStream<Uint8Array, Uint8Array>}
90
+ * @param {Promise<[CryptoKey,Uint8Array<ArrayBuffer>]>} key_iv
91
+ * @returns {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>}
91
92
  */
92
93
  export function createDecodeStream(key_iv) {
93
94
  let key = null
@@ -110,10 +111,10 @@ export function createDecodeStream(key_iv) {
110
111
  const HEADER_CHECK = 0xb1f7705f
111
112
 
112
113
  /**
113
- * @param {Uint8Array[]} queue
114
+ * @param {Uint8Array<ArrayBuffer>[]} queue
114
115
  * @param {CryptoKey} key
115
- * @param {Uint8Array} iv
116
- * @returns {Promise<Uint8Array>}
116
+ * @param {Uint8Array<ArrayBuffer>} iv
117
+ * @returns {Promise<Uint8Array<ArrayBuffer>>}
117
118
  */
118
119
  export async function buildBufferData(queue, key, iv) {
119
120
  let buffers = []
@@ -132,11 +133,11 @@ export async function buildBufferData(queue, key, iv) {
132
133
  /**
133
134
  * @param {Uint8Array<ArrayBuffer>} buffer
134
135
  * @param {CryptoKey} key
135
- * @param {Uint8Array} iv
136
- * @returns {Promise<[Uint8Array[],Uint8Array<ArrayBuffer>]>}
136
+ * @param {Uint8Array<ArrayBuffer>} iv
137
+ * @returns {Promise<[Uint8Array<ArrayBuffer>[],Uint8Array<ArrayBuffer>]>}
137
138
  */
138
139
  export async function parseBufferData(buffer, key, iv) {
139
- /** @type{Uint8Array[]} */
140
+ /** @type{Uint8Array<ArrayBuffer>[]} */
140
141
  let queue = []
141
142
  let offset = 0
142
143
  let remain = new Uint8Array(0)
@@ -315,7 +316,7 @@ export function printTcpTunnelData(data) {
315
316
  *
316
317
  * @param {string} password
317
318
  * @param {number} iterations
318
- * @returns {Promise<[CryptoKey,Uint8Array]>}
319
+ * @returns {Promise<[CryptoKey,Uint8Array<ArrayBuffer>]>}
319
320
  */
320
321
  export async function buildKeyIv(password, iterations) {
321
322
  if (!TUNNEL_TCP_WITH_CRYPTO) return [null, null]
@@ -351,9 +352,9 @@ export async function buildKeyIv(password, iterations) {
351
352
 
352
353
  /**
353
354
  *
354
- * @param {Uint8Array} data
355
+ * @param {Uint8Array<ArrayBuffer>} data
355
356
  * @param {CryptoKey} key
356
- * @param {Uint8Array} iv
357
+ * @param {Uint8Array<ArrayBuffer>} iv
357
358
  * @returns
358
359
  */
359
360
  export async function encrypt(data, key, iv) {
@@ -366,9 +367,9 @@ export async function encrypt(data, key, iv) {
366
367
  }
367
368
 
368
369
  /**
369
- * @param {Uint8Array} data
370
+ * @param {Uint8Array<ArrayBuffer>} data
370
371
  * @param {CryptoKey} key
371
- * @param {Uint8Array} iv
372
+ * @param {Uint8Array<ArrayBuffer>} iv
372
373
  * @returns
373
374
  */
374
375
  export async function decrypt(data, key, iv) {
@@ -437,7 +438,7 @@ export function buildTcpTunnelData(box) {
437
438
  }
438
439
 
439
440
  /**
440
- * @param {Uint8Array} buffer
441
+ * @param {Uint8Array<ArrayBuffer>} buffer
441
442
  */
442
443
  export function parseTcpTunnelData(buffer) {
443
444
  let offset = 0
@@ -534,25 +535,43 @@ export function pipeSocketDataWithChannel(channelMap, channelId, encodeWriter) {
534
535
  let signal = Promise_withResolvers()
535
536
  signal.resolve()
536
537
  let sendPackSize = 0
537
- let recvPackSize = 0
538
+ let remoteRecvPackSize = 0
538
539
  channel.notify = (size) => {
539
- recvPackSize = size
540
+ remoteRecvPackSize = size
540
541
  signal.resolve()
541
542
  }
542
543
  let [clientKey, clientIv] = channel.key_iv
543
544
  let bufferedTransform = createTimeBufferedTransformStream(50)
545
+ let backPressureTimer = null
544
546
  Readable.toWeb(socket).pipeThrough(bufferedTransform).pipeTo(new WritableStream({
547
+ /**
548
+ * @param {Uint8Array<ArrayBuffer>} chunk
549
+ */
545
550
  async write(chunk) {
546
551
  const buffer = await encrypt(chunk, clientKey, clientIv)
547
- let bufferPackSize = sendPackSize - recvPackSize
552
+ let bufferPackSize = sendPackSize - remoteRecvPackSize
553
+ if (DEBUG_TUNNEL_TCP) {
554
+ console.warn('bufferPackSize:', bufferPackSize)
555
+ }
548
556
  if (bufferPackSize > 10) {
549
557
  signal.resolve()
550
558
  signal = Promise_withResolvers()
559
+ const s = signal
560
+ backPressureTimer = setTimeout(() => {
561
+ s.resolve()
562
+ console.error('pipeSocketDataWithChannel timeout close channel')
563
+ sendPackSize = 0
564
+ this.close()
565
+ }, 10_000).unref()
551
566
  if (DEBUG_TUNNEL_TCP) {
552
- console.info('stop wait signal', ' sendPackSize:', sendPackSize, ' recvPackSize:', recvPackSize, ' bufferPackSize:', bufferPackSize)
567
+ console.info('stop wait signal', ' sendPackSize:', sendPackSize, ' recvPackSize:', remoteRecvPackSize, ' bufferPackSize:', bufferPackSize)
553
568
  }
554
569
  }
555
570
  await signal.promise
571
+ if (backPressureTimer) {
572
+ clearTimeout(backPressureTimer)
573
+ backPressureTimer = null
574
+ }
556
575
  await encodeWriter.write(buildTcpTunnelData({
557
576
  type: TUNNEL_TCP_TYPE_DATA,
558
577
  srcId: channel.srcId,
@@ -573,6 +592,7 @@ export function pipeSocketDataWithChannel(channelMap, channelId, encodeWriter) {
573
592
  buffer: new Uint8Array(0),
574
593
  })).catch((err) => { console.error('web stream write error', err.message) })
575
594
  channelMap.delete(channelId)
595
+ socket.destroy()
576
596
  }
577
597
  })).catch((err) => {
578
598
  console.error('web stream error', err.message)
@@ -611,8 +631,8 @@ function natTunnelData(param, data) {
611
631
 
612
632
  /**
613
633
  * @param {TunnelTcpServerHelperParam} param
614
- * @param {WritableStreamDefaultWriter<Uint8Array<ArrayBufferLike>>} encodeWriter
615
- * @param {Uint8Array<ArrayBufferLike>} chunk
634
+ * @param {WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>} encodeWriter
635
+ * @param {Uint8Array<ArrayBuffer>} chunk
616
636
  */
617
637
  async function dispatchServerBufferData(param, encodeWriter, chunk) {
618
638
  let data = parseTcpTunnelData(chunk)
@@ -658,10 +678,10 @@ async function dispatchServerBufferData(param, encodeWriter, chunk) {
658
678
  /**
659
679
  * @param {TunnelTcpClientHelperParam} param
660
680
  * @param {{ (): Promise<void>; }} setup
661
- * @param {Map<string,{host:string;port:number;key_iv:[CryptoKey, Uint8Array]}>} listenKeyParamMap
681
+ * @param {Map<string,{host:string;port:number;key_iv:[CryptoKey, Uint8Array<ArrayBuffer>]}>} listenKeyParamMap
662
682
  * @param {Map<number, SocketChannel>} channelMap
663
- * @param {WritableStreamDefaultWriter<Uint8Array<ArrayBufferLike>>} encodeWriter
664
- * @param {Uint8Array<ArrayBufferLike>} buffer
683
+ * @param {WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>} encodeWriter
684
+ * @param {Uint8Array<ArrayBuffer>} buffer
665
685
  */
666
686
  async function dispatchClientBufferData(param, setup, listenKeyParamMap, channelMap, encodeWriter, buffer) {
667
687
  let data = parseTcpTunnelData(buffer)
@@ -852,6 +872,9 @@ export function createTunnelTcpServerHelper(param) {
852
872
  if (DEBUG_TUNNEL_TCP) {
853
873
  let writer = encodeWriter
854
874
  encodeWriter = new WritableStream({
875
+ /**
876
+ * @param {Uint8Array<ArrayBuffer>} chunk
877
+ */
855
878
  async write(chunk) {
856
879
  tcpTunnelDataSend += chunk.length
857
880
  let data = parseTcpTunnelData(chunk)
@@ -862,6 +885,9 @@ export function createTunnelTcpServerHelper(param) {
862
885
  }
863
886
 
864
887
  decode.readable.pipeTo(new WritableStream({
888
+ /**
889
+ * @param {Uint8Array<ArrayBuffer>} chunk
890
+ */
865
891
  async write(chunk) {
866
892
  try {
867
893
  await dispatchServerBufferData(param, encodeWriter, chunk)
@@ -882,63 +908,112 @@ export function createTunnelTcpServerHelper(param) {
882
908
  buffer: new Uint8Array(0),
883
909
  }))
884
910
 
885
- /** @type{TUNNEL_TCP_SERVER_HELPER} */
886
- let helper = { readable: encode.readable, writable: decode.writable, reader: null, writer: null, dstId: id, }
887
- return helper
911
+ return new TunnelTcpServerHelper(id, encode, decode)
888
912
  }
889
913
 
890
914
  /**
891
915
  * @param {TunnelTcpClientHelperParam} param
892
916
  */
893
917
  export function createTunnelTcpClientHelper(param) {
894
- /** @type{Map<number,SocketChannel>} */
895
- let channelMap = new Map()
896
-
897
- /** @type{Map<string,{host:string;port:number;key_iv:[CryptoKey, Uint8Array]}>} */
898
- let listenKeyParamMap = new Map()
899
-
900
918
  let server_key_iv = buildKeyIv(param.serverKey, 10)
901
-
902
- param.signal.addEventListener('abort', () => {
903
- channelMap.values().forEach(o => {
904
- o.socket.destroy()
905
- })
906
- })
907
-
908
919
  let encode = createEncodeStream(server_key_iv)
909
920
  let decode = createDecodeStream(server_key_iv)
910
- let encodeWriter = encode.writable.getWriter()
911
- if (DEBUG_TUNNEL_TCP) {
912
- let writer = encodeWriter
913
- encodeWriter = new WritableStream({
914
- async write(chunk) {
915
- tcpTunnelDataSend += chunk.length
916
- let data = parseTcpTunnelData(chunk)
917
- console.info('send', printTcpTunnelData(data))
918
- writer.write(chunk)
919
- }
920
- }).getWriter()
921
+ return new TunnelTcpClientHelper(param, encode, decode)
922
+ }
923
+
924
+ export class TunnelTcpServerHelper {
925
+ dstId = 0
926
+ /** @type{ReadableStream<Uint8Array<ArrayBuffer>>} */
927
+ readable = null
928
+ /** @type{WritableStream<Uint8Array<ArrayBuffer>>} */
929
+ writable = null
930
+ /** @type{ReadableStreamDefaultReader<Uint8Array<ArrayBuffer>>} */
931
+ reader = null
932
+ /** @type{WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>} */
933
+ writer = null
934
+ /**
935
+ * @param {number} id
936
+ * @param {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>} encode
937
+ * @param {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>} decode
938
+ */
939
+ constructor(id, encode, decode) {
940
+ this.dstId = id
941
+ this.readable = encode.readable
942
+ this.writable = decode.writable
921
943
  }
944
+ }
922
945
 
923
- decode.readable.pipeTo(new WritableStream({
924
- async write(buffer) {
925
- try {
926
- await dispatchClientBufferData(param, setup, listenKeyParamMap, channelMap, encodeWriter, buffer)
927
- } catch (error) {
928
- console.error('decode.readable.pipeTo.write', error.message)
929
- }
946
+ export class TunnelTcpClientHelper {
947
+ /** @type{ReadableStream<Uint8Array<ArrayBuffer>>} */
948
+ readable = null
949
+ /** @type{WritableStream<Uint8Array<ArrayBuffer>>} */
950
+ writable = null
951
+ /** @type{ReadableStreamDefaultReader<Uint8Array<ArrayBuffer>>} */
952
+ reader = null
953
+ /** @type{WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>} */
954
+ writer = null
955
+ /** @type{TunnelTcpClientHelperParam} */
956
+ param = null
957
+
958
+ /** @type{Map<number,SocketChannel>} */
959
+ channelMap = new Map()
960
+ /** @type{Map<string,{host:string;port:number;key_iv:[CryptoKey, Uint8Array<ArrayBuffer>]}>} */
961
+ listenKeyParamMap = new Map()
962
+ /** @type{Set<ListenParam>} */
963
+ listenParams = new Set()
964
+ /** @type{WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>} */
965
+ encodeWriter = null
966
+
967
+ /**
968
+ * @param {TunnelTcpClientHelperParam} param
969
+ * @param {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>} encode
970
+ * @param {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>} decode
971
+ */
972
+ constructor(param, encode, decode) {
973
+ this.param = param
974
+ this.readable = encode.readable
975
+ this.writable = decode.writable
976
+
977
+ param.signal.addEventListener('abort', () => {
978
+ this.channelMap.values().forEach(o => {
979
+ o.socket.destroy()
980
+ })
981
+ })
982
+
983
+ this.encodeWriter = encode.writable.getWriter()
984
+ if (DEBUG_TUNNEL_TCP) {
985
+ let writer = this.encodeWriter
986
+ this.encodeWriter = new WritableStream({
987
+ async write(chunk) {
988
+ tcpTunnelDataSend += chunk.length
989
+ let data = parseTcpTunnelData(chunk)
990
+ console.info('send', printTcpTunnelData(data))
991
+ writer.write(chunk)
992
+ }
993
+ }).getWriter()
930
994
  }
931
- }))
932
995
 
933
- let outParam = param
934
- let listenParams = new Set()
996
+ const thiz = this
997
+ decode.readable.pipeTo(new WritableStream({
998
+ /**
999
+ * @param {Uint8Array<ArrayBuffer>} buffer
1000
+ */
1001
+ async write(buffer) {
1002
+ try {
1003
+ await dispatchClientBufferData(param, () => thiz.setup(), thiz.listenKeyParamMap, thiz.channelMap, thiz.encodeWriter, buffer)
1004
+ } catch (error) {
1005
+ console.error('decode.readable.pipeTo.write', error.message)
1006
+ }
1007
+ }
1008
+ }))
1009
+ }
935
1010
 
936
- async function setup() {
937
- channelMap.forEach((channel) => {
938
- channel.srcId = param.clientDataId
1011
+ async setup() {
1012
+ this.channelMap.forEach((channel) => {
1013
+ channel.srcId = this.param.clientDataId
939
1014
  /** @type{TUNNEL_TCP_DATA_PINGPONG} */
940
1015
  let pingData = { time: Date.now() }
941
- encodeWriter.write(buildTcpTunnelData({
1016
+ this.encodeWriter.write(buildTcpTunnelData({
942
1017
  type: TUNNEL_TCP_TYPE_PING,
943
1018
  srcId: channel.srcId,
944
1019
  srcChannel: channel.srcChannel,
@@ -947,34 +1022,29 @@ export function createTunnelTcpClientHelper(param) {
947
1022
  buffer: Uint8Array_from(JSON.stringify(pingData)),
948
1023
  }))
949
1024
  })
950
- for (const param of listenParams) {
951
- await listen(param)
1025
+ for (const param of this.listenParams) {
1026
+ await this.listen(param)
952
1027
  }
953
1028
  }
954
1029
 
955
1030
  /**
956
- * @param {{
957
- * clientKey?:string;
958
- * tunnelKey:string;
959
- * host?:string;
960
- * port:number;
961
- * }} param
1031
+ * @param {ListenParam} param
962
1032
  */
963
- async function listen(param) {
964
- listenParams.add(param)
965
- console.info('listenParams size', listenParams.size)
966
- if (outParam.clientDataId < 1) {
1033
+ async listen(param) {
1034
+ this.listenParams.add(param)
1035
+ console.info('listenParams size', this.listenParams.size)
1036
+ if (this.param.clientDataId < 1) {
967
1037
  console.info('skip send listen dataId == 0')
968
1038
  return
969
1039
  }
970
1040
  let key = sha512(param.tunnelKey)
971
1041
  let key_iv = await buildKeyIv(param.clientKey, 10)
972
- listenKeyParamMap.set(key, { host: param.host, port: param.port, key_iv })
1042
+ this.listenKeyParamMap.set(key, { host: param.host, port: param.port, key_iv })
973
1043
  /** @type{TUNNEL_TCP_DATA_LISTEN} */
974
1044
  let listenData = { key: key }
975
- await encodeWriter.write(buildTcpTunnelData({
1045
+ await this.encodeWriter.write(buildTcpTunnelData({
976
1046
  type: TUNNEL_TCP_TYPE_LISTEN,
977
- srcId: outParam.clientDataId,
1047
+ srcId: this.param.clientDataId,
978
1048
  srcChannel: 0,
979
1049
  dstId: 0,
980
1050
  dstChannel: 0,
@@ -983,19 +1053,15 @@ export function createTunnelTcpClientHelper(param) {
983
1053
  }
984
1054
 
985
1055
  /**
986
- * @param {{
987
- * clientKey?:string;
988
- * tunnelKey: string;
989
- * port: number;
990
- * }} param
1056
+ * @param {ConnectParam} param
991
1057
  */
992
- async function connect(param) {
1058
+ async connect(param) {
993
1059
  let key_iv = await buildKeyIv(param.clientKey, 10)
994
1060
  let server = net.createServer((socket) => {
995
- let channelId = outParam.uniqueId++
1061
+ let channelId = this.param.uniqueId++
996
1062
  socket.on('error', (err) => {
997
1063
  console.error('createTunnelTcpClientHelper on socket error', err.message)
998
- channelMap.delete(channelId)
1064
+ this.channelMap.delete(channelId)
999
1065
  })
1000
1066
  /** @type{TUNNEL_TCP_DATA_CONNECT} */
1001
1067
  let connectData = { key: sha512(param.tunnelKey) }
@@ -1003,7 +1069,7 @@ export function createTunnelTcpClientHelper(param) {
1003
1069
  let channel = {
1004
1070
  writer: Writable.toWeb(socket).getWriter(),
1005
1071
  socket,
1006
- srcId: outParam.clientDataId,
1072
+ srcId: this.param.clientDataId,
1007
1073
  srcChannel: channelId,
1008
1074
  dstId: 0,
1009
1075
  dstChannel: 0,
@@ -1011,8 +1077,8 @@ export function createTunnelTcpClientHelper(param) {
1011
1077
  key_iv,
1012
1078
  notify: null,
1013
1079
  }
1014
- channelMap.set(channelId, channel)
1015
- encodeWriter.write(buildTcpTunnelData({
1080
+ this.channelMap.set(channelId, channel)
1081
+ this.encodeWriter.write(buildTcpTunnelData({
1016
1082
  type: TUNNEL_TCP_TYPE_CONNECT,
1017
1083
  srcId: channel.srcId,
1018
1084
  srcChannel: channel.srcChannel,
@@ -1024,13 +1090,8 @@ export function createTunnelTcpClientHelper(param) {
1024
1090
  server.on('error', (err) => {
1025
1091
  console.error('createTunnelTcpClientHelper connect on server error', err.message)
1026
1092
  })
1027
- outParam.signal.addEventListener('abort', () => { server.close() })
1093
+ this.param.signal.addEventListener('abort', () => { server.close() })
1028
1094
  }
1029
-
1030
- /** @type{TUNNEL_TCP_CLIENT_HELPER} */
1031
- let helper = { readable: encode.readable, writable: decode.writable, reader: null, writer: null, listen, connect, param: outParam }
1032
- return helper
1033
-
1034
1095
  }
1035
1096
 
1036
1097
 
@@ -1351,6 +1412,9 @@ export function createTunnelTcpClientHttp(param) {
1351
1412
  let socketWriter = null
1352
1413
  let bufferedTransform = createTimeBufferedTransformStream(50)
1353
1414
  helper.readable.pipeThrough(bufferedTransform).pipeTo(new WritableStream({
1415
+ /**
1416
+ * @param {Uint8Array<ArrayBufferLike>} chunk
1417
+ */
1354
1418
  async write(chunk) {
1355
1419
  while (!param.signal.aborted && socketWriter == null) {
1356
1420
  await signal.promise
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
 
@@ -509,4 +509,144 @@ test('test-connect', { skip: SKIP_MANAUAL_TEST }, async () => {
509
509
 
510
510
  await sleep(10000_000)
511
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!')
512
652
  })
package/src/types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import net from 'node:net'
2
- import { WritableStream } from "stream/web"
2
+ import { WritableStream, ReadableStream, ReadableStreamDefaultReader, WritableStreamDefaultWriter } from "stream/web"
3
3
 
4
4
 
5
5
  export type PromiseResolvers = {
@@ -38,7 +38,7 @@ export type TCP_TUNNEL_DATA = {
38
38
  dstId: number
39
39
  srcChannel: number
40
40
  dstChannel: number
41
- buffer: Uint8Array
41
+ buffer: Uint8Array<ArrayBuffer>
42
42
  }
43
43
 
44
44
  export type TUNNEL_TCP_DATA_LISTEN = {
@@ -58,36 +58,21 @@ export type TUNNEL_TCP_SERVER = {
58
58
  encodeWriter: WritableStreamDefaultWriter<Uint8Array>
59
59
  }
60
60
 
61
- export type TUNNEL_TCP_SERVER_HELPER = {
62
- readable: ReadableStream<Uint8Array>
63
- writable: WritableStream<Uint8Array>
64
- reader: ReadableStreamDefaultReader<Uint8Array>
65
- writer: WritableStreamDefaultWriter<Uint8Array>
66
- dstId: number
61
+ export type ListenParam = {
62
+ clientKey?: string;
63
+ tunnelKey: string;
64
+ host?: string;
65
+ port: number;
67
66
  }
68
67
 
69
- export type TUNNEL_TCP_CLIENT_HELPER = {
70
- readable: ReadableStream<Uint8Array>
71
- writable: WritableStream<Uint8Array>
72
- reader: ReadableStreamDefaultReader<Uint8Array>
73
- writer: WritableStreamDefaultWriter<Uint8Array>
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>
68
+ export type ConnectParam = {
69
+ clientKey?: string;
70
+ tunnelKey: string;
71
+ port: number;
86
72
  }
87
73
 
88
-
89
74
  export type SocketChannel = {
90
- writer: WritableStreamDefaultWriter<Uint8Array>
75
+ writer: WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>
91
76
  socket: net.Socket
92
77
  srcId: number
93
78
  dstId: number
@@ -95,7 +80,7 @@ export type SocketChannel = {
95
80
  dstChannel: number
96
81
  notify: (size: number) => void
97
82
  recvPackSize: number
98
- key_iv: [CryptoKey, Uint8Array]
83
+ key_iv: [CryptoKey, Uint8Array<ArrayBuffer>]
99
84
  }
100
85
 
101
86