Chromium Code Reviews
|
| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
| 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/url_request/request_throttler_entry.h" | |
| 6 | |
| 7 #include <cmath> | |
| 8 | |
| 9 #include "base/logging.h" | |
| 10 #include "base/rand_util.h" | |
| 11 #include "base/string_number_conversions.h" | |
| 12 #include "net/url_request/request_throttler_header_interface.h" | |
| 13 | |
| 14 const int RequestThrottlerEntry::kDefaultSlidingWindowPeriodMs = 2000; | |
| 15 const int RequestThrottlerEntry::kDefaultMaxSendThreshold = 20; | |
| 16 const int RequestThrottlerEntry::kDefaultInitialBackoffMs = 700; | |
| 17 const int RequestThrottlerEntry::kDefaultAdditionalConstantMs = 100; | |
| 18 const double RequestThrottlerEntry::kDefaultMultiplyFactor = 2.0; | |
| 19 const double RequestThrottlerEntry::kDefaultJitterFactor = 0.4; | |
| 20 const int RequestThrottlerEntry::kDefaultMaximumBackoffMs = 24 * 60 * 60 * 1000; | |
| 21 const int RequestThrottlerEntry::kDefaultEntryLifetimeMs = 120000; | |
| 22 const char RequestThrottlerEntry::kRetryHeaderName[] = "X-Retry-After"; | |
| 23 | |
| 24 RequestThrottlerEntry::RequestThrottlerEntry() | |
| 25 : sliding_window_period_ms_(kDefaultSlidingWindowPeriodMs), | |
| 26 max_send_threshold_(kDefaultMaxSendThreshold), | |
| 27 initial_backoff_ms_(kDefaultInitialBackoffMs), | |
| 28 additional_constant_ms_(kDefaultAdditionalConstantMs), | |
| 29 multiply_factor_(kDefaultMultiplyFactor), | |
| 30 jitter_factor_(kDefaultJitterFactor), | |
| 31 maximum_backoff_ms_(kDefaultMaximumBackoffMs), | |
| 32 entry_lifetime_ms_(kDefaultEntryLifetimeMs) { | |
| 33 Initialize(); | |
| 34 } | |
| 35 | |
| 36 RequestThrottlerEntry::RequestThrottlerEntry( | |
| 37 int sliding_window_period_ms, | |
| 38 int max_send_threshold, | |
| 39 int initial_backoff_ms, | |
| 40 int additional_constant_ms, | |
| 41 double multiply_factor, | |
| 42 double jitter_factor, | |
| 43 int maximum_backoff_ms) | |
| 44 : sliding_window_period_ms_(sliding_window_period_ms), | |
| 45 max_send_threshold_(max_send_threshold), | |
| 46 initial_backoff_ms_(initial_backoff_ms), | |
| 47 additional_constant_ms_(additional_constant_ms), | |
| 48 multiply_factor_(multiply_factor), | |
| 49 jitter_factor_(jitter_factor), | |
| 50 maximum_backoff_ms_(maximum_backoff_ms), | |
| 51 entry_lifetime_ms_(-1) { | |
| 52 DCHECK(sliding_window_period_ms_ > 0 && | |
| 53 max_send_threshold_ > 0 && | |
| 54 initial_backoff_ms_ >= 0 && | |
| 55 additional_constant_ms_ >= 0 && | |
| 56 multiply_factor_ > 0 && | |
| 57 jitter_factor_ >= 0 && | |
| 58 maximum_backoff_ms_ >= 0); | |
| 59 | |
| 60 Initialize(); | |
| 61 } | |
| 62 | |
| 63 RequestThrottlerEntry::~RequestThrottlerEntry() { | |
| 64 } | |
| 65 | |
| 66 void RequestThrottlerEntry::Initialize() { | |
| 67 exponential_backoff_release_time_ = base::TimeTicks::Now(); | |
|
Jói
2010/11/12 00:12:08
suggest using GetTimeNow()
yzshen
2010/11/12 02:05:46
Using GetTimeNow() here is a little bit confusing,
Jói
2010/11/12 19:14:24
That's fine.
| |
| 68 failure_count_ = 0; | |
| 69 tracking_exponential_backoff_ = false; | |
| 70 | |
| 71 old_values_.exponential_backoff_release_time = | |
| 72 exponential_backoff_release_time_; | |
| 73 old_values_.failure_count = failure_count_; | |
| 74 | |
| 75 sliding_window_release_time_ = base::TimeTicks::Now(); | |
|
Jói
2010/11/12 00:12:08
likewise
yzshen
2010/11/12 02:05:46
Please see the comment above.
| |
| 76 } | |
| 77 | |
| 78 bool RequestThrottlerEntry::IsDuringExponentialBackoff() const { | |
| 79 AutoLock auto_lock(lock_); | |
| 80 return exponential_backoff_release_time_ > GetTimeNow(); | |
| 81 } | |
| 82 | |
| 83 int64 RequestThrottlerEntry::GetRecommendedDelayForNextRequest() { | |
| 84 AutoLock auto_lock(lock_); | |
| 85 | |
| 86 base::TimeTicks now = GetTimeNow(); | |
| 87 sliding_window_release_time_ = | |
| 88 std::max(now, std::max(exponential_backoff_release_time_, | |
| 89 sliding_window_release_time_)); | |
| 90 | |
| 91 int64 result = (sliding_window_release_time_ - now).InMillisecondsRoundedUp(); | |
| 92 | |
| 93 DCHECK(send_log_.empty() || | |
| 94 sliding_window_release_time_ >= send_log_.back()); | |
| 95 | |
| 96 // Log the new send event. | |
| 97 send_log_.push(sliding_window_release_time_); | |
| 98 | |
| 99 base::TimeDelta sliding_window_period = base::TimeDelta::FromMilliseconds( | |
| 100 sliding_window_period_ms_); | |
| 101 | |
| 102 // Drop the out-of-date events in the event list. | |
| 103 // We don't need to worry that the queue may become empty during this | |
| 104 // operation, since the last element is sliding_window_release_time_. | |
| 105 while ((send_log_.front() + sliding_window_period <= | |
| 106 sliding_window_release_time_) || | |
| 107 send_log_.size() > static_cast<unsigned>(max_send_threshold_)) { | |
| 108 send_log_.pop(); | |
| 109 } | |
| 110 | |
| 111 // Check if there are too many send events in recent time. | |
| 112 if (send_log_.size() == static_cast<unsigned>(max_send_threshold_)) | |
| 113 sliding_window_release_time_ = send_log_.front() + sliding_window_period; | |
| 114 | |
| 115 return result; | |
| 116 } | |
| 117 | |
| 118 void RequestThrottlerEntry::UpdateWithResponse( | |
| 119 const RequestThrottlerHeaderInterface* response) { | |
| 120 AutoLock auto_lock(lock_); | |
| 121 | |
| 122 SaveState(); | |
| 123 if (response->GetResponseCode() >= 500) { | |
| 124 failure_count_++; | |
| 125 exponential_backoff_release_time_ = | |
| 126 CalculateExponentialBackoffReleaseTime(); | |
| 127 tracking_exponential_backoff_ = true; | |
| 128 } else { | |
| 129 // We slowly decay the number of times delayed instead of resetting it to 0 | |
| 130 // in order to stay stable if we received lots of requests with | |
| 131 // malformed bodies at the same time. | |
| 132 if (failure_count_ > 0) | |
| 133 failure_count_--; | |
| 134 tracking_exponential_backoff_ = false; | |
|
Jói
2010/11/12 00:12:08
A comment on why this now gets set to false is in
yzshen
2010/11/12 02:05:46
Since this variable is mainly used for life span m
Jói
2010/11/12 19:14:24
The confusing part is that the invariant of data (
yzshen
2010/11/12 21:28:33
I could change its name to latest_response_is_erro
| |
| 135 // The reason why we are not just cutting the release time to GetTimeNow() | |
| 136 // is on the one hand, it would unset delay put by our custom retry-after | |
| 137 // header and on the other we would like to push every request up to our | |
| 138 // "horizon" when dealing with multiple in-flight requests. Ex: If we send | |
| 139 // three requests and we receive 2 failures and 1 success. The success that | |
| 140 // follows those failures will not reset the release time, further requests | |
| 141 // will then need to wait the delay caused by the 2 failures. | |
| 142 exponential_backoff_release_time_ = std::max( | |
| 143 GetTimeNow(), exponential_backoff_release_time_); | |
| 144 std::string retry_header = response->GetNormalizedValue(kRetryHeaderName); | |
| 145 if (!retry_header.empty()) | |
| 146 HandleCustomRetryAfter(retry_header); | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 bool RequestThrottlerEntry::IsEntryOutdated() const { | |
| 151 AutoLock auto_lock(lock_); | |
| 152 | |
| 153 if (entry_lifetime_ms_ == -1) | |
| 154 return false; | |
| 155 | |
| 156 base::TimeTicks now = GetTimeNow(); | |
| 157 | |
| 158 // If there are send events in the sliding window period, we still need this | |
| 159 // entry. | |
| 160 base::TimeDelta sliding_window_period = base::TimeDelta::FromMilliseconds( | |
| 161 sliding_window_period_ms_); | |
| 162 if (send_log_.size() > 0 && | |
| 163 send_log_.back() + sliding_window_period > now) { | |
| 164 return false; | |
| 165 } | |
| 166 | |
| 167 int64 unused_since_ms = | |
| 168 (now - exponential_backoff_release_time_).InMilliseconds(); | |
| 169 | |
| 170 // Release time is further than now, we are managing it. | |
| 171 if (unused_since_ms < 0) | |
| 172 return false; | |
| 173 | |
| 174 // There are two cases. First one, when the entry is currently being managed | |
| 175 // and should not be collected unless it is older than the maximum allowed | |
| 176 // back-off. The other one, when the entry is outdated, unmanaged and should | |
| 177 // be collected. | |
| 178 if (tracking_exponential_backoff_) | |
| 179 return unused_since_ms > std::max(maximum_backoff_ms_, entry_lifetime_ms_); | |
| 180 | |
| 181 return unused_since_ms > entry_lifetime_ms_; | |
| 182 } | |
| 183 | |
| 184 void RequestThrottlerEntry::ReceivedContentWasMalformed() { | |
| 185 AutoLock auto_lock(lock_); | |
| 186 | |
| 187 // We should never revert to less back-off or else an attacker could put a | |
| 188 // malformed body in cache and replay it to decrease delay. | |
| 189 failure_count_ = | |
| 190 std::max(old_values_.failure_count, failure_count_); | |
| 191 failure_count_++; | |
| 192 tracking_exponential_backoff_ = true; | |
| 193 exponential_backoff_release_time_ = | |
| 194 std::max(CalculateExponentialBackoffReleaseTime(), | |
| 195 old_values_.exponential_backoff_release_time); | |
| 196 } | |
| 197 | |
| 198 base::TimeTicks | |
| 199 RequestThrottlerEntry::CalculateExponentialBackoffReleaseTime() { | |
| 200 lock_.AssertAcquired(); | |
| 201 | |
| 202 double delay = initial_backoff_ms_; | |
| 203 delay *= pow(multiply_factor_, failure_count_); | |
| 204 delay += additional_constant_ms_; | |
| 205 delay -= base::RandDouble() * jitter_factor_ * delay; | |
| 206 | |
| 207 // Ensure that we do not exceed maximum delay. | |
| 208 int64 delay_int = static_cast<int64>(delay + 0.5); | |
| 209 delay_int = std::min(delay_int, static_cast<int64>(maximum_backoff_ms_)); | |
| 210 | |
| 211 return std::max(GetTimeNow() + base::TimeDelta::FromMilliseconds(delay_int), | |
| 212 exponential_backoff_release_time_); | |
| 213 } | |
| 214 | |
| 215 base::TimeTicks RequestThrottlerEntry::GetTimeNow() const { | |
| 216 return base::TimeTicks::Now(); | |
| 217 } | |
| 218 | |
| 219 void RequestThrottlerEntry::HandleCustomRetryAfter( | |
| 220 const std::string& header_value) { | |
| 221 lock_.AssertAcquired(); | |
| 222 | |
| 223 // Input parameter is the number of seconds to wait in a floating point value. | |
| 224 double time_in_sec = 0; | |
| 225 bool conversion_is_ok = base::StringToDouble(header_value, &time_in_sec); | |
| 226 | |
| 227 // Conversion of custom retry-after header value failed. | |
| 228 if (!conversion_is_ok) | |
| 229 return; | |
| 230 | |
| 231 // We must use an int value later so we transform this in milliseconds. | |
| 232 int64 value_ms = static_cast<int64>(0.5 + time_in_sec * 1000); | |
| 233 | |
| 234 if (maximum_backoff_ms_ < value_ms || value_ms < 0) | |
| 235 return; | |
| 236 | |
| 237 exponential_backoff_release_time_ = std::max( | |
| 238 (GetTimeNow() + base::TimeDelta::FromMilliseconds(value_ms)), | |
| 239 exponential_backoff_release_time_); | |
| 240 } | |
| 241 | |
| 242 void RequestThrottlerEntry::SaveState() { | |
| 243 lock_.AssertAcquired(); | |
| 244 | |
| 245 old_values_.exponential_backoff_release_time = | |
| 246 exponential_backoff_release_time_; | |
| 247 old_values_.failure_count = failure_count_; | |
| 248 } | |
| OLD | NEW |