Support for program checkable error by introducing custom errors with error types/codes
This finally closes #7
This commit is contained in:
parent
98b167c35d
commit
b6f3913499
9
index.js
9
index.js
|
@ -16,6 +16,7 @@ var Body = require('./lib/body');
|
|||
var Response = require('./lib/response');
|
||||
var Headers = require('./lib/headers');
|
||||
var Request = require('./lib/request');
|
||||
var FetchError = require('./lib/fetch-error');
|
||||
|
||||
// commonjs
|
||||
module.exports = Fetch;
|
||||
|
@ -114,14 +115,14 @@ function Fetch(url, opts) {
|
|||
req.once('socket', function(socket) {
|
||||
reqTimeout = setTimeout(function() {
|
||||
req.abort();
|
||||
reject(new Error('network timeout at: ' + options.url));
|
||||
reject(new FetchError('network timeout at: ' + options.url, 'socket-timeout'));
|
||||
}, options.timeout);
|
||||
});
|
||||
}
|
||||
|
||||
req.on('error', function(err) {
|
||||
clearTimeout(reqTimeout);
|
||||
reject(new Error('request to ' + options.url + ' failed, reason: ' + err.message));
|
||||
reject(new FetchError('request to ' + options.url + ' failed, reason: ' + err.message, 'system', err));
|
||||
});
|
||||
|
||||
req.on('response', function(res) {
|
||||
|
@ -130,12 +131,12 @@ function Fetch(url, opts) {
|
|||
// handle redirect
|
||||
if (self.isRedirect(res.statusCode)) {
|
||||
if (options.counter >= options.follow) {
|
||||
reject(new Error('maximum redirect reached at: ' + options.url));
|
||||
reject(new FetchError('maximum redirect reached at: ' + options.url, 'max-redirect'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!res.headers.location) {
|
||||
reject(new Error('redirect location header missing at: ' + options.url));
|
||||
reject(new FetchError('redirect location header missing at: ' + options.url, 'invalid-redirect'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
var convert = require('encoding').convert;
|
||||
var bodyStream = require('is-stream');
|
||||
var PassThrough = require('stream').PassThrough;
|
||||
var FetchError = require('./fetch-error');
|
||||
|
||||
module.exports = Body;
|
||||
|
||||
|
@ -91,13 +92,13 @@ Body.prototype._decode = function() {
|
|||
if (self.timeout) {
|
||||
resTimeout = setTimeout(function() {
|
||||
self._abort = true;
|
||||
reject(new Error('response timeout at ' + self.url + ' over limit: ' + self.timeout));
|
||||
reject(new FetchError('response timeout at ' + self.url + ' over limit: ' + self.timeout, 'body-timeout'));
|
||||
}, self.timeout);
|
||||
}
|
||||
|
||||
// handle stream error, such as incorrect content-encoding
|
||||
self.body.on('error', function(err) {
|
||||
reject(new Error('invalid response body at: ' + self.url + ' reason: ' + err.message));
|
||||
reject(new FetchError('invalid response body at: ' + self.url + ' reason: ' + err.message, 'system', err));
|
||||
});
|
||||
|
||||
self.body.on('data', function(chunk) {
|
||||
|
@ -107,7 +108,7 @@ Body.prototype._decode = function() {
|
|||
|
||||
if (self.size && self._bytes + chunk.length > self.size) {
|
||||
self._abort = true;
|
||||
reject(new Error('content size at ' + self.url + ' over limit: ' + self.size));
|
||||
reject(new FetchError('content size at ' + self.url + ' over limit: ' + self.size, 'max-size'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* fetch-error.js
|
||||
*
|
||||
* FetchError class for operational errors
|
||||
*/
|
||||
|
||||
module.exports = FetchError;
|
||||
|
||||
/**
|
||||
* Create FetchError
|
||||
*
|
||||
* @param String reason String type Error optionalSystemError
|
||||
* @return FetchError
|
||||
*/
|
||||
function FetchError(message, type, optionalSystemError) {
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
this.name = this.constructor.name;
|
||||
this.message = message;
|
||||
this.type = type;
|
||||
if (optionalSystemError) {
|
||||
this.code = this.errno = optionalSystemError.code;
|
||||
}
|
||||
}
|
||||
|
||||
require('util').inherits(FetchError, Error);
|
48
test/test.js
48
test/test.js
|
@ -20,6 +20,7 @@ var Headers = require('../lib/headers.js');
|
|||
var Response = require('../lib/response.js');
|
||||
var Request = require('../lib/request.js');
|
||||
var Body = require('../lib/body.js');
|
||||
var FetchError = require('../lib/fetch-error.js');
|
||||
// test with native promise on node 0.11, and bluebird for node 0.10
|
||||
fetch.Promise = fetch.Promise || bluebird;
|
||||
|
||||
|
@ -86,7 +87,9 @@ describe('node-fetch', function() {
|
|||
|
||||
it('should reject with error on network failure', function() {
|
||||
url = 'http://localhost:50000/';
|
||||
return expect(fetch(url)).to.eventually.be.rejectedWith(Error);
|
||||
return expect(fetch(url)).to.eventually.be.rejected
|
||||
.and.be.an.instanceOf(FetchError)
|
||||
.and.include({type: 'system', code: 'ECONNREFUSED', errno: 'ECONNREFUSED'});
|
||||
});
|
||||
|
||||
it('should resolve into response', function() {
|
||||
|
@ -280,7 +283,9 @@ describe('node-fetch', function() {
|
|||
opts = {
|
||||
follow: 1
|
||||
}
|
||||
return expect(fetch(url, opts)).to.eventually.be.rejectedWith(Error);
|
||||
return expect(fetch(url, opts)).to.eventually.be.rejected
|
||||
.and.be.an.instanceOf(FetchError)
|
||||
.and.have.property('type', 'max-redirect');
|
||||
});
|
||||
|
||||
it('should allow not following redirect', function() {
|
||||
|
@ -288,7 +293,9 @@ describe('node-fetch', function() {
|
|||
opts = {
|
||||
follow: 0
|
||||
}
|
||||
return expect(fetch(url, opts)).to.eventually.be.rejectedWith(Error);
|
||||
return expect(fetch(url, opts)).to.eventually.be.rejected
|
||||
.and.be.an.instanceOf(FetchError)
|
||||
.and.have.property('type', 'max-redirect');
|
||||
});
|
||||
|
||||
it('should follow redirect code 301 and keep existing headers', function() {
|
||||
|
@ -306,7 +313,9 @@ describe('node-fetch', function() {
|
|||
|
||||
it('should reject broken redirect', function() {
|
||||
url = base + '/error/redirect';
|
||||
return expect(fetch(url)).to.eventually.be.rejectedWith(Error);
|
||||
return expect(fetch(url)).to.eventually.be.rejected
|
||||
.and.be.an.instanceOf(FetchError)
|
||||
.and.have.property('type', 'invalid-redirect');
|
||||
});
|
||||
|
||||
it('should handle client-error response', function() {
|
||||
|
@ -341,7 +350,16 @@ describe('node-fetch', function() {
|
|||
|
||||
it('should handle network-error response', function() {
|
||||
url = base + '/error/reset';
|
||||
return expect(fetch(url)).to.eventually.be.rejectedWith(Error);
|
||||
return expect(fetch(url)).to.eventually.be.rejected
|
||||
.and.be.an.instanceOf(FetchError)
|
||||
.and.have.property('code', 'ECONNRESET');
|
||||
});
|
||||
|
||||
it('should handle DNS-error response', function() {
|
||||
url = 'http://invalid.commm';
|
||||
return expect(fetch(url)).to.eventually.be.rejected
|
||||
.and.be.an.instanceOf(FetchError)
|
||||
.and.have.property('code', 'ENOTFOUND');
|
||||
});
|
||||
|
||||
it('should reject invalid json response', function() {
|
||||
|
@ -402,7 +420,9 @@ describe('node-fetch', function() {
|
|||
url = base + '/invalid-content-encoding';
|
||||
return fetch(url).then(function(res) {
|
||||
expect(res.headers.get('content-type')).to.equal('text/plain');
|
||||
return expect(res.text()).to.eventually.be.rejectedWith(Error);
|
||||
return expect(res.text()).to.eventually.be.rejected
|
||||
.and.be.an.instanceOf(FetchError)
|
||||
.and.have.property('code', 'Z_DATA_ERROR');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -426,7 +446,9 @@ describe('node-fetch', function() {
|
|||
opts = {
|
||||
timeout: 100
|
||||
};
|
||||
return expect(fetch(url, opts)).to.eventually.be.rejectedWith(Error);
|
||||
return expect(fetch(url, opts)).to.eventually.be.rejected
|
||||
.and.be.an.instanceOf(FetchError)
|
||||
.and.have.property('type', 'socket-timeout');
|
||||
});
|
||||
|
||||
it('should allow custom timeout on response body', function() {
|
||||
|
@ -437,7 +459,9 @@ describe('node-fetch', function() {
|
|||
};
|
||||
return fetch(url, opts).then(function(res) {
|
||||
expect(res.ok).to.be.true;
|
||||
return expect(res.text()).to.eventually.be.rejectedWith(Error);
|
||||
return expect(res.text()).to.eventually.be.rejected
|
||||
.and.be.an.instanceOf(FetchError)
|
||||
.and.have.property('type', 'body-timeout');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -639,7 +663,9 @@ describe('node-fetch', function() {
|
|||
return fetch(url, opts).then(function(res) {
|
||||
expect(res.status).to.equal(200);
|
||||
expect(res.headers.get('content-type')).to.equal('text/plain');
|
||||
return expect(res.text()).to.eventually.be.rejectedWith(Error);
|
||||
return expect(res.text()).to.eventually.be.rejected
|
||||
.and.be.an.instanceOf(FetchError)
|
||||
.and.have.property('type', 'max-size');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -651,7 +677,9 @@ describe('node-fetch', function() {
|
|||
return fetch(url, opts).then(function(res) {
|
||||
expect(res.status).to.equal(200);
|
||||
expect(res.headers.get('content-type')).to.equal('text/plain');
|
||||
return expect(res.text()).to.eventually.be.rejectedWith(Error);
|
||||
return expect(res.text()).to.eventually.be.rejected
|
||||
.and.be.an.instanceOf(FetchError)
|
||||
.and.have.property('type', 'max-size');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue