1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/url-classifier/content/request-backoff.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,115 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +// This implements logic for stopping requests if the server starts to return 1.9 +// too many errors. If we get MAX_ERRORS errors in ERROR_PERIOD minutes, we 1.10 +// back off for TIMEOUT_INCREMENT minutes. If we get another error 1.11 +// immediately after we restart, we double the timeout and add 1.12 +// TIMEOUT_INCREMENT minutes, etc. 1.13 +// 1.14 +// This is similar to the logic used by the search suggestion service. 1.15 + 1.16 +// HTTP responses that count as an error. We also include any 5xx response 1.17 +// as an error. 1.18 +const HTTP_FOUND = 302; 1.19 +const HTTP_SEE_OTHER = 303; 1.20 +const HTTP_TEMPORARY_REDIRECT = 307; 1.21 + 1.22 +/** 1.23 + * @param maxErrors Number of times to request before backing off. 1.24 + * @param retryIncrement Time (ms) for each retry before backing off. 1.25 + * @param maxRequests Number the number of requests needed to trigger backoff 1.26 + * @param requestPeriod Number time (ms) in which maxRequests have to occur to 1.27 + * trigger the backoff behavior 1.28 + * @param timeoutIncrement Number time (ms) the starting timeout period 1.29 + * we double this time for consecutive errors 1.30 + * @param maxTimeout Number time (ms) maximum timeout period 1.31 + */ 1.32 +function RequestBackoff(maxErrors, retryIncrement, 1.33 + maxRequests, requestPeriod, 1.34 + timeoutIncrement, maxTimeout) { 1.35 + this.MAX_ERRORS_ = maxErrors; 1.36 + this.RETRY_INCREMENT_ = retryIncrement; 1.37 + this.MAX_REQUESTS_ = maxRequests; 1.38 + this.REQUEST_PERIOD_ = requestPeriod; 1.39 + this.TIMEOUT_INCREMENT_ = timeoutIncrement; 1.40 + this.MAX_TIMEOUT_ = maxTimeout; 1.41 + 1.42 + // Queue of ints keeping the time of all requests 1.43 + this.requestTimes_ = []; 1.44 + 1.45 + this.numErrors_ = 0; 1.46 + this.errorTimeout_ = 0; 1.47 + this.nextRequestTime_ = 0; 1.48 +} 1.49 + 1.50 +/** 1.51 + * Reset the object for reuse. 1.52 + */ 1.53 +RequestBackoff.prototype.reset = function() { 1.54 + this.numErrors_ = 0; 1.55 + this.errorTimeout_ = 0; 1.56 + this.nextRequestTime_ = 0; 1.57 +} 1.58 + 1.59 +/** 1.60 + * Check to see if we can make a request. 1.61 + */ 1.62 +RequestBackoff.prototype.canMakeRequest = function() { 1.63 + var now = Date.now(); 1.64 + if (now < this.nextRequestTime_) { 1.65 + return false; 1.66 + } 1.67 + 1.68 + return (this.requestTimes_.length < this.MAX_REQUESTS_ || 1.69 + (now - this.requestTimes_[0]) > this.REQUEST_PERIOD_); 1.70 +} 1.71 + 1.72 +RequestBackoff.prototype.noteRequest = function() { 1.73 + var now = Date.now(); 1.74 + this.requestTimes_.push(now); 1.75 + 1.76 + // We only care about keeping track of MAX_REQUESTS 1.77 + if (this.requestTimes_.length > this.MAX_REQUESTS_) 1.78 + this.requestTimes_.shift(); 1.79 +} 1.80 + 1.81 +RequestBackoff.prototype.nextRequestDelay = function() { 1.82 + return Math.max(0, this.nextRequestTime_ - Date.now()); 1.83 +} 1.84 + 1.85 +/** 1.86 + * Notify this object of the last server response. If it's an error, 1.87 + */ 1.88 +RequestBackoff.prototype.noteServerResponse = function(status) { 1.89 + if (this.isErrorStatus(status)) { 1.90 + this.numErrors_++; 1.91 + 1.92 + if (this.numErrors_ < this.MAX_ERRORS_) 1.93 + this.errorTimeout_ = this.RETRY_INCREMENT_; 1.94 + else if (this.numErrors_ == this.MAX_ERRORS_) 1.95 + this.errorTimeout_ = this.TIMEOUT_INCREMENT_; 1.96 + else 1.97 + this.errorTimeout_ *= 2; 1.98 + 1.99 + this.errorTimeout_ = Math.min(this.errorTimeout_, this.MAX_TIMEOUT_); 1.100 + this.nextRequestTime_ = Date.now() + this.errorTimeout_; 1.101 + } else { 1.102 + // Reset error timeout, allow requests to go through. 1.103 + this.reset(); 1.104 + } 1.105 +} 1.106 + 1.107 +/** 1.108 + * We consider 302, 303, 307, 4xx, and 5xx http responses to be errors. 1.109 + * @param status Number http status 1.110 + * @return Boolean true if we consider this http status an error 1.111 + */ 1.112 +RequestBackoff.prototype.isErrorStatus = function(status) { 1.113 + return ((400 <= status && status <= 599) || 1.114 + HTTP_FOUND == status || 1.115 + HTTP_SEE_OTHER == status || 1.116 + HTTP_TEMPORARY_REDIRECT == status); 1.117 +} 1.118 +