xref: /aosp_15_r20/external/cronet/net/base/backoff_entry_serializer.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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