Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 #include "net/url_request/url_request_throttler_manager.h" | |
| 13 | 14 |
| 14 namespace net { | 15 namespace net { |
| 15 | 16 |
| 16 const int URLRequestThrottlerEntry::kDefaultSlidingWindowPeriodMs = 2000; | 17 const int URLRequestThrottlerEntry::kDefaultSlidingWindowPeriodMs = 2000; |
| 17 const int URLRequestThrottlerEntry::kDefaultMaxSendThreshold = 20; | 18 const int URLRequestThrottlerEntry::kDefaultMaxSendThreshold = 20; |
| 19 | |
| 20 // This set of back-off parameters will (at maximum values, i.e. without | |
| 21 // the reduction caused by jitter) add 0-41% (distributed uniformly | |
| 22 // in that range) to the "perceived downtime" of the remote server, once | |
| 23 // exponential back-off kicks in and is throttling requests for more than | |
| 24 // about a second at a time. Once the maximum back-off is reached, the added | |
| 25 // perceived downtime decreases rapidly, percentage-wise. | |
| 26 // | |
| 27 // Another way to put it is that the maximum additional perceived downtime | |
| 28 // with these numbers is a couple of seconds shy of 15 minutes, and such | |
| 29 // a delay would not occur until the remote server has been actually | |
| 30 // unavailable at the end of each back-off period for a total of about | |
| 31 // 48 minutes. | |
| 32 // | |
| 33 // Ignoring the first 4 errors helps avoid back-off from kicking in on | |
| 34 // flaky connections. | |
| 35 const int URLRequestThrottlerEntry::kDefaultNumErrorsToIgnore = 4; | |
| 18 const int URLRequestThrottlerEntry::kDefaultInitialBackoffMs = 700; | 36 const int URLRequestThrottlerEntry::kDefaultInitialBackoffMs = 700; |
| 19 const double URLRequestThrottlerEntry::kDefaultMultiplyFactor = 1.4; | 37 const double URLRequestThrottlerEntry::kDefaultMultiplyFactor = 1.4; |
| 20 const double URLRequestThrottlerEntry::kDefaultJitterFactor = 0.4; | 38 const double URLRequestThrottlerEntry::kDefaultJitterFactor = 0.4; |
| 21 const int URLRequestThrottlerEntry::kDefaultMaximumBackoffMs = 60 * 60 * 1000; | 39 const int URLRequestThrottlerEntry::kDefaultMaximumBackoffMs = 15 * 60 * 1000; |
| 22 const int URLRequestThrottlerEntry::kDefaultEntryLifetimeMs = 120000; | 40 const int URLRequestThrottlerEntry::kDefaultEntryLifetimeMs = 2 * 60 * 1000; |
| 23 const char URLRequestThrottlerEntry::kRetryHeaderName[] = "X-Retry-After"; | 41 const char URLRequestThrottlerEntry::kRetryHeaderName[] = "X-Retry-After"; |
| 42 const char URLRequestThrottlerEntry::kExponentialThrottlingHeader[] = | |
| 43 "X-Chrome-Exponential-Throttling"; | |
| 44 const char URLRequestThrottlerEntry::kExponentialThrottlingDisableValue[] = | |
| 45 "disable"; | |
| 24 | 46 |
| 25 URLRequestThrottlerEntry::URLRequestThrottlerEntry() | 47 URLRequestThrottlerEntry::URLRequestThrottlerEntry( |
| 48 URLRequestThrottlerManager* manager) | |
| 26 : sliding_window_period_( | 49 : sliding_window_period_( |
| 27 base::TimeDelta::FromMilliseconds(kDefaultSlidingWindowPeriodMs)), | 50 base::TimeDelta::FromMilliseconds(kDefaultSlidingWindowPeriodMs)), |
| 28 max_send_threshold_(kDefaultMaxSendThreshold), | 51 max_send_threshold_(kDefaultMaxSendThreshold), |
| 29 backoff_entry_(&backoff_policy_) { | 52 is_backoff_disabled_(false), |
| 53 backoff_entry_(&backoff_policy_), | |
| 54 manager_(manager) { | |
| 55 DCHECK(manager_); | |
| 30 Initialize(); | 56 Initialize(); |
| 31 } | 57 } |
| 32 | 58 |
| 33 URLRequestThrottlerEntry::URLRequestThrottlerEntry( | 59 URLRequestThrottlerEntry::URLRequestThrottlerEntry( |
| 60 URLRequestThrottlerManager* manager, | |
| 34 int sliding_window_period_ms, | 61 int sliding_window_period_ms, |
| 35 int max_send_threshold, | 62 int max_send_threshold, |
| 36 int initial_backoff_ms, | 63 int initial_backoff_ms, |
| 37 double multiply_factor, | 64 double multiply_factor, |
| 38 double jitter_factor, | 65 double jitter_factor, |
| 39 int maximum_backoff_ms) | 66 int maximum_backoff_ms) |
| 40 : sliding_window_period_( | 67 : sliding_window_period_( |
| 41 base::TimeDelta::FromMilliseconds(sliding_window_period_ms)), | 68 base::TimeDelta::FromMilliseconds(sliding_window_period_ms)), |
| 42 max_send_threshold_(max_send_threshold), | 69 max_send_threshold_(max_send_threshold), |
| 43 backoff_entry_(&backoff_policy_) { | 70 is_backoff_disabled_(false), |
| 71 backoff_entry_(&backoff_policy_), | |
| 72 manager_(manager) { | |
| 44 DCHECK_GT(sliding_window_period_ms, 0); | 73 DCHECK_GT(sliding_window_period_ms, 0); |
| 45 DCHECK_GT(max_send_threshold_, 0); | 74 DCHECK_GT(max_send_threshold_, 0); |
| 46 DCHECK_GE(initial_backoff_ms, 0); | 75 DCHECK_GE(initial_backoff_ms, 0); |
| 47 DCHECK_GT(multiply_factor, 0); | 76 DCHECK_GT(multiply_factor, 0); |
| 48 DCHECK_GE(jitter_factor, 0.0); | 77 DCHECK_GE(jitter_factor, 0.0); |
| 49 DCHECK_LT(jitter_factor, 1.0); | 78 DCHECK_LT(jitter_factor, 1.0); |
| 50 DCHECK_GE(maximum_backoff_ms, 0); | 79 DCHECK_GE(maximum_backoff_ms, 0); |
| 80 DCHECK(manager_); | |
| 51 | 81 |
| 52 Initialize(); | 82 Initialize(); |
| 53 backoff_policy_.initial_backoff_ms = initial_backoff_ms; | 83 backoff_policy_.initial_backoff_ms = initial_backoff_ms; |
| 54 backoff_policy_.multiply_factor = multiply_factor; | 84 backoff_policy_.multiply_factor = multiply_factor; |
| 55 backoff_policy_.jitter_factor = jitter_factor; | 85 backoff_policy_.jitter_factor = jitter_factor; |
| 56 backoff_policy_.maximum_backoff_ms = maximum_backoff_ms; | 86 backoff_policy_.maximum_backoff_ms = maximum_backoff_ms; |
| 57 backoff_policy_.entry_lifetime_ms = -1; | 87 backoff_policy_.entry_lifetime_ms = -1; |
| 58 } | 88 } |
| 59 | 89 |
| 60 bool URLRequestThrottlerEntry::IsEntryOutdated() const { | 90 bool URLRequestThrottlerEntry::IsEntryOutdated() const { |
| 61 // If there is more than one reference on us, it means a URLRequestHttpJob | 91 // If there is more than one reference on us, it means a URLRequestHttpJob |
| 62 // or URLFetcher holds a reference. If we were garbage collected from the | 92 // or URLFetcher holds a reference. If we were garbage collected from the |
| 63 // URLRequestThrottlerManager map while this is true, then separate clients | 93 // URLRequestThrottlerManager map while this is true, then separate clients |
| 64 // could end up holding separate objects for the "same" request, which | 94 // could end up holding separate objects for the "same" request, which |
| 65 // is undesirable. These clients are short-lived anyway, so they shouldn't | 95 // is undesirable. These clients are short-lived anyway, so they shouldn't |
| 66 // normally prevent items for being garbage collected for long. | 96 // normally prevent items for being garbage collected for long. |
| 67 if (!HasOneRef()) | 97 if (!HasOneRef()) |
| 68 return false; | 98 return false; |
| 69 | 99 |
| 70 // If there are send events in the sliding window period, we still need this | 100 // If there are send events in the sliding window period, we still need this |
| 71 // entry. | 101 // entry. |
| 72 if (!send_log_.empty() && | 102 if (!send_log_.empty() && |
| 73 send_log_.back() + sliding_window_period_ > GetTimeNow()) { | 103 send_log_.back() + sliding_window_period_ > GetTimeNow()) { |
| 74 return false; | 104 return false; |
| 75 } | 105 } |
| 76 | 106 |
| 77 return GetBackoffEntry()->CanDiscard(); | 107 return GetBackoffEntry()->CanDiscard(); |
| 78 } | 108 } |
| 79 | 109 |
| 110 void URLRequestThrottlerEntry::DisableBackoffThrottling() { | |
| 111 is_backoff_disabled_ = true; | |
| 112 } | |
| 113 | |
| 80 bool URLRequestThrottlerEntry::IsDuringExponentialBackoff() const { | 114 bool URLRequestThrottlerEntry::IsDuringExponentialBackoff() const { |
| 115 if (is_backoff_disabled_) | |
|
yzshen1
2011/03/18 22:49:51
[optional]
We have a enforce_throttling member in
Jói
2011/03/23 23:38:24
I don't think so; it's enforced in URLRequestHttpJ
| |
| 116 return false; | |
| 117 | |
| 81 return GetBackoffEntry()->ShouldRejectRequest(); | 118 return GetBackoffEntry()->ShouldRejectRequest(); |
| 82 } | 119 } |
| 83 | 120 |
| 84 int64 URLRequestThrottlerEntry::ReserveSendingTimeForNextRequest( | 121 int64 URLRequestThrottlerEntry::ReserveSendingTimeForNextRequest( |
| 85 const base::TimeTicks& earliest_time) { | 122 const base::TimeTicks& earliest_time) { |
| 86 base::TimeTicks now = GetTimeNow(); | 123 base::TimeTicks now = GetTimeNow(); |
| 87 | 124 |
| 88 // If a lot of requests were successfully made recently, | 125 // If a lot of requests were successfully made recently, |
| 89 // sliding_window_release_time_ may be greater than | 126 // sliding_window_release_time_ may be greater than |
| 90 // exponential_backoff_release_time_. | 127 // exponential_backoff_release_time_. |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 111 | 148 |
| 112 // Check if there are too many send events in recent time. | 149 // Check if there are too many send events in recent time. |
| 113 if (send_log_.size() == static_cast<unsigned>(max_send_threshold_)) | 150 if (send_log_.size() == static_cast<unsigned>(max_send_threshold_)) |
| 114 sliding_window_release_time_ = send_log_.front() + sliding_window_period_; | 151 sliding_window_release_time_ = send_log_.front() + sliding_window_period_; |
| 115 | 152 |
| 116 return (recommended_sending_time - now).InMillisecondsRoundedUp(); | 153 return (recommended_sending_time - now).InMillisecondsRoundedUp(); |
| 117 } | 154 } |
| 118 | 155 |
| 119 base::TimeTicks | 156 base::TimeTicks |
| 120 URLRequestThrottlerEntry::GetExponentialBackoffReleaseTime() const { | 157 URLRequestThrottlerEntry::GetExponentialBackoffReleaseTime() const { |
| 158 if (is_backoff_disabled_) | |
| 159 return GetTimeNow(); | |
|
yzshen1
2011/03/18 22:49:51
[I don't have a strong opinion here, just to make
Jói
2011/03/23 23:38:24
I believe if a site opts out, it's likely because
| |
| 160 | |
| 121 return GetBackoffEntry()->GetReleaseTime(); | 161 return GetBackoffEntry()->GetReleaseTime(); |
| 122 } | 162 } |
| 123 | 163 |
| 124 void URLRequestThrottlerEntry::UpdateWithResponse( | 164 void URLRequestThrottlerEntry::UpdateWithResponse( |
| 165 const std::string& host, | |
| 125 const URLRequestThrottlerHeaderInterface* response) { | 166 const URLRequestThrottlerHeaderInterface* response) { |
| 126 if (response->GetResponseCode() >= 500) { | 167 if (response->GetResponseCode() >= 500) { |
| 127 GetBackoffEntry()->InformOfRequest(false); | 168 GetBackoffEntry()->InformOfRequest(false); |
| 128 } else { | 169 } else { |
| 129 GetBackoffEntry()->InformOfRequest(true); | 170 GetBackoffEntry()->InformOfRequest(true); |
| 130 | 171 |
| 131 std::string retry_header = response->GetNormalizedValue(kRetryHeaderName); | 172 std::string retry_header = response->GetNormalizedValue(kRetryHeaderName); |
| 132 if (!retry_header.empty()) | 173 if (!retry_header.empty()) |
| 133 HandleCustomRetryAfter(retry_header); | 174 HandleCustomRetryAfter(retry_header); |
| 175 | |
| 176 std::string throttling_header = response->GetNormalizedValue( | |
| 177 kExponentialThrottlingHeader); | |
| 178 if (!throttling_header.empty()) | |
| 179 HandleThrottlingHeader(throttling_header, host); | |
| 134 } | 180 } |
| 135 } | 181 } |
| 136 | 182 |
| 137 void URLRequestThrottlerEntry::ReceivedContentWasMalformed() { | 183 void URLRequestThrottlerEntry::ReceivedContentWasMalformed() { |
| 138 // We keep this simple and just count it as a single error. | 184 // We keep this simple and just count it as a single error. |
| 139 // | 185 // |
| 140 // If we wanted to get fancy, we would count two errors here, and decrease | 186 // If we wanted to get fancy, we would count two errors here, and decrease |
| 141 // the error count only by one when we receive a successful (by status | 187 // the error count only by one when we receive a successful (by status |
| 142 // code) response. Instead, we keep things simple by always resetting the | 188 // code) response. Instead, we keep things simple by always resetting the |
| 143 // error count on success, and therefore counting only a single error here. | 189 // error count on success, and therefore counting only a single error here. |
| 144 GetBackoffEntry()->InformOfRequest(false); | 190 GetBackoffEntry()->InformOfRequest(false); |
| 145 } | 191 } |
| 146 | 192 |
| 147 URLRequestThrottlerEntry::~URLRequestThrottlerEntry() { | 193 URLRequestThrottlerEntry::~URLRequestThrottlerEntry() { |
| 148 } | 194 } |
| 149 | 195 |
| 150 void URLRequestThrottlerEntry::Initialize() { | 196 void URLRequestThrottlerEntry::Initialize() { |
| 151 sliding_window_release_time_ = base::TimeTicks::Now(); | 197 sliding_window_release_time_ = base::TimeTicks::Now(); |
| 152 backoff_policy_.num_errors_to_ignore = 0; | 198 backoff_policy_.num_errors_to_ignore = kDefaultNumErrorsToIgnore; |
| 153 backoff_policy_.initial_backoff_ms = kDefaultInitialBackoffMs; | 199 backoff_policy_.initial_backoff_ms = kDefaultInitialBackoffMs; |
| 154 backoff_policy_.multiply_factor = kDefaultMultiplyFactor; | 200 backoff_policy_.multiply_factor = kDefaultMultiplyFactor; |
| 155 backoff_policy_.jitter_factor = kDefaultJitterFactor; | 201 backoff_policy_.jitter_factor = kDefaultJitterFactor; |
| 156 backoff_policy_.maximum_backoff_ms = kDefaultMaximumBackoffMs; | 202 backoff_policy_.maximum_backoff_ms = kDefaultMaximumBackoffMs; |
| 157 backoff_policy_.entry_lifetime_ms = kDefaultEntryLifetimeMs; | 203 backoff_policy_.entry_lifetime_ms = kDefaultEntryLifetimeMs; |
| 158 } | 204 } |
| 159 | 205 |
| 160 base::TimeTicks URLRequestThrottlerEntry::GetTimeNow() const { | 206 base::TimeTicks URLRequestThrottlerEntry::GetTimeNow() const { |
| 161 return base::TimeTicks::Now(); | 207 return base::TimeTicks::Now(); |
| 162 } | 208 } |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 176 | 222 |
| 177 // We do not check for an upper bound; the server can set any Retry-After it | 223 // We do not check for an upper bound; the server can set any Retry-After it |
| 178 // desires. Recovery from error would involve restarting the browser. | 224 // desires. Recovery from error would involve restarting the browser. |
| 179 if (value_ms < 0) | 225 if (value_ms < 0) |
| 180 return; | 226 return; |
| 181 | 227 |
| 182 GetBackoffEntry()->SetCustomReleaseTime( | 228 GetBackoffEntry()->SetCustomReleaseTime( |
| 183 GetTimeNow() + base::TimeDelta::FromMilliseconds(value_ms)); | 229 GetTimeNow() + base::TimeDelta::FromMilliseconds(value_ms)); |
| 184 } | 230 } |
| 185 | 231 |
| 232 void URLRequestThrottlerEntry::HandleThrottlingHeader( | |
| 233 const std::string& header_value, | |
| 234 const std::string& host) { | |
| 235 if (header_value == kExponentialThrottlingDisableValue) { | |
| 236 DisableBackoffThrottling(); | |
| 237 manager_->AddToOptOutList(host); | |
| 238 } else { | |
| 239 // TODO(joi): Log this. | |
| 240 } | |
| 241 } | |
| 242 | |
| 186 const BackoffEntry* URLRequestThrottlerEntry::GetBackoffEntry() const { | 243 const BackoffEntry* URLRequestThrottlerEntry::GetBackoffEntry() const { |
| 187 return &backoff_entry_; | 244 return &backoff_entry_; |
| 188 } | 245 } |
| 189 | 246 |
| 190 BackoffEntry* URLRequestThrottlerEntry::GetBackoffEntry() { | 247 BackoffEntry* URLRequestThrottlerEntry::GetBackoffEntry() { |
| 191 return &backoff_entry_; | 248 return &backoff_entry_; |
| 192 } | 249 } |
| 193 | 250 |
| 194 } // namespace net | 251 } // namespace net |
| OLD | NEW |