js-tcp-tunnel 1.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.
@@ -0,0 +1,511 @@
1
+ import { test } from 'node:test'
2
+ import { ok, strictEqual } from 'node:assert'
3
+ import { createTunnelTcpClientHttp, createTunnelTcpClientSocket, createTunnelTcpClientWebSocket, createTunnelTcpServerKoaRouter, createTunnelTcpServerSocket, createTunnelTcpServerWebSocket, formatSize, runWithAbortController, sleep, Uint8Array_toString } from './lib.js'
4
+ import net from 'net'
5
+ import http from 'http'
6
+ import { Readable, Transform } from 'node:stream'
7
+ import Koa from 'koa'
8
+ import Router from 'koa-router'
9
+ import { WebSocketServer } from 'ws'
10
+ import log4js from 'log4js'
11
+
12
+ log4js.configure({
13
+ appenders: { stdout: { type: "stdout", layout: { type: 'pattern', pattern: '[%d{yyyy-MM-dd hh:mm:ss,SSS}] %[%p %m%] %f{2}:%l:%o' } } },
14
+ categories: { default: { appenders: ["stdout"], level: "debug", enableCallStack: true } },
15
+ })
16
+
17
+ /** @type{*} */
18
+ const _log4js_ = log4js.getLogger()
19
+ _log4js_['table'] = globalThis.console.table
20
+ globalThis.console = _log4js_
21
+ /** @type{Omit<Console,'log'>} */
22
+ const console = _log4js_
23
+
24
+ test('tunnel-tcp-socket-test', async () => {
25
+ // node --test-name-pattern="^tunnel-tcp-socket-test$" src/lib.test.js
26
+
27
+ await runWithAbortController(async ac => {
28
+
29
+ console.info('创建socket服务')
30
+ let socketServer = net.createServer((socket) => {
31
+ socket.pipe(new Transform({
32
+ transform(chunk, _, callback) {
33
+ // console.info('transform chunk', chunk, Uint8Array_toString(chunk))
34
+ this.push(chunk)
35
+ callback()
36
+ }
37
+ })).pipe(socket)
38
+ socket.on('error', (err) => {
39
+ console.error(err.message)
40
+ })
41
+ }).listen(9006)
42
+ socketServer.on('error', (e) => { console.error(e.message) })
43
+ await sleep(100)
44
+
45
+ console.info('创建监听服务')
46
+ let connection1 = createTunnelTcpClientSocket({ signal: ac.signal, serverKey: '2934c57f790f9e99a52a121802df231c', serverHost: '127.0.0.1', serverPort: 9005, })
47
+ connection1.listen({ host: '127.0.0.1', port: 9006, tunnelKey: 'b0f5014acad2060d6bd3730a1721c97f', })
48
+ await sleep(100)
49
+
50
+ console.info('创建连接服务')
51
+ let connection2 = createTunnelTcpClientSocket({ signal: ac.signal, serverKey: '2934c57f790f9e99a52a121802df231c', serverHost: '127.0.0.1', serverPort: 9005, })
52
+ connection2.connect({ port: 9007, tunnelKey: 'b0f5014acad2060d6bd3730a1721c97f', })
53
+ await sleep(100)
54
+
55
+ console.info('创建转发服务')
56
+ createTunnelTcpServerSocket({ signal: ac.signal, serverKey: '2934c57f790f9e99a52a121802df231c', port: 9005, })
57
+ await sleep(1000)
58
+
59
+ console.info('tcp透传测试')
60
+ let socket = net.createConnection({ host: '127.0.0.1', port: 9007 })
61
+ socket.on('error', (err) => {
62
+ console.error(err.message)
63
+ })
64
+
65
+ let rec = null
66
+
67
+ await new Promise((resolve) => {
68
+ socket.on('connect', async () => {
69
+ socket.on('data', (chunk) => {
70
+ // console.info('receive chunk ', chunk, Uint8Array_toString(chunk))
71
+ rec = Uint8Array_toString(chunk)
72
+ })
73
+ for (let i = 0; i < 100; i++) {
74
+ // console.info('send','qwertyuiop - ' + i)
75
+ socket.write('qwertyuiop - ' + i)
76
+ // await sleep(10)
77
+ await sleep(1)
78
+ }
79
+ await sleep(2000)
80
+ resolve()
81
+ })
82
+ })
83
+
84
+ ac.signal.addEventListener('abort', () => {
85
+ socket.destroy()
86
+ socketServer.close()
87
+ })
88
+
89
+ strictEqual(rec, 'qwertyuiop - 99')
90
+
91
+ })
92
+ console.info('over!')
93
+ })
94
+
95
+ test('tunnel-tcp-socket-test-websocket', async () => {
96
+ // node --test-name-pattern="^tunnel-tcp-socket-test-websocket$" src/lib.test.js
97
+
98
+ await runWithAbortController(async ac => {
99
+
100
+ console.info('创建socket服务')
101
+ let socketServer = net.createServer((socket) => {
102
+ socket.pipe(new Transform({
103
+ transform(chunk, _, callback) {
104
+ // console.info('transform chunk', chunk, Uint8Array_toString(chunk))
105
+ this.push(chunk)
106
+ callback()
107
+ }
108
+ })).pipe(socket)
109
+ socket.on('error', (err) => {
110
+ console.error(err.message)
111
+ })
112
+ }).listen(9016)
113
+ socketServer.on('error', (e) => { console.error(e.message) })
114
+ await sleep(100)
115
+
116
+ console.info('创建监听服务')
117
+ let connection1 = createTunnelTcpClientWebSocket({ signal: ac.signal, serverKey: '2934c57f790f9e99a52a121802df231c', url: 'ws://127.0.0.1:9015/tunnel/ae145dce31bfa94f0c837749320030bb', })
118
+ connection1.listen({ host: '127.0.0.1', port: 9016, tunnelKey: 'b0f5014acad2060d6bd3730a1721c97f', })
119
+ await sleep(100)
120
+
121
+ console.info('创建连接服务')
122
+ let connection2 = createTunnelTcpClientWebSocket({ signal: ac.signal, serverKey: '2934c57f790f9e99a52a121802df231c', url: 'ws://127.0.0.1:9015/tunnel/ae145dce31bfa94f0c837749320030bb', })
123
+ connection2.connect({ port: 9017, tunnelKey: 'b0f5014acad2060d6bd3730a1721c97f', })
124
+ await sleep(100)
125
+
126
+ console.info('创建转发服务')
127
+ let server = http.createServer().listen(9015)
128
+ server.on('error', (e) => { console.error(e.message) })
129
+ let wss = new WebSocketServer({ server })
130
+ ac.signal.addEventListener('abort', () => server.close())
131
+ createTunnelTcpServerWebSocket({ signal: ac.signal, serverKey: '2934c57f790f9e99a52a121802df231c', wss, path: '/tunnel/ae145dce31bfa94f0c837749320030bb' })
132
+ await sleep(1000)
133
+
134
+ console.info('tcp透传测试')
135
+ let socket = net.createConnection({ host: '127.0.0.1', port: 9017 })
136
+ socket.on('error', (err) => {
137
+ console.error(err.message)
138
+ })
139
+
140
+ let rec = null
141
+
142
+ await new Promise((resolve) => {
143
+ socket.on('connect', async () => {
144
+ socket.on('data', (chunk) => {
145
+ // console.info('receive chunk ', chunk, Uint8Array_toString(chunk))
146
+ rec = Uint8Array_toString(chunk)
147
+ })
148
+ for (let i = 0; i < 100; i++) {
149
+ socket.write('qwertyuiop - ' + i)
150
+ await sleep(10)
151
+ }
152
+ await sleep(100)
153
+ resolve()
154
+ })
155
+ })
156
+
157
+ ac.signal.addEventListener('abort', () => {
158
+ socket.destroy()
159
+ socketServer.close()
160
+ })
161
+
162
+ strictEqual(rec, 'qwertyuiop - 99')
163
+
164
+ })
165
+ console.info('over!')
166
+ })
167
+
168
+ test('tunnel-tcp-socket-test-http', async () => {
169
+ // node --test-name-pattern="^tunnel-tcp-socket-test-http$" src/lib.test.js
170
+
171
+ await runWithAbortController(async ac => {
172
+
173
+ console.info('创建socket服务')
174
+ let socketServer = net.createServer((socket) => {
175
+ socket.pipe(new Transform({
176
+ transform(chunk, _, callback) {
177
+ // console.info('transform chunk', chunk, Uint8Array_toString(chunk))
178
+ this.push(chunk)
179
+ callback()
180
+ }
181
+ })).pipe(socket)
182
+ socket.on('error', (err) => {
183
+ console.error(err.message)
184
+ })
185
+ }).listen(9036)
186
+ socketServer.on('error', (e) => { console.error(e.message) })
187
+ await sleep(100)
188
+
189
+ console.info('创建监听服务')
190
+ let connection1 = createTunnelTcpClientHttp({ signal: ac.signal, serverKey: '2934c57f790f9e99a52a121802df231c', url: 'http://127.0.0.1:9035/tunnel/0f7c5b2c9080eaa9e4d6139126daac04', })
191
+ connection1.listen({ host: '127.0.0.1', port: 9036, tunnelKey: 'b0f5014acad2060d6bd3730a1721c97f', })
192
+ await sleep(100)
193
+
194
+ console.info('创建连接服务')
195
+ let connection2 = createTunnelTcpClientHttp({ signal: ac.signal, serverKey: '2934c57f790f9e99a52a121802df231c', url: 'http://127.0.0.1:9035/tunnel/0f7c5b2c9080eaa9e4d6139126daac04', })
196
+ connection2.connect({ port: 9037, tunnelKey: 'b0f5014acad2060d6bd3730a1721c97f', })
197
+ await sleep(100)
198
+
199
+ console.info('创建转发服务')
200
+ let app = new Koa()
201
+ let router = new Router()
202
+ createTunnelTcpServerKoaRouter({ signal: ac.signal, serverKey: '2934c57f790f9e99a52a121802df231c', router: router, path: '/tunnel/0f7c5b2c9080eaa9e4d6139126daac04', })
203
+ app.use(router.routes())
204
+ app.use(router.allowedMethods())
205
+ app.onerror = (err) => console.info(err.message)
206
+ let koaServer = http.createServer(app.callback())
207
+ koaServer.listen(9035)
208
+ koaServer.on('error', (e) => { console.error(e.message) })
209
+ ac.signal.addEventListener('abort', () => { koaServer.close() })
210
+ await sleep(1000)
211
+
212
+ console.info('tcp透传测试')
213
+ let socket = net.createConnection({ host: '127.0.0.1', port: 9037 })
214
+ socket.on('error', (err) => {
215
+ console.error(err.message)
216
+ })
217
+
218
+ let rec = null
219
+
220
+ await new Promise((resolve) => {
221
+ socket.on('connect', async () => {
222
+ socket.on('data', (chunk) => {
223
+ // console.info('receive chunk ', chunk, Uint8Array_toString(chunk))
224
+ rec = Uint8Array_toString(chunk)
225
+ })
226
+ for (let i = 0; i < 100; i++) {
227
+ socket.write('qwertyuiop - ' + i)
228
+ await sleep(10)
229
+ }
230
+ await sleep(100)
231
+ resolve()
232
+ })
233
+ })
234
+
235
+ ac.signal.addEventListener('abort', () => {
236
+ socket.destroy()
237
+ socketServer.close()
238
+ })
239
+
240
+ strictEqual(rec, 'qwertyuiop - 99')
241
+
242
+ })
243
+ console.info('over!')
244
+ })
245
+
246
+ test('backpressure-socket', async () => {
247
+ // node --test-name-pattern="^backpressure-socket$" src/lib.test.js
248
+
249
+ await runWithAbortController(async ac => {
250
+
251
+ console.info('创建socket服务')
252
+ let transformSize = 0
253
+ let socketServer = net.createServer((socket) => {
254
+ socket.pipe(new Transform({
255
+ transform(chunk, _, callback) {
256
+ // console.info('transform chunk', chunk.length)
257
+ transformSize += chunk.length
258
+ this.push(chunk)
259
+ callback()
260
+ }
261
+ })).pipe(socket).on('error', (err) => console.error(err.message))
262
+ }).listen(9036)
263
+ await sleep(100)
264
+ socketServer.on('error', (err) => {
265
+ console.error(err.message)
266
+ })
267
+
268
+ console.info('创建转发服务 http')
269
+ let app = new Koa()
270
+ let router = new Router()
271
+ createTunnelTcpServerKoaRouter({
272
+ signal: ac.signal,
273
+ router: router,
274
+ path: '/tunnel/0f7c5b2c9080eaa9e4d6139126daac04',
275
+ serverKey: '2934c57f790f9e99a52a121802df231c',
276
+ })
277
+ app.use(router.routes())
278
+ app.use(router.allowedMethods())
279
+ app.onerror = (err) => console.info(err.message)
280
+ let koaServer = http.createServer(app.callback())
281
+ koaServer.listen(9035)
282
+ koaServer.on('error', (e) => { console.error(e.message) })
283
+ ac.signal.addEventListener('abort', () => { koaServer.close() })
284
+ await sleep(1000)
285
+
286
+ console.info('创建监听服务 http')
287
+ let connection1 = createTunnelTcpClientHttp({
288
+ url: 'http://127.0.0.1:9035/tunnel/0f7c5b2c9080eaa9e4d6139126daac04',
289
+ signal: ac.signal,
290
+ serverKey: '2934c57f790f9e99a52a121802df231c',
291
+ })
292
+ connection1.listen({
293
+ clientKey: 'mmm',
294
+ tunnelKey: 'b0f5014acad2060d6bd3730a1721c97f',
295
+ host: '127.0.0.1',
296
+ port: 9036,
297
+ })
298
+ await sleep(100)
299
+
300
+ console.info('创建连接服务 http 1')
301
+ let connection2 = createTunnelTcpClientHttp({
302
+ url: 'http://127.0.0.1:9035/tunnel/0f7c5b2c9080eaa9e4d6139126daac04',
303
+ signal: ac.signal,
304
+ serverKey: '2934c57f790f9e99a52a121802df231c',
305
+ })
306
+ connection2.connect({
307
+ clientKey: 'mmm',
308
+ tunnelKey: 'b0f5014acad2060d6bd3730a1721c97f',
309
+ port: 9037,
310
+ })
311
+
312
+ await sleep(1000)
313
+
314
+ console.info('tcp透传测试 start')
315
+ let socket1 = net.createConnection({ host: '127.0.0.1', port: 9037 })
316
+ socket1.on('error', (err) => { console.error(err.message) })
317
+
318
+ let sendWriteSize = 0
319
+ let sendSize = 0
320
+ let receiveSize = 0
321
+ let stop = false
322
+ await new Promise((resolve) => {
323
+ setTimeout(() => {
324
+ stop = true
325
+ resolve()
326
+ }, 5_000)
327
+ socket1.on('connect', async () => {
328
+ socket1.pipe(new Transform({
329
+ async transform(chunk, _, callback) {
330
+ receiveSize += chunk.length
331
+ await sleep(500)
332
+ callback()
333
+ }
334
+ })).on('data', (chunk) => {
335
+ // console.info('1 receive chunk ', chunk, Uint8Array_toString(chunk))
336
+ })
337
+ // let data = new Uint8Array(1024 * 1024)
338
+ let data = new Uint8Array(1024)
339
+ // let data = Uint8Array_from('1234567890')
340
+ let readable = new Readable({
341
+ read() {
342
+ if (stop) {
343
+ this.push(null)
344
+ readable.destroy()
345
+ return
346
+ }
347
+ // data = Uint8Array_from('1234567890')
348
+ sendSize += data.length
349
+ this.push(data)
350
+ // stop = true
351
+ }
352
+ })
353
+ readable.on('data', (chunk) => {
354
+ sendWriteSize += chunk.length
355
+ if (!socket1.write(chunk)) {
356
+ readable.pause()
357
+ }
358
+ })
359
+ socket1.on('drain', () => {
360
+ readable.resume()
361
+ })
362
+ // readable.pipe(socket1)
363
+ })
364
+ })
365
+
366
+ console.info(`${'\n'.repeat(3)}sendSize:${formatSize(sendSize)} sendWriteSize:${formatSize(sendWriteSize)} receiveSize:${formatSize(receiveSize)} transformSize:${formatSize(transformSize)}`)
367
+
368
+ console.info('tcp透传测试 finish sendSize:', sendSize)
369
+
370
+ ac.signal.addEventListener('abort', () => {
371
+ socketServer.close()
372
+ socket1.destroy()
373
+ })
374
+
375
+ ok(receiveSize > 640_000)
376
+ ok(sendSize < 45000000)
377
+
378
+ })
379
+ console.info('over!')
380
+ })
381
+
382
+ // 跳过手动测试
383
+ const SKIP_MANAUAL_TEST = true
384
+
385
+ test('test-server', { skip: SKIP_MANAUAL_TEST }, async () => {
386
+ // node --test-name-pattern="^test-server$" src/lib.test.js
387
+ await runWithAbortController(async ac => {
388
+ console.info('创建转发服务 http')
389
+ let app = new Koa()
390
+ let router = new Router()
391
+ createTunnelTcpServerKoaRouter({
392
+ signal: ac.signal,
393
+ router: router,
394
+ path: '/tunnel/4b34c9275e1089c79327cba18497a37f',
395
+ serverKey: '2934c57f790f9e99a52a121802df231c',
396
+ })
397
+ app.use(router.routes())
398
+ app.use(router.allowedMethods())
399
+ app.onerror = (err) => console.info(err.message)
400
+ let koaServer = http.createServer(app.callback())
401
+ koaServer.listen(9035)
402
+ ac.signal.addEventListener('abort', () => { koaServer.close() })
403
+ await sleep(10000_000)
404
+ })
405
+ })
406
+ test('test-listen', { skip: SKIP_MANAUAL_TEST }, async () => {
407
+ // node --test-name-pattern="^test-listen$" src/lib.test.js
408
+ await runWithAbortController(async ac => {
409
+ console.info('创建socket服务')
410
+ let transformSize = 0
411
+ let socketServer = net.createServer((socket) => {
412
+ socket.pipe(new Transform({
413
+ transform(chunk, _, callback) {
414
+ // console.info('transform chunk', chunk.length)
415
+ transformSize += chunk.length
416
+ this.push(chunk)
417
+ callback()
418
+ }
419
+ })).pipe(socket)
420
+ }).listen(9036)
421
+ ac.signal.addEventListener('abort', () => { socketServer.close() })
422
+ await sleep(100)
423
+
424
+ console.info('创建监听服务 http')
425
+ let connection = createTunnelTcpClientHttp({
426
+ url: 'http://127.0.0.1:9035/tunnel/4b34c9275e1089c79327cba18497a37f',
427
+ signal: ac.signal,
428
+ serverKey: '2934c57f790f9e99a52a121802df231c',
429
+ })
430
+ connection.listen({
431
+ clientKey: 'mmm',
432
+ tunnelKey: 'b0f5014acad2060d6bd3730a1721c97f',
433
+ host: '127.0.0.1',
434
+ port: 9036,
435
+
436
+ })
437
+ await sleep(10000_000)
438
+ })
439
+ })
440
+ test('test-connect', { skip: SKIP_MANAUAL_TEST }, async () => {
441
+ // node --test-name-pattern="^test-connect$" src/lib.test.js
442
+ await runWithAbortController(async ac => {
443
+ console.info('创建连接服务 http 1')
444
+ let connection = createTunnelTcpClientHttp({
445
+ url: 'http://127.0.0.1:9035/tunnel/4b34c9275e1089c79327cba18497a37f',
446
+ signal: ac.signal,
447
+ serverKey: '2934c57f790f9e99a52a121802df231c',
448
+ })
449
+ connection.connect({
450
+ clientKey: 'mmm',
451
+ tunnelKey: 'b0f5014acad2060d6bd3730a1721c97f',
452
+ port: 9037,
453
+ })
454
+ await sleep(1000)
455
+ console.info('tcp透传测试 start')
456
+ let socket1 = net.createConnection({ host: '127.0.0.1', port: 9037 })
457
+
458
+ let rec1 = null
459
+
460
+ let sendWriteSize = 0
461
+ let sendSize = 0
462
+ let receiveSize = 0
463
+ let stop = false
464
+ await new Promise((resolve) => {
465
+ // setTimeout(() => {
466
+ // stop = true
467
+ // resolve()
468
+ // }, 5_000)
469
+ socket1.on('connect', async () => {
470
+ socket1.pipe(new Transform({
471
+ async transform(chunk, _, callback) {
472
+ receiveSize += chunk.length
473
+ await sleep(500)
474
+ callback()
475
+ }
476
+ })).on('data', (chunk) => {
477
+ console.info('1 receive chunk ', chunk, Uint8Array_toString(chunk))
478
+ rec1 = Uint8Array_toString(chunk)
479
+ })
480
+ // let data = new Uint8Array(1024 * 1024)
481
+ let data = new Uint8Array(1024)
482
+ // let data = Uint8Array_from('1234567890')
483
+ let readable = new Readable({
484
+ read() {
485
+ if (stop) {
486
+ this.push(null)
487
+ readable.destroy()
488
+ return
489
+ }
490
+ // data = Uint8Array_from('1234567890')
491
+ sendSize += data.length
492
+ this.push(data)
493
+ // stop = true
494
+ }
495
+ })
496
+ readable.on('data', (chunk) => {
497
+ sendWriteSize += chunk.length
498
+ if (!socket1.write(chunk)) {
499
+ readable.pause()
500
+ }
501
+ })
502
+ socket1.on('drain', () => {
503
+ readable.resume()
504
+ })
505
+ readable.pipe(socket1)
506
+ })
507
+ })
508
+
509
+ await sleep(10000_000)
510
+ })
511
+ })