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

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

mercurial