[dtd] Enable web socket ping to avoid dropped idle connections

This enables the web socket ping by default at an interval of 15s. Having ping enables can prevent proxies/antivirus from dropping the web connections if they have no traffic for some period.

Having Norton 360 installed results in idle DTD connections dropping after 60s even if every feature is disabled, because the connections still go through it and it still drops idle connections.

I implemented this in the server because it applies to all uses of DTD regardless of client and it was easier to do here because pingInterval isn't exposed everywhere (but pkg:shelf does have a pass-through).

See https://github.com/Dart-Code/Dart-Code/issues/5794

Fixes https://github.com/dart-lang/sdk/issues/62148

Change-Id: If9df674b061b397cef1aa321602b563f44b9525d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/465680
Reviewed-by: Jessy Yameogo <yjessy@google.com>
Commit-Queue: Ben Konyi <bkonyi@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
This commit is contained in:
Danny Tuppeny
2025-12-09 10:42:23 -08:00
committed by Commit Queue
parent 0f9d2927b5
commit 5d3a0c2f3c
2 changed files with 47 additions and 8 deletions
+32 -8
View File
@@ -60,6 +60,13 @@ enum DartToolingDaemonOptions {
negatable: false,
help: 'Uses fake analytics instances for the UnifiedAnalytics service.',
hide: true,
),
pingInterval.option(
'ping-interval',
defaultsTo: '15',
help: 'Sets the WebSocket ping interval in seconds (0 to disable). '
'Enabling ping helps avoid connections being dropped by some proxies/'
'antivirus products if a connection has no traffic for some period.',
);
const DartToolingDaemonOptions.flag(
@@ -134,6 +141,7 @@ class DartToolingDaemon {
bool ipv6 = false,
bool shouldLogRequests = false,
bool useFakeAnalytics = false,
this.pingInterval,
}) : _ipv6 = ipv6,
_uriAuthCode = disableServiceAuthCodes ? null : _generateSecret(),
_shouldLogRequests = shouldLogRequests {
@@ -153,6 +161,12 @@ class DartToolingDaemon {
}
static const _kSseHandlerPath = '\$debugHandler';
/// The ping interval to be set on any WebSocket connections.
///
/// Having ping enabled can prevent proxies (including antivirus) from
/// dropping connections to DTD because they appear idle.
final Duration? pingInterval;
/// Manages the streams for the current [DartToolingDaemon] service.
late final DTDStreamManager streamManager;
@@ -252,6 +266,12 @@ class DartToolingDaemon {
parsedArgs[DartToolingDaemonOptions.fakeAnalytics.name];
final port =
int.tryParse(parsedArgs[DartToolingDaemonOptions.port.name]) ?? 0;
final pingIntervalSeconds =
int.tryParse(parsedArgs[DartToolingDaemonOptions.pingInterval.name]) ??
15;
final pingInterval = pingIntervalSeconds == 0
? null
: Duration(seconds: pingIntervalSeconds);
final secret = _generateSecret();
final dtd = DartToolingDaemon._(
@@ -261,6 +281,7 @@ class DartToolingDaemon {
ipv6: ipv6,
shouldLogRequests: shouldLogRequests,
useFakeAnalytics: useFakeAnalytics,
pingInterval: pingInterval,
);
await dtd._startService(port: port);
if (machineMode) {
@@ -317,14 +338,17 @@ class DartToolingDaemon {
// Note: the WebSocketChannel type below is needed for compatibility with
// package:shelf_web_socket v2.
Handler _webSocketHandler() => webSocketHandler((WebSocketChannel ws, _) {
final client = DTDClient.fromWebSocket(
this,
ws,
);
_registerInternalServiceMethods(client);
clientManager.addClient(client);
});
Handler _webSocketHandler() => webSocketHandler(
(WebSocketChannel ws, _) {
final client = DTDClient.fromWebSocket(
this,
ws,
);
_registerInternalServiceMethods(client);
clientManager.addClient(client);
},
pingInterval: pingInterval,
);
Handler _sseHandler() {
final handler = SseHandler(
+15
View File
@@ -578,6 +578,21 @@ void main() {
uri = dtd!.uri!.toString();
expect(Uri.parse(uri).port, testPort);
});
group('ping-interval', () {
test('15s by default', () async {
dtd = await DartToolingDaemon.startService([]);
expect(dtd!.pingInterval, Duration(seconds: 15));
});
test('explicit', () async {
dtd = await DartToolingDaemon.startService(['--ping-interval=1']);
expect(dtd!.pingInterval, Duration(seconds: 1));
});
test('disabled', () async {
dtd = await DartToolingDaemon.startService(['--ping-interval=0']);
expect(dtd!.pingInterval, isNull);
});
});
});
}