From 5d3a0c2f3ccd1e7f3dda68199f4db493e4f0feb7 Mon Sep 17 00:00:00 2001 From: Danny Tuppeny Date: Tue, 9 Dec 2025 10:42:23 -0800 Subject: [PATCH] [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 Commit-Queue: Ben Konyi Reviewed-by: Ben Konyi --- pkg/dtd_impl/lib/src/dart_tooling_daemon.dart | 40 +++++++++++++++---- pkg/dtd_impl/test/dtd_test.dart | 15 +++++++ 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/pkg/dtd_impl/lib/src/dart_tooling_daemon.dart b/pkg/dtd_impl/lib/src/dart_tooling_daemon.dart index db51438294f..c970ac681cf 100644 --- a/pkg/dtd_impl/lib/src/dart_tooling_daemon.dart +++ b/pkg/dtd_impl/lib/src/dart_tooling_daemon.dart @@ -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( diff --git a/pkg/dtd_impl/test/dtd_test.dart b/pkg/dtd_impl/test/dtd_test.dart index b78e1393551..6c3327c55a1 100644 --- a/pkg/dtd_impl/test/dtd_test.dart +++ b/pkg/dtd_impl/test/dtd_test.dart @@ -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); + }); + }); }); }