| Index: chrome/browser/net/dns_probe_service.cc
|
| diff --git a/chrome/browser/net/dns_probe_service.cc b/chrome/browser/net/dns_probe_service.cc
|
| index 156313f796c4e1f560b8b4235bc30737c66c71aa..fb6b60760c1c2126f85c57da09da181aec8db8e5 100644
|
| --- a/chrome/browser/net/dns_probe_service.cc
|
| +++ b/chrome/browser/net/dns_probe_service.cc
|
| @@ -7,8 +7,6 @@
|
| #include "base/metrics/field_trial.h"
|
| #include "base/metrics/histogram.h"
|
| #include "base/strings/string_number_conversions.h"
|
| -#include "chrome/browser/net/dns_probe_job.h"
|
| -#include "chrome/common/net/net_error_info.h"
|
| #include "net/base/ip_endpoint.h"
|
| #include "net/base/net_util.h"
|
| #include "net/dns/dns_client.h"
|
| @@ -17,7 +15,7 @@
|
|
|
| using base::FieldTrialList;
|
| using base::StringToInt;
|
| -using chrome_common_net::DnsProbeResult;
|
| +using chrome_common_net::DnsProbeStatus;
|
| using net::DnsClient;
|
| using net::DnsConfig;
|
| using net::IPAddressNumber;
|
| @@ -36,8 +34,8 @@ const int kMaxResultAgeMs = 5000;
|
|
|
| // The public DNS servers used by the DnsProbeService to verify internet
|
| // connectivity.
|
| -const char kPublicDnsPrimary[] = "8.8.8.8";
|
| -const char kPublicDnsSecondary[] = "8.8.4.4";
|
| +const char kGooglePublicDns1[] = "8.8.8.8";
|
| +const char kGooglePublicDns2[] = "8.8.4.4";
|
|
|
| IPEndPoint MakeDnsEndPoint(const std::string& dns_ip_literal) {
|
| IPAddressNumber dns_ip_number;
|
| @@ -46,299 +44,217 @@ IPEndPoint MakeDnsEndPoint(const std::string& dns_ip_literal) {
|
| return IPEndPoint(dns_ip_number, net::dns_protocol::kDefaultPort);
|
| }
|
|
|
| -const int kAttemptsUseDefault = -1;
|
| -
|
| -const char kAttemptsFieldTrialName[] = "DnsProbe-Attempts";
|
| +DnsProbeStatus EvaluateResults(DnsProbeRunner::Result system_result,
|
| + DnsProbeRunner::Result public_result) {
|
| + // If the system DNS is working, assume the domain doesn't exist.
|
| + if (system_result == DnsProbeRunner::CORRECT)
|
| + return chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN;
|
|
|
| -int GetAttemptsFromFieldTrial() {
|
| - std::string group = FieldTrialList::FindFullName(kAttemptsFieldTrialName);
|
| - if (group == "" || group == "default")
|
| - return kAttemptsUseDefault;
|
| + // If the system DNS is not working but another public server is, assume the
|
| + // DNS config is bad (or perhaps the DNS servers are down or broken).
|
| + if (public_result == DnsProbeRunner::CORRECT)
|
| + return chrome_common_net::DNS_PROBE_FINISHED_BAD_CONFIG;
|
|
|
| - int attempts;
|
| - if (!StringToInt(group, &attempts))
|
| - return kAttemptsUseDefault;
|
| + // If the system DNS is not working and another public server is unreachable,
|
| + // assume the internet connection is down (note that system DNS may be a
|
| + // router on the LAN, so it may be reachable but returning errors.)
|
| + if (public_result == DnsProbeRunner::UNREACHABLE)
|
| + return chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET;
|
|
|
| - return attempts;
|
| + // Otherwise: the system DNS is not working and another public server is
|
| + // responding but with errors or incorrect results. This is an awkward case;
|
| + // an invasive captive portal or a restrictive firewall may be intercepting
|
| + // or rewriting DNS traffic, or the public server may itself be failing or
|
| + // down.
|
| + return chrome_common_net::DNS_PROBE_FINISHED_UNKNOWN;
|
| }
|
|
|
| -bool IsLocalhost(const IPAddressNumber& ip) {
|
| - return (ip.size() == net::kIPv4AddressSize)
|
| - && (ip[0] == 127) && (ip[1] == 0) && (ip[2] == 0) && (ip[3] == 1);
|
| -}
|
| +void HistogramProbe(DnsProbeStatus status, base::TimeDelta elapsed) {
|
| + DCHECK(chrome_common_net::DnsProbeStatusIsFinished(status));
|
| +
|
| + int result = status - chrome_common_net::DNS_PROBE_FINISHED_UNKNOWN;
|
| +
|
| + const int kMaxResult = chrome_common_net::DNS_PROBE_MAX -
|
| + chrome_common_net::DNS_PROBE_FINISHED_UNKNOWN;
|
|
|
| -// The maximum number of nameservers counted in histograms.
|
| -const int kNameserverCountMax = 10;
|
| + UMA_HISTOGRAM_ENUMERATION("DnsProbe.Status", result, kMaxResult);
|
| + UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed", elapsed);
|
| +
|
| + if (NetworkChangeNotifier::IsOffline()) {
|
| + UMA_HISTOGRAM_ENUMERATION("DnsProbe.Status_NcnOffline",
|
| + result, kMaxResult);
|
| + UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed_NcnOffline", elapsed);
|
| + } else {
|
| + UMA_HISTOGRAM_ENUMERATION("DnsProbe.Status_NcnOnline",
|
| + result, kMaxResult);
|
| + UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed_NcnOnline", elapsed);
|
| + }
|
| +
|
| + switch (status) {
|
| + case chrome_common_net::DNS_PROBE_FINISHED_UNKNOWN:
|
| + UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed_Unknown",
|
| + elapsed);
|
| + break;
|
| + case chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET:
|
| + UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed_NoInternet",
|
| + elapsed);
|
| + break;
|
| + case chrome_common_net::DNS_PROBE_FINISHED_BAD_CONFIG:
|
| + UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed_BadConfig",
|
| + elapsed);
|
| + break;
|
| + case chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN:
|
| + UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed_Nxdomain",
|
| + elapsed);
|
| + break;
|
| +
|
| + // These aren't actually results.
|
| + case chrome_common_net::DNS_PROBE_POSSIBLE:
|
| + case chrome_common_net::DNS_PROBE_NOT_RUN:
|
| + case chrome_common_net::DNS_PROBE_STARTED:
|
| + case chrome_common_net::DNS_PROBE_MAX:
|
| + NOTREACHED();
|
| + break;
|
| + }
|
| +}
|
|
|
| } // namespace
|
|
|
| DnsProbeService::DnsProbeService()
|
| - : system_result_(DnsProbeJob::SERVERS_UNKNOWN),
|
| - public_result_(DnsProbeJob::SERVERS_UNKNOWN),
|
| - state_(STATE_NO_RESULTS),
|
| - result_(chrome_common_net::DNS_PROBE_UNKNOWN),
|
| - dns_attempts_(GetAttemptsFromFieldTrial()) {
|
| - NetworkChangeNotifier::AddIPAddressObserver(this);
|
| + : state_(STATE_NO_RESULT) {
|
| + NetworkChangeNotifier::AddDNSObserver(this);
|
| + SetSystemClientToCurrentConfig();
|
| + SetPublicClientToGooglePublicDns();
|
| }
|
|
|
| DnsProbeService::~DnsProbeService() {
|
| - NetworkChangeNotifier::RemoveIPAddressObserver(this);
|
| + NetworkChangeNotifier::RemoveDNSObserver(this);
|
| }
|
|
|
| -void DnsProbeService::ProbeDns(const DnsProbeService::CallbackType& callback) {
|
| - callbacks_.push_back(callback);
|
| +void DnsProbeService::ProbeDns(const DnsProbeService::ProbeCallback& callback) {
|
| + pending_callbacks_.push_back(callback);
|
|
|
| - if (state_ == STATE_RESULTS_CACHED && ResultsExpired())
|
| - ExpireResults();
|
| + if (CachedResultIsExpired())
|
| + ClearCachedResult();
|
|
|
| switch (state_) {
|
| - case STATE_NO_RESULTS:
|
| - StartProbes();
|
| - break;
|
| - case STATE_RESULTS_CACHED:
|
| - CallCallbacks();
|
| - break;
|
| - case STATE_PROBE_RUNNING:
|
| - // do nothing; probe is already running, and will call the callback
|
| - break;
|
| + case STATE_NO_RESULT:
|
| + StartProbes();
|
| + break;
|
| + case STATE_RESULT_CACHED:
|
| + CallCallbacks();
|
| + break;
|
| + case STATE_PROBE_RUNNING:
|
| + // Do nothing; probe is already running, and will call the callback.
|
| + break;
|
| }
|
| }
|
|
|
| -scoped_ptr<DnsProbeJob> DnsProbeService::CreateSystemProbeJob(
|
| - const DnsProbeJob::CallbackType& job_callback) {
|
| - DnsConfig system_config;
|
| - GetSystemDnsConfig(&system_config);
|
| - return CreateProbeJob(system_config, job_callback);
|
| +void DnsProbeService::OnDNSChanged() {
|
| + ClearCachedResult();
|
| + SetSystemClientToCurrentConfig();
|
| }
|
|
|
| -scoped_ptr<DnsProbeJob> DnsProbeService::CreatePublicProbeJob(
|
| - const DnsProbeJob::CallbackType& job_callback) {
|
| - DnsConfig public_config;
|
| - GetPublicDnsConfig(&public_config);
|
| - return CreateProbeJob(public_config, job_callback);
|
| +void DnsProbeService::SetSystemClientForTesting(
|
| + scoped_ptr<DnsClient> system_client) {
|
| + system_runner_.SetClient(system_client.Pass());
|
| }
|
|
|
| -void DnsProbeService::OnIPAddressChanged() {
|
| - if (state_ == STATE_RESULTS_CACHED)
|
| - ExpireResults();
|
| +void DnsProbeService::SetPublicClientForTesting(
|
| + scoped_ptr<DnsClient> public_client) {
|
| + public_runner_.SetClient(public_client.Pass());
|
| }
|
|
|
| -void DnsProbeService::ExpireResults() {
|
| - DCHECK_EQ(STATE_RESULTS_CACHED, state_);
|
| -
|
| - state_ = STATE_NO_RESULTS;
|
| - result_ = chrome_common_net::DNS_PROBE_UNKNOWN;
|
| +void DnsProbeService::ClearCachedResultForTesting() {
|
| + ClearCachedResult();
|
| }
|
|
|
| -void DnsProbeService::StartProbes() {
|
| - DCHECK_NE(STATE_PROBE_RUNNING, state_);
|
| - DCHECK(!system_job_.get());
|
| - DCHECK(!public_job_.get());
|
| -
|
| - DnsProbeJob::CallbackType job_callback =
|
| - base::Bind(&DnsProbeService::OnProbeJobComplete,
|
| - base::Unretained(this));
|
| -
|
| - // TODO(ttuttle): Do we want to keep explicit flags for "job done"?
|
| - // Or maybe DnsProbeJob should have a "finished" flag?
|
| - system_result_ = DnsProbeJob::SERVERS_UNKNOWN;
|
| - public_result_ = DnsProbeJob::SERVERS_UNKNOWN;
|
| -
|
| - system_job_ = CreateSystemProbeJob(job_callback);
|
| - public_job_ = CreatePublicProbeJob(job_callback);
|
| -
|
| - // If we can't create one or both jobs, fail the probe immediately.
|
| - if (!system_job_.get() || !public_job_.get()) {
|
| - system_job_.reset();
|
| - public_job_.reset();
|
| - state_ = STATE_RESULTS_CACHED;
|
| - // TODO(ttuttle): Should this be BAD_CONFIG? Currently I think it only
|
| - // happens when the system DnsConfig has no servers.
|
| - result_ = chrome_common_net::DNS_PROBE_UNKNOWN;
|
| - CallCallbacks();
|
| - return;
|
| - }
|
| +void DnsProbeService::SetSystemClientToCurrentConfig() {
|
| + DnsConfig system_config;
|
| + NetworkChangeNotifier::GetDnsConfig(&system_config);
|
| + system_config.search.clear();
|
| + system_config.attempts = 1;
|
| + system_config.randomize_ports = false;
|
|
|
| - state_ = STATE_PROBE_RUNNING;
|
| - probe_start_time_ = base::Time::Now();
|
| -}
|
| + scoped_ptr<DnsClient> system_client(DnsClient::CreateClient(NULL));
|
| + system_client->SetConfig(system_config);
|
|
|
| -void DnsProbeService::OnProbesComplete() {
|
| - DCHECK_EQ(STATE_PROBE_RUNNING, state_);
|
| + system_runner_.SetClient(system_client.Pass());
|
| +}
|
|
|
| - state_ = STATE_RESULTS_CACHED;
|
| - result_ = EvaluateResults();
|
| +void DnsProbeService::SetPublicClientToGooglePublicDns() {
|
| + DnsConfig public_config;
|
| + public_config.nameservers.push_back(MakeDnsEndPoint(kGooglePublicDns1));
|
| + public_config.nameservers.push_back(MakeDnsEndPoint(kGooglePublicDns2));
|
| + public_config.attempts = 1;
|
| + public_config.randomize_ports = false;
|
|
|
| - HistogramProbes();
|
| + scoped_ptr<DnsClient> public_client(DnsClient::CreateClient(NULL));
|
| + public_client->SetConfig(public_config);
|
|
|
| - CallCallbacks();
|
| + public_runner_.SetClient(public_client.Pass());
|
| }
|
|
|
| -void DnsProbeService::HistogramProbes() const {
|
| - const DnsProbeResult kMaxResult = chrome_common_net::DNS_PROBE_MAX;
|
| -
|
| - DCHECK_EQ(STATE_RESULTS_CACHED, state_);
|
| - DCHECK_NE(kMaxResult, result_);
|
| -
|
| - base::TimeDelta elapsed = base::Time::Now() - probe_start_time_;
|
| +void DnsProbeService::StartProbes() {
|
| + DCHECK_EQ(STATE_NO_RESULT, state_);
|
|
|
| - UMA_HISTOGRAM_ENUMERATION("DnsProbe.Probe.Result", result_, kMaxResult);
|
| - UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Probe.Elapsed", elapsed);
|
| + DCHECK(!system_runner_.IsRunning());
|
| + DCHECK(!public_runner_.IsRunning());
|
|
|
| - if (NetworkChangeNotifier::IsOffline()) {
|
| - UMA_HISTOGRAM_ENUMERATION("DnsProbe.Probe.NcnOffline.Result",
|
| - result_, kMaxResult);
|
| - UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Probe.NcnOffline.Elapsed", elapsed);
|
| - } else {
|
| - UMA_HISTOGRAM_ENUMERATION("DnsProbe.Probe.NcnOnline.Result",
|
| - result_, kMaxResult);
|
| - UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Probe.NcnOnline.Elapsed", elapsed);
|
| - }
|
| + const base::Closure callback = base::Bind(&DnsProbeService::OnProbeComplete,
|
| + base::Unretained(this));
|
| + system_runner_.RunProbe(callback);
|
| + public_runner_.RunProbe(callback);
|
| + probe_start_time_ = base::Time::Now();
|
| + state_ = STATE_PROBE_RUNNING;
|
|
|
| - switch (result_) {
|
| - case chrome_common_net::DNS_PROBE_UNKNOWN:
|
| - UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Probe.ResultUnknown.Elapsed",
|
| - elapsed);
|
| - break;
|
| - case chrome_common_net::DNS_PROBE_NO_INTERNET:
|
| - UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Probe.ResultNoInternet.Elapsed",
|
| - elapsed);
|
| - break;
|
| - case chrome_common_net::DNS_PROBE_BAD_CONFIG:
|
| - UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Probe.ResultBadConfig.Elapsed",
|
| - elapsed);
|
| -
|
| - // Histogram some extra data to see why BAD_CONFIG is happening.
|
| - UMA_HISTOGRAM_ENUMERATION(
|
| - "DnsProbe.Probe.ResultBadConfig.SystemJobResult",
|
| - system_result_,
|
| - DnsProbeJob::MAX_RESULT);
|
| - UMA_HISTOGRAM_CUSTOM_COUNTS(
|
| - "DnsProbe.Probe.ResultBadConfig.SystemNameserverCount",
|
| - system_nameserver_count_,
|
| - 0, kNameserverCountMax, kNameserverCountMax + 1);
|
| - UMA_HISTOGRAM_BOOLEAN(
|
| - "DnsProbe.Probe.ResultBadConfig.SystemIsLocalhost",
|
| - system_is_localhost_);
|
| - break;
|
| - case chrome_common_net::DNS_PROBE_NXDOMAIN:
|
| - UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Probe.ResultNxdomain.Elapsed",
|
| - elapsed);
|
| - break;
|
| - case chrome_common_net::DNS_PROBE_MAX:
|
| - NOTREACHED();
|
| - break;
|
| - }
|
| + DCHECK(system_runner_.IsRunning());
|
| + DCHECK(public_runner_.IsRunning());
|
| }
|
|
|
| -DnsProbeResult DnsProbeService::EvaluateResults() const {
|
| - DCHECK_NE(DnsProbeJob::SERVERS_UNKNOWN, system_result_);
|
| - DCHECK_NE(DnsProbeJob::SERVERS_UNKNOWN, public_result_);
|
| +void DnsProbeService::OnProbeComplete() {
|
| + DCHECK_EQ(STATE_PROBE_RUNNING, state_);
|
|
|
| - // If the system DNS is working, assume the domain doesn't exist.
|
| - if (system_result_ == DnsProbeJob::SERVERS_CORRECT)
|
| - return chrome_common_net::DNS_PROBE_NXDOMAIN;
|
| + if (system_runner_.IsRunning() || public_runner_.IsRunning())
|
| + return;
|
|
|
| - // If the system DNS is not working but another public server is, assume the
|
| - // DNS config is bad (or perhaps the DNS servers are down or broken).
|
| - if (public_result_ == DnsProbeJob::SERVERS_CORRECT)
|
| - return chrome_common_net::DNS_PROBE_BAD_CONFIG;
|
| + cached_result_ = EvaluateResults(system_runner_.result(),
|
| + public_runner_.result());
|
| + state_ = STATE_RESULT_CACHED;
|
|
|
| - // If the system DNS is not working and another public server is unreachable,
|
| - // assume the internet connection is down (note that system DNS may be a
|
| - // router on the LAN, so it may be reachable but returning errors.)
|
| - if (public_result_ == DnsProbeJob::SERVERS_UNREACHABLE)
|
| - return chrome_common_net::DNS_PROBE_NO_INTERNET;
|
| + HistogramProbe(cached_result_, base::Time::Now() - probe_start_time_);
|
|
|
| - // Otherwise: the system DNS is not working and another public server is
|
| - // responding but with errors or incorrect results. This is an awkward case;
|
| - // an invasive captive portal or a restrictive firewall may be intercepting
|
| - // or rewriting DNS traffic, or the public server may itself be failing or
|
| - // down.
|
| - return chrome_common_net::DNS_PROBE_UNKNOWN;
|
| + CallCallbacks();
|
| }
|
|
|
| void DnsProbeService::CallCallbacks() {
|
| - DCHECK_EQ(STATE_RESULTS_CACHED, state_);
|
| - DCHECK(!callbacks_.empty());
|
| + DCHECK_EQ(STATE_RESULT_CACHED, state_);
|
| + DCHECK(chrome_common_net::DnsProbeStatusIsFinished(cached_result_));
|
| + DCHECK(!pending_callbacks_.empty());
|
|
|
| - std::vector<CallbackType> callbacks = callbacks_;
|
| - callbacks_.clear();
|
| + std::vector<ProbeCallback> callbacks;
|
| + callbacks.swap(pending_callbacks_);
|
|
|
| - for (std::vector<CallbackType>::const_iterator i = callbacks.begin();
|
| + for (std::vector<ProbeCallback>::const_iterator i = callbacks.begin();
|
| i != callbacks.end(); ++i) {
|
| - i->Run(result_);
|
| + i->Run(cached_result_);
|
| }
|
| }
|
|
|
| -scoped_ptr<DnsProbeJob> DnsProbeService::CreateProbeJob(
|
| - const DnsConfig& dns_config,
|
| - const DnsProbeJob::CallbackType& job_callback) {
|
| - if (!dns_config.IsValid())
|
| - return scoped_ptr<DnsProbeJob>();
|
| -
|
| - scoped_ptr<DnsClient> dns_client(DnsClient::CreateClient(NULL));
|
| - dns_client->SetConfig(dns_config);
|
| - return DnsProbeJob::CreateJob(dns_client.Pass(), job_callback, NULL);
|
| -}
|
| -
|
| -void DnsProbeService::OnProbeJobComplete(DnsProbeJob* job,
|
| - DnsProbeJob::Result result) {
|
| - DCHECK_EQ(STATE_PROBE_RUNNING, state_);
|
| -
|
| - if (job == system_job_.get()) {
|
| - system_result_ = result;
|
| - system_job_.reset();
|
| - } else if (job == public_job_.get()) {
|
| - public_result_ = result;
|
| - public_job_.reset();
|
| - } else {
|
| - NOTREACHED();
|
| - return;
|
| - }
|
| -
|
| - if (system_result_ != DnsProbeJob::SERVERS_UNKNOWN &&
|
| - public_result_ != DnsProbeJob::SERVERS_UNKNOWN) {
|
| - OnProbesComplete();
|
| +void DnsProbeService::ClearCachedResult() {
|
| + if (state_ == STATE_RESULT_CACHED) {
|
| + state_ = STATE_NO_RESULT;
|
| + cached_result_ = chrome_common_net::DNS_PROBE_MAX;
|
| }
|
| }
|
|
|
| -void DnsProbeService::GetSystemDnsConfig(DnsConfig* config) {
|
| - NetworkChangeNotifier::GetDnsConfig(config);
|
| -
|
| - // DNS probes don't need or want the suffix search list populated
|
| - config->search.clear();
|
| -
|
| - if (dns_attempts_ != kAttemptsUseDefault)
|
| - config->attempts = dns_attempts_;
|
| -
|
| - // Take notes in case the config turns out to be bad, so we can histogram
|
| - // some useful data.
|
| - system_nameserver_count_ = config->nameservers.size();
|
| - system_is_localhost_ = (system_nameserver_count_ == 1)
|
| - && IsLocalhost(config->nameservers[0].address());
|
| -
|
| - // Disable port randomization.
|
| - config->randomize_ports = false;
|
| -}
|
| -
|
| -void DnsProbeService::GetPublicDnsConfig(DnsConfig* config) {
|
| - *config = DnsConfig();
|
| -
|
| - config->nameservers.push_back(MakeDnsEndPoint(kPublicDnsPrimary));
|
| - config->nameservers.push_back(MakeDnsEndPoint(kPublicDnsSecondary));
|
| -
|
| - if (dns_attempts_ != kAttemptsUseDefault)
|
| - config->attempts = dns_attempts_;
|
| -
|
| - // Disable port randomization.
|
| - config->randomize_ports = false;
|
| -}
|
| +bool DnsProbeService::CachedResultIsExpired() const {
|
| + if (state_ != STATE_RESULT_CACHED)
|
| + return false;
|
|
|
| -bool DnsProbeService::ResultsExpired() {
|
| const base::TimeDelta kMaxResultAge =
|
| base::TimeDelta::FromMilliseconds(kMaxResultAgeMs);
|
| return base::Time::Now() - probe_start_time_ > kMaxResultAge;
|
| }
|
|
|
| -} // namespace chrome_browser_net
|
| +} // namespace chrome_browser_net
|
|
|