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