webhookdrop-tunnel 1.0.3 → 1.0.9
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/bin/tunnel.js +149 -7
- package/package.json +1 -1
package/bin/tunnel.js
CHANGED
|
@@ -34,7 +34,7 @@ try {
|
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
const VERSION = '1.0.
|
|
37
|
+
const VERSION = '1.0.8';
|
|
38
38
|
const DEFAULT_SERVER = 'wss://webhookdrop.app';
|
|
39
39
|
const POOL_SIZE = 20; // Number of WebSocket connections
|
|
40
40
|
const PING_INTERVAL = 10000; // 10 seconds
|
|
@@ -113,7 +113,7 @@ async function forwardRequest(data) {
|
|
|
113
113
|
// Request uncompressed content to avoid encoding issues
|
|
114
114
|
'accept-encoding': 'identity'
|
|
115
115
|
},
|
|
116
|
-
timeout:
|
|
116
|
+
timeout: 300000 // 5 minutes for very large files like n8n's nodes.json
|
|
117
117
|
};
|
|
118
118
|
|
|
119
119
|
const req = httpModule.request(requestOptions, (res) => {
|
|
@@ -122,13 +122,28 @@ async function forwardRequest(data) {
|
|
|
122
122
|
res.on('data', (chunk) => chunks.push(chunk));
|
|
123
123
|
res.on('end', () => {
|
|
124
124
|
const buffer = Buffer.concat(chunks);
|
|
125
|
+
const contentType = res.headers['content-type'] || '';
|
|
126
|
+
|
|
127
|
+
// Determine if content is text-based (like OnLocal approach)
|
|
128
|
+
const isText = contentType.includes('text/') ||
|
|
129
|
+
contentType.includes('json') ||
|
|
130
|
+
contentType.includes('javascript') ||
|
|
131
|
+
contentType.includes('xml') ||
|
|
132
|
+
contentType.includes('css');
|
|
133
|
+
|
|
134
|
+
// Use structured body format: {type, data}
|
|
135
|
+
let body;
|
|
136
|
+
if (isText) {
|
|
137
|
+
body = { type: 'text', data: buffer.toString('utf-8') };
|
|
138
|
+
} else {
|
|
139
|
+
body = { type: 'binary', data: buffer.toString('base64') };
|
|
140
|
+
}
|
|
141
|
+
|
|
125
142
|
resolve({
|
|
126
143
|
success: true,
|
|
127
144
|
status_code: res.statusCode,
|
|
128
145
|
headers: res.headers,
|
|
129
|
-
|
|
130
|
-
body: buffer.toString('base64'),
|
|
131
|
-
body_encoding: 'base64'
|
|
146
|
+
body: body
|
|
132
147
|
});
|
|
133
148
|
});
|
|
134
149
|
});
|
|
@@ -159,6 +174,8 @@ async function forwardRequest(data) {
|
|
|
159
174
|
|
|
160
175
|
// Connection pool
|
|
161
176
|
const connections = new Map();
|
|
177
|
+
// Local WebSocket sessions for passthrough
|
|
178
|
+
const localWsSessions = new Map();
|
|
162
179
|
let connectedCount = 0;
|
|
163
180
|
let totalRequests = 0;
|
|
164
181
|
let isShuttingDown = false;
|
|
@@ -228,6 +245,12 @@ function createConnection(connectionId) {
|
|
|
228
245
|
// Forward to local server
|
|
229
246
|
const response = await forwardRequest(requestData);
|
|
230
247
|
|
|
248
|
+
// Log response size for debugging large files
|
|
249
|
+
const bodySize = response.body?.data?.length || 0;
|
|
250
|
+
if (bodySize > 100000) {
|
|
251
|
+
console.log(`${timestamp()} ${chalk.yellow('⚠')} Large response: ${Math.round(bodySize / 1024)}KB`);
|
|
252
|
+
}
|
|
253
|
+
|
|
231
254
|
// Send response back
|
|
232
255
|
const responseMessage = {
|
|
233
256
|
type: message.type === 'webhook_request' ? 'webhook_response' : 'http_response',
|
|
@@ -235,13 +258,132 @@ function createConnection(connectionId) {
|
|
|
235
258
|
response: response
|
|
236
259
|
};
|
|
237
260
|
|
|
238
|
-
|
|
239
|
-
|
|
261
|
+
try {
|
|
262
|
+
const jsonStr = JSON.stringify(responseMessage);
|
|
263
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
264
|
+
ws.send(jsonStr);
|
|
265
|
+
}
|
|
266
|
+
} catch (jsonErr) {
|
|
267
|
+
console.error(`${timestamp()} ${chalk.red('✗')} JSON serialize error: ${jsonErr.message}`);
|
|
268
|
+
// Send error response instead
|
|
269
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
270
|
+
ws.send(JSON.stringify({
|
|
271
|
+
type: 'http_response',
|
|
272
|
+
request_id: requestId,
|
|
273
|
+
response: { success: false, status_code: 500, error: 'Response too large to serialize' }
|
|
274
|
+
}));
|
|
275
|
+
}
|
|
240
276
|
}
|
|
241
277
|
|
|
242
278
|
const statusColor = response.success && response.status_code < 400 ? chalk.green : chalk.red;
|
|
243
279
|
console.log(`${timestamp()} ${statusColor('←')} ${response.status_code || 502} ${response.success ? 'OK' : response.error || 'Error'}`);
|
|
244
280
|
}
|
|
281
|
+
|
|
282
|
+
// WebSocket passthrough: connect to local WebSocket
|
|
283
|
+
else if (message.type === 'ws_connect') {
|
|
284
|
+
const sessionId = message.data?.session_id || message.session_id;
|
|
285
|
+
const wsPath = message.data?.path || message.path || '/';
|
|
286
|
+
const wsQuery = message.data?.query || message.query || '';
|
|
287
|
+
const requestId = message.request_id;
|
|
288
|
+
|
|
289
|
+
console.log(`${timestamp()} ${chalk.magenta('⚡')} WS Connect: ${wsPath}`);
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
// Build local WebSocket URL
|
|
293
|
+
const localWsUrl = new URL(wsPath, options.local);
|
|
294
|
+
localWsUrl.protocol = localWsUrl.protocol.replace('http', 'ws');
|
|
295
|
+
if (wsQuery) {
|
|
296
|
+
localWsUrl.search = wsQuery;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Connect to local WebSocket
|
|
300
|
+
const localWs = new WebSocket(localWsUrl.toString());
|
|
301
|
+
|
|
302
|
+
localWs.on('open', () => {
|
|
303
|
+
console.log(`${timestamp()} ${chalk.green('✓')} WS Connected: ${sessionId.slice(0, 8)}`);
|
|
304
|
+
localWsSessions.set(sessionId, { ws: localWs, tunnelWs: ws });
|
|
305
|
+
|
|
306
|
+
// Send success response
|
|
307
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
308
|
+
ws.send(JSON.stringify({
|
|
309
|
+
type: 'http_response',
|
|
310
|
+
request_id: requestId,
|
|
311
|
+
response: { success: true }
|
|
312
|
+
}));
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
localWs.on('message', (data) => {
|
|
317
|
+
// Forward message from local WebSocket to server
|
|
318
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
319
|
+
ws.send(JSON.stringify({
|
|
320
|
+
type: 'ws_message',
|
|
321
|
+
session_id: sessionId,
|
|
322
|
+
message: data.toString()
|
|
323
|
+
}));
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
localWs.on('close', () => {
|
|
328
|
+
console.log(`${timestamp()} ${chalk.yellow('○')} WS Closed: ${sessionId.slice(0, 8)}`);
|
|
329
|
+
localWsSessions.delete(sessionId);
|
|
330
|
+
|
|
331
|
+
// Notify server
|
|
332
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
333
|
+
ws.send(JSON.stringify({
|
|
334
|
+
type: 'ws_closed',
|
|
335
|
+
session_id: sessionId
|
|
336
|
+
}));
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
localWs.on('error', (err) => {
|
|
341
|
+
console.log(`${timestamp()} ${chalk.red('✗')} WS Error: ${err.message}`);
|
|
342
|
+
localWsSessions.delete(sessionId);
|
|
343
|
+
|
|
344
|
+
// Send error response
|
|
345
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
346
|
+
ws.send(JSON.stringify({
|
|
347
|
+
type: 'http_response',
|
|
348
|
+
request_id: requestId,
|
|
349
|
+
response: { success: false, error: err.message }
|
|
350
|
+
}));
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
} catch (e) {
|
|
354
|
+
console.log(`${timestamp()} ${chalk.red('✗')} WS Connect Error: ${e.message}`);
|
|
355
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
356
|
+
ws.send(JSON.stringify({
|
|
357
|
+
type: 'http_response',
|
|
358
|
+
request_id: requestId,
|
|
359
|
+
response: { success: false, error: e.message }
|
|
360
|
+
}));
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// WebSocket passthrough: forward message to local WebSocket
|
|
366
|
+
else if (message.type === 'ws_message') {
|
|
367
|
+
const sessionId = message.data?.session_id || message.session_id;
|
|
368
|
+
const wsMessage = message.data?.message || message.message;
|
|
369
|
+
|
|
370
|
+
const session = localWsSessions.get(sessionId);
|
|
371
|
+
if (session && session.ws.readyState === WebSocket.OPEN) {
|
|
372
|
+
session.ws.send(wsMessage);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// WebSocket passthrough: close local WebSocket
|
|
377
|
+
else if (message.type === 'ws_close') {
|
|
378
|
+
const sessionId = message.data?.session_id || message.session_id;
|
|
379
|
+
|
|
380
|
+
const session = localWsSessions.get(sessionId);
|
|
381
|
+
if (session) {
|
|
382
|
+
session.ws.close();
|
|
383
|
+
localWsSessions.delete(sessionId);
|
|
384
|
+
console.log(`${timestamp()} ${chalk.yellow('○')} WS Close: ${sessionId.slice(0, 8)}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
245
387
|
} catch (e) {
|
|
246
388
|
console.error(`${timestamp()} ${chalk.red('Error processing message:')} ${e.message}`);
|
|
247
389
|
}
|