blob: 8c4a415489ec5e70d054f1149def885793a2d11e [file] [log] [blame]
// 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;
std::string absolute_release_time_string = serialized[3].GetString();
int64_t absolute_release_time_us;
if (!base::StringToInt64(absolute_release_time_string,
&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