Skip to content

Commit 1fe0895

Browse files
committed
implemented socket-based dwds
1 parent a321863 commit 1fe0895

24 files changed

+2964
-426
lines changed

dwds/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 24.3.12-wip
2+
3+
- Removed DWDS requirement for Chrome Debug Port by implementing a WebSocket-based communication protocol that provides essential developer tooling (hot reload, service extensions) when Chrome debugger access is unavailable. - [#2605](https://github.com/dart-lang/webdev/issues/2605)
4+
- Added WebSocket-based hot reload and service extension support via new `WebSocketProxyService` class that implements VM service protocol over WebSockets.
5+
- Created new files: `WebSocketProxyService`, `WebSocketDebugService`, `WebSocketAppDebugServices`, and `WebSocketDwdsVmClient` to support socket-based DWDS functionality.
6+
- Enhanced `DevHandler` with `useWebSocketConnection` flag to toggle between Chrome-based and WebSocket-based communication protocols.
7+
18
## 24.3.11
29

310
- Changed DWDS to always inject the client and added `useDwdsWebSocketConnection` flag to control communication protocol: when true uses socket-based implementation, when false uses Chrome-based communication protocol.

dwds/lib/dart_web_debug_service.dart

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@ class Dwds {
3131
final DevHandler _devHandler;
3232
final AssetReader _assetReader;
3333
final bool _enableDebugging;
34+
final bool _useDwdsWebSocketConnection;
3435

3536
Dwds._(
3637
this.middleware,
3738
this._devTools,
3839
this._devHandler,
3940
this._assetReader,
4041
this._enableDebugging,
42+
this._useDwdsWebSocketConnection,
4143
) : handler = _devHandler.handler;
4244

4345
Stream<AppConnection> get connectedApps => _devHandler.connectedApps;
@@ -53,14 +55,57 @@ class Dwds {
5355
await _assetReader.close();
5456
}
5557

58+
/// Creates a debug connection for the given app connection.
59+
///
60+
/// Returns a [DebugConnection] that wraps the appropriate debug services
61+
/// based on the connection type (WebSocket or Chrome-based).
5662
Future<DebugConnection> debugConnection(AppConnection appConnection) async {
5763
if (!_enableDebugging) throw StateError('Debugging is not enabled.');
64+
5865
final appDebugServices = await _devHandler.loadAppServices(appConnection);
59-
final chromeProxyService = appDebugServices.chromeProxyService;
60-
await chromeProxyService.isInitialized;
66+
67+
// Initialize the appropriate proxy service based on connection type
68+
if (_useDwdsWebSocketConnection) {
69+
await _initializeWebSocketService(appDebugServices);
70+
} else {
71+
await _initializeChromeService(appDebugServices);
72+
}
73+
6174
return DebugConnection(appDebugServices);
6275
}
6376

77+
/// Initializes and waits for WebSocket proxy service to be ready.
78+
Future<void> _initializeWebSocketService(dynamic appDebugServices) async {
79+
try {
80+
final webSocketProxyService = appDebugServices.webSocketProxyService;
81+
if (webSocketProxyService != null) {
82+
await webSocketProxyService.isInitialized;
83+
_logger.fine('WebSocket proxy service initialized successfully');
84+
} else {
85+
_logger.warning('WebSocket proxy service is null');
86+
}
87+
} catch (e) {
88+
_logger.severe('Failed to initialize WebSocket proxy service: $e');
89+
rethrow;
90+
}
91+
}
92+
93+
/// Initializes and waits for Chrome proxy service to be ready.
94+
Future<void> _initializeChromeService(dynamic appDebugServices) async {
95+
try {
96+
final chromeProxyService = appDebugServices.chromeProxyService;
97+
if (chromeProxyService != null) {
98+
await chromeProxyService.isInitialized;
99+
_logger.fine('Chrome proxy service initialized successfully');
100+
} else {
101+
_logger.warning('Chrome proxy service is null');
102+
}
103+
} catch (e) {
104+
_logger.severe('Failed to initialize Chrome proxy service: $e');
105+
rethrow;
106+
}
107+
}
108+
64109
static Future<Dwds> start({
65110
required AssetReader assetReader,
66111
required Stream<BuildResult> buildResults,
@@ -123,10 +168,7 @@ class Dwds {
123168
_logger.info('Serving DevTools at $uri\n');
124169
}
125170

126-
final injected = DwdsInjector(
127-
extensionUri: extensionUri,
128-
useDwdsWebSocketConnection: useDwdsWebSocketConnection,
129-
);
171+
final injected = DwdsInjector(extensionUri: extensionUri);
130172

131173
final devHandler = DevHandler(
132174
chromeConnection,
@@ -143,6 +185,7 @@ class Dwds {
143185
debugSettings.spawnDds,
144186
debugSettings.ddsPort,
145187
debugSettings.launchDevToolsInNewWindow,
188+
useWebSocketConnection: useDwdsWebSocketConnection,
146189
);
147190

148191
return Dwds._(
@@ -151,6 +194,7 @@ class Dwds {
151194
devHandler,
152195
assetReader,
153196
debugSettings.enableDebugging,
197+
useDwdsWebSocketConnection,
154198
);
155199
}
156200
}

dwds/lib/data/serializers.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import 'error_response.dart';
1414
import 'extension_request.dart';
1515
import 'hot_reload_request.dart';
1616
import 'hot_reload_response.dart';
17+
import 'service_extension_request.dart';
18+
import 'service_extension_response.dart';
1719
import 'isolate_events.dart';
1820
import 'register_event.dart';
1921
import 'run_request.dart';
@@ -40,5 +42,7 @@ part 'serializers.g.dart';
4042
ErrorResponse,
4143
RegisterEvent,
4244
RunRequest,
45+
ServiceExtensionRequest,
46+
ServiceExtensionResponse,
4347
])
4448
final Serializers serializers = _$serializers;

dwds/lib/data/serializers.g.dart

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) 2025, the Dart project authors. All rights reserved.
2+
// Defines the request for service extension calls over WebSocket.
3+
4+
import 'dart:convert';
5+
import 'package:built_value/built_value.dart';
6+
import 'package:built_value/serializer.dart';
7+
8+
part 'service_extension_request.g.dart';
9+
10+
abstract class ServiceExtensionRequest
11+
implements Built<ServiceExtensionRequest, ServiceExtensionRequestBuilder> {
12+
String get id;
13+
String get method;
14+
String
15+
get argsJson; // Store args as JSON string for built_value compatibility
16+
17+
// Helper method to get args as Map<String, dynamic>
18+
Map<String, dynamic> get args =>
19+
argsJson.isEmpty
20+
? <String, dynamic>{}
21+
: json.decode(argsJson) as Map<String, dynamic>;
22+
23+
ServiceExtensionRequest._();
24+
factory ServiceExtensionRequest([
25+
void Function(ServiceExtensionRequestBuilder) updates,
26+
]) = _$ServiceExtensionRequest;
27+
28+
// Convenient factory method to create with args Map
29+
factory ServiceExtensionRequest.fromArgs({
30+
required String id,
31+
required String method,
32+
required Map<String, dynamic> args,
33+
}) => ServiceExtensionRequest(
34+
(b) =>
35+
b
36+
..id = id
37+
..method = method
38+
..argsJson = json.encode(args),
39+
);
40+
41+
static Serializer<ServiceExtensionRequest> get serializer =>
42+
_$serviceExtensionRequestSerializer;
43+
}

0 commit comments

Comments
 (0)