Domain Reliability / Navigation Error Logging, part 1
This is just the logging portion of Domain Reliability; the uploading
and configuration parts will come later.
BUG=
Review URL: https://codereview.chromium.org/132283009
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@257815 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index cf73450..294b410 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -14,6 +14,7 @@
"+components/autofill/core/common",
"+components/breakpad",
"+components/dom_distiller",
+ "+components/domain_reliability",
"+components/keyed_service",
"+components/language_usage_metrics",
"+components/nacl/browser",
diff --git a/chrome/browser/net/chrome_network_delegate.cc b/chrome/browser/net/chrome_network_delegate.cc
index 6c99b19..d25f629b2 100644
--- a/chrome/browser/net/chrome_network_delegate.cc
+++ b/chrome/browser/net/chrome_network_delegate.cc
@@ -34,6 +34,7 @@
#include "chrome/browser/task_manager/task_manager.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
+#include "components/domain_reliability/monitor.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_view_host.h"
@@ -347,6 +348,7 @@
enable_do_not_track_(NULL),
force_google_safe_search_(NULL),
url_blacklist_manager_(NULL),
+ domain_reliability_monitor_(NULL),
received_content_length_(0),
original_content_length_(0) {
DCHECK(event_router);
@@ -518,6 +520,8 @@
void ChromeNetworkDelegate::OnBeforeRedirect(net::URLRequest* request,
const GURL& new_location) {
+ if (domain_reliability_monitor_)
+ domain_reliability_monitor_->OnBeforeRedirect(request);
ExtensionWebRequestEventRouter::GetInstance()->OnBeforeRedirect(
profile_, extension_info_map_.get(), request, new_location);
}
@@ -612,6 +616,8 @@
} else {
NOTREACHED();
}
+ if (domain_reliability_monitor_)
+ domain_reliability_monitor_->OnCompleted(request, started);
ForwardProxyErrors(request, event_router_.get(), profile_);
ForwardRequestStatus(REQUEST_DONE, request, profile_);
diff --git a/chrome/browser/net/chrome_network_delegate.h b/chrome/browser/net/chrome_network_delegate.h
index f6c8361..6adf283 100644
--- a/chrome/browser/net/chrome_network_delegate.h
+++ b/chrome/browser/net/chrome_network_delegate.h
@@ -32,6 +32,10 @@
class Predictor;
}
+namespace domain_reliability {
+class DomainReliabilityMonitor;
+} // namespace domain_reliability
+
namespace extensions {
class EventRouterForwarder;
class InfoMap;
@@ -96,6 +100,12 @@
force_google_safe_search_ = force_google_safe_search;
}
+ void set_domain_reliability_monitor(
+ domain_reliability::DomainReliabilityMonitor*
+ domain_reliability_monitor) {
+ domain_reliability_monitor_ = domain_reliability_monitor;
+ }
+
// Adds the Client Hints header to HTTP requests.
void SetEnableClientHints();
@@ -194,6 +204,7 @@
// Weak, owned by our owner.
const policy::URLBlacklistManager* url_blacklist_manager_;
+ domain_reliability::DomainReliabilityMonitor* domain_reliability_monitor_;
// When true, allow access to all file:// URLs.
static bool g_allow_file_access_;
diff --git a/chrome/browser/profiles/profile_impl_io_data.cc b/chrome/browser/profiles/profile_impl_io_data.cc
index 9649f41..dd51909 100644
--- a/chrome/browser/profiles/profile_impl_io_data.cc
+++ b/chrome/browser/profiles/profile_impl_io_data.cc
@@ -33,6 +33,7 @@
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
+#include "components/domain_reliability/monitor.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/cookie_store_factory.h"
#include "content/public/browser/notification_service.h"
@@ -80,6 +81,15 @@
#endif
}
+bool IsDomainReliabilityMonitoringEnabled() {
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ if (command_line->HasSwitch(switches::kDisableDomainReliability))
+ return false;
+ if (command_line->HasSwitch(switches::kEnableDomainReliability))
+ return true;
+ return base::FieldTrialList::FindFullName("DomRel-Enable") == "enable";
+}
+
} // namespace
using content::BrowserThread;
@@ -495,6 +505,13 @@
media_request_context_.reset(InitializeMediaRequestContext(main_context,
details));
+ if (IsDomainReliabilityMonitoringEnabled()) {
+ domain_reliability_monitor_.reset(
+ new domain_reliability::DomainReliabilityMonitor());
+ network_delegate()->set_domain_reliability_monitor(
+ domain_reliability_monitor_.get());
+ }
+
lazy_params_.reset();
}
diff --git a/chrome/browser/profiles/profile_impl_io_data.h b/chrome/browser/profiles/profile_impl_io_data.h
index 94af627..e03754093 100644
--- a/chrome/browser/profiles/profile_impl_io_data.h
+++ b/chrome/browser/profiles/profile_impl_io_data.h
@@ -22,6 +22,10 @@
class CookieCryptoDelegate;
} // namespace content
+namespace domain_reliability {
+class DomainReliabilityMonitor;
+} // namespace domain_reliability
+
namespace net {
class FtpTransactionFactory;
class HttpServerProperties;
@@ -210,6 +214,9 @@
mutable scoped_ptr<net::URLRequestJobFactory> main_job_factory_;
mutable scoped_ptr<net::URLRequestJobFactory> extensions_job_factory_;
+ mutable scoped_ptr<domain_reliability::DomainReliabilityMonitor>
+ domain_reliability_monitor_;
+
// Parameters needed for isolated apps.
base::FilePath profile_path_;
int app_cache_max_size_;
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 80a71a55..be98431 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -38,6 +38,7 @@
'../components/component_strings.gyp:component_strings',
'../components/components.gyp:autofill_core_browser',
'../components/components.gyp:cloud_devices',
+ '../components/components.gyp:domain_reliability',
'../components/components.gyp:navigation_metrics',
'../components/components.gyp:os_crypt',
'../components/components.gyp:password_manager_core_browser',
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 9d47608..0c5c4300 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -299,6 +299,9 @@
// Disables retrieval of PAC URLs from DHCP as per the WPAD standard.
const char kDisableDhcpWpad[] = "disable-dhcp-wpad";
+// Disables Domain Reliability Monitoring.
+const char kDisableDomainReliability[] = "disable-domain-reliability";
+
// Disable extensions.
const char kDisableExtensions[] = "disable-extensions";
@@ -526,6 +529,9 @@
// Enables the DOM distiller.
const char kEnableDomDistiller[] = "enable-dom-distiller";
+// Enables Domain Reliability Monitoring.
+const char kEnableDomainReliability[] = "enable-domain-reliability";
+
// Enable Enhanced Bookmarks.
const char kEnhancedBookmarksExperiment[] = "enhanced-bookmarks-experiment";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index f0e0573..3cae99b 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -93,6 +93,7 @@
extern const char kDisableDeviceDiscoveryNotifications[];
extern const char kDisableDhcpWpad[];
extern const char kDisableDnsProbes[];
+extern const char kDisableDomainReliability[];
extern const char kDisableExtensionsFileAccessCheck[];
extern const char kDisableExtensionsHttpThrottling[];
extern const char kDisableExtensionsResourceWhitelist[];
@@ -151,7 +152,8 @@
extern const char kEnableDevToolsExperiments[];
extern const char kEnableDeviceDiscoveryNotifications[];
extern const char kEnableDomDistiller[];
-extern const char kEnhancedBookmarksExperiment[];
+extern const char kEnableDomainReliability[];
+extern const char kEnableEnhancedBookmarks[];
extern const char kEnableEphemeralApps[];
extern const char kEnableExtensionActivityLogging[];
extern const char kEnableExtensionActivityLogTesting[];
@@ -207,6 +209,7 @@
extern const char kEnableUserAlternateProtocolPorts[];
extern const char kEnableWatchdog[];
extern const char kEnableWebSocketOverSpdy[];
+extern const char kEnhancedBookmarksExperiment[];
extern const char kExplicitlyAllowedPorts[];
extern const char kExtensionsInstallVerification[];
extern const char kExtensionsNotWebstore[];
diff --git a/components/OWNERS b/components/OWNERS
index 104cb19..427b187 100644
--- a/components/OWNERS
+++ b/components/OWNERS
@@ -18,6 +18,11 @@
per-file dom_distiller*[email protected]
per-file dom_distiller*[email protected]
+per-file [email protected]
+per-file [email protected]
+per-file [email protected]
+per-file [email protected]
+
per-file [email protected]
per-file [email protected]
per-file [email protected]
diff --git a/components/components.gyp b/components/components.gyp
index ff1222fc..aa24986 100644
--- a/components/components.gyp
+++ b/components/components.gyp
@@ -15,6 +15,7 @@
'breakpad.gypi',
'cloud_devices.gypi',
'dom_distiller.gypi',
+ 'domain_reliability.gypi',
'json_schema.gypi',
'keyed_service.gypi',
'language_usage_metrics.gypi',
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index d15b5c7..b2b32989 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -64,6 +64,11 @@
'dom_distiller/core/dom_distiller_store_unittest.cc',
'dom_distiller/core/task_tracker_unittest.cc',
'dom_distiller/core/url_utils_unittest.cc',
+ 'domain_reliability/context_unittest.cc',
+ 'domain_reliability/monitor_unittest.cc',
+ 'domain_reliability/test_util.cc',
+ 'domain_reliability/test_util.h',
+ 'domain_reliability/util_unittest.cc',
'json_schema/json_schema_validator_unittest.cc',
'json_schema/json_schema_validator_unittest_base.cc',
'json_schema/json_schema_validator_unittest_base.h',
@@ -165,8 +170,8 @@
'components.gyp:dom_distiller_core',
'components.gyp:dom_distiller_test_support',
- # Dependencies of os_crypt
- 'components.gyp:os_crypt',
+ # Dependencies of domain_reliability
+ 'components.gyp:domain_reliability',
# Dependencies of json_schema
'components.gyp:json_schema',
@@ -177,6 +182,9 @@
# Dependencies of language_usage_metrics
'components.gyp:language_usage_metrics',
+ # Dependencies of os_crypt
+ 'components.gyp:os_crypt',
+
# Dependencies of password_manager
'components.gyp:password_manager_core_browser',
'components.gyp:password_manager_core_browser_test_support',
diff --git a/components/domain_reliability.gypi b/components/domain_reliability.gypi
new file mode 100644
index 0000000..0c5052fd
--- /dev/null
+++ b/components/domain_reliability.gypi
@@ -0,0 +1,37 @@
+# Copyright 2014 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'domain_reliability',
+ 'type': '<(component)',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../content/content.gyp:content_browser',
+ '../net/net.gyp:net',
+ '../url/url.gyp:url_lib',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'defines': [
+ 'DOMAIN_RELIABILITY_IMPLEMENTATION',
+ ],
+ 'sources': [
+ 'domain_reliability/beacon.cc',
+ 'domain_reliability/beacon.h',
+ 'domain_reliability/config.cc',
+ 'domain_reliability/config.h',
+ 'domain_reliability/context.cc',
+ 'domain_reliability/context.h',
+ 'domain_reliability/domain_reliability_export.h',
+ 'domain_reliability/monitor.cc',
+ 'domain_reliability/monitor.h',
+ 'domain_reliability/util.cc',
+ 'domain_reliability/util.h',
+ ],
+ },
+ ],
+}
diff --git a/components/domain_reliability/DEPS b/components/domain_reliability/DEPS
new file mode 100644
index 0000000..70cc3c5
--- /dev/null
+++ b/components/domain_reliability/DEPS
@@ -0,0 +1,9 @@
+# Copyright 2014 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_rules = [
+ "+content/public/browser",
+ "+content/public/test",
+ "+net",
+]
diff --git a/components/domain_reliability/OWNERS b/components/domain_reliability/OWNERS
new file mode 100644
index 0000000..07a5e14
--- /dev/null
+++ b/components/domain_reliability/OWNERS
@@ -0,0 +1,4 @@
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/components/domain_reliability/beacon.cc b/components/domain_reliability/beacon.cc
new file mode 100644
index 0000000..8b738114
--- /dev/null
+++ b/components/domain_reliability/beacon.cc
@@ -0,0 +1,39 @@
+// Copyright 2014 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 "components/domain_reliability/beacon.h"
+
+#include "base/values.h"
+#include "net/base/net_errors.h"
+
+namespace domain_reliability {
+
+using base::Value;
+using base::DictionaryValue;
+
+DomainReliabilityBeacon::DomainReliabilityBeacon() {}
+DomainReliabilityBeacon::~DomainReliabilityBeacon() {}
+
+Value* DomainReliabilityBeacon::ToValue(base::TimeTicks upload_time) const {
+ DictionaryValue* beacon_value = new DictionaryValue();
+ beacon_value->SetString("status", status);
+ if (chrome_error != net::OK) {
+ DictionaryValue* failure_value = new DictionaryValue();
+ failure_value->SetString("custom_error",
+ net::ErrorToString(chrome_error));
+ beacon_value->Set("failure_data", failure_value);
+ }
+ beacon_value->SetString("server_ip", server_ip);
+ if (http_response_code >= 0)
+ beacon_value->SetInteger("http_response_code", http_response_code);
+ beacon_value->SetInteger("request_elapsed_ms",
+ elapsed.InMilliseconds());
+ beacon_value->SetInteger("request_age_ms",
+ (upload_time - start_time).InMilliseconds());
+ // TODO(ttuttle): Implement protocol and dns_resolver_ip[s] fields.
+
+ return beacon_value;
+}
+
+} // namespace domain_reliability
diff --git a/components/domain_reliability/beacon.h b/components/domain_reliability/beacon.h
new file mode 100644
index 0000000..42cfb1c
--- /dev/null
+++ b/components/domain_reliability/beacon.h
@@ -0,0 +1,47 @@
+// Copyright 2014 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 COMPONENTS_DOMAIN_RELIABILITY_BEACON_H_
+#define COMPONENTS_DOMAIN_RELIABILITY_BEACON_H_
+
+#include <string>
+
+#include "base/time/time.h"
+#include "components/domain_reliability/domain_reliability_export.h"
+
+namespace base {
+class Value;
+} // namespace base
+
+namespace domain_reliability {
+
+// The per-request data that is uploaded to the Domain Reliability collector.
+class DOMAIN_RELIABILITY_EXPORT DomainReliabilityBeacon {
+ public:
+ DomainReliabilityBeacon();
+ ~DomainReliabilityBeacon();
+
+ // Converts the Beacon to JSON format for uploading. Calculates the age
+ // relative to an upload time of |upload_time|.
+ base::Value* ToValue(base::TimeTicks upload_time) const;
+
+ // Status string (e.g. "ok", "dns.nxdomain", "http.403").
+ std::string status;
+ // Net error code. Encoded as a string in the final JSON.
+ int chrome_error;
+ // IP address of the server the request went to.
+ std::string server_ip;
+ // HTTP response code returned by the server, or -1 if none was received.
+ int http_response_code;
+ // Elapsed time between starting and completing the request.
+ base::TimeDelta elapsed;
+ // Start time of the request. Encoded as the request age in the final JSON.
+ base::TimeTicks start_time;
+
+ // Okay to copy and assign :)
+};
+
+} // namespace domain_reliability
+
+#endif // COMPONENTS_DOMAIN_RELIABILITY_BEACON_H_
diff --git a/components/domain_reliability/config.cc b/components/domain_reliability/config.cc
new file mode 100644
index 0000000..03d45972
--- /dev/null
+++ b/components/domain_reliability/config.cc
@@ -0,0 +1,110 @@
+// Copyright 2014 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 "components/domain_reliability/config.h"
+
+#include "base/json/json_reader.h"
+#include "base/json/json_value_converter.h"
+#include "base/rand_util.h"
+#include "base/strings/string_util.h"
+
+namespace {
+
+bool ConvertURL(const base::StringPiece& string_piece, GURL* url) {
+ *url = GURL(string_piece.as_string());
+ return url->is_valid();
+}
+
+} // namespace
+
+namespace domain_reliability {
+
+DomainReliabilityConfig::Resource::Resource() {}
+
+DomainReliabilityConfig::Resource::~Resource() {}
+
+bool DomainReliabilityConfig::Resource::MatchesUrlString(
+ const std::string& url_string) const {
+ ScopedVector<std::string>::const_iterator it;
+
+ for (it = url_patterns.begin(); it != url_patterns.end(); it++)
+ if (MatchPattern(url_string, **it))
+ return true;
+
+ return false;
+}
+
+bool DomainReliabilityConfig::Resource::DecideIfShouldReportRequest(
+ bool success) const {
+ double sample_rate = success ? success_sample_rate : failure_sample_rate;
+ return base::RandDouble() < sample_rate;
+}
+
+// static
+void DomainReliabilityConfig::Resource::RegisterJSONConverter(
+ base::JSONValueConverter<DomainReliabilityConfig::Resource>* converter) {
+ converter->RegisterStringField("resource_name", &Resource::name);
+ converter->RegisterRepeatedString("url_patterns", &Resource::url_patterns);
+ converter->RegisterDoubleField("success_sample_rate",
+ &Resource::success_sample_rate);
+ converter->RegisterDoubleField("failure_sample_rate",
+ &Resource::failure_sample_rate);
+}
+
+DomainReliabilityConfig::Collector::Collector() {}
+
+DomainReliabilityConfig::Collector::~Collector() {}
+
+// static
+void DomainReliabilityConfig::Collector::RegisterJSONConverter(
+ base::JSONValueConverter<DomainReliabilityConfig::Collector>* converter) {
+ converter->RegisterCustomField<GURL>("upload_url", &Collector::upload_url,
+ &ConvertURL);
+}
+
+DomainReliabilityConfig::DomainReliabilityConfig() {}
+
+DomainReliabilityConfig::~DomainReliabilityConfig() {}
+
+// static
+scoped_ptr<const DomainReliabilityConfig> DomainReliabilityConfig::FromJSON(
+ const base::StringPiece& json) {
+ base::Value* value = base::JSONReader::Read(json);
+ if (!value)
+ return scoped_ptr<const DomainReliabilityConfig>();
+
+ DomainReliabilityConfig* config = new DomainReliabilityConfig();
+ base::JSONValueConverter<DomainReliabilityConfig> converter;
+ if (!converter.Convert(*value, config)) {
+ return scoped_ptr<const DomainReliabilityConfig>();
+ }
+
+ return scoped_ptr<const DomainReliabilityConfig>(config);
+}
+
+int DomainReliabilityConfig::GetResourceIndexForUrl(const GURL& url) const {
+ const std::string& url_string = url.spec();
+
+ for (size_t i = 0; i < resources.size(); ++i) {
+ if (resources[i]->MatchesUrlString(url_string))
+ return static_cast<int>(i);
+ }
+
+ return -1;
+}
+
+// static
+void DomainReliabilityConfig::RegisterJSONConverter(
+ base::JSONValueConverter<DomainReliabilityConfig>* converter) {
+ converter->RegisterStringField("config_version",
+ &DomainReliabilityConfig::config_version);
+ converter->RegisterStringField("monitored_domain",
+ &DomainReliabilityConfig::domain);
+ converter->RegisterRepeatedMessage("monitored_resources",
+ &DomainReliabilityConfig::resources);
+ converter->RegisterRepeatedMessage("collectors",
+ &DomainReliabilityConfig::collectors);
+}
+
+} // namespace domain_reliability
diff --git a/components/domain_reliability/config.h b/components/domain_reliability/config.h
new file mode 100644
index 0000000..6453303
--- /dev/null
+++ b/components/domain_reliability/config.h
@@ -0,0 +1,112 @@
+// Copyright 2014 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 COMPONENTS_DOMAIN_RELIABILITY_CONFIG_H_
+#define COMPONENTS_DOMAIN_RELIABILITY_CONFIG_H_
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/json/json_value_converter.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "base/values.h"
+#include "components/domain_reliability/domain_reliability_export.h"
+#include "url/gurl.h"
+
+namespace domain_reliability {
+
+// The configuration that controls which requests are measured and reported,
+// with what frequency, and where the beacons are uploaded.
+class DOMAIN_RELIABILITY_EXPORT DomainReliabilityConfig {
+ public:
+ // A particular resource named in the config -- includes a set of URL
+ // patterns that the resource will match, along with sample rates for
+ // successful and unsuccessful requests.
+ class DOMAIN_RELIABILITY_EXPORT Resource {
+ public:
+ Resource();
+ ~Resource();
+
+ // Returns whether |url_string| matches at least one of the |url_patterns|
+ // in this Resource.
+ bool MatchesUrlString(const std::string& url_string) const;
+
+ // Returns whether a request (that was successful if |success| is true)
+ // should be reported (with a full beacon). (The output is random; it
+ // compares a random number to |success_sample_rate| or
+ // |failure_sample_rate|.)
+ bool DecideIfShouldReportRequest(bool success) const;
+
+ // Registers with the JSONValueConverter so it will know how to convert the
+ // JSON for a named resource into the struct.
+ static void RegisterJSONConverter(
+ base::JSONValueConverter<Resource>* converter);
+
+ // Name of the Resource, as will be reported in uploads.
+ std::string name;
+
+ // List of URL patterns to assign requests to this Resource.
+ ScopedVector<std::string> url_patterns;
+
+ // Sample rates for successful and unsuccessful requests, respectively.
+ // 0.0 reports no requests, and 1.0 reports every request.
+ double success_sample_rate;
+ double failure_sample_rate;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Resource);
+ };
+
+ // A particular endpoint for report uploads. Includes the URL to upload
+ // reports to. May include a verification URL or backoff/load management
+ // configuration in the future.
+ struct DOMAIN_RELIABILITY_EXPORT Collector {
+ public:
+ Collector();
+ ~Collector();
+
+ // Registers with the JSONValueConverter so it will know how to convert the
+ // JSON for a collector into the struct.
+ static void RegisterJSONConverter(
+ base::JSONValueConverter<Collector>* converter);
+
+ GURL upload_url;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Collector);
+ };
+
+ DomainReliabilityConfig();
+ ~DomainReliabilityConfig();
+
+ // Uses the JSONValueConverter to parse the JSON for a config into a struct.
+ static scoped_ptr<const DomainReliabilityConfig> FromJSON(
+ const base::StringPiece& json);
+
+ // Finds the index (in resources) of the first Resource that matches a
+ // particular URL. Returns -1 if the URL is not matched by any Resources.
+ int GetResourceIndexForUrl(const GURL& url) const;
+
+ // Registers with the JSONValueConverter so it will know how to convert the
+ // JSON for a config into the struct.
+ static void RegisterJSONConverter(
+ base::JSONValueConverter<DomainReliabilityConfig>* converter);
+
+ std::string config_version;
+ std::string domain;
+ ScopedVector<Resource> resources;
+ ScopedVector<Collector> collectors;
+
+ // TODO(ttuttle): Add config_valid_util when fetching and expiring configs
+ // is implemented.
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DomainReliabilityConfig);
+};
+
+} // namespace domain_reliability
+
+#endif // COMPONENTS_DOMAIN_RELIABILITY_CONFIG_H_
diff --git a/components/domain_reliability/context.cc b/components/domain_reliability/context.cc
new file mode 100644
index 0000000..5a69689
--- /dev/null
+++ b/components/domain_reliability/context.cc
@@ -0,0 +1,206 @@
+// Copyright 2014 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 "components/domain_reliability/context.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/values.h"
+#include "net/url_request/url_request_context_getter.h"
+
+using base::DictionaryValue;
+using base::ListValue;
+using base::Value;
+
+namespace domain_reliability {
+
+namespace {
+const char* kReporter = "chrome";
+typedef std::deque<DomainReliabilityBeacon> BeaconDeque;
+typedef BeaconDeque::iterator BeaconIterator;
+typedef BeaconDeque::const_iterator BeaconConstIterator;
+} // namespace
+
+const int DomainReliabilityContext::kMaxQueuedBeacons = 150;
+
+DomainReliabilityContext::DomainReliabilityContext(
+ MockableTime* time,
+ scoped_ptr<const DomainReliabilityConfig> config)
+ : config_(config.Pass()),
+ time_(time),
+ beacon_count_(0),
+ weak_factory_(this) {
+ InitializeResourceStates();
+}
+
+DomainReliabilityContext::~DomainReliabilityContext() {}
+
+void DomainReliabilityContext::AddBeacon(
+ const DomainReliabilityBeacon& beacon,
+ const GURL& url) {
+ int index = config_->GetResourceIndexForUrl(url);
+ if (index < 0)
+ return;
+ DCHECK_GT(states_.size(), static_cast<size_t>(index));
+
+ ResourceState* state = states_[index];
+ bool success = beacon.http_response_code >= 200 &&
+ beacon.http_response_code < 400;
+ if (success)
+ ++state->successful_requests;
+ else
+ ++state->failed_requests;
+
+ VLOG(1) << "Received Beacon: "
+ << state->config->name << " "
+ << beacon.status << " "
+ << beacon.chrome_error << " "
+ << beacon.http_response_code << " "
+ << beacon.server_ip << " "
+ << beacon.elapsed.InMilliseconds() << "ms";
+
+ if (state->config->DecideIfShouldReportRequest(success)) {
+ state->beacons.push_back(beacon);
+ ++beacon_count_;
+ if (beacon_count_ > kMaxQueuedBeacons)
+ RemoveOldestBeacon();
+ }
+}
+
+void DomainReliabilityContext::GetQueuedDataForTesting(
+ int resource_index,
+ std::vector<DomainReliabilityBeacon>* beacons_out,
+ int* successful_requests_out,
+ int* failed_requests_out) const {
+ DCHECK_LE(0, resource_index);
+ DCHECK_GT(static_cast<int>(states_.size()), resource_index);
+ const ResourceState& state = *states_[resource_index];
+ if (beacons_out) {
+ beacons_out->resize(state.beacons.size());
+ std::copy(state.beacons.begin(), state.beacons.end(), beacons_out->begin());
+ }
+ if (successful_requests_out)
+ *successful_requests_out = state.successful_requests;
+ if (failed_requests_out)
+ *failed_requests_out = state.failed_requests;
+}
+
+DomainReliabilityContext::ResourceState::ResourceState(
+ DomainReliabilityContext* context,
+ const DomainReliabilityConfig::Resource* config)
+ : context(context),
+ config(config),
+ successful_requests(0),
+ failed_requests(0) {}
+
+DomainReliabilityContext::ResourceState::~ResourceState() {}
+
+scoped_ptr<Value> DomainReliabilityContext::ResourceState::ToValue(
+ base::TimeTicks upload_time) const {
+ ListValue* beacons_value = new ListValue();
+ for (BeaconConstIterator it = beacons.begin(); it != beacons.end(); ++it)
+ beacons_value->Append(it->ToValue(upload_time));
+
+ DictionaryValue* resource_value = new DictionaryValue();
+ resource_value->SetString("resource_name", config->name);
+ resource_value->SetInteger("successful_requests", successful_requests);
+ resource_value->SetInteger("failed_requests", failed_requests);
+ resource_value->Set("beacons", beacons_value);
+
+ return scoped_ptr<Value>(resource_value);
+}
+
+void DomainReliabilityContext::ResourceState::MarkUpload() {
+ uploading_beacons_size = beacons.size();
+ uploading_successful_requests = successful_requests;
+ uploading_failed_requests = failed_requests;
+}
+
+void DomainReliabilityContext::ResourceState::CommitUpload() {
+ BeaconIterator begin = beacons.begin();
+ BeaconIterator end = begin + uploading_beacons_size;
+ beacons.erase(begin, end);
+ successful_requests -= uploading_successful_requests;
+ failed_requests -= uploading_failed_requests;
+}
+
+bool DomainReliabilityContext::ResourceState::GetOldestBeaconStart(
+ base::TimeTicks* oldest_start_out) const {
+ if (beacons.empty())
+ return false;
+
+ *oldest_start_out = beacons[0].start_time;
+ return true;
+}
+
+void DomainReliabilityContext::ResourceState::RemoveOldestBeacon() {
+ DCHECK(!beacons.empty());
+ beacons.erase(beacons.begin());
+ // If that just removed a beacon counted in uploading_beacons_size, decrement
+ // that.
+ if (uploading_beacons_size > 0)
+ --uploading_beacons_size;
+}
+
+void DomainReliabilityContext::InitializeResourceStates() {
+ ScopedVector<DomainReliabilityConfig::Resource>::const_iterator it;
+ for (it = config_->resources.begin(); it != config_->resources.end(); ++it)
+ states_.push_back(new ResourceState(this, *it));
+}
+
+scoped_ptr<const Value> DomainReliabilityContext::CreateReport(
+ base::TimeTicks upload_time) const {
+ ListValue* resources_value = new ListValue();
+ for (ResourceStateIterator it = states_.begin(); it != states_.end(); ++it)
+ resources_value->Append((*it)->ToValue(upload_time).release());
+
+ DictionaryValue* report_value = new DictionaryValue();
+ report_value->SetString("reporter", kReporter);
+ report_value->Set("resource_reports", resources_value);
+
+ return scoped_ptr<const Value>(report_value);
+}
+
+void DomainReliabilityContext::MarkUpload() {
+ for (ResourceStateIterator it = states_.begin(); it != states_.end(); ++it)
+ (*it)->MarkUpload();
+ uploading_beacon_count_ = beacon_count_;
+}
+
+void DomainReliabilityContext::CommitUpload() {
+ for (ResourceStateIterator it = states_.begin(); it != states_.end(); ++it)
+ (*it)->CommitUpload();
+ beacon_count_ -= uploading_beacon_count_;
+}
+
+void DomainReliabilityContext::RemoveOldestBeacon() {
+ DCHECK_LT(0, beacon_count_);
+
+ base::TimeTicks min_time;
+ ResourceState* min_resource = NULL;
+ for (ResourceStateIterator it = states_.begin(); it != states_.end(); ++it) {
+ base::TimeTicks oldest;
+ if ((*it)->GetOldestBeaconStart(&oldest)) {
+ if (!min_resource || oldest < min_time) {
+ min_time = oldest;
+ min_resource = *it;
+ }
+ }
+ }
+ DCHECK(min_resource);
+
+ VLOG(1) << "Removing oldest beacon from " << min_resource->config->name;
+
+ min_resource->RemoveOldestBeacon();
+ --beacon_count_;
+ // If that just removed a beacon counted in uploading_beacon_count_, decrement
+ // that.
+ if (uploading_beacon_count_ > 0)
+ --uploading_beacon_count_;
+}
+
+} // namespace domain_reliability
diff --git a/components/domain_reliability/context.h b/components/domain_reliability/context.h
new file mode 100644
index 0000000..bb6099d
--- /dev/null
+++ b/components/domain_reliability/context.h
@@ -0,0 +1,122 @@
+// Copyright 2014 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 COMPONENTS_DOMAIN_RELIABILITY_CONTEXT_H_
+#define COMPONENTS_DOMAIN_RELIABILITY_CONTEXT_H_
+
+#include <deque>
+#include <map>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "components/domain_reliability/beacon.h"
+#include "components/domain_reliability/config.h"
+#include "components/domain_reliability/domain_reliability_export.h"
+#include "components/domain_reliability/util.h"
+
+class GURL;
+class MockableTime;
+
+namespace domain_reliability {
+
+// The per-domain context for the Domain Reliability client; includes the
+// domain's config and per-resource beacon queues.
+class DOMAIN_RELIABILITY_EXPORT DomainReliabilityContext {
+ public:
+ DomainReliabilityContext(
+ MockableTime* time,
+ scoped_ptr<const DomainReliabilityConfig> config);
+ virtual ~DomainReliabilityContext();
+
+ void AddBeacon(const DomainReliabilityBeacon& beacon, const GURL& url);
+
+ void GetQueuedDataForTesting(
+ int resource_index,
+ std::vector<DomainReliabilityBeacon>* beacons_out,
+ int* successful_requests_out,
+ int* failed_requests_out) const;
+
+ const DomainReliabilityConfig& config() { return *config_.get(); }
+
+ // Maximum number of beacons queued per context; if more than this many are
+ // queued; the oldest beacons will be removed.
+ static const int kMaxQueuedBeacons;
+
+ private:
+ // Resource-specific state (queued beacons and request counts).
+ class ResourceState {
+ public:
+ ResourceState(DomainReliabilityContext* context,
+ const DomainReliabilityConfig::Resource* config);
+ ~ResourceState();
+
+ scoped_ptr<base::Value> ToValue(base::TimeTicks upload_time) const;
+
+ // Remembers the current state of the resource data when an upload starts.
+ void MarkUpload();
+
+ // Uses the state remembered by |MarkUpload| to remove successfully uploaded
+ // data but keep beacons and request counts added after the upload started.
+ void CommitUpload();
+
+ // Gets the start time of the oldest beacon, if there are any. Returns true
+ // and sets |oldest_start_out| if so; otherwise, returns false.
+ bool GetOldestBeaconStart(base::TimeTicks* oldest_start_out) const;
+ // Removes the oldest beacon. DCHECKs if there isn't one.
+ void RemoveOldestBeacon();
+
+ DomainReliabilityContext* context;
+ const DomainReliabilityConfig::Resource* config;
+
+ std::deque<DomainReliabilityBeacon> beacons;
+ int successful_requests;
+ int failed_requests;
+
+ // State saved during uploads; if an upload succeeds, these are used to
+ // remove uploaded data from the beacon list and request counters.
+ size_t uploading_beacons_size;
+ int uploading_successful_requests;
+ int uploading_failed_requests;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceState);
+ };
+
+ typedef ScopedVector<ResourceState> ResourceStateVector;
+ typedef ResourceStateVector::const_iterator ResourceStateIterator;
+
+ void InitializeResourceStates();
+
+ scoped_ptr<const base::Value> CreateReport(base::TimeTicks upload_time) const;
+
+ // Remembers the current state of the context when an upload starts.
+ void MarkUpload();
+
+ // Uses the state remembered by |MarkUpload| to remove successfully uploaded
+ // data but keep beacons and request counts added after the upload started.
+ void CommitUpload();
+
+ // Finds and removes the oldest beacon. DCHECKs if there is none. (Called
+ // when there are too many beacons queued.)
+ void RemoveOldestBeacon();
+
+ scoped_ptr<const DomainReliabilityConfig> config_;
+ MockableTime* time_;
+
+ // Each ResourceState in |states_| corresponds to the Resource of the same
+ // index in the config.
+ ResourceStateVector states_;
+ int beacon_count_;
+ int uploading_beacon_count_;
+
+ base::WeakPtrFactory<DomainReliabilityContext> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DomainReliabilityContext);
+};
+
+} // namespace domain_reliability
+
+#endif // COMPONENTS_DOMAIN_RELIABILITY_CONTEXT_H_
diff --git a/components/domain_reliability/context_unittest.cc b/components/domain_reliability/context_unittest.cc
new file mode 100644
index 0000000..323f448
--- /dev/null
+++ b/components/domain_reliability/context_unittest.cc
@@ -0,0 +1,127 @@
+// Copyright 2014 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 "components/domain_reliability/context.h"
+
+#include <map>
+#include <string>
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "components/domain_reliability/test_util.h"
+#include "net/base/net_errors.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace domain_reliability {
+
+namespace {
+
+typedef std::vector<DomainReliabilityBeacon> BeaconVector;
+
+DomainReliabilityBeacon MakeBeacon(MockableTime* time) {
+ DomainReliabilityBeacon beacon;
+ beacon.status = "ok";
+ beacon.chrome_error = net::OK;
+ beacon.server_ip = "127.0.0.1";
+ beacon.http_response_code = 200;
+ beacon.elapsed = base::TimeDelta::FromMilliseconds(250);
+ beacon.start_time = time->Now() - beacon.elapsed;
+ return beacon;
+}
+
+} // namespace
+
+class DomainReliabilityContextTest : public testing::Test {
+ protected:
+ DomainReliabilityContextTest()
+ : context_(&time_,
+ CreateConfig().Pass()) {}
+
+ bool CheckNoBeacons(int index) {
+ BeaconVector beacons;
+ context_.GetQueuedDataForTesting(index, &beacons, NULL, NULL);
+ return beacons.empty();
+ }
+
+ bool CheckCounts(int index, int expected_successful, int expected_failed) {
+ int successful, failed;
+ context_.GetQueuedDataForTesting(index, NULL, &successful, &failed);
+ return successful == expected_successful && failed == expected_failed;
+ }
+
+ MockTime time_;
+ DomainReliabilityContext context_;
+
+ private:
+ static scoped_ptr<const DomainReliabilityConfig> CreateConfig() {
+ DomainReliabilityConfig* config = new DomainReliabilityConfig();
+ DomainReliabilityConfig::Resource* resource;
+
+ resource = new DomainReliabilityConfig::Resource();
+ resource->name = "always_report";
+ resource->url_patterns.push_back(
+ new std::string("http://example/always_report"));
+ resource->success_sample_rate = 1.0;
+ resource->failure_sample_rate = 1.0;
+ config->resources.push_back(resource);
+
+ resource = new DomainReliabilityConfig::Resource();
+ resource->name = "never_report";
+ resource->url_patterns.push_back(
+ new std::string("http://example/never_report"));
+ resource->success_sample_rate = 0.0;
+ resource->failure_sample_rate = 0.0;
+ config->resources.push_back(resource);
+
+ DomainReliabilityConfig::Collector* collector;
+ collector = new DomainReliabilityConfig::Collector();
+ collector->upload_url = GURL("https://example/upload");
+ config->collectors.push_back(collector);
+
+ return scoped_ptr<const DomainReliabilityConfig>(config);
+ }
+};
+
+TEST_F(DomainReliabilityContextTest, Create) {
+ EXPECT_TRUE(CheckNoBeacons(0));
+ EXPECT_TRUE(CheckCounts(0, 0, 0));
+ EXPECT_TRUE(CheckNoBeacons(1));
+ EXPECT_TRUE(CheckCounts(1, 0, 0));
+}
+
+TEST_F(DomainReliabilityContextTest, NoResource) {
+ DomainReliabilityBeacon beacon = MakeBeacon(&time_);
+ context_.AddBeacon(beacon, GURL("http://example/no_resource"));
+
+ EXPECT_TRUE(CheckNoBeacons(0));
+ EXPECT_TRUE(CheckCounts(0, 0, 0));
+ EXPECT_TRUE(CheckNoBeacons(1));
+ EXPECT_TRUE(CheckCounts(1, 0, 0));
+}
+
+TEST_F(DomainReliabilityContextTest, NeverReport) {
+ DomainReliabilityBeacon beacon = MakeBeacon(&time_);
+ context_.AddBeacon(beacon, GURL("http://example/never_report"));
+
+ EXPECT_TRUE(CheckNoBeacons(0));
+ EXPECT_TRUE(CheckCounts(0, 0, 0));
+ EXPECT_TRUE(CheckNoBeacons(1));
+ EXPECT_TRUE(CheckCounts(1, 1, 0));
+}
+
+TEST_F(DomainReliabilityContextTest, AlwaysReport) {
+ DomainReliabilityBeacon beacon = MakeBeacon(&time_);
+ context_.AddBeacon(beacon, GURL("http://example/always_report"));
+
+ BeaconVector beacons;
+ context_.GetQueuedDataForTesting(0, &beacons, NULL, NULL);
+ EXPECT_EQ(1u, beacons.size());
+ EXPECT_TRUE(CheckCounts(0, 1, 0));
+ EXPECT_TRUE(CheckNoBeacons(1));
+ EXPECT_TRUE(CheckCounts(1, 0, 0));
+}
+
+} // namespace domain_reliability
diff --git a/components/domain_reliability/domain_reliability_export.h b/components/domain_reliability/domain_reliability_export.h
new file mode 100644
index 0000000..d0951cc
--- /dev/null
+++ b/components/domain_reliability/domain_reliability_export.h
@@ -0,0 +1,29 @@
+// Copyright 2014 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 COMPONENTS_DOMAIN_RELIABILITY_DOMAIN_RELIABILITY_EXPORT_H_
+#define COMPONENTS_DOMAIN_RELIABILITY_DOMAIN_RELIABILITY_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(DOMAIN_RELIABILITY_IMPLEMENTATION)
+#define DOMAIN_RELIABILITY_EXPORT __declspec(dllexport)
+#else
+#define DOMAIN_RELIABILITY_EXPORT __declspec(dllimport)
+#endif // defined(DOMAIN_RELIABILITY_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(DOMAIN_RELIABILITY_IMPLEMENTATION)
+#define DOMAIN_RELIABILITY_EXPORT __attribute__((visibility("default")))
+#else
+#define DOMAIN_RELIABILITY_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define DOMAIN_RELIABILITY_EXPORT
+#endif
+
+#endif // COMPONENTS_DOMAIN_RELIABILITY_DOMAIN_RELIABILITY_EXPORT_H_
diff --git a/components/domain_reliability/monitor.cc b/components/domain_reliability/monitor.cc
new file mode 100644
index 0000000..09f122fd
--- /dev/null
+++ b/components/domain_reliability/monitor.cc
@@ -0,0 +1,114 @@
+// Copyright 2014 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 "components/domain_reliability/monitor.h"
+
+#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace {
+
+bool OnIOThread() {
+ return content::BrowserThread::CurrentlyOn(content::BrowserThread::IO);
+}
+
+} // namespace
+
+namespace domain_reliability {
+
+DomainReliabilityMonitor::DomainReliabilityMonitor()
+ : time_(new ActualTime()) {
+ DCHECK(OnIOThread());
+}
+
+DomainReliabilityMonitor::DomainReliabilityMonitor(
+ scoped_ptr<MockableTime> time)
+ : time_(time.Pass()) {
+ DCHECK(OnIOThread());
+}
+
+DomainReliabilityMonitor::~DomainReliabilityMonitor() {
+ DCHECK(OnIOThread());
+ STLDeleteContainerPairSecondPointers(
+ contexts_.begin(), contexts_.end());
+}
+
+void DomainReliabilityMonitor::OnBeforeRedirect(net::URLRequest* request) {
+ DCHECK(OnIOThread());
+ RequestInfo request_info(*request);
+ // Record the redirect itself in addition to the final request.
+ OnRequestLegComplete(request_info);
+}
+
+void DomainReliabilityMonitor::OnCompleted(net::URLRequest* request,
+ bool started) {
+ DCHECK(OnIOThread());
+ if (!started)
+ return;
+ RequestInfo request_info(*request);
+ OnRequestLegComplete(request_info);
+}
+
+DomainReliabilityContext* DomainReliabilityMonitor::AddContextForTesting(
+ scoped_ptr<const DomainReliabilityConfig> config) {
+ DomainReliabilityContext*& context_ref = contexts_[config->domain];
+ DCHECK(!context_ref);
+ context_ref = new DomainReliabilityContext(time_.get(), config.Pass());
+ return context_ref;
+}
+
+DomainReliabilityMonitor::RequestInfo::RequestInfo() {}
+
+DomainReliabilityMonitor::RequestInfo::RequestInfo(
+ const net::URLRequest& request)
+ : url(request.url()),
+ status(request.status()),
+ response_code(-1),
+ socket_address(request.GetSocketAddress()),
+ was_cached(request.was_cached()) {
+ request.GetLoadTimingInfo(&load_timing_info);
+ // Can't get response code of a canceled request -- there's no transaction.
+ if (status.status() != net::URLRequestStatus::CANCELED)
+ response_code = request.GetResponseCode();
+}
+
+DomainReliabilityMonitor::RequestInfo::~RequestInfo() {}
+
+void DomainReliabilityMonitor::OnRequestLegComplete(
+ const RequestInfo& request) {
+ if (request.was_cached ||
+ request.status.status() == net::URLRequestStatus::CANCELED) {
+ return;
+ }
+
+ std::map<std::string, DomainReliabilityContext*>::iterator it =
+ contexts_.find(request.url.host());
+ if (it == contexts_.end())
+ return;
+ DomainReliabilityContext* context = it->second;
+
+ std::string beacon_status;
+ bool got_status = DomainReliabilityUtil::GetBeaconStatus(
+ request.status.error(),
+ request.response_code,
+ &beacon_status);
+ if (!got_status)
+ return;
+
+ DomainReliabilityBeacon beacon;
+ beacon.status = beacon_status;
+ beacon.chrome_error = request.status.error();
+ beacon.server_ip = request.socket_address.host();
+ beacon.http_response_code = request.response_code;
+ beacon.start_time = request.load_timing_info.request_start;
+ beacon.elapsed = time_->Now() - beacon.start_time;
+ context->AddBeacon(beacon, request.url);
+}
+
+} // namespace domain_reliability
diff --git a/components/domain_reliability/monitor.h b/components/domain_reliability/monitor.h
new file mode 100644
index 0000000..5d189dc
--- /dev/null
+++ b/components/domain_reliability/monitor.h
@@ -0,0 +1,74 @@
+// Copyright 2014 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 COMPONENTS_DOMAIN_RELIABILITY_MONITOR_H_
+#define COMPONENTS_DOMAIN_RELIABILITY_MONITOR_H_
+
+#include <map>
+
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "components/domain_reliability/beacon.h"
+#include "components/domain_reliability/config.h"
+#include "components/domain_reliability/context.h"
+#include "components/domain_reliability/domain_reliability_export.h"
+#include "components/domain_reliability/util.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/load_timing_info.h"
+#include "net/url_request/url_request_status.h"
+
+namespace net {
+class URLRequest;
+}
+
+namespace domain_reliability {
+
+// The top-level per-profile object that measures requests and hands off the
+// measurements to the proper |DomainReliabilityContext|. Referenced by the
+// |ChromeNetworkDelegate|, which calls the On* methods.
+class DOMAIN_RELIABILITY_EXPORT DomainReliabilityMonitor {
+ public:
+ // NB: We don't take a URLRequestContextGetter because we already live on the
+ // I/O thread.
+ DomainReliabilityMonitor();
+ explicit DomainReliabilityMonitor(scoped_ptr<MockableTime> time);
+ ~DomainReliabilityMonitor();
+
+ // Should be called from the profile's NetworkDelegate on the corresponding
+ // events:
+ void OnBeforeRedirect(net::URLRequest* request);
+ void OnCompleted(net::URLRequest* request, bool started);
+
+ // Creates a context for testing, adds it to the monitor, and returns a
+ // pointer to it. (The pointer is only valid until the MOnitor is destroyed.)
+ DomainReliabilityContext* AddContextForTesting(
+ scoped_ptr<const DomainReliabilityConfig> config);
+
+ private:
+ friend class DomainReliabilityMonitorTest;
+
+ struct DOMAIN_RELIABILITY_EXPORT RequestInfo {
+ RequestInfo();
+ RequestInfo(const net::URLRequest& request);
+ ~RequestInfo();
+
+ GURL url;
+ net::URLRequestStatus status;
+ int response_code;
+ net::HostPortPair socket_address;
+ net::LoadTimingInfo load_timing_info;
+ bool was_cached;
+ };
+
+ void OnRequestLegComplete(const RequestInfo& info);
+
+ scoped_ptr<MockableTime> time_;
+ std::map<std::string, DomainReliabilityContext*> contexts_;
+
+ DISALLOW_COPY_AND_ASSIGN(DomainReliabilityMonitor);
+};
+
+} // namespace domain_reliability
+
+#endif // COMPONENTS_DOMAIN_RELIABILITY_MONITOR_H_
diff --git a/components/domain_reliability/monitor_unittest.cc b/components/domain_reliability/monitor_unittest.cc
new file mode 100644
index 0000000..9d15114
--- /dev/null
+++ b/components/domain_reliability/monitor_unittest.cc
@@ -0,0 +1,115 @@
+// Copyright 2014 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 "components/domain_reliability/monitor.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "components/domain_reliability/beacon.h"
+#include "components/domain_reliability/config.h"
+#include "components/domain_reliability/test_util.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "net/url_request/url_request_status.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace domain_reliability {
+
+typedef std::vector<DomainReliabilityBeacon> BeaconVector;
+
+class DomainReliabilityMonitorTest : public testing::Test {
+ protected:
+ typedef DomainReliabilityMonitor::RequestInfo RequestInfo;
+
+ DomainReliabilityMonitorTest()
+ : bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
+ time_(new MockTime()),
+ monitor_(scoped_ptr<MockableTime>(time_)),
+ context_(monitor_.AddContextForTesting(CreateConfig())) {}
+
+ static scoped_ptr<const DomainReliabilityConfig> CreateConfig() {
+ DomainReliabilityConfig* config = new DomainReliabilityConfig();
+ DomainReliabilityConfig::Resource* resource;
+
+ resource = new DomainReliabilityConfig::Resource();
+ resource->name = "always_report";
+ resource->url_patterns.push_back(
+ new std::string("http://example/always_report"));
+ resource->success_sample_rate = 1.0;
+ resource->failure_sample_rate = 1.0;
+ config->resources.push_back(resource);
+
+ resource = new DomainReliabilityConfig::Resource();
+ resource->name = "never_report";
+ resource->url_patterns.push_back(
+ new std::string("http://example/never_report"));
+ resource->success_sample_rate = 0.0;
+ resource->failure_sample_rate = 0.0;
+ config->resources.push_back(resource);
+
+ DomainReliabilityConfig::Collector* collector;
+ collector = new DomainReliabilityConfig::Collector();
+ collector->upload_url = GURL("https://example/upload");
+ config->domain = "example";
+ config->collectors.push_back(collector);
+
+ return scoped_ptr<const DomainReliabilityConfig>(config);
+ }
+
+ RequestInfo MakeRequestInfo() {
+ RequestInfo request;
+ request.status = net::URLRequestStatus();
+ request.response_code = 200;
+ request.was_cached = false;
+ return request;
+ }
+
+ bool CheckNoBeacons(int index) {
+ BeaconVector beacons;
+ int successful, failed;
+ context_->GetQueuedDataForTesting(index, &beacons, &successful, &failed);
+ return beacons.empty() && successful == 0 && failed == 0;
+ }
+
+ void OnRequestLegComplete(const RequestInfo& info) {
+ monitor_.OnRequestLegComplete(info);
+ }
+
+ content::TestBrowserThreadBundle bundle_;
+ MockTime* time_;
+ DomainReliabilityMonitor monitor_;
+ DomainReliabilityContext* context_;
+ DomainReliabilityMonitor::RequestInfo request_;
+};
+
+TEST_F(DomainReliabilityMonitorTest, Create) {
+ EXPECT_TRUE(CheckNoBeacons(0));
+ EXPECT_TRUE(CheckNoBeacons(1));
+}
+
+TEST_F(DomainReliabilityMonitorTest, NoContextRequest) {
+ RequestInfo request = MakeRequestInfo();
+ request.url = GURL("http://no-context/");
+ OnRequestLegComplete(request);
+
+ EXPECT_TRUE(CheckNoBeacons(0));
+ EXPECT_TRUE(CheckNoBeacons(1));
+}
+
+TEST_F(DomainReliabilityMonitorTest, ContextRequest) {
+ RequestInfo request = MakeRequestInfo();
+ request.url = GURL("http://example/always_report");
+ OnRequestLegComplete(request);
+
+ BeaconVector beacons;
+ context_->GetQueuedDataForTesting(0, &beacons, NULL, NULL);
+ EXPECT_EQ(1u, beacons.size());
+ EXPECT_TRUE(CheckNoBeacons(1));
+}
+
+} // namespace domain_reliability
diff --git a/components/domain_reliability/test_util.cc b/components/domain_reliability/test_util.cc
new file mode 100644
index 0000000..08ddd04
--- /dev/null
+++ b/components/domain_reliability/test_util.cc
@@ -0,0 +1,125 @@
+// Copyright 2014 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 "components/domain_reliability/test_util.h"
+
+#include "base/bind.h"
+#include "net/url_request/url_request_status.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace domain_reliability {
+
+namespace {
+
+class MockTimer : public MockableTime::Timer {
+ public:
+ MockTimer(MockTime* time)
+ : time_(time),
+ running_(false),
+ callback_sequence_number_(0),
+ weak_factory_(this) {
+ DCHECK(time);
+ }
+ virtual ~MockTimer() {}
+
+ virtual void Start(const tracked_objects::Location& posted_from,
+ base::TimeDelta delay,
+ const base::Closure& user_task) OVERRIDE {
+ DCHECK(!user_task.is_null());
+
+ if (running_)
+ ++callback_sequence_number_;
+ running_ = true;
+ user_task_ = user_task;
+ time_->AddTask(delay,
+ base::Bind(&MockTimer::OnDelayPassed,
+ weak_factory_.GetWeakPtr(),
+ callback_sequence_number_));
+ }
+
+ virtual void Stop() OVERRIDE {
+ if (running_) {
+ ++callback_sequence_number_;
+ running_ = false;
+ }
+ }
+
+ virtual bool IsRunning() OVERRIDE { return running_; }
+
+ private:
+ void OnDelayPassed(int expected_callback_sequence_number) {
+ if (callback_sequence_number_ != expected_callback_sequence_number)
+ return;
+
+ DCHECK(running_);
+ running_ = false;
+
+ // Grab user task in case it re-entrantly starts the timer again.
+ base::Closure task_to_run = user_task_;
+ user_task_.Reset();
+ task_to_run.Run();
+ }
+
+ MockTime* time_;
+ bool running_;
+ int callback_sequence_number_;
+ base::Closure user_task_;
+ base::WeakPtrFactory<MockTimer> weak_factory_;
+};
+
+} // namespace
+
+TestCallback::TestCallback()
+ : callback_(base::Bind(&TestCallback::OnCalled,
+ base::Unretained(this))),
+ called_(false) {}
+
+TestCallback::~TestCallback() {}
+
+void TestCallback::OnCalled() {
+ EXPECT_FALSE(called_);
+ called_ = true;
+}
+
+MockTime::MockTime()
+ : now_(base::TimeTicks::Now()),
+ epoch_(now_),
+ task_sequence_number_(0) {
+ VLOG(1) << "Creating mock time: T=" << elapsed_sec() << "s";
+}
+
+MockTime::~MockTime() {}
+
+base::TimeTicks MockTime::Now() { return now_; }
+
+scoped_ptr<MockableTime::Timer> MockTime::CreateTimer() {
+ return scoped_ptr<MockableTime::Timer>(new MockTimer(this));
+}
+
+void MockTime::Advance(base::TimeDelta delta) {
+ base::TimeTicks target = now_ + delta;
+
+ while (!tasks_.empty() && tasks_.begin()->first.time <= target) {
+ TaskKey key = tasks_.begin()->first;
+ base::Closure task = tasks_.begin()->second;
+ tasks_.erase(tasks_.begin());
+
+ DCHECK(now_ <= key.time);
+ DCHECK(key.time <= target);
+ now_ = key.time;
+ VLOG(1) << "Advancing mock time: task at T=" << elapsed_sec() << "s";
+
+ task.Run();
+ }
+
+ DCHECK(now_ <= target);
+ now_ = target;
+ VLOG(1) << "Advanced mock time: T=" << elapsed_sec() << "s";
+}
+
+void MockTime::AddTask(base::TimeDelta delay, const base::Closure& task) {
+ tasks_[TaskKey(now_ + delay, task_sequence_number_++)] = task;
+}
+
+} // namespace domain_reliability
diff --git a/components/domain_reliability/test_util.h b/components/domain_reliability/test_util.h
new file mode 100644
index 0000000..c7a1f2d50
--- /dev/null
+++ b/components/domain_reliability/test_util.h
@@ -0,0 +1,90 @@
+// Copyright 2014 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 COMPONENTS_DOMAIN_RELIABILITY_TEST_UTIL_H_
+#define COMPONENTS_DOMAIN_RELIABILITY_TEST_UTIL_H_
+
+#include "base/callback.h"
+#include "components/domain_reliability/monitor.h"
+#include "components/domain_reliability/util.h"
+#include "net/base/host_port_pair.h"
+
+namespace net {
+class URLRequestStatus;
+} // namespace net
+
+namespace domain_reliability {
+
+// A simple test callback that remembers whether it's been called.
+class TestCallback {
+ public:
+ TestCallback();
+ ~TestCallback();
+
+ // Returns a callback that can be called only once.
+ const base::Closure& callback() const { return callback_; }
+ // Returns whether the callback returned by |callback()| has been called.
+ bool called() const { return called_; }
+
+ private:
+ void OnCalled();
+
+ base::Closure callback_;
+ bool called_;
+};
+
+class MockTime : public MockableTime {
+ public:
+ MockTime();
+ // N.B.: Tasks (and therefore Timers) scheduled to run in the future will
+ // never be run if MockTime is destroyed before the mock time is advanced
+ // to their scheduled time.
+ virtual ~MockTime();
+
+ virtual base::TimeTicks Now() OVERRIDE;
+ virtual scoped_ptr<MockableTime::Timer> CreateTimer() OVERRIDE;
+
+ // Pretends that |delta| has passed, and runs tasks that would've happened
+ // during that interval (with |Now()| returning proper values while they
+ // execute!)
+ void Advance(base::TimeDelta delta);
+
+ // Queues |task| to be run after |delay|. (Lighter-weight than mocking an
+ // entire message pump.)
+ void AddTask(base::TimeDelta delay, const base::Closure& task);
+
+ private:
+ // Key used to store tasks in the task map. Includes the time the task should
+ // run and a sequence number to disambiguate tasks with the same time.
+ struct TaskKey {
+ TaskKey(base::TimeTicks time, int sequence_number)
+ : time(time),
+ sequence_number(sequence_number) {}
+
+ base::TimeTicks time;
+ int sequence_number;
+ };
+
+ // Comparator for TaskKey; sorts by time, then by sequence number.
+ struct TaskKeyCompare {
+ bool operator() (const TaskKey& lhs, const TaskKey& rhs) const {
+ return lhs.time < rhs.time ||
+ (lhs.time == rhs.time &&
+ lhs.sequence_number < rhs.sequence_number);
+ }
+ };
+
+ typedef std::map<TaskKey, base::Closure, TaskKeyCompare> TaskMap;
+
+ int elapsed_sec() { return (now_ - epoch_).InSeconds(); }
+
+ base::TimeTicks now_;
+ base::TimeTicks epoch_;
+ int task_sequence_number_;
+ TaskMap tasks_;
+};
+
+} // namespace domain_reliability
+
+#endif // COMPONENTS_DOMAIN_RELIABILITY_TEST_UTIL_H_
diff --git a/components/domain_reliability/util.cc b/components/domain_reliability/util.cc
new file mode 100644
index 0000000..75164d53
--- /dev/null
+++ b/components/domain_reliability/util.cc
@@ -0,0 +1,118 @@
+// Copyright 2014 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 "components/domain_reliability/util.h"
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "net/base/net_errors.h"
+
+namespace domain_reliability {
+
+namespace {
+
+class ActualTimer : public MockableTime::Timer {
+ public:
+ // Initialize base timer with retain_user_info and is_repeating false.
+ ActualTimer() : base_timer_(false, false) {}
+ virtual ~ActualTimer() {}
+
+ virtual void Start(const tracked_objects::Location& posted_from,
+ base::TimeDelta delay,
+ const base::Closure& user_task) OVERRIDE {
+ base_timer_.Start(posted_from, delay, user_task);
+ }
+
+ virtual void Stop() OVERRIDE {
+ base_timer_.Stop();
+ }
+
+ virtual bool IsRunning() OVERRIDE {
+ return base_timer_.IsRunning();
+ }
+
+ private:
+ base::Timer base_timer_;
+};
+
+const struct NetErrorMapping {
+ int net_error;
+ const char* beacon_status;
+} net_error_map[] = {
+ { net::OK, "ok" },
+ { net::ERR_TIMED_OUT, "tcp.connection.timed_out" },
+ { net::ERR_CONNECTION_CLOSED, "tcp.connection.closed" },
+ { net::ERR_CONNECTION_RESET, "tcp.connection.reset" },
+ { net::ERR_CONNECTION_REFUSED, "tcp.connection.refused" },
+ { net::ERR_CONNECTION_ABORTED, "tcp.connection.aborted" },
+ { net::ERR_CONNECTION_FAILED, "tcp.connection.failed" },
+ { net::ERR_NAME_NOT_RESOLVED, "dns" },
+ { net::ERR_SSL_PROTOCOL_ERROR, "ssl.protocol.error" },
+ { net::ERR_ADDRESS_INVALID, "tcp.connection.address_invalid" },
+ { net::ERR_ADDRESS_UNREACHABLE, "tcp.connection.address_unreachable" },
+ { net::ERR_CONNECTION_TIMED_OUT, "tcp.connection.timed_out" },
+ { net::ERR_NAME_RESOLUTION_FAILED, "dns" },
+ { net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN,
+ "ssl.pinned_key_not_in_cert_chain" },
+ { net::ERR_CERT_COMMON_NAME_INVALID, "ssl.cert.name_invalid" },
+ { net::ERR_CERT_DATE_INVALID, "ssl.cert.date_invalid" },
+ { net::ERR_CERT_AUTHORITY_INVALID, "ssl.cert.authority_invalid" },
+ { net::ERR_CERT_REVOKED, "ssl.cert.revoked" },
+ { net::ERR_CERT_INVALID, "ssl.cert.invalid" },
+ { net::ERR_EMPTY_RESPONSE, "http.empty_response" },
+ { net::ERR_SPDY_PING_FAILED, "spdy.ping_failed" },
+ { net::ERR_SPDY_PROTOCOL_ERROR, "spdy.protocol" },
+ { net::ERR_QUIC_PROTOCOL_ERROR, "quic.protocol" },
+ { net::ERR_DNS_MALFORMED_RESPONSE, "dns.protocol" },
+ { net::ERR_DNS_SERVER_FAILED, "dns.server" },
+ { net::ERR_DNS_TIMED_OUT, "dns.timed_out" },
+};
+
+} // namespace
+
+// static
+bool DomainReliabilityUtil::GetBeaconStatus(
+ int net_error,
+ int http_response_code,
+ std::string* beacon_status_out) {
+ if (net_error == net::OK) {
+ if (http_response_code >= 400 && http_response_code < 600)
+ *beacon_status_out = base::StringPrintf("http.%d", http_response_code);
+ else
+ *beacon_status_out = "ok";
+ return true;
+ } else {
+ for (size_t i = 0; i < arraysize(net_error_map); i++) {
+ if (net_error_map[i].net_error == net_error) {
+ *beacon_status_out = net_error_map[i].beacon_status;
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+MockableTime::Timer::~Timer() {}
+
+MockableTime::Timer::Timer() {}
+
+MockableTime::~MockableTime() {}
+
+MockableTime::MockableTime() {}
+
+ActualTime::ActualTime() {}
+
+ActualTime::~ActualTime() {}
+
+base::TimeTicks ActualTime::Now() { return base::TimeTicks::Now(); }
+
+scoped_ptr<MockableTime::Timer> ActualTime::CreateTimer() {
+ return scoped_ptr<MockableTime::Timer>(new ActualTimer());
+}
+
+} // namespace domain_reliability
diff --git a/components/domain_reliability/util.h b/components/domain_reliability/util.h
new file mode 100644
index 0000000..2603d8d
--- /dev/null
+++ b/components/domain_reliability/util.h
@@ -0,0 +1,74 @@
+// Copyright 2014 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 COMPONENTS_DOMAIN_RELIABILITY_UTIL_H_
+#define COMPONENTS_DOMAIN_RELIABILITY_UTIL_H_
+
+#include <map>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/time/time.h"
+#include "base/tracked_objects.h"
+#include "components/domain_reliability/domain_reliability_export.h"
+
+namespace domain_reliability {
+
+class DOMAIN_RELIABILITY_EXPORT DomainReliabilityUtil {
+ public:
+ // Attempts to convert a net error and an HTTP response code into the status
+ // string that should be recorded in a beacon. Returns true if it could.
+ static bool GetBeaconStatus(
+ int net_error,
+ int http_response_code,
+ std::string* beacon_status_out);
+};
+
+// Mockable wrapper around TimeTicks::Now and Timer. Mock version is in
+// test_util.h.
+class DOMAIN_RELIABILITY_EXPORT MockableTime {
+ public:
+ // Mockable wrapper around (a subset of) base::Timer.
+ class DOMAIN_RELIABILITY_EXPORT Timer {
+ public:
+ virtual ~Timer();
+
+ virtual void Start(const tracked_objects::Location& posted_from,
+ base::TimeDelta delay,
+ const base::Closure& user_task) = 0;
+ virtual void Stop() = 0;
+ virtual bool IsRunning() = 0;
+
+ protected:
+ Timer();
+ };
+
+ virtual ~MockableTime();
+
+ // Returns base::TimeTicks::Now() or a mocked version thereof.
+ virtual base::TimeTicks Now() = 0;
+ // Returns a new Timer, or a mocked version thereof.
+ virtual scoped_ptr<MockableTime::Timer> CreateTimer() = 0;
+
+ protected:
+ MockableTime();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockableTime);
+};
+
+// Implementation of MockableTime that passes through to base::TimeTicks::Now()
+// and base::Timer.
+class DOMAIN_RELIABILITY_EXPORT ActualTime : public MockableTime {
+ public:
+ ActualTime();
+ virtual ~ActualTime();
+
+ virtual base::TimeTicks Now() OVERRIDE;
+ virtual scoped_ptr<MockableTime::Timer> CreateTimer() OVERRIDE;
+};
+
+} // namespace domain_reliability
+
+#endif // COMPONENTS_DOMAIN_RELIABILITY_UTIL_H_
diff --git a/components/domain_reliability/util_unittest.cc b/components/domain_reliability/util_unittest.cc
new file mode 100644
index 0000000..3146eda
--- /dev/null
+++ b/components/domain_reliability/util_unittest.cc
@@ -0,0 +1,123 @@
+// Copyright 2014 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 "components/domain_reliability/util.h"
+
+#include "base/bind.h"
+#include "components/domain_reliability/test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::TimeDelta;
+using base::TimeTicks;
+
+namespace domain_reliability {
+
+class DomainReliabilityMockTimeTest : public testing::Test {
+ protected:
+ MockTime time_;
+};
+
+TEST_F(DomainReliabilityMockTimeTest, Null) {
+}
+
+TEST_F(DomainReliabilityMockTimeTest, NowAndAdvance) {
+ const TimeDelta delta = TimeDelta::FromSeconds(1);
+
+ TimeTicks initial = time_.Now();
+ time_.Advance(delta);
+ TimeTicks final = time_.Now();
+ EXPECT_EQ(delta, final - initial);
+}
+
+TEST_F(DomainReliabilityMockTimeTest, AddTask) {
+ const TimeDelta delta = TimeDelta::FromSeconds(1);
+ TestCallback callback;
+
+ time_.AddTask(2 * delta, callback.callback());
+ time_.Advance(delta);
+ EXPECT_FALSE(callback.called());
+ time_.Advance(delta);
+ EXPECT_TRUE(callback.called());
+}
+
+TEST_F(DomainReliabilityMockTimeTest, TimerCreate) {
+ scoped_ptr<MockTime::Timer> timer(time_.CreateTimer());
+}
+
+TEST_F(DomainReliabilityMockTimeTest, TimerIsRunning) {
+ const TimeDelta delta = TimeDelta::FromSeconds(1);
+ TestCallback callback;
+
+ scoped_ptr<MockTime::Timer> timer(time_.CreateTimer());
+ EXPECT_FALSE(timer->IsRunning());
+ timer->Start(FROM_HERE, delta, callback.callback());
+ EXPECT_TRUE(timer->IsRunning());
+ timer->Stop();
+ EXPECT_FALSE(timer->IsRunning());
+}
+
+TEST_F(DomainReliabilityMockTimeTest, TimerGoesOff) {
+ const TimeDelta delta = TimeDelta::FromSeconds(1);
+ TestCallback callback;
+
+ scoped_ptr<MockTime::Timer> timer(time_.CreateTimer());
+
+ timer->Start(FROM_HERE, 2 * delta, callback.callback());
+ time_.Advance(delta);
+ EXPECT_FALSE(callback.called());
+ time_.Advance(delta);
+ EXPECT_TRUE(callback.called());
+}
+
+TEST_F(DomainReliabilityMockTimeTest, TimerStopped) {
+ const TimeDelta delta = TimeDelta::FromSeconds(1);
+ TestCallback callback;
+
+ scoped_ptr<MockTime::Timer> timer(time_.CreateTimer());
+
+ timer->Start(FROM_HERE, 2 * delta, callback.callback());
+ time_.Advance(delta);
+ timer->Stop();
+ time_.Advance(delta);
+ EXPECT_FALSE(callback.called());
+}
+
+TEST_F(DomainReliabilityMockTimeTest, TimerRestarted) {
+ const TimeDelta delta = TimeDelta::FromSeconds(1);
+ TestCallback callback;
+
+ scoped_ptr<MockTime::Timer> timer(time_.CreateTimer());
+
+ timer->Start(FROM_HERE, 2 * delta, callback.callback());
+ time_.Advance(delta);
+ timer->Start(FROM_HERE, 2 * delta, callback.callback());
+ time_.Advance(delta);
+ EXPECT_FALSE(callback.called());
+ time_.Advance(delta);
+ EXPECT_TRUE(callback.called());
+}
+
+TEST_F(DomainReliabilityMockTimeTest, TimerReentrantStart) {
+ const TimeDelta delta = TimeDelta::FromSeconds(1);
+ scoped_ptr<MockTime::Timer> timer(time_.CreateTimer());
+ TestCallback callback;
+
+ timer->Start(
+ FROM_HERE,
+ delta,
+ base::Bind(
+ &MockTime::Timer::Start,
+ base::Unretained(timer.get()),
+ FROM_HERE,
+ delta,
+ callback.callback()));
+ time_.Advance(delta);
+ EXPECT_FALSE(callback.called());
+ EXPECT_TRUE(timer->IsRunning());
+ time_.Advance(delta);
+ EXPECT_TRUE(callback.called());
+ EXPECT_FALSE(timer->IsRunning());
+}
+
+} // namespace domain_reliability