Skip to content

Commit 1f9065d

Browse files
author
GitLab CI
committed
feat: CDP auto-reconnect on connection drop
- _onDisconnect triggers async auto-reconnect with exponential backoff (1s→16s, 5 attempts) - _call waits up to 30s for in-progress reconnection instead of immediately throwing - Handles CfT restart, network blip, Chrome crash gracefully - Re-enables Page/DOM/Runtime domains after reconnection
1 parent 24fe0c5 commit 1f9065d

File tree

1 file changed

+54
-0
lines changed

1 file changed

+54
-0
lines changed

lib/src/bridge/cdp_driver.dart

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,6 +1774,13 @@ class CdpDriver implements AppDriver {
17741774

17751775
Future<Map<String, dynamic>> _call(String method,
17761776
[Map<String, dynamic>? params]) async {
1777+
// If disconnected but reconnecting, wait up to 30s for reconnection
1778+
if ((_ws == null || !_connected) && _reconnecting) {
1779+
for (int i = 0; i < 60; i++) {
1780+
await Future.delayed(const Duration(milliseconds: 500));
1781+
if (_connected && _ws != null) break;
1782+
}
1783+
}
17771784
if (_ws == null || !_connected) {
17781785
throw Exception('Not connected via CDP');
17791786
}
@@ -2057,9 +2064,56 @@ function _dqAll(sel, root) {
20572064
}
20582065
}
20592066

2067+
/// Whether auto-reconnect is in progress.
2068+
bool _reconnecting = false;
2069+
2070+
/// Max auto-reconnect attempts.
2071+
static const int _maxReconnectAttempts = 5;
2072+
20602073
void _onDisconnect() {
20612074
_connected = false;
20622075
_failAllPending('Connection lost');
2076+
// Trigger auto-reconnect (non-blocking)
2077+
_autoReconnect();
2078+
}
2079+
2080+
/// Auto-reconnect to CDP when connection drops (e.g. CfT restart).
2081+
Future<void> _autoReconnect() async {
2082+
if (_reconnecting) return;
2083+
_reconnecting = true;
2084+
try {
2085+
for (int attempt = 1; attempt <= _maxReconnectAttempts; attempt++) {
2086+
// Exponential backoff: 1s, 2s, 4s, 8s, 16s
2087+
await Future.delayed(Duration(seconds: 1 << (attempt - 1)));
2088+
try {
2089+
final wsUrl = await _discoverTarget();
2090+
if (wsUrl == null) continue;
2091+
2092+
_ws = await WebSocket.connect(wsUrl)
2093+
.timeout(const Duration(seconds: 10));
2094+
_connected = true;
2095+
2096+
_ws!.listen(
2097+
_onMessage,
2098+
onDone: _onDisconnect,
2099+
onError: (_) => _onDisconnect(),
2100+
cancelOnError: false,
2101+
);
2102+
2103+
// Re-enable required CDP domains
2104+
await Future.wait([
2105+
_call('Page.enable'),
2106+
_call('DOM.enable'),
2107+
_call('Runtime.enable'),
2108+
]);
2109+
return; // Success
2110+
} catch (_) {
2111+
// Retry
2112+
}
2113+
}
2114+
} finally {
2115+
_reconnecting = false;
2116+
}
20632117
}
20642118

20652119
void _failAllPending(String reason) {

0 commit comments

Comments
 (0)