cdp-tunnel 2.5.11 → 2.5.12
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 +1 -1
- package/server/proxy-server.js +133 -177
package/package.json
CHANGED
package/server/proxy-server.js
CHANGED
|
@@ -322,6 +322,132 @@ wss.on('connection', (ws, req) => {
|
|
|
322
322
|
}
|
|
323
323
|
});
|
|
324
324
|
|
|
325
|
+
function cleanupClient(ws, id, reason) {
|
|
326
|
+
const sessionsToClean = [];
|
|
327
|
+
for (const [sessionId, clientId] of sessionToClientId.entries()) {
|
|
328
|
+
if (clientId === id) {
|
|
329
|
+
sessionsToClean.push(sessionId);
|
|
330
|
+
sessionToClientId.delete(sessionId);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
clientConnections.delete(ws);
|
|
335
|
+
clientById.delete(id);
|
|
336
|
+
|
|
337
|
+
logConnectionEvent('CLIENT_DISCONNECTED', {
|
|
338
|
+
id,
|
|
339
|
+
reason,
|
|
340
|
+
sessionsCleaned: sessionsToClean.length,
|
|
341
|
+
totalPlugins: pluginConnections.size,
|
|
342
|
+
totalClients: clientConnections.size
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
logDisconnect('CLIENT_CLEANUP', {
|
|
346
|
+
clientId: id,
|
|
347
|
+
reason,
|
|
348
|
+
sessionsLost: sessionsToClean.length,
|
|
349
|
+
cdpMethodsUsed: ws.cdpTrace ? [...new Set(ws.cdpTrace)] : [],
|
|
350
|
+
uptime: ws.connectedAt ? `${((Date.now() - ws.connectedAt) / 1000).toFixed(0)}s` : 'unknown',
|
|
351
|
+
remainingClients: clientConnections.size,
|
|
352
|
+
pluginAlive: pluginConnections.size > 0,
|
|
353
|
+
pairedPluginId: ws.pairedPlugin?.id || null
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
if (ws.cdpTrace && ws.cdpTrace.length && shouldLog('debug')) {
|
|
357
|
+
const unique = [...new Set(ws.cdpTrace)];
|
|
358
|
+
console.log(`[CDP TRACE] ${id} methods (${ws.cdpTrace.length}): ${unique.join(', ')}`);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (ws.pairedPlugin) {
|
|
362
|
+
safeSend(ws.pairedPlugin, JSON.stringify({
|
|
363
|
+
type: 'client-disconnected',
|
|
364
|
+
clientId: id,
|
|
365
|
+
sessions: sessionsToClean
|
|
366
|
+
}), 'plugin');
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
broadcastClientList();
|
|
370
|
+
|
|
371
|
+
for (const [tId, cId] of targetIdToClientId.entries()) {
|
|
372
|
+
if (cId === id) targetIdToClientId.delete(tId);
|
|
373
|
+
}
|
|
374
|
+
for (const [bcId, cId] of browserContextToClientId.entries()) {
|
|
375
|
+
if (cId === id) browserContextToClientId.delete(bcId);
|
|
376
|
+
}
|
|
377
|
+
if (clientIdToBrowserContext.has(id)) {
|
|
378
|
+
clientIdToBrowserContext.delete(id);
|
|
379
|
+
}
|
|
380
|
+
for (const [gId, mapping] of globalRequestIdMap.entries()) {
|
|
381
|
+
if (mapping.clientId === id) globalRequestIdMap.delete(gId);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (ws.pairedPlugin) {
|
|
385
|
+
ws.pairedPlugin.pairedClientId = null;
|
|
386
|
+
}
|
|
387
|
+
connectionPairs.delete(id);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function sendPendingRequestErrors(pluginWs) {
|
|
391
|
+
const toDelete = [];
|
|
392
|
+
for (const [gId, mapping] of globalRequestIdMap.entries()) {
|
|
393
|
+
const clientWs = clientById.get(mapping.clientId);
|
|
394
|
+
if (clientWs && clientWs.pairedPlugin === pluginWs) {
|
|
395
|
+
const errorResponse = {
|
|
396
|
+
id: mapping.originalId,
|
|
397
|
+
error: { code: -32000, message: 'Plugin disconnected: request cancelled' }
|
|
398
|
+
};
|
|
399
|
+
if (mapping.sessionId) {
|
|
400
|
+
errorResponse.sessionId = mapping.sessionId;
|
|
401
|
+
}
|
|
402
|
+
safeSend(clientWs, JSON.stringify(errorResponse), 'client');
|
|
403
|
+
toDelete.push(gId);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
toDelete.forEach(gId => globalRequestIdMap.delete(gId));
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function cleanupPlugin(ws, id, reason) {
|
|
410
|
+
pluginConnections.delete(ws);
|
|
411
|
+
|
|
412
|
+
if (pluginConnections.size === 0) {
|
|
413
|
+
updateExtensionState(false);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
sendPendingRequestErrors(ws);
|
|
417
|
+
|
|
418
|
+
const affectedClients = [];
|
|
419
|
+
clientConnections.forEach(clientWs => {
|
|
420
|
+
if (clientWs.pairedPlugin === ws) {
|
|
421
|
+
if (clientWs.pluginMessageHandler) {
|
|
422
|
+
ws.off('message', clientWs.pluginMessageHandler);
|
|
423
|
+
clientWs.pluginMessageHandler = null;
|
|
424
|
+
}
|
|
425
|
+
clientWs.pairedPlugin = null;
|
|
426
|
+
affectedClients.push(clientWs.id);
|
|
427
|
+
if (clientWs.readyState === WebSocket.OPEN) {
|
|
428
|
+
clientWs.send(JSON.stringify({
|
|
429
|
+
type: 'plugin-disconnected',
|
|
430
|
+
message: 'Plugin connection lost'
|
|
431
|
+
}));
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
logDisconnect('PLUGIN_CLEANUP', {
|
|
437
|
+
pluginId: id,
|
|
438
|
+
reason,
|
|
439
|
+
remainingPlugins: pluginConnections.size,
|
|
440
|
+
affectedClients,
|
|
441
|
+
uptime: ws.connectedAt ? `${((Date.now() - ws.connectedAt) / 1000).toFixed(0)}s` : 'unknown',
|
|
442
|
+
activeSessions: sessionToClientId.size,
|
|
443
|
+
pendingRequests: pendingAttachRequests.size
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
if (ws.pairedClientId) {
|
|
447
|
+
connectionPairs.delete(ws.pairedClientId);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
325
451
|
/**
|
|
326
452
|
* 处理 Chrome 扩展连接
|
|
327
453
|
*/
|
|
@@ -687,15 +813,12 @@ function handlePluginConnection(ws, clientInfo) {
|
|
|
687
813
|
}
|
|
688
814
|
});
|
|
689
815
|
|
|
690
|
-
// 连接关闭
|
|
691
816
|
ws.on('close', (code, reason) => {
|
|
692
|
-
pluginConnections.delete(ws);
|
|
693
817
|
if (shouldLog('info')) {
|
|
694
818
|
console.log(`\n[PLUGIN DISCONNECTED] ${id}`);
|
|
695
819
|
console.log(` - Code: ${code}, Reason: ${reason || 'none'}`);
|
|
696
820
|
console.log(` - Total plugin connections: ${pluginConnections.size}`);
|
|
697
821
|
}
|
|
698
|
-
|
|
699
822
|
logConnectionEvent('PLUGIN_DISCONNECTED', {
|
|
700
823
|
id,
|
|
701
824
|
code,
|
|
@@ -703,51 +826,7 @@ function handlePluginConnection(ws, clientInfo) {
|
|
|
703
826
|
totalPlugins: pluginConnections.size,
|
|
704
827
|
totalClients: clientConnections.size
|
|
705
828
|
});
|
|
706
|
-
|
|
707
|
-
if (pluginConnections.size === 0) {
|
|
708
|
-
updateExtensionState(false);
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
// 清理配对关系并通知所有受影响的 Client
|
|
712
|
-
const affectedClients = [];
|
|
713
|
-
clientConnections.forEach(clientWs => {
|
|
714
|
-
if (clientWs.pairedPlugin === ws) {
|
|
715
|
-
// 清理 page 连接的事件监听器
|
|
716
|
-
if (clientWs.pluginMessageHandler) {
|
|
717
|
-
ws.off('message', clientWs.pluginMessageHandler);
|
|
718
|
-
clientWs.pluginMessageHandler = null;
|
|
719
|
-
}
|
|
720
|
-
clientWs.pairedPlugin = null;
|
|
721
|
-
affectedClients.push(clientWs.id);
|
|
722
|
-
if (clientWs.readyState === WebSocket.OPEN) {
|
|
723
|
-
clientWs.send(JSON.stringify({
|
|
724
|
-
type: 'plugin-disconnected',
|
|
725
|
-
message: 'Plugin connection lost'
|
|
726
|
-
}));
|
|
727
|
-
}
|
|
728
|
-
if (shouldLog('debug')) {
|
|
729
|
-
console.log(` - Cleared pairedPlugin for client: ${clientWs.id}`);
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
});
|
|
733
|
-
|
|
734
|
-
logDisconnect('PLUGIN_DISCONNECTED', {
|
|
735
|
-
pluginId: id,
|
|
736
|
-
code, reason: reason?.toString() || 'none',
|
|
737
|
-
remainingPlugins: pluginConnections.size,
|
|
738
|
-
affectedClients,
|
|
739
|
-
uptime: ws.connectedAt ? `${((Date.now() - ws.connectedAt) / 1000).toFixed(0)}s` : 'unknown',
|
|
740
|
-
activeSessions: sessionToClientId.size,
|
|
741
|
-
pendingRequests: pendingAttachRequests.size
|
|
742
|
-
});
|
|
743
|
-
|
|
744
|
-
if (affectedClients.length > 0) {
|
|
745
|
-
logConnectionEvent('PLUGIN_DISCONNECT_AFFECTED_CLIENTS', { pluginId: id, affectedClients });
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
if (ws.pairedClientId) {
|
|
749
|
-
connectionPairs.delete(ws.pairedClientId);
|
|
750
|
-
}
|
|
829
|
+
cleanupPlugin(ws, id, `close:${code}`);
|
|
751
830
|
});
|
|
752
831
|
|
|
753
832
|
// 错误处理
|
|
@@ -998,111 +1077,24 @@ function handleClientConnection(ws, clientInfo, customClientId = null) {
|
|
|
998
1077
|
}
|
|
999
1078
|
});
|
|
1000
1079
|
|
|
1001
|
-
// 连接关闭
|
|
1002
1080
|
ws.on('close', async (code, reason) => {
|
|
1003
|
-
// 记录断开事件到日志文件
|
|
1004
1081
|
logCDP('EVENT', `CLIENT DISCONNECTED id=${id} code=${code} reason=${reason.toString() || 'none'}`);
|
|
1005
|
-
|
|
1006
|
-
// 收集该 client 的所有 session
|
|
1007
|
-
const sessionsToClean = [];
|
|
1008
|
-
for (const [sessionId, clientId] of sessionToClientId.entries()) {
|
|
1009
|
-
if (clientId === id) {
|
|
1010
|
-
sessionsToClean.push(sessionId);
|
|
1011
|
-
sessionToClientId.delete(sessionId);
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
clientConnections.delete(ws);
|
|
1016
|
-
clientById.delete(id);
|
|
1017
1082
|
if (shouldLog('info')) {
|
|
1018
1083
|
console.log(`\n[CLIENT DISCONNECTED] ${id}`);
|
|
1019
1084
|
console.log(` - Code: ${code}, Reason: ${reason || 'none'}`);
|
|
1020
|
-
console.log(` - Sessions to clean: ${sessionsToClean.length}`);
|
|
1021
|
-
console.log(` - Total client connections: ${clientConnections.size}`);
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
logConnectionEvent('CLIENT_DISCONNECTED', {
|
|
1025
|
-
id,
|
|
1026
|
-
code,
|
|
1027
|
-
reason: reason?.toString() || 'none',
|
|
1028
|
-
sessionsCleaned: sessionsToClean.length,
|
|
1029
|
-
totalPlugins: pluginConnections.size,
|
|
1030
|
-
totalClients: clientConnections.size
|
|
1031
|
-
});
|
|
1032
|
-
|
|
1033
|
-
const isUnexpected = code !== 1000 && code !== 1001;
|
|
1034
|
-
if (isUnexpected) {
|
|
1035
|
-
logDisconnect('CLIENT_DISCONNECTED_UNEXPECTED', {
|
|
1036
|
-
clientId: id,
|
|
1037
|
-
code, reason: reason?.toString() || 'none',
|
|
1038
|
-
sessionsLost: sessionsToClean.length,
|
|
1039
|
-
cdpMethodsUsed: ws.cdpTrace ? [...new Set(ws.cdpTrace)] : [],
|
|
1040
|
-
uptime: ws.connectedAt ? `${((Date.now() - ws.connectedAt) / 1000).toFixed(0)}s` : 'unknown',
|
|
1041
|
-
remainingClients: clientConnections.size,
|
|
1042
|
-
pluginAlive: pluginConnections.size > 0,
|
|
1043
|
-
pairedPluginId: ws.pairedPlugin?.id || null
|
|
1044
|
-
});
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
if (ws.cdpTrace && ws.cdpTrace.length && shouldLog('debug')) {
|
|
1048
|
-
const unique = [...new Set(ws.cdpTrace)];
|
|
1049
|
-
console.log(`[CDP TRACE] ${id} methods (${ws.cdpTrace.length}): ${unique.join(', ')}`);
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
// 向 plugin 发送清理命令
|
|
1053
|
-
if (ws.pairedPlugin) {
|
|
1054
|
-
safeSend(ws.pairedPlugin, JSON.stringify({
|
|
1055
|
-
type: 'client-disconnected',
|
|
1056
|
-
clientId: id,
|
|
1057
|
-
sessions: sessionsToClean
|
|
1058
|
-
}), 'plugin');
|
|
1059
|
-
if (shouldLog('debug')) {
|
|
1060
|
-
console.log(` - Notified plugin of client disconnect`);
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
// 广播更新后的客户端列表
|
|
1065
|
-
broadcastClientList();
|
|
1066
|
-
|
|
1067
|
-
// 清理该 client 的所有映射
|
|
1068
|
-
for (const [tId, cId] of targetIdToClientId.entries()) {
|
|
1069
|
-
if (cId === id) targetIdToClientId.delete(tId);
|
|
1070
|
-
}
|
|
1071
|
-
for (const [bcId, cId] of browserContextToClientId.entries()) {
|
|
1072
|
-
if (cId === id) browserContextToClientId.delete(bcId);
|
|
1073
|
-
}
|
|
1074
|
-
if (clientIdToBrowserContext.has(id)) {
|
|
1075
|
-
clientIdToBrowserContext.delete(id);
|
|
1076
|
-
}
|
|
1077
|
-
for (const [gId, mapping] of globalRequestIdMap.entries()) {
|
|
1078
|
-
if (mapping.clientId === id) globalRequestIdMap.delete(gId);
|
|
1079
1085
|
}
|
|
1080
|
-
|
|
1081
|
-
// 清理配对关系
|
|
1082
|
-
if (ws.pairedPlugin) {
|
|
1083
|
-
ws.pairedPlugin.pairedClientId = null;
|
|
1084
|
-
}
|
|
1085
|
-
connectionPairs.delete(id);
|
|
1086
|
+
cleanupClient(ws, id, `close:${code}`);
|
|
1086
1087
|
});
|
|
1087
1088
|
|
|
1088
|
-
// 错误处理
|
|
1089
1089
|
ws.on('error', (error) => {
|
|
1090
1090
|
console.error(`[CLIENT ERROR] ${id}:`, error.message);
|
|
1091
|
-
|
|
1092
1091
|
logConnectionEvent('CLIENT_ERROR', {
|
|
1093
1092
|
id,
|
|
1094
1093
|
error: error.message,
|
|
1095
1094
|
totalPlugins: pluginConnections.size,
|
|
1096
1095
|
totalClients: clientConnections.size
|
|
1097
1096
|
});
|
|
1098
|
-
|
|
1099
|
-
clientConnections.delete(ws);
|
|
1100
|
-
clientById.delete(id);
|
|
1101
|
-
|
|
1102
|
-
if (ws.pairedPlugin) {
|
|
1103
|
-
ws.pairedPlugin.pairedClientId = null;
|
|
1104
|
-
}
|
|
1105
|
-
connectionPairs.delete(id);
|
|
1097
|
+
cleanupClient(ws, id, `error:${error.message}`);
|
|
1106
1098
|
});
|
|
1107
1099
|
}
|
|
1108
1100
|
|
|
@@ -1430,29 +1422,13 @@ const heartbeatInterval = setInterval(() => {
|
|
|
1430
1422
|
const now = new Date().toISOString();
|
|
1431
1423
|
const nowMs = Date.now();
|
|
1432
1424
|
|
|
1433
|
-
// 检查 plugin 连接
|
|
1434
1425
|
pluginConnections.forEach((ws) => {
|
|
1435
1426
|
if (!ws.isAlive) {
|
|
1436
1427
|
if (shouldLog('warn')) {
|
|
1437
1428
|
console.log(`[${now}] Plugin ${ws.id} not responding, terminating...`);
|
|
1438
1429
|
}
|
|
1439
1430
|
logConnectionEvent('HEARTBEAT_TIMEOUT', { type: 'plugin', id: ws.id });
|
|
1440
|
-
|
|
1441
|
-
pluginId: ws.id,
|
|
1442
|
-
pairedClientId: ws.pairedClientId || null,
|
|
1443
|
-
uptime: ws.connectedAt ? `${((Date.now() - ws.connectedAt) / 1000).toFixed(0)}s` : 'unknown',
|
|
1444
|
-
remainingPlugins: pluginConnections.size,
|
|
1445
|
-
activeClients: clientConnections.size,
|
|
1446
|
-
activeSessions: sessionToClientId.size
|
|
1447
|
-
});
|
|
1448
|
-
pluginConnections.delete(ws);
|
|
1449
|
-
if (ws.pairedClientId) {
|
|
1450
|
-
connectionPairs.delete(ws.pairedClientId);
|
|
1451
|
-
const clientWs = clientById.get(ws.pairedClientId);
|
|
1452
|
-
if (clientWs) {
|
|
1453
|
-
clientWs.pairedPlugin = null;
|
|
1454
|
-
}
|
|
1455
|
-
}
|
|
1431
|
+
cleanupPlugin(ws, ws.id, 'heartbeat_timeout');
|
|
1456
1432
|
return ws.terminate();
|
|
1457
1433
|
}
|
|
1458
1434
|
ws.isAlive = false;
|
|
@@ -1460,29 +1436,16 @@ const heartbeatInterval = setInterval(() => {
|
|
|
1460
1436
|
logConnectionEvent('HEARTBEAT_PING', { type: 'plugin', id: ws.id, bufferedAmount: ws.bufferedAmount });
|
|
1461
1437
|
});
|
|
1462
1438
|
|
|
1463
|
-
// 检查 client 连接
|
|
1464
1439
|
clientConnections.forEach((ws) => {
|
|
1465
1440
|
if (!ws.isAlive) {
|
|
1466
1441
|
if (shouldLog('warn')) {
|
|
1467
1442
|
console.log(`[${now}] Client ${ws.id} not responding, terminating...`);
|
|
1468
1443
|
}
|
|
1469
1444
|
logConnectionEvent('HEARTBEAT_TIMEOUT', { type: 'client', id: ws.id });
|
|
1470
|
-
|
|
1471
|
-
clientId: ws.id,
|
|
1472
|
-
uptime: ws.connectedAt ? `${((Date.now() - ws.connectedAt) / 1000).toFixed(0)}s` : 'unknown',
|
|
1473
|
-
remainingClients: clientConnections.size,
|
|
1474
|
-
pluginAlive: pluginConnections.size > 0
|
|
1475
|
-
});
|
|
1476
|
-
clientConnections.delete(ws);
|
|
1477
|
-
clientById.delete(ws.id);
|
|
1478
|
-
if (ws.pairedPlugin) {
|
|
1479
|
-
ws.pairedPlugin.pairedClientId = null;
|
|
1480
|
-
}
|
|
1481
|
-
connectionPairs.delete(ws.id);
|
|
1445
|
+
cleanupClient(ws, ws.id, 'heartbeat_timeout');
|
|
1482
1446
|
return ws.terminate();
|
|
1483
1447
|
}
|
|
1484
1448
|
|
|
1485
|
-
// 检查空闲超时
|
|
1486
1449
|
if (ws.lastActivityTime && (nowMs - ws.lastActivityTime > CONFIG.CLIENT_IDLE_TIMEOUT)) {
|
|
1487
1450
|
const idleSeconds = Math.round((nowMs - ws.lastActivityTime) / 1000);
|
|
1488
1451
|
if (shouldLog('info')) {
|
|
@@ -1512,10 +1475,7 @@ setInterval(() => {
|
|
|
1512
1475
|
}
|
|
1513
1476
|
});
|
|
1514
1477
|
toRemove.forEach(ws => {
|
|
1515
|
-
|
|
1516
|
-
if (shouldLog('debug')) {
|
|
1517
|
-
console.log(`[CLEANUP] Removed zombie plugin: ${ws.id}, state: ${ws.readyState}`);
|
|
1518
|
-
}
|
|
1478
|
+
cleanupPlugin(ws, ws.id, 'zombie_cleanup');
|
|
1519
1479
|
});
|
|
1520
1480
|
|
|
1521
1481
|
toRemove.length = 0;
|
|
@@ -1525,11 +1485,7 @@ setInterval(() => {
|
|
|
1525
1485
|
}
|
|
1526
1486
|
});
|
|
1527
1487
|
toRemove.forEach(ws => {
|
|
1528
|
-
|
|
1529
|
-
clientById.delete(ws.id);
|
|
1530
|
-
if (shouldLog('debug')) {
|
|
1531
|
-
console.log(`[CLEANUP] Removed zombie client: ${ws.id}, state: ${ws.readyState}`);
|
|
1532
|
-
}
|
|
1488
|
+
cleanupClient(ws, ws.id, 'zombie_cleanup');
|
|
1533
1489
|
});
|
|
1534
1490
|
}, 60000);
|
|
1535
1491
|
|