| 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; |
| 88 backoff_policy_.num_errors_to_ignore = 0; |
| 58 } | 89 } |
| 59 | 90 |
| 60 bool URLRequestThrottlerEntry::IsEntryOutdated() const { | 91 bool URLRequestThrottlerEntry::IsEntryOutdated() const { |
| 61 // This function is called by the URLRequestThrottlerManager to determine | 92 // This function is called by the URLRequestThrottlerManager to determine |
| 62 // whether entries should be discarded from its url_entries_ map. We | 93 // whether entries should be discarded from its url_entries_ map. We |
| 63 // want to ensure that it does not remove entries from the map while there | 94 // want to ensure that it does not remove entries from the map while there |
| 64 // are clients (objects other than the manager) holding references to | 95 // are clients (objects other than the manager) holding references to |
| 65 // the entry, otherwise separate clients could end up holding separate | 96 // the entry, otherwise separate clients could end up holding separate |
| 66 // entries for a request to the same URL, which is undesirable. Therefore, | 97 // entries for a request to the same URL, which is undesirable. Therefore, |
| 67 // if an entry has more than one reference (the map will always hold one), | 98 // if an entry has more than one reference (the map will always hold one), |
| 68 // it should not be considered outdated. | 99 // it should not be considered outdated. |
| 69 // | 100 // |
| 70 // TODO(joi): Once the manager is not a Singleton, revisit whether | 101 // TODO(joi): Once the manager is not a Singleton, revisit whether |
| 71 // refcounting is needed at all. | 102 // refcounting is needed at all. |
| 72 if (!HasOneRef()) | 103 if (!HasOneRef()) |
| 73 return false; | 104 return false; |
| 74 | 105 |
| 75 // If there are send events in the sliding window period, we still need this | 106 // If there are send events in the sliding window period, we still need this |
| 76 // entry. | 107 // entry. |
| 77 if (!send_log_.empty() && | 108 if (!send_log_.empty() && |
| 78 send_log_.back() + sliding_window_period_ > GetTimeNow()) { | 109 send_log_.back() + sliding_window_period_ > GetTimeNow()) { |
| 79 return false; | 110 return false; |
| 80 } | 111 } |
| 81 | 112 |
| 82 return GetBackoffEntry()->CanDiscard(); | 113 return GetBackoffEntry()->CanDiscard(); |
| 83 } | 114 } |
| 84 | 115 |
| 116 void URLRequestThrottlerEntry::DisableBackoffThrottling() { |
| 117 is_backoff_disabled_ = true; |
| 118 } |
| 119 |
| 120 void URLRequestThrottlerEntry::DetachManager() { |
| 121 manager_ = NULL; |
| 122 } |
| 123 |
| 85 bool URLRequestThrottlerEntry::IsDuringExponentialBackoff() const { | 124 bool URLRequestThrottlerEntry::IsDuringExponentialBackoff() const { |
| 125 if (is_backoff_disabled_) |
| 126 return false; |
| 127 |
| 86 return GetBackoffEntry()->ShouldRejectRequest(); | 128 return GetBackoffEntry()->ShouldRejectRequest(); |
| 87 } | 129 } |
| 88 | 130 |
| 89 int64 URLRequestThrottlerEntry::ReserveSendingTimeForNextRequest( | 131 int64 URLRequestThrottlerEntry::ReserveSendingTimeForNextRequest( |
| 90 const base::TimeTicks& earliest_time) { | 132 const base::TimeTicks& earliest_time) { |
| 91 base::TimeTicks now = GetTimeNow(); | 133 base::TimeTicks now = GetTimeNow(); |
| 92 | 134 |
| 93 // If a lot of requests were successfully made recently, | 135 // If a lot of requests were successfully made recently, |
| 94 // sliding_window_release_time_ may be greater than | 136 // sliding_window_release_time_ may be greater than |
| 95 // exponential_backoff_release_time_. | 137 // exponential_backoff_release_time_. |
| (...skipping 20 matching lines...) Expand all Loading... |
| 116 | 158 |
| 117 // Check if there are too many send events in recent time. | 159 // Check if there are too many send events in recent time. |
| 118 if (send_log_.size() == static_cast<unsigned>(max_send_threshold_)) | 160 if (send_log_.size() == static_cast<unsigned>(max_send_threshold_)) |
| 119 sliding_window_release_time_ = send_log_.front() + sliding_window_period_; | 161 sliding_window_release_time_ = send_log_.front() + sliding_window_period_; |
| 120 | 162 |
| 121 return (recommended_sending_time - now).InMillisecondsRoundedUp(); | 163 return (recommended_sending_time - now).InMillisecondsRoundedUp(); |
| 122 } | 164 } |
| 123 | 165 |
| 124 base::TimeTicks | 166 base::TimeTicks |
| 125 URLRequestThrottlerEntry::GetExponentialBackoffReleaseTime() const { | 167 URLRequestThrottlerEntry::GetExponentialBackoffReleaseTime() const { |
| 168 // If a site opts out, it's likely because they have problems that trigger |
| 169 // the back-off mechanism when it shouldn't be triggered, in which case |
| 170 // returning the calculated back-off release time would probably be the |
| 171 // wrong thing to do (i.e. it would likely be too long). Therefore, we |
| 172 // return "now" so that retries are not delayed. |
| 173 if (is_backoff_disabled_) |
| 174 return GetTimeNow(); |
| 175 |
| 126 return GetBackoffEntry()->GetReleaseTime(); | 176 return GetBackoffEntry()->GetReleaseTime(); |
| 127 } | 177 } |
| 128 | 178 |
| 129 void URLRequestThrottlerEntry::UpdateWithResponse( | 179 void URLRequestThrottlerEntry::UpdateWithResponse( |
| 180 const std::string& host, |
| 130 const URLRequestThrottlerHeaderInterface* response) { | 181 const URLRequestThrottlerHeaderInterface* response) { |
| 131 if (response->GetResponseCode() >= 500) { | 182 if (response->GetResponseCode() >= 500) { |
| 132 GetBackoffEntry()->InformOfRequest(false); | 183 GetBackoffEntry()->InformOfRequest(false); |
| 133 } else { | 184 } else { |
| 134 GetBackoffEntry()->InformOfRequest(true); | 185 GetBackoffEntry()->InformOfRequest(true); |
| 135 | 186 |
| 136 std::string retry_header = response->GetNormalizedValue(kRetryHeaderName); | 187 std::string retry_header = response->GetNormalizedValue(kRetryHeaderName); |
| 137 if (!retry_header.empty()) | 188 if (!retry_header.empty()) |
| 138 HandleCustomRetryAfter(retry_header); | 189 HandleCustomRetryAfter(retry_header); |
| 190 |
| 191 std::string throttling_header = response->GetNormalizedValue( |
| 192 kExponentialThrottlingHeader); |
| 193 if (!throttling_header.empty()) |
| 194 HandleThrottlingHeader(throttling_header, host); |
| 139 } | 195 } |
| 140 } | 196 } |
| 141 | 197 |
| 142 void URLRequestThrottlerEntry::ReceivedContentWasMalformed() { | 198 void URLRequestThrottlerEntry::ReceivedContentWasMalformed() { |
| 143 // We keep this simple and just count it as a single error. | 199 // We keep this simple and just count it as a single error. |
| 144 // | 200 // |
| 145 // If we wanted to get fancy, we would count two errors here, and decrease | 201 // If we wanted to get fancy, we would count two errors here, and decrease |
| 146 // the error count only by one when we receive a successful (by status | 202 // the error count only by one when we receive a successful (by status |
| 147 // code) response. Instead, we keep things simple by always resetting the | 203 // code) response. Instead, we keep things simple by always resetting the |
| 148 // error count on success, and therefore counting only a single error here. | 204 // error count on success, and therefore counting only a single error here. |
| 149 GetBackoffEntry()->InformOfRequest(false); | 205 GetBackoffEntry()->InformOfRequest(false); |
| 150 } | 206 } |
| 151 | 207 |
| 152 URLRequestThrottlerEntry::~URLRequestThrottlerEntry() { | 208 URLRequestThrottlerEntry::~URLRequestThrottlerEntry() { |
| 153 } | 209 } |
| 154 | 210 |
| 155 void URLRequestThrottlerEntry::Initialize() { | 211 void URLRequestThrottlerEntry::Initialize() { |
| 156 sliding_window_release_time_ = base::TimeTicks::Now(); | 212 sliding_window_release_time_ = base::TimeTicks::Now(); |
| 157 backoff_policy_.num_errors_to_ignore = 0; | 213 backoff_policy_.num_errors_to_ignore = kDefaultNumErrorsToIgnore; |
| 158 backoff_policy_.initial_backoff_ms = kDefaultInitialBackoffMs; | 214 backoff_policy_.initial_backoff_ms = kDefaultInitialBackoffMs; |
| 159 backoff_policy_.multiply_factor = kDefaultMultiplyFactor; | 215 backoff_policy_.multiply_factor = kDefaultMultiplyFactor; |
| 160 backoff_policy_.jitter_factor = kDefaultJitterFactor; | 216 backoff_policy_.jitter_factor = kDefaultJitterFactor; |
| 161 backoff_policy_.maximum_backoff_ms = kDefaultMaximumBackoffMs; | 217 backoff_policy_.maximum_backoff_ms = kDefaultMaximumBackoffMs; |
| 162 backoff_policy_.entry_lifetime_ms = kDefaultEntryLifetimeMs; | 218 backoff_policy_.entry_lifetime_ms = kDefaultEntryLifetimeMs; |
| 163 } | 219 } |
| 164 | 220 |
| 165 base::TimeTicks URLRequestThrottlerEntry::GetTimeNow() const { | 221 base::TimeTicks URLRequestThrottlerEntry::GetTimeNow() const { |
| 166 return base::TimeTicks::Now(); | 222 return base::TimeTicks::Now(); |
| 167 } | 223 } |
| (...skipping 13 matching lines...) Expand all Loading... |
| 181 | 237 |
| 182 // We do not check for an upper bound; the server can set any Retry-After it | 238 // We do not check for an upper bound; the server can set any Retry-After it |
| 183 // desires. Recovery from error would involve restarting the browser. | 239 // desires. Recovery from error would involve restarting the browser. |
| 184 if (value_ms < 0) | 240 if (value_ms < 0) |
| 185 return; | 241 return; |
| 186 | 242 |
| 187 GetBackoffEntry()->SetCustomReleaseTime( | 243 GetBackoffEntry()->SetCustomReleaseTime( |
| 188 GetTimeNow() + base::TimeDelta::FromMilliseconds(value_ms)); | 244 GetTimeNow() + base::TimeDelta::FromMilliseconds(value_ms)); |
| 189 } | 245 } |
| 190 | 246 |
| 247 void URLRequestThrottlerEntry::HandleThrottlingHeader( |
| 248 const std::string& header_value, |
| 249 const std::string& host) { |
| 250 if (header_value == kExponentialThrottlingDisableValue) { |
| 251 DisableBackoffThrottling(); |
| 252 if (manager_) |
| 253 manager_->AddToOptOutList(host); |
| 254 } else { |
| 255 // TODO(joi): Log this. |
| 256 } |
| 257 } |
| 258 |
| 191 const BackoffEntry* URLRequestThrottlerEntry::GetBackoffEntry() const { | 259 const BackoffEntry* URLRequestThrottlerEntry::GetBackoffEntry() const { |
| 192 return &backoff_entry_; | 260 return &backoff_entry_; |
| 193 } | 261 } |
| 194 | 262 |
| 195 BackoffEntry* URLRequestThrottlerEntry::GetBackoffEntry() { | 263 BackoffEntry* URLRequestThrottlerEntry::GetBackoffEntry() { |
| 196 return &backoff_entry_; | 264 return &backoff_entry_; |
| 197 } | 265 } |
| 198 | 266 |
| 199 } // namespace net | 267 } // namespace net |
| OLD | NEW |