Unverified Commit e792c6bb authored by Todd Volkert's avatar Todd Volkert Committed by GitHub

Add support for binding to IPv6 localhost in `flutter run` (#13136)

This allows `flutter run` to work on hosts that are IPv6-only.
parent f5d9c777
......@@ -345,6 +345,7 @@ class AndroidDevice extends Device {
bool prebuiltApplication: false,
bool applicationNeedsRebuild: false,
bool usesTerminalUi: true,
bool ipv6: false,
}) async {
if (!await _checkForSupportedAdbVersion() || !await _checkForSupportedAndroidVersion())
return new LaunchResult.failed();
......@@ -381,7 +382,11 @@ class AndroidDevice extends Device {
// TODO(devoncarew): Remember the forwarding information (so we can later remove the
// port forwarding or set it up again when adb fails on us).
observatoryDiscovery = new ProtocolDiscovery.observatory(
getLogReader(), portForwarder: portForwarder, hostPort: debuggingOptions.observatoryPort);
getLogReader(),
portForwarder: portForwarder,
hostPort: debuggingOptions.observatoryPort,
ipv6: ipv6,
);
}
List<String> cmd;
......
......@@ -28,6 +28,12 @@ abstract class RunCommandBase extends FlutterCommand {
negatable: true,
defaultsTo: false,
help: 'Start tracing during startup.');
argParser.addFlag('ipv6',
hide: true,
negatable: false,
defaultsTo: false,
help: 'Binds to IPv6 localhost instead of IPv4 when the flutter tool\n'
'forwards the host port to a device port.');
argParser.addOption('route',
help: 'Which route to load when running the app.');
usesTargetOption();
......@@ -36,6 +42,7 @@ abstract class RunCommandBase extends FlutterCommand {
}
bool get traceStartup => argResults['trace-startup'];
bool get ipv6 => argResults['ipv6'];
String get route => argResults['route'];
void usesPortOptions() {
......@@ -304,6 +311,7 @@ class RunCommand extends RunCommandBase {
packagesFilePath: argResults['packages'],
projectAssets: argResults['project-assets'],
stayResident: stayResident,
ipv6: ipv6,
);
} else {
runner = new ColdRunner(
......@@ -314,6 +322,7 @@ class RunCommand extends RunCommandBase {
applicationBinary: argResults['use-application-binary'],
previewDart2: argResults['preview-dart-2'],
stayResident: stayResident,
ipv6: ipv6,
);
}
......
......@@ -227,6 +227,7 @@ abstract class Device {
bool prebuiltApplication: false,
bool applicationNeedsRebuild: false,
bool usesTerminalUi: true,
bool ipv6: false,
});
/// Does this device implement support for hot reloading / restarting?
......
......@@ -68,6 +68,7 @@ class FuchsiaDevice extends Device {
bool previewDart2: false,
bool applicationNeedsRebuild: false,
bool usesTerminalUi: false,
bool ipv6: false,
}) => new Future<Null>.error('unimplemented');
@override
......
......@@ -163,6 +163,7 @@ class IOSDevice extends Device {
bool previewDart2: false,
bool applicationNeedsRebuild: false,
bool usesTerminalUi: true,
bool ipv6: false,
}) async {
if (!prebuiltApplication) {
// TODO(chinmaygarde): Use mainPath, route.
......@@ -255,7 +256,11 @@ class IOSDevice extends Device {
// TODO(danrubel): The Android device class does something similar to this code below.
// The various Device subclasses should be refactored and common code moved into the superclass.
final ProtocolDiscovery observatoryDiscovery = new ProtocolDiscovery.observatory(
getLogReader(app: app), portForwarder: portForwarder, hostPort: debuggingOptions.observatoryPort);
getLogReader(app: app),
portForwarder: portForwarder,
hostPort: debuggingOptions.observatoryPort,
ipv6: ipv6,
);
final Future<Uri> forwardObservatoryUri = observatoryDiscovery.uri;
......
......@@ -312,6 +312,7 @@ class IOSSimulator extends Device {
bool prebuiltApplication: false,
bool applicationNeedsRebuild: false,
bool usesTerminalUi: true,
bool ipv6: false,
}) async {
if (!prebuiltApplication) {
printTrace('Building ${app.name} for $id.');
......@@ -352,7 +353,8 @@ class IOSSimulator extends Device {
ProtocolDiscovery observatoryDiscovery;
if (debuggingOptions.debuggingEnabled)
observatoryDiscovery = new ProtocolDiscovery.observatory(getLogReader(app: app));
observatoryDiscovery = new ProtocolDiscovery.observatory(
getLogReader(app: app), ipv6: ipv6);
// Launch the updated application in the simulator.
try {
......
......@@ -5,6 +5,7 @@
import 'dart:async';
import 'base/common.dart';
import 'base/io.dart';
import 'base/port_scanner.dart';
import 'device.dart';
import 'globals.dart';
......@@ -18,6 +19,7 @@ class ProtocolDiscovery {
this.portForwarder,
this.hostPort,
this.defaultHostPort,
this.ipv6,
}) : assert(logReader != null),
assert(portForwarder == null || defaultHostPort != null),
_prefix = '$serviceName listening on ' {
......@@ -28,6 +30,7 @@ class ProtocolDiscovery {
DeviceLogReader logReader, {
DevicePortForwarder portForwarder,
int hostPort,
bool ipv6: false,
}) {
const String kObservatoryService = 'Observatory';
return new ProtocolDiscovery._(
......@@ -35,6 +38,7 @@ class ProtocolDiscovery {
portForwarder: portForwarder,
hostPort: hostPort,
defaultHostPort: kDefaultObservatoryPort,
ipv6: ipv6,
);
}
......@@ -43,6 +47,7 @@ class ProtocolDiscovery {
final DevicePortForwarder portForwarder;
final int hostPort;
final int defaultHostPort;
final bool ipv6;
final String _prefix;
final Completer<Uri> _completer = new Completer<Uri>();
......@@ -90,6 +95,11 @@ class ProtocolDiscovery {
hostUri = deviceUri.replace(port: hostPort);
}
assert(new InternetAddress(hostUri.host).isLoopback);
if (ipv6) {
hostUri = hostUri.replace(host: InternetAddress.LOOPBACK_IP_V6.host);
}
return hostUri;
}
}
......@@ -273,6 +273,7 @@ class FlutterDevice {
prebuiltApplication: prebuiltMode,
applicationNeedsRebuild: shouldBuild || hasDirtyDependencies,
usesTerminalUi: hotRunner.usesTerminalUI,
ipv6: hotRunner.ipv6,
);
final LaunchResult result = await futureResult;
......@@ -316,9 +317,9 @@ class FlutterDevice {
return 1;
}
Map<String, dynamic> platformArgs;
final Map<String, dynamic> platformArgs = <String, dynamic>{};
if (coldRunner.traceStartup != null)
platformArgs = <String, dynamic>{ 'trace-startup': coldRunner.traceStartup };
platformArgs['trace-startup'] = coldRunner.traceStartup;
startEchoingDeviceLog();
......@@ -332,6 +333,7 @@ class FlutterDevice {
prebuiltApplication: prebuiltMode,
applicationNeedsRebuild: shouldBuild || hasDirtyDependencies,
usesTerminalUi: coldRunner.usesTerminalUI,
ipv6: coldRunner.ipv6,
);
if (!result.started) {
......@@ -394,6 +396,7 @@ abstract class ResidentRunner {
String packagesFilePath,
String projectAssets,
this.stayResident,
this.ipv6,
}) {
_mainPath = findMainDartFile(target);
_projectRootPath = projectRootPath ?? fs.currentDirectory.path;
......@@ -410,6 +413,7 @@ abstract class ResidentRunner {
final DebuggingOptions debuggingOptions;
final bool usesTerminalUI;
final bool stayResident;
final bool ipv6;
final Completer<int> _finished = new Completer<int>();
bool _stopped = false;
String _packagesFilePath;
......
......@@ -22,11 +22,13 @@ class ColdRunner extends ResidentRunner {
this.applicationBinary,
this.previewDart2 : false,
bool stayResident: true,
bool ipv6: false,
}) : super(devices,
target: target,
debuggingOptions: debuggingOptions,
usesTerminalUI: usesTerminalUI,
stayResident: stayResident);
stayResident: stayResident,
ipv6: ipv6);
final bool traceStartup;
final String applicationBinary;
......
......@@ -45,6 +45,7 @@ class HotRunner extends ResidentRunner {
String packagesFilePath,
String projectAssets,
bool stayResident: true,
bool ipv6: false,
}) : super(devices,
target: target,
debuggingOptions: debuggingOptions,
......@@ -52,7 +53,8 @@ class HotRunner extends ResidentRunner {
projectRootPath: projectRootPath,
packagesFilePath: packagesFilePath,
projectAssets: projectAssets,
stayResident: stayResident);
stayResident: stayResident,
ipv6: ipv6);
final String applicationBinary;
final bool hostIsIde;
......
......@@ -104,47 +104,68 @@ void main() {
testUsingContext('discovers uri if log line contains non-localhost', () async {
initialize();
final Future<Uri> uriFuture = discoverer.uri;
logReader.addLine('I/flutter : Observatory listening on http://somehost:54804/PTwjm8Ii8qg=/');
logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:54804/PTwjm8Ii8qg=/');
final Uri uri = await uriFuture;
expect(uri.port, 54804);
expect('$uri', 'http://somehost:54804/PTwjm8Ii8qg=/');
expect('$uri', 'http://127.0.0.1:54804/PTwjm8Ii8qg=/');
});
});
testUsingContext('port forwarding - default port', () async {
final MockDeviceLogReader logReader = new MockDeviceLogReader();
final ProtocolDiscovery discoverer = new ProtocolDiscovery.observatory(
logReader,
portForwarder: new MockPortForwarder(99),
hostPort: 54777);
// Get next port future.
final Future<Uri> nextUri = discoverer.uri;
logReader.addLine('I/flutter : Observatory listening on http://somehost:54804/PTwjm8Ii8qg=/');
final Uri uri = await nextUri;
expect(uri.port, 54777);
expect('$uri', 'http://somehost:54777/PTwjm8Ii8qg=/');
discoverer.cancel();
logReader.dispose();
});
group('port forwarding', () {
testUsingContext('default port', () async {
final MockDeviceLogReader logReader = new MockDeviceLogReader();
final ProtocolDiscovery discoverer = new ProtocolDiscovery.observatory(
logReader,
portForwarder: new MockPortForwarder(99),
hostPort: 54777);
// Get next port future.
final Future<Uri> nextUri = discoverer.uri;
logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:54804/PTwjm8Ii8qg=/');
final Uri uri = await nextUri;
expect(uri.port, 54777);
expect('$uri', 'http://127.0.0.1:54777/PTwjm8Ii8qg=/');
discoverer.cancel();
logReader.dispose();
});
testUsingContext('specified port', () async {
final MockDeviceLogReader logReader = new MockDeviceLogReader();
final ProtocolDiscovery discoverer = new ProtocolDiscovery.observatory(
logReader,
portForwarder: new MockPortForwarder(99),
hostPort: 1243);
// Get next port future.
final Future<Uri> nextUri = discoverer.uri;
logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:54804/PTwjm8Ii8qg=/');
final Uri uri = await nextUri;
expect(uri.port, 1243);
expect('$uri', 'http://127.0.0.1:1243/PTwjm8Ii8qg=/');
discoverer.cancel();
logReader.dispose();
});
testUsingContext('ipv6', () async {
final MockDeviceLogReader logReader = new MockDeviceLogReader();
final ProtocolDiscovery discoverer = new ProtocolDiscovery.observatory(
logReader,
portForwarder: new MockPortForwarder(99),
hostPort: 54777,
ipv6: true);
testUsingContext('port forwarding - specified port', () async {
final MockDeviceLogReader logReader = new MockDeviceLogReader();
final ProtocolDiscovery discoverer = new ProtocolDiscovery.observatory(
logReader,
portForwarder: new MockPortForwarder(99),
hostPort: 1243);
// Get next port future.
final Future<Uri> nextUri = discoverer.uri;
logReader.addLine('I/flutter : Observatory listening on http://somehost:54804/PTwjm8Ii8qg=/');
final Uri uri = await nextUri;
expect(uri.port, 1243);
expect('$uri', 'http://somehost:1243/PTwjm8Ii8qg=/');
discoverer.cancel();
logReader.dispose();
// Get next port future.
final Future<Uri> nextUri = discoverer.uri;
logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:54804/PTwjm8Ii8qg=/');
final Uri uri = await nextUri;
expect(uri.port, 54777);
expect('$uri', 'http://[::1]:54777/PTwjm8Ii8qg=/');
discoverer.cancel();
logReader.dispose();
});
});
});
}
......
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