Skip to content

Commit 32f165e

Browse files
committed
created common interface for debugService and webSocketDebugService
1 parent 18617bd commit 32f165e

File tree

6 files changed

+182
-182
lines changed

6 files changed

+182
-182
lines changed

dwds/lib/src/handlers/dev_handler.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import 'package:dwds/src/services/app_debug_services.dart';
3434
import 'package:dwds/src/services/debug_service.dart';
3535
import 'package:dwds/src/services/expression_compiler.dart';
3636
import 'package:dwds/src/services/web_socket_app_debug_services.dart';
37-
import 'package:dwds/src/services/web_socket_debug_service.dart';
3837
import 'package:dwds/src/utilities/shared.dart';
3938
import 'package:dwds/src/web_socket_dwds_vm_client.dart';
4039
import 'package:logging/logging.dart';

dwds/lib/src/services/app_debug_services.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import 'package:dwds/src/services/debug_service.dart';
1010

1111
/// Common interface for debug service containers.
1212
abstract class IAppDebugServices {
13-
dynamic get debugService;
13+
IDebugService get debugService;
1414
dynamic get dwdsVmClient;
1515
dynamic get dwdsStats;
1616
Uri? get ddsUri;

dwds/lib/src/services/debug_service.dart

Lines changed: 179 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import 'package:dwds/src/events.dart';
1717
import 'package:dwds/src/readers/asset_reader.dart';
1818
import 'package:dwds/src/services/chrome_proxy_service.dart';
1919
import 'package:dwds/src/services/expression_compiler.dart';
20+
import 'package:dwds/src/services/web_socket_proxy_service.dart';
2021
import 'package:dwds/src/utilities/server.dart';
2122
import 'package:dwds/src/utilities/shared.dart';
2223
import 'package:logging/logging.dart';
@@ -126,15 +127,28 @@ Future<void> _handleSseConnections(
126127
}
127128
}
128129

