// 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 #include #include #include #include #include #include "base/bind.h" #include "base/test/test_simple_task_runner.h" #include "components/domain_reliability/baked_in_configs.h" #include "components/domain_reliability/beacon.h" #include "components/domain_reliability/config.h" #include "components/domain_reliability/google_configs.h" #include "components/domain_reliability/test_util.h" #include "net/base/host_port_pair.h" #include "net/base/load_flags.h" #include "net/http/http_response_headers.h" #include "net/http/http_util.h" #include "net/url_request/url_request_context_getter.h" #include "net/url_request/url_request_status.h" #include "net/url_request/url_request_test_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace domain_reliability { namespace { typedef std::vector BeaconVector; scoped_refptr MakeHttpResponseHeaders( const std::string& headers) { return scoped_refptr( new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders( headers.c_str(), headers.length()))); } size_t CountQueuedBeacons(DomainReliabilityContext* context) { BeaconVector beacons; context->GetQueuedBeaconsForTesting(&beacons); return beacons.size(); } } // namespace class DomainReliabilityMonitorTest : public testing::Test { protected: typedef DomainReliabilityMonitor::RequestInfo RequestInfo; DomainReliabilityMonitorTest() : pref_task_runner_(new base::TestSimpleTaskRunner()), network_task_runner_(new base::TestSimpleTaskRunner()), url_request_context_getter_( new net::TestURLRequestContextGetter(network_task_runner_)), time_(new MockTime()), monitor_("test-reporter", pref_task_runner_, network_task_runner_, std::unique_ptr(time_)) { monitor_.MoveToNetworkThread(); monitor_.InitURLRequestContext(url_request_context_getter_); monitor_.SetDiscardUploads(false); } ~DomainReliabilityMonitorTest() override { monitor_.Shutdown(); } static RequestInfo MakeRequestInfo() { RequestInfo request; request.status = net::URLRequestStatus(); request.response_info.socket_address = net::HostPortPair::FromString("12.34.56.78:80"); request.response_info.headers = MakeHttpResponseHeaders( "HTTP/1.1 200 OK\n\n"); request.response_info.was_cached = false; request.response_info.network_accessed = true; request.response_info.was_fetched_via_proxy = false; request.load_flags = 0; request.upload_depth = 0; return request; } void OnRequestLegComplete(const RequestInfo& info) { monitor_.OnRequestLegComplete(info); } DomainReliabilityContext* CreateAndAddContext() { return monitor_.AddContextForTesting(MakeTestConfig()); } DomainReliabilityContext* CreateAndAddContextForOrigin(const GURL& origin, bool wildcard) { std::unique_ptr config( MakeTestConfigWithOrigin(origin)); config->include_subdomains = wildcard; return monitor_.AddContextForTesting(std::move(config)); } scoped_refptr pref_task_runner_; scoped_refptr network_task_runner_; scoped_refptr url_request_context_getter_; MockTime* time_; DomainReliabilityMonitor monitor_; DomainReliabilityMonitor::RequestInfo request_; }; namespace { TEST_F(DomainReliabilityMonitorTest, Create) { } TEST_F(DomainReliabilityMonitorTest, NoContext) { DomainReliabilityContext* context = CreateAndAddContext(); RequestInfo request = MakeRequestInfo(); request.url = GURL("http://no-context/"); OnRequestLegComplete(request); EXPECT_EQ(0u, CountQueuedBeacons(context)); } TEST_F(DomainReliabilityMonitorTest, NetworkFailure) { DomainReliabilityContext* context = CreateAndAddContext(); RequestInfo request = MakeRequestInfo(); request.url = GURL("http://example/"); request.status = net::URLRequestStatus::FromError(net::ERR_CONNECTION_RESET); request.response_info.headers = nullptr; OnRequestLegComplete(request); EXPECT_EQ(1u, CountQueuedBeacons(context)); } TEST_F(DomainReliabilityMonitorTest, GoAwayWithPortMigrationDetected) { DomainReliabilityContext* context = CreateAndAddContext(); RequestInfo request = MakeRequestInfo(); request.url = GURL("http://example/"); request.details.quic_port_migration_detected = true; request.response_info.headers = nullptr; OnRequestLegComplete(request); EXPECT_EQ(1u, CountQueuedBeacons(context)); } TEST_F(DomainReliabilityMonitorTest, ServerFailure) { DomainReliabilityContext* context = CreateAndAddContext(); RequestInfo request = MakeRequestInfo(); request.url = GURL("http://example/"); request.response_info.headers = MakeHttpResponseHeaders("HTTP/1.1 500 :(\n\n"); OnRequestLegComplete(request); EXPECT_EQ(1u, CountQueuedBeacons(context)); } // Make sure the monitor does not log requests that did not access the network. TEST_F(DomainReliabilityMonitorTest, DidNotAccessNetwork) { DomainReliabilityContext* context = CreateAndAddContext(); RequestInfo request = MakeRequestInfo(); request.url = GURL("http://example/"); request.response_info.network_accessed = false; OnRequestLegComplete(request); EXPECT_EQ(0u, CountQueuedBeacons(context)); } // Make sure the monitor does not log requests that don't send cookies. TEST_F(DomainReliabilityMonitorTest, DoNotSendCookies) { DomainReliabilityContext* context = CreateAndAddContext(); RequestInfo request = MakeRequestInfo(); request.url = GURL("http://example/"); request.load_flags = net::LOAD_DO_NOT_SEND_COOKIES; OnRequestLegComplete(request); EXPECT_EQ(0u, CountQueuedBeacons(context)); } // Make sure the monitor does not log a network-local error. TEST_F(DomainReliabilityMonitorTest, LocalError) { DomainReliabilityContext* context = CreateAndAddContext(); RequestInfo request = MakeRequestInfo(); request.url = GURL("http://example/"); request.status = net::URLRequestStatus::FromError(net::ERR_PROXY_CONNECTION_FAILED); OnRequestLegComplete(request); EXPECT_EQ(0u, CountQueuedBeacons(context)); } // Make sure the monitor does not log the proxy's IP if one was used. TEST_F(DomainReliabilityMonitorTest, WasFetchedViaProxy) { DomainReliabilityContext* context = CreateAndAddContext(); RequestInfo request = MakeRequestInfo(); request.url = GURL("http://example/"); request.status = net::URLRequestStatus::FromError(net::ERR_CONNECTION_RESET); request.response_info.socket_address = net::HostPortPair::FromString("127.0.0.1:3128"); request.response_info.was_fetched_via_proxy = true; OnRequestLegComplete(request); BeaconVector beacons; context->GetQueuedBeaconsForTesting(&beacons); EXPECT_EQ(1u, beacons.size()); EXPECT_TRUE(beacons[0]->server_ip.empty()); } // Make sure the monitor does not log the cached IP returned after a successful // cache revalidation request. TEST_F(DomainReliabilityMonitorTest, NoCachedIPFromSuccessfulRevalidationRequest) { std::unique_ptr config = MakeTestConfig(); config->success_sample_rate = 1.0; DomainReliabilityContext* context = monitor_.AddContextForTesting(std::move(config)); RequestInfo request = MakeRequestInfo(); request.url = GURL("http://example/"); request.response_info.was_cached = true; OnRequestLegComplete(request); BeaconVector beacons; context->GetQueuedBeaconsForTesting(&beacons); EXPECT_EQ(1u, beacons.size()); EXPECT_TRUE(beacons[0]->server_ip.empty()); } // Make sure the monitor does not log the cached IP returned with a failed // cache revalidation request. TEST_F(DomainReliabilityMonitorTest, NoCachedIPFromFailedRevalidationRequest) { DomainReliabilityContext* context = CreateAndAddContext(); RequestInfo request = MakeRequestInfo(); request.url = GURL("http://example/"); request.response_info.was_cached = true; request.status = net::URLRequestStatus::FromError(net::ERR_NAME_RESOLUTION_FAILED); OnRequestLegComplete(request); BeaconVector beacons; context->GetQueuedBeaconsForTesting(&beacons); EXPECT_EQ(1u, beacons.size()); EXPECT_TRUE(beacons[0]->server_ip.empty()); } TEST_F(DomainReliabilityMonitorTest, AtLeastOneBakedInConfig) { DCHECK(kBakedInJsonConfigs[0] != nullptr); } // Make sure the monitor does log uploads, even though they have // LOAD_DO_NOT_SEND_COOKIES. TEST_F(DomainReliabilityMonitorTest, Upload) { DomainReliabilityContext* context = CreateAndAddContext(); RequestInfo request = MakeRequestInfo(); request.url = GURL("http://example/"); request.load_flags = net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES; request.status = net::URLRequestStatus::FromError(net::ERR_CONNECTION_RESET); request.upload_depth = 1; OnRequestLegComplete(request); EXPECT_EQ(1u, CountQueuedBeacons(context)); } // Will fail when baked-in configs expire, as a reminder to update them. // (Contact juliatuttle@chromium.org if this starts failing.) TEST_F(DomainReliabilityMonitorTest, AddBakedInConfigs) { // AddBakedInConfigs DCHECKs that the baked-in configs parse correctly, so // this unittest will fail if someone tries to add an invalid config to the // source tree. monitor_.AddBakedInConfigs(); // Count the number of baked-in configs. size_t num_baked_in_configs = 0; for (const char* const* p = kBakedInJsonConfigs; *p; ++p) ++num_baked_in_configs; // Also count the Google configs stored in abbreviated form. std::vector> google_configs; GetAllGoogleConfigs(&google_configs); size_t num_google_configs = google_configs.size(); // The monitor should have contexts for all of the baked-in configs. EXPECT_EQ(num_baked_in_configs + num_google_configs, monitor_.contexts_size_for_testing()); } TEST_F(DomainReliabilityMonitorTest, ClearBeacons) { DomainReliabilityContext* context = CreateAndAddContext(); // Initially the monitor should have just the test context, with no beacons. EXPECT_EQ(1u, monitor_.contexts_size_for_testing()); EXPECT_EQ(0u, CountQueuedBeacons(context)); // Add a beacon. RequestInfo request = MakeRequestInfo(); request.url = GURL("http://example/"); request.status = net::URLRequestStatus::FromError(net::ERR_CONNECTION_RESET); OnRequestLegComplete(request); // Make sure it was added. EXPECT_EQ(1u, CountQueuedBeacons(context)); monitor_.ClearBrowsingData( CLEAR_BEACONS, base::Callback()); // Make sure the beacon was cleared, but not the contexts. EXPECT_EQ(1u, monitor_.contexts_size_for_testing()); EXPECT_EQ(0u, CountQueuedBeacons(context)); } TEST_F(DomainReliabilityMonitorTest, ClearBeaconsWithFilter) { // Create two contexts, each with one beacon. GURL origin1("http://example.com/"); GURL origin2("http://example.org/"); DomainReliabilityContext* context1 = CreateAndAddContextForOrigin(origin1, false); RequestInfo request = MakeRequestInfo(); request.url = origin1; request.status = net::URLRequestStatus::FromError(net::ERR_CONNECTION_RESET); OnRequestLegComplete(request); DomainReliabilityContext* context2 = CreateAndAddContextForOrigin(origin2, false); request = MakeRequestInfo(); request.url = origin2; request.status = net::URLRequestStatus::FromError(net::ERR_CONNECTION_RESET); OnRequestLegComplete(request); // Delete the beacons for |origin1|. monitor_.ClearBrowsingData( CLEAR_BEACONS, base::Bind(static_cast(operator==), origin1)); // Beacons for |context1| were cleared. Beacons for |context2| and // the contexts themselves were not. EXPECT_EQ(2u, monitor_.contexts_size_for_testing()); EXPECT_EQ(0u, CountQueuedBeacons(context1)); EXPECT_EQ(1u, CountQueuedBeacons(context2)); } TEST_F(DomainReliabilityMonitorTest, ClearContexts) { CreateAndAddContext(); // Initially the monitor should have just the test context. EXPECT_EQ(1u, monitor_.contexts_size_for_testing()); monitor_.ClearBrowsingData( CLEAR_CONTEXTS, base::Callback()); // Clearing contexts should leave the monitor with none. EXPECT_EQ(0u, monitor_.contexts_size_for_testing()); } TEST_F(DomainReliabilityMonitorTest, ClearContextsWithFilter) { GURL origin1("http://example.com/"); GURL origin2("http://example.org/"); CreateAndAddContextForOrigin(origin1, false); CreateAndAddContextForOrigin(origin2, false); EXPECT_EQ(2u, monitor_.contexts_size_for_testing()); // Delete the contexts for |origin1|. monitor_.ClearBrowsingData( CLEAR_CONTEXTS, base::Bind(static_cast(operator==), origin1)); // Only one of the contexts should have been deleted. EXPECT_EQ(1u, monitor_.contexts_size_for_testing()); } TEST_F(DomainReliabilityMonitorTest, WildcardMatchesSelf) { DomainReliabilityContext* context = CreateAndAddContextForOrigin(GURL("https://wildcard/"), true); RequestInfo request = MakeRequestInfo(); request.url = GURL("http://wildcard/"); request.status = net::URLRequestStatus::FromError(net::ERR_CONNECTION_RESET); OnRequestLegComplete(request); EXPECT_EQ(1u, CountQueuedBeacons(context)); } TEST_F(DomainReliabilityMonitorTest, WildcardMatchesSubdomain) { DomainReliabilityContext* context = CreateAndAddContextForOrigin(GURL("https://wildcard/"), true); RequestInfo request = MakeRequestInfo(); request.url = GURL("http://test.wildcard/"); request.status = net::URLRequestStatus::FromError(net::ERR_CONNECTION_RESET); OnRequestLegComplete(request); EXPECT_EQ(1u, CountQueuedBeacons(context)); } TEST_F(DomainReliabilityMonitorTest, WildcardDoesntMatchSubsubdomain) { DomainReliabilityContext* context = CreateAndAddContextForOrigin(GURL("https://wildcard/"), true); RequestInfo request = MakeRequestInfo(); request.url = GURL("http://test.test.wildcard/"); request.status = net::URLRequestStatus::FromError(net::ERR_CONNECTION_RESET); OnRequestLegComplete(request); EXPECT_EQ(0u, CountQueuedBeacons(context)); } TEST_F(DomainReliabilityMonitorTest, WildcardPrefersSelfToParentWildcard) { DomainReliabilityContext* context1 = CreateAndAddContextForOrigin(GURL("https://test.wildcard/"), false); DomainReliabilityContext* context2 = CreateAndAddContextForOrigin(GURL("https://wildcard/"), true); RequestInfo request = MakeRequestInfo(); request.url = GURL("http://test.wildcard/"); request.status = net::URLRequestStatus::FromError(net::ERR_CONNECTION_RESET); OnRequestLegComplete(request); EXPECT_EQ(1u, CountQueuedBeacons(context1)); EXPECT_EQ(0u, CountQueuedBeacons(context2)); } TEST_F(DomainReliabilityMonitorTest, WildcardPrefersSelfWildcardToParentWildcard) { DomainReliabilityContext* context1 = CreateAndAddContextForOrigin(GURL("https://test.wildcard/"), true); DomainReliabilityContext* context2 = CreateAndAddContextForOrigin(GURL("https://wildcard/"), true); RequestInfo request = MakeRequestInfo(); request.url = GURL("http://test.wildcard/"); request.status = net::URLRequestStatus::FromError(net::ERR_CONNECTION_RESET); OnRequestLegComplete(request); EXPECT_EQ(1u, CountQueuedBeacons(context1)); EXPECT_EQ(0u, CountQueuedBeacons(context2)); } } // namespace } // namespace domain_reliability