Unverified Commit 3764cb85 authored by Ben Konyi's avatar Ben Konyi Committed by GitHub

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.
parent 086fd993
4b9966f5cb412a73fa50462b3aee9082f436a62a ca31a7c57bada458fa7f5c0d3f36bc1af4ccbc79
...@@ -26,7 +26,7 @@ void main() { ...@@ -26,7 +26,7 @@ void main() {
print('run: starting...'); print('run: starting...');
final Process run = await startProcess( final Process run = await startProcess(
path.join(flutterDirectory.path, 'bin', 'flutter'), path.join(flutterDirectory.path, 'bin', 'flutter'),
<String>['run', '--verbose', '-d', device.deviceId, 'lib/commands.dart'], <String>['run', '--verbose', '--disable-service-auth-codes', '-d', device.deviceId, 'lib/commands.dart'],
); );
final StreamController<String> stdout = StreamController<String>.broadcast(); final StreamController<String> stdout = StreamController<String>.broadcast();
run.stdout run.stdout
......
...@@ -21,7 +21,8 @@ void main() { ...@@ -21,7 +21,8 @@ void main() {
section('Compile and run the tester app'); section('Compile and run the tester app');
Completer<void> firstNameFound = Completer<void>(); Completer<void> firstNameFound = Completer<void>();
Completer<void> secondNameFound = Completer<void>(); Completer<void> secondNameFound = Completer<void>();
final Process runProcess = await _run(device: device, command: <String>['run'], stdoutListener: (String line) { final Process runProcess = await _run(device: device, command:
<String>['run', '--disable-service-auth-codes'], stdoutListener: (String line) {
if (line.contains(_kFirstIsolateName)) { if (line.contains(_kFirstIsolateName)) {
firstNameFound.complete(); firstNameFound.complete();
} else if (line.contains(_kSecondIsolateName)) { } else if (line.contains(_kSecondIsolateName)) {
......
...@@ -34,7 +34,7 @@ void main() { ...@@ -34,7 +34,7 @@ void main() {
print('run: starting...'); print('run: starting...');
final Process run = await startProcess( final Process run = await startProcess(
path.join(flutterDirectory.path, 'bin', 'flutter'), path.join(flutterDirectory.path, 'bin', 'flutter'),
<String>['run', '--verbose', '-d', device.deviceId, '--route', '/smuggle-it', 'lib/route.dart'], <String>['run', '--verbose', '--disable-service-auth-codes', '-d', device.deviceId, '--route', '/smuggle-it', 'lib/route.dart'],
); );
run.stdout run.stdout
.transform<String>(utf8.decoder) .transform<String>(utf8.decoder)
......
...@@ -26,7 +26,7 @@ void main() { ...@@ -26,7 +26,7 @@ void main() {
print('run: starting...'); print('run: starting...');
final Process run = await startProcess( final Process run = await startProcess(
path.join(flutterDirectory.path, 'bin', 'flutter'), path.join(flutterDirectory.path, 'bin', 'flutter'),
<String>['run', '--verbose', '-d', device.deviceId, 'lib/main.dart'], <String>['run', '--verbose', '--disable-service-auth-codes', '-d', device.deviceId, 'lib/main.dart'],
); );
run.stdout run.stdout
.transform<String>(utf8.decoder) .transform<String>(utf8.decoder)
......
...@@ -41,16 +41,16 @@ Future<Map<String, dynamic>> runTask(String taskName, { bool silent = false }) a ...@@ -41,16 +41,16 @@ Future<Map<String, dynamic>> runTask(String taskName, { bool silent = false }) a
runnerFinished = true; runnerFinished = true;
}); });
final Completer<int> port = Completer<int>(); final Completer<Uri> uri = Completer<Uri>();
final StreamSubscription<String> stdoutSub = runner.stdout final StreamSubscription<String> stdoutSub = runner.stdout
.transform<String>(const Utf8Decoder()) .transform<String>(const Utf8Decoder())
.transform<String>(const LineSplitter()) .transform<String>(const LineSplitter())
.listen((String line) { .listen((String line) {
if (!port.isCompleted) { if (!uri.isCompleted) {
final int portValue = parseServicePort(line, prefix: 'Observatory listening on '); final Uri serviceUri = parseServiceUri(line, prefix: 'Observatory listening on ');
if (portValue != null) if (serviceUri != null)
port.complete(portValue); uri.complete(serviceUri);
} }
if (!silent) { if (!silent) {
stdout.writeln('[$taskName] [STDOUT] $line'); stdout.writeln('[$taskName] [STDOUT] $line');
...@@ -66,7 +66,7 @@ Future<Map<String, dynamic>> runTask(String taskName, { bool silent = false }) a ...@@ -66,7 +66,7 @@ Future<Map<String, dynamic>> runTask(String taskName, { bool silent = false }) a
String waitingFor = 'connection'; String waitingFor = 'connection';
try { try {
final VMIsolateRef isolate = await _connectToRunnerIsolate(await port.future); final VMIsolateRef isolate = await _connectToRunnerIsolate(await uri.future);
waitingFor = 'task completion'; waitingFor = 'task completion';
final Map<String, dynamic> taskResult = final Map<String, dynamic> taskResult =
await isolate.invokeExtension('ext.cocoonRunTask').timeout(taskTimeoutWithGracePeriod); await isolate.invokeExtension('ext.cocoonRunTask').timeout(taskTimeoutWithGracePeriod);
...@@ -88,8 +88,15 @@ Future<Map<String, dynamic>> runTask(String taskName, { bool silent = false }) a ...@@ -88,8 +88,15 @@ Future<Map<String, dynamic>> runTask(String taskName, { bool silent = false }) a
} }
} }
Future<VMIsolateRef> _connectToRunnerIsolate(int vmServicePort) async { Future<VMIsolateRef> _connectToRunnerIsolate(Uri vmServiceUri) async {
final String url = 'ws://localhost:$vmServicePort/ws'; final List<String> pathSegments = <String>[];
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(); final DateTime started = DateTime.now();
// TODO(yjbanov): due to lack of imagination at the moment the handshake with // TODO(yjbanov): due to lack of imagination at the moment the handshake with
......
...@@ -529,19 +529,43 @@ String extractCloudAuthTokenArg(List<String> rawArgs) { ...@@ -529,19 +529,43 @@ String extractCloudAuthTokenArg(List<String> rawArgs) {
return token; 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. /// Tries to extract a port from the string.
/// ///
/// The `prefix`, if specified, is a regular expression pattern and must not contain groups. /// 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: `.
/// The `multiLine` flag should be set to true if `line` is actually a buffer of many lines.
int parseServicePort(String line, { int parseServicePort(String line, {
String prefix = 'An Observatory debugger .* is available at: ', Pattern prefix,
bool multiLine = false,
}) { }) {
// e.g. "An Observatory debugger and profiler on ... is available at: http://127.0.0.1:8100/" prefix ??= _obsRegExp;
final RegExp pattern = RegExp('$prefix(\\S+:(\\d+)/\\S*)\$', multiLine: multiLine); final Match prefixMatch = prefix.matchAsPrefix(line);
final Match match = pattern.firstMatch(line); if (prefixMatch == null) {
return match == null ? null : int.parse(match.group(2)); return null;
}
final List<Match> 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<Match> 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 /// If FLUTTER_ENGINE environment variable is set then we need to pass
......
...@@ -16,4 +16,21 @@ void main() { ...@@ -16,4 +16,21 @@ void main() {
expect(grep(RegExp('^b'), from: 'ab\nba'), <String>['ba']); expect(grep(RegExp('^b'), from: 'ab\nba'), <String>['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);
});
});
} }
...@@ -438,6 +438,8 @@ class AndroidDevice extends Device { ...@@ -438,6 +438,8 @@ class AndroidDevice extends Device {
} }
if (debuggingOptions.startPaused) if (debuggingOptions.startPaused)
cmd.addAll(<String>['--ez', 'start-paused', 'true']); cmd.addAll(<String>['--ez', 'start-paused', 'true']);
if (debuggingOptions.disableServiceAuthCodes)
cmd.addAll(<String>['--ez', 'disable-service-auth-codes', 'true']);
if (debuggingOptions.useTestFonts) if (debuggingOptions.useTestFonts)
cmd.addAll(<String>['--ez', 'use-test-fonts', 'true']); cmd.addAll(<String>['--ez', 'use-test-fonts', 'true']);
if (debuggingOptions.verboseSystemLogs) { if (debuggingOptions.verboseSystemLogs) {
......
...@@ -31,6 +31,12 @@ import '../runner/flutter_command.dart'; ...@@ -31,6 +31,12 @@ import '../runner/flutter_command.dart';
/// With an application already running, a HotRunner can be attached to it /// With an application already running, a HotRunner can be attached to it
/// with: /// 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 /// $ flutter attach --debug-port 12345
/// ``` /// ```
/// ///
...@@ -56,7 +62,14 @@ class AttachCommand extends FlutterCommand { ...@@ -56,7 +62,14 @@ class AttachCommand extends FlutterCommand {
argParser argParser
..addOption( ..addOption(
'debug-port', '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( )..addOption(
'app-id', 'app-id',
help: 'The package name (Android) or bundle identifier (iOS) for the application. ' help: 'The package name (Android) or bundle identifier (iOS) for the application. '
...@@ -102,6 +115,17 @@ class AttachCommand extends FlutterCommand { ...@@ -102,6 +115,17 @@ class AttachCommand extends FlutterCommand {
return null; 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 { String get appId {
return argResults['app-id']; return argResults['app-id'];
} }
...@@ -112,24 +136,26 @@ class AttachCommand extends FlutterCommand { ...@@ -112,24 +136,26 @@ class AttachCommand extends FlutterCommand {
if (await findTargetDevice() == null) if (await findTargetDevice() == null)
throwToolExit(null); throwToolExit(null);
debugPort; debugPort;
if (debugPort == null && argResults.wasParsed(FlutterCommand.ipv6Flag)) { if (debugPort == null && debugUri == null && argResults.wasParsed(FlutterCommand.ipv6Flag)) {
throwToolExit( 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.', 'the value of --ipv6 on its own.',
); );
} }
if (debugPort == null && argResults.wasParsed(FlutterCommand.observatoryPortOption)) { if (debugPort == null && debugUri == null && argResults.wasParsed(FlutterCommand.observatoryPortOption)) {
throwToolExit( 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.', 'the value of --observatory-port.',
); );
} }
if (debugPort != null && debugUri != null) {
throwToolExit(
'Either --debugPort or --debugUri can be provided, not both.');
}
} }
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
final String ipv4Loopback = InternetAddress.loopbackIPv4.address;
final String ipv6Loopback = InternetAddress.loopbackIPv6.address;
final FlutterProject flutterProject = await FlutterProject.current(); final FlutterProject flutterProject = await FlutterProject.current();
Cache.releaseLockEarly(); Cache.releaseLockEarly();
...@@ -147,7 +173,6 @@ class AttachCommand extends FlutterCommand { ...@@ -147,7 +173,6 @@ class AttachCommand extends FlutterCommand {
// simulators support it. // simulators support it.
// If/when we do this on Android or other platforms, we can update it here. // If/when we do this on Android or other platforms, we can update it here.
if (device is IOSDevice || device is IOSSimulator) { if (device is IOSDevice || device is IOSSimulator) {
return MDnsObservatoryPortDiscovery().queryForPort(applicationId: appId);
} }
return null; return null;
} }
...@@ -159,9 +184,13 @@ class AttachCommand extends FlutterCommand { ...@@ -159,9 +184,13 @@ class AttachCommand extends FlutterCommand {
: null; : null;
Uri observatoryUri; 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; bool attachLogger = false;
if (devicePort == null) { if (devicePort == null && debugUri == null) {
if (device is FuchsiaDevice) { if (device is FuchsiaDevice) {
attachLogger = true; attachLogger = true;
final String module = argResults['module']; final String module = argResults['module'];
...@@ -181,7 +210,12 @@ class AttachCommand extends FlutterCommand { ...@@ -181,7 +210,12 @@ class AttachCommand extends FlutterCommand {
} }
rethrow; 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; ProtocolDiscovery observatoryDiscovery;
try { try {
observatoryDiscovery = ProtocolDiscovery.observatory( observatoryDiscovery = ProtocolDiscovery.observatory(
...@@ -198,12 +232,8 @@ class AttachCommand extends FlutterCommand { ...@@ -198,12 +232,8 @@ class AttachCommand extends FlutterCommand {
} }
} }
} else { } else {
usesIpv6 = ipv6; observatoryUri = await _buildObservatoryUri(device,
final int localPort = observatoryPort debugUri?.host ?? hostname, devicePort, debugUri?.path);
?? await device.portForwarder.forward(devicePort);
observatoryUri = usesIpv6
? Uri.parse('http://[$ipv6Loopback]:$localPort/')
: Uri.parse('http://$ipv4Loopback:$localPort/');
} }
try { try {
final bool useHot = getBuildInfo().isDebug; final bool useHot = getBuildInfo().isDebug;
...@@ -277,6 +307,22 @@ class AttachCommand extends FlutterCommand { ...@@ -277,6 +307,22 @@ class AttachCommand extends FlutterCommand {
} }
Future<void> _validateArguments() async { } Future<void> _validateArguments() async { }
Future<Uri> _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 { class HotRunnerFactory {
...@@ -310,15 +356,21 @@ class HotRunnerFactory { ...@@ -310,15 +356,21 @@ class HotRunnerFactory {
); );
} }
/// A wrapper around [MDnsClient] to find a Dart observatory port. class MDnsObservatoryDiscoveryResult {
class MDnsObservatoryPortDiscovery { MDnsObservatoryDiscoveryResult(this.port, this.authCode);
/// Creates a new [MDnsObservatoryPortDiscovery] object. 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 [client] parameter will be defaulted to a new [MDnsClient] if null.
/// The [applicationId] parameter may be null, and can be used to /// The [applicationId] parameter may be null, and can be used to
/// automatically select which application to use if multiple are advertising /// automatically select which application to use if multiple are advertising
/// Dart observatory ports. /// Dart observatory ports.
MDnsObservatoryPortDiscovery({MDnsClient mdnsClient}) MDnsObservatoryDiscovery({MDnsClient mdnsClient})
: client = mdnsClient ?? MDnsClient(); : client = mdnsClient ?? MDnsClient();
/// The [MDnsClient] used to do a lookup. /// The [MDnsClient] used to do a lookup.
...@@ -326,14 +378,14 @@ class MDnsObservatoryPortDiscovery { ...@@ -326,14 +378,14 @@ class MDnsObservatoryPortDiscovery {
static const String dartObservatoryName = '_dartobservatory._tcp.local'; 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 /// 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 /// to find. For Android, it refers to the package name; on iOS, it refers to
/// the bundle ID. /// the bundle ID.
/// ///
/// If it is not null, this method will find the port of the /// If it is not null, this method will find the port and authentication code
/// Dart Observatory for that application. If it cannot find a Dart /// of the Dart Observatory for that application. If it cannot find a Dart
/// Observatory matching that application identifier, it will call /// Observatory matching that application identifier, it will call
/// [throwToolExit]. /// [throwToolExit].
/// ///
...@@ -341,9 +393,10 @@ class MDnsObservatoryPortDiscovery { ...@@ -341,9 +393,10 @@ class MDnsObservatoryPortDiscovery {
/// prompted with a list of available observatory ports and asked to select /// prompted with a list of available observatory ports and asked to select
/// one. /// one.
/// ///
/// If it is null and there is only one available port, it will return that /// If it is null and there is only one available instance of Observatory,
/// port regardless of what application the port is for. /// it will return that instance's information regardless of what application
Future<int> queryForPort({String applicationId}) async { /// the Observatory instance is for.
Future<MDnsObservatoryDiscoveryResult> query({String applicationId}) async {
printStatus('Checking for advertised Dart observatories...'); printStatus('Checking for advertised Dart observatories...');
try { try {
await client.start(); await client.start();
...@@ -399,7 +452,33 @@ class MDnsObservatoryPortDiscovery { ...@@ -399,7 +452,33 @@ class MDnsObservatoryPortDiscovery {
printError('Unexpectedly found more than one observatory report for $domainName ' printError('Unexpectedly found more than one observatory report for $domainName '
'- using first one (${srv.first.port}).'); '- using first one (${srv.first.port}).');
} }
return srv.first.port; printStatus('Checking for authentication code for $domainName');
final List<TxtResourceRecord> txt = await client
.lookup<TxtResourceRecord>(
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 [<length byte>, 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 { } finally {
client.stop(); client.stop();
} }
......
...@@ -169,6 +169,11 @@ class RunCommand extends RunCommandBase { ...@@ -169,6 +169,11 @@ class RunCommand extends RunCommandBase {
'results out to "refresh_benchmark.json", and exit. This flag is ' 'results out to "refresh_benchmark.json", and exit. This flag is '
'intended for use in generating automated flutter benchmarks.', '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.kExtraFrontEndOptions, hide: true)
..addOption(FlutterOptions.kExtraGenSnapshotOptions, hide: true) ..addOption(FlutterOptions.kExtraGenSnapshotOptions, hide: true)
..addMultiOption(FlutterOptions.kEnableExperiment, ..addMultiOption(FlutterOptions.kEnableExperiment,
...@@ -262,6 +267,7 @@ class RunCommand extends RunCommandBase { ...@@ -262,6 +267,7 @@ class RunCommand extends RunCommandBase {
return DebuggingOptions.enabled( return DebuggingOptions.enabled(
buildInfo, buildInfo,
startPaused: argResults['start-paused'], startPaused: argResults['start-paused'],
disableServiceAuthCodes: argResults['disable-service-auth-codes'],
useTestFonts: argResults['use-test-fonts'], useTestFonts: argResults['use-test-fonts'],
enableSoftwareRendering: argResults['enable-software-rendering'], enableSoftwareRendering: argResults['enable-software-rendering'],
skiaDeterministicRendering: argResults['skia-deterministic-rendering'], skiaDeterministicRendering: argResults['skia-deterministic-rendering'],
......
...@@ -43,6 +43,13 @@ class TestCommand extends FastFlutterCommand { ...@@ -43,6 +43,13 @@ class TestCommand extends FastFlutterCommand {
'Instructions for connecting with a debugger and printed to the ' 'Instructions for connecting with a debugger and printed to the '
'console once the test has started.', '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', ..addFlag('coverage',
defaultsTo: false, defaultsTo: false,
negatable: false, negatable: false,
...@@ -194,6 +201,9 @@ class TestCommand extends FastFlutterCommand { ...@@ -194,6 +201,9 @@ class TestCommand extends FastFlutterCommand {
} }
} }
final bool disableServiceAuthCodes =
argResults['disable-service-auth-codes'];
final int result = await runTests( final int result = await runTests(
files, files,
workDir: workDir, workDir: workDir,
...@@ -202,6 +212,7 @@ class TestCommand extends FastFlutterCommand { ...@@ -202,6 +212,7 @@ class TestCommand extends FastFlutterCommand {
watcher: watcher, watcher: watcher,
enableObservatory: collector != null || startPaused, enableObservatory: collector != null || startPaused,
startPaused: startPaused, startPaused: startPaused,
disableServiceAuthCodes: disableServiceAuthCodes,
ipv6: argResults['ipv6'], ipv6: argResults['ipv6'],
machine: machine, machine: machine,
trackWidgetCreation: argResults['track-widget-creation'], trackWidgetCreation: argResults['track-widget-creation'],
......
...@@ -367,6 +367,7 @@ class DebuggingOptions { ...@@ -367,6 +367,7 @@ class DebuggingOptions {
DebuggingOptions.enabled( DebuggingOptions.enabled(
this.buildInfo, { this.buildInfo, {
this.startPaused = false, this.startPaused = false,
this.disableServiceAuthCodes = false,
this.enableSoftwareRendering = false, this.enableSoftwareRendering = false,
this.skiaDeterministicRendering = false, this.skiaDeterministicRendering = false,
this.traceSkia = false, this.traceSkia = false,
...@@ -381,6 +382,7 @@ class DebuggingOptions { ...@@ -381,6 +382,7 @@ class DebuggingOptions {
: debuggingEnabled = false, : debuggingEnabled = false,
useTestFonts = false, useTestFonts = false,
startPaused = false, startPaused = false,
disableServiceAuthCodes = false,
enableSoftwareRendering = false, enableSoftwareRendering = false,
skiaDeterministicRendering = false, skiaDeterministicRendering = false,
traceSkia = false, traceSkia = false,
...@@ -393,6 +395,7 @@ class DebuggingOptions { ...@@ -393,6 +395,7 @@ class DebuggingOptions {
final BuildInfo buildInfo; final BuildInfo buildInfo;
final bool startPaused; final bool startPaused;
final bool disableServiceAuthCodes;
final bool enableSoftwareRendering; final bool enableSoftwareRendering;
final bool skiaDeterministicRendering; final bool skiaDeterministicRendering;
final bool traceSkia; final bool traceSkia;
......
...@@ -279,6 +279,9 @@ class IOSDevice extends Device { ...@@ -279,6 +279,9 @@ class IOSDevice extends Device {
if (debuggingOptions.startPaused) if (debuggingOptions.startPaused)
launchArguments.add('--start-paused'); launchArguments.add('--start-paused');
if (debuggingOptions.disableServiceAuthCodes)
launchArguments.add('--disable-service-auth-codes');
if (debuggingOptions.useTestFonts) if (debuggingOptions.useTestFonts)
launchArguments.add('--use-test-fonts'); launchArguments.add('--use-test-fonts');
......
...@@ -325,6 +325,8 @@ class IOSSimulator extends Device { ...@@ -325,6 +325,8 @@ class IOSSimulator extends Device {
]); ]);
if (debuggingOptions.startPaused) if (debuggingOptions.startPaused)
args.add('--start-paused'); args.add('--start-paused');
if (debuggingOptions.disableServiceAuthCodes)
args.add('--disable-service-auth-codes');
if (debuggingOptions.skiaDeterministicRendering) if (debuggingOptions.skiaDeterministicRendering)
args.add('--skia-deterministic-rendering'); args.add('--skia-deterministic-rendering');
if (debuggingOptions.useTestFonts) if (debuggingOptions.useTestFonts)
......
...@@ -59,8 +59,7 @@ class ProtocolDiscovery { ...@@ -59,8 +59,7 @@ class ProtocolDiscovery {
void _handleLine(String line) { void _handleLine(String line) {
Uri uri; 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); final Match match = r.firstMatch(line);
if (match != null) { if (match != null) {
......
...@@ -83,6 +83,7 @@ void installHook({ ...@@ -83,6 +83,7 @@ void installHook({
bool enableObservatory = false, bool enableObservatory = false,
bool machine = false, bool machine = false,
bool startPaused = false, bool startPaused = false,
bool disableServiceAuthCodes = false,
int port = 0, int port = 0,
String precompiledDillPath, String precompiledDillPath,
Map<String, String> precompiledDillFiles, Map<String, String> precompiledDillFiles,
...@@ -104,6 +105,7 @@ void installHook({ ...@@ -104,6 +105,7 @@ void installHook({
machine: machine, machine: machine,
enableObservatory: enableObservatory, enableObservatory: enableObservatory,
startPaused: startPaused, startPaused: startPaused,
disableServiceAuthCodes: disableServiceAuthCodes,
explicitObservatoryPort: observatoryPort, explicitObservatoryPort: observatoryPort,
host: _kHosts[serverType], host: _kHosts[serverType],
port: port, port: port,
...@@ -385,6 +387,7 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -385,6 +387,7 @@ class _FlutterPlatform extends PlatformPlugin {
this.enableObservatory, this.enableObservatory,
this.machine, this.machine,
this.startPaused, this.startPaused,
this.disableServiceAuthCodes,
this.explicitObservatoryPort, this.explicitObservatoryPort,
this.host, this.host,
this.port, this.port,
...@@ -403,6 +406,7 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -403,6 +406,7 @@ class _FlutterPlatform extends PlatformPlugin {
final bool enableObservatory; final bool enableObservatory;
final bool machine; final bool machine;
final bool startPaused; final bool startPaused;
final bool disableServiceAuthCodes;
final int explicitObservatoryPort; final int explicitObservatoryPort;
final InternetAddress host; final InternetAddress host;
final int port; final int port;
...@@ -585,6 +589,7 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -585,6 +589,7 @@ class _FlutterPlatform extends PlatformPlugin {
packages: PackageMap.globalPackagesPath, packages: PackageMap.globalPackagesPath,
enableObservatory: enableObservatory, enableObservatory: enableObservatory,
startPaused: startPaused, startPaused: startPaused,
disableServiceAuthCodes: disableServiceAuthCodes,
observatoryPort: explicitObservatoryPort, observatoryPort: explicitObservatoryPort,
serverPort: server.port, serverPort: server.port,
); );
...@@ -932,6 +937,7 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -932,6 +937,7 @@ class _FlutterPlatform extends PlatformPlugin {
String packages, String packages,
bool enableObservatory = false, bool enableObservatory = false,
bool startPaused = false, bool startPaused = false,
bool disableServiceAuthCodes = false,
int observatoryPort, int observatoryPort,
int serverPort, int serverPort,
}) { }) {
...@@ -953,6 +959,9 @@ class _FlutterPlatform extends PlatformPlugin { ...@@ -953,6 +959,9 @@ class _FlutterPlatform extends PlatformPlugin {
if (startPaused) { if (startPaused) {
command.add('--start-paused'); command.add('--start-paused');
} }
if (disableServiceAuthCodes) {
command.add('--disable-service-auth-codes');
}
} else { } else {
command.add('--disable-observatory'); command.add('--disable-observatory');
} }
......
...@@ -27,6 +27,7 @@ Future<int> runTests( ...@@ -27,6 +27,7 @@ Future<int> runTests(
List<String> plainNames = const <String>[], List<String> plainNames = const <String>[],
bool enableObservatory = false, bool enableObservatory = false,
bool startPaused = false, bool startPaused = false,
bool disableServiceAuthCodes = false,
bool ipv6 = false, bool ipv6 = false,
bool machine = false, bool machine = false,
String precompiledDillPath, String precompiledDillPath,
...@@ -79,6 +80,7 @@ Future<int> runTests( ...@@ -79,6 +80,7 @@ Future<int> runTests(
enableObservatory: enableObservatory, enableObservatory: enableObservatory,
machine: machine, machine: machine,
startPaused: startPaused, startPaused: startPaused,
disableServiceAuthCodes: disableServiceAuthCodes,
serverType: serverType, serverType: serverType,
precompiledDillPath: precompiledDillPath, precompiledDillPath: precompiledDillPath,
precompiledDillFiles: precompiledDillFiles, precompiledDillFiles: precompiledDillFiles,
......
...@@ -120,6 +120,8 @@ class FlutterTesterDevice extends Device { ...@@ -120,6 +120,8 @@ class FlutterTesterDevice extends Device {
if (debuggingOptions.debuggingEnabled) { if (debuggingOptions.debuggingEnabled) {
if (debuggingOptions.startPaused) if (debuggingOptions.startPaused)
command.add('--start-paused'); command.add('--start-paused');
if (debuggingOptions.disableServiceAuthCodes)
command.add('--disable-service-auth-codes');
if (debuggingOptions.hasObservatoryPort) if (debuggingOptions.hasObservatoryPort)
command.add('--observatory-port=${debuggingOptions.observatoryPort}'); command.add('--observatory-port=${debuggingOptions.observatoryPort}');
} }
......
...@@ -178,7 +178,7 @@ void main() { ...@@ -178,7 +178,7 @@ void main() {
await expectLater( await expectLater(
createTestCommandRunner(command).run(<String>['attach', '--ipv6']), createTestCommandRunner(command).run(<String>['attach', '--ipv6']),
throwsToolExit( 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.', 'the value of --ipv6 on its own.',
), ),
); );
...@@ -193,7 +193,7 @@ void main() { ...@@ -193,7 +193,7 @@ void main() {
await expectLater( await expectLater(
createTestCommandRunner(command).run(<String>['attach', '--observatory-port', '100']), createTestCommandRunner(command).run(<String>['attach', '--observatory-port', '100']),
throwsToolExit( 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.', 'the value of --observatory-port.',
), ),
); );
...@@ -438,7 +438,7 @@ void main() { ...@@ -438,7 +438,7 @@ void main() {
final MDnsClient client = MockMDnsClient(); final MDnsClient client = MockMDnsClient();
when(client.lookup<PtrResourceRecord>( when(client.lookup<PtrResourceRecord>(
ResourceRecordQuery.serverPointer(MDnsObservatoryPortDiscovery.dartObservatoryName), ResourceRecordQuery.serverPointer(MDnsObservatoryDiscovery.dartObservatoryName),
)).thenAnswer((_) => Stream<PtrResourceRecord>.fromIterable(ptrRecords)); )).thenAnswer((_) => Stream<PtrResourceRecord>.fromIterable(ptrRecords));
for (final MapEntry<String, List<SrvResourceRecord>> entry in srvResponse.entries) { for (final MapEntry<String, List<SrvResourceRecord>> entry in srvResponse.entries) {
...@@ -452,8 +452,8 @@ void main() { ...@@ -452,8 +452,8 @@ void main() {
testUsingContext('No ports available', () async { testUsingContext('No ports available', () async {
final MDnsClient client = getMockClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{}); final MDnsClient client = getMockClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{});
final MDnsObservatoryPortDiscovery portDiscovery = MDnsObservatoryPortDiscovery(mdnsClient: client); final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(mdnsClient: client);
final int port = await portDiscovery.queryForPort(); final int port = (await portDiscovery.query())?.port;
expect(port, isNull); expect(port, isNull);
}); });
...@@ -469,8 +469,8 @@ void main() { ...@@ -469,8 +469,8 @@ void main() {
}, },
); );
final MDnsObservatoryPortDiscovery portDiscovery = MDnsObservatoryPortDiscovery(mdnsClient: client); final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(mdnsClient: client);
final int port = await portDiscovery.queryForPort(); final int port = (await portDiscovery.query())?.port;
expect(port, 123); expect(port, 123);
}); });
...@@ -490,8 +490,8 @@ void main() { ...@@ -490,8 +490,8 @@ void main() {
}, },
); );
final MDnsObservatoryPortDiscovery portDiscovery = MDnsObservatoryPortDiscovery(mdnsClient: client); final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(mdnsClient: client);
expect(() => portDiscovery.queryForPort(), throwsToolExit()); expect(() => portDiscovery.query(), throwsToolExit());
}); });
testUsingContext('Multiple ports available, with appId', () async { testUsingContext('Multiple ports available, with appId', () async {
...@@ -510,8 +510,8 @@ void main() { ...@@ -510,8 +510,8 @@ void main() {
}, },
); );
final MDnsObservatoryPortDiscovery portDiscovery = MDnsObservatoryPortDiscovery(mdnsClient: client); final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(mdnsClient: client);
final int port = await portDiscovery.queryForPort(applicationId: 'fiz'); final int port = (await portDiscovery.query(applicationId: 'fiz'))?.port;
expect(port, 321); expect(port, 321);
}); });
...@@ -533,8 +533,8 @@ void main() { ...@@ -533,8 +533,8 @@ void main() {
}, },
); );
final MDnsObservatoryPortDiscovery portDiscovery = MDnsObservatoryPortDiscovery(mdnsClient: client); final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(mdnsClient: client);
final int port = await portDiscovery.queryForPort(applicationId: 'bar'); final int port = (await portDiscovery.query(applicationId: 'bar'))?.port;
expect(port, 1234); expect(port, 1234);
}); });
}); });
......
...@@ -417,6 +417,7 @@ class FlutterRunTestDriver extends FlutterTestDriver { ...@@ -417,6 +417,7 @@ class FlutterRunTestDriver extends FlutterTestDriver {
await _setupProcess( await _setupProcess(
<String>[ <String>[
'run', 'run',
'--disable-service-auth-codes',
'--machine', '--machine',
'-d', '-d',
'flutter-tester', 'flutter-tester',
...@@ -607,6 +608,7 @@ class FlutterTestTestDriver extends FlutterTestDriver { ...@@ -607,6 +608,7 @@ class FlutterTestTestDriver extends FlutterTestDriver {
}) async { }) async {
await _setupProcess(<String>[ await _setupProcess(<String>[
'test', 'test',
'--disable-service-auth-codes',
'--machine', '--machine',
'-d', '-d',
'flutter-tester', 'flutter-tester',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment