OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/url_request/url_request_throttler_entry.h" | 5 #include "net/url_request/url_request_throttler_entry.h" |
6 | 6 |
7 #include <cmath> | 7 #include <cmath> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/rand_util.h" | 10 #include "base/rand_util.h" |
11 #include "base/string_number_conversions.h" | 11 #include "base/string_number_conversions.h" |
12 #include "net/url_request/url_request_throttler_header_interface.h" | 12 #include "net/url_request/url_request_throttler_header_interface.h" |
13 | 13 |
14 namespace net { | 14 namespace net { |
15 | 15 |
16 const int URLRequestThrottlerEntry::kDefaultSlidingWindowPeriodMs = 2000; | 16 const int URLRequestThrottlerEntry::kDefaultSlidingWindowPeriodMs = 2000; |
17 const int URLRequestThrottlerEntry::kDefaultMaxSendThreshold = 20; | 17 const int URLRequestThrottlerEntry::kDefaultMaxSendThreshold = 20; |
18 const int URLRequestThrottlerEntry::kDefaultInitialBackoffMs = 700; | 18 const int URLRequestThrottlerEntry::kDefaultInitialBackoffMs = 700; |
19 const int URLRequestThrottlerEntry::kDefaultAdditionalConstantMs = 100; | |
20 const double URLRequestThrottlerEntry::kDefaultMultiplyFactor = 1.4; | 19 const double URLRequestThrottlerEntry::kDefaultMultiplyFactor = 1.4; |
21 const double URLRequestThrottlerEntry::kDefaultJitterFactor = 0.4; | 20 const double URLRequestThrottlerEntry::kDefaultJitterFactor = 0.4; |
22 const int URLRequestThrottlerEntry::kDefaultMaximumBackoffMs = 60 * 60 * 1000; | 21 const int URLRequestThrottlerEntry::kDefaultMaximumBackoffMs = 60 * 60 * 1000; |
23 const int URLRequestThrottlerEntry::kDefaultEntryLifetimeMs = 120000; | 22 const int URLRequestThrottlerEntry::kDefaultEntryLifetimeMs = 120000; |
24 const char URLRequestThrottlerEntry::kRetryHeaderName[] = "X-Retry-After"; | 23 const char URLRequestThrottlerEntry::kRetryHeaderName[] = "X-Retry-After"; |
25 | 24 |
26 URLRequestThrottlerEntry::URLRequestThrottlerEntry() | 25 URLRequestThrottlerEntry::URLRequestThrottlerEntry() |
27 : sliding_window_period_( | 26 : sliding_window_period_( |
28 base::TimeDelta::FromMilliseconds(kDefaultSlidingWindowPeriodMs)), | 27 base::TimeDelta::FromMilliseconds(kDefaultSlidingWindowPeriodMs)), |
29 max_send_threshold_(kDefaultMaxSendThreshold), | 28 max_send_threshold_(kDefaultMaxSendThreshold), |
30 initial_backoff_ms_(kDefaultInitialBackoffMs), | 29 backoff_entry_(&backoff_policy_) { |
31 additional_constant_ms_(kDefaultAdditionalConstantMs), | |
32 multiply_factor_(kDefaultMultiplyFactor), | |
33 jitter_factor_(kDefaultJitterFactor), | |
34 maximum_backoff_ms_(kDefaultMaximumBackoffMs), | |
35 entry_lifetime_ms_(kDefaultEntryLifetimeMs) { | |
36 Initialize(); | 30 Initialize(); |
37 } | 31 } |
38 | 32 |
39 URLRequestThrottlerEntry::URLRequestThrottlerEntry( | 33 URLRequestThrottlerEntry::URLRequestThrottlerEntry( |
40 int sliding_window_period_ms, | 34 int sliding_window_period_ms, |
41 int max_send_threshold, | 35 int max_send_threshold, |
42 int initial_backoff_ms, | 36 int initial_backoff_ms, |
43 int additional_constant_ms, | |
44 double multiply_factor, | 37 double multiply_factor, |
45 double jitter_factor, | 38 double jitter_factor, |
46 int maximum_backoff_ms) | 39 int maximum_backoff_ms) |
47 : sliding_window_period_( | 40 : sliding_window_period_( |
48 base::TimeDelta::FromMilliseconds(sliding_window_period_ms)), | 41 base::TimeDelta::FromMilliseconds(sliding_window_period_ms)), |
49 max_send_threshold_(max_send_threshold), | 42 max_send_threshold_(max_send_threshold), |
50 initial_backoff_ms_(initial_backoff_ms), | 43 backoff_entry_(&backoff_policy_) { |
51 additional_constant_ms_(additional_constant_ms), | |
52 multiply_factor_(multiply_factor), | |
53 jitter_factor_(jitter_factor), | |
54 maximum_backoff_ms_(maximum_backoff_ms), | |
55 entry_lifetime_ms_(-1) { | |
56 DCHECK_GT(sliding_window_period_ms, 0); | 44 DCHECK_GT(sliding_window_period_ms, 0); |
57 DCHECK_GT(max_send_threshold_, 0); | 45 DCHECK_GT(max_send_threshold_, 0); |
58 DCHECK_GE(initial_backoff_ms_, 0); | 46 DCHECK_GE(initial_backoff_ms, 0); |
59 DCHECK_GE(additional_constant_ms_, 0); | 47 DCHECK_GT(multiply_factor, 0); |
60 DCHECK_GT(multiply_factor_, 0); | 48 DCHECK_GE(jitter_factor, 0.0); |
61 DCHECK_GE(jitter_factor_, 0); | 49 DCHECK_LT(jitter_factor, 1.0); |
62 DCHECK_LT(jitter_factor_, 1); | 50 DCHECK_GE(maximum_backoff_ms, 0); |
63 DCHECK_GE(maximum_backoff_ms_, 0); | |
64 | 51 |
65 Initialize(); | 52 Initialize(); |
| 53 backoff_policy_.initial_backoff_ms_ = initial_backoff_ms; |
| 54 backoff_policy_.multiply_factor_ = multiply_factor; |
| 55 backoff_policy_.jitter_factor_ = jitter_factor; |
| 56 backoff_policy_.maximum_backoff_ms_ = maximum_backoff_ms; |
| 57 backoff_policy_.entry_lifetime_ms_ = -1; |
66 } | 58 } |
67 | 59 |
68 bool URLRequestThrottlerEntry::IsEntryOutdated() const { | 60 bool URLRequestThrottlerEntry::IsEntryOutdated() const { |
69 CHECK(this); // to help track crbug.com/71721 | 61 return GetBackoffEntry()->CanDiscard(); |
70 if (entry_lifetime_ms_ == -1) | |
71 return false; | |
72 | |
73 base::TimeTicks now = GetTimeNow(); | |
74 | |
75 // If there are send events in the sliding window period, we still need this | |
76 // entry. | |
77 if (!send_log_.empty() && | |
78 send_log_.back() + sliding_window_period_ > now) { | |
79 return false; | |
80 } | |
81 | |
82 int64 unused_since_ms = | |
83 (now - exponential_backoff_release_time_).InMilliseconds(); | |
84 | |
85 // Release time is further than now, we are managing it. | |
86 if (unused_since_ms < 0) | |
87 return false; | |
88 | |
89 // latest_response_was_failure_ is true indicates that the latest one or | |
90 // more requests encountered server errors or had malformed response bodies. | |
91 // In that case, we don't want to collect the entry unless it hasn't been used | |
92 // for longer than the maximum allowed back-off. | |
93 if (latest_response_was_failure_) | |
94 return unused_since_ms > std::max(maximum_backoff_ms_, entry_lifetime_ms_); | |
95 | |
96 // Otherwise, consider the entry is outdated if it hasn't been used for the | |
97 // specified lifetime period. | |
98 return unused_since_ms > entry_lifetime_ms_; | |
99 } | 62 } |
100 | 63 |
101 bool URLRequestThrottlerEntry::IsDuringExponentialBackoff() const { | 64 bool URLRequestThrottlerEntry::IsDuringExponentialBackoff() const { |
102 return exponential_backoff_release_time_ > GetTimeNow(); | 65 return GetBackoffEntry()->ShouldRejectRequest(); |
103 } | 66 } |
104 | 67 |
105 int64 URLRequestThrottlerEntry::ReserveSendingTimeForNextRequest( | 68 int64 URLRequestThrottlerEntry::ReserveSendingTimeForNextRequest( |
106 const base::TimeTicks& earliest_time) { | 69 const base::TimeTicks& earliest_time) { |
107 base::TimeTicks now = GetTimeNow(); | 70 base::TimeTicks now = GetTimeNow(); |
| 71 |
108 // If a lot of requests were successfully made recently, | 72 // If a lot of requests were successfully made recently, |
109 // sliding_window_release_time_ may be greater than | 73 // sliding_window_release_time_ may be greater than |
110 // exponential_backoff_release_time_. | 74 // exponential_backoff_release_time_. |
111 base::TimeTicks recommended_sending_time = | 75 base::TimeTicks recommended_sending_time = |
112 std::max(std::max(now, earliest_time), | 76 std::max(std::max(now, earliest_time), |
113 std::max(exponential_backoff_release_time_, | 77 std::max(GetBackoffEntry()->GetReleaseTime(), |
114 sliding_window_release_time_)); | 78 sliding_window_release_time_)); |
115 | 79 |
116 DCHECK(send_log_.empty() || | 80 DCHECK(send_log_.empty() || |
117 recommended_sending_time >= send_log_.back()); | 81 recommended_sending_time >= send_log_.back()); |
118 // Log the new send event. | 82 // Log the new send event. |
119 send_log_.push(recommended_sending_time); | 83 send_log_.push(recommended_sending_time); |
120 | 84 |
121 sliding_window_release_time_ = recommended_sending_time; | 85 sliding_window_release_time_ = recommended_sending_time; |
122 | 86 |
123 // Drop the out-of-date events in the event list. | 87 // Drop the out-of-date events in the event list. |
124 // We don't need to worry that the queue may become empty during this | 88 // We don't need to worry that the queue may become empty during this |
125 // operation, since the last element is sliding_window_release_time_. | 89 // operation, since the last element is sliding_window_release_time_. |
126 while ((send_log_.front() + sliding_window_period_ <= | 90 while ((send_log_.front() + sliding_window_period_ <= |
127 sliding_window_release_time_) || | 91 sliding_window_release_time_) || |
128 send_log_.size() > static_cast<unsigned>(max_send_threshold_)) { | 92 send_log_.size() > static_cast<unsigned>(max_send_threshold_)) { |
129 send_log_.pop(); | 93 send_log_.pop(); |
130 } | 94 } |
131 | 95 |
132 // Check if there are too many send events in recent time. | 96 // Check if there are too many send events in recent time. |
133 if (send_log_.size() == static_cast<unsigned>(max_send_threshold_)) | 97 if (send_log_.size() == static_cast<unsigned>(max_send_threshold_)) |
134 sliding_window_release_time_ = send_log_.front() + sliding_window_period_; | 98 sliding_window_release_time_ = send_log_.front() + sliding_window_period_; |
135 | 99 |
136 return (recommended_sending_time - now).InMillisecondsRoundedUp(); | 100 return (recommended_sending_time - now).InMillisecondsRoundedUp(); |
137 } | 101 } |
138 | 102 |
139 base::TimeTicks | 103 base::TimeTicks |
140 URLRequestThrottlerEntry::GetExponentialBackoffReleaseTime() const { | 104 URLRequestThrottlerEntry::GetExponentialBackoffReleaseTime() const { |
141 return exponential_backoff_release_time_; | 105 return GetBackoffEntry()->GetReleaseTime(); |
142 } | 106 } |
143 | 107 |
144 void URLRequestThrottlerEntry::UpdateWithResponse( | 108 void URLRequestThrottlerEntry::UpdateWithResponse( |
145 const URLRequestThrottlerHeaderInterface* response) { | 109 const URLRequestThrottlerHeaderInterface* response) { |
146 if (response->GetResponseCode() >= 500) { | 110 if (response->GetResponseCode() >= 500) { |
147 failure_count_++; | 111 GetBackoffEntry()->InformOfRequest(false); |
148 latest_response_was_failure_ = true; | |
149 exponential_backoff_release_time_ = | |
150 CalculateExponentialBackoffReleaseTime(); | |
151 } else { | 112 } else { |
152 // We slowly decay the number of times delayed instead of resetting it to 0 | 113 GetBackoffEntry()->InformOfRequest(true); |
153 // in order to stay stable if we received lots of requests with | |
154 // malformed bodies at the same time. | |
155 if (failure_count_ > 0) | |
156 failure_count_--; | |
157 | |
158 latest_response_was_failure_ = false; | |
159 | |
160 // The reason why we are not just cutting the release time to GetTimeNow() | |
161 // is on the one hand, it would unset delay put by our custom retry-after | |
162 // header and on the other we would like to push every request up to our | |
163 // "horizon" when dealing with multiple in-flight requests. Ex: If we send | |
164 // three requests and we receive 2 failures and 1 success. The success that | |
165 // follows those failures will not reset the release time, further requests | |
166 // will then need to wait the delay caused by the 2 failures. | |
167 exponential_backoff_release_time_ = std::max( | |
168 GetTimeNow(), exponential_backoff_release_time_); | |
169 | 114 |
170 std::string retry_header = response->GetNormalizedValue(kRetryHeaderName); | 115 std::string retry_header = response->GetNormalizedValue(kRetryHeaderName); |
171 if (!retry_header.empty()) | 116 if (!retry_header.empty()) |
172 HandleCustomRetryAfter(retry_header); | 117 HandleCustomRetryAfter(retry_header); |
173 } | 118 } |
174 } | 119 } |
175 | 120 |
176 void URLRequestThrottlerEntry::ReceivedContentWasMalformed() { | 121 void URLRequestThrottlerEntry::ReceivedContentWasMalformed() { |
177 // For any response that is marked as malformed now, we have probably | 122 // We keep this simple and just count it as an error. |
178 // considered it as a success when receiving it and decreased the failure | 123 GetBackoffEntry()->InformOfRequest(false); |
179 // count by 1. As a result, we increase the failure count by 2 here to undo | |
180 // the effect and record a failure. | |
181 // | |
182 // Please note that this may lead to a larger failure count than expected, | |
183 // because we don't decrease the failure count for successful responses when | |
184 // it has already reached 0. | |
185 failure_count_ += 2; | |
186 latest_response_was_failure_ = true; | |
187 exponential_backoff_release_time_ = CalculateExponentialBackoffReleaseTime(); | |
188 } | |
189 | |
190 void URLRequestThrottlerEntry::SetEntryLifetimeMsForTest(int lifetime_ms) { | |
191 entry_lifetime_ms_ = lifetime_ms; | |
192 } | 124 } |
193 | 125 |
194 URLRequestThrottlerEntry::~URLRequestThrottlerEntry() { | 126 URLRequestThrottlerEntry::~URLRequestThrottlerEntry() { |
195 } | 127 } |
196 | 128 |
197 void URLRequestThrottlerEntry::Initialize() { | 129 void URLRequestThrottlerEntry::Initialize() { |
198 // Since this method is called by the constructors, GetTimeNow() (a virtual | |
199 // method) is not used. | |
200 exponential_backoff_release_time_ = base::TimeTicks::Now(); | |
201 failure_count_ = 0; | |
202 latest_response_was_failure_ = false; | |
203 | |
204 sliding_window_release_time_ = base::TimeTicks::Now(); | 130 sliding_window_release_time_ = base::TimeTicks::Now(); |
205 } | 131 backoff_policy_.num_errors_to_ignore_ = 0; |
206 | 132 backoff_policy_.initial_backoff_ms_ = kDefaultInitialBackoffMs; |
207 base::TimeTicks | 133 backoff_policy_.multiply_factor_ = kDefaultMultiplyFactor; |
208 URLRequestThrottlerEntry::CalculateExponentialBackoffReleaseTime() { | 134 backoff_policy_.jitter_factor_ = kDefaultJitterFactor; |
209 double delay = initial_backoff_ms_; | 135 backoff_policy_.maximum_backoff_ms_ = kDefaultMaximumBackoffMs; |
210 delay *= pow(multiply_factor_, failure_count_); | 136 backoff_policy_.entry_lifetime_ms_ = kDefaultEntryLifetimeMs; |
211 delay += additional_constant_ms_; | |
212 delay -= base::RandDouble() * jitter_factor_ * delay; | |
213 | |
214 // Ensure that we do not exceed maximum delay. | |
215 int64 delay_int = static_cast<int64>(delay + 0.5); | |
216 delay_int = std::min(delay_int, static_cast<int64>(maximum_backoff_ms_)); | |
217 | |
218 return std::max(GetTimeNow() + base::TimeDelta::FromMilliseconds(delay_int), | |
219 exponential_backoff_release_time_); | |
220 } | 137 } |
221 | 138 |
222 base::TimeTicks URLRequestThrottlerEntry::GetTimeNow() const { | 139 base::TimeTicks URLRequestThrottlerEntry::GetTimeNow() const { |
223 return base::TimeTicks::Now(); | 140 return base::TimeTicks::Now(); |
224 } | 141 } |
225 | 142 |
226 void URLRequestThrottlerEntry::HandleCustomRetryAfter( | 143 void URLRequestThrottlerEntry::HandleCustomRetryAfter( |
227 const std::string& header_value) { | 144 const std::string& header_value) { |
228 // Input parameter is the number of seconds to wait in a floating point value. | 145 // Input parameter is the number of seconds to wait in a floating point value. |
229 double time_in_sec = 0; | 146 double time_in_sec = 0; |
230 bool conversion_is_ok = base::StringToDouble(header_value, &time_in_sec); | 147 bool conversion_is_ok = base::StringToDouble(header_value, &time_in_sec); |
231 | 148 |
232 // Conversion of custom retry-after header value failed. | 149 // Conversion of custom retry-after header value failed. |
233 if (!conversion_is_ok) | 150 if (!conversion_is_ok) |
234 return; | 151 return; |
235 | 152 |
236 // We must use an int value later so we transform this in milliseconds. | 153 // We must use an int value later so we transform this in milliseconds. |
237 int64 value_ms = static_cast<int64>(0.5 + time_in_sec * 1000); | 154 int64 value_ms = static_cast<int64>(0.5 + time_in_sec * 1000); |
238 | 155 |
239 if (maximum_backoff_ms_ < value_ms || value_ms < 0) | 156 // We do not check for an upper bound; the server can set any Retry-After it |
| 157 // desires. Recovery from error would involve restarting the browser. |
| 158 if (value_ms < 0) |
240 return; | 159 return; |
241 | 160 |
242 exponential_backoff_release_time_ = std::max( | 161 GetBackoffEntry()->SetCustomReleaseTime( |
243 (GetTimeNow() + base::TimeDelta::FromMilliseconds(value_ms)), | 162 GetTimeNow() + base::TimeDelta::FromMilliseconds(value_ms)); |
244 exponential_backoff_release_time_); | 163 } |
| 164 |
| 165 const BackoffEntry* URLRequestThrottlerEntry::GetBackoffEntry() const { |
| 166 return &backoff_entry_; |
| 167 } |
| 168 |
| 169 BackoffEntry* URLRequestThrottlerEntry::GetBackoffEntry() { |
| 170 return &backoff_entry_; |
245 } | 171 } |
246 | 172 |
247 } // namespace net | 173 } // namespace net |
OLD | NEW |