Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 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/metrics/field_trial.h" | |
| 11 #include "base/metrics/histogram.h" | |
| 10 #include "base/rand_util.h" | 12 #include "base/rand_util.h" |
| 11 #include "base/string_number_conversions.h" | 13 #include "base/string_number_conversions.h" |
| 14 #include "base/time.h" | |
| 15 #include "base/values.h" | |
| 16 #include "net/base/net_log.h" | |
| 12 #include "net/url_request/url_request_throttler_header_interface.h" | 17 #include "net/url_request/url_request_throttler_header_interface.h" |
| 13 #include "net/url_request/url_request_throttler_manager.h" | 18 #include "net/url_request/url_request_throttler_manager.h" |
| 14 | 19 |
| 15 namespace net { | 20 namespace net { |
| 16 | 21 |
| 17 const int URLRequestThrottlerEntry::kDefaultSlidingWindowPeriodMs = 2000; | 22 const int URLRequestThrottlerEntry::kDefaultSlidingWindowPeriodMs = 2000; |
| 18 const int URLRequestThrottlerEntry::kDefaultMaxSendThreshold = 20; | 23 const int URLRequestThrottlerEntry::kDefaultMaxSendThreshold = 20; |
| 19 | 24 |
| 20 // This set of back-off parameters will (at maximum values, i.e. without | 25 // This set of back-off parameters will (at maximum values, i.e. without |
| 21 // the reduction caused by jitter) add 0-41% (distributed uniformly | 26 // the reduction caused by jitter) add 0-41% (distributed uniformly |
| 22 // in that range) to the "perceived downtime" of the remote server, once | 27 // 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 | 28 // 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 | 29 // about a second at a time. Once the maximum back-off is reached, the added |
| 25 // perceived downtime decreases rapidly, percentage-wise. | 30 // perceived downtime decreases rapidly, percentage-wise. |
| 26 // | 31 // |
| 27 // Another way to put it is that the maximum additional perceived downtime | 32 // 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 | 33 // 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 | 34 // 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 | 35 // unavailable at the end of each back-off period for a total of about |
| 31 // 48 minutes. | 36 // 48 minutes. |
| 32 // | 37 // |
| 33 // Ignoring the first 4 errors helps avoid back-off from kicking in on | 38 // Ignoring the first couple of errors is just a conservative measure to |
| 34 // flaky connections. | 39 // avoid false positives. It should help avoid back-off from kicking in e.g. |
| 35 const int URLRequestThrottlerEntry::kDefaultNumErrorsToIgnore = 4; | 40 // on flaky connections. |
| 41 const int URLRequestThrottlerEntry::kDefaultNumErrorsToIgnore = 2; | |
| 36 const int URLRequestThrottlerEntry::kDefaultInitialBackoffMs = 700; | 42 const int URLRequestThrottlerEntry::kDefaultInitialBackoffMs = 700; |
| 37 const double URLRequestThrottlerEntry::kDefaultMultiplyFactor = 1.4; | 43 const double URLRequestThrottlerEntry::kDefaultMultiplyFactor = 1.4; |
| 38 const double URLRequestThrottlerEntry::kDefaultJitterFactor = 0.4; | 44 const double URLRequestThrottlerEntry::kDefaultJitterFactor = 0.4; |
| 39 const int URLRequestThrottlerEntry::kDefaultMaximumBackoffMs = 15 * 60 * 1000; | 45 const int URLRequestThrottlerEntry::kDefaultMaximumBackoffMs = 15 * 60 * 1000; |
| 40 const int URLRequestThrottlerEntry::kDefaultEntryLifetimeMs = 2 * 60 * 1000; | 46 const int URLRequestThrottlerEntry::kDefaultEntryLifetimeMs = 2 * 60 * 1000; |
| 41 const char URLRequestThrottlerEntry::kRetryHeaderName[] = "X-Retry-After"; | 47 const char URLRequestThrottlerEntry::kRetryHeaderName[] = "X-Retry-After"; |
| 42 const char URLRequestThrottlerEntry::kExponentialThrottlingHeader[] = | 48 const char URLRequestThrottlerEntry::kExponentialThrottlingHeader[] = |
| 43 "X-Chrome-Exponential-Throttling"; | 49 "X-Chrome-Exponential-Throttling"; |
| 44 const char URLRequestThrottlerEntry::kExponentialThrottlingDisableValue[] = | 50 const char URLRequestThrottlerEntry::kExponentialThrottlingDisableValue[] = |
| 45 "disable"; | 51 "disable"; |
| 46 | 52 |
| 53 // NetLog parameters when a request is rejected by throttling. | |
| 54 class RejectedRequestParameters : public NetLog::EventParameters { | |
| 55 public: | |
| 56 RejectedRequestParameters(const std::string& url_id, | |
| 57 int num_failures, | |
| 58 int release_after_ms) | |
| 59 : url_id_(url_id), | |
| 60 num_failures_(num_failures), | |
| 61 release_after_ms_(release_after_ms) { | |
| 62 } | |
| 63 | |
| 64 virtual Value* ToValue() const { | |
| 65 DictionaryValue* dict = new DictionaryValue(); | |
| 66 dict->SetString("url", url_id_); | |
| 67 dict->SetInteger("num_failures", num_failures_); | |
| 68 dict->SetInteger("release_after_ms", release_after_ms_); | |
| 69 return dict; | |
| 70 } | |
| 71 | |
| 72 private: | |
| 73 std::string url_id_; | |
| 74 int num_failures_; | |
| 75 int release_after_ms_; | |
| 76 }; | |
| 77 | |
| 78 // NetLog parameters when a response contains an X-Retry-After header. | |
| 79 class RetryAfterParameters : public NetLog::EventParameters { | |
| 80 public: | |
| 81 RetryAfterParameters(const std::string& url_id, | |
| 82 int retry_after_ms) | |
|
yzshen1
2011/05/25 03:49:06
Wrong indent.
Jói
2011/05/25 14:30:35
Done.
| |
| 83 : url_id_(url_id), | |
| 84 retry_after_ms_(retry_after_ms) { | |
| 85 } | |
| 86 | |
| 87 virtual Value* ToValue() const { | |
| 88 DictionaryValue* dict = new DictionaryValue(); | |
| 89 dict->SetString("url", url_id_); | |
| 90 dict->SetInteger("retry_after_ms", retry_after_ms_); | |
| 91 return dict; | |
| 92 } | |
| 93 | |
| 94 private: | |
| 95 std::string url_id_; | |
| 96 int retry_after_ms_; | |
| 97 }; | |
| 98 | |
| 47 URLRequestThrottlerEntry::URLRequestThrottlerEntry( | 99 URLRequestThrottlerEntry::URLRequestThrottlerEntry( |
| 48 URLRequestThrottlerManager* manager) | 100 URLRequestThrottlerManager* manager, |
| 101 const std::string& url_id) | |
| 49 : sliding_window_period_( | 102 : sliding_window_period_( |
| 50 base::TimeDelta::FromMilliseconds(kDefaultSlidingWindowPeriodMs)), | 103 base::TimeDelta::FromMilliseconds(kDefaultSlidingWindowPeriodMs)), |
| 51 max_send_threshold_(kDefaultMaxSendThreshold), | 104 max_send_threshold_(kDefaultMaxSendThreshold), |
| 52 is_backoff_disabled_(false), | 105 is_backoff_disabled_(false), |
| 53 backoff_entry_(&backoff_policy_), | 106 backoff_entry_(&backoff_policy_), |
| 54 manager_(manager) { | 107 manager_(manager), |
| 108 url_id_(url_id), | |
| 109 net_log_(BoundNetLog::Make( | |
| 110 manager->net_log(), NetLog::SOURCE_EXPONENTIAL_BACKOFF_THROTTLING)) { | |
| 55 DCHECK(manager_); | 111 DCHECK(manager_); |
| 56 Initialize(); | 112 Initialize(); |
| 57 } | 113 } |
| 58 | 114 |
| 59 URLRequestThrottlerEntry::URLRequestThrottlerEntry( | 115 URLRequestThrottlerEntry::URLRequestThrottlerEntry( |
| 60 URLRequestThrottlerManager* manager, | 116 URLRequestThrottlerManager* manager, |
| 117 const std::string& url_id, | |
| 61 int sliding_window_period_ms, | 118 int sliding_window_period_ms, |
| 62 int max_send_threshold, | 119 int max_send_threshold, |
| 63 int initial_backoff_ms, | 120 int initial_backoff_ms, |
| 64 double multiply_factor, | 121 double multiply_factor, |
| 65 double jitter_factor, | 122 double jitter_factor, |
| 66 int maximum_backoff_ms) | 123 int maximum_backoff_ms) |
| 67 : sliding_window_period_( | 124 : sliding_window_period_( |
| 68 base::TimeDelta::FromMilliseconds(sliding_window_period_ms)), | 125 base::TimeDelta::FromMilliseconds(sliding_window_period_ms)), |
| 69 max_send_threshold_(max_send_threshold), | 126 max_send_threshold_(max_send_threshold), |
| 70 is_backoff_disabled_(false), | 127 is_backoff_disabled_(false), |
| 71 backoff_entry_(&backoff_policy_), | 128 backoff_entry_(&backoff_policy_), |
| 72 manager_(manager) { | 129 manager_(manager), |
| 130 url_id_(url_id) { | |
| 73 DCHECK_GT(sliding_window_period_ms, 0); | 131 DCHECK_GT(sliding_window_period_ms, 0); |
| 74 DCHECK_GT(max_send_threshold_, 0); | 132 DCHECK_GT(max_send_threshold_, 0); |
| 75 DCHECK_GE(initial_backoff_ms, 0); | 133 DCHECK_GE(initial_backoff_ms, 0); |
| 76 DCHECK_GT(multiply_factor, 0); | 134 DCHECK_GT(multiply_factor, 0); |
| 77 DCHECK_GE(jitter_factor, 0.0); | 135 DCHECK_GE(jitter_factor, 0.0); |
| 78 DCHECK_LT(jitter_factor, 1.0); | 136 DCHECK_LT(jitter_factor, 1.0); |
| 79 DCHECK_GE(maximum_backoff_ms, 0); | 137 DCHECK_GE(maximum_backoff_ms, 0); |
| 80 DCHECK(manager_); | 138 DCHECK(manager_); |
| 81 | 139 |
| 82 Initialize(); | 140 Initialize(); |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 99 // it should not be considered outdated. | 157 // it should not be considered outdated. |
| 100 // | 158 // |
| 101 // TODO(joi): Once the manager is not a Singleton, revisit whether | 159 // TODO(joi): Once the manager is not a Singleton, revisit whether |
| 102 // refcounting is needed at all. | 160 // refcounting is needed at all. |
| 103 if (!HasOneRef()) | 161 if (!HasOneRef()) |
| 104 return false; | 162 return false; |
| 105 | 163 |
| 106 // If there are send events in the sliding window period, we still need this | 164 // If there are send events in the sliding window period, we still need this |
| 107 // entry. | 165 // entry. |
| 108 if (!send_log_.empty() && | 166 if (!send_log_.empty() && |
| 109 send_log_.back() + sliding_window_period_ > GetTimeNow()) { | 167 send_log_.back() + sliding_window_period_ > ImplGetTimeNow()) { |
| 110 return false; | 168 return false; |
| 111 } | 169 } |
| 112 | 170 |
| 113 return GetBackoffEntry()->CanDiscard(); | 171 return GetBackoffEntry()->CanDiscard(); |
| 114 } | 172 } |
| 115 | 173 |
| 116 void URLRequestThrottlerEntry::DisableBackoffThrottling() { | 174 void URLRequestThrottlerEntry::DisableBackoffThrottling() { |
| 117 is_backoff_disabled_ = true; | 175 is_backoff_disabled_ = true; |
| 118 } | 176 } |
| 119 | 177 |
| 120 void URLRequestThrottlerEntry::DetachManager() { | 178 void URLRequestThrottlerEntry::DetachManager() { |
| 121 manager_ = NULL; | 179 manager_ = NULL; |
| 122 } | 180 } |
| 123 | 181 |
| 124 bool URLRequestThrottlerEntry::IsDuringExponentialBackoff() const { | 182 bool URLRequestThrottlerEntry::IsDuringExponentialBackoff() const { |
| 125 if (is_backoff_disabled_) | 183 bool reject_request = false; |
| 126 return false; | 184 if (!is_backoff_disabled_ && GetBackoffEntry()->ShouldRejectRequest()) { |
| 185 int num_failures = GetBackoffEntry()->failure_count(); | |
| 186 int release_after_ms = | |
| 187 (GetBackoffEntry()->GetReleaseTime() - base::TimeTicks::Now()) | |
| 188 .InMilliseconds(); | |
| 127 | 189 |
| 128 return GetBackoffEntry()->ShouldRejectRequest(); | 190 net_log_.AddEvent( |
| 191 NetLog::TYPE_THROTTLING_REJECTED_REQUEST, | |
|
yzshen1
2011/05/25 03:49:06
Wrong indent.
Jói
2011/05/25 14:30:35
Done.
| |
| 192 make_scoped_refptr( | |
| 193 new RejectedRequestParameters(url_id_, | |
| 194 num_failures, | |
| 195 release_after_ms))); | |
| 196 | |
| 197 reject_request = true; | |
| 198 } | |
| 199 | |
| 200 int reject_count = reject_request ? 1 : 0; | |
| 201 UMA_HISTOGRAM_ENUMERATION( | |
| 202 "Throttling.RequestThrottled", reject_count, 2); | |
| 203 if (base::FieldTrialList::TrialExists("ThrottlingEnabled")) { | |
| 204 UMA_HISTOGRAM_ENUMERATION(base::FieldTrial::MakeName( | |
| 205 "Throttling.RequestThrottled", "ThrottlingEnabled"), reject_count, 2); | |
| 206 } | |
| 207 | |
| 208 return reject_request; | |
| 129 } | 209 } |
| 130 | 210 |
| 131 int64 URLRequestThrottlerEntry::ReserveSendingTimeForNextRequest( | 211 int64 URLRequestThrottlerEntry::ReserveSendingTimeForNextRequest( |
| 132 const base::TimeTicks& earliest_time) { | 212 const base::TimeTicks& earliest_time) { |
| 133 base::TimeTicks now = GetTimeNow(); | 213 base::TimeTicks now = ImplGetTimeNow(); |
| 134 | 214 |
| 135 // If a lot of requests were successfully made recently, | 215 // If a lot of requests were successfully made recently, |
| 136 // sliding_window_release_time_ may be greater than | 216 // sliding_window_release_time_ may be greater than |
| 137 // exponential_backoff_release_time_. | 217 // exponential_backoff_release_time_. |
| 138 base::TimeTicks recommended_sending_time = | 218 base::TimeTicks recommended_sending_time = |
| 139 std::max(std::max(now, earliest_time), | 219 std::max(std::max(now, earliest_time), |
| 140 std::max(GetBackoffEntry()->GetReleaseTime(), | 220 std::max(GetBackoffEntry()->GetReleaseTime(), |
| 141 sliding_window_release_time_)); | 221 sliding_window_release_time_)); |
| 142 | 222 |
| 143 DCHECK(send_log_.empty() || | 223 DCHECK(send_log_.empty() || |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 164 } | 244 } |
| 165 | 245 |
| 166 base::TimeTicks | 246 base::TimeTicks |
| 167 URLRequestThrottlerEntry::GetExponentialBackoffReleaseTime() const { | 247 URLRequestThrottlerEntry::GetExponentialBackoffReleaseTime() const { |
| 168 // If a site opts out, it's likely because they have problems that trigger | 248 // 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 | 249 // 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 | 250 // 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 | 251 // wrong thing to do (i.e. it would likely be too long). Therefore, we |
| 172 // return "now" so that retries are not delayed. | 252 // return "now" so that retries are not delayed. |
| 173 if (is_backoff_disabled_) | 253 if (is_backoff_disabled_) |
| 174 return GetTimeNow(); | 254 return ImplGetTimeNow(); |
| 175 | 255 |
| 176 return GetBackoffEntry()->GetReleaseTime(); | 256 return GetBackoffEntry()->GetReleaseTime(); |
| 177 } | 257 } |
| 178 | 258 |
| 179 void URLRequestThrottlerEntry::UpdateWithResponse( | 259 void URLRequestThrottlerEntry::UpdateWithResponse( |
| 180 const std::string& host, | 260 const std::string& host, |
| 181 const URLRequestThrottlerHeaderInterface* response) { | 261 const URLRequestThrottlerHeaderInterface* response) { |
| 182 if (response->GetResponseCode() >= 500) { | 262 int status_code = response->GetResponseCode(); |
| 263 HandleMetricsTracking(status_code); | |
| 264 | |
| 265 if (IsConsideredError(status_code)) { | |
| 183 GetBackoffEntry()->InformOfRequest(false); | 266 GetBackoffEntry()->InformOfRequest(false); |
| 184 } else { | 267 } else { |
| 185 GetBackoffEntry()->InformOfRequest(true); | 268 GetBackoffEntry()->InformOfRequest(true); |
| 186 | 269 |
| 187 std::string retry_header = response->GetNormalizedValue(kRetryHeaderName); | 270 std::string retry_header = response->GetNormalizedValue(kRetryHeaderName); |
| 188 if (!retry_header.empty()) | 271 if (!retry_header.empty()) |
| 189 HandleCustomRetryAfter(retry_header); | 272 HandleCustomRetryAfter(retry_header); |
| 190 | 273 |
| 191 std::string throttling_header = response->GetNormalizedValue( | 274 std::string throttling_header = response->GetNormalizedValue( |
| 192 kExponentialThrottlingHeader); | 275 kExponentialThrottlingHeader); |
| 193 if (!throttling_header.empty()) | 276 if (!throttling_header.empty()) |
| 194 HandleThrottlingHeader(throttling_header, host); | 277 HandleThrottlingHeader(throttling_header, host); |
| 195 } | 278 } |
| 196 } | 279 } |
| 197 | 280 |
| 198 void URLRequestThrottlerEntry::ReceivedContentWasMalformed() { | 281 void URLRequestThrottlerEntry::ReceivedContentWasMalformed(int status_code) { |
| 199 // A malformed body can only occur when the request to fetch a resource | 282 // A malformed body can only occur when the request to fetch a resource |
| 200 // was successful. Therefore, in such a situation, we will receive one | 283 // was successful. Therefore, in such a situation, we will receive one |
| 201 // call to ReceivedContentWasMalformed() and one call to UpdateWithResponse() | 284 // call to ReceivedContentWasMalformed() and one call to |
| 202 // with a response categorized as "good". To end up counting one failure, | 285 // UpdateWithResponse() with a response categorized as "good". To end |
| 203 // we need to count two failures here against the one success in | 286 // up counting one failure, we need to count two failures here against |
| 204 // UpdateWithResponse(). | 287 // the one success in UpdateWithResponse(). |
| 205 GetBackoffEntry()->InformOfRequest(false); | 288 // |
| 206 GetBackoffEntry()->InformOfRequest(false); | 289 // We do nothing for a response that is already being considered an error |
| 290 // based on its status code (otherwise we would count 3 errors instead of 1). | |
| 291 if (!IsConsideredError(status_code)) { | |
| 292 GetBackoffEntry()->InformOfRequest(false); | |
| 293 GetBackoffEntry()->InformOfRequest(false); | |
| 294 } | |
| 207 } | 295 } |
| 208 | 296 |
| 209 URLRequestThrottlerEntry::~URLRequestThrottlerEntry() { | 297 URLRequestThrottlerEntry::~URLRequestThrottlerEntry() { |
| 210 } | 298 } |
| 211 | 299 |
| 212 void URLRequestThrottlerEntry::Initialize() { | 300 void URLRequestThrottlerEntry::Initialize() { |
| 213 sliding_window_release_time_ = base::TimeTicks::Now(); | 301 sliding_window_release_time_ = base::TimeTicks::Now(); |
| 214 backoff_policy_.num_errors_to_ignore = kDefaultNumErrorsToIgnore; | 302 backoff_policy_.num_errors_to_ignore = kDefaultNumErrorsToIgnore; |
| 215 backoff_policy_.initial_backoff_ms = kDefaultInitialBackoffMs; | 303 backoff_policy_.initial_backoff_ms = kDefaultInitialBackoffMs; |
| 216 backoff_policy_.multiply_factor = kDefaultMultiplyFactor; | 304 backoff_policy_.multiply_factor = kDefaultMultiplyFactor; |
| 217 backoff_policy_.jitter_factor = kDefaultJitterFactor; | 305 backoff_policy_.jitter_factor = kDefaultJitterFactor; |
| 218 backoff_policy_.maximum_backoff_ms = kDefaultMaximumBackoffMs; | 306 backoff_policy_.maximum_backoff_ms = kDefaultMaximumBackoffMs; |
| 219 backoff_policy_.entry_lifetime_ms = kDefaultEntryLifetimeMs; | 307 backoff_policy_.entry_lifetime_ms = kDefaultEntryLifetimeMs; |
| 308 | |
| 309 // We pretend we just had a successful response so that we have a | |
| 310 // starting point to our tracking. | |
| 311 last_successful_response_time_ = base::TimeTicks::Now(); | |
|
yzshen1
2011/05/25 03:49:06
It would be good to comment about why ImplGetTimeN
Jói
2011/05/25 14:30:35
Done.
| |
| 312 last_response_was_success_ = true; | |
| 220 } | 313 } |
| 221 | 314 |
| 222 base::TimeTicks URLRequestThrottlerEntry::GetTimeNow() const { | 315 bool URLRequestThrottlerEntry::IsConsideredError(int status_code) { |
| 316 // We throttle only for the status codes most likely to indicate the server | |
| 317 // is failing because it is too busy or otherwise are likely to be | |
| 318 // because of DDoS. | |
| 319 // | |
| 320 // 500 is the generic error when no better message is suitable, and | |
| 321 // as such does not necessarily indicate a temporary state, but | |
| 322 // other status codes cover most of the permanent error states. | |
| 323 // 503 is explicitly documented as a temporary state where the server | |
| 324 // is either overloaded or down for maintenance. | |
| 325 // 509 is the (non-standard but widely implemented) Bandwidth Limit Exceeded | |
| 326 // status code, which might indicate DDoS. | |
| 327 // | |
| 328 // We do not back off on 502 or 504, which are reported by gateways | |
| 329 // (proxies) on timeouts or failures, because in many cases these requests | |
| 330 // have not made it to the destination server and so we do not actually | |
| 331 // know that it is down or busy. One degenerate case could be a proxy on | |
| 332 // localhost, where you are not actually connected to the network. | |
| 333 return (status_code == 500 || | |
| 334 status_code == 503 || | |
| 335 status_code == 509); | |
| 336 } | |
| 337 | |
| 338 base::TimeTicks URLRequestThrottlerEntry::ImplGetTimeNow() const { | |
| 223 return base::TimeTicks::Now(); | 339 return base::TimeTicks::Now(); |
| 224 } | 340 } |
| 225 | 341 |
| 226 void URLRequestThrottlerEntry::HandleCustomRetryAfter( | 342 void URLRequestThrottlerEntry::HandleCustomRetryAfter( |
| 227 const std::string& header_value) { | 343 const std::string& header_value) { |
| 228 // Input parameter is the number of seconds to wait in a floating point value. | 344 // Input parameter is the number of seconds to wait in a floating point value. |
| 229 double time_in_sec = 0; | 345 double time_in_sec = 0; |
| 230 bool conversion_is_ok = base::StringToDouble(header_value, &time_in_sec); | 346 bool conversion_is_ok = base::StringToDouble(header_value, &time_in_sec); |
| 231 | 347 |
| 232 // Conversion of custom retry-after header value failed. | 348 // Conversion of custom retry-after header value failed. |
| 233 if (!conversion_is_ok) | 349 if (!conversion_is_ok) |
| 234 return; | 350 return; |
| 235 | 351 |
| 236 // We must use an int value later so we transform this in milliseconds. | 352 // 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); | 353 int64 value_ms = static_cast<int64>(0.5 + time_in_sec * 1000); |
| 238 | 354 |
| 239 // We do not check for an upper bound; the server can set any Retry-After it | 355 // We do not check for an upper bound; the server can set any Retry-After it |
| 240 // desires. Recovery from error would involve restarting the browser. | 356 // desires. Recovery from error would involve restarting the browser. |
| 241 if (value_ms < 0) | 357 if (value_ms < 0) |
| 242 return; | 358 return; |
| 243 | 359 |
| 244 GetBackoffEntry()->SetCustomReleaseTime( | 360 net_log_.AddEvent( |
| 245 GetTimeNow() + base::TimeDelta::FromMilliseconds(value_ms)); | 361 NetLog::TYPE_THROTTLING_GOT_CUSTOM_RETRY_AFTER, |
| 362 make_scoped_refptr(new RetryAfterParameters(url_id_, value_ms))); | |
| 363 | |
| 364 base::TimeDelta value = base::TimeDelta::FromMilliseconds(value_ms); | |
| 365 GetBackoffEntry()->SetCustomReleaseTime(ImplGetTimeNow() + value); | |
| 366 | |
| 367 UMA_HISTOGRAM_CUSTOM_TIMES( | |
| 368 "Throttling.CustomRetryAfterMs", value, | |
| 369 base::TimeDelta::FromSeconds(1), base::TimeDelta::FromHours(12), 50); | |
| 370 if (base::FieldTrialList::TrialExists("ThrottlingEnabled")) { | |
| 371 UMA_HISTOGRAM_CUSTOM_TIMES( | |
| 372 base::FieldTrial::MakeName("Throttling.CustomRetryAfterMs", | |
| 373 "ThrottlingEnabled"), value, | |
| 374 base::TimeDelta::FromSeconds(1), base::TimeDelta::FromHours(12), 50); | |
| 375 } | |
| 246 } | 376 } |
| 247 | 377 |
| 248 void URLRequestThrottlerEntry::HandleThrottlingHeader( | 378 void URLRequestThrottlerEntry::HandleThrottlingHeader( |
| 249 const std::string& header_value, | 379 const std::string& header_value, |
| 250 const std::string& host) { | 380 const std::string& host) { |
| 251 if (header_value == kExponentialThrottlingDisableValue) { | 381 if (header_value == kExponentialThrottlingDisableValue) { |
| 252 DisableBackoffThrottling(); | 382 DisableBackoffThrottling(); |
| 253 if (manager_) | 383 if (manager_) |
| 254 manager_->AddToOptOutList(host); | 384 manager_->AddToOptOutList(host); |
| 255 } else { | 385 } else { |
| 256 // TODO(joi): Log this. | 386 // TODO(joi): Log this. |
| 257 } | 387 } |
| 258 } | 388 } |
| 259 | 389 |
| 390 void URLRequestThrottlerEntry::HandleMetricsTracking(int status_code) { | |
| 391 // This is essentially the same as the "Net.HttpResponseCode" UMA stat | |
| 392 // but we are tracking it separately here for the throttling experiment | |
| 393 // to make sure we count only the responses seen by throttling. | |
| 394 // TODO(joi): Remove after experiment. | |
| 395 UMA_HISTOGRAM_ENUMERATION("Throttling.HttpResponseCode", status_code, 600); | |
| 396 if (base::FieldTrialList::TrialExists("ThrottlingEnabled")) { | |
| 397 UMA_HISTOGRAM_ENUMERATION(base::FieldTrial::MakeName( | |
| 398 "Throttling.HttpResponseCode", "ThrottlingEnabled"), status_code, 600); | |
| 399 } | |
| 400 | |
| 401 // Note that we are not interested in whether the code is considered | |
| 402 // an error for the backoff logic, but whether it is a 5xx error in | |
| 403 // general. This is because here, we are tracking the apparent total | |
| 404 // downtime of a server. | |
| 405 if (status_code >= 500) { | |
| 406 last_response_was_success_ = false; | |
| 407 } else { | |
| 408 base::TimeTicks now = base::TimeTicks::Now(); | |
|
yzshen1
2011/05/25 03:49:06
Is it intended to not use ImplGetTimeNow here?
Jói
2011/05/25 14:30:35
No, thanks for catching that. I am writing tests
| |
| 409 if (!last_response_was_success_) { | |
| 410 // We are transitioning from failure to success, so generate our stats. | |
| 411 base::TimeDelta down_time = now - last_successful_response_time_; | |
| 412 int failure_count = GetBackoffEntry()->failure_count(); | |
| 413 | |
| 414 UMA_HISTOGRAM_COUNTS("Throttling.FailureCountAtSuccess", failure_count); | |
| 415 UMA_HISTOGRAM_CUSTOM_TIMES( | |
| 416 "Throttling.PerceivedDowntime", down_time, | |
| 417 base::TimeDelta::FromMilliseconds(10), | |
| 418 base::TimeDelta::FromHours(6), 50); | |
| 419 | |
| 420 if (base::FieldTrialList::TrialExists("ThrottlingEnabled")) { | |
| 421 UMA_HISTOGRAM_COUNTS(base::FieldTrial::MakeName( | |
| 422 "Throttling.FailureCountAtSuccess", "ThrottlingEnabled"), | |
| 423 failure_count); | |
| 424 UMA_HISTOGRAM_CUSTOM_TIMES(base::FieldTrial::MakeName( | |
| 425 "Throttling.PerceivedDowntime", "ThrottlingEnabled"), down_time, | |
| 426 base::TimeDelta::FromMilliseconds(10), | |
| 427 base::TimeDelta::FromHours(6), 50); | |
| 428 } | |
| 429 } | |
| 430 | |
| 431 last_successful_response_time_ = now; | |
| 432 last_response_was_success_ = true; | |
| 433 } | |
| 434 } | |
| 435 | |
| 260 const BackoffEntry* URLRequestThrottlerEntry::GetBackoffEntry() const { | 436 const BackoffEntry* URLRequestThrottlerEntry::GetBackoffEntry() const { |
| 261 return &backoff_entry_; | 437 return &backoff_entry_; |
| 262 } | 438 } |
| 263 | 439 |
| 264 BackoffEntry* URLRequestThrottlerEntry::GetBackoffEntry() { | 440 BackoffEntry* URLRequestThrottlerEntry::GetBackoffEntry() { |
| 265 return &backoff_entry_; | 441 return &backoff_entry_; |
| 266 } | 442 } |
| 267 | 443 |
| 268 } // namespace net | 444 } // namespace net |
| OLD | NEW |