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