diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-05-20 09:47:09 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-06-07 11:15:42 +0000 |
commit | 189d4fd8fad9e3c776873be51938cd31a42b6177 (patch) | |
tree | 6497caeff5e383937996768766ab3bb2081a40b2 /chromium/components/domain_reliability | |
parent | 8bc75099d364490b22f43a7ce366b366c08f4164 (diff) | |
download | qtwebengine-chromium-189d4fd8fad9e3c776873be51938cd31a42b6177.tar.gz |
BASELINE: Update Chromium to 90.0.4430.221
Change-Id: Iff4d9d18d2fcf1a576f3b1f453010f744a232920
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/components/domain_reliability')
19 files changed, 1204 insertions, 72 deletions
diff --git a/chromium/components/domain_reliability/BUILD.gn b/chromium/components/domain_reliability/BUILD.gn index 14ac5738ca9..175e908c274 100644 --- a/chromium/components/domain_reliability/BUILD.gn +++ b/chromium/components/domain_reliability/BUILD.gn @@ -58,6 +58,8 @@ component("domain_reliability") { "dispatcher.cc", "dispatcher.h", "domain_reliability_export.h", + "features.cc", + "features.h", "google_configs.cc", "google_configs.h", "monitor.cc", diff --git a/chromium/components/domain_reliability/DIR_METADATA b/chromium/components/domain_reliability/DIR_METADATA new file mode 100644 index 00000000000..cb1a69b404a --- /dev/null +++ b/chromium/components/domain_reliability/DIR_METADATA @@ -0,0 +1,5 @@ +monorail { + component: "Internals>Network>ReportingAndNEL" +} + +team_email: "net-dev@chromium.org" diff --git a/chromium/components/domain_reliability/OWNERS b/chromium/components/domain_reliability/OWNERS index 6016d30496f..22067875472 100644 --- a/chromium/components/domain_reliability/OWNERS +++ b/chromium/components/domain_reliability/OWNERS @@ -1,5 +1,2 @@ file://net/OWNERS file://net/network_error_logging/OWNERS - -# COMPONENT: Internals>Network>ReportingAndNEL -# TEAM: net-dev@chromium.org diff --git a/chromium/components/domain_reliability/beacon.cc b/chromium/components/domain_reliability/beacon.cc index bcee54997fb..5c7db8a7408 100644 --- a/chromium/components/domain_reliability/beacon.cc +++ b/chromium/components/domain_reliability/beacon.cc @@ -7,6 +7,7 @@ #include <memory> #include <utility> +#include "base/metrics/histogram_functions.h" #include "base/values.h" #include "components/domain_reliability/util.h" #include "net/base/net_errors.h" @@ -16,10 +17,17 @@ namespace domain_reliability { using base::Value; using base::DictionaryValue; -DomainReliabilityBeacon::DomainReliabilityBeacon() {} +DomainReliabilityBeacon::DomainReliabilityBeacon() = default; + DomainReliabilityBeacon::DomainReliabilityBeacon( const DomainReliabilityBeacon& other) = default; -DomainReliabilityBeacon::~DomainReliabilityBeacon() {} + +DomainReliabilityBeacon::~DomainReliabilityBeacon() { + if (outcome != Outcome::kUnknown) { + base::UmaHistogramEnumeration("Net.DomainReliability.BeaconOutcome", + outcome); + } +} std::unique_ptr<Value> DomainReliabilityBeacon::ToValue( base::TimeTicks upload_time, diff --git a/chromium/components/domain_reliability/beacon.h b/chromium/components/domain_reliability/beacon.h index c27d2a1cb59..38701256c01 100644 --- a/chromium/components/domain_reliability/beacon.h +++ b/chromium/components/domain_reliability/beacon.h @@ -11,6 +11,7 @@ #include "base/time/time.h" #include "components/domain_reliability/domain_reliability_export.h" #include "net/base/net_error_details.h" +#include "net/base/network_isolation_key.h" #include "url/gurl.h" namespace base { @@ -26,6 +27,26 @@ struct DOMAIN_RELIABILITY_EXPORT DomainReliabilityBeacon { DomainReliabilityBeacon(const DomainReliabilityBeacon& other); ~DomainReliabilityBeacon(); + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + enum class Outcome { + // Default value. This should not be recorded to the histogram. + kUnknown = 0, + // Successfully uploaded. + kUploaded = 1, + // Removed for being expired. + kExpired = 2, + // Evicted to make room for newer beacons. + kEvicted = 3, + // Deleted for user clearing browsing data. + kCleared = 5, + // Beacon was deleted upon context shutdown. + kContextShutDown = 5, + + // Keep last. + kMaxValue = kContextShutDown, + }; + // Converts the Beacon to JSON format for uploading. Calculates the age // relative to an upload time of |upload_time|. // @@ -44,8 +65,10 @@ struct DOMAIN_RELIABILITY_EXPORT DomainReliabilityBeacon { // The URL that the beacon is reporting on, if included. // The scheme can be non-secure. GURL url; - // The resource name that the beacon is reporting on, if included. - std::string resource; + // The NetworkIsolationKey associated with the request being reported on. Must + // also be used to upload any report. This field does not appear in the + // uploaded report. + net::NetworkIsolationKey network_isolation_key; // Status string (e.g. "ok", "dns.nxdomain", "http.403"). std::string status; // Granular QUIC error string (e.g. "quic.peer_going_away"). @@ -75,6 +98,9 @@ struct DOMAIN_RELIABILITY_EXPORT DomainReliabilityBeacon { // The probability that this request had of being reported ("sample rate"). double sample_rate; + // Records the ultimate outcome of this beacon, for metrics. + Outcome outcome = Outcome::kUnknown; + // Okay to copy and assign. }; diff --git a/chromium/components/domain_reliability/context.cc b/chromium/components/domain_reliability/context.cc index d3084337475..0bd03c41038 100644 --- a/chromium/components/domain_reliability/context.cc +++ b/chromium/components/domain_reliability/context.cc @@ -5,6 +5,7 @@ #include "components/domain_reliability/context.h" #include <algorithm> +#include <limits> #include <utility> #include "base/bind.h" @@ -54,7 +55,11 @@ DomainReliabilityContext::DomainReliabilityContext( last_network_change_time_(last_network_change_time), upload_allowed_callback_(upload_allowed_callback) {} -DomainReliabilityContext::~DomainReliabilityContext() = default; +DomainReliabilityContext::~DomainReliabilityContext() { + for (auto& beacon_ptr : beacons_) { + beacon_ptr->outcome = DomainReliabilityBeacon::Outcome::kContextShutDown; + } +} void DomainReliabilityContext::OnBeacon( std::unique_ptr<DomainReliabilityBeacon> beacon) { @@ -77,6 +82,9 @@ void DomainReliabilityContext::OnBeacon( } void DomainReliabilityContext::ClearBeacons() { + for (auto& beacon_ptr : beacons_) { + beacon_ptr->outcome = DomainReliabilityBeacon::Outcome::kCleared; + } beacons_.clear(); uploading_beacons_size_ = 0; } @@ -131,7 +139,19 @@ void DomainReliabilityContext::StartUpload() { if (beacons_.empty()) return; - MarkUpload(); + // Find the first beacon with an `upload_depth` of at most + // kMaxUploadDepthToSchedule, in preparation to create a report containing all + // beacons with matching NetworkIsolationKeys. + bool found_beacon_to_upload = false; + for (const auto& beacon : beacons_) { + if (beacon->upload_depth <= kMaxUploadDepthToSchedule) { + uploading_beacons_network_isolation_key_ = beacon->network_isolation_key; + found_beacon_to_upload = true; + break; + } + } + if (!found_beacon_to_upload) + return; size_t collector_index = scheduler_.OnUploadStart(); const GURL& collector_url = *config().collectors[collector_index]; @@ -150,6 +170,7 @@ void DomainReliabilityContext::StartUpload() { uploader_->UploadReport( report_json, max_upload_depth, collector_url, + uploading_beacons_network_isolation_key_, base::BindOnce(&DomainReliabilityContext::OnUploadComplete, weak_factory_.GetWeakPtr())); } @@ -164,24 +185,42 @@ void DomainReliabilityContext::OnUploadComplete( DCHECK(!upload_time_.is_null()); last_upload_time_ = upload_time_; upload_time_ = base::TimeTicks(); + + // If there are pending beacons with a low enough depth, inform the scheduler + // - it's possible only some beacons were added because of NetworkIsolationKey + // mismatches, rather than due to new beacons being created. + if (GetMinBeaconUploadDepth() <= kMaxUploadDepthToSchedule) + scheduler_.OnBeaconAdded(); } std::unique_ptr<const Value> DomainReliabilityContext::CreateReport( base::TimeTicks upload_time, const GURL& collector_url, - int* max_upload_depth_out) const { + int* max_upload_depth_out) { + DCHECK_GT(beacons_.size(), 0u); + DCHECK_EQ(0u, uploading_beacons_size_); + int max_upload_depth = 0; std::unique_ptr<ListValue> beacons_value(new ListValue()); for (const auto& beacon : beacons_) { + // Only include beacons with a matching NetworkIsolationKey in the report. + if (beacon->network_isolation_key != + uploading_beacons_network_isolation_key_) { + continue; + } + beacons_value->Append(beacon->ToValue(upload_time, *last_network_change_time_, collector_url, config().path_prefixes)); if (beacon->upload_depth > max_upload_depth) max_upload_depth = beacon->upload_depth; + ++uploading_beacons_size_; } + DCHECK_GT(uploading_beacons_size_, 0u); + std::unique_ptr<DictionaryValue> report_value(new DictionaryValue()); report_value->SetString("reporter", upload_reporter_string_); report_value->Set("entries", std::move(beacons_value)); @@ -190,22 +229,23 @@ std::unique_ptr<const Value> DomainReliabilityContext::CreateReport( return std::move(report_value); } -void DomainReliabilityContext::MarkUpload() { - DCHECK_EQ(0u, uploading_beacons_size_); - uploading_beacons_size_ = beacons_.size(); - DCHECK_NE(0u, uploading_beacons_size_); -} - void DomainReliabilityContext::CommitUpload() { - auto begin = beacons_.begin(); - auto end = begin + uploading_beacons_size_; - beacons_.erase(begin, end); - DCHECK_NE(0u, uploading_beacons_size_); - uploading_beacons_size_ = 0; + auto current = beacons_.begin(); + while (uploading_beacons_size_ > 0) { + DCHECK(current != beacons_.end()); + + auto last = current; + ++current; + if ((*last)->network_isolation_key == + uploading_beacons_network_isolation_key_) { + (*last)->outcome = DomainReliabilityBeacon::Outcome::kUploaded; + beacons_.erase(last); + --uploading_beacons_size_; + } + } } void DomainReliabilityContext::RollbackUpload() { - DCHECK_NE(0u, uploading_beacons_size_); uploading_beacons_size_ = 0; } @@ -215,19 +255,35 @@ void DomainReliabilityContext::RemoveOldestBeacon() { DVLOG(1) << "Beacon queue for " << config().origin << " full; " << "removing oldest beacon"; - beacons_.pop_front(); - - // If that just removed a beacon counted in uploading_beacons_size_, decrement - // that. - if (uploading_beacons_size_ > 0) + // If the beacon being removed has a NetworkIsolationKey that matches that of + // the current upload, decrement |uploading_beacons_size_|. + if (uploading_beacons_size_ > 0 && + beacons_.front()->network_isolation_key == + uploading_beacons_network_isolation_key_) { --uploading_beacons_size_; + } + + beacons_.front()->outcome = DomainReliabilityBeacon::Outcome::kEvicted; + beacons_.pop_front(); } void DomainReliabilityContext::RemoveExpiredBeacons() { base::TimeTicks now = time_->NowTicks(); const base::TimeDelta kMaxAge = base::TimeDelta::FromHours(1); - while (!beacons_.empty() && now - beacons_.front()->start_time >= kMaxAge) + while (!beacons_.empty() && now - beacons_.front()->start_time >= kMaxAge) { + beacons_.front()->outcome = DomainReliabilityBeacon::Outcome::kExpired; beacons_.pop_front(); + } +} + +// Gets the minimum depth of all entries in |beacons_|. +int DomainReliabilityContext::GetMinBeaconUploadDepth() const { + int min = std::numeric_limits<int>::max(); + for (const auto& beacon : beacons_) { + if (beacon->upload_depth < min) + min = beacon->upload_depth; + } + return min; } } // namespace domain_reliability diff --git a/chromium/components/domain_reliability/context.h b/chromium/components/domain_reliability/context.h index 13da8e391a3..5f10fbf77d9 100644 --- a/chromium/components/domain_reliability/context.h +++ b/chromium/components/domain_reliability/context.h @@ -7,10 +7,10 @@ #include <stddef.h> +#include <list> #include <memory> #include <vector> -#include "base/containers/circular_deque.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/time/time.h" @@ -19,6 +19,7 @@ #include "components/domain_reliability/domain_reliability_export.h" #include "components/domain_reliability/scheduler.h" #include "components/domain_reliability/uploader.h" +#include "net/base/network_isolation_key.h" class GURL; @@ -68,7 +69,7 @@ class DOMAIN_RELIABILITY_EXPORT DomainReliabilityContext { // debugging purposes. std::unique_ptr<base::Value> GetWebUIData() const; - // Gets the beacons queued for upload in this context. |*beacons_out| will be + // Gets the beacons queued for upload in this context. `*beacons_out` will be // cleared and filled with pointers to the beacons; the pointers remain valid // as long as no other requests are reported to the DomainReliabilityMonitor. void GetQueuedBeaconsForTesting( @@ -87,17 +88,14 @@ class DOMAIN_RELIABILITY_EXPORT DomainReliabilityContext { void StartUpload(); void OnUploadComplete(const DomainReliabilityUploader::UploadResult& result); - std::unique_ptr<const base::Value> CreateReport( - base::TimeTicks upload_time, - const GURL& collector_url, - int* max_beacon_depth_out) const; + // Creates a report from all beacons associated with + // `uploading_beacons_network_isolation_key_`. Updates + // `uploading_beacons_size_`. + std::unique_ptr<const base::Value> CreateReport(base::TimeTicks upload_time, + const GURL& collector_url, + int* max_beacon_depth_out); - // Remembers the current state of the context when an upload starts. Can be - // called multiple times in a row (without |CommitUpload|) if uploads fail - // and are retried. - void MarkUpload(); - - // Uses the state remembered by |MarkUpload| to remove successfully uploaded + // Uses the state remembered by `MarkUpload` to remove successfully uploaded // data but keep beacons and request counts added after the upload started. void CommitUpload(); @@ -109,6 +107,9 @@ class DOMAIN_RELIABILITY_EXPORT DomainReliabilityContext { void RemoveExpiredBeacons(); + // Gets the minimum upload depth of all entries in |beacons_|. + int GetMinBeaconUploadDepth() const; + std::unique_ptr<const DomainReliabilityConfig> config_; const MockableTime* time_; const std::string& upload_reporter_string_; @@ -116,8 +117,16 @@ class DOMAIN_RELIABILITY_EXPORT DomainReliabilityContext { DomainReliabilityDispatcher* dispatcher_; DomainReliabilityUploader* uploader_; - base::circular_deque<std::unique_ptr<DomainReliabilityBeacon>> beacons_; + std::list<std::unique_ptr<DomainReliabilityBeacon>> beacons_; + size_t uploading_beacons_size_; + // The NetworkIsolationKey associated with the beacons being uploaded. The + // first `uploading_beacons_size_` beacons that have NIK equal to + // `uploading_beacons_network_isolation_key_` are currently being uploaded. + // It's possible for this number to be 0 when there's still an active upload + // if all currently uploading beacons have been evicted. + net::NetworkIsolationKey uploading_beacons_network_isolation_key_; + base::TimeTicks upload_time_; base::TimeTicks last_upload_time_; // The last network change time is not tracked per-context, so this is a diff --git a/chromium/components/domain_reliability/context_unittest.cc b/chromium/components/domain_reliability/context_unittest.cc index 5506f286467..7a9bd45aeeb 100644 --- a/chromium/components/domain_reliability/context_unittest.cc +++ b/chromium/components/domain_reliability/context_unittest.cc @@ -14,6 +14,8 @@ #include "base/bind.h" #include "base/json/json_reader.h" #include "base/strings/string_piece.h" +#include "base/strings/stringprintf.h" +#include "base/test/metrics/histogram_tester.h" #include "components/domain_reliability/beacon.h" #include "components/domain_reliability/dispatcher.h" #include "components/domain_reliability/scheduler.h" @@ -32,6 +34,8 @@ using base::Value; typedef std::vector<const DomainReliabilityBeacon*> BeaconVector; +const char kBeaconOutcomeHistogram[] = "Net.DomainReliability.BeaconOutcome"; + std::unique_ptr<DomainReliabilityBeacon> MakeCustomizedBeacon( MockableTime* time, std::string status, @@ -53,11 +57,32 @@ std::unique_ptr<DomainReliabilityBeacon> MakeCustomizedBeacon( beacon->start_time = time->NowTicks() - beacon->elapsed; beacon->upload_depth = 0; beacon->sample_rate = 1.0; + beacon->network_isolation_key = net::NetworkIsolationKey(); return beacon; } std::unique_ptr<DomainReliabilityBeacon> MakeBeacon(MockableTime* time) { - return MakeCustomizedBeacon(time, "tcp.connection_reset", "", false); + return MakeCustomizedBeacon(time, "tcp.connection_reset" /* status */, + "" /* quic_error */, + false /* quic_port_migration_detected */); +} + +std::unique_ptr<DomainReliabilityBeacon> MakeBeaconWithNetworkIsolationKey( + MockableTime* time, + const std::string& status, + const net::NetworkIsolationKey& network_isolation_key) { + std::unique_ptr<DomainReliabilityBeacon> beacon = + MakeCustomizedBeacon(time, status, "" /* quic_error */, + false /* quic_port_migration_detected */); + beacon->network_isolation_key = network_isolation_key; + return beacon; +} + +// Create a status string from in integer. For eviction tests. Include string +// values before and after the string representation of the integer, to make +// sure only exact matches are found when searching a JSON string. +std::string StatusFromInt(int i) { + return base::StringPrintf("status%i.test", i); } template <typename ValueType, @@ -98,8 +123,9 @@ class DomainReliabilityContextTest : public testing::Test { : last_network_change_time_(time_.NowTicks()), dispatcher_(&time_), params_(MakeTestSchedulerParams()), - uploader_(base::BindOnce(&DomainReliabilityContextTest::OnUploadRequest, - base::Unretained(this))), + uploader_( + base::BindRepeating(&DomainReliabilityContextTest::OnUploadRequest, + base::Unretained(this))), upload_reporter_string_("test-reporter"), upload_allowed_callback_(base::BindRepeating( &DomainReliabilityContextTest::UploadAllowedCallback, @@ -117,6 +143,8 @@ class DomainReliabilityContextTest : public testing::Test { upload_allowed_callback_, &dispatcher_, &uploader_, std::move(config))); } + void ShutDownContext() { context_.reset(); } + base::TimeDelta min_delay() const { return params_.minimum_upload_delay; } base::TimeDelta max_delay() const { return params_.maximum_upload_delay; } base::TimeDelta retry_interval() const { @@ -147,10 +175,17 @@ class DomainReliabilityContextTest : public testing::Test { return upload_url_; } + const net::NetworkIsolationKey& upload_network_isolation_key() const { + EXPECT_TRUE(upload_pending_); + return upload_network_isolation_key_; + } + void CallUploadCallback(DomainReliabilityUploader::UploadResult result) { ASSERT_TRUE(upload_pending_); std::move(upload_callback_).Run(result); upload_pending_ = false; + ++num_uploads_completed_; + EXPECT_EQ(num_uploads_completed_, num_uploads_); } bool CheckNoBeacons() { @@ -179,13 +214,17 @@ class DomainReliabilityContextTest : public testing::Test { void OnUploadRequest(const std::string& report_json, int max_upload_depth, const GURL& upload_url, + const net::NetworkIsolationKey& network_isolation_key, DomainReliabilityUploader::UploadCallback callback) { + EXPECT_EQ(num_uploads_completed_, num_uploads_); ASSERT_FALSE(upload_pending_); upload_report_ = report_json; upload_max_depth_ = max_upload_depth; upload_url_ = upload_url; + upload_network_isolation_key_ = network_isolation_key; upload_callback_ = std::move(callback); upload_pending_ = true; + ++num_uploads_; } void UploadAllowedCallback(const GURL& origin, @@ -194,10 +233,14 @@ class DomainReliabilityContextTest : public testing::Test { upload_allowed_result_callback_ = std::move(callback); } + int num_uploads_ = 0; + int num_uploads_completed_ = 0; + bool upload_pending_; std::string upload_report_; int upload_max_depth_; GURL upload_url_; + net::NetworkIsolationKey upload_network_isolation_key_; DomainReliabilityUploader::UploadCallback upload_callback_; GURL upload_allowed_origin_; @@ -209,13 +252,20 @@ TEST_F(DomainReliabilityContextTest, Create) { EXPECT_TRUE(CheckNoBeacons()); } -TEST_F(DomainReliabilityContextTest, Report) { +TEST_F(DomainReliabilityContextTest, QueueBeacon) { + base::HistogramTester histograms; InitContext(MakeTestConfig()); context_->OnBeacon(MakeBeacon(&time_)); BeaconVector beacons; context_->GetQueuedBeaconsForTesting(&beacons); EXPECT_EQ(1u, beacons.size()); + + ShutDownContext(); + histograms.ExpectBucketCount( + kBeaconOutcomeHistogram, + DomainReliabilityBeacon::Outcome::kContextShutDown, 1); + histograms.ExpectTotalCount(kBeaconOutcomeHistogram, 1); } TEST_F(DomainReliabilityContextTest, MaxNestedBeaconSchedules) { @@ -251,6 +301,7 @@ TEST_F(DomainReliabilityContextTest, OverlyNestedBeaconDoesNotSchedule) { TEST_F(DomainReliabilityContextTest, MaxNestedBeaconAfterOverlyNestedBeaconSchedules) { + base::HistogramTester histograms; InitContext(MakeTestConfig()); // Add a beacon for a report that's too nested to schedule a beacon. std::unique_ptr<DomainReliabilityBeacon> beacon = MakeBeacon(&time_); @@ -284,10 +335,17 @@ TEST_F(DomainReliabilityContextTest, result.status = DomainReliabilityUploader::UploadResult::SUCCESS; CallUploadCallback(result); + histograms.ExpectBucketCount(kBeaconOutcomeHistogram, + DomainReliabilityBeacon::Outcome::kUploaded, 2); + histograms.ExpectTotalCount(kBeaconOutcomeHistogram, 2); + EXPECT_TRUE(CheckNoBeacons()); + ShutDownContext(); + histograms.ExpectTotalCount(kBeaconOutcomeHistogram, 2); } TEST_F(DomainReliabilityContextTest, ReportUpload) { + base::HistogramTester histograms; InitContext(MakeTestConfig()); context_->OnBeacon( MakeCustomizedBeacon(&time_, "tcp.connection_reset", "", true)); @@ -326,9 +384,276 @@ TEST_F(DomainReliabilityContextTest, ReportUpload) { result.status = DomainReliabilityUploader::UploadResult::SUCCESS; CallUploadCallback(result); + histograms.ExpectBucketCount(kBeaconOutcomeHistogram, + DomainReliabilityBeacon::Outcome::kUploaded, 1); + histograms.ExpectTotalCount(kBeaconOutcomeHistogram, 1); + + EXPECT_TRUE(CheckNoBeacons()); + ShutDownContext(); + histograms.ExpectTotalCount(kBeaconOutcomeHistogram, 1); +} + +TEST_F(DomainReliabilityContextTest, ReportUploadFails) { + InitContext(MakeTestConfig()); + context_->OnBeacon( + MakeCustomizedBeacon(&time_, "tcp.connection_reset", "", true)); + + BeaconVector beacons; + context_->GetQueuedBeaconsForTesting(&beacons); + EXPECT_EQ(1u, beacons.size()); + + time_.Advance(max_delay()); + EXPECT_TRUE(upload_allowed_callback_pending()); + CallUploadAllowedResultCallback(true); + EXPECT_TRUE(upload_pending()); + EXPECT_EQ(0, upload_max_depth()); + EXPECT_EQ(GURL("https://exampleuploader/upload"), upload_url()); + + // The upload fails. + DomainReliabilityUploader::UploadResult result; + result.status = DomainReliabilityUploader::UploadResult::FAILURE; + CallUploadCallback(result); + + // The beacon should still be pending. + beacons.clear(); + context_->GetQueuedBeaconsForTesting(&beacons); + EXPECT_EQ(1u, beacons.size()); + + // Another upload should be queued. + time_.Advance(max_delay()); + EXPECT_TRUE(upload_allowed_callback_pending()); +} + +// Make sure that requests with only one NetworkIsolationKey are uploaded at a +// time, in FIFO order. +TEST_F(DomainReliabilityContextTest, ReportUploadNetworkIsolationKey) { + const net::NetworkIsolationKey kNetworkIsolationKey1 = + net::NetworkIsolationKey::CreateTransient(); + const net::NetworkIsolationKey kNetworkIsolationKey2 = + net::NetworkIsolationKey::CreateTransient(); + const net::NetworkIsolationKey kNetworkIsolationKey3 = + net::NetworkIsolationKey::CreateTransient(); + + InitContext(MakeTestConfig()); + + // Three beacons with kNetworkIsolationKey1, two with kNetworkIsolationKey2, + // and one with kNetworkIsolationKey3. Have beacons with the same key both + // adjacent to each other, and separated by beacons with other keys. Give + // each a unique status, so it's easy to check which beacons are included in + // each report. + const char kStatusNik11[] = "nik1.status1"; + const char kStatusNik12[] = "nik1.status2"; + const char kStatusNik13[] = "nik1.status3"; + const char kStatusNik21[] = "nik2.status1"; + const char kStatusNik22[] = "nik2.status2"; + const char kStatusNik31[] = "nik3.status1"; + context_->OnBeacon(MakeBeaconWithNetworkIsolationKey(&time_, kStatusNik11, + kNetworkIsolationKey1)); + context_->OnBeacon(MakeBeaconWithNetworkIsolationKey(&time_, kStatusNik12, + kNetworkIsolationKey1)); + context_->OnBeacon(MakeBeaconWithNetworkIsolationKey(&time_, kStatusNik21, + kNetworkIsolationKey2)); + context_->OnBeacon(MakeBeaconWithNetworkIsolationKey(&time_, kStatusNik31, + kNetworkIsolationKey3)); + context_->OnBeacon(MakeBeaconWithNetworkIsolationKey(&time_, kStatusNik13, + kNetworkIsolationKey1)); + context_->OnBeacon(MakeBeaconWithNetworkIsolationKey(&time_, kStatusNik22, + kNetworkIsolationKey2)); + + // All the beacons should be queued, in FIFO order. + BeaconVector beacons; + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(6u, beacons.size()); + EXPECT_EQ(kNetworkIsolationKey1, beacons[0]->network_isolation_key); + EXPECT_EQ(kStatusNik11, beacons[0]->status); + EXPECT_EQ(kNetworkIsolationKey1, beacons[1]->network_isolation_key); + EXPECT_EQ(kStatusNik12, beacons[1]->status); + EXPECT_EQ(kNetworkIsolationKey2, beacons[2]->network_isolation_key); + EXPECT_EQ(kStatusNik21, beacons[2]->status); + EXPECT_EQ(kNetworkIsolationKey3, beacons[3]->network_isolation_key); + EXPECT_EQ(kStatusNik31, beacons[3]->status); + EXPECT_EQ(kNetworkIsolationKey1, beacons[4]->network_isolation_key); + EXPECT_EQ(kStatusNik13, beacons[4]->status); + EXPECT_EQ(kNetworkIsolationKey2, beacons[5]->network_isolation_key); + EXPECT_EQ(kStatusNik22, beacons[5]->status); + + // Wait for the report to start being uploaded. + time_.Advance(max_delay()); + EXPECT_TRUE(upload_allowed_callback_pending()); + CallUploadAllowedResultCallback(true); + EXPECT_TRUE(upload_pending()); + EXPECT_EQ(0, upload_max_depth()); + EXPECT_EQ(GURL("https://exampleuploader/upload"), upload_url()); + EXPECT_EQ(kNetworkIsolationKey1, upload_network_isolation_key()); + + // Check that only the strings associated with the first NIK are present in + // the report. + EXPECT_NE(upload_report().find(kStatusNik11), std::string::npos); + EXPECT_NE(upload_report().find(kStatusNik12), std::string::npos); + EXPECT_NE(upload_report().find(kStatusNik13), std::string::npos); + EXPECT_EQ(upload_report().find(kStatusNik21), std::string::npos); + EXPECT_EQ(upload_report().find(kStatusNik22), std::string::npos); + EXPECT_EQ(upload_report().find(kStatusNik31), std::string::npos); + + // Complete upload. + DomainReliabilityUploader::UploadResult successful_result; + successful_result.status = DomainReliabilityUploader::UploadResult::SUCCESS; + CallUploadCallback(successful_result); + + // There should still be 3 beacons queued, in the same order as before. + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(3u, beacons.size()); + EXPECT_EQ(kNetworkIsolationKey2, beacons[0]->network_isolation_key); + EXPECT_EQ(kStatusNik21, beacons[0]->status); + EXPECT_EQ(kNetworkIsolationKey3, beacons[1]->network_isolation_key); + EXPECT_EQ(kStatusNik31, beacons[1]->status); + EXPECT_EQ(kNetworkIsolationKey2, beacons[2]->network_isolation_key); + EXPECT_EQ(kStatusNik22, beacons[2]->status); + + // The next upload should automatically trigger. + time_.Advance(max_delay()); + EXPECT_TRUE(upload_allowed_callback_pending()); + CallUploadAllowedResultCallback(true); + EXPECT_TRUE(upload_pending()); + EXPECT_EQ(0, upload_max_depth()); + EXPECT_EQ(GURL("https://exampleuploader/upload"), upload_url()); + EXPECT_EQ(kNetworkIsolationKey2, upload_network_isolation_key()); + + // Check that only the strings associated with the second NIK are present in + // the report. + EXPECT_EQ(upload_report().find(kStatusNik11), std::string::npos); + EXPECT_EQ(upload_report().find(kStatusNik12), std::string::npos); + EXPECT_EQ(upload_report().find(kStatusNik13), std::string::npos); + EXPECT_NE(upload_report().find(kStatusNik21), std::string::npos); + EXPECT_NE(upload_report().find(kStatusNik22), std::string::npos); + EXPECT_EQ(upload_report().find(kStatusNik31), std::string::npos); + // Complete upload. + CallUploadCallback(successful_result); + + // There should still be 1 beacon queued. + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(1u, beacons.size()); + EXPECT_EQ(kNetworkIsolationKey3, beacons[0]->network_isolation_key); + EXPECT_EQ(kStatusNik31, beacons[0]->status); + + // The next upload should automatically trigger. + time_.Advance(max_delay()); + EXPECT_TRUE(upload_allowed_callback_pending()); + CallUploadAllowedResultCallback(true); + EXPECT_TRUE(upload_pending()); + EXPECT_EQ(0, upload_max_depth()); + EXPECT_EQ(GURL("https://exampleuploader/upload"), upload_url()); + EXPECT_EQ(kNetworkIsolationKey3, upload_network_isolation_key()); + + // Check that only the strings associated with the third NIK are present in + // the report. + EXPECT_EQ(upload_report().find(kStatusNik11), std::string::npos); + EXPECT_EQ(upload_report().find(kStatusNik12), std::string::npos); + EXPECT_EQ(upload_report().find(kStatusNik13), std::string::npos); + EXPECT_EQ(upload_report().find(kStatusNik21), std::string::npos); + EXPECT_EQ(upload_report().find(kStatusNik22), std::string::npos); + EXPECT_NE(upload_report().find(kStatusNik31), std::string::npos); + // Complete upload. + CallUploadCallback(successful_result); + EXPECT_TRUE(CheckNoBeacons()); } +// Make sure that kMaxUploadDepthToSchedule is respected when requests have +// different NetworkIsolationKeys. +TEST_F(DomainReliabilityContextTest, ReportUploadDepthNetworkIsolationKey) { + const net::NetworkIsolationKey kNetworkIsolationKey1 = + net::NetworkIsolationKey::CreateTransient(); + const net::NetworkIsolationKey kNetworkIsolationKey2 = + net::NetworkIsolationKey::CreateTransient(); + + InitContext(MakeTestConfig()); + + const char kStatusNik1ExceedsMaxDepth[] = "nik1.exceeds_max_depth"; + const char kStatusNik2ExceedsMaxDepth[] = "nik2.exceeds_max_depth"; + const char kStatusNik2MaxDepth[] = "nik2.max_depth"; + + // Add a beacon with kNetworkIsolationKey1 and a depth that exceeds the max + // depth to trigger an upload. No upload should be queued. + std::unique_ptr<DomainReliabilityBeacon> beacon = + MakeBeaconWithNetworkIsolationKey(&time_, kStatusNik1ExceedsMaxDepth, + kNetworkIsolationKey1); + beacon->upload_depth = + DomainReliabilityContext::kMaxUploadDepthToSchedule + 1; + context_->OnBeacon(std::move(beacon)); + time_.Advance(max_delay()); + EXPECT_FALSE(upload_allowed_callback_pending()); + + // Add a beacon with kNetworkIsolationKey2 and a depth that exceeds the max + // depth to trigger an upload. No upload should be queued. + beacon = MakeBeaconWithNetworkIsolationKey(&time_, kStatusNik2ExceedsMaxDepth, + kNetworkIsolationKey2); + beacon->upload_depth = + DomainReliabilityContext::kMaxUploadDepthToSchedule + 1; + context_->OnBeacon(std::move(beacon)); + time_.Advance(max_delay()); + EXPECT_FALSE(upload_allowed_callback_pending()); + + // Add a beacon with kNetworkIsolationKey2 and a depth that equals the max + // depth to trigger an upload. An upload should be queued. + beacon = MakeBeaconWithNetworkIsolationKey(&time_, kStatusNik2MaxDepth, + kNetworkIsolationKey2); + beacon->upload_depth = DomainReliabilityContext::kMaxUploadDepthToSchedule; + context_->OnBeacon(std::move(beacon)); + time_.Advance(max_delay()); + EXPECT_TRUE(upload_allowed_callback_pending()); + + // All the beacons should still be queued. + BeaconVector beacons; + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(3u, beacons.size()); + EXPECT_EQ(kNetworkIsolationKey1, beacons[0]->network_isolation_key); + EXPECT_EQ(kStatusNik1ExceedsMaxDepth, beacons[0]->status); + EXPECT_EQ(DomainReliabilityContext::kMaxUploadDepthToSchedule + 1, + beacons[0]->upload_depth); + EXPECT_EQ(kNetworkIsolationKey2, beacons[1]->network_isolation_key); + EXPECT_EQ(kStatusNik2ExceedsMaxDepth, beacons[1]->status); + EXPECT_EQ(DomainReliabilityContext::kMaxUploadDepthToSchedule + 1, + beacons[1]->upload_depth); + EXPECT_EQ(kNetworkIsolationKey2, beacons[2]->network_isolation_key); + EXPECT_EQ(kStatusNik2MaxDepth, beacons[2]->status); + EXPECT_EQ(DomainReliabilityContext::kMaxUploadDepthToSchedule, + beacons[2]->upload_depth); + + // Start the upload. + CallUploadAllowedResultCallback(true); + EXPECT_TRUE(upload_pending()); + EXPECT_EQ(DomainReliabilityContext::kMaxUploadDepthToSchedule + 1, + upload_max_depth()); + EXPECT_EQ(GURL("https://exampleuploader/upload"), upload_url()); + EXPECT_EQ(kNetworkIsolationKey2, upload_network_isolation_key()); + + // Check that only the strings associated with the second NIK are present in + // the report. + EXPECT_EQ(upload_report().find(kStatusNik1ExceedsMaxDepth), + std::string::npos); + EXPECT_NE(upload_report().find(kStatusNik2ExceedsMaxDepth), + std::string::npos); + EXPECT_NE(upload_report().find(kStatusNik2MaxDepth), std::string::npos); + + // Complete upload. + DomainReliabilityUploader::UploadResult successful_result; + successful_result.status = DomainReliabilityUploader::UploadResult::SUCCESS; + CallUploadCallback(successful_result); + + // There should still be 1 beacon queued. + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(1u, beacons.size()); + EXPECT_EQ(kNetworkIsolationKey1, beacons[0]->network_isolation_key); + EXPECT_EQ(kStatusNik1ExceedsMaxDepth, beacons[0]->status); + EXPECT_EQ(DomainReliabilityContext::kMaxUploadDepthToSchedule + 1, + beacons[0]->upload_depth); + + // No upload should be queued, since the depth is too high. + time_.Advance(max_delay()); + EXPECT_FALSE(upload_allowed_callback_pending()); +} + TEST_F(DomainReliabilityContextTest, UploadForbidden) { InitContext(MakeTestConfig()); context_->OnBeacon( @@ -601,6 +926,7 @@ TEST_F(DomainReliabilityContextTest, SampleNoBeacons) { } TEST_F(DomainReliabilityContextTest, ExpiredBeaconDoesNotUpload) { + base::HistogramTester histograms; InitContext(MakeTestConfig()); std::unique_ptr<DomainReliabilityBeacon> beacon = MakeBeacon(&time_); time_.Advance(base::TimeDelta::FromHours(2)); @@ -611,6 +937,451 @@ TEST_F(DomainReliabilityContextTest, ExpiredBeaconDoesNotUpload) { BeaconVector beacons; context_->GetQueuedBeaconsForTesting(&beacons); EXPECT_TRUE(beacons.empty()); + + histograms.ExpectBucketCount(kBeaconOutcomeHistogram, + DomainReliabilityBeacon::Outcome::kExpired, 1); + histograms.ExpectTotalCount(kBeaconOutcomeHistogram, 1); + + ShutDownContext(); + histograms.ExpectTotalCount(kBeaconOutcomeHistogram, 1); +} + +TEST_F(DomainReliabilityContextTest, EvictOldestBeacon) { + base::HistogramTester histograms; + InitContext(MakeTestConfig()); + + std::unique_ptr<DomainReliabilityBeacon> oldest_beacon = MakeBeacon(&time_); + const DomainReliabilityBeacon* oldest_beacon_ptr = oldest_beacon.get(); + time_.Advance(base::TimeDelta::FromSeconds(1)); + context_->OnBeacon(std::move(oldest_beacon)); + + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + std::unique_ptr<DomainReliabilityBeacon> beacon = MakeBeacon(&time_); + time_.Advance(base::TimeDelta::FromSeconds(1)); + context_->OnBeacon(std::move(beacon)); + } + + BeaconVector beacons; + context_->GetQueuedBeaconsForTesting(&beacons); + EXPECT_EQ(DomainReliabilityContext::kMaxQueuedBeacons, beacons.size()); + + for (const DomainReliabilityBeacon* beacon : beacons) { + EXPECT_NE(oldest_beacon_ptr, beacon); + } + + histograms.ExpectBucketCount(kBeaconOutcomeHistogram, + DomainReliabilityBeacon::Outcome::kEvicted, 1); + histograms.ExpectTotalCount(kBeaconOutcomeHistogram, 1); + + ShutDownContext(); + histograms.ExpectBucketCount( + kBeaconOutcomeHistogram, + DomainReliabilityBeacon::Outcome::kContextShutDown, + DomainReliabilityContext::kMaxQueuedBeacons); + histograms.ExpectTotalCount(kBeaconOutcomeHistogram, + 1 + DomainReliabilityContext::kMaxQueuedBeacons); +} + +// Test eviction when there's no active upload. +TEST_F(DomainReliabilityContextTest, Eviction) { + InitContext(MakeTestConfig()); + + // Add |DomainReliabilityContext::kMaxQueuedBeacons| beacons. + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + context_->OnBeacon( + MakeCustomizedBeacon(&time_, StatusFromInt(i), "" /* quic_error */, + false /* quic_port_migration_detected */)); + } + + // No beacons should have been evicted. + BeaconVector beacons; + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(DomainReliabilityContext::kMaxQueuedBeacons, beacons.size()); + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + EXPECT_EQ(beacons[i]->status, StatusFromInt(i)); + } + + // Add one more beacon. + context_->OnBeacon(MakeCustomizedBeacon( + &time_, StatusFromInt(DomainReliabilityContext::kMaxQueuedBeacons), + "" /* quic_error */, false /* quic_port_migration_detected */)); + + // The first beacon should have been evicted. + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(DomainReliabilityContext::kMaxQueuedBeacons, beacons.size()); + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + EXPECT_EQ(beacons[i]->status, StatusFromInt(i + 1)); + } + + // Wait for the report to start being uploaded. + time_.Advance(max_delay()); + EXPECT_TRUE(upload_allowed_callback_pending()); + CallUploadAllowedResultCallback(true); + EXPECT_TRUE(upload_pending()); + // All beacons but the first should be in the report. + EXPECT_EQ(upload_report().find(StatusFromInt(0)), std::string::npos); + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + EXPECT_NE(upload_report().find(StatusFromInt(i + 1)), std::string::npos); + } + + DomainReliabilityUploader::UploadResult result; + result.status = DomainReliabilityUploader::UploadResult::SUCCESS; + CallUploadCallback(result); + + EXPECT_TRUE(CheckNoBeacons()); +} + +// Test eviction when there's an upload that eventually succeeds. +TEST_F(DomainReliabilityContextTest, EvictionDuringSuccessfulUpload) { + InitContext(MakeTestConfig()); + + // Add |DomainReliabilityContext::kMaxQueuedBeacons| beacons. + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + context_->OnBeacon( + MakeCustomizedBeacon(&time_, StatusFromInt(i), "" /* quic_error */, + false /* quic_port_migration_detected */)); + } + + // No beacons should have been evicted. + BeaconVector beacons; + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(DomainReliabilityContext::kMaxQueuedBeacons, beacons.size()); + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + EXPECT_EQ(beacons[i]->status, StatusFromInt(i)); + } + + // Wait for the report to start being uploaded. + time_.Advance(max_delay()); + EXPECT_TRUE(upload_allowed_callback_pending()); + CallUploadAllowedResultCallback(true); + EXPECT_TRUE(upload_pending()); + // All beacons should be in the report. + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + EXPECT_NE(upload_report().find(StatusFromInt(i)), std::string::npos); + } + + // Add one more beacon. + context_->OnBeacon(MakeCustomizedBeacon( + &time_, StatusFromInt(DomainReliabilityContext::kMaxQueuedBeacons), + "" /* quic_error */, false /* quic_port_migration_detected */)); + + // The first beacon should have been evicted. + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(DomainReliabilityContext::kMaxQueuedBeacons, beacons.size()); + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + EXPECT_EQ(beacons[i]->status, StatusFromInt(i + 1)); + } + + // The upload completes. + DomainReliabilityUploader::UploadResult successful_result; + successful_result.status = DomainReliabilityUploader::UploadResult::SUCCESS; + CallUploadCallback(successful_result); + + // The last beacon should still be queued. + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(1u, beacons.size()); + EXPECT_EQ(beacons[0]->status, + StatusFromInt(DomainReliabilityContext::kMaxQueuedBeacons)); + + // Another upload should have still been queued, for the new report. Wait for + // it to start uploading. + time_.Advance(max_delay()); + EXPECT_TRUE(upload_allowed_callback_pending()); + CallUploadAllowedResultCallback(true); + EXPECT_TRUE(upload_pending()); + // Only the last beacon should be in the report. + EXPECT_NE(upload_report().find( + StatusFromInt(DomainReliabilityContext::kMaxQueuedBeacons)), + std::string::npos); + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + EXPECT_EQ(upload_report().find(StatusFromInt(i)), std::string::npos); + } + + // The upload completes. + CallUploadCallback(successful_result); + + EXPECT_TRUE(CheckNoBeacons()); +} + +// Test eviction when there's an upload that eventually fails. +TEST_F(DomainReliabilityContextTest, EvictionDuringUnsuccessfulUpload) { + InitContext(MakeTestConfig()); + + // Add |DomainReliabilityContext::kMaxQueuedBeacons| beacons. + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + context_->OnBeacon( + MakeCustomizedBeacon(&time_, StatusFromInt(i), "" /* quic_error */, + false /* quic_port_migration_detected */)); + } + + // No beacons should have been evicted. + BeaconVector beacons; + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(DomainReliabilityContext::kMaxQueuedBeacons, beacons.size()); + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + EXPECT_EQ(beacons[i]->status, StatusFromInt(i)); + } + + // Wait for the report to start being uploaded. + time_.Advance(max_delay()); + EXPECT_TRUE(upload_allowed_callback_pending()); + CallUploadAllowedResultCallback(true); + EXPECT_TRUE(upload_pending()); + // All beacons should be in the report. + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + EXPECT_NE(upload_report().find(StatusFromInt(i)), std::string::npos); + } + + // Add one more beacon. + context_->OnBeacon(MakeCustomizedBeacon( + &time_, StatusFromInt(DomainReliabilityContext::kMaxQueuedBeacons), + "" /* quic_error */, false /* quic_port_migration_detected */)); + + // The first beacon should have been evicted. + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(DomainReliabilityContext::kMaxQueuedBeacons, beacons.size()); + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + EXPECT_EQ(beacons[i]->status, StatusFromInt(i + 1)); + } + + // The upload fails. + DomainReliabilityUploader::UploadResult result; + result.status = DomainReliabilityUploader::UploadResult::FAILURE; + CallUploadCallback(result); + + // All beacons but the first should still be queued. + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(DomainReliabilityContext::kMaxQueuedBeacons, beacons.size()); + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + EXPECT_EQ(beacons[i]->status, StatusFromInt(i + 1)); + } + + // Wait for the report to start being uploaded. + time_.Advance(max_delay()); + EXPECT_TRUE(upload_allowed_callback_pending()); + CallUploadAllowedResultCallback(true); + EXPECT_TRUE(upload_pending()); + // All beacons but the first should be in the report. + EXPECT_EQ(upload_report().find(StatusFromInt(0)), std::string::npos); + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + EXPECT_NE(upload_report().find(StatusFromInt(i + 1)), std::string::npos); + } + + // The upload completes successfully. + result.status = DomainReliabilityUploader::UploadResult::SUCCESS; + CallUploadCallback(result); + + EXPECT_TRUE(CheckNoBeacons()); +} + +// Test eviction of all initially pending reports when there's an upload that +// eventually succeeds. +TEST_F(DomainReliabilityContextTest, EvictAllDuringSuccessfulUpload) { + InitContext(MakeTestConfig()); + + // Add |DomainReliabilityContext::kMaxQueuedBeacons| beacons. + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + context_->OnBeacon( + MakeCustomizedBeacon(&time_, StatusFromInt(i), "" /* quic_error */, + false /* quic_port_migration_detected */)); + } + + // No beacons should have been evicted. + BeaconVector beacons; + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(DomainReliabilityContext::kMaxQueuedBeacons, beacons.size()); + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + EXPECT_EQ(beacons[i]->status, StatusFromInt(i)); + } + + // Wait for the report to start being uploaded. + time_.Advance(max_delay()); + EXPECT_TRUE(upload_allowed_callback_pending()); + CallUploadAllowedResultCallback(true); + EXPECT_TRUE(upload_pending()); + // All beacons should be in the report. + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + EXPECT_NE(upload_report().find(StatusFromInt(i)), std::string::npos); + } + + // Evict all beacons, twice. It's important to add a beacon after all beacons + // from the original report have already been deleted, to make sure that + // eviction works correctly once |uploading_beacons_size_| reaches 0. + for (size_t i = 0; i < 2 * DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + context_->OnBeacon(MakeCustomizedBeacon( + &time_, StatusFromInt(i + DomainReliabilityContext::kMaxQueuedBeacons), + "" /* quic_error */, false /* quic_port_migration_detected */)); + } + + // All the original beacons should have been evicted, as should the first + // |DomainReliabilityContext::kMaxQueuedBeacons| beacons from the above loop. + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(DomainReliabilityContext::kMaxQueuedBeacons, beacons.size()); + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + EXPECT_EQ( + beacons[i]->status, + StatusFromInt(i + 2 * DomainReliabilityContext::kMaxQueuedBeacons)); + } + + // The upload succeeds, but no beacons should be removed, since all the + // original beacons have already been evicted. + DomainReliabilityUploader::UploadResult successful_result; + successful_result.status = DomainReliabilityUploader::UploadResult::SUCCESS; + CallUploadCallback(successful_result); + + // The same beacons as before should be queued. + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(DomainReliabilityContext::kMaxQueuedBeacons, beacons.size()); + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + EXPECT_EQ( + beacons[i]->status, + StatusFromInt(i + 2 * DomainReliabilityContext::kMaxQueuedBeacons)); + } + + // Wait for the report to start being uploaded. + time_.Advance(max_delay()); + EXPECT_TRUE(upload_allowed_callback_pending()); + CallUploadAllowedResultCallback(true); + EXPECT_TRUE(upload_pending()); + // Check the expected beacons are in the report. + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons * 3; ++i) { + if (i < DomainReliabilityContext::kMaxQueuedBeacons * 2) { + EXPECT_EQ(upload_report().find(StatusFromInt(i)), std::string::npos); + } else { + EXPECT_NE(upload_report().find(StatusFromInt(i)), std::string::npos); + } + } + + // The upload completes successfully. + CallUploadCallback(successful_result); + + EXPECT_TRUE(CheckNoBeacons()); +} + +// Make sure that evictions account for when there are different +// NetworkIsolationKeys in use. +TEST_F(DomainReliabilityContextTest, + EvictionDuringSuccessfulUploadNetworkIsolationKey) { + ASSERT_EQ(0u, DomainReliabilityContext::kMaxQueuedBeacons % 2) + << "DomainReliabilityContext::kMaxQueuedBeacons must be even."; + + InitContext(MakeTestConfig()); + + net::NetworkIsolationKey network_isolation_keys[] = { + net::NetworkIsolationKey::CreateTransient(), + net::NetworkIsolationKey::CreateTransient(), + }; + + // Add |DomainReliabilityContext::kMaxQueuedBeacons| beacons, using a + // different NetworkIsolationKey for every other beacon. + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + context_->OnBeacon(MakeBeaconWithNetworkIsolationKey( + &time_, StatusFromInt(i), network_isolation_keys[i % 2])); + } + + // No beacons should have been evicted. + BeaconVector beacons; + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(DomainReliabilityContext::kMaxQueuedBeacons, beacons.size()); + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + EXPECT_EQ(beacons[i]->status, StatusFromInt(i)); + EXPECT_EQ(beacons[i]->network_isolation_key, network_isolation_keys[i % 2]); + } + + // Wait for the report to start being uploaded. + time_.Advance(max_delay()); + EXPECT_TRUE(upload_allowed_callback_pending()); + CallUploadAllowedResultCallback(true); + EXPECT_TRUE(upload_pending()); + EXPECT_EQ(network_isolation_keys[0], upload_network_isolation_key()); + // All even-numbered beacons should be in the report. + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + if (i % 2 == 0) { + EXPECT_NE(upload_report().find(StatusFromInt(i)), std::string::npos); + } else { + EXPECT_EQ(upload_report().find(StatusFromInt(i)), std::string::npos); + } + } + + // Add two more beacons, using the same pattern as before + for (size_t i = 0; i < 2; ++i) { + context_->OnBeacon(MakeBeaconWithNetworkIsolationKey( + &time_, StatusFromInt(i + DomainReliabilityContext::kMaxQueuedBeacons), + network_isolation_keys[i % 2])); + } + + // Only the first two beacons should have been evicted. + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(DomainReliabilityContext::kMaxQueuedBeacons, beacons.size()); + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + EXPECT_EQ(beacons[i]->status, StatusFromInt(i + 2)); + EXPECT_EQ(beacons[i]->network_isolation_key, network_isolation_keys[i % 2]); + } + + // The upload succeeds. Every beacon using the first NetworkIsolationKey, + // except the second to last, should have been evicted. + DomainReliabilityUploader::UploadResult successful_result; + successful_result.status = DomainReliabilityUploader::UploadResult::SUCCESS; + CallUploadCallback(successful_result); + + // Check remaining beacons. + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(DomainReliabilityContext::kMaxQueuedBeacons / 2 + 1, + beacons.size()); + int beacon_index = 0; + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons; ++i) { + if (i % 2 == 0 && i < DomainReliabilityContext::kMaxQueuedBeacons - 2) + continue; + EXPECT_EQ(beacons[beacon_index]->status, StatusFromInt(i + 2)); + EXPECT_EQ(beacons[beacon_index]->network_isolation_key, + network_isolation_keys[i % 2]); + beacon_index++; + } + + // Another report should be queued. Wait for it to start being uploaded. + time_.Advance(max_delay()); + EXPECT_TRUE(upload_allowed_callback_pending()); + CallUploadAllowedResultCallback(true); + EXPECT_TRUE(upload_pending()); + EXPECT_EQ(network_isolation_keys[1], upload_network_isolation_key()); + // Check the expected beacons are in the report. + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons + 2; ++i) { + if (i % 2 == 0 || i < 2) { + EXPECT_EQ(upload_report().find(StatusFromInt(i)), std::string::npos); + } else { + EXPECT_NE(upload_report().find(StatusFromInt(i)), std::string::npos); + } + } + + // The upload completes successfully. + CallUploadCallback(successful_result); + + // Check remaining beacons. There should only be one left. + context_->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(1u, beacons.size()); + EXPECT_EQ(beacons[0]->status, + StatusFromInt(DomainReliabilityContext::kMaxQueuedBeacons)); + EXPECT_EQ(beacons[0]->network_isolation_key, network_isolation_keys[0]); + + // Another report should be queued. Wait for it to start being uploaded. + time_.Advance(max_delay()); + EXPECT_TRUE(upload_allowed_callback_pending()); + CallUploadAllowedResultCallback(true); + EXPECT_TRUE(upload_pending()); + EXPECT_EQ(network_isolation_keys[0], upload_network_isolation_key()); + // Check the expected beacons are in the report. + for (size_t i = 0; i < DomainReliabilityContext::kMaxQueuedBeacons + 2; ++i) { + if (i == DomainReliabilityContext::kMaxQueuedBeacons) { + EXPECT_NE(upload_report().find(StatusFromInt(i)), std::string::npos); + } else { + EXPECT_EQ(upload_report().find(StatusFromInt(i)), std::string::npos); + } + } + + // The upload completes successfully. + CallUploadCallback(successful_result); + + EXPECT_TRUE(CheckNoBeacons()); } // TODO(juliatuttle): Add beacon_unittest.cc to test serialization. diff --git a/chromium/components/domain_reliability/features.cc b/chromium/components/domain_reliability/features.cc new file mode 100644 index 00000000000..d216eb0d13e --- /dev/null +++ b/chromium/components/domain_reliability/features.cc @@ -0,0 +1,16 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/domain_reliability/features.h" + +namespace domain_reliability { +namespace features { + +DOMAIN_RELIABILITY_EXPORT extern const base::Feature + kPartitionDomainReliabilityByNetworkIsolationKey{ + "PartitionDomainReliabilityByNetworkIsolationKey", + base::FEATURE_ENABLED_BY_DEFAULT}; + +} // namespace features +} // namespace domain_reliability diff --git a/chromium/components/domain_reliability/features.h b/chromium/components/domain_reliability/features.h new file mode 100644 index 00000000000..9eda8ad9097 --- /dev/null +++ b/chromium/components/domain_reliability/features.h @@ -0,0 +1,21 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_DOMAIN_RELIABILITY_FEATURES_H_ +#define COMPONENTS_DOMAIN_RELIABILITY_FEATURES_H_ + +#include "base/feature_list.h" +#include "components/domain_reliability/domain_reliability_export.h" + +namespace domain_reliability { +namespace features { + +// Partitions Domain Reliability beacons and upload by NetworkIsolationKey. +DOMAIN_RELIABILITY_EXPORT extern const base::Feature + kPartitionDomainReliabilityByNetworkIsolationKey; + +} // namespace features +} // namespace domain_reliability + +#endif // COMPONENTS_DOMAIN_RELIABILITY_FEATURES_H_
\ No newline at end of file diff --git a/chromium/components/domain_reliability/monitor.cc b/chromium/components/domain_reliability/monitor.cc index 2d9125fbe35..8b6a03ad95f 100644 --- a/chromium/components/domain_reliability/monitor.cc +++ b/chromium/components/domain_reliability/monitor.cc @@ -10,12 +10,16 @@ #include "base/bind.h" #include "base/check.h" #include "base/command_line.h" +#include "base/feature_list.h" #include "base/notreached.h" #include "components/domain_reliability/baked_in_configs.h" +#include "components/domain_reliability/features.h" #include "components/domain_reliability/google_configs.h" #include "components/domain_reliability/quic_error_mapping.h" +#include "net/base/isolation_info.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" +#include "net/base/network_isolation_key.h" #include "net/http/http_response_headers.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context.h" @@ -186,6 +190,7 @@ DomainReliabilityMonitor::RequestInfo::RequestInfo( const net::URLRequest& request, int net_error) : url(request.url()), + network_isolation_key(request.isolation_info().network_isolation_key()), net_error(net_error), response_info(request.response_info()), // This ignores cookie blocking by the NetworkDelegate, but probably @@ -267,6 +272,10 @@ void DomainReliabilityMonitor::OnRequestLegComplete( beacon_template.elapsed = time_->NowTicks() - beacon_template.start_time; beacon_template.was_proxied = request.response_info.was_fetched_via_proxy; beacon_template.url = request.url; + if (base::FeatureList::IsEnabled( + features::kPartitionDomainReliabilityByNetworkIsolationKey)) { + beacon_template.network_isolation_key = request.network_isolation_key; + } beacon_template.upload_depth = request.upload_depth; beacon_template.details = request.details; diff --git a/chromium/components/domain_reliability/monitor.h b/chromium/components/domain_reliability/monitor.h index 9306877409e..a606a84fd65 100644 --- a/chromium/components/domain_reliability/monitor.h +++ b/chromium/components/domain_reliability/monitor.h @@ -28,6 +28,7 @@ #include "net/base/load_timing_info.h" #include "net/base/net_error_details.h" #include "net/base/network_change_notifier.h" +#include "net/base/network_isolation_key.h" #include "net/http/http_response_info.h" #include "net/socket/connection_attempts.h" @@ -58,6 +59,7 @@ class DOMAIN_RELIABILITY_EXPORT DomainReliabilityMonitor static bool ShouldReportRequest(const RequestInfo& request); GURL url; + net::NetworkIsolationKey network_isolation_key; int net_error; net::HttpResponseInfo response_info; bool allow_credentials; diff --git a/chromium/components/domain_reliability/monitor_unittest.cc b/chromium/components/domain_reliability/monitor_unittest.cc index 8031bb46b86..f51f05e529f 100644 --- a/chromium/components/domain_reliability/monitor_unittest.cc +++ b/chromium/components/domain_reliability/monitor_unittest.cc @@ -15,15 +15,28 @@ #include "base/bind.h" #include "base/callback_helpers.h" #include "base/strings/string_piece.h" +#include "base/test/metrics/histogram_tester.h" +#include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" +#include "base/time/time.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/features.h" #include "components/domain_reliability/google_configs.h" #include "components/domain_reliability/test_util.h" +#include "net/base/isolation_info.h" +#include "net/base/load_timing_info.h" #include "net/base/net_errors.h" +#include "net/base/network_isolation_key.h" +#include "net/base/request_priority.h" #include "net/http/http_response_headers.h" +#include "net/http/http_response_info.h" #include "net/http/http_util.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "net/test/gtest_util.h" +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/url_request_test_util.h" #include "testing/gtest/include/gtest/gtest.h" @@ -33,6 +46,8 @@ namespace { typedef std::vector<const DomainReliabilityBeacon*> BeaconVector; +const char kBeaconOutcomeHistogram[] = "Net.DomainReliability.BeaconOutcome"; + scoped_refptr<net::HttpResponseHeaders> MakeHttpResponseHeaders( base::StringPiece headers) { return base::MakeRefCounted<net::HttpResponseHeaders>( @@ -103,7 +118,8 @@ class DomainReliabilityMonitorTest : public testing::Test { return monitor_.AddContextForTesting(std::move(config)); } - base::test::SingleThreadTaskEnvironment task_environment_; + base::test::SingleThreadTaskEnvironment task_environment_{ + base::test::TaskEnvironment::MainThreadType::IO}; net::TestURLRequestContext url_request_context_; MockTime* time_; DomainReliabilityMonitor monitor_; @@ -267,6 +283,46 @@ TEST_F(DomainReliabilityMonitorTest, Upload) { EXPECT_EQ(1u, CountQueuedBeacons(context)); } +// Make sure NetworkIsolationKey is populated in the beacon, or not, depending +// on features::kPartitionDomainReliabilityByNetworkIsolationKey. +TEST_F(DomainReliabilityMonitorTest, NetworkIsolationKey) { + const net::NetworkIsolationKey kNetworkIsolationKey = + net::NetworkIsolationKey::CreateTransient(); + + const DomainReliabilityContext* context = CreateAndAddContext(); + + size_t index = 0; + for (bool partitioning_enabled : {false, true}) { + base::test::ScopedFeatureList feature_list; + if (partitioning_enabled) { + feature_list.InitAndEnableFeature( + features::kPartitionDomainReliabilityByNetworkIsolationKey); + } else { + feature_list.InitAndDisableFeature( + features::kPartitionDomainReliabilityByNetworkIsolationKey); + } + RequestInfo request = MakeRequestInfo(); + request.url = GURL("http://example/"); + request.allow_credentials = false; + request.net_error = net::ERR_CONNECTION_RESET; + request.upload_depth = 1; + request.network_isolation_key = kNetworkIsolationKey; + OnRequestLegComplete(request); + + BeaconVector beacons; + context->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(index + 1, beacons.size()); + if (partitioning_enabled) { + EXPECT_EQ(kNetworkIsolationKey, beacons[index]->network_isolation_key); + } else { + EXPECT_EQ(net::NetworkIsolationKey(), + beacons[index]->network_isolation_key); + } + + ++index; + } +} + // Will fail when baked-in configs expire, as a reminder to update them. // (File a bug in Internals>Network>ReportingAndNEL if this starts failing.) TEST_F(DomainReliabilityMonitorTest, BakedInAndGoogleConfigs) { @@ -337,6 +393,7 @@ TEST_F(DomainReliabilityMonitorTest, GoogleConfigOnDemand) { } TEST_F(DomainReliabilityMonitorTest, ClearBeacons) { + base::HistogramTester histograms; const DomainReliabilityContext* context = CreateAndAddContext(); // Initially the monitor should have just the test context, with no beacons. @@ -357,9 +414,14 @@ TEST_F(DomainReliabilityMonitorTest, ClearBeacons) { // Make sure the beacon was cleared, but not the contexts. EXPECT_EQ(1u, monitor_.contexts_size_for_testing()); EXPECT_EQ(0u, CountQueuedBeacons(context)); + + histograms.ExpectBucketCount(kBeaconOutcomeHistogram, + DomainReliabilityBeacon::Outcome::kCleared, 1); + histograms.ExpectTotalCount(kBeaconOutcomeHistogram, 1); } TEST_F(DomainReliabilityMonitorTest, ClearBeaconsWithFilter) { + base::HistogramTester histograms; // Create two contexts, each with one beacon. GURL origin1("http://example.com/"); GURL origin2("http://example.org/"); @@ -390,18 +452,35 @@ TEST_F(DomainReliabilityMonitorTest, ClearBeaconsWithFilter) { EXPECT_EQ(2u, monitor_.contexts_size_for_testing()); EXPECT_EQ(0u, CountQueuedBeacons(context1)); EXPECT_EQ(1u, CountQueuedBeacons(context2)); + + histograms.ExpectBucketCount(kBeaconOutcomeHistogram, + DomainReliabilityBeacon::Outcome::kCleared, 1); + histograms.ExpectTotalCount(kBeaconOutcomeHistogram, 1); } TEST_F(DomainReliabilityMonitorTest, ClearContexts) { - CreateAndAddContext(); + base::HistogramTester histograms; + const DomainReliabilityContext* context = CreateAndAddContext(); // Initially the monitor should have just the test context. EXPECT_EQ(1u, monitor_.contexts_size_for_testing()); + // Add a beacon so we can test histogram behavior. + RequestInfo request = MakeRequestInfo(); + request.url = GURL("http://example/"); + request.net_error = net::ERR_CONNECTION_RESET; + OnRequestLegComplete(request); + EXPECT_EQ(1u, CountQueuedBeacons(context)); + monitor_.ClearBrowsingData(CLEAR_CONTEXTS, base::NullCallback()); // Clearing contexts should leave the monitor with none. EXPECT_EQ(0u, monitor_.contexts_size_for_testing()); + + histograms.ExpectBucketCount( + kBeaconOutcomeHistogram, + DomainReliabilityBeacon::Outcome::kContextShutDown, 1); + histograms.ExpectTotalCount(kBeaconOutcomeHistogram, 1); } TEST_F(DomainReliabilityMonitorTest, ClearContextsWithFilter) { @@ -491,6 +570,72 @@ TEST_F(DomainReliabilityMonitorTest, EXPECT_EQ(0u, CountQueuedBeacons(context2)); } +// Uses a real request, to make sure CreateBeaconFromAttempt() works as +// expected. +TEST_F(DomainReliabilityMonitorTest, RealRequest) { + const net::IsolationInfo kIsolationInfo = + net::IsolationInfo::CreateTransient(); + + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + features::kPartitionDomainReliabilityByNetworkIsolationKey); + + net::test_server::EmbeddedTestServer test_server; + test_server.AddDefaultHandlers(); + ASSERT_TRUE(test_server.Start()); + + std::unique_ptr<DomainReliabilityConfig> config( + MakeTestConfigWithOrigin(test_server.base_url())); + const DomainReliabilityContext* context = + monitor_.AddContextForTesting(std::move(config)); + + base::TimeTicks start = base::TimeTicks::Now(); + + net::TestDelegate test_delegate; + std::unique_ptr<net::URLRequest> url_request = + url_request_context_.CreateRequest(test_server.GetURL("/close-socket"), + net::DEFAULT_PRIORITY, &test_delegate, + TRAFFIC_ANNOTATION_FOR_TESTS); + url_request->set_isolation_info(kIsolationInfo); + url_request->Start(); + + test_delegate.RunUntilComplete(); + EXPECT_THAT(test_delegate.request_status(), + net::test::IsError(net::ERR_EMPTY_RESPONSE)); + + net::LoadTimingInfo load_timing_info; + url_request->GetLoadTimingInfo(&load_timing_info); + base::TimeDelta expected_elapsed = base::TimeDelta::FromSeconds(1); + time_->Advance(load_timing_info.request_start - time_->NowTicks() + + expected_elapsed); + + monitor_.OnCompleted(url_request.get(), true /* started */, + test_delegate.request_status()); + BeaconVector beacons; + context->GetQueuedBeaconsForTesting(&beacons); + ASSERT_EQ(1u, beacons.size()); + EXPECT_EQ(url_request->url(), beacons[0]->url); + EXPECT_EQ(kIsolationInfo.network_isolation_key(), + beacons[0]->network_isolation_key); + EXPECT_EQ("http.response.empty", beacons[0]->status); + EXPECT_EQ("", beacons[0]->quic_error); + EXPECT_EQ(net::ERR_EMPTY_RESPONSE, beacons[0]->chrome_error); + EXPECT_EQ(test_server.base_url().host() + ":" + test_server.base_url().port(), + beacons[0]->server_ip); + EXPECT_FALSE(beacons[0]->was_proxied); + EXPECT_EQ("HTTP", beacons[0]->protocol); + EXPECT_FALSE(beacons[0]->details.quic_broken); + EXPECT_EQ(quic::QUIC_NO_ERROR, beacons[0]->details.quic_connection_error); + EXPECT_EQ(net::HttpResponseInfo::CONNECTION_INFO_HTTP1_1, + beacons[0]->details.connection_info); + EXPECT_FALSE(beacons[0]->details.quic_port_migration_detected); + EXPECT_EQ(-1, beacons[0]->http_response_code); + EXPECT_EQ(expected_elapsed, beacons[0]->elapsed); + EXPECT_LE(start, beacons[0]->start_time); + EXPECT_EQ(0, beacons[0]->upload_depth); + EXPECT_LE(0.99, beacons[0]->sample_rate); +} + } // namespace } // namespace domain_reliability diff --git a/chromium/components/domain_reliability/quic_error_mapping.cc b/chromium/components/domain_reliability/quic_error_mapping.cc index 70aeff4bf41..1dbd1ebc531 100644 --- a/chromium/components/domain_reliability/quic_error_mapping.cc +++ b/chromium/components/domain_reliability/quic_error_mapping.cc @@ -431,6 +431,27 @@ const struct QuicErrorMapping { "quic.invalid.application_close_data"}, {quic::QUIC_MAX_AGE_TIMEOUT, "quic.quic_max_age_timeout"}, + {quic::QUIC_INVALID_0RTT_PACKET_NUMBER_OUT_OF_ORDER, + "quic.quic_invalid_0rtt_packet_number_out_of_order"}, + {quic::QUIC_INVALID_PRIORITY_UPDATE, "quic::quic_invalid_priority_update"}, + {quic::QUIC_PEER_PORT_CHANGE_HANDSHAKE_UNCONFIRMED, + "quic.peer_port_change_handshake_unconfirmed"}, + + {quic::QUIC_TLS_BAD_CERTIFICATE, "quic::quic_tls_bad_certificate"}, + {quic::QUIC_TLS_UNSUPPORTED_CERTIFICATE, + "quic::quic_tls_unsupported_certificate"}, + {quic::QUIC_TLS_CERTIFICATE_REVOKED, "quic::quic_tls_certificate_revoked"}, + {quic::QUIC_TLS_CERTIFICATE_EXPIRED, "quic::quic_tls_certificate_expired"}, + {quic::QUIC_TLS_CERTIFICATE_UNKNOWN, "quic::quic_tls_certificate_unknown"}, + {quic::QUIC_TLS_INTERNAL_ERROR, "quic::quic_tls_internal_error"}, + {quic::QUIC_TLS_UNRECOGNIZED_NAME, "quic::quic_tls_unrecognized_name"}, + {quic::QUIC_TLS_CERTIFICATE_REQUIRED, + "quic::quic_tls_certificate_required"}, + {quic::QUIC_CONNECTION_ID_LIMIT_ERROR, + "quic::quic_connection_id_limit_error"}, + {quic::QUIC_TOO_MANY_CONNECTION_ID_WAITING_TO_RETIRE, + "quic::quic_too_many_connection_id_waiting_to_retire"}, + // No error. Used as bound while iterating. {quic::QUIC_LAST_ERROR, "quic.last_error"}}; diff --git a/chromium/components/domain_reliability/test_util.cc b/chromium/components/domain_reliability/test_util.cc index 05db9cbec7b..6926d8a9baa 100644 --- a/chromium/components/domain_reliability/test_util.cc +++ b/chromium/components/domain_reliability/test_util.cc @@ -84,18 +84,20 @@ void TestCallback::OnCalled() { } MockUploader::MockUploader(UploadRequestCallback callback) - : callback_(std::move(callback)), discard_uploads_(true) {} + : callback_(callback), discard_uploads_(true) {} MockUploader::~MockUploader() = default; bool MockUploader::discard_uploads() const { return discard_uploads_; } -void MockUploader::UploadReport(const std::string& report_json, - int max_upload_depth, - const GURL& upload_url, - UploadCallback callback) { - std::move(callback_).Run(report_json, max_upload_depth, upload_url, - std::move(callback)); +void MockUploader::UploadReport( + const std::string& report_json, + int max_upload_depth, + const GURL& upload_url, + const net::NetworkIsolationKey& network_isolation_key, + UploadCallback callback) { + callback_.Run(report_json, max_upload_depth, upload_url, + network_isolation_key, std::move(callback)); } void MockUploader::Shutdown() {} diff --git a/chromium/components/domain_reliability/test_util.h b/chromium/components/domain_reliability/test_util.h index 11c2505ae66..211f96cb64d 100644 --- a/chromium/components/domain_reliability/test_util.h +++ b/chromium/components/domain_reliability/test_util.h @@ -15,6 +15,10 @@ #include "net/base/host_port_pair.h" #include "url/gurl.h" +namespace net { +class NetworkIsolationKey; +} // namespace net + namespace domain_reliability { // A simple test callback that remembers whether it's been called. @@ -37,10 +41,12 @@ class TestCallback { class MockUploader : public DomainReliabilityUploader { public: - typedef base::OnceCallback<void(const std::string& report_json, - int max_upload_depth, - const GURL& upload_url, - UploadCallback upload_callback)> + typedef base::RepeatingCallback<void( + const std::string& report_json, + int max_upload_depth, + const GURL& upload_url, + const net::NetworkIsolationKey& network_isolation_key, + UploadCallback upload_callback)> UploadRequestCallback; explicit MockUploader(UploadRequestCallback callback); @@ -53,6 +59,7 @@ class MockUploader : public DomainReliabilityUploader { void UploadReport(const std::string& report_json, int max_upload_depth, const GURL& upload_url, + const net::NetworkIsolationKey& network_isolation_key, UploadCallback callback) override; void Shutdown() override; void SetDiscardUploads(bool discard_uploads) override; diff --git a/chromium/components/domain_reliability/uploader.cc b/chromium/components/domain_reliability/uploader.cc index 159526609b6..b674567c549 100644 --- a/chromium/components/domain_reliability/uploader.cc +++ b/chromium/components/domain_reliability/uploader.cc @@ -12,6 +12,7 @@ #include "base/supports_user_data.h" #include "components/domain_reliability/util.h" #include "net/base/elements_upload_data_stream.h" +#include "net/base/isolation_info.h" #include "net/base/net_errors.h" #include "net/base/upload_bytes_element_reader.h" #include "net/http/http_response_headers.h" @@ -70,6 +71,7 @@ class DomainReliabilityUploaderImpl : public DomainReliabilityUploader, const std::string& report_json, int max_upload_depth, const GURL& upload_url, + const net::NetworkIsolationKey& network_isolation_key, DomainReliabilityUploader::UploadCallback callback) override { DVLOG(1) << "Uploading report to " << upload_url; DVLOG(2) << "Report JSON: " << report_json; @@ -119,6 +121,8 @@ class DomainReliabilityUploaderImpl : public DomainReliabilityUploader, request->set_allow_credentials(false); request->SetExtraRequestHeaderByName(net::HttpRequestHeaders::kContentType, kJsonMimeType, true /* overwrite */); + request->set_isolation_info(net::IsolationInfo::CreatePartial( + net::IsolationInfo::RequestType::kOther, network_isolation_key)); std::vector<char> report_data(report_json.begin(), report_json.end()); auto upload_reader = std::make_unique<net::UploadOwnedBytesElementReader>(&report_data); diff --git a/chromium/components/domain_reliability/uploader.h b/chromium/components/domain_reliability/uploader.h index 4e2e56ca066..9a0169e1f98 100644 --- a/chromium/components/domain_reliability/uploader.h +++ b/chromium/components/domain_reliability/uploader.h @@ -15,6 +15,7 @@ #include "url/gurl.h" namespace net { +class NetworkIsolationKey; class URLRequest; class URLRequestContext; } // namespace net @@ -55,10 +56,12 @@ class DOMAIN_RELIABILITY_EXPORT DomainReliabilityUploader { // Uploads |report_json| to |upload_url| and calls |callback| when the upload // has either completed or failed. - virtual void UploadReport(const std::string& report_json, - int max_beacon_depth, - const GURL& upload_url, - UploadCallback callback) = 0; + virtual void UploadReport( + const std::string& report_json, + int max_beacon_depth, + const GURL& upload_url, + const net::NetworkIsolationKey& network_isolation_key, + UploadCallback callback) = 0; // Shuts down the uploader prior to destruction. Currently, terminates pending // uploads and prevents the uploader from starting new ones to avoid hairy diff --git a/chromium/components/domain_reliability/uploader_unittest.cc b/chromium/components/domain_reliability/uploader_unittest.cc index 548448bbbff..c899cf41040 100644 --- a/chromium/components/domain_reliability/uploader_unittest.cc +++ b/chromium/components/domain_reliability/uploader_unittest.cc @@ -16,7 +16,9 @@ #include "base/test/test_simple_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "components/domain_reliability/test_util.h" +#include "net/base/isolation_info.h" #include "net/base/load_flags.h" +#include "net/base/network_isolation_key.h" #include "net/http/http_response_headers.h" #include "net/http/http_response_info.h" #include "net/log/net_log_with_source.h" @@ -101,12 +103,19 @@ class UploadMockURLRequestJob : public net::URLRequestJob { class UploadInterceptor : public net::URLRequestInterceptor { public: - UploadInterceptor() : request_count_(0), last_upload_depth_(-1) {} + explicit UploadInterceptor( + const net::IsolationInfo& expected_network_isolation_info) + : expected_network_isolation_info_(expected_network_isolation_info), + request_count_(0), + last_upload_depth_(-1) {} ~UploadInterceptor() override { EXPECT_TRUE(results_.empty()); } std::unique_ptr<net::URLRequestJob> MaybeInterceptRequest( net::URLRequest* request) const override { + EXPECT_TRUE(expected_network_isolation_info_.IsEqualForTesting( + request->isolation_info())); + EXPECT_FALSE(results_.empty()); MockUploadResult result = results_.front(); results_.pop_front(); @@ -137,6 +146,8 @@ class UploadInterceptor : public net::URLRequestInterceptor { int last_upload_depth() const { return last_upload_depth_; } private: + const net::IsolationInfo expected_network_isolation_info_; + mutable std::list<MockUploadResult> results_; mutable int request_count_; mutable int last_upload_depth_; @@ -171,7 +182,8 @@ class DomainReliabilityUploaderTest : public testing::Test { DomainReliabilityUploaderTest() : uploader_( DomainReliabilityUploader::Create(&time_, &url_request_context_)) { - auto interceptor = std::make_unique<UploadInterceptor>(); + auto interceptor = + std::make_unique<UploadInterceptor>(expected_isolation_info_); interceptor_ = interceptor.get(); net::URLRequestFilter::GetInstance()->AddUrlInterceptor( GURL(kUploadURL), std::move(interceptor)); @@ -188,9 +200,17 @@ class DomainReliabilityUploaderTest : public testing::Test { return &url_request_context_; } + const net::NetworkIsolationKey& network_isolation_key() const { + return expected_isolation_info_.network_isolation_key(); + } + private: base::test::SingleThreadTaskEnvironment task_environment_{ base::test::SingleThreadTaskEnvironment::MainThreadType::IO}; + + const net::IsolationInfo expected_isolation_info_ = + net::IsolationInfo::CreateTransient(); + net::TestURLRequestContext url_request_context_; UploadInterceptor* interceptor_; MockTime time_; @@ -205,7 +225,8 @@ TEST_F(DomainReliabilityUploaderTest, SuccessfulUpload) { interceptor()->ExpectRequestAndReturnResponseHeaders("HTTP/1.1 200\r\n\r\n"); TestUploadCallback c; - uploader()->UploadReport("{}", 0, GURL(kUploadURL), c.callback()); + uploader()->UploadReport("{}", 0, GURL(kUploadURL), network_isolation_key(), + c.callback()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1u, c.called_count()); EXPECT_TRUE(c.last_result().is_success()); @@ -217,7 +238,8 @@ TEST_F(DomainReliabilityUploaderTest, NetworkErrorUpload) { interceptor()->ExpectRequestAndReturnError(net::ERR_CONNECTION_REFUSED); TestUploadCallback c; - uploader()->UploadReport("{}", 0, GURL(kUploadURL), c.callback()); + uploader()->UploadReport("{}", 0, GURL(kUploadURL), network_isolation_key(), + c.callback()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1u, c.called_count()); EXPECT_TRUE(c.last_result().is_failure()); @@ -229,7 +251,8 @@ TEST_F(DomainReliabilityUploaderTest, ServerErrorUpload) { interceptor()->ExpectRequestAndReturnResponseHeaders("HTTP/1.1 500\r\n\r\n"); TestUploadCallback c; - uploader()->UploadReport("{}", 0, GURL(kUploadURL), c.callback()); + uploader()->UploadReport("{}", 0, GURL(kUploadURL), network_isolation_key(), + c.callback()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1u, c.called_count()); EXPECT_TRUE(c.last_result().is_failure()); @@ -242,7 +265,8 @@ TEST_F(DomainReliabilityUploaderTest, RetryAfterUpload) { "HTTP/1.1 503 Ugh\nRetry-After: 3600\n\n"); TestUploadCallback c; - uploader()->UploadReport("{}", 0, GURL(kUploadURL), c.callback()); + uploader()->UploadReport("{}", 0, GURL(kUploadURL), network_isolation_key(), + c.callback()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1u, c.called_count()); EXPECT_TRUE(c.last_result().is_retry_after()); @@ -254,7 +278,8 @@ TEST_F(DomainReliabilityUploaderTest, UploadDepth1) { interceptor()->ExpectRequestAndReturnResponseHeaders("HTTP/1.1 200\r\n\r\n"); TestUploadCallback c; - uploader()->UploadReport("{}", 0, GURL(kUploadURL), c.callback()); + uploader()->UploadReport("{}", 0, GURL(kUploadURL), network_isolation_key(), + c.callback()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1u, c.called_count()); @@ -267,7 +292,8 @@ TEST_F(DomainReliabilityUploaderTest, UploadDepth2) { interceptor()->ExpectRequestAndReturnResponseHeaders("HTTP/1.1 200\r\n\r\n"); TestUploadCallback c; - uploader()->UploadReport("{}", 1, GURL(kUploadURL), c.callback()); + uploader()->UploadReport("{}", 1, GURL(kUploadURL), network_isolation_key(), + c.callback()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1u, c.called_count()); @@ -280,7 +306,8 @@ TEST_F(DomainReliabilityUploaderTest, UploadCanceledAtShutdown) { interceptor()->ExpectRequestAndReturnError(net::ERR_IO_PENDING); TestUploadCallback c; - uploader()->UploadReport("{}", 1, GURL(kUploadURL), c.callback()); + uploader()->UploadReport("{}", 1, GURL(kUploadURL), network_isolation_key(), + c.callback()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1, interceptor()->request_count()); EXPECT_EQ(0u, c.called_count()); @@ -296,7 +323,8 @@ TEST_F(DomainReliabilityUploaderTest, NoUploadAfterShutdown) { uploader()->Shutdown(); TestUploadCallback c; - uploader()->UploadReport("{}", 1, GURL(kUploadURL), c.callback()); + uploader()->UploadReport("{}", 1, GURL(kUploadURL), network_isolation_key(), + c.callback()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1u, c.called_count()); EXPECT_EQ(0, interceptor()->request_count()); |