diff --git a/packages/flutter_tools/lib/src/doctor.dart b/packages/flutter_tools/lib/src/doctor.dart index e9a34a06686..090e9e66cea 100644 --- a/packages/flutter_tools/lib/src/doctor.dart +++ b/packages/flutter_tools/lib/src/doctor.dart @@ -25,6 +25,7 @@ import 'globals.dart'; import 'intellij/intellij.dart'; import 'ios/ios_workflow.dart'; import 'ios/plist_utils.dart'; +import 'proxy_validator.dart'; import 'tester/flutter_tester.dart'; import 'version.dart'; import 'vscode/vscode_validator.dart'; @@ -66,6 +67,9 @@ class _DefaultDoctorValidatorsProvider implements DoctorValidatorsProvider { else _validators.add(NoIdeValidator()); + if (ProxyValidator.shouldShow) + _validators.add(ProxyValidator()); + if (deviceManager.canListAnything) _validators.add(DeviceValidator()); } diff --git a/packages/flutter_tools/lib/src/proxy_validator.dart b/packages/flutter_tools/lib/src/proxy_validator.dart new file mode 100644 index 00000000000..211a9d5833c --- /dev/null +++ b/packages/flutter_tools/lib/src/proxy_validator.dart @@ -0,0 +1,55 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'base/platform.dart'; +import 'doctor.dart'; + +class ProxyValidator extends DoctorValidator { + ProxyValidator() : super('Proxy Configuration'); + + static bool get shouldShow => _getEnv('HTTP_PROXY').isNotEmpty; + + final String _httpProxy = _getEnv('HTTP_PROXY'); + final String _noProxy = _getEnv('NO_PROXY'); + + /// Gets a trimmed, non-null environment variable. If the variable is not set + /// an empty string will be returned. Checks for the lowercase version of the + /// environment variable first, then uppercase to match Dart's HTTP implementation. + static String _getEnv(String key) => + platform.environment[key.toLowerCase()]?.trim() ?? + platform.environment[key.toUpperCase()]?.trim() ?? + ''; + + @override + Future validate() async { + final List messages = []; + + if (_httpProxy.isNotEmpty) { + messages.add(ValidationMessage('HTTP_PROXY is set')); + + if (_noProxy.isEmpty) { + messages.add(ValidationMessage.hint('NO_PROXY is not set')); + } else { + messages.add(ValidationMessage('NO_PROXY is $_noProxy')); + for (String host in const ['127.0.0.1', 'localhost']) { + final ValidationMessage msg = _noProxy.contains(host) + ? ValidationMessage('NO_PROXY contains $host') + : ValidationMessage.hint('NO_PROXY does not contain $host'); + + messages.add(msg); + } + } + } + + final bool hasIssues = + messages.any((ValidationMessage msg) => msg.isHint || msg.isHint); + + return ValidationResult( + hasIssues ? ValidationType.partial : ValidationType.installed, + messages, + ); + } +} diff --git a/packages/flutter_tools/test/commands/doctor_test.dart b/packages/flutter_tools/test/commands/doctor_test.dart index c3321d151b8..95c9141ffca 100644 --- a/packages/flutter_tools/test/commands/doctor_test.dart +++ b/packages/flutter_tools/test/commands/doctor_test.dart @@ -8,6 +8,7 @@ import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/terminal.dart'; import 'package:flutter_tools/src/doctor.dart'; +import 'package:flutter_tools/src/proxy_validator.dart'; import 'package:flutter_tools/src/vscode/vscode.dart'; import 'package:flutter_tools/src/vscode/vscode_validator.dart'; @@ -91,6 +92,95 @@ void main() { }, overrides: noColorTerminalOverride); }); + group('proxy validator', () { + testUsingContext('does not show if HTTP_PROXY is not set', () { + expect(ProxyValidator.shouldShow, isFalse); + }, overrides: { + Platform: () => FakePlatform()..environment = {}, + }); + + testUsingContext('does not show if HTTP_PROXY is only whitespace', () { + expect(ProxyValidator.shouldShow, isFalse); + }, overrides: { + Platform: () => + FakePlatform()..environment = {'HTTP_PROXY': ' '}, + }); + + testUsingContext('shows when HTTP_PROXY is set', () { + expect(ProxyValidator.shouldShow, isTrue); + }, overrides: { + Platform: () => FakePlatform() + ..environment = {'HTTP_PROXY': 'fakeproxy.local'}, + }); + + testUsingContext('shows when http_proxy is set', () { + expect(ProxyValidator.shouldShow, isTrue); + }, overrides: { + Platform: () => FakePlatform() + ..environment = {'http_proxy': 'fakeproxy.local'}, + }); + + testUsingContext('reports success when NO_PROXY is configured correctly', + () async { + final ValidationResult results = await ProxyValidator().validate(); + final List issues = results.messages + .where((ValidationMessage msg) => msg.isError || msg.isHint) + .toList(); + expect(issues, hasLength(0)); + }, overrides: { + Platform: () => FakePlatform() + ..environment = { + 'HTTP_PROXY': 'fakeproxy.local', + 'NO_PROXY': 'localhost,127.0.0.1' + }, + }); + + testUsingContext('reports success when no_proxy is configured correctly', + () async { + final ValidationResult results = await ProxyValidator().validate(); + final List issues = results.messages + .where((ValidationMessage msg) => msg.isError || msg.isHint) + .toList(); + expect(issues, hasLength(0)); + }, overrides: { + Platform: () => FakePlatform() + ..environment = { + 'http_proxy': 'fakeproxy.local', + 'no_proxy': 'localhost,127.0.0.1' + }, + }); + + testUsingContext('reports issues when NO_PROXY is missing localhost', + () async { + final ValidationResult results = await ProxyValidator().validate(); + final List issues = results.messages + .where((ValidationMessage msg) => msg.isError || msg.isHint) + .toList(); + expect(issues, isNot(hasLength(0))); + }, overrides: { + Platform: () => FakePlatform() + ..environment = { + 'HTTP_PROXY': 'fakeproxy.local', + 'NO_PROXY': '127.0.0.1' + }, + }); + + testUsingContext('reports issues when NO_PROXY is missing 127.0.0.1', + () async { + final ValidationResult results = await ProxyValidator().validate(); + final List issues = results.messages + .where((ValidationMessage msg) => msg.isError || msg.isHint) + .toList(); + expect(issues, isNot(hasLength(0))); + }, overrides: { + Platform: () => FakePlatform() + ..environment = { + 'HTTP_PROXY': 'fakeproxy.local', + 'NO_PROXY': 'localhost' + }, + }); + }); + group('doctor with overriden validators', () { testUsingContext('validate non-verbose output format for run without issues', () async { expect(await doctor.diagnose(verbose: false), isTrue);