130+
/// Common interface for debug services (Chrome or WebSocket based).
131+
abstract class IDebugService {
132+
String get hostname;
133+
int get port;
134+
String get uri;
135+
Future<String> get encodedUri;
136+
ServiceExtensionRegistry get serviceExtensionRegistry;
137+
Future<void> close();
138+
}
139+
129140
/// A Dart Web Debug Service.
130141
///
131142
/// Creates a [ChromeProxyService] from an existing Chrome instance.
132-
class DebugService {
143+
class DebugService implements IDebugService {
133144
static String? _ddsUri;
134145

135146
final VmServiceInterface chromeProxyService;
147+
@override
136148
final String hostname;
149+
@override
137150
final ServiceExtensionRegistry serviceExtensionRegistry;
151+
@override
138152
final int port;
139153
final String authToken;
140154
final HttpServer _server;
@@ -162,6 +176,7 @@ class DebugService {
162176
this._urlEncoder,
163177
);
164178

179+
@override
165180
Future<void> close() =>
166181
_closed ??= Future.wait([
167182
_server.close(),
@@ -183,6 +198,7 @@ class DebugService {
183198
return _dds!;
184199
}
185200

201+
@override
186202
String get uri {
187203
final dds = _dds;
188204
if (_spawnDds && dds != null) {
@@ -200,6 +216,7 @@ class DebugService {
200216
}
201217

202218
String? _encodedUri;
219+
@override
203220
Future<String> get encodedUri async {
204221
if (_encodedUri != null) return _encodedUri!;
205222
var encoded = uri;
@@ -303,6 +320,167 @@ class DebugService {
303320
}
304321
}
305322

323+
/// Defines callbacks for sending messages to the connected client.
324+
/// Returns the number of clients the request was successfully sent to.
325+
typedef SendClientRequest = int Function(Object request);
326+
327+
/// WebSocket-based debug service for web debugging.
328+
class WebSocketDebugService implements IDebugService {
329+
@override
330+
final String hostname;
331+
@override
332+
final int port;
333+
final String authToken;
334+
final HttpServer _server;
335+
final WebSocketProxyService _webSocketProxyService;
336+
final ServiceExtensionRegistry _serviceExtensionRegistry;
337+
final UrlEncoder? _urlEncoder;
338+
339+
Future<void>? _closed;
340+
DartDevelopmentServiceLauncher? _dds;
341+
String? _encodedUri;
342+
343+
WebSocketDebugService._(
344+
this.hostname,
345+
this.port,
346+
this.authToken,
347+
this._webSocketProxyService,
348+
this._serviceExtensionRegistry,
349+
this._server,
350+
this._urlEncoder,
351+
);
352+
353+
/// Returns the WebSocketProxyService instance.
354+
WebSocketProxyService get webSocketProxyService => _webSocketProxyService;
355+
356+
/// Returns the ServiceExtensionRegistry instance.
357+
@override
358+
ServiceExtensionRegistry get serviceExtensionRegistry =>
359+
_serviceExtensionRegistry;
360+
361+
/// Closes the debug service and associated resources.
362+
@override
363+
Future<void> close() =>
364+
_closed ??= Future.wait([
365+
_server.close(),
366+
if (_dds != null) _dds!.shutdown(),
367+
]);
368+
369+
/// Starts DDS (Dart Development Service).
370+
Future<DartDevelopmentServiceLauncher> startDartDevelopmentService({
371+
int? ddsPort,
372+
}) async {
373+
const timeout = Duration(seconds: 10);
374+
375+
try {
376+
_dds = await DartDevelopmentServiceLauncher.start(
377+
remoteVmServiceUri: Uri(
378+
scheme: 'http',
379+
host: hostname,
380+
port: port,
381+
path: authToken,
382+
),
383+
serviceUri: Uri(scheme: 'http', host: hostname, port: ddsPort ?? 0),
384+
).timeout(timeout);
385+
} catch (e) {
386+
throw Exception('Failed to start DDS: $e');
387+
}
388+
return _dds!;
389+
}
390+
391+
@override
392+
String get uri =>
393+
Uri(scheme: 'ws', host: hostname, port: port, path: authToken).toString();
394+
395+
@override
396+
Future<String> get encodedUri async {
397+
if (_encodedUri != null) return _encodedUri!;
398+
var encoded = uri;
399+
if (_urlEncoder != null) encoded = await _urlEncoder(encoded);
400+
return _encodedUri = encoded;
401+
}
402+
403+
static Future<WebSocketDebugService> start(
404+
String hostname,
405+
AppConnection appConnection, {
406+
required SendClientRequest sendClientRequest,
407+
UrlEncoder? urlEncoder,
408+
}) async {
409+
final authToken = _makeAuthToken();
410+
final serviceExtensionRegistry = ServiceExtensionRegistry();
411+
412+
final webSocketProxyService = await WebSocketProxyService.create(
413+
sendClientRequest,
414+
appConnection,
415+
);
416+
417+
final handler = _createWebSocketHandler(
418+
serviceExtensionRegistry,
419+
webSocketProxyService,
420+
);
421+
422+
final server = await startHttpServer(hostname, port: 0);
423+
serveHttpRequests(server, handler, (e, s) {
424+
Logger('WebSocketDebugService').warning('Error serving requests', e);
425+
});
426+
427+
return WebSocketDebugService._(
428+
server.address.host,
429+
server.port,
430+
authToken,
431+
webSocketProxyService,
432+
serviceExtensionRegistry,
433+
server,
434+
urlEncoder,
435+
);
436+
}
437+
438+
/// Creates the WebSocket handler for incoming connections.
439+
static dynamic _createWebSocketHandler(
440+
ServiceExtensionRegistry serviceExtensionRegistry,
441+
WebSocketProxyService webSocketProxyService,
442+
) {
443+
return webSocketHandler((WebSocketChannel webSocket) {
444+
if (!_acceptNewConnections) {
445+
webSocket.sink.add(
446+
jsonEncode({
447+
'error': 'Cannot connect: another service has taken control.',
448+
}),
449+
);
450+
webSocket.sink.close();
451+
return;
452+
}
453+
454+
final responseController = StreamController<Map<String, Object?>>();
455+
webSocket.sink.addStream(responseController.stream.map(jsonEncode));
456+
457+
final inputStream = webSocket.stream.map((value) {
458+
if (value is List<int>) {
459+
value = utf8.decode(value);
460+
} else if (value is! String) {
461+
throw StateError(
462+
'Unexpected value type from web socket: ${value.runtimeType}',
463+
);
464+
}
465+
return Map<String, Object>.from(jsonDecode(value));
466+
});
467+
468+
++_clientsConnected;
469+
VmServerConnection(
470+
inputStream,
471+
responseController.sink,
472+
serviceExtensionRegistry,
473+
webSocketProxyService,
474+
).done.whenComplete(() {
475+
--_clientsConnected;
476+
if (!_acceptNewConnections && _clientsConnected == 0) {
477+
_acceptNewConnections = true;
478+
}
479+
});
480+
});
481+
}
482+
}
483+
306484
// Creates a random auth token for more secure connections.
307485
String _makeAuthToken() {
308486
final tokenBytes = 8;

dwds/lib/src/services/web_socket_app_debug_services.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import 'dart:async';
66
import 'package:dwds/src/services/app_debug_services.dart';
7-
import 'package:dwds/src/services/web_socket_debug_service.dart';
7+
import 'package:dwds/src/services/debug_service.dart';
88
import 'package:dwds/src/web_socket_dwds_vm_client.dart';
99

1010
/// WebSocket-based implementation of app debug services.

0 commit comments

Comments
 (0)