Skip to content

Commit 6dc3dde

Browse files
authored
Fix duplicate connection/logs in Webdev (#2635)
* bump dwds version to 24.3.9 * updated dwds constraints to 24.3.11 * fix duplicate logs * fix duplicate logs * Revert "fix duplicate logs" This reverts commit 2ccd2d9. * updated changelog * check and resuse active app state for each appId * addressed comments
1 parent 0c8a17b commit 6dc3dde

File tree

4 files changed

+63
-34
lines changed

4 files changed

+63
-34
lines changed

webdev/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
## 3.7.2-wip
1+
## 3.7.2
22

3+
- Fixed duplicate app logs on page refresh by preventing multiple stdout listeners for the same appId.
34
- Adds `--offline` flag [#2483](https://github.com/dart-lang/webdev/pull/2483).
45
- Support the `--hostname` flag when the `--tls-cert-key` and `--tls-cert-chain` flags are present [#2588](https://github.com/dart-lang/webdev/pull/2588).
6+
- Update `dwds` constraint to `24.3.11`.
57

68
## 3.7.1
79

webdev/lib/src/daemon/app_domain.dart

Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -63,46 +63,33 @@ class AppDomain extends Domain {
6363

6464
Future<void> _handleAppConnections(WebDevServer server) async {
6565
final dwds = server.dwds!;
66+
6667
// The connection is established right before `main()` is called.
6768
await for (final appConnection in dwds.connectedApps) {
69+
final appId = appConnection.request.appId;
70+
71+
// Check if we already have an active app state for this appId
72+
if (_appStates.containsKey(appId)) {
73+
// Reuse existing connection, just run main again
74+
appConnection.runMain();
75+
continue;
76+
}
77+
6878
final debugConnection = await dwds.debugConnection(appConnection);
6979
final debugUri = debugConnection.ddsUri ?? debugConnection.uri;
7080
final vmService = await vmServiceConnectUri(debugUri);
71-
final appId = appConnection.request.appId;
72-
unawaited(debugConnection.onDone.then((_) {
73-
sendEvent('app.log', {
74-
'appId': appId,
75-
'log': 'Lost connection to device.',
76-
});
77-
sendEvent('app.stop', {
78-
'appId': appId,
79-
});
80-
daemon.shutdown();
81-
}));
81+
8282
sendEvent('app.start', {
8383
'appId': appId,
8484
'directory': Directory.current.path,
8585
'deviceId': 'chrome',
8686
'launchMode': 'run'
8787
});
88-
// TODO(grouma) - limit the catch to the appropriate error.
89-
try {
90-
await vmService.streamCancel('Stdout');
91-
} catch (_) {}
92-
try {
93-
await vmService.streamListen('Stdout');
94-
} catch (_) {}
95-
try {
96-
vmService.onServiceEvent.listen(_onServiceEvent);
97-
await vmService.streamListen('Service');
98-
} catch (_) {}
88+
89+
// Set up VM service listeners for this appId
9990
// ignore: cancel_subscriptions
100-
final stdOutSub = vmService.onStdoutEvent.listen((log) {
101-
sendEvent('app.log', {
102-
'appId': appId,
103-
'log': utf8.decode(base64.decode(log.bytes!)),
104-
});
105-
});
91+
final stdOutSub = await _setupVmServiceListeners(appId, vmService);
92+
10693
sendEvent('app.debugPort', {
10794
'appId': appId,
10895
'port': debugConnection.port,
@@ -120,9 +107,19 @@ class AppDomain extends Domain {
120107

121108
appConnection.runMain();
122109

110+
// Handle connection termination - send events first, then cleanup
123111
unawaited(debugConnection.onDone.whenComplete(() {
124-
appState.dispose();
125-
_appStates.remove(appId);
112+
sendEvent('app.log', {
113+
'appId': appId,
114+
'log': 'Lost connection to device.',
115+
});
116+
sendEvent('app.stop', {
117+
'appId': appId,
118+
});
119+
daemon.shutdown();
120+
121+
// Clean up app resources
122+
_cleanupAppConnection(appId, appState);
126123
}));
127124
}
128125

@@ -223,6 +220,36 @@ class AppDomain extends Domain {
223220
return true;
224221
}
225222

223+
/// Sets up VM service listeners for the given appId.
224+
/// Returns the stdout subscription.
225+
Future<StreamSubscription<Event>> _setupVmServiceListeners(
226+
String appId, VmService vmService) async {
227+
try {
228+
vmService.onServiceEvent.listen(_onServiceEvent);
229+
await vmService.streamListen(EventStreams.kService);
230+
} catch (_) {}
231+
232+
// ignore: cancel_subscriptions
233+
final stdoutSubscription = vmService.onStdoutEvent.listen((log) {
234+
sendEvent('app.log', {
235+
'appId': appId,
236+
'log': utf8.decode(base64.decode(log.bytes!)),
237+
});
238+
});
239+
240+
try {
241+
await vmService.streamListen(EventStreams.kStdout);
242+
} catch (_) {}
243+
244+
return stdoutSubscription;
245+
}
246+
247+
/// Cleans up an app connection and its associated listeners.
248+
void _cleanupAppConnection(String appId, _AppState appState) {
249+
appState.dispose();
250+
_appStates.remove(appId);
251+
}
252+
226253
@override
227254
void dispose() {
228255
_isShutdown = true;

webdev/lib/src/version.dart

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webdev/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: webdev
22
# Every time this changes you need to run `dart run build_runner build`.
3-
version: 3.7.2-wip
3+
version: 3.7.2
44
# We should not depend on a dev SDK before publishing.
55
# publish_to: none
66
description: >-
@@ -19,7 +19,7 @@ dependencies:
1919
crypto: ^3.0.2
2020
dds: ^4.1.0
2121
# Pin DWDS to avoid dependency conflicts with vm_service:
22-
dwds: 24.3.5
22+
dwds: 24.3.11
2323
http: ^1.0.0
2424
http_multi_server: ^3.2.0
2525
io: ^1.0.3

0 commit comments

Comments
 (0)