toolkit/components/url-classifier/content/request-backoff.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 // This implements logic for stopping requests if the server starts to return
michael@0 6 // too many errors. If we get MAX_ERRORS errors in ERROR_PERIOD minutes, we
michael@0 7 // back off for TIMEOUT_INCREMENT minutes. If we get another error
michael@0 8 // immediately after we restart, we double the timeout and add
michael@0 9 // TIMEOUT_INCREMENT minutes, etc.
michael@0 10 //
michael@0 11 // This is similar to the logic used by the search suggestion service.
michael@0 12
michael@0 13 // HTTP responses that count as an error. We also include any 5xx response
michael@0 14 // as an error.
michael@0 15 const HTTP_FOUND = 302;
michael@0 16 const HTTP_SEE_OTHER = 303;
michael@0 17 const HTTP_TEMPORARY_REDIRECT = 307;
michael@0 18
michael@0 19 /**
michael@0 20 * @param maxErrors Number of times to request before backing off.
michael@0 21 * @param retryIncrement Time (ms) for each retry before backing off.
michael@0 22 * @param maxRequests Number the number of requests needed to trigger backoff
michael@0 23 * @param requestPeriod Number time (ms) in which maxRequests have to occur to
michael@0 24 * trigger the backoff behavior
michael@0 25 * @param timeoutIncrement Number time (ms) the starting timeout period
michael@0 26 * we double this time for consecutive errors
michael@0 27 * @param maxTimeout Number time (ms) maximum timeout period
michael@0 28 */
michael@0 29 function RequestBackoff(maxErrors, retryIncrement,
michael@0 30 maxRequests, requestPeriod,
michael@0 31 timeoutIncrement, maxTimeout) {
michael@0 32 this.MAX_ERRORS_ = maxErrors;
michael@0 33 this.RETRY_INCREMENT_ = retryIncrement;
michael@0 34 this.MAX_REQUESTS_ = maxRequests;
michael@0 35 this.REQUEST_PERIOD_ = requestPeriod;
michael@0 36 this.TIMEOUT_INCREMENT_ = timeoutIncrement;
michael@0 37 this.MAX_TIMEOUT_ = maxTimeout;
michael@0 38
michael@0 39 // Queue of ints keeping the time of all requests
michael@0 40 this.requestTimes_ = [];
michael@0 41
michael@0 42 this.numErrors_ = 0;
michael@0 43 this.errorTimeout_ = 0;
michael@0 44 this.nextRequestTime_ = 0;
michael@0 45 }
michael@0 46
michael@0 47 /**
michael@0 48 * Reset the object for reuse.
michael@0 49 */
michael@0 50 RequestBackoff.prototype.reset = function() {
michael@0 51 this.numErrors_ = 0;
michael@0 52 this.errorTimeout_ = 0;
michael@0 53 this.nextRequestTime_ = 0;
michael@0 54 }
michael@0 55
michael@0 56 /**
michael@0 57 * Check to see if we can make a request.
michael@0 58 */
michael@0 59 RequestBackoff.prototype.canMakeRequest = function() {
michael@0 60 var now = Date.now();
michael@0 61 if (now < this.nextRequestTime_) {
michael@0 62 return false;
michael@0 63 }
michael@0 64
michael@0 65 return (this.requestTimes_.length < this.MAX_REQUESTS_ ||
michael@0 66 (now - this.requestTimes_[0]) > this.REQUEST_PERIOD_);
michael@0 67 }
michael@0 68
michael@0 69 RequestBackoff.prototype.noteRequest = function() {
michael@0 70 var now = Date.now();
michael@0 71 this.requestTimes_.push(now);
michael@0 72
michael@0 73 // We only care about keeping track of MAX_REQUESTS
michael@0 74 if (this.requestTimes_.length > this.MAX_REQUESTS_)
michael@0 75 this.requestTimes_.shift();
michael@0 76 }
michael@0 77
michael@0 78 RequestBackoff.prototype.nextRequestDelay = function() {
michael@0 79 return Math.max(0, this.nextRequestTime_ - Date.now());
michael@0 80 }
michael@0 81
michael@0 82 /**
michael@0 83 * Notify this object of the last server response. If it's an error,
michael@0 84 */
michael@0 85 RequestBackoff.prototype.noteServerResponse = function(status) {
michael@0 86 if (this.isErrorStatus(status)) {
michael@0 87 this.numErrors_++;
michael@0 88
michael@0 89 if (this.numErrors_ < this.MAX_ERRORS_)
michael@0 90 this.errorTimeout_ = this.RETRY_INCREMENT_;
michael@0 91 else if (this.numErrors_ == this.MAX_ERRORS_)
michael@0 92 this.errorTimeout_ = this.TIMEOUT_INCREMENT_;
michael@0 93 else
michael@0 94 this.errorTimeout_ *= 2;
michael@0 95
michael@0 96 this.errorTimeout_ = Math.min(this.errorTimeout_, this.MAX_TIMEOUT_);
michael@0 97 this.nextRequestTime_ = Date.now() + this.errorTimeout_;
michael@0 98 } else {
michael@0 99 // Reset error timeout, allow requests to go through.
michael@0 100 this.reset();
michael@0 101 }
michael@0 102 }
michael@0 103
michael@0 104 /**
michael@0 105 * We consider 302, 303, 307, 4xx, and 5xx http responses to be errors.
michael@0 106 * @param status Number http status
michael@0 107 * @return Boolean true if we consider this http status an error
michael@0 108 */
michael@0 109 RequestBackoff.prototype.isErrorStatus = function(status) {
michael@0 110 return ((400 <= status && status <= 599) ||
michael@0 111 HTTP_FOUND == status ||
michael@0 112 HTTP_SEE_OTHER == status ||
michael@0 113 HTTP_TEMPORARY_REDIRECT == status);
michael@0 114 }
michael@0 115

mercurial