diff --git a/packages/flutter_tools/lib/src/proxy_validator.dart b/packages/flutter_tools/lib/src/proxy_validator.dart index c2b7133a87..31a9631d3d 100644 --- a/packages/flutter_tools/lib/src/proxy_validator.dart +++ b/packages/flutter_tools/lib/src/proxy_validator.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'base/io.dart'; import 'base/platform.dart'; import 'doctor_validator.dart'; @@ -43,7 +44,7 @@ class ProxyValidator extends DoctorValidator { else ...[ ValidationMessage('NO_PROXY is $_noProxy'), - for (String host in const ['127.0.0.1', 'localhost']) + for (final String host in await _getLoopbackAddresses()) if (_noProxy.contains(host)) ValidationMessage('NO_PROXY contains $host') else @@ -59,4 +60,21 @@ class ProxyValidator extends DoctorValidator { messages, ); } + + Future> _getLoopbackAddresses() async { + final List loopBackAddresses = ['localhost']; + + final List networkInterfaces = + await listNetworkInterfaces(includeLinkLocal: true, includeLoopback: true); + + for (final NetworkInterface networkInterface in networkInterfaces) { + for (final InternetAddress internetAddress in networkInterface.addresses) { + if (internetAddress.isLoopback) { + loopBackAddresses.add(internetAddress.address); + } + } + } + + return loopBackAddresses; + } } diff --git a/packages/flutter_tools/test/commands.shard/hermetic/proxy_validator_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/proxy_validator_test.dart index c3da3a174f..be600cba5f 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/proxy_validator_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/proxy_validator_test.dart @@ -2,6 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io' as io; +import 'dart:typed_data'; + +import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/doctor_validator.dart'; import 'package:flutter_tools/src/proxy_validator.dart'; @@ -9,6 +13,30 @@ import 'package:flutter_tools/src/proxy_validator.dart'; import '../../src/common.dart'; void main() { + setUp(() { + setNetworkInterfaceLister( + ({ + bool includeLoopback = true, + bool includeLinkLocal = true, + InternetAddressType type = InternetAddressType.any, + }) async { + final List interfaces = [ + FakeNetworkInterface([ + const FakeInternetAddress('127.0.0.1') + ]), + FakeNetworkInterface([ + const FakeInternetAddress('::1') + ]) + ]; + + return Future>.value(interfaces); + }); + }); + + tearDown(() { + resetNetworkInterfaceLister(); + }); + testWithoutContext('ProxyValidator does not show if HTTP_PROXY is not set', () { final Platform platform = FakePlatform(environment: {}); @@ -37,16 +65,17 @@ void main() { final Platform platform = FakePlatform( environment: { 'HTTP_PROXY': 'fakeproxy.local', - 'NO_PROXY': 'localhost,127.0.0.1', + 'NO_PROXY': 'localhost,127.0.0.1,::1', }, ); final ValidationResult results = await ProxyValidator(platform: platform).validate(); expect(results.messages, const [ ValidationMessage('HTTP_PROXY is set'), - ValidationMessage('NO_PROXY is localhost,127.0.0.1'), - ValidationMessage('NO_PROXY contains 127.0.0.1'), + ValidationMessage('NO_PROXY is localhost,127.0.0.1,::1'), ValidationMessage('NO_PROXY contains localhost'), + ValidationMessage('NO_PROXY contains 127.0.0.1'), + ValidationMessage('NO_PROXY contains ::1'), ]); }); @@ -54,20 +83,89 @@ void main() { final Platform platform = FakePlatform( environment: { 'http_proxy': 'fakeproxy.local', - 'no_proxy': 'localhost,127.0.0.1', + 'no_proxy': 'localhost,127.0.0.1,::1', }, ); final ValidationResult results = await ProxyValidator(platform: platform).validate(); expect(results.messages, const [ ValidationMessage('HTTP_PROXY is set'), - ValidationMessage('NO_PROXY is localhost,127.0.0.1'), - ValidationMessage('NO_PROXY contains 127.0.0.1'), + ValidationMessage('NO_PROXY is localhost,127.0.0.1,::1'), ValidationMessage('NO_PROXY contains localhost'), + ValidationMessage('NO_PROXY contains 127.0.0.1'), + ValidationMessage('NO_PROXY contains ::1'), ]); }); testWithoutContext('ProxyValidator reports issues when NO_PROXY is missing localhost', () async { + final Platform platform = FakePlatform( + environment: { + 'HTTP_PROXY': 'fakeproxy.local', + 'NO_PROXY': '127.0.0.1,::1', + }, + ); + final ValidationResult results = await ProxyValidator(platform: platform).validate(); + + expect(results.messages, const [ + ValidationMessage('HTTP_PROXY is set'), + ValidationMessage('NO_PROXY is 127.0.0.1,::1'), + ValidationMessage.hint('NO_PROXY does not contain localhost'), + ValidationMessage('NO_PROXY contains 127.0.0.1'), + ValidationMessage('NO_PROXY contains ::1'), + ]); + }); + + testWithoutContext('ProxyValidator reports issues when NO_PROXY is missing 127.0.0.1', () async { + final Platform platform = FakePlatform(environment: { + 'HTTP_PROXY': 'fakeproxy.local', + 'NO_PROXY': 'localhost,::1', + }); + final ValidationResult results = await ProxyValidator(platform: platform).validate(); + + expect(results.messages, const [ + ValidationMessage('HTTP_PROXY is set'), + ValidationMessage('NO_PROXY is localhost,::1'), + ValidationMessage('NO_PROXY contains localhost'), + ValidationMessage.hint('NO_PROXY does not contain 127.0.0.1'), + ValidationMessage('NO_PROXY contains ::1'), + ]); + }); + + testWithoutContext('ProxyValidator reports issues when NO_PROXY is missing ::1', () async { + final Platform platform = FakePlatform(environment: { + 'HTTP_PROXY': 'fakeproxy.local', + 'NO_PROXY': 'localhost,127.0.0.1', + }); + final ValidationResult results = await ProxyValidator(platform: platform).validate(); + + expect(results.messages, const [ + ValidationMessage('HTTP_PROXY is set'), + ValidationMessage('NO_PROXY is localhost,127.0.0.1'), + ValidationMessage('NO_PROXY contains localhost'), + ValidationMessage('NO_PROXY contains 127.0.0.1'), + ValidationMessage.hint('NO_PROXY does not contain ::1'), + ]); + }); + + testWithoutContext('ProxyValidator reports issues when NO_PROXY is missing localhost, 127.0.0.1', () async { + final Platform platform = FakePlatform( + environment: { + 'HTTP_PROXY': 'fakeproxy.local', + 'NO_PROXY': '::1', + }, + ); + final ValidationResult results = await ProxyValidator(platform: platform).validate(); + + expect(results.messages, const [ + ValidationMessage('HTTP_PROXY is set'), + ValidationMessage('NO_PROXY is ::1'), + ValidationMessage.hint('NO_PROXY does not contain localhost'), + ValidationMessage.hint('NO_PROXY does not contain 127.0.0.1'), + ValidationMessage('NO_PROXY contains ::1'), + ]); + }); + + testWithoutContext('ProxyValidator reports issues when NO_PROXY is missing localhost, ::1', () async { final Platform platform = FakePlatform( environment: { 'HTTP_PROXY': 'fakeproxy.local', @@ -79,23 +177,81 @@ void main() { expect(results.messages, const [ ValidationMessage('HTTP_PROXY is set'), ValidationMessage('NO_PROXY is 127.0.0.1'), - ValidationMessage('NO_PROXY contains 127.0.0.1'), ValidationMessage.hint('NO_PROXY does not contain localhost'), + ValidationMessage('NO_PROXY contains 127.0.0.1'), + ValidationMessage.hint('NO_PROXY does not contain ::1'), ]); }); - testWithoutContext('ProxyValidator reports issues when NO_PROXY is missing 127.0.0.1', () async { - final Platform platform = FakePlatform(environment: { - 'HTTP_PROXY': 'fakeproxy.local', - 'NO_PROXY': 'localhost', - }); + testWithoutContext('ProxyValidator reports issues when NO_PROXY is missing 127.0.0.1, ::1', () async { + final Platform platform = FakePlatform( + environment: { + 'HTTP_PROXY': 'fakeproxy.local', + 'NO_PROXY': 'localhost', + }, + ); final ValidationResult results = await ProxyValidator(platform: platform).validate(); expect(results.messages, const [ ValidationMessage('HTTP_PROXY is set'), ValidationMessage('NO_PROXY is localhost'), - ValidationMessage.hint('NO_PROXY does not contain 127.0.0.1'), ValidationMessage('NO_PROXY contains localhost'), + ValidationMessage.hint('NO_PROXY does not contain 127.0.0.1'), + ValidationMessage.hint('NO_PROXY does not contain ::1'), ]); }); } + +class FakeNetworkInterface extends NetworkInterface { + FakeNetworkInterface(List addresses): + super(FakeNetworkInterfaceDelegate(addresses)); + + @override + String get name => 'FakeNetworkInterface$index'; +} + +class FakeNetworkInterfaceDelegate implements io.NetworkInterface { + FakeNetworkInterfaceDelegate(this._fakeAddresses); + + final List _fakeAddresses; + + @override + List get addresses => _fakeAddresses; + + @override + int get index => addresses.length; + + @override + String get name => 'FakeNetworkInterfaceDelegate$index'; +} + +class FakeInternetAddress implements io.InternetAddress { + const FakeInternetAddress(this._fakeAddress); + + final String _fakeAddress; + + @override + String get address => _fakeAddress; + + @override + String get host => throw UnimplementedError(); + + @override + bool get isLinkLocal => throw UnimplementedError(); + + @override + bool get isLoopback => true; + + @override + bool get isMulticast => throw UnimplementedError(); + + @override + Uint8List get rawAddress => throw UnimplementedError(); + + @override + Future reverse() => + throw UnimplementedError(); + + @override + io.InternetAddressType get type => throw UnimplementedError(); +}