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);