Add support for binding to IPv6 localhost in `flutter run` (#13136)
This allows `flutter run` to work on hosts that are IPv6-only.
diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart
index 49872b3..44cd03f 100644
--- a/packages/flutter_tools/lib/src/android/android_device.dart
+++ b/packages/flutter_tools/lib/src/android/android_device.dart
@@ -345,6 +345,7 @@
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 @@
// 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;
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart
index f297f3f..dccace1 100644
--- a/packages/flutter_tools/lib/src/commands/run.dart
+++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -28,6 +28,12 @@
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 @@
}
bool get traceStartup => argResults['trace-startup'];
+ bool get ipv6 => argResults['ipv6'];
String get route => argResults['route'];
void usesPortOptions() {
@@ -304,6 +311,7 @@
packagesFilePath: argResults['packages'],
projectAssets: argResults['project-assets'],
stayResident: stayResident,
+ ipv6: ipv6,
);
} else {
runner = new ColdRunner(
@@ -314,6 +322,7 @@
applicationBinary: argResults['use-application-binary'],
previewDart2: argResults['preview-dart-2'],
stayResident: stayResident,
+ ipv6: ipv6,
);
}
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index e79c73f..aabe18a 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -227,6 +227,7 @@
bool prebuiltApplication: false,
bool applicationNeedsRebuild: false,
bool usesTerminalUi: true,
+ bool ipv6: false,
});
/// Does this device implement support for hot reloading / restarting?
diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
index 08af8c7..cf105f2 100644
--- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
+++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
@@ -68,6 +68,7 @@
bool previewDart2: false,
bool applicationNeedsRebuild: false,
bool usesTerminalUi: false,
+ bool ipv6: false,
}) => new Future<Null>.error('unimplemented');
@override
diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart
index e79da6d..861cc25 100644
--- a/packages/flutter_tools/lib/src/ios/devices.dart
+++ b/packages/flutter_tools/lib/src/ios/devices.dart
@@ -163,6 +163,7 @@
bool previewDart2: false,
bool applicationNeedsRebuild: false,
bool usesTerminalUi: true,
+ bool ipv6: false,
}) async {
if (!prebuiltApplication) {
// TODO(chinmaygarde): Use mainPath, route.
@@ -255,7 +256,11 @@
// 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;
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart
index c9a748c..7831b65 100644
--- a/packages/flutter_tools/lib/src/ios/simulators.dart
+++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -312,6 +312,7 @@
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 @@
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 {
diff --git a/packages/flutter_tools/lib/src/protocol_discovery.dart b/packages/flutter_tools/lib/src/protocol_discovery.dart
index 1bc35d3..4cd0509 100644
--- a/packages/flutter_tools/lib/src/protocol_discovery.dart
+++ b/packages/flutter_tools/lib/src/protocol_discovery.dart
@@ -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 @@
this.portForwarder,
this.hostPort,
this.defaultHostPort,
+ this.ipv6,
}) : assert(logReader != null),
assert(portForwarder == null || defaultHostPort != null),
_prefix = '$serviceName listening on ' {
@@ -28,6 +30,7 @@
DeviceLogReader logReader, {
DevicePortForwarder portForwarder,
int hostPort,
+ bool ipv6: false,
}) {
const String kObservatoryService = 'Observatory';
return new ProtocolDiscovery._(
@@ -35,6 +38,7 @@
portForwarder: portForwarder,
hostPort: hostPort,
defaultHostPort: kDefaultObservatoryPort,
+ ipv6: ipv6,
);
}
@@ -43,6 +47,7 @@
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 @@
hostUri = deviceUri.replace(port: hostPort);
}
+ assert(new InternetAddress(hostUri.host).isLoopback);
+ if (ipv6) {
+ hostUri = hostUri.replace(host: InternetAddress.LOOPBACK_IP_V6.host);
+ }
+
return hostUri;
}
}
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index 2e557d2..c284dce 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -273,6 +273,7 @@
prebuiltApplication: prebuiltMode,
applicationNeedsRebuild: shouldBuild || hasDirtyDependencies,
usesTerminalUi: hotRunner.usesTerminalUI,
+ ipv6: hotRunner.ipv6,
);
final LaunchResult result = await futureResult;
@@ -316,9 +317,9 @@
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 @@
prebuiltApplication: prebuiltMode,
applicationNeedsRebuild: shouldBuild || hasDirtyDependencies,
usesTerminalUi: coldRunner.usesTerminalUI,
+ ipv6: coldRunner.ipv6,
);
if (!result.started) {
@@ -394,6 +396,7 @@
String packagesFilePath,
String projectAssets,
this.stayResident,
+ this.ipv6,
}) {
_mainPath = findMainDartFile(target);
_projectRootPath = projectRootPath ?? fs.currentDirectory.path;
@@ -410,6 +413,7 @@
final DebuggingOptions debuggingOptions;
final bool usesTerminalUI;
final bool stayResident;
+ final bool ipv6;
final Completer<int> _finished = new Completer<int>();
bool _stopped = false;
String _packagesFilePath;
diff --git a/packages/flutter_tools/lib/src/run_cold.dart b/packages/flutter_tools/lib/src/run_cold.dart
index 713d61c..7a004f2 100644
--- a/packages/flutter_tools/lib/src/run_cold.dart
+++ b/packages/flutter_tools/lib/src/run_cold.dart
@@ -22,11 +22,13 @@
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;
diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart
index 9986ef6..b7a6f38 100644
--- a/packages/flutter_tools/lib/src/run_hot.dart
+++ b/packages/flutter_tools/lib/src/run_hot.dart
@@ -45,6 +45,7 @@
String packagesFilePath,
String projectAssets,
bool stayResident: true,
+ bool ipv6: false,
}) : super(devices,
target: target,
debuggingOptions: debuggingOptions,
@@ -52,7 +53,8 @@
projectRootPath: projectRootPath,
packagesFilePath: packagesFilePath,
projectAssets: projectAssets,
- stayResident: stayResident);
+ stayResident: stayResident,
+ ipv6: ipv6);
final String applicationBinary;
final bool hostIsIde;
diff --git a/packages/flutter_tools/test/protocol_discovery_test.dart b/packages/flutter_tools/test/protocol_discovery_test.dart
index d9237e0..02d047e 100644
--- a/packages/flutter_tools/test/protocol_discovery_test.dart
+++ b/packages/flutter_tools/test/protocol_discovery_test.dart
@@ -104,47 +104,68 @@
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);
+ 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://somehost:54804/PTwjm8Ii8qg=/');
- final Uri uri = await nextUri;
- expect(uri.port, 54777);
- expect('$uri', 'http://somehost:54777/PTwjm8Ii8qg=/');
+ // 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();
- });
+ discoverer.cancel();
+ logReader.dispose();
+ });
- testUsingContext('port forwarding - specified port', () async {
- final MockDeviceLogReader logReader = new MockDeviceLogReader();
- final ProtocolDiscovery discoverer = new ProtocolDiscovery.observatory(
- logReader,
- portForwarder: new MockPortForwarder(99),
- hostPort: 1243);
+ 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://somehost:54804/PTwjm8Ii8qg=/');
- final Uri uri = await nextUri;
- expect(uri.port, 1243);
- expect('$uri', 'http://somehost:1243/PTwjm8Ii8qg=/');
+ // 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();
+ 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);
+
+ // 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();
+ });
});
});
}