Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit f6b0c6d

Browse files
authored
Use first Dart VM Service found with mDNS if there are duplicates (#119545)
* fix when duplicate mdns results are found * put mdns auth code in it's own function and update tests * add comments, refactor auth code parsing, other small tweaks
1 parent a16d82c commit f6b0c6d

File tree

2 files changed

+192
-28
lines changed

2 files changed

+192
-28
lines changed

packages/flutter_tools/lib/src/mdns_discovery.dart

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'base/context.dart';
1010
import 'base/io.dart';
1111
import 'base/logger.dart';
1212
import 'build_info.dart';
13+
import 'convert.dart';
1314
import 'device.dart';
1415
import 'reporting/reporting.dart';
1516

@@ -201,7 +202,16 @@ class MDnsVmServiceDiscovery {
201202

202203
final List<MDnsVmServiceDiscoveryResult> results =
203204
<MDnsVmServiceDiscoveryResult>[];
205+
206+
// uniqueDomainNames is used to track all domain names of Dart VM services
207+
// It is later used in this function to determine whether or not to throw an error.
208+
// We do not want to throw the error if it was unable to find any domain
209+
// names because that indicates it may be a problem with mDNS, which has
210+
// a separate error message in _checkForIPv4LinkLocal.
204211
final Set<String> uniqueDomainNames = <String>{};
212+
// uniqueDomainNamesInResults is used to filter out duplicates with exactly
213+
// the same domain name from the results.
214+
final Set<String> uniqueDomainNamesInResults = <String>{};
205215

206216
// Listen for mDNS connections until timeout.
207217
final Stream<PtrResourceRecord> ptrResourceStream = client.lookup<PtrResourceRecord>(
@@ -223,6 +233,11 @@ class MDnsVmServiceDiscovery {
223233
domainName = ptr.domainName;
224234
}
225235

236+
// Result with same domain name was already found, skip it.
237+
if (uniqueDomainNamesInResults.contains(domainName)) {
238+
continue;
239+
}
240+
226241
_logger.printTrace('Checking for available port on $domainName');
227242
final List<SrvResourceRecord> srvRecords = await client
228243
.lookup<SrvResourceRecord>(
@@ -279,41 +294,18 @@ class MDnsVmServiceDiscovery {
279294
ResourceRecordQuery.text(domainName),
280295
)
281296
.toList();
282-
if (txt.isEmpty) {
283-
results.add(MDnsVmServiceDiscoveryResult(domainName, srvRecord.port, ''));
284-
if (quitOnFind) {
285-
return results;
286-
}
287-
continue;
288-
}
289-
const String authCodePrefix = 'authCode=';
290-
String? raw;
291-
for (final String record in txt.first.text.split('\n')) {
292-
if (record.startsWith(authCodePrefix)) {
293-
raw = record;
294-
break;
295-
}
296-
}
297-
if (raw == null) {
298-
results.add(MDnsVmServiceDiscoveryResult(domainName, srvRecord.port, ''));
299-
if (quitOnFind) {
300-
return results;
301-
}
302-
continue;
303-
}
304-
String authCode = raw.substring(authCodePrefix.length);
305-
// The Dart VM Service currently expects a trailing '/' as part of the
306-
// URI, otherwise an invalid authentication code response is given.
307-
if (!authCode.endsWith('/')) {
308-
authCode += '/';
309-
}
310297

298+
String authCode = '';
299+
if (txt.isNotEmpty) {
300+
authCode = _getAuthCode(txt.first.text);
301+
}
311302
results.add(MDnsVmServiceDiscoveryResult(
312303
domainName,
313304
srvRecord.port,
314305
authCode,
315306
ipAddress: ipAddress
316307
));
308+
uniqueDomainNamesInResults.add(domainName);
317309
if (quitOnFind) {
318310
return results;
319311
}
@@ -338,6 +330,22 @@ class MDnsVmServiceDiscovery {
338330
}
339331
}
340332

333+
String _getAuthCode(String txtRecord) {
334+
const String authCodePrefix = 'authCode=';
335+
final Iterable<String> matchingRecords =
336+
LineSplitter.split(txtRecord).where((String record) => record.startsWith(authCodePrefix));
337+
if (matchingRecords.isEmpty) {
338+
return '';
339+
}
340+
String authCode = matchingRecords.first.substring(authCodePrefix.length);
341+
// The Dart VM Service currently expects a trailing '/' as part of the
342+
// URI, otherwise an invalid authentication code response is given.
343+
if (!authCode.endsWith('/')) {
344+
authCode += '/';
345+
}
346+
return authCode;
347+
}
348+
341349
/// Gets Dart VM Service Uri for `flutter attach`.
342350
/// Executes an mDNS query and waits until a Dart VM Service is found.
343351
///

packages/flutter_tools/test/general.shard/mdns_discovery_test.dart

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,56 @@ void main() {
112112
expect(portDiscovery.queryForAttach, throwsToolExit());
113113
});
114114

115+
testWithoutContext('Find duplicates in preliminary client', () async {
116+
final MDnsClient client = FakeMDnsClient(
117+
<PtrResourceRecord>[
118+
PtrResourceRecord('foo', future, domainName: 'bar'),
119+
PtrResourceRecord('foo', future, domainName: 'bar'),
120+
],
121+
<String, List<SrvResourceRecord>>{
122+
'bar': <SrvResourceRecord>[
123+
SrvResourceRecord('bar', future, port: 123, weight: 1, priority: 1, target: 'appId'),
124+
],
125+
},
126+
);
127+
128+
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
129+
mdnsClient: emptyClient,
130+
preliminaryMDnsClient: client,
131+
logger: BufferLogger.test(),
132+
flutterUsage: TestUsage(),
133+
);
134+
135+
final MDnsVmServiceDiscoveryResult? result = await portDiscovery.queryForAttach();
136+
expect(result, isNotNull);
137+
});
138+
139+
testWithoutContext('Find similar named in preliminary client', () async {
140+
final MDnsClient client = FakeMDnsClient(
141+
<PtrResourceRecord>[
142+
PtrResourceRecord('foo', future, domainName: 'bar'),
143+
PtrResourceRecord('foo', future, domainName: 'bar (2)'),
144+
],
145+
<String, List<SrvResourceRecord>>{
146+
'bar': <SrvResourceRecord>[
147+
SrvResourceRecord('bar', future, port: 123, weight: 1, priority: 1, target: 'appId'),
148+
],
149+
'bar (2)': <SrvResourceRecord>[
150+
SrvResourceRecord('bar', future, port: 123, weight: 1, priority: 1, target: 'appId'),
151+
],
152+
},
153+
);
154+
155+
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
156+
mdnsClient: emptyClient,
157+
preliminaryMDnsClient: client,
158+
logger: BufferLogger.test(),
159+
flutterUsage: TestUsage(),
160+
);
161+
162+
expect(portDiscovery.queryForAttach, throwsToolExit());
163+
});
164+
115165
testWithoutContext('No ports available', () async {
116166
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
117167
mdnsClient: emptyClient,
@@ -680,6 +730,112 @@ void main() {
680730
expect(result?.domainName, 'srv-bar');
681731
expect(result?.port, 222);
682732
});
733+
testWithoutContext('find with no txt record', () async {
734+
final MDnsClient client = FakeMDnsClient(
735+
<PtrResourceRecord>[
736+
PtrResourceRecord('foo', future, domainName: 'srv-foo'),
737+
],
738+
<String, List<SrvResourceRecord>>{
739+
'srv-foo': <SrvResourceRecord>[
740+
SrvResourceRecord('srv-foo', future, port: 111, weight: 1, priority: 1, target: 'target-foo'),
741+
],
742+
},
743+
ipResponse: <String, List<IPAddressResourceRecord>>{
744+
'target-foo': <IPAddressResourceRecord>[
745+
IPAddressResourceRecord('target-foo', 0, address: InternetAddress.tryParse('111.111.111.111')!),
746+
],
747+
},
748+
);
749+
750+
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
751+
mdnsClient: client,
752+
logger: BufferLogger.test(),
753+
flutterUsage: TestUsage(),
754+
);
755+
final MDnsVmServiceDiscoveryResult? result = await portDiscovery.firstMatchingVmService(
756+
client,
757+
applicationId: 'srv-foo',
758+
isNetworkDevice: true,
759+
);
760+
expect(result?.domainName, 'srv-foo');
761+
expect(result?.port, 111);
762+
expect(result?.authCode, '');
763+
expect(result?.ipAddress?.address, '111.111.111.111');
764+
});
765+
testWithoutContext('find with empty txt record', () async {
766+
final MDnsClient client = FakeMDnsClient(
767+
<PtrResourceRecord>[
768+
PtrResourceRecord('foo', future, domainName: 'srv-foo'),
769+
],
770+
<String, List<SrvResourceRecord>>{
771+
'srv-foo': <SrvResourceRecord>[
772+
SrvResourceRecord('srv-foo', future, port: 111, weight: 1, priority: 1, target: 'target-foo'),
773+
],
774+
},
775+
txtResponse: <String, List<TxtResourceRecord>>{
776+
'srv-foo': <TxtResourceRecord>[
777+
TxtResourceRecord('srv-foo', future, text: ''),
778+
],
779+
},
780+
ipResponse: <String, List<IPAddressResourceRecord>>{
781+
'target-foo': <IPAddressResourceRecord>[
782+
IPAddressResourceRecord('target-foo', 0, address: InternetAddress.tryParse('111.111.111.111')!),
783+
],
784+
},
785+
);
786+
787+
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
788+
mdnsClient: client,
789+
logger: BufferLogger.test(),
790+
flutterUsage: TestUsage(),
791+
);
792+
final MDnsVmServiceDiscoveryResult? result = await portDiscovery.firstMatchingVmService(
793+
client,
794+
applicationId: 'srv-foo',
795+
isNetworkDevice: true,
796+
);
797+
expect(result?.domainName, 'srv-foo');
798+
expect(result?.port, 111);
799+
expect(result?.authCode, '');
800+
expect(result?.ipAddress?.address, '111.111.111.111');
801+
});
802+
testWithoutContext('find with valid txt record', () async {
803+
final MDnsClient client = FakeMDnsClient(
804+
<PtrResourceRecord>[
805+
PtrResourceRecord('foo', future, domainName: 'srv-foo'),
806+
],
807+
<String, List<SrvResourceRecord>>{
808+
'srv-foo': <SrvResourceRecord>[
809+
SrvResourceRecord('srv-foo', future, port: 111, weight: 1, priority: 1, target: 'target-foo'),
810+
],
811+
},
812+
txtResponse: <String, List<TxtResourceRecord>>{
813+
'srv-foo': <TxtResourceRecord>[
814+
TxtResourceRecord('srv-foo', future, text: 'authCode=xyz\n'),
815+
],
816+
},
817+
ipResponse: <String, List<IPAddressResourceRecord>>{
818+
'target-foo': <IPAddressResourceRecord>[
819+
IPAddressResourceRecord('target-foo', 0, address: InternetAddress.tryParse('111.111.111.111')!),
820+
],
821+
},
822+
);
823+
824+
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
825+
mdnsClient: client,
826+
logger: BufferLogger.test(),
827+
flutterUsage: TestUsage(),
828+
);
829+
final MDnsVmServiceDiscoveryResult? result = await portDiscovery.firstMatchingVmService(
830+
client,
831+
applicationId: 'srv-foo',
832+
isNetworkDevice: true,
833+
);
834+
expect(result?.domainName, 'srv-foo');
835+
expect(result?.port, 111);
836+
expect(result?.authCode, 'xyz/');
837+
expect(result?.ipAddress?.address, '111.111.111.111');
838+
});
683839
});
684840
}
685841

0 commit comments

Comments
 (0)