@dainprotocol/tunnel 1.1.7 → 1.1.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/dist/client/index.js +6 -51
- package/dist/server/index.js +84 -151
- package/package.json +2 -2
package/dist/client/index.js
CHANGED
|
@@ -136,7 +136,6 @@ class DainTunnel extends events_1.EventEmitter {
|
|
|
136
136
|
});
|
|
137
137
|
}
|
|
138
138
|
handleMessage(message, resolve) {
|
|
139
|
-
console.log(`[Tunnel Client] Received message type: ${message.type}`);
|
|
140
139
|
switch (message.type) {
|
|
141
140
|
case "tunnelUrl":
|
|
142
141
|
this.tunnelUrl = message.url;
|
|
@@ -150,18 +149,14 @@ class DainTunnel extends events_1.EventEmitter {
|
|
|
150
149
|
this.handleWebSocketConnection(message);
|
|
151
150
|
break;
|
|
152
151
|
case "sse_connection":
|
|
153
|
-
console.log(`[Tunnel Client] Routing to handleSSEConnection`);
|
|
154
152
|
this.handleSSEConnection(message);
|
|
155
153
|
break;
|
|
156
154
|
case "sse_close":
|
|
157
|
-
console.log(`[Tunnel Client] Routing to handleSSEClose`);
|
|
158
155
|
this.handleSSEClose(message);
|
|
159
156
|
break;
|
|
160
157
|
case "websocket":
|
|
161
158
|
this.handleWebSocketMessage(message);
|
|
162
159
|
break;
|
|
163
|
-
default:
|
|
164
|
-
console.log(`[Tunnel Client] Unknown message type: ${message.type}`);
|
|
165
160
|
}
|
|
166
161
|
}
|
|
167
162
|
async handleRequest(request) {
|
|
@@ -255,11 +250,6 @@ class DainTunnel extends events_1.EventEmitter {
|
|
|
255
250
|
}
|
|
256
251
|
}
|
|
257
252
|
handleSSEConnection(message) {
|
|
258
|
-
console.log(`[Tunnel Client] ====== SSE CONNECTION HANDLER START ======`);
|
|
259
|
-
console.log(`[Tunnel Client] SSE ID: ${message.id}`);
|
|
260
|
-
console.log(`[Tunnel Client] Path: ${message.path}`);
|
|
261
|
-
console.log(`[Tunnel Client] Method: ${message.method}`);
|
|
262
|
-
console.log(`[Tunnel Client] Local port: ${this.port}`);
|
|
263
253
|
try {
|
|
264
254
|
// Create an EventSource-like stream to the local server
|
|
265
255
|
// Since Node.js doesn't have a built-in EventSource, we'll use HTTP
|
|
@@ -270,13 +260,8 @@ class DainTunnel extends events_1.EventEmitter {
|
|
|
270
260
|
method: message.method || 'GET', // Use provided method (supports POST tool calls)
|
|
271
261
|
headers: message.headers,
|
|
272
262
|
};
|
|
273
|
-
console.log(`[Tunnel Client] Creating HTTP request to: http://localhost:${this.port}${message.path}`);
|
|
274
|
-
console.log(`[Tunnel Client] Request headers:`, JSON.stringify(message.headers, null, 2));
|
|
275
263
|
const req = http_1.default.request(options, (res) => {
|
|
276
|
-
console.log(`[Tunnel Client] Response received with status: ${res.statusCode}`);
|
|
277
|
-
console.log(`[Tunnel Client] Response headers:`, JSON.stringify(res.headers, null, 2));
|
|
278
264
|
if (res.statusCode !== 200) {
|
|
279
|
-
console.error(`[Tunnel Client] ❌ Non-200 status code: ${res.statusCode}`);
|
|
280
265
|
if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
|
|
281
266
|
this.ws.send(JSON.stringify({
|
|
282
267
|
type: 'sse',
|
|
@@ -287,22 +272,17 @@ class DainTunnel extends events_1.EventEmitter {
|
|
|
287
272
|
}
|
|
288
273
|
return;
|
|
289
274
|
}
|
|
290
|
-
console.log(`[Tunnel Client] ✓ Starting SSE stream processing for ${message.id}`);
|
|
291
275
|
// Process SSE stream
|
|
292
276
|
let buffer = '';
|
|
293
|
-
let eventCount = 0;
|
|
294
277
|
res.on('data', (chunk) => {
|
|
295
278
|
const chunkStr = chunk.toString();
|
|
296
|
-
console.log(`[Tunnel Client] SSE chunk
|
|
297
|
-
console.log(`[Tunnel Client] Chunk preview:`, chunkStr.substring(0, 200));
|
|
279
|
+
console.log(`[Tunnel Client] SSE chunk received (${chunkStr.length} bytes):`, chunkStr.substring(0, 200));
|
|
298
280
|
buffer += chunkStr;
|
|
299
281
|
// Process complete SSE messages
|
|
300
|
-
let messagesProcessed = 0;
|
|
301
282
|
while (buffer.includes('\n\n')) {
|
|
302
283
|
const messageEndIndex = buffer.indexOf('\n\n');
|
|
303
284
|
const messageData = buffer.substring(0, messageEndIndex);
|
|
304
285
|
buffer = buffer.substring(messageEndIndex + 2);
|
|
305
|
-
console.log(`[Tunnel Client] Processing SSE message ${++messagesProcessed}:`, messageData);
|
|
306
286
|
// Parse the SSE message
|
|
307
287
|
const lines = messageData.split('\n');
|
|
308
288
|
let event = 'message';
|
|
@@ -320,27 +300,18 @@ class DainTunnel extends events_1.EventEmitter {
|
|
|
320
300
|
data = data.substring(0, data.length - 1);
|
|
321
301
|
}
|
|
322
302
|
// Forward to server
|
|
323
|
-
console.log(`[Tunnel Client] Forwarding SSE event
|
|
324
|
-
console.log(`[Tunnel Client] SSE ID: ${message.id}`);
|
|
325
|
-
console.log(`[Tunnel Client] Event: ${event}`);
|
|
326
|
-
console.log(`[Tunnel Client] Data length: ${data.length}`);
|
|
303
|
+
console.log(`[Tunnel Client] Forwarding SSE event: ${event}, data length: ${data.length}`);
|
|
327
304
|
if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
|
|
328
|
-
|
|
305
|
+
this.ws.send(JSON.stringify({
|
|
329
306
|
type: 'sse',
|
|
330
307
|
id: message.id,
|
|
331
308
|
event,
|
|
332
309
|
data
|
|
333
|
-
};
|
|
334
|
-
this.ws.send(JSON.stringify(forwardMessage));
|
|
335
|
-
console.log(`[Tunnel Client] ✓ SSE event forwarded to tunnel server`);
|
|
336
|
-
}
|
|
337
|
-
else {
|
|
338
|
-
console.error(`[Tunnel Client] ❌ Cannot forward SSE event - WebSocket not open`);
|
|
310
|
+
}));
|
|
339
311
|
}
|
|
340
312
|
}
|
|
341
313
|
});
|
|
342
314
|
res.on('end', () => {
|
|
343
|
-
console.log(`[Tunnel Client] SSE stream ended for ${message.id}`);
|
|
344
315
|
if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
|
|
345
316
|
this.ws.send(JSON.stringify({
|
|
346
317
|
type: 'sse',
|
|
@@ -348,15 +319,10 @@ class DainTunnel extends events_1.EventEmitter {
|
|
|
348
319
|
event: 'close',
|
|
349
320
|
data: 'Connection closed'
|
|
350
321
|
}));
|
|
351
|
-
console.log(`[Tunnel Client] Sent close event to tunnel server`);
|
|
352
322
|
}
|
|
353
323
|
});
|
|
354
|
-
res.on('error', (error) => {
|
|
355
|
-
console.error(`[Tunnel Client] Response stream error for ${message.id}:`, error);
|
|
356
|
-
});
|
|
357
324
|
});
|
|
358
325
|
req.on('error', (error) => {
|
|
359
|
-
console.error(`[Tunnel Client] ❌ Request error for ${message.id}:`, error);
|
|
360
326
|
if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
|
|
361
327
|
this.ws.send(JSON.stringify({
|
|
362
328
|
type: 'sse',
|
|
@@ -368,20 +334,15 @@ class DainTunnel extends events_1.EventEmitter {
|
|
|
368
334
|
});
|
|
369
335
|
// Write request body for POST requests
|
|
370
336
|
if (message.body && message.method !== 'GET') {
|
|
371
|
-
|
|
372
|
-
console.log(`[Tunnel Client] Writing request body (${bodyBuffer.length} bytes)`);
|
|
373
|
-
req.write(bodyBuffer);
|
|
337
|
+
req.write(Buffer.from(message.body, 'base64'));
|
|
374
338
|
}
|
|
375
339
|
req.end();
|
|
376
|
-
console.log(`[Tunnel Client] Request sent to local service`);
|
|
377
340
|
// Store a reference to abort the connection later if needed
|
|
378
341
|
this.sseClients.set(message.id, req);
|
|
379
|
-
console.log(`[Tunnel Client] SSE client stored. Total SSE clients: ${this.sseClients.size}`);
|
|
380
342
|
this.emit("sse_connection", { id: message.id, path: message.path });
|
|
381
|
-
console.log(`[Tunnel Client] ====== SSE CONNECTION HANDLER COMPLETE ======`);
|
|
382
343
|
}
|
|
383
344
|
catch (error) {
|
|
384
|
-
console.error(
|
|
345
|
+
console.error('Error establishing SSE connection:', error);
|
|
385
346
|
if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
|
|
386
347
|
this.ws.send(JSON.stringify({
|
|
387
348
|
type: 'sse',
|
|
@@ -393,17 +354,11 @@ class DainTunnel extends events_1.EventEmitter {
|
|
|
393
354
|
}
|
|
394
355
|
}
|
|
395
356
|
handleSSEClose(message) {
|
|
396
|
-
console.log(`[Tunnel Client] SSE close requested for: ${message.id}`);
|
|
397
357
|
const client = this.sseClients.get(message.id);
|
|
398
358
|
if (client) {
|
|
399
|
-
console.log(`[Tunnel Client] Destroying SSE client: ${message.id}`);
|
|
400
359
|
// Abort the request if it's still active
|
|
401
360
|
client.destroy();
|
|
402
361
|
this.sseClients.delete(message.id);
|
|
403
|
-
console.log(`[Tunnel Client] SSE client destroyed. Remaining: ${this.sseClients.size}`);
|
|
404
|
-
}
|
|
405
|
-
else {
|
|
406
|
-
console.log(`[Tunnel Client] SSE client not found: ${message.id}`);
|
|
407
362
|
}
|
|
408
363
|
}
|
|
409
364
|
forwardRequest(request) {
|
package/dist/server/index.js
CHANGED
|
@@ -12,6 +12,31 @@ const cors_1 = __importDefault(require("cors"));
|
|
|
12
12
|
const auth_1 = require("@dainprotocol/service-sdk/service/auth");
|
|
13
13
|
const crypto_1 = require("crypto");
|
|
14
14
|
const url_1 = require("url");
|
|
15
|
+
/**
|
|
16
|
+
* Force immediate flush of SSE data to prevent buffering
|
|
17
|
+
* SSE requires immediate delivery; Node.js res.write() doesn't guarantee this
|
|
18
|
+
*
|
|
19
|
+
* @param res Express response object
|
|
20
|
+
*/
|
|
21
|
+
function flushSSE(res) {
|
|
22
|
+
try {
|
|
23
|
+
// Access underlying socket to force flush
|
|
24
|
+
// Type assertion needed as Express types don't expose socket methods
|
|
25
|
+
const socket = res.socket;
|
|
26
|
+
if (socket && typeof socket.flush === 'function') {
|
|
27
|
+
socket.flush();
|
|
28
|
+
}
|
|
29
|
+
// Fallback: try connection property (older Node.js versions)
|
|
30
|
+
const connection = res.connection;
|
|
31
|
+
if (connection && typeof connection.flush === 'function') {
|
|
32
|
+
connection.flush();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
// Flush is best-effort; log but don't fail the request
|
|
37
|
+
console.error('[SSE Flush] Error flushing socket:', error);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
15
40
|
class DainTunnelServer {
|
|
16
41
|
constructor(hostname, port) {
|
|
17
42
|
this.hostname = hostname;
|
|
@@ -300,55 +325,46 @@ class DainTunnelServer {
|
|
|
300
325
|
}
|
|
301
326
|
}
|
|
302
327
|
handleSSEMessage(data) {
|
|
303
|
-
var _a;
|
|
304
|
-
console.log(`[Tunnel Server] ====== RECEIVED SSE MESSAGE ======`);
|
|
305
|
-
console.log(`[Tunnel Server] SSE ID: ${data.id}`);
|
|
306
|
-
console.log(`[Tunnel Server] Event: ${data.event}`);
|
|
307
|
-
console.log(`[Tunnel Server] Data length: ${((_a = data.data) === null || _a === void 0 ? void 0 : _a.length) || 0}`);
|
|
308
|
-
console.log(`[Tunnel Server] Active SSE connections: ${this.sseConnections.size}`);
|
|
309
|
-
console.log(`[Tunnel Server] Active SSE IDs: ${Array.from(this.sseConnections.keys()).join(', ')}`);
|
|
310
328
|
const connection = this.sseConnections.get(data.id);
|
|
311
329
|
if (!connection) {
|
|
312
|
-
console.
|
|
313
|
-
console.error(`[Tunnel Server] Available connections: ${Array.from(this.sseConnections.keys()).join(', ')}`);
|
|
314
|
-
console.error(`[Tunnel Server] This means either:`);
|
|
315
|
-
console.error(`[Tunnel Server] 1. The connection was never created (handleSSERequest not called)`);
|
|
316
|
-
console.error(`[Tunnel Server] 2. The connection was already closed`);
|
|
317
|
-
console.error(`[Tunnel Server] 3. There's an ID mismatch`);
|
|
330
|
+
console.log(`SSE connection not found: ${data.id}`);
|
|
318
331
|
return;
|
|
319
332
|
}
|
|
320
|
-
console.log(`[Tunnel Server] ✓ SSE connection found for ${data.id}`);
|
|
321
333
|
const { res } = connection;
|
|
334
|
+
// Check if response is still writable before attempting to write
|
|
335
|
+
if (!res.writable) {
|
|
336
|
+
console.log(`SSE connection ${data.id} is no longer writable`);
|
|
337
|
+
this.sseConnections.delete(data.id);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
322
340
|
try {
|
|
323
341
|
if (data.event) {
|
|
324
342
|
res.write(`event: ${data.event}\n`);
|
|
325
|
-
console.log(`[Tunnel Server] Wrote event: ${data.event}`);
|
|
326
343
|
}
|
|
327
344
|
// Write data as-is (don't split - data is already properly formatted)
|
|
328
345
|
res.write(`data: ${data.data}\n`);
|
|
329
346
|
res.write('\n');
|
|
330
|
-
|
|
347
|
+
// Force immediate flush to prevent buffering
|
|
348
|
+
// This is critical for SSE - without it, events may be delayed or lost
|
|
349
|
+
flushSSE(res);
|
|
331
350
|
// Check if this is a "close" event
|
|
332
351
|
if (data.event === 'close') {
|
|
333
|
-
console.log(`[Tunnel Server] Close event received, ending SSE connection ${data.id}`);
|
|
334
|
-
// Clear keepalive interval if it exists
|
|
335
|
-
if (connection.keepaliveInterval) {
|
|
336
|
-
clearInterval(connection.keepaliveInterval);
|
|
337
|
-
console.log(`[Tunnel Server] Cleared keepalive interval for ${data.id}`);
|
|
338
|
-
}
|
|
339
352
|
res.end();
|
|
340
353
|
this.sseConnections.delete(data.id);
|
|
341
|
-
console.log(`[Tunnel Server] SSE connection closed. Remaining: ${this.sseConnections.size}`);
|
|
342
354
|
}
|
|
343
|
-
console.log(`[Tunnel Server] ====== SSE MESSAGE HANDLED ======`);
|
|
344
355
|
}
|
|
345
356
|
catch (error) {
|
|
346
|
-
console.error(`
|
|
347
|
-
// Clear keepalive interval if it exists
|
|
348
|
-
if (connection === null || connection === void 0 ? void 0 : connection.keepaliveInterval) {
|
|
349
|
-
clearInterval(connection.keepaliveInterval);
|
|
350
|
-
}
|
|
357
|
+
console.error(`Error writing SSE message for ${data.id}:`, error);
|
|
351
358
|
this.sseConnections.delete(data.id);
|
|
359
|
+
// Best-effort cleanup: try to end the response
|
|
360
|
+
try {
|
|
361
|
+
if (res.writable) {
|
|
362
|
+
res.end();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
catch (endError) {
|
|
366
|
+
console.error(`Error ending SSE connection ${data.id}:`, endError);
|
|
367
|
+
}
|
|
352
368
|
}
|
|
353
369
|
}
|
|
354
370
|
handleWebSocketMessage(data) {
|
|
@@ -379,14 +395,9 @@ class DainTunnelServer {
|
|
|
379
395
|
}
|
|
380
396
|
async handleRequest(req, res) {
|
|
381
397
|
const tunnelId = req.params.tunnelId;
|
|
382
|
-
// Log incoming request with headers for debugging
|
|
383
|
-
console.log(`[Tunnel Server] Incoming request: ${req.method} ${req.url} to tunnel ${tunnelId}`);
|
|
384
|
-
console.log(`[Tunnel Server] Accept header: "${req.headers.accept}"`);
|
|
385
|
-
console.log(`[Tunnel Server] Content-Type: "${req.headers['content-type']}"`);
|
|
386
398
|
// Check for upgraded connections (WebSockets) - these are handled by the WebSocket server
|
|
387
399
|
if (req.headers.upgrade && req.headers.upgrade.toLowerCase() === 'websocket') {
|
|
388
400
|
// This is handled by the WebSocket server now
|
|
389
|
-
console.log(`[Tunnel Server] WebSocket upgrade detected, skipping`);
|
|
390
401
|
return;
|
|
391
402
|
}
|
|
392
403
|
let tunnel;
|
|
@@ -404,12 +415,8 @@ class DainTunnelServer {
|
|
|
404
415
|
console.log(`Tunnel not found after retries: ${tunnelId}`);
|
|
405
416
|
return res.status(404).send("Tunnel not found");
|
|
406
417
|
}
|
|
407
|
-
// Check for SSE request
|
|
408
|
-
|
|
409
|
-
const isSSE = acceptHeader.includes('text/event-stream');
|
|
410
|
-
console.log(`[Tunnel Server] Is SSE request: ${isSSE} (accept: "${acceptHeader}")`);
|
|
411
|
-
if (isSSE) {
|
|
412
|
-
console.log(`[Tunnel Server] Routing to SSE handler`);
|
|
418
|
+
// Check for SSE request
|
|
419
|
+
if (req.headers.accept && req.headers.accept.includes('text/event-stream')) {
|
|
413
420
|
return this.handleSSERequest(req, res, tunnelId, tunnel);
|
|
414
421
|
}
|
|
415
422
|
// Handle regular HTTP request
|
|
@@ -432,118 +439,44 @@ class DainTunnelServer {
|
|
|
432
439
|
handleSSERequest(req, res, tunnelId, tunnel) {
|
|
433
440
|
// Setup SSE connection
|
|
434
441
|
const sseId = (0, uuid_1.v4)();
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
console.log(`[Tunnel Server] Keepalive sent for ${sseId}`);
|
|
472
|
-
}
|
|
473
|
-
catch (error) {
|
|
474
|
-
console.error(`[Tunnel Server] Error sending keepalive for ${sseId}:`, error);
|
|
475
|
-
clearInterval(keepaliveInterval);
|
|
476
|
-
this.sseConnections.delete(sseId);
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
else {
|
|
480
|
-
clearInterval(keepaliveInterval);
|
|
481
|
-
}
|
|
482
|
-
}, 5000);
|
|
483
|
-
// Store the SSE connection BEFORE notifying tunnel client
|
|
484
|
-
this.sseConnections.set(sseId, {
|
|
485
|
-
req,
|
|
486
|
-
res,
|
|
487
|
-
id: sseId,
|
|
488
|
-
tunnelId,
|
|
489
|
-
keepaliveInterval
|
|
490
|
-
});
|
|
491
|
-
console.log(`[Tunnel Server] SSE connection stored. Total connections: ${this.sseConnections.size}`);
|
|
492
|
-
console.log(`[Tunnel Server] Active SSE IDs: ${Array.from(this.sseConnections.keys()).join(', ')}`);
|
|
493
|
-
// Notify the tunnel client about the new SSE connection
|
|
494
|
-
const sseConnectionMessage = {
|
|
495
|
-
type: "sse_connection",
|
|
496
|
-
id: sseId,
|
|
497
|
-
path: req.url,
|
|
498
|
-
method: req.method, // Include HTTP method for POST tool calls
|
|
499
|
-
headers: req.headers,
|
|
500
|
-
body: req.method !== "GET" && req.body ? req.body.toString("base64") : undefined
|
|
501
|
-
};
|
|
502
|
-
console.log(`[Tunnel Server] Sending sse_connection message to tunnel client`);
|
|
503
|
-
try {
|
|
504
|
-
tunnel.ws.send(JSON.stringify(sseConnectionMessage));
|
|
505
|
-
console.log(`[Tunnel Server] sse_connection message sent successfully for ${sseId}`);
|
|
506
|
-
}
|
|
507
|
-
catch (sendError) {
|
|
508
|
-
console.error(`[Tunnel Server] FAILED to send sse_connection message for ${sseId}:`, sendError);
|
|
509
|
-
// Clean up the SSE connection if we can't notify the client
|
|
510
|
-
this.sseConnections.delete(sseId);
|
|
511
|
-
res.status(500).end();
|
|
512
|
-
return;
|
|
513
|
-
}
|
|
514
|
-
// Handle client disconnect
|
|
515
|
-
req.on('close', () => {
|
|
516
|
-
console.log(`[Tunnel Server] SSE client disconnected: ${sseId}`);
|
|
517
|
-
console.log(`[Tunnel Server] Connection was active for tunnel: ${tunnelId}`);
|
|
518
|
-
clearInterval(keepaliveInterval);
|
|
519
|
-
try {
|
|
520
|
-
tunnel.ws.send(JSON.stringify({
|
|
521
|
-
type: "sse_close",
|
|
522
|
-
id: sseId
|
|
523
|
-
}));
|
|
524
|
-
console.log(`[Tunnel Server] Sent sse_close message for ${sseId}`);
|
|
525
|
-
}
|
|
526
|
-
catch (closeError) {
|
|
527
|
-
console.error(`[Tunnel Server] Error sending sse_close message:`, closeError);
|
|
528
|
-
}
|
|
529
|
-
this.sseConnections.delete(sseId);
|
|
530
|
-
console.log(`[Tunnel Server] SSE connection removed. Remaining: ${this.sseConnections.size}`);
|
|
531
|
-
});
|
|
532
|
-
// Add error handler for the response
|
|
533
|
-
res.on('error', (error) => {
|
|
534
|
-
console.error(`[Tunnel Server] SSE response error for ${sseId}:`, error);
|
|
535
|
-
clearInterval(keepaliveInterval);
|
|
536
|
-
this.sseConnections.delete(sseId);
|
|
537
|
-
});
|
|
538
|
-
console.log(`[Tunnel Server] ====== SSE CONNECTION SETUP COMPLETE ======`);
|
|
539
|
-
}
|
|
540
|
-
catch (error) {
|
|
541
|
-
console.error(`[Tunnel Server] CRITICAL ERROR in handleSSERequest for ${sseId}:`, error);
|
|
442
|
+
// Set SSE headers with anti-buffering directives
|
|
443
|
+
res.writeHead(200, {
|
|
444
|
+
'Content-Type': 'text/event-stream',
|
|
445
|
+
'Cache-Control': 'no-cache',
|
|
446
|
+
'Connection': 'keep-alive',
|
|
447
|
+
'X-Accel-Buffering': 'no', // Disable nginx buffering
|
|
448
|
+
});
|
|
449
|
+
// Flush headers immediately to establish SSE connection
|
|
450
|
+
// Without this, headers may be buffered, delaying connection setup
|
|
451
|
+
res.flushHeaders();
|
|
452
|
+
// Send initial connection event
|
|
453
|
+
res.write('\n');
|
|
454
|
+
// Flush the initial write
|
|
455
|
+
flushSSE(res);
|
|
456
|
+
// Store the SSE connection
|
|
457
|
+
this.sseConnections.set(sseId, {
|
|
458
|
+
req,
|
|
459
|
+
res,
|
|
460
|
+
id: sseId,
|
|
461
|
+
tunnelId
|
|
462
|
+
});
|
|
463
|
+
// Notify the tunnel client about the new SSE connection
|
|
464
|
+
tunnel.ws.send(JSON.stringify({
|
|
465
|
+
type: "sse_connection",
|
|
466
|
+
id: sseId,
|
|
467
|
+
path: req.url,
|
|
468
|
+
method: req.method, // Include HTTP method for POST tool calls
|
|
469
|
+
headers: req.headers,
|
|
470
|
+
body: req.method !== "GET" && req.body ? req.body.toString("base64") : undefined
|
|
471
|
+
}));
|
|
472
|
+
// Handle client disconnect
|
|
473
|
+
req.on('close', () => {
|
|
474
|
+
tunnel.ws.send(JSON.stringify({
|
|
475
|
+
type: "sse_close",
|
|
476
|
+
id: sseId
|
|
477
|
+
}));
|
|
542
478
|
this.sseConnections.delete(sseId);
|
|
543
|
-
|
|
544
|
-
res.status(500).send('SSE setup failed');
|
|
545
|
-
}
|
|
546
|
-
}
|
|
479
|
+
});
|
|
547
480
|
}
|
|
548
481
|
removeTunnel(ws) {
|
|
549
482
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dainprotocol/tunnel",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.9",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"private": false,
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"author": "Ryan",
|
|
20
20
|
"license": "ISC",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@dainprotocol/service-sdk": "^2.0.
|
|
22
|
+
"@dainprotocol/service-sdk": "^2.0.30",
|
|
23
23
|
"@types/body-parser": "^1.19.5",
|
|
24
24
|
"@types/cors": "^2.8.17",
|
|
25
25
|
"@types/eventsource": "^3.0.0",
|