Skip to content

Commit 8a9e74e

Browse files
authored
Avoid forwarding the data after socket is disconnected. (#146665)
In a ProxiedDevicePortForwarder, there might be a race condition where the local socket has been disconnected, but the remote end was still sending new data. In this case, avoid forwarding new data to the socket.
1 parent 7a30d2b commit 8a9e74e

File tree

2 files changed

+33
-2
lines changed

2 files changed

+33
-2
lines changed

packages/flutter_tools/lib/src/proxied_devices/devices.dart

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -633,11 +633,18 @@ class ProxiedPortForwarder extends DevicePortForwarder {
633633
'port': devicePort,
634634
}));
635635
final Stream<List<int>> dataStream = connection.listenToEvent('proxy.data.$id').asyncExpand((DaemonEventData event) => event.binary);
636-
dataStream.listen(socket.add);
636+
final StreamSubscription<List<int>> subscription = dataStream.listen(socket.add);
637637
final Future<DaemonEventData> disconnectFuture = connection.listenToEvent('proxy.disconnected.$id').first;
638+
639+
bool socketDoneCalled = false;
640+
638641
unawaited(disconnectFuture.then<void>((_) async {
639642
try {
640-
await socket.close();
643+
if (socketDoneCalled) {
644+
await subscription.cancel();
645+
} else {
646+
await (subscription.cancel(), socket.close()).wait;
647+
}
641648
} on Exception {
642649
// ignore
643650
}
@@ -670,6 +677,8 @@ class ProxiedPortForwarder extends DevicePortForwarder {
670677
// Do nothing here. Everything will be handled in the `then` block below.
671678
return false;
672679
}).whenComplete(() {
680+
socketDoneCalled = true;
681+
unawaited(subscription.cancel());
673682
// Send a proxy disconnect event just in case.
674683
unawaited(connection.sendRequest('proxy.disconnect', <String, Object>{
675684
'id': id,

packages/flutter_tools/test/general.shard/proxied_devices/proxied_devices_test.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,28 @@ void main() {
231231
// Wait the event queue and make sure that it doesn't crash.
232232
await pumpEventQueue();
233233
});
234+
235+
testWithoutContext('should not forward new data to socket after disconnection', () async {
236+
// Data will be forwarded before disconnection
237+
serverDaemonConnection.sendEvent('proxy.data.$id', null, <int>[1, 2, 3]);
238+
await pumpEventQueue();
239+
expect(fakeSocket.addedData, <List<int>>[<int>[1, 2, 3]]);
240+
241+
// It will try to disconnect the remote port when socket is done.
242+
fakeSocket.doneCompleter.complete(true);
243+
final DaemonMessage message = await broadcastOutput.first;
244+
245+
expect(message.data['id'], isNotNull);
246+
expect(message.data['method'], 'proxy.disconnect');
247+
expect(message.data['params'], <String, Object?>{
248+
'id': 'random_id',
249+
});
250+
await pumpEventQueue();
251+
252+
serverDaemonConnection.sendEvent('proxy.data.$id', null, <int>[4, 5, 6]);
253+
await pumpEventQueue();
254+
expect(fakeSocket.addedData, <List<int>>[<int>[1, 2, 3]]);
255+
});
234256
});
235257

236258
testWithoutContext('disposes multiple sockets correctly', () async {

0 commit comments

Comments
 (0)