// 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:io'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/doctor_validator.dart'; import 'package:flutter_tools/src/http_host_validator.dart'; import '../../src/common.dart'; import '../../src/fake_http_client.dart'; import '../../src/fakes.dart'; // The environment variables used to override some URLs const kTestEnvPubHost = 'https://pub.flutter-io.cn'; const kTestEnvGCloudHost = 'https://storage.flutter-io.cn'; const kTestEnvironment = { 'PUB_HOSTED_URL': kTestEnvPubHost, 'FLUTTER_STORAGE_BASE_URL': kTestEnvGCloudHost, }; void main() { group('http host validator', () { const osTested = ['windows', 'macos', 'linux']; group('no env variables', () { testWithoutContext('all http hosts are available', () async { final mockClient = FakeHttpClient.any(); // Run the check for all operating systems one by one for (final os in osTested) { final httpHostValidator = HttpHostValidator( platform: FakePlatform(operatingSystem: os), featureFlags: TestFeatureFlags(), httpClient: mockClient, ); // Run the validation check and get the results final ValidationResult result = await httpHostValidator.validate(); // Check for a ValidationType.success result expect(result.type, equals(ValidationType.success)); } }); testWithoutContext('all http hosts are not available', () async { // Run the check for all operating systems one by one for (final os in osTested) { final Platform platform = FakePlatform(operatingSystem: os); final httpHostValidator = HttpHostValidator( platform: platform, featureFlags: TestFeatureFlags(), httpClient: FakeHttpClient.list([ FakeRequest( Uri.parse(kCloudHost), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2), ), FakeRequest( Uri.parse(kCocoaPods), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2), ), FakeRequest( Uri.parse(kGitHub), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2), ), FakeRequest( Uri.parse(kMaven), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2), ), FakeRequest( Uri.parse(kPubDev), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2), ), ]), ); // Run the validation check and get the results final ValidationResult result = await httpHostValidator.validate(); // Check for a ValidationType.notAvailable result expect(result.type, equals(ValidationType.notAvailable)); } }); testWithoutContext('one http host is not available', () async { // Run the check for all operating systems one by one for (final os in osTested) { final Platform platform = FakePlatform(operatingSystem: os); final httpHostValidator = HttpHostValidator( platform: platform, featureFlags: TestFeatureFlags(), httpClient: FakeHttpClient.list([ FakeRequest( Uri.parse(kCloudHost), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2), ), FakeRequest(Uri.parse(kCocoaPods), method: HttpMethod.head), FakeRequest(Uri.parse(kGitHub), method: HttpMethod.head), FakeRequest(Uri.parse(kMaven), method: HttpMethod.head), FakeRequest(Uri.parse(kPubDev), method: HttpMethod.head), ]), ); // Run the validation check and get the results final ValidationResult result = await httpHostValidator.validate(); // Check for a ValidationType.partial result expect(result.type, equals(ValidationType.partial)); } }); }); group('with env variables', () { testWithoutContext('all http hosts are available', () async { final mockClient = FakeHttpClient.any(); // Run the check for all operating systems one by one for (final os in osTested) { final httpHostValidator = HttpHostValidator( platform: FakePlatform(operatingSystem: os, environment: kTestEnvironment), featureFlags: TestFeatureFlags(), httpClient: mockClient, ); // Run the validation check and get the results final ValidationResult result = await httpHostValidator.validate(); // Check for a ValidationType.success result expect(result.type, equals(ValidationType.success)); } }); testWithoutContext('all http hosts are not available', () async { // Run the check for all operating systems one by one for (final os in osTested) { final Platform platform = FakePlatform( operatingSystem: os, environment: kTestEnvironment, ); final httpHostValidator = HttpHostValidator( platform: platform, featureFlags: TestFeatureFlags(), httpClient: FakeHttpClient.list([ FakeRequest( Uri.parse(kCocoaPods), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2), ), FakeRequest( Uri.parse(kGitHub), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2), ), FakeRequest( Uri.parse(kTestEnvGCloudHost), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2), ), FakeRequest( Uri.parse(kTestEnvPubHost), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2), ), ]), ); // Run the validation check and get the results final ValidationResult result = await httpHostValidator.validate(); // Check for a ValidationType.notAvailable result expect(result.type, equals(ValidationType.notAvailable)); } }); testWithoutContext('one http host is not available', () async { // Run the check for all operating systems one by one for (final os in osTested) { final Platform platform = FakePlatform( operatingSystem: os, environment: kTestEnvironment, ); final httpHostValidator = HttpHostValidator( platform: platform, featureFlags: TestFeatureFlags(), httpClient: FakeHttpClient.list([ FakeRequest(Uri.parse(kCocoaPods), method: HttpMethod.head), FakeRequest( Uri.parse(kGitHub), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2), ), FakeRequest( Uri.parse(kTestEnvGCloudHost), method: HttpMethod.head, responseError: const OSError('Name or service not known', -2), ), FakeRequest(Uri.parse(kTestEnvPubHost), method: HttpMethod.head), ]), ); // Run the validation check and get the results final ValidationResult result = await httpHostValidator.validate(); // Check for a ValidationType.partial result expect(result.type, equals(ValidationType.partial)); } }); testWithoutContext('does not throw on unparseable user-defined host uri', () async { final httpHostValidator = HttpHostValidator( platform: FakePlatform( environment: { 'PUB_HOSTED_URL': '::Not A Uri::', 'FLUTTER_STORAGE_BASE_URL': kTestEnvGCloudHost, }, ), featureFlags: TestFeatureFlags(isAndroidEnabled: false), httpClient: FakeHttpClient.any(), ); // Run the validation check and get the results final ValidationResult result = await httpHostValidator.validate(); expect(result.type, equals(ValidationType.partial)); expect( result.messages, contains( const ValidationMessage.error( 'Environment variable PUB_HOSTED_URL does not specify a valid URL: "::Not A Uri::"\n' 'Please see https://flutter.dev/to/use-mirror-site for an example of how to use it.', ), ), ); }); testWithoutContext('does not throw on invalid user-defined host', () async { final httpHostValidator = HttpHostValidator( platform: FakePlatform( environment: { 'PUB_HOSTED_URL': kTestEnvPubHost, 'FLUTTER_STORAGE_BASE_URL': '', }, ), featureFlags: TestFeatureFlags(isAndroidEnabled: false), httpClient: FakeHttpClient.any(), ); // Run the validation check and get the results final ValidationResult result = await httpHostValidator.validate(); expect(result.type, equals(ValidationType.partial)); expect( result.messages, contains( const ValidationMessage.error( 'Environment variable FLUTTER_STORAGE_BASE_URL does not specify a valid URL: ""\n' 'Please see https://flutter.dev/to/use-mirror-site for an example of how to use it.', ), ), ); }); }); group('specific os disabled', () { testWithoutContext('all http hosts are available - android disabled', () async { // Run the check for all operating systems one by one for (final os in osTested) { final httpHostValidator = HttpHostValidator( platform: FakePlatform(operatingSystem: os), featureFlags: TestFeatureFlags(isAndroidEnabled: false), httpClient: FakeHttpClient.list([ FakeRequest(Uri.parse(kCloudHost), method: HttpMethod.head), FakeRequest(Uri.parse(kCocoaPods), method: HttpMethod.head), FakeRequest(Uri.parse(kGitHub), method: HttpMethod.head), FakeRequest(Uri.parse(kPubDev), method: HttpMethod.head), ]), ); // Run the validation check and get the results final ValidationResult result = await httpHostValidator.validate(); // Check for a ValidationType.success result expect(result.type, equals(ValidationType.success)); } }); testWithoutContext('all http hosts are available - iOS disabled', () async { // Run the check for all operating systems one by one for (final os in osTested) { final Platform platform = FakePlatform(operatingSystem: os); final httpHostValidator = HttpHostValidator( platform: platform, featureFlags: TestFeatureFlags(isIOSEnabled: false), httpClient: FakeHttpClient.list([ FakeRequest(Uri.parse(kCloudHost), method: HttpMethod.head), FakeRequest(Uri.parse(kGitHub), method: HttpMethod.head), FakeRequest(Uri.parse(kMaven), method: HttpMethod.head), FakeRequest(Uri.parse(kPubDev), method: HttpMethod.head), ]), ); // Run the validation check and get the results final ValidationResult result = await httpHostValidator.validate(); // Check for a ValidationType.success result expect(result.type, equals(ValidationType.success)); } }); testWithoutContext('all http hosts are available - android, iOS disabled', () async { // Run the check for all operating systems one by one for (final os in osTested) { final httpHostValidator = HttpHostValidator( platform: FakePlatform(operatingSystem: os), featureFlags: TestFeatureFlags(isAndroidEnabled: false, isIOSEnabled: false), httpClient: FakeHttpClient.list([ FakeRequest(Uri.parse(kCloudHost), method: HttpMethod.head), FakeRequest(Uri.parse(kGitHub), method: HttpMethod.head), FakeRequest(Uri.parse(kPubDev), method: HttpMethod.head), ]), ); // Run the validation check and get the results final ValidationResult result = await httpHostValidator.validate(); // Check for a ValidationType.success result expect(result.type, equals(ValidationType.success)); } }); }); }); testWithoutContext('Does not throw on HandshakeException', () async { const handshakeMessage = ''' Handshake error in client (OS Error: BLOCK_TYPE_IS_NOT_01(../../third_party/boringssl/src/crypto/fipsmodule/rsa/padding.c:108) PADDING_CHECK_FAILED(../../third_party/boringssl/src/crypto/fipsmodule/rsa/rsa_impl.c:676) public key routines(../../third_party/boringssl/src/crypto/x509/a_verify.c:108) CERTIFICATE_VERIFY_FAILED: certificate signature failure(../../third_party/boringssl/src/ssl/handshake.cc:393)) '''; final httpHostValidator = HttpHostValidator( platform: FakePlatform(environment: kTestEnvironment), featureFlags: TestFeatureFlags(isAndroidEnabled: false), httpClient: FakeHttpClient.list([ FakeRequest( Uri.parse(kTestEnvPubHost), method: HttpMethod.head, responseError: const HandshakeException(handshakeMessage), ), FakeRequest(Uri.parse(kTestEnvGCloudHost), method: HttpMethod.head), FakeRequest(Uri.parse(kGitHub), method: HttpMethod.head), ]), ); // Run the validation check and get the results final ValidationResult result = await httpHostValidator.validate(); expect( result.messages.first, isA().having( (ValidationMessage msg) => msg.message, 'message', contains(handshakeMessage), ), ); }); }