Unverified Commit eb1a851f authored by crisboarna's avatar crisboarna Committed by GitHub

feat(flutter_tools): Added doctor host validation feat (#95386)

parent 1fb9b5b3
......@@ -10,6 +10,7 @@ import 'artifacts.dart';
import 'base/async_guard.dart';
import 'base/context.dart';
import 'base/file_system.dart';
import 'base/io.dart';
import 'base/logger.dart';
import 'base/os.dart';
import 'base/platform.dart';
......@@ -22,6 +23,7 @@ import 'doctor_validator.dart';
import 'features.dart';
import 'fuchsia/fuchsia_workflow.dart';
import 'globals.dart' as globals;
import 'http_host_validator.dart';
import 'intellij/intellij_validator.dart';
import 'linux/linux_doctor.dart';
import 'linux/linux_workflow.dart';
......@@ -132,6 +134,11 @@ class _DefaultDoctorValidatorsProvider implements DoctorValidatorsProvider {
deviceManager: globals.deviceManager,
userMessages: globals.userMessages,
),
HttpHostValidator(
platform: globals.platform,
featureFlags: featureFlags,
httpClient: globals.httpClientFactory?.call() ?? HttpClient(),
),
];
return _validators!;
}
......
// Copyright 2014 The Flutter 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/io.dart';
import 'base/platform.dart';
import 'doctor_validator.dart';
import 'features.dart';
// Overridable environment variables
const String kEnvPubHostedUrl = 'PUB_HOSTED_URL';
const String kEnvCloudUrl = 'FLUTTER_STORAGE_BASE_URL';
const String kDoctorHostTimeout = 'FLUTTER_DOCTOR_HOST_TIMEOUT';
/// Common Flutter HTTP hosts.
const String kPubDevHttpHost = 'https://pub.dev/';
const String kgCloudHttpHost = 'https://cloud.google.com/';
/// Android specific required HTTP hosts.
const List<String> androidRequiredHttpHosts = <String>[
'https://maven.google.com/',
];
/// MacOS specific required HTTP hosts.
const List<String> macOSRequiredHttpHosts = <String>[
'https://cocoapods.org/',
];
// Validator that checks all provided hosts are reachable and responsive
class HttpHostValidator extends DoctorValidator {
HttpHostValidator(
{required Platform platform,
required FeatureFlags featureFlags,
required HttpClient httpClient})
: _platform = platform,
_featureFlags = featureFlags,
_httpClient = httpClient,
super('HTTP Host Availability');
final Platform _platform;
final FeatureFlags _featureFlags;
final HttpClient _httpClient;
@override
String get slowWarning =>
'HTTP Host availability check is taking a long time...';
List<String> get _requiredHosts => <String>[
if (_featureFlags.isMacOSEnabled) ...macOSRequiredHttpHosts,
if (_featureFlags.isAndroidEnabled) ...androidRequiredHttpHosts,
_platform.environment[kEnvPubHostedUrl] ?? kPubDevHttpHost,
_platform.environment[kEnvCloudUrl] ?? kgCloudHttpHost,
];
/// Make a head request to the HTTP host. HTTP Host is available if no exception happened
Future<_HostValidationResult> _checkHostAvailability(String host) async {
try {
final int timeout =
int.parse(_platform.environment[kDoctorHostTimeout] ?? '10');
final HttpClientRequest req = await _httpClient.headUrl(Uri.parse(host));
await req.close().timeout(Duration(seconds: timeout));
// HTTP Host is available if no exception happened
return _HostValidationResult.success(host);
} on TimeoutException {
return _HostValidationResult.fail(
host, 'Failed to connect to $host in seconds');
} on SocketException catch (e) {
return _HostValidationResult.fail(
host, 'An error occurred while checking the HTTP host: ${e.message}');
} on HttpException catch (e) {
return _HostValidationResult.fail(host,
'An error occurred while checking the HTTP host: ${e.toString()}');
} on OSError catch (e) {
return _HostValidationResult.fail(
host, 'An error occurred while checking the HTTP host: ${e.message}');
}
}
@override
Future<ValidationResult> validate() async {
final List<ValidationMessage> messages = <ValidationMessage>[];
final Iterable<Future<_HostValidationResult>> availabilityResultFutures =
_requiredHosts.map(_checkHostAvailability);
final List<_HostValidationResult> availabilityResults =
(await Future.wait(availabilityResultFutures)).toList();
if (availabilityResults
.every((_HostValidationResult result) => result.available)) {
return ValidationResult(
ValidationType.installed,
messages
..add(const ValidationMessage(
'All required HTTP hosts are available')));
}
availabilityResults
.removeWhere((_HostValidationResult result) => result.available);
for (final _HostValidationResult result in availabilityResults) {
messages.add(ValidationMessage.error(
'HTTP host ${result.host} is not reachable. Reason: ${result.failResultInfo}'));
}
return ValidationResult(
availabilityResults.length == _requiredHosts.length
? ValidationType.notAvailable
: ValidationType.partial,
messages);
}
}
class _HostValidationResult {
_HostValidationResult.success(this.host)
: failResultInfo = '',
available = true;
_HostValidationResult.fail(this.host, this.failResultInfo)
: available = false;
final String failResultInfo;
final String host;
final bool available;
}
......@@ -12,6 +12,7 @@ import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart' as io;
import 'package:flutter_tools/src/base/net.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/user_messages.dart';
import 'package:flutter_tools/src/cache.dart';
......@@ -22,6 +23,7 @@ import 'package:flutter_tools/src/runner/flutter_command.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/fake_http_client.dart';
const String kCustomBugInstructions = 'These are instructions to report with a custom bug tracker.';
......@@ -96,6 +98,7 @@ void main() {
ProcessManager: () => FakeProcessManager.any(),
Usage: () => CrashingUsage(),
Artifacts: () => Artifacts.test(),
HttpClientFactory: () => () => FakeHttpClient.any()
});
// This Completer completes when CrashingFlutterCommand.runCommand
......@@ -138,6 +141,7 @@ void main() {
ProcessManager: () => FakeProcessManager.any(),
CrashReporter: () => WaitingCrashReporter(commandCompleter.future),
Artifacts: () => Artifacts.test(),
HttpClientFactory: () => () => FakeHttpClient.any()
});
testUsingContext('create local report', () async {
......@@ -206,7 +210,8 @@ void main() {
ProcessManager: () => FakeProcessManager.any(),
UserMessages: () => CustomBugInstructions(),
Artifacts: () => Artifacts.test(),
CrashReporter: () => WaitingCrashReporter(Future<void>.value())
CrashReporter: () => WaitingCrashReporter(Future<void>.value()),
HttpClientFactory: () => () => FakeHttpClient.any()
});
});
}
......
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