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