| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/metrics/field_trial.h" | 10 #include "base/metrics/field_trial.h" |
| 11 #include "base/metrics/histogram.h" | 11 #include "base/metrics/histogram.h" |
| 12 #include "base/rand_util.h" | 12 #include "base/rand_util.h" |
| 13 #include "base/string_number_conversions.h" | 13 #include "base/string_number_conversions.h" |
| 14 #include "base/values.h" | 14 #include "base/values.h" |
| 15 #include "net/base/load_flags.h" | 15 #include "net/base/load_flags.h" |
| 16 #include "net/base/net_log.h" | 16 #include "net/base/net_log.h" |
| 17 #include "net/url_request/url_request.h" |
| 18 #include "net/url_request/url_request_context.h" |
| 17 #include "net/url_request/url_request_throttler_header_interface.h" | 19 #include "net/url_request/url_request_throttler_header_interface.h" |
| 18 #include "net/url_request/url_request_throttler_manager.h" | 20 #include "net/url_request/url_request_throttler_manager.h" |
| 19 | 21 |
| 20 namespace net { | 22 namespace net { |
| 21 | 23 |
| 22 const int URLRequestThrottlerEntry::kDefaultSlidingWindowPeriodMs = 2000; | 24 const int URLRequestThrottlerEntry::kDefaultSlidingWindowPeriodMs = 2000; |
| 23 const int URLRequestThrottlerEntry::kDefaultMaxSendThreshold = 20; | 25 const int URLRequestThrottlerEntry::kDefaultMaxSendThreshold = 20; |
| 24 | 26 |
| 25 // This set of back-off parameters will (at maximum values, i.e. without | 27 // This set of back-off parameters will (at maximum values, i.e. without |
| 26 // the reduction caused by jitter) add 0-41% (distributed uniformly | 28 // the reduction caused by jitter) add 0-41% (distributed uniformly |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 131 bool URLRequestThrottlerEntry::IsEntryOutdated() const { | 133 bool URLRequestThrottlerEntry::IsEntryOutdated() const { |
| 132 // This function is called by the URLRequestThrottlerManager to determine | 134 // This function is called by the URLRequestThrottlerManager to determine |
| 133 // whether entries should be discarded from its url_entries_ map. We | 135 // whether entries should be discarded from its url_entries_ map. We |
| 134 // want to ensure that it does not remove entries from the map while there | 136 // want to ensure that it does not remove entries from the map while there |
| 135 // are clients (objects other than the manager) holding references to | 137 // are clients (objects other than the manager) holding references to |
| 136 // the entry, otherwise separate clients could end up holding separate | 138 // the entry, otherwise separate clients could end up holding separate |
| 137 // entries for a request to the same URL, which is undesirable. Therefore, | 139 // entries for a request to the same URL, which is undesirable. Therefore, |
| 138 // if an entry has more than one reference (the map will always hold one), | 140 // if an entry has more than one reference (the map will always hold one), |
| 139 // it should not be considered outdated. | 141 // it should not be considered outdated. |
| 140 // | 142 // |
| 141 // TODO(joi): Once the manager is not a Singleton, revisit whether | 143 // We considered whether to make URLRequestThrottlerEntry objects |
| 142 // refcounting is needed at all. | 144 // non-refcounted, but since any means of knowing whether they are |
| 145 // currently in use by others than the manager would be more or less |
| 146 // equivalent to a refcount, we kept them refcounted. |
| 143 if (!HasOneRef()) | 147 if (!HasOneRef()) |
| 144 return false; | 148 return false; |
| 145 | 149 |
| 146 // If there are send events in the sliding window period, we still need this | 150 // If there are send events in the sliding window period, we still need this |
| 147 // entry. | 151 // entry. |
| 148 if (!send_log_.empty() && | 152 if (!send_log_.empty() && |
| 149 send_log_.back() + sliding_window_period_ > ImplGetTimeNow()) { | 153 send_log_.back() + sliding_window_period_ > ImplGetTimeNow()) { |
| 150 return false; | 154 return false; |
| 151 } | 155 } |
| 152 | 156 |
| 153 return GetBackoffEntry()->CanDiscard(); | 157 return GetBackoffEntry()->CanDiscard(); |
| 154 } | 158 } |
| 155 | 159 |
| 156 void URLRequestThrottlerEntry::DisableBackoffThrottling() { | 160 void URLRequestThrottlerEntry::DisableBackoffThrottling() { |
| 157 is_backoff_disabled_ = true; | 161 is_backoff_disabled_ = true; |
| 158 } | 162 } |
| 159 | 163 |
| 160 void URLRequestThrottlerEntry::DetachManager() { | 164 void URLRequestThrottlerEntry::DetachManager() { |
| 161 manager_ = NULL; | 165 manager_ = NULL; |
| 162 } | 166 } |
| 163 | 167 |
| 164 bool URLRequestThrottlerEntry::ShouldRejectRequest(int load_flags) const { | 168 bool URLRequestThrottlerEntry::ShouldRejectRequest( |
| 169 const URLRequest& request) const { |
| 165 bool reject_request = false; | 170 bool reject_request = false; |
| 166 if (!is_backoff_disabled_ && !ExplicitUserRequest(load_flags) && | 171 if (!is_backoff_disabled_ && !ExplicitUserRequest(request.load_flags()) && |
| 172 (!request.context() || !request.context()->network_delegate() || |
| 173 request.context()->network_delegate()->CanThrottleRequest(request)) && |
| 167 GetBackoffEntry()->ShouldRejectRequest()) { | 174 GetBackoffEntry()->ShouldRejectRequest()) { |
| 168 int num_failures = GetBackoffEntry()->failure_count(); | 175 int num_failures = GetBackoffEntry()->failure_count(); |
| 169 int release_after_ms = | 176 int release_after_ms = |
| 170 GetBackoffEntry()->GetTimeUntilRelease().InMilliseconds(); | 177 GetBackoffEntry()->GetTimeUntilRelease().InMilliseconds(); |
| 171 | 178 |
| 172 net_log_.AddEvent( | 179 net_log_.AddEvent( |
| 173 NetLog::TYPE_THROTTLING_REJECTED_REQUEST, | 180 NetLog::TYPE_THROTTLING_REJECTED_REQUEST, |
| 174 make_scoped_refptr( | 181 make_scoped_refptr( |
| 175 new RejectedRequestParameters(url_id_, | 182 new RejectedRequestParameters(url_id_, |
| 176 num_failures, | 183 num_failures, |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 230 // return "now" so that retries are not delayed. | 237 // return "now" so that retries are not delayed. |
| 231 if (is_backoff_disabled_) | 238 if (is_backoff_disabled_) |
| 232 return ImplGetTimeNow(); | 239 return ImplGetTimeNow(); |
| 233 | 240 |
| 234 return GetBackoffEntry()->GetReleaseTime(); | 241 return GetBackoffEntry()->GetReleaseTime(); |
| 235 } | 242 } |
| 236 | 243 |
| 237 void URLRequestThrottlerEntry::UpdateWithResponse( | 244 void URLRequestThrottlerEntry::UpdateWithResponse( |
| 238 const std::string& host, | 245 const std::string& host, |
| 239 const URLRequestThrottlerHeaderInterface* response) { | 246 const URLRequestThrottlerHeaderInterface* response) { |
| 240 int response_code = response->GetResponseCode(); | 247 if (IsConsideredError(response->GetResponseCode())) { |
| 241 HandleMetricsTracking(response_code); | |
| 242 | |
| 243 if (IsConsideredError(response_code)) { | |
| 244 GetBackoffEntry()->InformOfRequest(false); | 248 GetBackoffEntry()->InformOfRequest(false); |
| 245 } else { | 249 } else { |
| 246 GetBackoffEntry()->InformOfRequest(true); | 250 GetBackoffEntry()->InformOfRequest(true); |
| 247 | 251 |
| 248 std::string throttling_header = response->GetNormalizedValue( | 252 std::string throttling_header = response->GetNormalizedValue( |
| 249 kExponentialThrottlingHeader); | 253 kExponentialThrottlingHeader); |
| 250 if (!throttling_header.empty()) | 254 if (!throttling_header.empty()) |
| 251 HandleThrottlingHeader(throttling_header, host); | 255 HandleThrottlingHeader(throttling_header, host); |
| 252 } | 256 } |
| 253 } | 257 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 273 | 277 |
| 274 void URLRequestThrottlerEntry::Initialize() { | 278 void URLRequestThrottlerEntry::Initialize() { |
| 275 sliding_window_release_time_ = base::TimeTicks::Now(); | 279 sliding_window_release_time_ = base::TimeTicks::Now(); |
| 276 backoff_policy_.num_errors_to_ignore = kDefaultNumErrorsToIgnore; | 280 backoff_policy_.num_errors_to_ignore = kDefaultNumErrorsToIgnore; |
| 277 backoff_policy_.initial_delay_ms = kDefaultInitialDelayMs; | 281 backoff_policy_.initial_delay_ms = kDefaultInitialDelayMs; |
| 278 backoff_policy_.multiply_factor = kDefaultMultiplyFactor; | 282 backoff_policy_.multiply_factor = kDefaultMultiplyFactor; |
| 279 backoff_policy_.jitter_factor = kDefaultJitterFactor; | 283 backoff_policy_.jitter_factor = kDefaultJitterFactor; |
| 280 backoff_policy_.maximum_backoff_ms = kDefaultMaximumBackoffMs; | 284 backoff_policy_.maximum_backoff_ms = kDefaultMaximumBackoffMs; |
| 281 backoff_policy_.entry_lifetime_ms = kDefaultEntryLifetimeMs; | 285 backoff_policy_.entry_lifetime_ms = kDefaultEntryLifetimeMs; |
| 282 backoff_policy_.always_use_initial_delay = false; | 286 backoff_policy_.always_use_initial_delay = false; |
| 283 | |
| 284 // We pretend we just had a successful response so that we have a | |
| 285 // starting point to our tracking. This is called from the | |
| 286 // constructor so we do not use the virtual ImplGetTimeNow(). | |
| 287 last_successful_response_time_ = base::TimeTicks::Now(); | |
| 288 last_response_was_success_ = true; | |
| 289 } | 287 } |
| 290 | 288 |
| 291 bool URLRequestThrottlerEntry::IsConsideredError(int response_code) { | 289 bool URLRequestThrottlerEntry::IsConsideredError(int response_code) { |
| 292 // We throttle only for the status codes most likely to indicate the server | 290 // We throttle only for the status codes most likely to indicate the server |
| 293 // is failing because it is too busy or otherwise are likely to be | 291 // is failing because it is too busy or otherwise are likely to be |
| 294 // because of DDoS. | 292 // because of DDoS. |
| 295 // | 293 // |
| 296 // 500 is the generic error when no better message is suitable, and | 294 // 500 is the generic error when no better message is suitable, and |
| 297 // as such does not necessarily indicate a temporary state, but | 295 // as such does not necessarily indicate a temporary state, but |
| 298 // other status codes cover most of the permanent error states. | 296 // other status codes cover most of the permanent error states. |
| (...skipping 16 matching lines...) Expand all Loading... |
| 315 return base::TimeTicks::Now(); | 313 return base::TimeTicks::Now(); |
| 316 } | 314 } |
| 317 | 315 |
| 318 void URLRequestThrottlerEntry::HandleThrottlingHeader( | 316 void URLRequestThrottlerEntry::HandleThrottlingHeader( |
| 319 const std::string& header_value, | 317 const std::string& header_value, |
| 320 const std::string& host) { | 318 const std::string& host) { |
| 321 if (header_value == kExponentialThrottlingDisableValue) { | 319 if (header_value == kExponentialThrottlingDisableValue) { |
| 322 DisableBackoffThrottling(); | 320 DisableBackoffThrottling(); |
| 323 if (manager_) | 321 if (manager_) |
| 324 manager_->AddToOptOutList(host); | 322 manager_->AddToOptOutList(host); |
| 325 } else { | |
| 326 // TODO(joi): Log this. | |
| 327 } | 323 } |
| 328 } | 324 } |
| 329 | 325 |
| 330 void URLRequestThrottlerEntry::HandleMetricsTracking(int response_code) { | |
| 331 // Note that we are not interested in whether the code is considered | |
| 332 // an error for the backoff logic, but whether it is a 5xx error in | |
| 333 // general. This is because here, we are tracking the apparent total | |
| 334 // downtime of a server. | |
| 335 if (response_code >= 500) { | |
| 336 last_response_was_success_ = false; | |
| 337 } else { | |
| 338 base::TimeTicks now = ImplGetTimeNow(); | |
| 339 if (!last_response_was_success_) { | |
| 340 // We are transitioning from failure to success, so generate our stats. | |
| 341 base::TimeDelta down_time = now - last_successful_response_time_; | |
| 342 int failure_count = GetBackoffEntry()->failure_count(); | |
| 343 | |
| 344 UMA_HISTOGRAM_COUNTS("Throttling.FailureCountAtSuccess", failure_count); | |
| 345 UMA_HISTOGRAM_CUSTOM_TIMES( | |
| 346 "Throttling.PerceivedDowntime", down_time, | |
| 347 base::TimeDelta::FromMilliseconds(10), | |
| 348 base::TimeDelta::FromHours(6), 50); | |
| 349 } | |
| 350 | |
| 351 last_successful_response_time_ = now; | |
| 352 last_response_was_success_ = true; | |
| 353 } | |
| 354 } | |
| 355 | |
| 356 const BackoffEntry* URLRequestThrottlerEntry::GetBackoffEntry() const { | 326 const BackoffEntry* URLRequestThrottlerEntry::GetBackoffEntry() const { |
| 357 return &backoff_entry_; | 327 return &backoff_entry_; |
| 358 } | 328 } |
| 359 | 329 |
| 360 BackoffEntry* URLRequestThrottlerEntry::GetBackoffEntry() { | 330 BackoffEntry* URLRequestThrottlerEntry::GetBackoffEntry() { |
| 361 return &backoff_entry_; | 331 return &backoff_entry_; |
| 362 } | 332 } |
| 363 | 333 |
| 364 // static | 334 // static |
| 365 bool URLRequestThrottlerEntry::ExplicitUserRequest(const int load_flags) { | 335 bool URLRequestThrottlerEntry::ExplicitUserRequest(const int load_flags) { |
| 366 return (load_flags & LOAD_MAYBE_USER_GESTURE) != 0; | 336 return (load_flags & LOAD_MAYBE_USER_GESTURE) != 0; |
| 367 } | 337 } |
| 368 | 338 |
| 369 } // namespace net | 339 } // namespace net |
| OLD | NEW |