Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / toolkit / components / url-classifier / content / request-backoff.js
blob86adbf4bc8bd5285e49367df8952463968267a7d
1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3  *
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/
8  *
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
12  * License.
13  *
14  * The Original Code is Google Safe Browsing.
15  *
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.
19  *
20  * Contributor(s):
21  *   Tony Chang <tc@google.com> (original author)
22  *
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.
34  *
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.
42 // 
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
46 // as an error.
47 const HTTP_FOUND                 = 302;
48 const HTTP_SEE_OTHER             = 303;
49 const HTTP_TEMPORARY_REDIRECT    = 307;
51 /**
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
60  */
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_ = [];
74   this.numErrors_ = 0;
75   this.errorTimeout_ = 0;
76   this.nextRequestTime_ = 0;
79 /**
80  * Reset the object for reuse.
81  */
82 RequestBackoff.prototype.reset = function() {
83   this.numErrors_ = 0;
84   this.errorTimeout_ = 0;
85   this.nextRequestTime_ = 0;
88 /**
89  * Check to see if we can make a request.
90  */
91 RequestBackoff.prototype.canMakeRequest = function() {
92   var now = Date.now();
93   if (now < this.nextRequestTime_) {
94     return false;
95   }
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,
116  */
117 RequestBackoff.prototype.noteServerResponse = function(status) {
118   if (this.isErrorStatus(status)) {
119     this.numErrors_++;
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_;
125     else
126       this.errorTimeout_ *= 2;
128     this.errorTimeout_ = Math.min(this.errorTimeout_, this.MAX_TIMEOUT_);
129     this.nextRequestTime_ = Date.now() + this.errorTimeout_;
130   } else {
131     // Reset error timeout, allow requests to go through.
132     this.reset();
133   }
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
140  */
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);