1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is Google Safe Browsing.
16 * The Initial Developer of the Original Code is Google Inc.
17 * Portions created by the Initial Developer are Copyright (C) 2006
18 * the Initial Developer. All Rights Reserved.
21 * Tony Chang <tc@google.com> (original author)
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
37 // This implements logic for stopping requests if the server starts to return
38 // too many errors. If we get MAX_ERRORS errors in ERROR_PERIOD minutes, we
39 // back off for TIMEOUT_INCREMENT minutes. If we get another error
40 // immediately after we restart, we double the timeout and add
41 // TIMEOUT_INCREMENT minutes, etc.
43 // This is similar to the logic used by the search suggestion service.
45 // HTTP responses that count as an error. We also include any 5xx response
47 const HTTP_FOUND
= 302;
48 const HTTP_SEE_OTHER
= 303;
49 const HTTP_TEMPORARY_REDIRECT
= 307;
52 * @param maxErrors Number of times to request before backing off.
53 * @param retryIncrement Time (ms) for each retry before backing off.
54 * @param maxRequests Number the number of requests needed to trigger backoff
55 * @param requestPeriod Number time (ms) in which maxRequests have to occur to
56 * trigger the backoff behavior
57 * @param timeoutIncrement Number time (ms) the starting timeout period
58 * we double this time for consecutive errors
59 * @param maxTimeout Number time (ms) maximum timeout period
61 function RequestBackoff(maxErrors
, retryIncrement
,
62 maxRequests
, requestPeriod
,
63 timeoutIncrement
, maxTimeout
) {
64 this.MAX_ERRORS_
= maxErrors
;
65 this.RETRY_INCREMENT_
= retryIncrement
;
66 this.MAX_REQUESTS_
= maxRequests
;
67 this.REQUEST_PERIOD_
= requestPeriod
;
68 this.TIMEOUT_INCREMENT_
= timeoutIncrement
;
69 this.MAX_TIMEOUT_
= maxTimeout
;
71 // Queue of ints keeping the time of all requests
72 this.requestTimes_
= [];
75 this.errorTimeout_
= 0;
76 this.nextRequestTime_
= 0;
80 * Reset the object for reuse.
82 RequestBackoff
.prototype.reset = function() {
84 this.errorTimeout_
= 0;
85 this.nextRequestTime_
= 0;
89 * Check to see if we can make a request.
91 RequestBackoff
.prototype.canMakeRequest = function() {
93 if (now
< this.nextRequestTime_
) {
97 return (this.requestTimes_
.length
< this.MAX_REQUESTS_
||
98 (now
- this.requestTimes_
[0]) > this.REQUEST_PERIOD_
);
101 RequestBackoff
.prototype.noteRequest = function() {
102 var now
= Date
.now();
103 this.requestTimes_
.push(now
);
105 // We only care about keeping track of MAX_REQUESTS
106 if (this.requestTimes_
.length
> this.MAX_REQUESTS_
)
107 this.requestTimes_
.shift();
110 RequestBackoff
.prototype.nextRequestDelay = function() {
111 return Math
.max(0, this.nextRequestTime_
- Date
.now());
115 * Notify this object of the last server response. If it's an error,
117 RequestBackoff
.prototype.noteServerResponse = function(status
) {
118 if (this.isErrorStatus(status
)) {
121 if (this.numErrors_
< this.MAX_ERRORS_
)
122 this.errorTimeout_
= this.RETRY_INCREMENT_
;
123 else if (this.numErrors_
== this.MAX_ERRORS_
)
124 this.errorTimeout_
= this.TIMEOUT_INCREMENT_
;
126 this.errorTimeout_
*= 2;
128 this.errorTimeout_
= Math
.min(this.errorTimeout_
, this.MAX_TIMEOUT_
);
129 this.nextRequestTime_
= Date
.now() + this.errorTimeout_
;
131 // Reset error timeout, allow requests to go through.
137 * We consider 302, 303, 307, 4xx, and 5xx http responses to be errors.
138 * @param status Number http status
139 * @return Boolean true if we consider this http status an error
141 RequestBackoff
.prototype.isErrorStatus = function(status
) {
142 return ((400 <= status
&& status
<= 599) ||
143 HTTP_FOUND
== status
||
144 HTTP_SEE_OTHER
== status
||
145 HTTP_TEMPORARY_REDIRECT
== status
);