summaryrefslogtreecommitdiffstats
path: root/net/url_request/url_request_throttler_entry.cc
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2011-01-07 14:18:56 +0000
committerBen Murdoch <benm@google.com>2011-01-11 10:23:13 +0000
commit201ade2fbba22bfb27ae029f4d23fca6ded109a0 (patch)
treeb793f4ed916f73cf18357ea467ff3deb5ffb5b52 /net/url_request/url_request_throttler_entry.cc
parentd8c4c37a7d0961944bfdfaa117d5c68c8e129c97 (diff)
downloadexternal_chromium-201ade2fbba22bfb27ae029f4d23fca6ded109a0.zip
external_chromium-201ade2fbba22bfb27ae029f4d23fca6ded109a0.tar.gz
external_chromium-201ade2fbba22bfb27ae029f4d23fca6ded109a0.tar.bz2
Merge chromium at 9.0.597.55: Initial merge by git.
Change-Id: Id686a88437441ec7e17abb3328a404c7b6c3c6ad
Diffstat (limited to 'net/url_request/url_request_throttler_entry.cc')
-rw-r--r--net/url_request/url_request_throttler_entry.cc242
1 files changed, 242 insertions, 0 deletions
diff --git a/net/url_request/url_request_throttler_entry.cc b/net/url_request/url_request_throttler_entry.cc
new file mode 100644
index 0000000..4abb438
--- /dev/null
+++ b/net/url_request/url_request_throttler_entry.cc
@@ -0,0 +1,242 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/url_request/url_request_throttler_entry.h"
+
+#include <cmath>
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/string_number_conversions.h"
+#include "net/url_request/url_request_throttler_header_interface.h"
+
+namespace net {
+
+const int URLRequestThrottlerEntry::kDefaultSlidingWindowPeriodMs = 2000;
+const int URLRequestThrottlerEntry::kDefaultMaxSendThreshold = 20;
+const int URLRequestThrottlerEntry::kDefaultInitialBackoffMs = 700;
+const int URLRequestThrottlerEntry::kDefaultAdditionalConstantMs = 100;
+const double URLRequestThrottlerEntry::kDefaultMultiplyFactor = 1.4;
+const double URLRequestThrottlerEntry::kDefaultJitterFactor = 0.4;
+const int URLRequestThrottlerEntry::kDefaultMaximumBackoffMs = 60 * 60 * 1000;
+const int URLRequestThrottlerEntry::kDefaultEntryLifetimeMs = 120000;
+const char URLRequestThrottlerEntry::kRetryHeaderName[] = "X-Retry-After";
+
+URLRequestThrottlerEntry::URLRequestThrottlerEntry()
+ : sliding_window_period_(
+ base::TimeDelta::FromMilliseconds(kDefaultSlidingWindowPeriodMs)),
+ max_send_threshold_(kDefaultMaxSendThreshold),
+ initial_backoff_ms_(kDefaultInitialBackoffMs),
+ additional_constant_ms_(kDefaultAdditionalConstantMs),
+ multiply_factor_(kDefaultMultiplyFactor),
+ jitter_factor_(kDefaultJitterFactor),
+ maximum_backoff_ms_(kDefaultMaximumBackoffMs),
+ entry_lifetime_ms_(kDefaultEntryLifetimeMs) {
+ Initialize();
+}
+
+URLRequestThrottlerEntry::URLRequestThrottlerEntry(
+ int sliding_window_period_ms,
+ int max_send_threshold,
+ int initial_backoff_ms,
+ int additional_constant_ms,
+ double multiply_factor,
+ double jitter_factor,
+ int maximum_backoff_ms)
+ : sliding_window_period_(
+ base::TimeDelta::FromMilliseconds(sliding_window_period_ms)),
+ max_send_threshold_(max_send_threshold),
+ initial_backoff_ms_(initial_backoff_ms),
+ additional_constant_ms_(additional_constant_ms),
+ multiply_factor_(multiply_factor),
+ jitter_factor_(jitter_factor),
+ maximum_backoff_ms_(maximum_backoff_ms),
+ entry_lifetime_ms_(-1) {
+ DCHECK_GT(sliding_window_period_ms, 0);
+ DCHECK_GT(max_send_threshold_, 0);
+ DCHECK_GE(initial_backoff_ms_, 0);
+ DCHECK_GE(additional_constant_ms_, 0);
+ DCHECK_GT(multiply_factor_, 0);
+ DCHECK_GE(jitter_factor_, 0);
+ DCHECK_LT(jitter_factor_, 1);
+ DCHECK_GE(maximum_backoff_ms_, 0);
+
+ Initialize();
+}
+
+URLRequestThrottlerEntry::~URLRequestThrottlerEntry() {
+}
+
+void URLRequestThrottlerEntry::Initialize() {
+ // Since this method is called by the constructors, GetTimeNow() (a virtual
+ // method) is not used.
+ exponential_backoff_release_time_ = base::TimeTicks::Now();
+ failure_count_ = 0;
+ latest_response_was_failure_ = false;
+
+ sliding_window_release_time_ = base::TimeTicks::Now();
+}
+
+bool URLRequestThrottlerEntry::IsDuringExponentialBackoff() const {
+ return exponential_backoff_release_time_ > GetTimeNow();
+}
+
+int64 URLRequestThrottlerEntry::ReserveSendingTimeForNextRequest(
+ const base::TimeTicks& earliest_time) {
+ base::TimeTicks now = GetTimeNow();
+ // If a lot of requests were successfully made recently,
+ // sliding_window_release_time_ may be greater than
+ // exponential_backoff_release_time_.
+ base::TimeTicks recommended_sending_time =
+ std::max(std::max(now, earliest_time),
+ std::max(exponential_backoff_release_time_,
+ sliding_window_release_time_));
+
+ DCHECK(send_log_.empty() ||
+ recommended_sending_time >= send_log_.back());
+ // Log the new send event.
+ send_log_.push(recommended_sending_time);
+
+ sliding_window_release_time_ = recommended_sending_time;
+
+ // Drop the out-of-date events in the event list.
+ // We don't need to worry that the queue may become empty during this
+ // operation, since the last element is sliding_window_release_time_.
+ while ((send_log_.front() + sliding_window_period_ <=
+ sliding_window_release_time_) ||
+ send_log_.size() > static_cast<unsigned>(max_send_threshold_)) {
+ send_log_.pop();
+ }
+
+ // Check if there are too many send events in recent time.
+ if (send_log_.size() == static_cast<unsigned>(max_send_threshold_))
+ sliding_window_release_time_ = send_log_.front() + sliding_window_period_;
+
+ return (recommended_sending_time - now).InMillisecondsRoundedUp();
+}
+
+base::TimeTicks
+ URLRequestThrottlerEntry::GetExponentialBackoffReleaseTime() const {
+ return exponential_backoff_release_time_;
+}
+
+void URLRequestThrottlerEntry::UpdateWithResponse(
+ const URLRequestThrottlerHeaderInterface* response) {
+ if (response->GetResponseCode() >= 500) {
+ failure_count_++;
+ latest_response_was_failure_ = true;
+ exponential_backoff_release_time_ =
+ CalculateExponentialBackoffReleaseTime();
+ } else {
+ // We slowly decay the number of times delayed instead of resetting it to 0
+ // in order to stay stable if we received lots of requests with
+ // malformed bodies at the same time.
+ if (failure_count_ > 0)
+ failure_count_--;
+
+ latest_response_was_failure_ = false;
+
+ // The reason why we are not just cutting the release time to GetTimeNow()
+ // is on the one hand, it would unset delay put by our custom retry-after
+ // header and on the other we would like to push every request up to our
+ // "horizon" when dealing with multiple in-flight requests. Ex: If we send
+ // three requests and we receive 2 failures and 1 success. The success that
+ // follows those failures will not reset the release time, further requests
+ // will then need to wait the delay caused by the 2 failures.
+ exponential_backoff_release_time_ = std::max(
+ GetTimeNow(), exponential_backoff_release_time_);
+
+ std::string retry_header = response->GetNormalizedValue(kRetryHeaderName);
+ if (!retry_header.empty())
+ HandleCustomRetryAfter(retry_header);
+ }
+}
+
+bool URLRequestThrottlerEntry::IsEntryOutdated() const {
+ if (entry_lifetime_ms_ == -1)
+ return false;
+
+ base::TimeTicks now = GetTimeNow();
+
+ // If there are send events in the sliding window period, we still need this
+ // entry.
+ if (send_log_.size() > 0 &&
+ send_log_.back() + sliding_window_period_ > now) {
+ return false;
+ }
+
+ int64 unused_since_ms =
+ (now - exponential_backoff_release_time_).InMilliseconds();
+
+ // Release time is further than now, we are managing it.
+ if (unused_since_ms < 0)
+ return false;
+
+ // latest_response_was_failure_ is true indicates that the latest one or
+ // more requests encountered server errors or had malformed response bodies.
+ // In that case, we don't want to collect the entry unless it hasn't been used
+ // for longer than the maximum allowed back-off.
+ if (latest_response_was_failure_)
+ return unused_since_ms > std::max(maximum_backoff_ms_, entry_lifetime_ms_);
+
+ // Otherwise, consider the entry is outdated if it hasn't been used for the
+ // specified lifetime period.
+ return unused_since_ms > entry_lifetime_ms_;
+}
+
+void URLRequestThrottlerEntry::ReceivedContentWasMalformed() {
+ // For any response that is marked as malformed now, we have probably
+ // considered it as a success when receiving it and decreased the failure
+ // count by 1. As a result, we increase the failure count by 2 here to undo
+ // the effect and record a failure.
+ //
+ // Please note that this may lead to a larger failure count than expected,
+ // because we don't decrease the failure count for successful responses when
+ // it has already reached 0.
+ failure_count_ += 2;
+ latest_response_was_failure_ = true;
+ exponential_backoff_release_time_ = CalculateExponentialBackoffReleaseTime();
+}
+
+base::TimeTicks
+ URLRequestThrottlerEntry::CalculateExponentialBackoffReleaseTime() {
+ double delay = initial_backoff_ms_;
+ delay *= pow(multiply_factor_, failure_count_);
+ delay += additional_constant_ms_;
+ delay -= base::RandDouble() * jitter_factor_ * delay;
+
+ // Ensure that we do not exceed maximum delay.
+ int64 delay_int = static_cast<int64>(delay + 0.5);
+ delay_int = std::min(delay_int, static_cast<int64>(maximum_backoff_ms_));
+
+ return std::max(GetTimeNow() + base::TimeDelta::FromMilliseconds(delay_int),
+ exponential_backoff_release_time_);
+}
+
+base::TimeTicks URLRequestThrottlerEntry::GetTimeNow() const {
+ return base::TimeTicks::Now();
+}
+
+void URLRequestThrottlerEntry::HandleCustomRetryAfter(
+ const std::string& header_value) {
+ // Input parameter is the number of seconds to wait in a floating point value.
+ double time_in_sec = 0;
+ bool conversion_is_ok = base::StringToDouble(header_value, &time_in_sec);
+
+ // Conversion of custom retry-after header value failed.
+ if (!conversion_is_ok)
+ return;
+
+ // We must use an int value later so we transform this in milliseconds.
+ int64 value_ms = static_cast<int64>(0.5 + time_in_sec * 1000);
+
+ if (maximum_backoff_ms_ < value_ms || value_ms < 0)
+ return;
+
+ exponential_backoff_release_time_ = std::max(
+ (GetTimeNow() + base::TimeDelta::FromMilliseconds(value_ms)),
+ exponential_backoff_release_time_);
+}
+
+} // namespace net