johnme | 6727a62a | 2015-05-07 13:48:30 | [diff] [blame] | 1 | // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "net/base/backoff_entry_serializer.h" |
| 6 | |
Daniel McArdle | 5aea464 | 2020-08-05 16:59:54 | [diff] [blame] | 7 | #include <algorithm> |
dcheng | c7eeda42 | 2015-12-26 03:56:48 | [diff] [blame] | 8 | #include <utility> |
| 9 | |
Dan McArdle | 1b605dc | 2021-09-23 17:54:31 | [diff] [blame] | 10 | #include "base/notreached.h" |
johnme | 6727a62a | 2015-05-07 13:48:30 | [diff] [blame] | 11 | #include "base/strings/string_number_conversions.h" |
| 12 | #include "base/time/tick_clock.h" |
| 13 | #include "base/values.h" |
| 14 | #include "net/base/backoff_entry.h" |
| 15 | |
| 16 | namespace { |
Daniel McArdle | 5aea464 | 2020-08-05 16:59:54 | [diff] [blame] | 17 | // This max defines how many times we are willing to call |
| 18 | // |BackoffEntry::InformOfRequest| in |DeserializeFromValue|. |
| 19 | // |
| 20 | // This value is meant to large enough that the computed backoff duration can |
| 21 | // still be saturated. Given that the duration is an int64 and assuming 1.01 as |
| 22 | // a conservative lower bound for BackoffEntry::Policy::multiply_factor, |
| 23 | // ceil(log(2**63-1, 1.01)) = 4389. |
| 24 | const int kMaxFailureCount = 4389; |
Dan McArdle | 22c2f64 | 2021-03-18 23:40:10 | [diff] [blame] | 25 | |
Dan McArdle | 1b605dc | 2021-09-23 17:54:31 | [diff] [blame] | 26 | // This function returns true iff |duration| is finite and can be serialized and |
| 27 | // deserialized without becoming infinite. This function is aligned with the |
| 28 | // latest version. |
Dan McArdle | 22c2f64 | 2021-03-18 23:40:10 | [diff] [blame] | 29 | bool BackoffDurationSafeToSerialize(const base::TimeDelta& duration) { |
| 30 | return !duration.is_inf() && |
Peter Kasting | e5a38ed | 2021-10-02 03:06:35 | [diff] [blame] | 31 | !base::Microseconds(duration.InMicroseconds()).is_inf(); |
Dan McArdle | 22c2f64 | 2021-03-18 23:40:10 | [diff] [blame] | 32 | } |
johnme | 6727a62a | 2015-05-07 13:48:30 | [diff] [blame] | 33 | } // namespace |
| 34 | |
| 35 | namespace net { |
| 36 | |
Maks Orlovich | 691496c | 2021-05-18 15:03:21 | [diff] [blame] | 37 | base::Value BackoffEntrySerializer::SerializeToValue(const BackoffEntry& entry, |
| 38 | base::Time time_now) { |
Matt Menke | 7c5e077 | 2022-05-31 21:35:45 | [diff] [blame] | 39 | base::Value::List serialized; |
| 40 | serialized.Append(SerializationFormatVersion::kVersion2); |
johnme | 6727a62a | 2015-05-07 13:48:30 | [diff] [blame] | 41 | |
Matt Menke | 7c5e077 | 2022-05-31 21:35:45 | [diff] [blame] | 42 | serialized.Append(entry.failure_count()); |
johnme | 6727a62a | 2015-05-07 13:48:30 | [diff] [blame] | 43 | |
Dan McArdle | e4960f7 | 2021-02-04 18:01:11 | [diff] [blame] | 44 | // Convert both |base::TimeTicks| values into |base::TimeDelta| values by |
| 45 | // subtracting |kZeroTicks. This way, the top-level subtraction uses |
| 46 | // |base::TimeDelta::operator-|, which has clamping semantics. |
| 47 | const base::TimeTicks kZeroTicks; |
Dan McArdle | 3e929bc | 2021-02-12 16:57:59 | [diff] [blame] | 48 | const base::TimeDelta kReleaseTime = entry.GetReleaseTime() - kZeroTicks; |
| 49 | const base::TimeDelta kTimeTicksNow = entry.GetTimeTicksNow() - kZeroTicks; |
| 50 | base::TimeDelta backoff_duration; |
| 51 | if (!kReleaseTime.is_inf() && !kTimeTicksNow.is_inf()) { |
| 52 | backoff_duration = kReleaseTime - kTimeTicksNow; |
| 53 | } |
Dan McArdle | 22c2f64 | 2021-03-18 23:40:10 | [diff] [blame] | 54 | if (!BackoffDurationSafeToSerialize(backoff_duration)) { |
Dan McArdle | e4960f7 | 2021-02-04 18:01:11 | [diff] [blame] | 55 | backoff_duration = base::TimeDelta(); |
| 56 | } |
| 57 | |
Dan McArdle | 22c2f64 | 2021-03-18 23:40:10 | [diff] [blame] | 58 | base::Time absolute_release_time = backoff_duration + time_now; |
| 59 | // If the computed release time is infinite, default to zero. The deserializer |
| 60 | // should pick up on this. |
| 61 | if (absolute_release_time.is_inf()) { |
| 62 | absolute_release_time = base::Time(); |
| 63 | } |
| 64 | |
johnme | 6727a62a | 2015-05-07 13:48:30 | [diff] [blame] | 65 | // Redundantly stores both the remaining time delta and the absolute time. |
| 66 | // The delta is used to work around some cases where wall clock time changes. |
Matt Menke | 7c5e077 | 2022-05-31 21:35:45 | [diff] [blame] | 67 | serialized.Append(base::NumberToString(backoff_duration.InMicroseconds())); |
| 68 | serialized.Append( |
Raul Tambre | 8c1981d | 2019-02-08 02:22:26 | [diff] [blame] | 69 | base::NumberToString(absolute_release_time.ToInternalValue())); |
johnme | 6727a62a | 2015-05-07 13:48:30 | [diff] [blame] | 70 | |
Maks Orlovich | 691496c | 2021-05-18 15:03:21 | [diff] [blame] | 71 | return base::Value(std::move(serialized)); |
johnme | 6727a62a | 2015-05-07 13:48:30 | [diff] [blame] | 72 | } |
| 73 | |
Roland Bock | 652d22d | 2022-08-11 15:22:02 | [diff] [blame] | 74 | std::unique_ptr<BackoffEntry> BackoffEntrySerializer::DeserializeFromList( |
| 75 | const base::Value::List& serialized, |
danakj | 7f767e6 | 2016-04-16 23:20:23 | [diff] [blame] | 76 | const BackoffEntry::Policy* policy, |
Greg Thompson | aa48ce8d | 2018-04-03 06:11:43 | [diff] [blame] | 77 | const base::TickClock* tick_clock, |
danakj | 7f767e6 | 2016-04-16 23:20:23 | [diff] [blame] | 78 | base::Time time_now) { |
Roland Bock | 652d22d | 2022-08-11 15:22:02 | [diff] [blame] | 79 | if (serialized.size() != 4) |
Maks Orlovich | 691496c | 2021-05-18 15:03:21 | [diff] [blame] | 80 | return nullptr; |
| 81 | |
Roland Bock | 652d22d | 2022-08-11 15:22:02 | [diff] [blame] | 82 | if (!serialized[0].is_int()) |
Maks Orlovich | 691496c | 2021-05-18 15:03:21 | [diff] [blame] | 83 | return nullptr; |
Roland Bock | 652d22d | 2022-08-11 15:22:02 | [diff] [blame] | 84 | int version_number = serialized[0].GetInt(); |
Dan McArdle | 1b605dc | 2021-09-23 17:54:31 | [diff] [blame] | 85 | if (version_number != kVersion1 && version_number != kVersion2) |
Maks Orlovich | 691496c | 2021-05-18 15:03:21 | [diff] [blame] | 86 | return nullptr; |
| 87 | |
Roland Bock | 652d22d | 2022-08-11 15:22:02 | [diff] [blame] | 88 | if (!serialized[1].is_int()) |
Maks Orlovich | 691496c | 2021-05-18 15:03:21 | [diff] [blame] | 89 | return nullptr; |
Roland Bock | 652d22d | 2022-08-11 15:22:02 | [diff] [blame] | 90 | int failure_count = serialized[1].GetInt(); |
Maks Orlovich | 691496c | 2021-05-18 15:03:21 | [diff] [blame] | 91 | if (failure_count < 0) { |
johnme | 6727a62a | 2015-05-07 13:48:30 | [diff] [blame] | 92 | return nullptr; |
Daniel McArdle | 5aea464 | 2020-08-05 16:59:54 | [diff] [blame] | 93 | } |
| 94 | failure_count = std::min(failure_count, kMaxFailureCount); |
| 95 | |
Dan McArdle | 1b605dc | 2021-09-23 17:54:31 | [diff] [blame] | 96 | base::TimeDelta original_backoff_duration; |
| 97 | switch (version_number) { |
| 98 | case kVersion1: { |
Roland Bock | 652d22d | 2022-08-11 15:22:02 | [diff] [blame] | 99 | if (!serialized[2].is_double()) |
Dan McArdle | 1b605dc | 2021-09-23 17:54:31 | [diff] [blame] | 100 | return nullptr; |
Roland Bock | 652d22d | 2022-08-11 15:22:02 | [diff] [blame] | 101 | double original_backoff_duration_double = serialized[2].GetDouble(); |
Dan McArdle | 1b605dc | 2021-09-23 17:54:31 | [diff] [blame] | 102 | original_backoff_duration = |
Peter Kasting | e5a38ed | 2021-10-02 03:06:35 | [diff] [blame] | 103 | base::Seconds(original_backoff_duration_double); |
Dan McArdle | 1b605dc | 2021-09-23 17:54:31 | [diff] [blame] | 104 | break; |
| 105 | } |
| 106 | case kVersion2: { |
Roland Bock | 652d22d | 2022-08-11 15:22:02 | [diff] [blame] | 107 | if (!serialized[2].is_string()) |
Dan McArdle | 1b605dc | 2021-09-23 17:54:31 | [diff] [blame] | 108 | return nullptr; |
Roland Bock | 652d22d | 2022-08-11 15:22:02 | [diff] [blame] | 109 | std::string original_backoff_duration_string = serialized[2].GetString(); |
Dan McArdle | 1b605dc | 2021-09-23 17:54:31 | [diff] [blame] | 110 | int64_t original_backoff_duration_us; |
| 111 | if (!base::StringToInt64(original_backoff_duration_string, |
| 112 | &original_backoff_duration_us)) { |
| 113 | return nullptr; |
| 114 | } |
| 115 | original_backoff_duration = |
Peter Kasting | e5a38ed | 2021-10-02 03:06:35 | [diff] [blame] | 116 | base::Microseconds(original_backoff_duration_us); |
Dan McArdle | 1b605dc | 2021-09-23 17:54:31 | [diff] [blame] | 117 | break; |
| 118 | } |
| 119 | default: |
| 120 | NOTREACHED() << "Unexpected version_number: " << version_number; |
| 121 | } |
Maks Orlovich | 691496c | 2021-05-18 15:03:21 | [diff] [blame] | 122 | |
Roland Bock | 652d22d | 2022-08-11 15:22:02 | [diff] [blame] | 123 | if (!serialized[3].is_string()) |
johnme | 6727a62a | 2015-05-07 13:48:30 | [diff] [blame] | 124 | return nullptr; |
Roland Bock | 652d22d | 2022-08-11 15:22:02 | [diff] [blame] | 125 | std::string absolute_release_time_string = serialized[3].GetString(); |
Maks Orlovich | 691496c | 2021-05-18 15:03:21 | [diff] [blame] | 126 | |
wtc | 69f8ea8 | 2015-06-04 00:08:13 | [diff] [blame] | 127 | int64_t absolute_release_time_us; |
johnme | 6727a62a | 2015-05-07 13:48:30 | [diff] [blame] | 128 | if (!base::StringToInt64(absolute_release_time_string, |
Daniel McArdle | 209ee46 | 2020-09-18 19:59:28 | [diff] [blame] | 129 | &absolute_release_time_us)) { |
johnme | 6727a62a | 2015-05-07 13:48:30 | [diff] [blame] | 130 | return nullptr; |
| 131 | } |
| 132 | |
Tsuyoshi Horo | f8861cb | 2022-07-05 23:50:20 | [diff] [blame] | 133 | auto entry = std::make_unique<BackoffEntry>(policy, tick_clock); |
johnme | 6727a62a | 2015-05-07 13:48:30 | [diff] [blame] | 134 | |
| 135 | for (int n = 0; n < failure_count; n++) |
| 136 | entry->InformOfRequest(false); |
| 137 | |
johnme | 6727a62a | 2015-05-07 13:48:30 | [diff] [blame] | 138 | base::Time absolute_release_time = |
| 139 | base::Time::FromInternalValue(absolute_release_time_us); |
Dan McArdle | 22c2f64 | 2021-03-18 23:40:10 | [diff] [blame] | 140 | |
| 141 | base::TimeDelta backoff_duration; |
| 142 | if (absolute_release_time == base::Time()) { |
| 143 | // When the serializer cannot compute a finite release time, it uses zero. |
| 144 | // When we see this, fall back to the redundant original_backoff_duration. |
johnme | 6727a62a | 2015-05-07 13:48:30 | [diff] [blame] | 145 | backoff_duration = original_backoff_duration; |
Dan McArdle | 22c2f64 | 2021-03-18 23:40:10 | [diff] [blame] | 146 | } else { |
| 147 | // Before computing |backoff_duration|, throw out +/- infinity values for |
| 148 | // either operand. This way, we can use base::TimeDelta's saturated math. |
| 149 | if (absolute_release_time.is_inf() || time_now.is_inf()) |
| 150 | return nullptr; |
| 151 | |
| 152 | backoff_duration = absolute_release_time.ToDeltaSinceWindowsEpoch() - |
| 153 | time_now.ToDeltaSinceWindowsEpoch(); |
| 154 | |
| 155 | // In cases where the system wall clock is rewound, use the redundant |
| 156 | // original_backoff_duration to ensure the backoff duration isn't longer |
| 157 | // than it was before serializing (note that it's not possible to protect |
| 158 | // against the clock being wound forward). |
| 159 | if (backoff_duration > original_backoff_duration) |
| 160 | backoff_duration = original_backoff_duration; |
| 161 | } |
| 162 | if (!BackoffDurationSafeToSerialize(backoff_duration)) |
Daniel McArdle | 52ec069 | 2020-07-06 21:50:19 | [diff] [blame] | 163 | return nullptr; |
Dan McArdle | 6d0831d | 2022-02-08 15:02:40 | [diff] [blame] | 164 | |
| 165 | const base::TimeTicks release_time = |
| 166 | entry->BackoffDurationToReleaseTime(backoff_duration); |
| 167 | if (release_time.is_inf()) |
| 168 | return nullptr; |
| 169 | entry->SetCustomReleaseTime(release_time); |
johnme | 6727a62a | 2015-05-07 13:48:30 | [diff] [blame] | 170 | |
| 171 | return entry; |
| 172 | } |
| 173 | |
Roland Bock | 652d22d | 2022-08-11 15:22:02 | [diff] [blame] | 174 | std::unique_ptr<BackoffEntry> BackoffEntrySerializer::DeserializeFromValue( |
| 175 | const base::Value& serialized, |
| 176 | const BackoffEntry::Policy* policy, |
| 177 | const base::TickClock* tick_clock, |
| 178 | base::Time time_now) { |
| 179 | if (!serialized.is_list()) |
| 180 | return nullptr; |
| 181 | return DeserializeFromList(serialized.GetList(), policy, tick_clock, |
| 182 | time_now); |
| 183 | } |
| 184 | |
johnme | 6727a62a | 2015-05-07 13:48:30 | [diff] [blame] | 185 | } // namespace net |