js-tcp-tunnel 1.1.0 → 1.2.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.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "js tcp tunnel",
5
5
  "main": "index.js",
6
6
  "type": "module",
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
11
  * @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'
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
@@ -307,7 +308,6 @@ export function printTcpTunnelData(data) {
307
308
  [TUNNEL_TCP_TYPE_CLOSE]: 'TUNNEL_TCP_TYPE_CLOSE ',
308
309
  [TUNNEL_TCP_TYPE_PING]: 'TUNNEL_TCP_TYPE_PING ',
309
310
  [TUNNEL_TCP_TYPE_PONG]: 'TUNNEL_TCP_TYPE_PONG ',
310
- [TUNNEL_TCP_TYPE_ACK]: 'TUNNEL_TCP_TYPE_ACK ',
311
311
  }[data.type]} recv: ${(tcpTunnelDataRecv)} send: ${(tcpTunnelDataSend)} size:${data.buffer.length}`
312
312
  }
313
313
 
@@ -420,7 +420,6 @@ export const TUNNEL_TCP_TYPE_ERROR = 0x8117f762
420
420
  export const TUNNEL_TCP_TYPE_CLOSE = 0x72fd6470
421
421
  export const TUNNEL_TCP_TYPE_PING = 0x4768e1ba
422
422
  export const TUNNEL_TCP_TYPE_PONG = 0x106f43fb
423
- export const TUNNEL_TCP_TYPE_ACK = 0xc5870539
424
423
 
425
424
  /**
426
425
  * @param {TCP_TUNNEL_DATA} box
@@ -531,46 +530,15 @@ export function createTimeBufferedTransformStream(bufferTime) {
531
530
  export function pipeSocketDataWithChannel(channelMap, channelId, encodeWriter) {
532
531
  let channel = channelMap.get(channelId)
533
532
  let socket = channel.socket
534
- let signal = Promise_withResolvers()
535
- signal.resolve()
536
533
  let sendPackSize = 0
537
- let remoteRecvPackSize = 0
538
- channel.notify = (size) => {
539
- remoteRecvPackSize = size
540
- signal.resolve()
541
- }
542
534
  let [clientKey, clientIv] = channel.key_iv
543
535
  let bufferedTransform = createTimeBufferedTransformStream(50)
544
- let backPressureTimer = null
545
536
  Readable.toWeb(socket).pipeThrough(bufferedTransform).pipeTo(new WritableStream({
546
537
  /**
547
538
  * @param {Uint8Array<ArrayBuffer>} chunk
548
539
  */
549
540
  async write(chunk) {
550
541
  const buffer = await encrypt(chunk, clientKey, clientIv)
551
- let bufferPackSize = sendPackSize - remoteRecvPackSize
552
- if (DEBUG_TUNNEL_TCP) {
553
- console.warn('bufferPackSize:', bufferPackSize)
554
- }
555
- if (bufferPackSize > 10) {
556
- signal.resolve()
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()
565
- if (DEBUG_TUNNEL_TCP) {
566
- console.info('stop wait signal', ' sendPackSize:', sendPackSize, ' recvPackSize:', remoteRecvPackSize, ' bufferPackSize:', bufferPackSize)
567
- }
568
- }
569
- await signal.promise
570
- if (backPressureTimer) {
571
- clearTimeout(backPressureTimer)
572
- backPressureTimer = null
573
- }
574
542
  await encodeWriter.write(buildTcpTunnelData({
575
543
  type: TUNNEL_TCP_TYPE_DATA,
576
544
  srcId: channel.srcId,
@@ -710,7 +678,6 @@ async function dispatchClientBufferData(param, setup, listenKeyParamMap, channel
710
678
  dstChannel: data.srcChannel,
711
679
  recvPackSize: 0,
712
680
  key_iv,
713
- notify: null,
714
681
  }
715
682
  channelMap.set(channelId, channel)
716
683
  connectSocket.on('connect', () => {
@@ -805,43 +772,10 @@ async function dispatchClientBufferData(param, setup, listenKeyParamMap, channel
805
772
  await channel.writer.write(buffer)
806
773
  }
807
774
  channel.recvPackSize++
808
- await sendAck(encodeWriter, channel)
809
775
  } else {
810
776
  await closeRemoteChannel(encodeWriter, data)
811
777
  }
812
778
  }
813
- if (data.type == TUNNEL_TCP_TYPE_ACK) {
814
- let channelId = data.dstChannel
815
- let channel = channelMap.get(channelId)
816
- if (channel) {
817
- channel.srcId = data.dstId
818
- channel.dstId = data.srcId
819
- let size = readUInt32LE(data.buffer, 0)
820
- channel.notify(size)
821
- } else {
822
- await closeRemoteChannel(encodeWriter, data)
823
- }
824
- }
825
- }
826
-
827
- /**
828
- * @param {WritableStreamDefaultWriter<Uint8Array>} encodeWriter
829
- * @param {SocketChannel} channel
830
- */
831
- export async function sendAck(encodeWriter, channel) {
832
- if (channel.recvPackSize % 5 != 0) {
833
- return
834
- }
835
- let sizeBuffer = new Uint8Array(4)
836
- writeUInt32LE(sizeBuffer, channel.recvPackSize, 0)
837
- await encodeWriter.write(buildTcpTunnelData({
838
- type: TUNNEL_TCP_TYPE_ACK,
839
- srcId: channel.srcId,
840
- srcChannel: channel.srcChannel,
841
- dstId: channel.dstId,
842
- dstChannel: channel.dstChannel,
843
- buffer: sizeBuffer,
844
- }))
845
779
  }
846
780
 
847
781
  /**
@@ -871,6 +805,9 @@ export function createTunnelTcpServerHelper(param) {
871
805
  if (DEBUG_TUNNEL_TCP) {
872
806
  let writer = encodeWriter
873
807
  encodeWriter = new WritableStream({
808
+ /**
809
+ * @param {Uint8Array<ArrayBuffer>} chunk
810
+ */
874
811
  async write(chunk) {
875
812
  tcpTunnelDataSend += chunk.length
876
813
  let data = parseTcpTunnelData(chunk)
@@ -881,6 +818,9 @@ export function createTunnelTcpServerHelper(param) {
881
818
  }
882
819
 
883
820
  decode.readable.pipeTo(new WritableStream({
821
+ /**
822
+ * @param {Uint8Array<ArrayBuffer>} chunk
823
+ */
884
824
  async write(chunk) {
885
825
  try {
886
826
  await dispatchServerBufferData(param, encodeWriter, chunk)
@@ -901,66 +841,115 @@ export function createTunnelTcpServerHelper(param) {
901
841
  buffer: new Uint8Array(0),
902
842
  }))
903
843
 
904
- /** @type{TUNNEL_TCP_SERVER_HELPER} */
905
- let helper = { readable: encode.readable, writable: decode.writable, reader: null, writer: null, dstId: id, }
906
- return helper
844
+ return new TunnelTcpServerHelper(id, encode, decode)
907
845
  }
908
846
 
909
847
  /**
910
848
  * @param {TunnelTcpClientHelperParam} param
911
849
  */
912
850
  export function createTunnelTcpClientHelper(param) {
913
- /** @type{Map<number,SocketChannel>} */
914
- let channelMap = new Map()
915
-
916
- /** @type{Map<string,{host:string;port:number;key_iv:[CryptoKey, Uint8Array<ArrayBuffer>]}>} */
917
- let listenKeyParamMap = new Map()
918
-
919
851
  let server_key_iv = buildKeyIv(param.serverKey, 10)
920
-
921
- param.signal.addEventListener('abort', () => {
922
- channelMap.values().forEach(o => {
923
- o.socket.destroy()
924
- })
925
- })
926
-
927
852
  let encode = createEncodeStream(server_key_iv)
928
853
  let decode = createDecodeStream(server_key_iv)
929
- let encodeWriter = encode.writable.getWriter()
930
- if (DEBUG_TUNNEL_TCP) {
931
- let writer = encodeWriter
932
- encodeWriter = new WritableStream({
933
- async write(chunk) {
934
- tcpTunnelDataSend += chunk.length
935
- let data = parseTcpTunnelData(chunk)
936
- console.info('send', printTcpTunnelData(data))
937
- writer.write(chunk)
938
- }
939
- }).getWriter()
854
+ return new TunnelTcpClientHelper(param, encode, decode)
855
+ }
856
+
857
+ export class TunnelTcpServerHelper {
858
+ dstId = 0
859
+ /** @type{ReadableStream<Uint8Array<ArrayBuffer>>} */
860
+ readable = null
861
+ /** @type{WritableStream<Uint8Array<ArrayBuffer>>} */
862
+ writable = null
863
+ /** @type{ReadableStreamDefaultReader<Uint8Array<ArrayBuffer>>} */
864
+ reader = null
865
+ /** @type{WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>} */
866
+ writer = null
867
+ /**
868
+ * @param {number} id
869
+ * @param {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>} encode
870
+ * @param {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>} decode
871
+ */
872
+ constructor(id, encode, decode) {
873
+ this.dstId = id
874
+ this.readable = encode.readable
875
+ this.writable = decode.writable
940
876
  }
877
+ }
941
878
 
942
- decode.readable.pipeTo(new WritableStream({
943
- /**
944
- * @param {Uint8Array<ArrayBuffer>} buffer
945
- */
946
- async write(buffer) {
947
- try {
948
- await dispatchClientBufferData(param, setup, listenKeyParamMap, channelMap, encodeWriter, buffer)
949
- } catch (error) {
950
- console.error('decode.readable.pipeTo.write', error.message)
951
- }
879
+ export class TunnelTcpClientHelper {
880
+ /** @type{ReadableStream<Uint8Array<ArrayBuffer>>} */
881
+ readable = null
882
+ /** @type{WritableStream<Uint8Array<ArrayBuffer>>} */
883
+ writable = null
884
+ /** @type{ReadableStreamDefaultReader<Uint8Array<ArrayBuffer>>} */
885
+ reader = null
886
+ /** @type{WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>} */
887
+ writer = null
888
+ /** @type{TunnelTcpClientHelperParam} */
889
+ param = null
890
+
891
+ /** @type{Map<number,SocketChannel>} */
892
+ channelMap = new Map()
893
+ /** @type{Map<string,{host:string;port:number;key_iv:[CryptoKey, Uint8Array<ArrayBuffer>]}>} */
894
+ listenKeyParamMap = new Map()
895
+ /** @type{Set<ListenParam>} */
896
+ listenParams = new Set()
897
+ /** @type{WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>} */
898
+ encodeWriter = null
899
+
900
+ /**
901
+ * @param {TunnelTcpClientHelperParam} param
902
+ * @param {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>} encode
903
+ * @param {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>} decode
904
+ */
905
+ constructor(param, encode, decode) {
906
+ this.param = param
907
+ this.readable = encode.readable
908
+ this.writable = decode.writable
909
+
910
+ param.signal.addEventListener('abort', () => {
911
+ this.channelMap.values().forEach(o => {
912
+ o.socket.destroy()
913
+ })
914
+ })
915
+
916
+ this.encodeWriter = encode.writable.getWriter()
917
+ if (DEBUG_TUNNEL_TCP) {
918
+ let writer = this.encodeWriter
919
+ this.encodeWriter = new WritableStream({
920
+ async write(chunk) {
921
+ tcpTunnelDataSend += chunk.length
922
+ let data = parseTcpTunnelData(chunk)
923
+ console.info('send', printTcpTunnelData(data))
924
+ writer.write(chunk)
925
+ }
926
+ }).getWriter()
952
927
  }
953
- }))
954
928
 
955
- let outParam = param
956
- let listenParams = new Set()
929
+ const thiz = this
930
+ decode.readable.pipeTo(new WritableStream({
931
+ /**
932
+ * @param {Uint8Array<ArrayBuffer>} buffer
933
+ */
934
+ async write(buffer) {
935
+ try {
936
+ await Promise.race([
937
+ sleep(5000),
938
+ dispatchClientBufferData(param, () => thiz.setup(), thiz.listenKeyParamMap, thiz.channelMap, thiz.encodeWriter, buffer),
939
+ ])
940
+ } catch (error) {
941
+ console.error('decode.readable.pipeTo.write', error.message)
942
+ }
943
+ }
944
+ }))
945
+ }
957
946
 
958
- async function setup() {
959
- channelMap.forEach((channel) => {
960
- channel.srcId = param.clientDataId
947
+ async setup() {
948
+ this.channelMap.forEach((channel) => {
949
+ channel.srcId = this.param.clientDataId
961
950
  /** @type{TUNNEL_TCP_DATA_PINGPONG} */
962
951
  let pingData = { time: Date.now() }
963
- encodeWriter.write(buildTcpTunnelData({
952
+ this.encodeWriter.write(buildTcpTunnelData({
964
953
  type: TUNNEL_TCP_TYPE_PING,
965
954
  srcId: channel.srcId,
966
955
  srcChannel: channel.srcChannel,
@@ -969,34 +958,29 @@ export function createTunnelTcpClientHelper(param) {
969
958
  buffer: Uint8Array_from(JSON.stringify(pingData)),
970
959
  }))
971
960
  })
972
- for (const param of listenParams) {
973
- await listen(param)
961
+ for (const param of this.listenParams) {
962
+ await this.listen(param)
974
963
  }
975
964
  }
976
965
 
977
966
  /**
978
- * @param {{
979
- * clientKey?:string;
980
- * tunnelKey:string;
981
- * host?:string;
982
- * port:number;
983
- * }} param
967
+ * @param {ListenParam} param
984
968
  */
985
- async function listen(param) {
986
- listenParams.add(param)
987
- console.info('listenParams size', listenParams.size)
988
- if (outParam.clientDataId < 1) {
969
+ async listen(param) {
970
+ this.listenParams.add(param)
971
+ console.info('listenParams size', this.listenParams.size)
972
+ if (this.param.clientDataId < 1) {
989
973
  console.info('skip send listen dataId == 0')
990
974
  return
991
975
  }
992
976
  let key = sha512(param.tunnelKey)
993
977
  let key_iv = await buildKeyIv(param.clientKey, 10)
994
- listenKeyParamMap.set(key, { host: param.host, port: param.port, key_iv })
978
+ this.listenKeyParamMap.set(key, { host: param.host, port: param.port, key_iv })
995
979
  /** @type{TUNNEL_TCP_DATA_LISTEN} */
996
980
  let listenData = { key: key }
997
- await encodeWriter.write(buildTcpTunnelData({
981
+ await this.encodeWriter.write(buildTcpTunnelData({
998
982
  type: TUNNEL_TCP_TYPE_LISTEN,
999
- srcId: outParam.clientDataId,
983
+ srcId: this.param.clientDataId,
1000
984
  srcChannel: 0,
1001
985
  dstId: 0,
1002
986
  dstChannel: 0,
@@ -1005,19 +989,15 @@ export function createTunnelTcpClientHelper(param) {
1005
989
  }
1006
990
 
1007
991
  /**
1008
- * @param {{
1009
- * clientKey?:string;
1010
- * tunnelKey: string;
1011
- * port: number;
1012
- * }} param
992
+ * @param {ConnectParam} param
1013
993
  */
1014
- async function connect(param) {
994
+ async connect(param) {
1015
995
  let key_iv = await buildKeyIv(param.clientKey, 10)
1016
996
  let server = net.createServer((socket) => {
1017
- let channelId = outParam.uniqueId++
997
+ let channelId = this.param.uniqueId++
1018
998
  socket.on('error', (err) => {
1019
999
  console.error('createTunnelTcpClientHelper on socket error', err.message)
1020
- channelMap.delete(channelId)
1000
+ this.channelMap.delete(channelId)
1021
1001
  })
1022
1002
  /** @type{TUNNEL_TCP_DATA_CONNECT} */
1023
1003
  let connectData = { key: sha512(param.tunnelKey) }
@@ -1025,16 +1005,15 @@ export function createTunnelTcpClientHelper(param) {
1025
1005
  let channel = {
1026
1006
  writer: Writable.toWeb(socket).getWriter(),
1027
1007
  socket,
1028
- srcId: outParam.clientDataId,
1008
+ srcId: this.param.clientDataId,
1029
1009
  srcChannel: channelId,
1030
1010
  dstId: 0,
1031
1011
  dstChannel: 0,
1032
1012
  recvPackSize: 0,
1033
1013
  key_iv,
1034
- notify: null,
1035
1014
  }
1036
- channelMap.set(channelId, channel)
1037
- encodeWriter.write(buildTcpTunnelData({
1015
+ this.channelMap.set(channelId, channel)
1016
+ this.encodeWriter.write(buildTcpTunnelData({
1038
1017
  type: TUNNEL_TCP_TYPE_CONNECT,
1039
1018
  srcId: channel.srcId,
1040
1019
  srcChannel: channel.srcChannel,
@@ -1046,13 +1025,8 @@ export function createTunnelTcpClientHelper(param) {
1046
1025
  server.on('error', (err) => {
1047
1026
  console.error('createTunnelTcpClientHelper connect on server error', err.message)
1048
1027
  })
1049
- outParam.signal.addEventListener('abort', () => { server.close() })
1028
+ this.param.signal.addEventListener('abort', () => { server.close() })
1050
1029
  }
1051
-
1052
- /** @type{TUNNEL_TCP_CLIENT_HELPER} */
1053
- let helper = { readable: encode.readable, writable: decode.writable, reader: null, writer: null, listen, connect, param: outParam }
1054
- return helper
1055
-
1056
1030
  }
1057
1031
 
1058
1032
 
@@ -1246,7 +1220,10 @@ export function createTunnelTcpClientWebSocket(param) {
1246
1220
  await signal.promise
1247
1221
  }
1248
1222
  if (!param.signal.aborted) {
1249
- await socketWriter.write(chunk)
1223
+ await Promise.race([
1224
+ sleep(5000),
1225
+ socketWriter.write(chunk),
1226
+ ])
1250
1227
  }
1251
1228
  }
1252
1229
  }))
package/src/lib.test.js CHANGED
@@ -645,8 +645,8 @@ test('terminal-server', async () => {
645
645
 
646
646
  await sleep(20_000)
647
647
 
648
- strictEqual(socketCreateCount, 2)
649
- strictEqual(recvCount, 14)
648
+ strictEqual(socketCreateCount, 1)
649
+ strictEqual(recvCount, 25)
650
650
 
651
651
  console.info('over!')
652
652
  })
package/src/types.ts CHANGED
@@ -58,34 +58,19 @@ 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<ArrayBuffer>>
63
- writable: WritableStream<Uint8Array<ArrayBuffer>>
64
- reader: ReadableStreamDefaultReader<Uint8Array<ArrayBuffer>>
65
- writer: WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>
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<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>
68
+ export type ConnectParam = {
69
+ clientKey?: string;
70
+ tunnelKey: string;
71
+ port: number;
86
72
  }
87
73
 
88
-
89
74
  export type SocketChannel = {
90
75
  writer: WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>
91
76
  socket: net.Socket
@@ -93,7 +78,6 @@ export type SocketChannel = {
93
78
  dstId: number
94
79
  srcChannel: number
95
80
  dstChannel: number
96
- notify: (size: number) => void
97
81
  recvPackSize: number
98
82
  key_iv: [CryptoKey, Uint8Array<ArrayBuffer>]
99
83
  }