@limrun/api 0.13.1 → 0.14.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/CHANGELOG.md +12 -0
- package/README.md +2 -2
- package/client.d.mts +2 -2
- package/client.d.mts.map +1 -1
- package/client.d.ts +2 -2
- package/client.d.ts.map +1 -1
- package/client.js +2 -2
- package/client.js.map +1 -1
- package/client.mjs +2 -2
- package/client.mjs.map +1 -1
- package/index.d.mts +1 -1
- package/index.d.mts.map +1 -1
- package/index.d.ts +1 -1
- package/index.d.ts.map +1 -1
- package/index.js +3 -3
- package/index.js.map +1 -1
- package/index.mjs +1 -1
- package/index.mjs.map +1 -1
- package/instance-client.d.mts +32 -0
- package/instance-client.d.mts.map +1 -1
- package/instance-client.d.ts +32 -0
- package/instance-client.d.ts.map +1 -1
- package/instance-client.js +193 -87
- package/instance-client.js.map +1 -1
- package/instance-client.mjs +193 -87
- package/instance-client.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +3 -3
- package/src/index.ts +1 -1
- package/src/instance-client.ts +269 -101
- package/src/tunnel.ts +262 -43
- package/src/version.ts +1 -1
- package/tunnel.d.mts +50 -7
- package/tunnel.d.mts.map +1 -1
- package/tunnel.d.ts +50 -7
- package/tunnel.d.ts.map +1 -1
- package/tunnel.js +195 -44
- package/tunnel.js.map +1 -1
- package/tunnel.mjs +195 -44
- package/tunnel.mjs.map +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
package/tunnel.js
CHANGED
|
@@ -5,95 +5,246 @@ const tslib_1 = require("./internal/tslib.js");
|
|
|
5
5
|
const net = tslib_1.__importStar(require("net"));
|
|
6
6
|
const ws_1 = require("ws");
|
|
7
7
|
/**
|
|
8
|
-
* Starts a
|
|
8
|
+
* Starts a persistent TCP → WebSocket proxy.
|
|
9
9
|
*
|
|
10
10
|
* The function creates a local TCP server that listens on an ephemeral port on
|
|
11
|
-
* 127.0.0.1.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* proxy instance.
|
|
11
|
+
* 127.0.0.1. When a TCP client connects, it forwards all traffic between that
|
|
12
|
+
* client and `remoteURL` through an authenticated WebSocket. The server remains
|
|
13
|
+
* active even after the client disconnects, allowing reconnection without
|
|
14
|
+
* recreating the tunnel.
|
|
16
15
|
*
|
|
17
16
|
* @param remoteURL Remote WebSocket endpoint (e.g. wss://example.com/instance)
|
|
18
17
|
* @param token Bearer token sent as `Authorization` header
|
|
19
18
|
* @param hostname Optional IP address to listen on. Default is 127.0.0.1
|
|
20
19
|
* @param port Optional port number to listen on. Default is to ask Node.js
|
|
21
20
|
* to find an available non-privileged port.
|
|
21
|
+
* @param options Optional reconnection configuration
|
|
22
22
|
*/
|
|
23
|
-
async function startTcpTunnel(remoteURL, token, hostname, port) {
|
|
23
|
+
async function startTcpTunnel(remoteURL, token, hostname, port, options) {
|
|
24
|
+
const maxReconnectAttempts = options?.maxReconnectAttempts ?? 6;
|
|
25
|
+
const reconnectDelay = options?.reconnectDelay ?? 1000;
|
|
26
|
+
const maxReconnectDelay = options?.maxReconnectDelay ?? 30000;
|
|
27
|
+
const logLevel = options?.logLevel ?? 'info';
|
|
28
|
+
// Logger functions
|
|
29
|
+
const logger = {
|
|
30
|
+
debug: (...args) => {
|
|
31
|
+
if (logLevel === 'debug')
|
|
32
|
+
console.log('[Tunnel]', ...args);
|
|
33
|
+
},
|
|
34
|
+
info: (...args) => {
|
|
35
|
+
if (logLevel === 'info' || logLevel === 'debug')
|
|
36
|
+
console.log('[Tunnel]', ...args);
|
|
37
|
+
},
|
|
38
|
+
warn: (...args) => {
|
|
39
|
+
if (logLevel === 'warn' || logLevel === 'info' || logLevel === 'debug')
|
|
40
|
+
console.warn('[Tunnel]', ...args);
|
|
41
|
+
},
|
|
42
|
+
error: (...args) => {
|
|
43
|
+
if (logLevel !== 'none')
|
|
44
|
+
console.error('[Tunnel]', ...args);
|
|
45
|
+
},
|
|
46
|
+
};
|
|
24
47
|
return new Promise((resolve, reject) => {
|
|
25
48
|
const server = net.createServer();
|
|
26
49
|
let ws;
|
|
27
50
|
let pingInterval;
|
|
28
|
-
|
|
29
|
-
|
|
51
|
+
let reconnectTimeout;
|
|
52
|
+
let reconnectAttempts = 0;
|
|
53
|
+
let intentionalDisconnect = false;
|
|
54
|
+
let tcpSocket;
|
|
55
|
+
let connectionState = 'connecting';
|
|
56
|
+
const stateChangeCallbacks = new Set();
|
|
57
|
+
const updateConnectionState = (newState) => {
|
|
58
|
+
if (connectionState !== newState) {
|
|
59
|
+
connectionState = newState;
|
|
60
|
+
logger.debug(`Connection state changed to: ${newState}`);
|
|
61
|
+
stateChangeCallbacks.forEach((callback) => {
|
|
62
|
+
try {
|
|
63
|
+
callback(newState);
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
logger.error('Error in connection state callback:', err);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const cleanup = () => {
|
|
72
|
+
if (reconnectTimeout) {
|
|
73
|
+
clearTimeout(reconnectTimeout);
|
|
74
|
+
reconnectTimeout = undefined;
|
|
75
|
+
}
|
|
30
76
|
if (pingInterval) {
|
|
31
77
|
clearInterval(pingInterval);
|
|
32
78
|
pingInterval = undefined;
|
|
33
79
|
}
|
|
34
|
-
if (ws
|
|
35
|
-
ws.
|
|
80
|
+
if (ws) {
|
|
81
|
+
ws.removeAllListeners();
|
|
82
|
+
if (ws.readyState === ws_1.WebSocket.OPEN || ws.readyState === ws_1.WebSocket.CONNECTING) {
|
|
83
|
+
ws.close(1000, 'close');
|
|
84
|
+
}
|
|
85
|
+
ws = undefined;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const close = () => {
|
|
89
|
+
intentionalDisconnect = true;
|
|
90
|
+
cleanup();
|
|
91
|
+
updateConnectionState('disconnected');
|
|
92
|
+
if (tcpSocket && !tcpSocket.destroyed) {
|
|
93
|
+
tcpSocket.destroy();
|
|
36
94
|
}
|
|
37
95
|
if (server.listening) {
|
|
38
96
|
server.close();
|
|
39
97
|
}
|
|
40
98
|
};
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (
|
|
99
|
+
const scheduleReconnect = () => {
|
|
100
|
+
if (intentionalDisconnect) {
|
|
101
|
+
logger.debug('Skipping reconnection (intentional disconnect)');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (!tcpSocket || tcpSocket.destroyed) {
|
|
105
|
+
logger.debug('Skipping reconnection (TCP socket closed)');
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (reconnectAttempts >= maxReconnectAttempts) {
|
|
109
|
+
logger.error(`Max reconnection attempts (${maxReconnectAttempts}) reached. Closing tunnel.`);
|
|
51
110
|
close();
|
|
52
|
-
return
|
|
111
|
+
return;
|
|
53
112
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
113
|
+
const currentDelay = Math.min(reconnectDelay * Math.pow(2, reconnectAttempts), maxReconnectDelay);
|
|
114
|
+
reconnectAttempts++;
|
|
115
|
+
logger.debug(`Scheduling reconnection attempt ${reconnectAttempts} in ${currentDelay}ms...`);
|
|
116
|
+
updateConnectionState('reconnecting');
|
|
117
|
+
reconnectTimeout = setTimeout(() => {
|
|
118
|
+
logger.debug(`Attempting to reconnect (attempt ${reconnectAttempts})...`);
|
|
119
|
+
setupWebSocket();
|
|
120
|
+
}, currentDelay);
|
|
121
|
+
};
|
|
122
|
+
const setupWebSocket = () => {
|
|
123
|
+
if (!tcpSocket || tcpSocket.destroyed) {
|
|
124
|
+
logger.error('Cannot setup WebSocket: TCP socket is closed');
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
cleanup();
|
|
128
|
+
updateConnectionState('connecting');
|
|
60
129
|
ws = new ws_1.WebSocket(remoteURL, {
|
|
61
130
|
headers: { Authorization: `Bearer ${token}` },
|
|
62
131
|
perMessageDeflate: false,
|
|
63
132
|
});
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
133
|
+
ws.on('error', (err) => {
|
|
134
|
+
logger.error('WebSocket error:', err.message || err);
|
|
135
|
+
});
|
|
136
|
+
ws.on('close', () => {
|
|
137
|
+
if (pingInterval) {
|
|
138
|
+
clearInterval(pingInterval);
|
|
139
|
+
pingInterval = undefined;
|
|
140
|
+
}
|
|
141
|
+
const shouldReconnect = !intentionalDisconnect && connectionState !== 'disconnected';
|
|
142
|
+
updateConnectionState('disconnected');
|
|
143
|
+
logger.debug('WebSocket disconnected');
|
|
144
|
+
// Pause TCP socket to apply backpressure - TCP will handle buffering
|
|
145
|
+
if (tcpSocket && !tcpSocket.destroyed && !tcpSocket.isPaused()) {
|
|
146
|
+
logger.debug('Pausing TCP socket (applying backpressure)');
|
|
147
|
+
tcpSocket.pause();
|
|
148
|
+
}
|
|
149
|
+
if (shouldReconnect && tcpSocket && !tcpSocket.destroyed) {
|
|
150
|
+
scheduleReconnect();
|
|
151
|
+
}
|
|
69
152
|
});
|
|
70
|
-
ws.
|
|
71
|
-
const socket = ws;
|
|
153
|
+
ws.on('open', () => {
|
|
154
|
+
const socket = ws;
|
|
155
|
+
logger.debug('WebSocket connected');
|
|
156
|
+
reconnectAttempts = 0;
|
|
157
|
+
updateConnectionState('connected');
|
|
72
158
|
pingInterval = setInterval(() => {
|
|
73
159
|
if (socket.readyState === ws_1.WebSocket.OPEN) {
|
|
74
160
|
socket.ping();
|
|
75
161
|
}
|
|
76
162
|
}, 30000);
|
|
77
|
-
// TCP
|
|
78
|
-
tcpSocket.
|
|
163
|
+
// Resume TCP socket - queued data will flow through
|
|
164
|
+
if (tcpSocket && tcpSocket.isPaused()) {
|
|
165
|
+
logger.debug('Resuming TCP socket (releasing backpressure)');
|
|
166
|
+
tcpSocket.resume();
|
|
167
|
+
}
|
|
168
|
+
// TCP → WS: Forward data directly
|
|
169
|
+
const onTcpData = (chunk) => {
|
|
79
170
|
if (socket.readyState === ws_1.WebSocket.OPEN) {
|
|
80
171
|
socket.send(chunk);
|
|
81
172
|
}
|
|
82
|
-
|
|
173
|
+
// If WebSocket is not ready, data will queue in TCP buffers (backpressure)
|
|
174
|
+
};
|
|
175
|
+
// Remove old listener if exists and add new one
|
|
176
|
+
tcpSocket.removeListener('data', onTcpData);
|
|
177
|
+
tcpSocket.on('data', onTcpData);
|
|
83
178
|
// WS → TCP
|
|
84
179
|
socket.on('message', (data) => {
|
|
85
|
-
if (!tcpSocket.destroyed) {
|
|
180
|
+
if (tcpSocket && !tcpSocket.destroyed) {
|
|
86
181
|
tcpSocket.write(data);
|
|
87
182
|
}
|
|
88
183
|
});
|
|
89
184
|
});
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
185
|
+
};
|
|
186
|
+
// TCP server error
|
|
187
|
+
server.once('error', (err) => {
|
|
188
|
+
close();
|
|
189
|
+
reject(new Error(`TCP server error: ${err.message}`));
|
|
190
|
+
});
|
|
191
|
+
const getConnectionState = () => {
|
|
192
|
+
return connectionState;
|
|
193
|
+
};
|
|
194
|
+
const onConnectionStateChange = (callback) => {
|
|
195
|
+
stateChangeCallbacks.add(callback);
|
|
196
|
+
return () => {
|
|
197
|
+
stateChangeCallbacks.delete(callback);
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
|
+
// Listening
|
|
201
|
+
server.once('listening', () => {
|
|
202
|
+
const address = server.address();
|
|
203
|
+
if (!address || typeof address === 'string') {
|
|
94
204
|
close();
|
|
205
|
+
return reject(new Error('Failed to obtain listening address'));
|
|
206
|
+
}
|
|
207
|
+
resolve({
|
|
208
|
+
address,
|
|
209
|
+
close,
|
|
210
|
+
getConnectionState,
|
|
211
|
+
onConnectionStateChange,
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
// Helper to clean up current connection but keep server alive
|
|
215
|
+
const cleanupConnection = () => {
|
|
216
|
+
cleanup();
|
|
217
|
+
updateConnectionState('disconnected');
|
|
218
|
+
if (tcpSocket && !tcpSocket.destroyed) {
|
|
219
|
+
tcpSocket.destroy();
|
|
220
|
+
tcpSocket = undefined;
|
|
221
|
+
}
|
|
222
|
+
// Reset reconnection state for next connection
|
|
223
|
+
reconnectAttempts = 0;
|
|
224
|
+
intentionalDisconnect = false;
|
|
225
|
+
logger.debug('Connection cleaned up, ready for new TCP connection');
|
|
226
|
+
};
|
|
227
|
+
// On TCP connection (can happen multiple times)
|
|
228
|
+
server.on('connection', (socket) => {
|
|
229
|
+
// If there's already an active connection, reject the new one
|
|
230
|
+
if (tcpSocket && !tcpSocket.destroyed) {
|
|
231
|
+
logger.debug('Rejecting new TCP connection - already have an active connection');
|
|
232
|
+
socket.destroy();
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
logger.debug('TCP client connected');
|
|
236
|
+
tcpSocket = socket;
|
|
237
|
+
// TCP socket handlers
|
|
238
|
+
tcpSocket.on('close', () => {
|
|
239
|
+
logger.debug('TCP socket closed by client');
|
|
240
|
+
cleanupConnection();
|
|
241
|
+
});
|
|
242
|
+
tcpSocket.on('error', (err) => {
|
|
243
|
+
logger.error('TCP socket error:', err);
|
|
244
|
+
cleanupConnection();
|
|
95
245
|
});
|
|
96
|
-
|
|
246
|
+
// Start WebSocket connection
|
|
247
|
+
setupWebSocket();
|
|
97
248
|
});
|
|
98
249
|
// Start listening
|
|
99
250
|
server.listen(port, hostname);
|
package/tunnel.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tunnel.js","sourceRoot":"","sources":["src/tunnel.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"tunnel.js","sourceRoot":"","sources":["src/tunnel.ts"],"names":[],"mappings":"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"}
|
package/tunnel.mjs
CHANGED
|
@@ -1,95 +1,246 @@
|
|
|
1
1
|
import * as net from 'net';
|
|
2
2
|
import { WebSocket } from 'ws';
|
|
3
3
|
/**
|
|
4
|
-
* Starts a
|
|
4
|
+
* Starts a persistent TCP → WebSocket proxy.
|
|
5
5
|
*
|
|
6
6
|
* The function creates a local TCP server that listens on an ephemeral port on
|
|
7
|
-
* 127.0.0.1.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* proxy instance.
|
|
7
|
+
* 127.0.0.1. When a TCP client connects, it forwards all traffic between that
|
|
8
|
+
* client and `remoteURL` through an authenticated WebSocket. The server remains
|
|
9
|
+
* active even after the client disconnects, allowing reconnection without
|
|
10
|
+
* recreating the tunnel.
|
|
12
11
|
*
|
|
13
12
|
* @param remoteURL Remote WebSocket endpoint (e.g. wss://example.com/instance)
|
|
14
13
|
* @param token Bearer token sent as `Authorization` header
|
|
15
14
|
* @param hostname Optional IP address to listen on. Default is 127.0.0.1
|
|
16
15
|
* @param port Optional port number to listen on. Default is to ask Node.js
|
|
17
16
|
* to find an available non-privileged port.
|
|
17
|
+
* @param options Optional reconnection configuration
|
|
18
18
|
*/
|
|
19
|
-
export async function startTcpTunnel(remoteURL, token, hostname, port) {
|
|
19
|
+
export async function startTcpTunnel(remoteURL, token, hostname, port, options) {
|
|
20
|
+
const maxReconnectAttempts = options?.maxReconnectAttempts ?? 6;
|
|
21
|
+
const reconnectDelay = options?.reconnectDelay ?? 1000;
|
|
22
|
+
const maxReconnectDelay = options?.maxReconnectDelay ?? 30000;
|
|
23
|
+
const logLevel = options?.logLevel ?? 'info';
|
|
24
|
+
// Logger functions
|
|
25
|
+
const logger = {
|
|
26
|
+
debug: (...args) => {
|
|
27
|
+
if (logLevel === 'debug')
|
|
28
|
+
console.log('[Tunnel]', ...args);
|
|
29
|
+
},
|
|
30
|
+
info: (...args) => {
|
|
31
|
+
if (logLevel === 'info' || logLevel === 'debug')
|
|
32
|
+
console.log('[Tunnel]', ...args);
|
|
33
|
+
},
|
|
34
|
+
warn: (...args) => {
|
|
35
|
+
if (logLevel === 'warn' || logLevel === 'info' || logLevel === 'debug')
|
|
36
|
+
console.warn('[Tunnel]', ...args);
|
|
37
|
+
},
|
|
38
|
+
error: (...args) => {
|
|
39
|
+
if (logLevel !== 'none')
|
|
40
|
+
console.error('[Tunnel]', ...args);
|
|
41
|
+
},
|
|
42
|
+
};
|
|
20
43
|
return new Promise((resolve, reject) => {
|
|
21
44
|
const server = net.createServer();
|
|
22
45
|
let ws;
|
|
23
46
|
let pingInterval;
|
|
24
|
-
|
|
25
|
-
|
|
47
|
+
let reconnectTimeout;
|
|
48
|
+
let reconnectAttempts = 0;
|
|
49
|
+
let intentionalDisconnect = false;
|
|
50
|
+
let tcpSocket;
|
|
51
|
+
let connectionState = 'connecting';
|
|
52
|
+
const stateChangeCallbacks = new Set();
|
|
53
|
+
const updateConnectionState = (newState) => {
|
|
54
|
+
if (connectionState !== newState) {
|
|
55
|
+
connectionState = newState;
|
|
56
|
+
logger.debug(`Connection state changed to: ${newState}`);
|
|
57
|
+
stateChangeCallbacks.forEach((callback) => {
|
|
58
|
+
try {
|
|
59
|
+
callback(newState);
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
logger.error('Error in connection state callback:', err);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const cleanup = () => {
|
|
68
|
+
if (reconnectTimeout) {
|
|
69
|
+
clearTimeout(reconnectTimeout);
|
|
70
|
+
reconnectTimeout = undefined;
|
|
71
|
+
}
|
|
26
72
|
if (pingInterval) {
|
|
27
73
|
clearInterval(pingInterval);
|
|
28
74
|
pingInterval = undefined;
|
|
29
75
|
}
|
|
30
|
-
if (ws
|
|
31
|
-
ws.
|
|
76
|
+
if (ws) {
|
|
77
|
+
ws.removeAllListeners();
|
|
78
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
79
|
+
ws.close(1000, 'close');
|
|
80
|
+
}
|
|
81
|
+
ws = undefined;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const close = () => {
|
|
85
|
+
intentionalDisconnect = true;
|
|
86
|
+
cleanup();
|
|
87
|
+
updateConnectionState('disconnected');
|
|
88
|
+
if (tcpSocket && !tcpSocket.destroyed) {
|
|
89
|
+
tcpSocket.destroy();
|
|
32
90
|
}
|
|
33
91
|
if (server.listening) {
|
|
34
92
|
server.close();
|
|
35
93
|
}
|
|
36
94
|
};
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (
|
|
95
|
+
const scheduleReconnect = () => {
|
|
96
|
+
if (intentionalDisconnect) {
|
|
97
|
+
logger.debug('Skipping reconnection (intentional disconnect)');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (!tcpSocket || tcpSocket.destroyed) {
|
|
101
|
+
logger.debug('Skipping reconnection (TCP socket closed)');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (reconnectAttempts >= maxReconnectAttempts) {
|
|
105
|
+
logger.error(`Max reconnection attempts (${maxReconnectAttempts}) reached. Closing tunnel.`);
|
|
47
106
|
close();
|
|
48
|
-
return
|
|
107
|
+
return;
|
|
49
108
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
109
|
+
const currentDelay = Math.min(reconnectDelay * Math.pow(2, reconnectAttempts), maxReconnectDelay);
|
|
110
|
+
reconnectAttempts++;
|
|
111
|
+
logger.debug(`Scheduling reconnection attempt ${reconnectAttempts} in ${currentDelay}ms...`);
|
|
112
|
+
updateConnectionState('reconnecting');
|
|
113
|
+
reconnectTimeout = setTimeout(() => {
|
|
114
|
+
logger.debug(`Attempting to reconnect (attempt ${reconnectAttempts})...`);
|
|
115
|
+
setupWebSocket();
|
|
116
|
+
}, currentDelay);
|
|
117
|
+
};
|
|
118
|
+
const setupWebSocket = () => {
|
|
119
|
+
if (!tcpSocket || tcpSocket.destroyed) {
|
|
120
|
+
logger.error('Cannot setup WebSocket: TCP socket is closed');
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
cleanup();
|
|
124
|
+
updateConnectionState('connecting');
|
|
56
125
|
ws = new WebSocket(remoteURL, {
|
|
57
126
|
headers: { Authorization: `Bearer ${token}` },
|
|
58
127
|
perMessageDeflate: false,
|
|
59
128
|
});
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
129
|
+
ws.on('error', (err) => {
|
|
130
|
+
logger.error('WebSocket error:', err.message || err);
|
|
131
|
+
});
|
|
132
|
+
ws.on('close', () => {
|
|
133
|
+
if (pingInterval) {
|
|
134
|
+
clearInterval(pingInterval);
|
|
135
|
+
pingInterval = undefined;
|
|
136
|
+
}
|
|
137
|
+
const shouldReconnect = !intentionalDisconnect && connectionState !== 'disconnected';
|
|
138
|
+
updateConnectionState('disconnected');
|
|
139
|
+
logger.debug('WebSocket disconnected');
|
|
140
|
+
// Pause TCP socket to apply backpressure - TCP will handle buffering
|
|
141
|
+
if (tcpSocket && !tcpSocket.destroyed && !tcpSocket.isPaused()) {
|
|
142
|
+
logger.debug('Pausing TCP socket (applying backpressure)');
|
|
143
|
+
tcpSocket.pause();
|
|
144
|
+
}
|
|
145
|
+
if (shouldReconnect && tcpSocket && !tcpSocket.destroyed) {
|
|
146
|
+
scheduleReconnect();
|
|
147
|
+
}
|
|
65
148
|
});
|
|
66
|
-
ws.
|
|
67
|
-
const socket = ws;
|
|
149
|
+
ws.on('open', () => {
|
|
150
|
+
const socket = ws;
|
|
151
|
+
logger.debug('WebSocket connected');
|
|
152
|
+
reconnectAttempts = 0;
|
|
153
|
+
updateConnectionState('connected');
|
|
68
154
|
pingInterval = setInterval(() => {
|
|
69
155
|
if (socket.readyState === WebSocket.OPEN) {
|
|
70
156
|
socket.ping();
|
|
71
157
|
}
|
|
72
158
|
}, 30000);
|
|
73
|
-
// TCP
|
|
74
|
-
tcpSocket.
|
|
159
|
+
// Resume TCP socket - queued data will flow through
|
|
160
|
+
if (tcpSocket && tcpSocket.isPaused()) {
|
|
161
|
+
logger.debug('Resuming TCP socket (releasing backpressure)');
|
|
162
|
+
tcpSocket.resume();
|
|
163
|
+
}
|
|
164
|
+
// TCP → WS: Forward data directly
|
|
165
|
+
const onTcpData = (chunk) => {
|
|
75
166
|
if (socket.readyState === WebSocket.OPEN) {
|
|
76
167
|
socket.send(chunk);
|
|
77
168
|
}
|
|
78
|
-
|
|
169
|
+
// If WebSocket is not ready, data will queue in TCP buffers (backpressure)
|
|
170
|
+
};
|
|
171
|
+
// Remove old listener if exists and add new one
|
|
172
|
+
tcpSocket.removeListener('data', onTcpData);
|
|
173
|
+
tcpSocket.on('data', onTcpData);
|
|
79
174
|
// WS → TCP
|
|
80
175
|
socket.on('message', (data) => {
|
|
81
|
-
if (!tcpSocket.destroyed) {
|
|
176
|
+
if (tcpSocket && !tcpSocket.destroyed) {
|
|
82
177
|
tcpSocket.write(data);
|
|
83
178
|
}
|
|
84
179
|
});
|
|
85
180
|
});
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
181
|
+
};
|
|
182
|
+
// TCP server error
|
|
183
|
+
server.once('error', (err) => {
|
|
184
|
+
close();
|
|
185
|
+
reject(new Error(`TCP server error: ${err.message}`));
|
|
186
|
+
});
|
|
187
|
+
const getConnectionState = () => {
|
|
188
|
+
return connectionState;
|
|
189
|
+
};
|
|
190
|
+
const onConnectionStateChange = (callback) => {
|
|
191
|
+
stateChangeCallbacks.add(callback);
|
|
192
|
+
return () => {
|
|
193
|
+
stateChangeCallbacks.delete(callback);
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
// Listening
|
|
197
|
+
server.once('listening', () => {
|
|
198
|
+
const address = server.address();
|
|
199
|
+
if (!address || typeof address === 'string') {
|
|
90
200
|
close();
|
|
201
|
+
return reject(new Error('Failed to obtain listening address'));
|
|
202
|
+
}
|
|
203
|
+
resolve({
|
|
204
|
+
address,
|
|
205
|
+
close,
|
|
206
|
+
getConnectionState,
|
|
207
|
+
onConnectionStateChange,
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
// Helper to clean up current connection but keep server alive
|
|
211
|
+
const cleanupConnection = () => {
|
|
212
|
+
cleanup();
|
|
213
|
+
updateConnectionState('disconnected');
|
|
214
|
+
if (tcpSocket && !tcpSocket.destroyed) {
|
|
215
|
+
tcpSocket.destroy();
|
|
216
|
+
tcpSocket = undefined;
|
|
217
|
+
}
|
|
218
|
+
// Reset reconnection state for next connection
|
|
219
|
+
reconnectAttempts = 0;
|
|
220
|
+
intentionalDisconnect = false;
|
|
221
|
+
logger.debug('Connection cleaned up, ready for new TCP connection');
|
|
222
|
+
};
|
|
223
|
+
// On TCP connection (can happen multiple times)
|
|
224
|
+
server.on('connection', (socket) => {
|
|
225
|
+
// If there's already an active connection, reject the new one
|
|
226
|
+
if (tcpSocket && !tcpSocket.destroyed) {
|
|
227
|
+
logger.debug('Rejecting new TCP connection - already have an active connection');
|
|
228
|
+
socket.destroy();
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
logger.debug('TCP client connected');
|
|
232
|
+
tcpSocket = socket;
|
|
233
|
+
// TCP socket handlers
|
|
234
|
+
tcpSocket.on('close', () => {
|
|
235
|
+
logger.debug('TCP socket closed by client');
|
|
236
|
+
cleanupConnection();
|
|
237
|
+
});
|
|
238
|
+
tcpSocket.on('error', (err) => {
|
|
239
|
+
logger.error('TCP socket error:', err);
|
|
240
|
+
cleanupConnection();
|
|
91
241
|
});
|
|
92
|
-
|
|
242
|
+
// Start WebSocket connection
|
|
243
|
+
setupWebSocket();
|
|
93
244
|
});
|
|
94
245
|
// Start listening
|
|
95
246
|
server.listen(port, hostname);
|
package/tunnel.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tunnel.mjs","sourceRoot":"","sources":["src/tunnel.ts"],"names":[],"mappings":"OAAO,KAAK,GAAG,MAAM,KAAK;OACnB,EAAE,SAAS,EAAE,MAAM,IAAI;
|
|
1
|
+
{"version":3,"file":"tunnel.mjs","sourceRoot":"","sources":["src/tunnel.ts"],"names":[],"mappings":"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"}
|