Skip to content

Commit 3764cb8

Browse files
authored
Added support for authentication codes for the VM service. (flutter#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.
1 parent 086fd99 commit 3764cb8

21 files changed

+235
-66
lines changed

bin/internal/engine.version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
4b9966f5cb412a73fa50462b3aee9082f436a62a
1+
ca31a7c57bada458fa7f5c0d3f36bc1af4ccbc79

dev/devicelab/bin/tasks/commands_test.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ void main() {
2626
print('run: starting...');
2727
final Process run = await startProcess(
2828
path.join(flutterDirectory.path, 'bin', 'flutter'),
29-
<String>['run', '--verbose', '-d', device.deviceId, 'lib/commands.dart'],
29+
<String>['run', '--verbose', '--disable-service-auth-codes', '-d', device.deviceId, 'lib/commands.dart'],
3030
);
3131
final StreamController<String> stdout = StreamController<String>.broadcast();
3232
run.stdout

dev/devicelab/bin/tasks/named_isolates_test.dart

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ void main() {
2121
section('Compile and run the tester app');
2222
Completer<void> firstNameFound = Completer<void>();
2323
Completer<void> secondNameFound = Completer<void>();
24-
final Process runProcess = await _run(device: device, command: <String>['run'], stdoutListener: (String line) {
24+
final Process runProcess = await _run(device: device, command:
25+
<String>['run', '--disable-service-auth-codes'], stdoutListener: (String line) {
2526
if (line.contains(_kFirstIsolateName)) {
2627
firstNameFound.complete();
2728
} else if (line.contains(_kSecondIsolateName)) {

dev/devicelab/bin/tasks/routing_test.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ void main() {
3434
print('run: starting...');
3535
final Process run = await startProcess(
3636
path.join(flutterDirectory.path, 'bin', 'flutter'),
37-
<String>['run', '--verbose', '-d', device.deviceId, '--route', '/smuggle-it', 'lib/route.dart'],
37+
<String>['run', '--verbose', '--disable-service-auth-codes', '-d', device.deviceId, '--route', '/smuggle-it', 'lib/route.dart'],
3838
);
3939
run.stdout
4040
.transform<String>(utf8.decoder)

dev/devicelab/bin/tasks/service_extensions_test.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ void main() {
2626
print('run: starting...');
2727
final Process run = await startProcess(
2828
path.join(flutterDirectory.path, 'bin', 'flutter'),
29-
<String>['run', '--verbose', '-d', device.deviceId, 'lib/main.dart'],
29+
<String>['run', '--verbose', '--disable-service-auth-codes', '-d', device.deviceId, 'lib/main.dart'],
3030
);
3131
run.stdout
3232
.transform<String>(utf8.decoder)

dev/devicelab/lib/framework/runner.dart

+16-9
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,16 @@ Future<Map<String, dynamic>> runTask(String taskName, { bool silent = false }) a
4141
runnerFinished = true;
4242
});
4343

44-
final Completer<int> port = Completer<int>();
44+
final Completer<Uri> uri = Completer<Uri>();
4545

4646
final StreamSubscription<String> stdoutSub = runner.stdout
4747
.transform<String>(const Utf8Decoder())
4848
.transform<String>(const LineSplitter())
4949
.listen((String line) {
50-
if (!port.isCompleted) {
51-
final int portValue = parseServicePort(line, prefix: 'Observatory listening on ');
52-
if (portValue != null)
53-
port.complete(portValue);
50+
if (!uri.isCompleted) {
51+
final Uri serviceUri = parseServiceUri(line, prefix: 'Observatory listening on ');
52+
if (serviceUri != null)
53+
uri.complete(serviceUri);
5454
}
5555
if (!silent) {
5656
stdout.writeln('[$taskName] [STDOUT] $line');
@@ -66,7 +66,7 @@ Future<Map<String, dynamic>> runTask(String taskName, { bool silent = false }) a
6666

6767
String waitingFor = 'connection';
6868
try {
69-
final VMIsolateRef isolate = await _connectToRunnerIsolate(await port.future);
69+
final VMIsolateRef isolate = await _connectToRunnerIsolate(await uri.future);
7070
waitingFor = 'task completion';
7171
final Map<String, dynamic> taskResult =
7272
await isolate.invokeExtension('ext.cocoonRunTask').timeout(taskTimeoutWithGracePeriod);
@@ -88,8 +88,15 @@ Future<Map<String, dynamic>> runTask(String taskName, { bool silent = false }) a
8888
}
8989
}
9090

91-
Future<VMIsolateRef> _connectToRunnerIsolate(int vmServicePort) async {
92-
final String url = 'ws://localhost:$vmServicePort/ws';
91+
Future<VMIsolateRef> _connectToRunnerIsolate(Uri vmServiceUri) async {
92+
final List<String> pathSegments = <String>[];
93+
if (vmServiceUri.pathSegments.isNotEmpty) {
94+
// Add authentication code.
95+
pathSegments.add(vmServiceUri.pathSegments[0]);
96+
}
97+
pathSegments.add('ws');
98+
final String url = vmServiceUri.replace(scheme: 'ws', pathSegments:
99+
pathSegments).toString();
93100
final DateTime started = DateTime.now();
94101

95102
// TODO(yjbanov): due to lack of imagination at the moment the handshake with
@@ -163,4 +170,4 @@ Future<void> cleanupSystem() async {
163170
} else {
164171
print('Could not determine JAVA_HOME; not shutting down Gradle.');
165172
}
166-
}
173+
}

dev/devicelab/lib/framework/utils.dart

+32-8
Original file line numberDiff line numberDiff line change
@@ -529,19 +529,43 @@ String extractCloudAuthTokenArg(List<String> rawArgs) {
529529
return token;
530530
}
531531

532+
final RegExp _obsRegExp =
533+
RegExp('An Observatory debugger .* is available at: ');
534+
final RegExp _obsPortRegExp = RegExp('(\\S+:(\\d+)/\\S*)\$');
535+
final RegExp _obsUriRegExp = RegExp('((http|\/\/)[a-zA-Z0-9:/=_\\-\.\\[\\]]+)');
536+
532537
/// Tries to extract a port from the string.
533538
///
534539
/// The `prefix`, if specified, is a regular expression pattern and must not contain groups.
535-
///
536-
/// The `multiLine` flag should be set to true if `line` is actually a buffer of many lines.
540+
/// `prefix` defaults to the RegExp: `An Observatory debugger .* is available at: `.
537541
int parseServicePort(String line, {
538-
String prefix = 'An Observatory debugger .* is available at: ',
539-
bool multiLine = false,
542+
Pattern prefix,
540543
}) {
541-
// e.g. "An Observatory debugger and profiler on ... is available at: http://127.0.0.1:8100/"
542-
final RegExp pattern = RegExp('$prefix(\\S+:(\\d+)/\\S*)\$', multiLine: multiLine);
543-
final Match match = pattern.firstMatch(line);
544-
return match == null ? null : int.parse(match.group(2));
544+
prefix ??= _obsRegExp;
545+
final Match prefixMatch = prefix.matchAsPrefix(line);
546+
if (prefixMatch == null) {
547+
return null;
548+
}
549+
final List<Match> matches =
550+
_obsPortRegExp.allMatches(line, prefixMatch.end).toList();
551+
return matches.isEmpty ? null : int.parse(matches[0].group(2));
552+
}
553+
554+
/// Tries to extract a Uri from the string.
555+
///
556+
/// The `prefix`, if specified, is a regular expression pattern and must not contain groups.
557+
/// `prefix` defaults to the RegExp: `An Observatory debugger .* is available at: `.
558+
Uri parseServiceUri(String line, {
559+
Pattern prefix,
560+
}) {
561+
prefix ??= _obsRegExp;
562+
final Match prefixMatch = prefix.matchAsPrefix(line);
563+
if (prefixMatch == null) {
564+
return null;
565+
}
566+
final List<Match> matches =
567+
_obsUriRegExp.allMatches(line, prefixMatch.end).toList();
568+
return matches.isEmpty ? null : Uri.parse(matches[0].group(0));
545569
}
546570

547571
/// If FLUTTER_ENGINE environment variable is set then we need to pass

dev/devicelab/test/utils_test.dart

+17
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,21 @@ void main() {
1616
expect(grep(RegExp('^b'), from: 'ab\nba'), <String>['ba']);
1717
});
1818
});
19+
20+
group('parse service', () {
21+
const String badOutput = 'No uri here';
22+
const String sampleOutput = 'An Observatory debugger and profiler on '
23+
'Pixel 3 XL is available at: http://127.0.0.1:9090/LpjUpsdEjqI=/';
24+
25+
test('uri', () {
26+
expect(parseServiceUri(sampleOutput),
27+
Uri.parse('http://127.0.0.1:9090/LpjUpsdEjqI=/'));
28+
expect(parseServiceUri(badOutput), null);
29+
});
30+
31+
test('port', () {
32+
expect(parseServicePort(sampleOutput), 9090);
33+
expect(parseServicePort(badOutput), null);
34+
});
35+
});
1936
}

packages/flutter_tools/lib/src/android/android_device.dart

+2
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,8 @@ class AndroidDevice extends Device {
438438
}
439439
if (debuggingOptions.startPaused)
440440
cmd.addAll(<String>['--ez', 'start-paused', 'true']);
441+
if (debuggingOptions.disableServiceAuthCodes)
442+
cmd.addAll(<String>['--ez', 'disable-service-auth-codes', 'true']);
441443
if (debuggingOptions.useTestFonts)
442444
cmd.addAll(<String>['--ez', 'use-test-fonts', 'true']);
443445
if (debuggingOptions.verboseSystemLogs) {

0 commit comments

Comments
 (0)