Commit 607b2ea5 authored by Kartik Hegde's avatar Kartik Hegde Committed by Commit Bot

network_diagnostics: Add HttpsLatencyRoutine

Tests whether the HTTPS latency is within established tolerance levels
for the system.

BUG=chromium:956783
TEST=unit_tests --gtest_filter=HttpsLatencyRoutineTest*

Change-Id: I7a28b103321f9c62dc21c660a60cc4c7487534a3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2378664
Commit-Queue: Kartik Hegde <khegde@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarSteven Bennetts <stevenjb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#814409}
parent 2bf83434
......@@ -1907,6 +1907,8 @@ source_set("chromeos") {
"net/network_diagnostics/http_firewall_routine.h",
"net/network_diagnostics/http_request_manager.cc",
"net/network_diagnostics/http_request_manager.h",
"net/network_diagnostics/https_latency_routine.cc",
"net/network_diagnostics/https_latency_routine.h",
"net/network_diagnostics/lan_connectivity_routine.cc",
"net/network_diagnostics/lan_connectivity_routine.h",
"net/network_diagnostics/network_diagnostics.cc",
......@@ -3445,6 +3447,7 @@ source_set("unit_tests") {
"net/network_diagnostics/has_secure_wifi_connection_routine_unittest.cc",
"net/network_diagnostics/http_firewall_routine_unittest.cc",
"net/network_diagnostics/http_request_manager_unittest.cc",
"net/network_diagnostics/https_latency_routine_unittest.cc",
"net/network_diagnostics/lan_connectivity_routine_unittest.cc",
"net/network_diagnostics/network_diagnostics_routine_unittest.cc",
"net/network_diagnostics/network_diagnostics_unittest.cc",
......
......@@ -146,6 +146,20 @@ Problems:
* `kFirewallDetected`: Firewall detected.
* `kPotentialFirewall`: A firewall may potentially exist.
### Google Services Routines
Tests successful communication with various Google domains.
#### HttpsLatency
Tests whether the HTTPS latency is below an acceptable threshold.
Problems:
* `kFailedDnsResolutions`: One or more DNS resolutions resulted in a failure.
* `kFailedHttpRequests`: One or more HTTPS requests resulted in a failure.
* `kSlightlyAboveThreshold`: Average HTTPS request latency is slightly above the expected threshold.
* `kSignificantlyAboveThreshold`: Average HTTPS request latency is significantly above the expected threshold.
[Network Health and Configuration]: https://docs.google.com/document/d/10DSy-jZXaRo9I9aq1UqERy76t7HkgGvInWk57pHEkzg
[network_diagnostics.mojom]: https://source.chromium.org/chromium/chromium/src/+/master:chromeos/services/network_health/public/mojom/network_diagnostics.mojom?originalUrl=https:%2F%2Fcs.chromium.org%2F
[NetworkHealthService]: https://source.chromium.org/chromium/chromium/src/+/master:chrome/browser/chromeos/net/network_health/network_health_service.h?originalUrl=https:%2F%2Fcs.chromium.org%2F
......
// Copyright 2020 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.
#include "chrome/browser/chromeos/net/network_diagnostics/https_latency_routine.h"
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/time/default_tick_clock.h"
#include "chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_util.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "content/public/browser/storage_partition.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/host_port_pair.h"
#include "net/base/net_errors.h"
#include "services/network/public/mojom/network_context.mojom.h"
namespace chromeos {
namespace network_diagnostics {
namespace {
constexpr int kTotalHostsToQuery = 3;
// The length of a random eight letter prefix.
constexpr int kHostPrefixLength = 8;
constexpr int kHttpPort = 80;
constexpr char kHttpsScheme[] = "https://";
constexpr base::TimeDelta ksRequestTimeoutMs =
base::TimeDelta::FromMilliseconds(5 * 1000);
// Requests taking longer than 1000 ms are problematic.
constexpr base::TimeDelta kProblemLatencyMs =
base::TimeDelta::FromMilliseconds(1000);
// Requests lasting between 500 ms and 1000 ms are potentially problematic.
constexpr base::TimeDelta kPotentialProblemLatencyMs =
base::TimeDelta::FromMilliseconds(500);
base::TimeDelta MedianLatency(std::vector<base::TimeDelta>& latencies) {
if (latencies.size() == 0) {
return base::TimeDelta::Max();
}
std::sort(latencies.begin(), latencies.end());
if (latencies.size() % 2 != 0) {
return latencies[latencies.size() / 2];
}
auto sum =
latencies[latencies.size() / 2] + latencies[(latencies.size() + 1) / 2];
return sum / 2.0;
}
} // namespace
class HttpsLatencyRoutine::HostResolver
: public network::ResolveHostClientBase {
public:
explicit HostResolver(HttpsLatencyRoutine* https_latency_routine);
HostResolver(const HostResolver&) = delete;
HostResolver& operator=(const HostResolver&) = delete;
~HostResolver() override;
// network::mojom::ResolveHostClient:
void OnComplete(
int result,
const net::ResolveErrorInfo& resolve_error_info,
const base::Optional<net::AddressList>& resolved_addresses) override;
// Performs the DNS resolution.
void Run(const std::string& hostname);
network::mojom::NetworkContext* network_context() const {
return network_context_;
}
Profile* profile() const { return profile_; }
void set_network_context_for_testing(
network::mojom::NetworkContext* network_context) {
network_context_ = network_context;
}
void set_profile_for_testing(Profile* profile) { profile_ = profile; }
private:
void CreateHostResolver();
void OnMojoConnectionError();
Profile* profile_ = nullptr; // Unowned
network::mojom::NetworkContext* network_context_ = nullptr; // Unowned
HttpsLatencyRoutine* https_latency_routine_ = nullptr; // Unowned
mojo::Receiver<network::mojom::ResolveHostClient> receiver_{this};
mojo::Remote<network::mojom::HostResolver> host_resolver_;
};
HttpsLatencyRoutine::HostResolver::HostResolver(
HttpsLatencyRoutine* https_latency_routine)
: profile_(util::GetUserProfile()),
network_context_(
content::BrowserContext::GetDefaultStoragePartition(profile_)
->GetNetworkContext()),
https_latency_routine_(https_latency_routine) {
DCHECK(https_latency_routine_);
DCHECK(network_context_);
}
HttpsLatencyRoutine::HostResolver::~HostResolver() = default;
void HttpsLatencyRoutine::HostResolver::OnComplete(
int result,
const net::ResolveErrorInfo& resolve_error_info,
const base::Optional<net::AddressList>& resolved_addresses) {
receiver_.reset();
https_latency_routine_->OnHostResolutionComplete(result, resolve_error_info,
resolved_addresses);
}
void HttpsLatencyRoutine::HostResolver::Run(const std::string& hostname) {
if (!host_resolver_) {
CreateHostResolver();
}
DCHECK(host_resolver_);
DCHECK(!receiver_.is_bound());
network::mojom::ResolveHostParametersPtr parameters =
network::mojom::ResolveHostParameters::New();
parameters->dns_query_type = net::DnsQueryType::A;
parameters->source = net::HostResolverSource::DNS;
parameters->cache_usage =
network::mojom::ResolveHostParameters::CacheUsage::DISALLOWED;
host_resolver_->ResolveHost(net::HostPortPair(hostname, kHttpPort),
net::NetworkIsolationKey::CreateTransient(),
std::move(parameters),
receiver_.BindNewPipeAndPassRemote());
}
void HttpsLatencyRoutine::HostResolver::CreateHostResolver() {
host_resolver_.reset();
network_context()->CreateHostResolver(
net::DnsConfigOverrides(), host_resolver_.BindNewPipeAndPassReceiver());
// Disconnect handler will be invoked if the network service crashes.
host_resolver_.set_disconnect_handler(base::BindOnce(
&HostResolver::OnMojoConnectionError, base::Unretained(this)));
}
void HttpsLatencyRoutine::HostResolver::OnMojoConnectionError() {
CreateHostResolver();
OnComplete(net::ERR_NAME_NOT_RESOLVED, net::ResolveErrorInfo(net::ERR_FAILED),
base::nullopt);
}
HttpsLatencyRoutine::HttpsLatencyRoutine()
: tick_clock_(base::DefaultTickClock::GetInstance()),
hostnames_to_query_dns_(
util::GetRandomHostsWithSchemeAndGenerate204Path(kTotalHostsToQuery,
kHostPrefixLength,
kHttpsScheme)),
hostnames_to_query_https_(hostnames_to_query_dns_),
host_resolver_(std::make_unique<HostResolver>(this)),
http_request_manager_(
std::make_unique<HttpRequestManager>(host_resolver_->profile())) {
DCHECK(http_request_manager_);
}
HttpsLatencyRoutine::~HttpsLatencyRoutine() = default;
void HttpsLatencyRoutine::RunRoutine(HttpsLatencyRoutineCallback callback) {
if (!CanRun()) {
std::move(callback).Run(verdict(), problems_);
return;
}
routine_completed_callback_ = std::move(callback);
// Before making HTTPS requests to the hosts, add the IP addresses are added
// to the DNS cache. This ensures the HTTPS latency does not include DNS
// resolution time, allowing us to identify issues with HTTPS more precisely.
AttemptNextResolution();
}
void HttpsLatencyRoutine::AnalyzeResultsAndExecuteCallback() {
base::TimeDelta median_latency = MedianLatency(latencies_);
if (!successfully_resolved_hosts_) {
set_verdict(mojom::RoutineVerdict::kProblem);
problems_.emplace_back(mojom::HttpsLatencyProblem::kFailedDnsResolutions);
} else if (failed_connection_) {
set_verdict(mojom::RoutineVerdict::kProblem);
problems_.emplace_back(mojom::HttpsLatencyProblem::kFailedHttpsRequests);
} else if (median_latency <= kProblemLatencyMs &&
median_latency > kPotentialProblemLatencyMs) {
set_verdict(mojom::RoutineVerdict::kProblem);
problems_.emplace_back(mojom::HttpsLatencyProblem::kHighLatency);
} else if (median_latency > kProblemLatencyMs) {
set_verdict(mojom::RoutineVerdict::kProblem);
problems_.emplace_back(mojom::HttpsLatencyProblem::kVeryHighLatency);
} else {
set_verdict(mojom::RoutineVerdict::kNoProblem);
}
std::move(routine_completed_callback_).Run(verdict(), problems_);
}
void HttpsLatencyRoutine::AttemptNextResolution() {
std::string hostname = hostnames_to_query_dns_.back();
hostnames_to_query_dns_.pop_back();
host_resolver_->Run(hostname);
}
void HttpsLatencyRoutine::OnHostResolutionComplete(
int result,
const net::ResolveErrorInfo& resolve_error_info,
const base::Optional<net::AddressList>& resolved_addresses) {
bool success = result == net::OK && !resolved_addresses->empty() &&
resolved_addresses.has_value();
if (!success) {
successfully_resolved_hosts_ = false;
AnalyzeResultsAndExecuteCallback();
return;
}
if (hostnames_to_query_dns_.size() > 0) {
AttemptNextResolution();
return;
}
MakeHttpsRequest();
}
void HttpsLatencyRoutine::SetNetworkContextForTesting(
network::mojom::NetworkContext* network_context) {
host_resolver_->set_network_context_for_testing(network_context);
}
void HttpsLatencyRoutine::SetProfileForTesting(Profile* profile) {
host_resolver_->set_profile_for_testing(profile);
}
void HttpsLatencyRoutine::MakeHttpsRequest() {
std::string hostname = hostnames_to_query_https_.back();
hostnames_to_query_https_.pop_back();
request_start_time_ = tick_clock_->NowTicks();
http_request_manager_->MakeRequest(
GURL(hostname), ksRequestTimeoutMs,
base::BindOnce(&HttpsLatencyRoutine::OnHttpsRequestComplete, weak_ptr()));
}
void HttpsLatencyRoutine::OnHttpsRequestComplete(bool connected) {
request_end_time_ = tick_clock_->NowTicks();
if (!connected) {
failed_connection_ = true;
AnalyzeResultsAndExecuteCallback();
return;
}
const base::TimeDelta latency = request_end_time_ - request_start_time_;
latencies_.emplace_back(latency);
if (hostnames_to_query_https_.size() > 0) {
MakeHttpsRequest();
return;
}
AnalyzeResultsAndExecuteCallback();
}
} // namespace network_diagnostics
} // namespace chromeos
// Copyright 2020 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.
#ifndef CHROME_BROWSER_CHROMEOS_NET_NETWORK_DIAGNOSTICS_HTTPS_LATENCY_ROUTINE_H_
#define CHROME_BROWSER_CHROMEOS_NET_NETWORK_DIAGNOSTICS_HTTPS_LATENCY_ROUTINE_H_
#include <memory>
#include <string>
#include <vector>
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/net/network_diagnostics/http_request_manager.h"
#include "chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_routine.h"
#include "net/base/address_list.h"
#include "net/dns/public/resolve_error_info.h"
#include "services/network/public/cpp/resolve_host_client_base.h"
#include "services/network/public/mojom/host_resolver.mojom.h"
class HttpRequestManager;
class Profile;
namespace base {
class TickClock;
} // namespace base
namespace network {
namespace mojom {
class NetworkContext;
}
} // namespace network
namespace chromeos {
namespace network_diagnostics {
// Tests whether the HTTPS latency is within established tolerance levels for
// the system.
class HttpsLatencyRoutine : public NetworkDiagnosticsRoutine {
public:
class HostResolver;
using HttpsLatencyRoutineCallback =
mojom::NetworkDiagnosticsRoutines::HttpsLatencyCallback;
HttpsLatencyRoutine();
HttpsLatencyRoutine(const HttpsLatencyRoutine&) = delete;
HttpsLatencyRoutine& operator=(const HttpsLatencyRoutine&) = delete;
~HttpsLatencyRoutine() override;
// NetworkDiagnosticsRoutine:
void AnalyzeResultsAndExecuteCallback() override;
// Run the core logic of this routine. Set |callback| to
// |routine_completed_callback_|, which is to be executed in
// AnalyzeResultsAndExecuteCallback().
void RunRoutine(HttpsLatencyRoutineCallback callback);
// Processes the results of the DNS resolution done by |host_resolver_|.
void OnHostResolutionComplete(
int result,
const net::ResolveErrorInfo& resolve_error_info,
const base::Optional<net::AddressList>& resolved_addresses);
void SetNetworkContextForTesting(
network::mojom::NetworkContext* network_context);
void SetProfileForTesting(Profile* profile);
// HttpRequestManager setter for tests.
void set_http_request_manager_for_testing(
std::unique_ptr<HttpRequestManager> http_request_manager) {
http_request_manager_ = std::move(http_request_manager);
}
// Mimics actual time conditions.
void set_tick_clock_for_testing(const base::TickClock* tick_clock) {
tick_clock_ = tick_clock;
}
private:
// Attempts the next DNS resolution.
void AttemptNextResolution();
// Makes a https request to the host.
void MakeHttpsRequest();
// Processes the results of an https request.
void OnHttpsRequestComplete(bool connected);
// Returns the weak pointer to |this|.
base::WeakPtr<HttpsLatencyRoutine> weak_ptr() {
return weak_factory_.GetWeakPtr();
}
bool successfully_resolved_hosts_ = true;
bool failed_connection_ = false;
const base::TickClock* tick_clock_ = nullptr; // Unowned
base::TimeTicks request_start_time_;
base::TimeTicks request_end_time_;
std::vector<std::string> hostnames_to_query_dns_;
std::vector<std::string> hostnames_to_query_https_;
std::vector<base::TimeDelta> latencies_;
std::unique_ptr<HostResolver> host_resolver_;
std::unique_ptr<HttpRequestManager> http_request_manager_;
std::vector<mojom::HttpsLatencyProblem> problems_;
HttpsLatencyRoutineCallback routine_completed_callback_;
base::WeakPtrFactory<HttpsLatencyRoutine> weak_factory_{this};
};
} // namespace network_diagnostics
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_NET_NETWORK_DIAGNOSTICS_HTTPS_LATENCY_ROUTINE_H_
// Copyright 2020 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.
#include "chrome/browser/chromeos/net/network_diagnostics/https_latency_routine.h"
#include <deque>
#include <memory>
#include <utility>
#include "base/run_loop.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/session_manager/core/session_manager.h"
#include "content/public/test/browser_task_environment.h"
#include "services/network/test/test_network_context.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace network_diagnostics {
namespace {
const base::TimeDelta kNoProblemDelayMs =
base::TimeDelta::FromMilliseconds(100);
const base::TimeDelta kHighLatencyDelayMs =
base::TimeDelta::FromMilliseconds(550);
const base::TimeDelta kVeryHighLatencyDelayMs =
base::TimeDelta::FromMilliseconds(1050);
// The number of hosts the the routine tests for. Based on GetHostnamesToQuery()
// in https_latency_routine.cc.
const int kTotalHosts = 3;
// Represents a fake port number of a fake ip address returned by the
// FakeHostResolver.
const int kFakePortNumber = 1234;
const char kFakeTestProfile[] = "test";
net::IPEndPoint FakeIPAddress() {
return net::IPEndPoint(net::IPAddress::IPv4Localhost(), kFakePortNumber);
}
class FakeHostResolver : public network::mojom::HostResolver {
public:
struct DnsResult {
DnsResult(int32_t result,
net::ResolveErrorInfo resolve_error_info,
base::Optional<net::AddressList> resolved_addresses)
: result(result),
resolve_error_info(resolve_error_info),
resolved_addresses(resolved_addresses) {}
int result;
net::ResolveErrorInfo resolve_error_info;
base::Optional<net::AddressList> resolved_addresses;
};
FakeHostResolver(mojo::PendingReceiver<network::mojom::HostResolver> receiver,
std::deque<DnsResult*> fake_dns_results)
: receiver_(this, std::move(receiver)),
fake_dns_results_(std::move(fake_dns_results)) {}
~FakeHostResolver() override {}
// network::mojom::HostResolver
void ResolveHost(const net::HostPortPair& host,
const net::NetworkIsolationKey& network_isolation_key,
network::mojom::ResolveHostParametersPtr optional_parameters,
mojo::PendingRemote<network::mojom::ResolveHostClient>
pending_response_client) override {
mojo::Remote<network::mojom::ResolveHostClient> response_client(
std::move(pending_response_client));
DnsResult* result = fake_dns_results_.front();
DCHECK(result);
fake_dns_results_.pop_front();
response_client->OnComplete(result->result, result->resolve_error_info,
result->resolved_addresses);
}
void MdnsListen(
const net::HostPortPair& host,
net::DnsQueryType query_type,
mojo::PendingRemote<network::mojom::MdnsListenClient> response_client,
MdnsListenCallback callback) override {
NOTREACHED();
}
private:
mojo::Receiver<network::mojom::HostResolver> receiver_;
// Use the list of fake dns results to fake different responses for multiple
// calls to the host_resolver's ResolveHost().
std::deque<DnsResult*> fake_dns_results_;
};
class FakeNetworkContext : public network::TestNetworkContext {
public:
FakeNetworkContext() = default;
explicit FakeNetworkContext(
std::deque<FakeHostResolver::DnsResult*> fake_dns_results)
: fake_dns_results_(std::move(fake_dns_results)) {}
~FakeNetworkContext() override {}
// network::TestNetworkContext:
void CreateHostResolver(
const base::Optional<net::DnsConfigOverrides>& config_overrides,
mojo::PendingReceiver<network::mojom::HostResolver> receiver) override {
ASSERT_FALSE(resolver_);
resolver_ = std::make_unique<FakeHostResolver>(
std::move(receiver), std::move(fake_dns_results_));
}
private:
std::unique_ptr<FakeHostResolver> resolver_;
std::deque<FakeHostResolver::DnsResult*> fake_dns_results_;
};
class FakeTickClock : public base::TickClock {
public:
// The |request_delay| fakes the duration of an HTTP request.
explicit FakeTickClock(
const base::TimeDelta& request_delay = base::TimeDelta())
: request_delay_(request_delay) {}
FakeTickClock(const FakeTickClock&) = delete;
FakeTickClock& operator=(const FakeTickClock&) = delete;
~FakeTickClock() override = default;
base::TimeTicks NowTicks() const override {
base::TimeTicks current = current_time_;
// Advance the current time by |request_delay_| so that each NowTicks()
// invocation will have this delay. This allows tests to mimic realistic
// time conditions.
current_time_ = current_time_ + request_delay_;
return current;
}
private:
mutable base::TimeTicks current_time_ = base::TimeTicks::Now();
const base::TimeDelta request_delay_;
};
// Fake implementation of HttpRequestManager used to facilitate testing.
class FakeHttpRequestManager final : public HttpRequestManager {
public:
FakeHttpRequestManager() : HttpRequestManager(nullptr) {}
FakeHttpRequestManager(const FakeHttpRequestManager&) = delete;
FakeHttpRequestManager& operator=(const FakeHttpRequestManager&) = delete;
~FakeHttpRequestManager() override = default;
// HttpRequestManager:
void MakeRequest(const GURL& url,
const base::TimeDelta& timeout,
HttpRequestCallback callback) override {
std::move(callback).Run(connected_);
}
void set_connected(bool connected) { connected_ = connected; }
private:
bool connected_ = false;
};
} // namespace
class HttpsLatencyRoutineTest : public ::testing::Test {
public:
HttpsLatencyRoutineTest()
: profile_manager_(TestingBrowserProcess::GetGlobal()) {
session_manager::SessionManager::Get()->SetSessionState(
session_manager::SessionState::LOGIN_PRIMARY);
}
HttpsLatencyRoutineTest(const HttpsLatencyRoutineTest&) = delete;
HttpsLatencyRoutineTest& operator=(const HttpsLatencyRoutineTest&) = delete;
~HttpsLatencyRoutineTest() override = default;
void CompareVerdict(
mojom::RoutineVerdict expected_verdict,
const std::vector<mojom::HttpsLatencyProblem>& expected_problems,
mojom::RoutineVerdict actual_verdict,
const std::vector<mojom::HttpsLatencyProblem>& actual_problems) {
EXPECT_EQ(expected_verdict, actual_verdict);
EXPECT_EQ(expected_problems, actual_problems);
run_loop_.Quit();
}
protected:
void RunRoutine(
mojom::RoutineVerdict expected_routine_verdict,
const std::vector<mojom::HttpsLatencyProblem>& expected_problems) {
https_latency_routine_->RunRoutine(
base::BindOnce(&HttpsLatencyRoutineTest::CompareVerdict, weak_ptr(),
expected_routine_verdict, expected_problems));
run_loop_.Run();
}
void SetUpRoutine(std::deque<FakeHostResolver::DnsResult*> fake_dns_results,
bool connected,
const base::TickClock* fake_tick_clock) {
ASSERT_TRUE(profile_manager_.SetUp());
// DNS-related fakes.
fake_network_context_ =
std::make_unique<FakeNetworkContext>(std::move(fake_dns_results));
test_profile_ = profile_manager_.CreateTestingProfile(kFakeTestProfile);
// HTTPS-related fakes.
std::unique_ptr<FakeHttpRequestManager> fake_http_request_manager =
std::make_unique<FakeHttpRequestManager>();
fake_http_request_manager->set_connected(connected);
https_latency_routine_ = std::make_unique<HttpsLatencyRoutine>();
// Set up routine with fakes.
https_latency_routine_->SetNetworkContextForTesting(
fake_network_context_.get());
https_latency_routine_->SetProfileForTesting(test_profile_);
https_latency_routine_->set_tick_clock_for_testing(fake_tick_clock);
https_latency_routine_->set_http_request_manager_for_testing(
std::move(fake_http_request_manager));
}
base::WeakPtr<HttpsLatencyRoutineTest> weak_ptr() {
return weak_factory_.GetWeakPtr();
}
private:
content::BrowserTaskEnvironment task_environment_;
base::RunLoop run_loop_;
session_manager::SessionManager session_manager_;
std::unique_ptr<FakeNetworkContext> fake_network_context_;
Profile* test_profile_; // Unowned
TestingProfileManager profile_manager_;
std::unique_ptr<HttpsLatencyRoutine> https_latency_routine_;
base::WeakPtrFactory<HttpsLatencyRoutineTest> weak_factory_{this};
};
TEST_F(HttpsLatencyRoutineTest, TestFailedDnsResolution) {
std::deque<FakeHostResolver::DnsResult*> fake_dns_results;
std::vector<std::unique_ptr<FakeHostResolver::DnsResult>> resolutions;
// kTotalHosts = 3
for (int i = 0; i < kTotalHosts; i++) {
std::unique_ptr<FakeHostResolver::DnsResult> resolution;
if (i == 2) {
resolution = std::make_unique<FakeHostResolver::DnsResult>(
net::ERR_NAME_NOT_RESOLVED,
net::ResolveErrorInfo(net::ERR_NAME_NOT_RESOLVED),
net::AddressList());
} else {
resolution = std::make_unique<FakeHostResolver::DnsResult>(
net::OK, net::ResolveErrorInfo(net::OK),
net::AddressList(FakeIPAddress()));
}
fake_dns_results.push_back(resolution.get());
resolutions.emplace_back(std::move(resolution));
}
std::unique_ptr<FakeTickClock> fake_tick_clock =
std::make_unique<FakeTickClock>(kNoProblemDelayMs);
SetUpRoutine(std::move(fake_dns_results), true, fake_tick_clock.get());
RunRoutine(mojom::RoutineVerdict::kProblem,
{mojom::HttpsLatencyProblem::kFailedDnsResolutions});
}
TEST_F(HttpsLatencyRoutineTest, TestLowLatency) {
std::deque<FakeHostResolver::DnsResult*> fake_dns_results;
std::vector<std::unique_ptr<FakeHostResolver::DnsResult>> resolutions;
// kTotalHosts = 3
for (int i = 0; i < kTotalHosts; i++) {
std::unique_ptr<FakeHostResolver::DnsResult> resolution;
resolution = std::make_unique<FakeHostResolver::DnsResult>(
net::OK, net::ResolveErrorInfo(net::OK),
net::AddressList(FakeIPAddress()));
fake_dns_results.push_back(resolution.get());
resolutions.emplace_back(std::move(resolution));
}
std::unique_ptr<FakeTickClock> fake_tick_clock =
std::make_unique<FakeTickClock>(kNoProblemDelayMs);
SetUpRoutine(std::move(fake_dns_results), true, fake_tick_clock.get());
RunRoutine(mojom::RoutineVerdict::kNoProblem, {});
}
TEST_F(HttpsLatencyRoutineTest, TestFailedHttpRequest) {
std::deque<FakeHostResolver::DnsResult*> fake_dns_results;
std::vector<std::unique_ptr<FakeHostResolver::DnsResult>> resolutions;
// kTotalHosts = 3
for (int i = 0; i < kTotalHosts; i++) {
std::unique_ptr<FakeHostResolver::DnsResult> resolution;
resolution = std::make_unique<FakeHostResolver::DnsResult>(
net::OK, net::ResolveErrorInfo(net::OK),
net::AddressList(FakeIPAddress()));
fake_dns_results.push_back(resolution.get());
resolutions.emplace_back(std::move(resolution));
}
std::unique_ptr<FakeTickClock> fake_tick_clock =
std::make_unique<FakeTickClock>(kNoProblemDelayMs);
SetUpRoutine(std::move(fake_dns_results), false, fake_tick_clock.get());
RunRoutine(mojom::RoutineVerdict::kProblem,
{mojom::HttpsLatencyProblem::kFailedHttpsRequests});
}
TEST_F(HttpsLatencyRoutineTest, TestHighLatency) {
std::deque<FakeHostResolver::DnsResult*> fake_dns_results;
std::vector<std::unique_ptr<FakeHostResolver::DnsResult>> resolutions;
// kTotalHosts = 3
for (int i = 0; i < kTotalHosts; i++) {
std::unique_ptr<FakeHostResolver::DnsResult> resolution;
resolution = std::make_unique<FakeHostResolver::DnsResult>(
net::OK, net::ResolveErrorInfo(net::OK),
net::AddressList(FakeIPAddress()));
fake_dns_results.push_back(resolution.get());
resolutions.emplace_back(std::move(resolution));
}
std::unique_ptr<FakeTickClock> fake_tick_clock =
std::make_unique<FakeTickClock>(kHighLatencyDelayMs);
SetUpRoutine(std::move(fake_dns_results), true, fake_tick_clock.get());
RunRoutine(mojom::RoutineVerdict::kProblem,
{mojom::HttpsLatencyProblem::kHighLatency});
}
TEST_F(HttpsLatencyRoutineTest, TestVeryHighLatency) {
std::deque<FakeHostResolver::DnsResult*> fake_dns_results;
std::vector<std::unique_ptr<FakeHostResolver::DnsResult>> resolutions;
// kTotalHosts = 3
for (int i = 0; i < kTotalHosts; i++) {
std::unique_ptr<FakeHostResolver::DnsResult> resolution;
resolution = std::make_unique<FakeHostResolver::DnsResult>(
net::OK, net::ResolveErrorInfo(net::OK),
net::AddressList(FakeIPAddress()));
fake_dns_results.push_back(resolution.get());
resolutions.emplace_back(std::move(resolution));
}
std::unique_ptr<FakeTickClock> fake_tick_clock =
std::make_unique<FakeTickClock>(kVeryHighLatencyDelayMs);
SetUpRoutine(std::move(fake_dns_results), true, fake_tick_clock.get());
RunRoutine(mojom::RoutineVerdict::kProblem,
{mojom::HttpsLatencyProblem::kVeryHighLatency});
}
} // namespace network_diagnostics
} // namespace chromeos
......@@ -15,6 +15,7 @@
#include "chrome/browser/chromeos/net/network_diagnostics/gateway_can_be_pinged_routine.h"
#include "chrome/browser/chromeos/net/network_diagnostics/has_secure_wifi_connection_routine.h"
#include "chrome/browser/chromeos/net/network_diagnostics/http_firewall_routine.h"
#include "chrome/browser/chromeos/net/network_diagnostics/https_latency_routine.h"
#include "chrome/browser/chromeos/net/network_diagnostics/lan_connectivity_routine.h"
#include "chrome/browser/chromeos/net/network_diagnostics/signal_strength_routine.h"
#include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
......@@ -168,5 +169,19 @@ void NetworkDiagnostics::HttpFirewall(HttpFirewallCallback callback) {
std::move(routine), std::move(callback)));
}
void NetworkDiagnostics::HttpsLatency(HttpsLatencyCallback callback) {
auto routine = std::make_unique<HttpsLatencyRoutine>();
// RunRoutine() takes a lambda callback that takes ownership of the routine.
// This ensures that the routine stays alive when it makes asynchronous mojo
// calls. The routine will be destroyed when the lambda exits.
routine->RunRoutine(base::BindOnce(
[](std::unique_ptr<HttpsLatencyRoutine> routine,
HttpsLatencyCallback callback, mojom::RoutineVerdict verdict,
const std::vector<mojom::HttpsLatencyProblem>& problems) {
std::move(callback).Run(verdict, problems);
},
std::move(routine), std::move(callback)));
}
} // namespace network_diagnostics
} // namespace chromeos
......@@ -37,6 +37,7 @@ class NetworkDiagnostics : public mojom::NetworkDiagnosticsRoutines {
void DnsLatency(DnsLatencyCallback callback) override;
void DnsResolution(DnsResolutionCallback callback) override;
void CaptivePortal(CaptivePortalCallback callback) override;
void HttpsLatency(HttpsLatencyCallback callback) override;
private:
// An unowned pointer to the DebugDaemonClient instance.
......
......@@ -209,6 +209,10 @@ class MockNetworkDiagnosticsRoutines : public NetworkDiagnosticsRoutines {
HttpFirewall,
(NetworkDiagnosticsRoutines::HttpFirewallCallback),
(override));
MOCK_METHOD(void,
HttpsLatency,
(NetworkDiagnosticsRoutines::HttpsLatencyCallback),
(override));
mojo::PendingRemote<NetworkDiagnosticsRoutines> pending_remote() {
if (receiver_.is_bound()) {
......
......@@ -99,6 +99,19 @@ enum HttpFirewallProblem {
kPotentialFirewall,
};
// Problems related to the HttpsLatency routine.
[Extensible]
enum HttpsLatencyProblem {
// One or more DNS resolutions resulted in a failure.
kFailedDnsResolutions,
// One or more HTTPS requests resulted in a failure.
kFailedHttpsRequests,
// HTTPS request latency is high.
kHighLatency,
// HTTPS request latency is very high.
kVeryHighLatency,
};
// This interface is to be used by any clients that need to run specific
// network-related diagnostics. Expected clients of this interface are
// NetworkHealth, cros_healthd, and a connectivity diagnostics Web UI (to name
......@@ -142,4 +155,9 @@ interface NetworkDiagnosticsRoutines {
// Tests whether a firewall is blocking HTTP port 80.
HttpFirewall() => (RoutineVerdict verdict,
array<HttpFirewallProblem> problems);
// Tests whether the HTTPS latency is within established tolerance levels for
// the system.
HttpsLatency() => (RoutineVerdict verdict,
array<HttpsLatencyProblem> problems);
};
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