From 3764cb851572dd92b82e0d0b0c24e34d17c043b9 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Thu, 18 Apr 2019 21:01:50 -0700 Subject: [PATCH] Added support for authentication codes for the VM service. (#30857) * Added support for authentication codes for the VM service. Previously, a valid web socket connection would use the following URI: `ws://127.0.0.1/ws` Now, by default, the VM service requires a connection to be made with a URI similar to the following: `ws://127.0.0.1:8181/Ug_U0QVsqFs=/ws` where `Ug_U0QVsqFs` is an authentication code generated and shared by the service. This behavior can be disabled with the `--disable-service-auth-codes` flag. --- bin/internal/engine.version | 2 +- dev/devicelab/bin/tasks/commands_test.dart | 2 +- .../bin/tasks/named_isolates_test.dart | 3 +- dev/devicelab/bin/tasks/routing_test.dart | 2 +- .../bin/tasks/service_extensions_test.dart | 2 +- dev/devicelab/lib/framework/runner.dart | 25 ++-- dev/devicelab/lib/framework/utils.dart | 40 ++++- dev/devicelab/test/utils_test.dart | 17 +++ .../lib/src/android/android_device.dart | 2 + .../lib/src/commands/attach.dart | 137 ++++++++++++++---- .../flutter_tools/lib/src/commands/run.dart | 6 + .../flutter_tools/lib/src/commands/test.dart | 11 ++ packages/flutter_tools/lib/src/device.dart | 3 + .../flutter_tools/lib/src/ios/devices.dart | 3 + .../flutter_tools/lib/src/ios/simulators.dart | 2 + .../lib/src/protocol_discovery.dart | 3 +- .../lib/src/test/flutter_platform.dart | 9 ++ .../flutter_tools/lib/src/test/runner.dart | 2 + .../lib/src/tester/flutter_tester.dart | 2 + .../test/commands/attach_test.dart | 26 ++-- .../test/integration/test_driver.dart | 2 + 21 files changed, 235 insertions(+), 66 deletions(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index f7e75b1d72..00091dde7d 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -4b9966f5cb412a73fa50462b3aee9082f436a62a +ca31a7c57bada458fa7f5c0d3f36bc1af4ccbc79 diff --git a/dev/devicelab/bin/tasks/commands_test.dart b/dev/devicelab/bin/tasks/commands_test.dart index 1611fd3a0d..1441384333 100644 --- a/dev/devicelab/bin/tasks/commands_test.dart +++ b/dev/devicelab/bin/tasks/commands_test.dart @@ -26,7 +26,7 @@ void main() { print('run: starting...'); final Process run = await startProcess( path.join(flutterDirectory.path, 'bin', 'flutter'), - ['run', '--verbose', '-d', device.deviceId, 'lib/commands.dart'], + ['run', '--verbose', '--disable-service-auth-codes', '-d', device.deviceId, 'lib/commands.dart'], ); final StreamController stdout = StreamController.broadcast(); run.stdout diff --git a/dev/devicelab/bin/tasks/named_isolates_test.dart b/dev/devicelab/bin/tasks/named_isolates_test.dart index c0ca4c1774..9ebe3b84a8 100644 --- a/dev/devicelab/bin/tasks/named_isolates_test.dart +++ b/dev/devicelab/bin/tasks/named_isolates_test.dart @@ -21,7 +21,8 @@ void main() { section('Compile and run the tester app'); Completer firstNameFound = Completer(); Completer secondNameFound = Completer(); - final Process runProcess = await _run(device: device, command: ['run'], stdoutListener: (String line) { + final Process runProcess = await _run(device: device, command: + ['run', '--disable-service-auth-codes'], stdoutListener: (String line) { if (line.contains(_kFirstIsolateName)) { firstNameFound.complete(); } else if (line.contains(_kSecondIsolateName)) { diff --git a/dev/devicelab/bin/tasks/routing_test.dart b/dev/devicelab/bin/tasks/routing_test.dart index 6098132fee..aa7ca5f2f5 100644 --- a/dev/devicelab/bin/tasks/routing_test.dart +++ b/dev/devicelab/bin/tasks/routing_test.dart @@ -34,7 +34,7 @@ void main() { print('run: starting...'); final Process run = await startProcess( path.join(flutterDirectory.path, 'bin', 'flutter'), - ['run', '--verbose', '-d', device.deviceId, '--route', '/smuggle-it', 'lib/route.dart'], + ['run', '--verbose', '--disable-service-auth-codes', '-d', device.deviceId, '--route', '/smuggle-it', 'lib/route.dart'], ); run.stdout .transform(utf8.decoder) diff --git a/dev/devicelab/bin/tasks/service_extensions_test.dart b/dev/devicelab/bin/tasks/service_extensions_test.dart index d946f40faa..728cc33332 100644 --- a/dev/devicelab/bin/tasks/service_extensions_test.dart +++ b/dev/devicelab/bin/tasks/service_extensions_test.dart @@ -26,7 +26,7 @@ void main() { print('run: starting...'); final Process run = await startProcess( path.join(flutterDirectory.path, 'bin', 'flutter'), - ['run', '--verbose', '-d', device.deviceId, 'lib/main.dart'], + ['run', '--verbose', '--disable-service-auth-codes', '-d', device.deviceId, 'lib/main.dart'], ); run.stdout .transform(utf8.decoder) diff --git a/dev/devicelab/lib/framework/runner.dart b/dev/devicelab/lib/framework/runner.dart index 69cbe36364..e6a0fa45c2 100644 --- a/dev/devicelab/lib/framework/runner.dart +++ b/dev/devicelab/lib/framework/runner.dart @@ -41,16 +41,16 @@ Future> runTask(String taskName, { bool silent = false }) a runnerFinished = true; }); - final Completer port = Completer(); + final Completer uri = Completer(); final StreamSubscription stdoutSub = runner.stdout .transform(const Utf8Decoder()) .transform(const LineSplitter()) .listen((String line) { - if (!port.isCompleted) { - final int portValue = parseServicePort(line, prefix: 'Observatory listening on '); - if (portValue != null) - port.complete(portValue); + if (!uri.isCompleted) { + final Uri serviceUri = parseServiceUri(line, prefix: 'Observatory listening on '); + if (serviceUri != null) + uri.complete(serviceUri); } if (!silent) { stdout.writeln('[$taskName] [STDOUT] $line'); @@ -66,7 +66,7 @@ Future> runTask(String taskName, { bool silent = false }) a String waitingFor = 'connection'; try { - final VMIsolateRef isolate = await _connectToRunnerIsolate(await port.future); + final VMIsolateRef isolate = await _connectToRunnerIsolate(await uri.future); waitingFor = 'task completion'; final Map taskResult = await isolate.invokeExtension('ext.cocoonRunTask').timeout(taskTimeoutWithGracePeriod); @@ -88,8 +88,15 @@ Future> runTask(String taskName, { bool silent = false }) a } } -Future _connectToRunnerIsolate(int vmServicePort) async { - final String url = 'ws://localhost:$vmServicePort/ws'; +Future _connectToRunnerIsolate(Uri vmServiceUri) async { + final List pathSegments = []; + if (vmServiceUri.pathSegments.isNotEmpty) { + // Add authentication code. + pathSegments.add(vmServiceUri.pathSegments[0]); + } + pathSegments.add('ws'); + final String url = vmServiceUri.replace(scheme: 'ws', pathSegments: + pathSegments).toString(); final DateTime started = DateTime.now(); // TODO(yjbanov): due to lack of imagination at the moment the handshake with @@ -163,4 +170,4 @@ Future cleanupSystem() async { } else { print('Could not determine JAVA_HOME; not shutting down Gradle.'); } -} \ No newline at end of file +} diff --git a/dev/devicelab/lib/framework/utils.dart b/dev/devicelab/lib/framework/utils.dart index c562a3fd9a..72067a8fd6 100644 --- a/dev/devicelab/lib/framework/utils.dart +++ b/dev/devicelab/lib/framework/utils.dart @@ -529,19 +529,43 @@ String extractCloudAuthTokenArg(List rawArgs) { return token; } +final RegExp _obsRegExp = + RegExp('An Observatory debugger .* is available at: '); +final RegExp _obsPortRegExp = RegExp('(\\S+:(\\d+)/\\S*)\$'); +final RegExp _obsUriRegExp = RegExp('((http|\/\/)[a-zA-Z0-9:/=_\\-\.\\[\\]]+)'); + /// Tries to extract a port from the string. /// /// The `prefix`, if specified, is a regular expression pattern and must not contain groups. -/// -/// The `multiLine` flag should be set to true if `line` is actually a buffer of many lines. +/// `prefix` defaults to the RegExp: `An Observatory debugger .* is available at: `. int parseServicePort(String line, { - String prefix = 'An Observatory debugger .* is available at: ', - bool multiLine = false, + Pattern prefix, }) { - // e.g. "An Observatory debugger and profiler on ... is available at: http://127.0.0.1:8100/" - final RegExp pattern = RegExp('$prefix(\\S+:(\\d+)/\\S*)\$', multiLine: multiLine); - final Match match = pattern.firstMatch(line); - return match == null ? null : int.parse(match.group(2)); + prefix ??= _obsRegExp; + final Match prefixMatch = prefix.matchAsPrefix(line); + if (prefixMatch == null) { + return null; + } + final List matches = + _obsPortRegExp.allMatches(line, prefixMatch.end).toList(); + return matches.isEmpty ? null : int.parse(matches[0].group(2)); +} + +/// Tries to extract a Uri from the string. +/// +/// The `prefix`, if specified, is a regular expression pattern and must not contain groups. +/// `prefix` defaults to the RegExp: `An Observatory debugger .* is available at: `. +Uri parseServiceUri(String line, { + Pattern prefix, +}) { + prefix ??= _obsRegExp; + final Match prefixMatch = prefix.matchAsPrefix(line); + if (prefixMatch == null) { + return null; + } + final List matches = + _obsUriRegExp.allMatches(line, prefixMatch.end).toList(); + return matches.isEmpty ? null : Uri.parse(matches[0].group(0)); } /// If FLUTTER_ENGINE environment variable is set then we need to pass diff --git a/dev/devicelab/test/utils_test.dart b/dev/devicelab/test/utils_test.dart index 10d17b1889..c1932b1521 100644 --- a/dev/devicelab/test/utils_test.dart +++ b/dev/devicelab/test/utils_test.dart @@ -16,4 +16,21 @@ void main() { expect(grep(RegExp('^b'), from: 'ab\nba'), ['ba']); }); }); + + group('parse service', () { + const String badOutput = 'No uri here'; + const String sampleOutput = 'An Observatory debugger and profiler on ' + 'Pixel 3 XL is available at: http://127.0.0.1:9090/LpjUpsdEjqI=/'; + + test('uri', () { + expect(parseServiceUri(sampleOutput), + Uri.parse('http://127.0.0.1:9090/LpjUpsdEjqI=/')); + expect(parseServiceUri(badOutput), null); + }); + + test('port', () { + expect(parseServicePort(sampleOutput), 9090); + expect(parseServicePort(badOutput), null); + }); + }); } diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart index 335a149b47..6a1c88cbfb 100644 --- a/packages/flutter_tools/lib/src/android/android_device.dart +++ b/packages/flutter_tools/lib/src/android/android_device.dart @@ -438,6 +438,8 @@ class AndroidDevice extends Device { } if (debuggingOptions.startPaused) cmd.addAll(['--ez', 'start-paused', 'true']); + if (debuggingOptions.disableServiceAuthCodes) + cmd.addAll(['--ez', 'disable-service-auth-codes', 'true']); if (debuggingOptions.useTestFonts) cmd.addAll(['--ez', 'use-test-fonts', 'true']); if (debuggingOptions.verboseSystemLogs) { diff --git a/packages/flutter_tools/lib/src/commands/attach.dart b/packages/flutter_tools/lib/src/commands/attach.dart index 43f15a1491..4703d971d7 100644 --- a/packages/flutter_tools/lib/src/commands/attach.dart +++ b/packages/flutter_tools/lib/src/commands/attach.dart @@ -31,6 +31,12 @@ import '../runner/flutter_command.dart'; /// With an application already running, a HotRunner can be attached to it /// with: /// ``` +/// $ flutter attach --debug-uri http://127.0.0.1:12345/QqL7EFEDNG0=/ +/// ``` +/// +/// If `--disable-service-auth-codes` was provided to the application at startup +/// time, a HotRunner can be attached with just a port: +/// ``` /// $ flutter attach --debug-port 12345 /// ``` /// @@ -56,7 +62,14 @@ class AttachCommand extends FlutterCommand { argParser ..addOption( 'debug-port', - help: 'Device port where the observatory is listening.', + hide: !verboseHelp, + help: 'Device port where the observatory is listening. Requires ' + '--disable-service-auth-codes to also be provided to the Flutter ' + 'application at launch, otherwise this command will fail to connect to ' + 'the application. In general, --debug-uri should be used instead.', + )..addOption( + 'debug-uri', + help: 'The URI at which the observatory is listening.', )..addOption( 'app-id', help: 'The package name (Android) or bundle identifier (iOS) for the application. ' @@ -102,6 +115,17 @@ class AttachCommand extends FlutterCommand { return null; } + Uri get debugUri { + if (argResults['debug-uri'] == null) { + return null; + } + final Uri uri = Uri.parse(argResults['debug-uri']); + if (!uri.hasPort) { + throwToolExit('Port not specified for `--debug-uri`: $uri'); + } + return uri; + } + String get appId { return argResults['app-id']; } @@ -112,24 +136,26 @@ class AttachCommand extends FlutterCommand { if (await findTargetDevice() == null) throwToolExit(null); debugPort; - if (debugPort == null && argResults.wasParsed(FlutterCommand.ipv6Flag)) { + if (debugPort == null && debugUri == null && argResults.wasParsed(FlutterCommand.ipv6Flag)) { throwToolExit( - 'When the --debug-port is unknown, this command determines ' + 'When the --debug-port or --debug-uri is unknown, this command determines ' 'the value of --ipv6 on its own.', ); } - if (debugPort == null && argResults.wasParsed(FlutterCommand.observatoryPortOption)) { + if (debugPort == null && debugUri == null && argResults.wasParsed(FlutterCommand.observatoryPortOption)) { throwToolExit( - 'When the --debug-port is unknown, this command does not use ' + 'When the --debug-port or --debug-uri is unknown, this command does not use ' 'the value of --observatory-port.', ); } + if (debugPort != null && debugUri != null) { + throwToolExit( + 'Either --debugPort or --debugUri can be provided, not both.'); + } } @override Future runCommand() async { - final String ipv4Loopback = InternetAddress.loopbackIPv4.address; - final String ipv6Loopback = InternetAddress.loopbackIPv6.address; final FlutterProject flutterProject = await FlutterProject.current(); Cache.releaseLockEarly(); @@ -147,7 +173,6 @@ class AttachCommand extends FlutterCommand { // simulators support it. // If/when we do this on Android or other platforms, we can update it here. if (device is IOSDevice || device is IOSSimulator) { - return MDnsObservatoryPortDiscovery().queryForPort(applicationId: appId); } return null; } @@ -159,9 +184,13 @@ class AttachCommand extends FlutterCommand { : null; Uri observatoryUri; - bool usesIpv6 = false; + bool usesIpv6 = ipv6; + final String ipv6Loopback = InternetAddress.loopbackIPv6.address; + final String ipv4Loopback = InternetAddress.loopbackIPv4.address; + final String hostname = usesIpv6 ? ipv6Loopback : ipv4Loopback; + bool attachLogger = false; - if (devicePort == null) { + if (devicePort == null && debugUri == null) { if (device is FuchsiaDevice) { attachLogger = true; final String module = argResults['module']; @@ -181,7 +210,12 @@ class AttachCommand extends FlutterCommand { } rethrow; } - } else { + } else if ((device is IOSDevice) || (device is IOSSimulator)) { + final MDnsObservatoryDiscoveryResult result = await MDnsObservatoryDiscovery().query(applicationId: appId); + observatoryUri = await _buildObservatoryUri(device, hostname, result.port, result.authCode); + } + // If MDNS discovery fails or we're not on iOS, fallback to ProtocolDiscovery. + if (observatoryUri == null) { ProtocolDiscovery observatoryDiscovery; try { observatoryDiscovery = ProtocolDiscovery.observatory( @@ -198,12 +232,8 @@ class AttachCommand extends FlutterCommand { } } } else { - usesIpv6 = ipv6; - final int localPort = observatoryPort - ?? await device.portForwarder.forward(devicePort); - observatoryUri = usesIpv6 - ? Uri.parse('http://[$ipv6Loopback]:$localPort/') - : Uri.parse('http://$ipv4Loopback:$localPort/'); + observatoryUri = await _buildObservatoryUri(device, + debugUri?.host ?? hostname, devicePort, debugUri?.path); } try { final bool useHot = getBuildInfo().isDebug; @@ -277,6 +307,22 @@ class AttachCommand extends FlutterCommand { } Future _validateArguments() async { } + + Future _buildObservatoryUri(Device device, + String host, int devicePort, [String authCode]) async { + String path = '/'; + if (authCode != null) { + path = authCode; + } + // Not having a trailing slash can cause problems in some situations. + // Ensure that there's one present. + if (!path.endsWith('/')) { + path += '/'; + } + final int localPort = observatoryPort + ?? await device.portForwarder.forward(devicePort); + return Uri(scheme: 'http', host: host, port: localPort, path: path); + } } class HotRunnerFactory { @@ -310,15 +356,21 @@ class HotRunnerFactory { ); } -/// A wrapper around [MDnsClient] to find a Dart observatory port. -class MDnsObservatoryPortDiscovery { - /// Creates a new [MDnsObservatoryPortDiscovery] object. +class MDnsObservatoryDiscoveryResult { + MDnsObservatoryDiscoveryResult(this.port, this.authCode); + final int port; + final String authCode; +} + +/// A wrapper around [MDnsClient] to find a Dart observatory instance. +class MDnsObservatoryDiscovery { + /// Creates a new [MDnsObservatoryDiscovery] object. /// /// The [client] parameter will be defaulted to a new [MDnsClient] if null. /// The [applicationId] parameter may be null, and can be used to /// automatically select which application to use if multiple are advertising /// Dart observatory ports. - MDnsObservatoryPortDiscovery({MDnsClient mdnsClient}) + MDnsObservatoryDiscovery({MDnsClient mdnsClient}) : client = mdnsClient ?? MDnsClient(); /// The [MDnsClient] used to do a lookup. @@ -326,14 +378,14 @@ class MDnsObservatoryPortDiscovery { static const String dartObservatoryName = '_dartobservatory._tcp.local'; - /// Executes an mDNS query for a Dart Observatory port. + /// Executes an mDNS query for a Dart Observatory. /// /// The [applicationId] parameter may be used to specify which application /// to find. For Android, it refers to the package name; on iOS, it refers to /// the bundle ID. /// - /// If it is not null, this method will find the port of the - /// Dart Observatory for that application. If it cannot find a Dart + /// If it is not null, this method will find the port and authentication code + /// of the Dart Observatory for that application. If it cannot find a Dart /// Observatory matching that application identifier, it will call /// [throwToolExit]. /// @@ -341,9 +393,10 @@ class MDnsObservatoryPortDiscovery { /// prompted with a list of available observatory ports and asked to select /// one. /// - /// If it is null and there is only one available port, it will return that - /// port regardless of what application the port is for. - Future queryForPort({String applicationId}) async { + /// If it is null and there is only one available instance of Observatory, + /// it will return that instance's information regardless of what application + /// the Observatory instance is for. + Future query({String applicationId}) async { printStatus('Checking for advertised Dart observatories...'); try { await client.start(); @@ -378,7 +431,7 @@ class MDnsObservatoryPortDiscovery { buffer.writeln('There are multiple observatory ports available.'); buffer.writeln('Rerun this command with one of the following passed in as the appId:'); buffer.writeln(''); - for (final String uniqueDomainName in uniqueDomainNames) { + for (final String uniqueDomainName in uniqueDomainNames) { buffer.writeln(' flutter attach --app-id ${uniqueDomainName.replaceAll('.$dartObservatoryName', '')}'); } throwToolExit(buffer.toString()); @@ -399,7 +452,33 @@ class MDnsObservatoryPortDiscovery { printError('Unexpectedly found more than one observatory report for $domainName ' '- using first one (${srv.first.port}).'); } - return srv.first.port; + printStatus('Checking for authentication code for $domainName'); + final List txt = await client + .lookup( + ResourceRecordQuery.text(domainName), + ) + ?.toList(); + if (txt == null || txt.isEmpty) { + return MDnsObservatoryDiscoveryResult(srv.first.port, ''); + } + String authCode = ''; + const String authCodePrefix = 'authCode='; + String raw = txt.first.text; + // TXT has a format of [, text], so if the length is 2, + // that means that TXT is empty. + if (raw.length > 2) { + // Remove length byte from raw txt. + raw = raw.substring(1); + if (raw.startsWith(authCodePrefix)) { + authCode = raw.substring(authCodePrefix.length); + // The Observatory currently expects a trailing '/' as part of the + // URI, otherwise an invalid authentication code response is given. + if (!authCode.endsWith('/')) { + authCode += '/'; + } + } + } + return MDnsObservatoryDiscoveryResult(srv.first.port, authCode); } finally { client.stop(); } diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index 926154c0f2..959fb9d0c8 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart @@ -169,6 +169,11 @@ class RunCommand extends RunCommandBase { 'results out to "refresh_benchmark.json", and exit. This flag is ' 'intended for use in generating automated flutter benchmarks.', ) + ..addFlag('disable-service-auth-codes', + negatable: false, + hide: !verboseHelp, + help: 'No longer require an authentication code to connect to the VM ' + 'service (not recommended).') ..addOption(FlutterOptions.kExtraFrontEndOptions, hide: true) ..addOption(FlutterOptions.kExtraGenSnapshotOptions, hide: true) ..addMultiOption(FlutterOptions.kEnableExperiment, @@ -262,6 +267,7 @@ class RunCommand extends RunCommandBase { return DebuggingOptions.enabled( buildInfo, startPaused: argResults['start-paused'], + disableServiceAuthCodes: argResults['disable-service-auth-codes'], useTestFonts: argResults['use-test-fonts'], enableSoftwareRendering: argResults['enable-software-rendering'], skiaDeterministicRendering: argResults['skia-deterministic-rendering'], diff --git a/packages/flutter_tools/lib/src/commands/test.dart b/packages/flutter_tools/lib/src/commands/test.dart index c776bd72d8..aa93bf01cb 100644 --- a/packages/flutter_tools/lib/src/commands/test.dart +++ b/packages/flutter_tools/lib/src/commands/test.dart @@ -43,6 +43,13 @@ class TestCommand extends FastFlutterCommand { 'Instructions for connecting with a debugger and printed to the ' 'console once the test has started.', ) + ..addFlag('disable-service-auth-codes', + hide: !verboseHelp, + defaultsTo: false, + negatable: false, + help: 'No longer require an authentication code to connect to the VM ' + 'service (not recommended).' + ) ..addFlag('coverage', defaultsTo: false, negatable: false, @@ -194,6 +201,9 @@ class TestCommand extends FastFlutterCommand { } } + final bool disableServiceAuthCodes = + argResults['disable-service-auth-codes']; + final int result = await runTests( files, workDir: workDir, @@ -202,6 +212,7 @@ class TestCommand extends FastFlutterCommand { watcher: watcher, enableObservatory: collector != null || startPaused, startPaused: startPaused, + disableServiceAuthCodes: disableServiceAuthCodes, ipv6: argResults['ipv6'], machine: machine, trackWidgetCreation: argResults['track-widget-creation'], diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 1ff9e3840b..29bb3e7392 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -367,6 +367,7 @@ class DebuggingOptions { DebuggingOptions.enabled( this.buildInfo, { this.startPaused = false, + this.disableServiceAuthCodes = false, this.enableSoftwareRendering = false, this.skiaDeterministicRendering = false, this.traceSkia = false, @@ -381,6 +382,7 @@ class DebuggingOptions { : debuggingEnabled = false, useTestFonts = false, startPaused = false, + disableServiceAuthCodes = false, enableSoftwareRendering = false, skiaDeterministicRendering = false, traceSkia = false, @@ -393,6 +395,7 @@ class DebuggingOptions { final BuildInfo buildInfo; final bool startPaused; + final bool disableServiceAuthCodes; final bool enableSoftwareRendering; final bool skiaDeterministicRendering; final bool traceSkia; diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart index 9cc4272a5c..d469da18f3 100644 --- a/packages/flutter_tools/lib/src/ios/devices.dart +++ b/packages/flutter_tools/lib/src/ios/devices.dart @@ -279,6 +279,9 @@ class IOSDevice extends Device { if (debuggingOptions.startPaused) launchArguments.add('--start-paused'); + if (debuggingOptions.disableServiceAuthCodes) + launchArguments.add('--disable-service-auth-codes'); + if (debuggingOptions.useTestFonts) launchArguments.add('--use-test-fonts'); diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart index 82f2ecf29f..a60ba54f59 100644 --- a/packages/flutter_tools/lib/src/ios/simulators.dart +++ b/packages/flutter_tools/lib/src/ios/simulators.dart @@ -325,6 +325,8 @@ class IOSSimulator extends Device { ]); if (debuggingOptions.startPaused) args.add('--start-paused'); + if (debuggingOptions.disableServiceAuthCodes) + args.add('--disable-service-auth-codes'); if (debuggingOptions.skiaDeterministicRendering) args.add('--skia-deterministic-rendering'); if (debuggingOptions.useTestFonts) diff --git a/packages/flutter_tools/lib/src/protocol_discovery.dart b/packages/flutter_tools/lib/src/protocol_discovery.dart index cfd6b6f937..96ea8d2bba 100644 --- a/packages/flutter_tools/lib/src/protocol_discovery.dart +++ b/packages/flutter_tools/lib/src/protocol_discovery.dart @@ -59,8 +59,7 @@ class ProtocolDiscovery { void _handleLine(String line) { Uri uri; - - final RegExp r = RegExp('${RegExp.escape(serviceName)} listening on ((http|\/\/)[a-zA-Z0-9:/=\.\\[\\]]+)'); + final RegExp r = RegExp('${RegExp.escape(serviceName)} listening on ((http|\/\/)[a-zA-Z0-9:/=_\\-\.\\[\\]]+)'); final Match match = r.firstMatch(line); if (match != null) { diff --git a/packages/flutter_tools/lib/src/test/flutter_platform.dart b/packages/flutter_tools/lib/src/test/flutter_platform.dart index 6e43615e9c..6230059580 100644 --- a/packages/flutter_tools/lib/src/test/flutter_platform.dart +++ b/packages/flutter_tools/lib/src/test/flutter_platform.dart @@ -83,6 +83,7 @@ void installHook({ bool enableObservatory = false, bool machine = false, bool startPaused = false, + bool disableServiceAuthCodes = false, int port = 0, String precompiledDillPath, Map precompiledDillFiles, @@ -104,6 +105,7 @@ void installHook({ machine: machine, enableObservatory: enableObservatory, startPaused: startPaused, + disableServiceAuthCodes: disableServiceAuthCodes, explicitObservatoryPort: observatoryPort, host: _kHosts[serverType], port: port, @@ -385,6 +387,7 @@ class _FlutterPlatform extends PlatformPlugin { this.enableObservatory, this.machine, this.startPaused, + this.disableServiceAuthCodes, this.explicitObservatoryPort, this.host, this.port, @@ -403,6 +406,7 @@ class _FlutterPlatform extends PlatformPlugin { final bool enableObservatory; final bool machine; final bool startPaused; + final bool disableServiceAuthCodes; final int explicitObservatoryPort; final InternetAddress host; final int port; @@ -585,6 +589,7 @@ class _FlutterPlatform extends PlatformPlugin { packages: PackageMap.globalPackagesPath, enableObservatory: enableObservatory, startPaused: startPaused, + disableServiceAuthCodes: disableServiceAuthCodes, observatoryPort: explicitObservatoryPort, serverPort: server.port, ); @@ -932,6 +937,7 @@ class _FlutterPlatform extends PlatformPlugin { String packages, bool enableObservatory = false, bool startPaused = false, + bool disableServiceAuthCodes = false, int observatoryPort, int serverPort, }) { @@ -953,6 +959,9 @@ class _FlutterPlatform extends PlatformPlugin { if (startPaused) { command.add('--start-paused'); } + if (disableServiceAuthCodes) { + command.add('--disable-service-auth-codes'); + } } else { command.add('--disable-observatory'); } diff --git a/packages/flutter_tools/lib/src/test/runner.dart b/packages/flutter_tools/lib/src/test/runner.dart index 26ce0db470..2e1bc76473 100644 --- a/packages/flutter_tools/lib/src/test/runner.dart +++ b/packages/flutter_tools/lib/src/test/runner.dart @@ -27,6 +27,7 @@ Future runTests( List plainNames = const [], bool enableObservatory = false, bool startPaused = false, + bool disableServiceAuthCodes = false, bool ipv6 = false, bool machine = false, String precompiledDillPath, @@ -79,6 +80,7 @@ Future runTests( enableObservatory: enableObservatory, machine: machine, startPaused: startPaused, + disableServiceAuthCodes: disableServiceAuthCodes, serverType: serverType, precompiledDillPath: precompiledDillPath, precompiledDillFiles: precompiledDillFiles, diff --git a/packages/flutter_tools/lib/src/tester/flutter_tester.dart b/packages/flutter_tools/lib/src/tester/flutter_tester.dart index d5f5c223f4..14c5d3eb8e 100644 --- a/packages/flutter_tools/lib/src/tester/flutter_tester.dart +++ b/packages/flutter_tools/lib/src/tester/flutter_tester.dart @@ -120,6 +120,8 @@ class FlutterTesterDevice extends Device { if (debuggingOptions.debuggingEnabled) { if (debuggingOptions.startPaused) command.add('--start-paused'); + if (debuggingOptions.disableServiceAuthCodes) + command.add('--disable-service-auth-codes'); if (debuggingOptions.hasObservatoryPort) command.add('--observatory-port=${debuggingOptions.observatoryPort}'); } diff --git a/packages/flutter_tools/test/commands/attach_test.dart b/packages/flutter_tools/test/commands/attach_test.dart index 87dcb92009..7093640c48 100644 --- a/packages/flutter_tools/test/commands/attach_test.dart +++ b/packages/flutter_tools/test/commands/attach_test.dart @@ -178,7 +178,7 @@ void main() { await expectLater( createTestCommandRunner(command).run(['attach', '--ipv6']), throwsToolExit( - message: 'When the --debug-port is unknown, this command determines ' + message: 'When the --debug-port or --debug-uri is unknown, this command determines ' 'the value of --ipv6 on its own.', ), ); @@ -193,7 +193,7 @@ void main() { await expectLater( createTestCommandRunner(command).run(['attach', '--observatory-port', '100']), throwsToolExit( - message: 'When the --debug-port is unknown, this command does not use ' + message: 'When the --debug-port or --debug-uri is unknown, this command does not use ' 'the value of --observatory-port.', ), ); @@ -438,7 +438,7 @@ void main() { final MDnsClient client = MockMDnsClient(); when(client.lookup( - ResourceRecordQuery.serverPointer(MDnsObservatoryPortDiscovery.dartObservatoryName), + ResourceRecordQuery.serverPointer(MDnsObservatoryDiscovery.dartObservatoryName), )).thenAnswer((_) => Stream.fromIterable(ptrRecords)); for (final MapEntry> entry in srvResponse.entries) { @@ -452,8 +452,8 @@ void main() { testUsingContext('No ports available', () async { final MDnsClient client = getMockClient([], >{}); - final MDnsObservatoryPortDiscovery portDiscovery = MDnsObservatoryPortDiscovery(mdnsClient: client); - final int port = await portDiscovery.queryForPort(); + final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(mdnsClient: client); + final int port = (await portDiscovery.query())?.port; expect(port, isNull); }); @@ -469,8 +469,8 @@ void main() { }, ); - final MDnsObservatoryPortDiscovery portDiscovery = MDnsObservatoryPortDiscovery(mdnsClient: client); - final int port = await portDiscovery.queryForPort(); + final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(mdnsClient: client); + final int port = (await portDiscovery.query())?.port; expect(port, 123); }); @@ -490,8 +490,8 @@ void main() { }, ); - final MDnsObservatoryPortDiscovery portDiscovery = MDnsObservatoryPortDiscovery(mdnsClient: client); - expect(() => portDiscovery.queryForPort(), throwsToolExit()); + final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(mdnsClient: client); + expect(() => portDiscovery.query(), throwsToolExit()); }); testUsingContext('Multiple ports available, with appId', () async { @@ -510,8 +510,8 @@ void main() { }, ); - final MDnsObservatoryPortDiscovery portDiscovery = MDnsObservatoryPortDiscovery(mdnsClient: client); - final int port = await portDiscovery.queryForPort(applicationId: 'fiz'); + final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(mdnsClient: client); + final int port = (await portDiscovery.query(applicationId: 'fiz'))?.port; expect(port, 321); }); @@ -533,8 +533,8 @@ void main() { }, ); - final MDnsObservatoryPortDiscovery portDiscovery = MDnsObservatoryPortDiscovery(mdnsClient: client); - final int port = await portDiscovery.queryForPort(applicationId: 'bar'); + final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(mdnsClient: client); + final int port = (await portDiscovery.query(applicationId: 'bar'))?.port; expect(port, 1234); }); }); diff --git a/packages/flutter_tools/test/integration/test_driver.dart b/packages/flutter_tools/test/integration/test_driver.dart index de346742ec..7bb3ceb997 100644 --- a/packages/flutter_tools/test/integration/test_driver.dart +++ b/packages/flutter_tools/test/integration/test_driver.dart @@ -417,6 +417,7 @@ class FlutterRunTestDriver extends FlutterTestDriver { await _setupProcess( [ 'run', + '--disable-service-auth-codes', '--machine', '-d', 'flutter-tester', @@ -607,6 +608,7 @@ class FlutterTestTestDriver extends FlutterTestDriver { }) async { await _setupProcess([ 'test', + '--disable-service-auth-codes', '--machine', '-d', 'flutter-tester',