diff --git a/README.md b/README.md index fbdab68..17c8888 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ request({ maxAttempts: 5, // (default) try 5 times retryDelay: 5000 // (default) wait for 5s before trying again }, function(err, response, body){ - // this callback will only be called when the request succeeded or after maxAttempts. + // this callback will only be called when the request succeeded or after maxAttempts or on error }); ``` @@ -37,6 +37,7 @@ Install with [npm](https://npmjs.org/package/requestretry). ## Changelog -v1.0.0: Initial commit +v1.0.0: request now yield an Request instance with a `.abort()` method. +v0.0.1: Initial commit Copyright 2014, [Francois-Guillaume Ribreau](http://fgribreau.com) (npm@fgribreau.com) diff --git a/index.js b/index.js index a641030..12728ef 100644 --- a/index.js +++ b/index.js @@ -1,14 +1,15 @@ 'use strict'; /* - * retriablerequest + * Request * * Copyright(c) 2014 Francois-Guillaume Ribreau * MIT Licensed * */ -var request = require('request'); -var _ = require('fg-lodash'); +var request = require('request'); +var _ = require('fg-lodash'); +var Cancelable = require('cancelable'); var RETRIABLE_ERRORS = ['ECONNRESET', 'ENOTFOUND', 'ESOCKETTIMEDOUT', 'ETIMEDOUT', 'ECONNREFUSED']; @@ -17,26 +18,50 @@ var DEFAULTS = { retryDelay: 5000, // wait for 5s before trying again }; -function isRetriableRequest(err, response){ - // Inspired from https://github.com/geoffreak/request-enhanced/blob/master/src/request-enhanced.coffee#L107 - return (err && _.contains(RETRIABLE_ERRORS, err.code)) || (response && 500 <= response.statusCode && response.statusCode < 600); +function Request(options, f, maxAttempts, retryDelay){ + this.maxAttempts = maxAttempts; + this.retryDelay = retryDelay; + this.options = options; + this.f = _.once(f); + this._timeout = null; + this._req = null; } -function tryUntilFail(options, f, retryOptions){ - retryOptions.maxAttempts--; +Request.request = request; + +Request.prototype._tryUntilFail = function(){ + this.maxAttempts--; - request(options, function(err, response, body){ - if(isRetriableRequest(err, response) && retryOptions.maxAttempts >= 0){ - return setTimeout(tryUntilFail.bind(null, options, f, retryOptions), retryOptions.retryDelay); + this._req = Request.request(this.options, function(err, response, body){ + if(this._isRetriable(err, response) && this.maxAttempts >= 0){ + this._timeout = setTimeout(this._tryUntilFail.bind(this), this.retryDelay); + return; } - return f(err, response, body); - }); -} + return this.f(err, response, body); + }.bind(this)); +}; + +Request.prototype._isRetriable = function(err, response){ + // Inspired from https://github.com/geoffreak/request-enhanced/blob/master/src/request-enhanced.coffee#L107 + return (err && _.contains(RETRIABLE_ERRORS, err.code)) || (response && 500 <= response.statusCode && response.statusCode < 600); +}; -function RetriableRequest(options, f){ - var retryOptions = _(options || {}).defaults(DEFAULTS).pick(Object.keys(DEFAULTS)).value(); - tryUntilFail(options, f, retryOptions); +Request.prototype.abort = function(){ + if(this._req){ + this._req.abort(); + } + clearTimeout(this._timeout); + this.f(new Error('Aborted')); +}; + +function Factory(options, f){ + var retry = _(options || {}).defaults(DEFAULTS).pick(Object.keys(DEFAULTS)).value(); + var req = new Request(options, f, retry.maxAttempts, retry.retryDelay); + req._tryUntilFail(); + return req; } -module.exports = RetriableRequest; +module.exports = Factory; + +Factory.Request = Request; diff --git a/package.json b/package.json index dc27a8c..f702ebf 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "email": "npm@fgribreau.com", "url": "http://fgribreau.com" }, - "version": "0.0.1", + "version": "1.0.0", "repository": { "url": "https://github.com/FGRibreau/node-request-retry" }, @@ -15,5 +15,9 @@ "dependencies": { "fg-lodash": "0.0.1", "request": "~2.34.0" + }, + "devDependencies": { + "chai": "~1.9.1", + "mocha": "~1.18.2" } } diff --git a/test/abort.js b/test/abort.js new file mode 100644 index 0000000..d3455ce --- /dev/null +++ b/test/abort.js @@ -0,0 +1,20 @@ +'use strict'; + +var request = require('../'); +var t = require('chai').assert; + +describe('Request-retry', function () { + + it('should return a RequestRetry handler object with a abort method', function (done) { + var start = +new Date(); + var o = request({ + url: 'http://filltext.com/?rows=1&delay=10', // wait for 4s + json:true + }, function(err){ + t.strictEqual(err.toString(), 'Error: Aborted'); + done(); + }); + + o.abort(); + }); +});