node-fetch
Advanced tools
Comparing version 1.4.1 to 1.5.0
@@ -8,4 +8,10 @@ | ||
## v1.4.1 (master) | ||
## v1.5.0 (master) | ||
- Enhance: rejected promise now use custom `Error` (thx to @pekeler) | ||
- Enhance: `FetchError` contains `err.type` and `err.code`, allows for better error handling (thx to @pekeler) | ||
- Enhance: basic support for redirect mode `manual` and `error`, allows for location header extraction (thx to @jimmywarting for the initial PR) | ||
## v1.4.1 | ||
- Fix: wrapping Request instance with FormData body again should preserve the body as-is | ||
@@ -12,0 +18,0 @@ |
21
index.js
@@ -19,2 +19,3 @@ | ||
var Request = require('./lib/request'); | ||
var FetchError = require('./lib/fetch-error'); | ||
@@ -118,3 +119,3 @@ // commonjs | ||
req.abort(); | ||
reject(new Error('network timeout at: ' + options.url)); | ||
reject(new FetchError('network timeout at: ' + options.url, 'request-timeout')); | ||
}, options.timeout); | ||
@@ -126,3 +127,3 @@ }); | ||
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)); | ||
}); | ||
@@ -134,5 +135,10 @@ | ||
// handle redirect | ||
if (self.isRedirect(res.statusCode)) { | ||
if (self.isRedirect(res.statusCode) && options.redirect !== 'manual') { | ||
if (options.redirect === 'error') { | ||
reject(new FetchError('redirect mode is set to error: ' + options.url, 'no-redirect')); | ||
return; | ||
} | ||
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; | ||
@@ -142,3 +148,3 @@ } | ||
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; | ||
@@ -176,2 +182,7 @@ } | ||
// normalize location header for manual redirect mode | ||
if (options.redirect === 'manual') { | ||
headers.set('location', resolve_url(options.url, headers.get('location'))); | ||
} | ||
// response object | ||
@@ -178,0 +189,0 @@ var output = new Response(body, { |
@@ -0,5 +1,6 @@ | ||
/** | ||
* response.js | ||
* body.js | ||
* | ||
* Response class provides content decoding | ||
* Body interface provides common methods for Request and Response | ||
*/ | ||
@@ -10,2 +11,3 @@ | ||
var PassThrough = require('stream').PassThrough; | ||
var FetchError = require('./fetch-error'); | ||
@@ -15,3 +17,3 @@ module.exports = Body; | ||
/** | ||
* Response class | ||
* Body class | ||
* | ||
@@ -96,3 +98,3 @@ * @param Stream body Readable stream | ||
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); | ||
@@ -103,3 +105,3 @@ } | ||
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)); | ||
}); | ||
@@ -114,3 +116,3 @@ | ||
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; | ||
@@ -117,0 +119,0 @@ } |
@@ -0,1 +1,2 @@ | ||
/** | ||
@@ -2,0 +3,0 @@ * headers.js |
@@ -0,1 +1,2 @@ | ||
/** | ||
@@ -46,2 +47,3 @@ * request.js | ||
this.method = init.method || input.method || 'GET'; | ||
this.redirect = init.redirect || input.redirect || 'follow'; | ||
this.headers = new Headers(init.headers || input.headers || {}); | ||
@@ -48,0 +50,0 @@ this.url = url; |
@@ -0,1 +1,2 @@ | ||
/** | ||
@@ -2,0 +3,0 @@ * response.js |
The MIT License (MIT) | ||
Copyright (c) 2015 David Frank | ||
Copyright (c) 2016 David Frank | ||
@@ -5,0 +5,0 @@ Permission is hereby granted, free of charge, to any person obtaining a copy |
@@ -17,4 +17,6 @@ | ||
- Also, you can handle rejected fetch requests through checking `err.type` and `err.code`. | ||
- Only support `res.text()` and `res.json()` at the moment, until there are good use-cases for blob. | ||
- There is currently no built-in caching, as server-side caching varies by use-cases. |
{ | ||
"name": "node-fetch", | ||
"version": "1.4.1", | ||
"version": "1.5.0", | ||
"description": "A light-weight module that brings window.fetch to node.js and io.js", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -9,3 +9,3 @@ | ||
A light-weight module that brings `window.fetch` to node.js & io.js | ||
A light-weight module that brings `window.fetch` to Node.js | ||
@@ -19,3 +19,3 @@ | ||
Hence `node-fetch`, minimal code for a `window.fetch` compatible API on node.js/io.js runtime. | ||
Hence `node-fetch`, minimal code for a `window.fetch` compatible API on Node.js runtime. | ||
@@ -30,3 +30,3 @@ | ||
- Decode content encoding (gzip/deflate) properly, and convert string output (such as `res.text()` and `res.json()`) to utf-8 automatically. | ||
- Useful extensions such as timeout, redirect limit, response size limit. | ||
- Useful extensions such as timeout, redirect limit, response size limit, explicit reject errors. | ||
@@ -163,3 +163,3 @@ | ||
, body: empty // request body, can be a string or readable stream | ||
, agent: null // custom http.Agent instance | ||
, agent: null // http.Agent instance, allows custom proxy, certificate etc. | ||
} | ||
@@ -166,0 +166,0 @@ ``` |
@@ -23,2 +23,3 @@ | ||
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 | ||
@@ -90,3 +91,5 @@ fetch.Promise = fetch.Promise || bluebird; | ||
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' }); | ||
}); | ||
@@ -285,3 +288,5 @@ | ||
} | ||
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'); | ||
}); | ||
@@ -294,5 +299,29 @@ | ||
} | ||
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 support redirect mode, manual flag', function() { | ||
url = base + '/redirect/301'; | ||
opts = { | ||
redirect: 'manual' | ||
}; | ||
return fetch(url, opts).then(function(res) { | ||
expect(res.url).to.equal(base + '/redirect/301'); | ||
expect(res.status).to.equal(301); | ||
expect(res.headers.get('location')).to.equal(base + '/inspect'); | ||
}); | ||
}); | ||
it('should support redirect mode, error flag', function() { | ||
url = base + '/redirect/301'; | ||
opts = { | ||
redirect: 'error' | ||
}; | ||
return expect(fetch(url, opts)).to.eventually.be.rejected | ||
.and.be.an.instanceOf(FetchError) | ||
.and.have.property('type', 'no-redirect'); | ||
}); | ||
it('should follow redirect code 301 and keep existing headers', function() { | ||
@@ -313,3 +342,5 @@ url = base + '/redirect/301'; | ||
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'); | ||
}); | ||
@@ -349,5 +380,14 @@ | ||
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://domain.invalid'; | ||
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() { | ||
@@ -411,3 +451,5 @@ url = base + '/error/json'; | ||
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'); | ||
}); | ||
@@ -436,3 +478,5 @@ }); | ||
}; | ||
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', 'request-timeout'); | ||
}); | ||
@@ -448,3 +492,5 @@ | ||
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'); | ||
}); | ||
@@ -651,3 +697,5 @@ }); | ||
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'); | ||
}); | ||
@@ -664,3 +712,5 @@ }); | ||
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'); | ||
}); | ||
@@ -1131,2 +1181,3 @@ }); | ||
, method: 'POST' | ||
, redirect: 'manual' | ||
, headers: { | ||
@@ -1142,2 +1193,3 @@ b: '2' | ||
expect(cl.method).to.equal('POST'); | ||
expect(cl.redirect).to.equal('manual'); | ||
expect(cl.headers.get('b')).to.equal('2'); | ||
@@ -1163,2 +1215,16 @@ expect(cl.follow).to.equal(3); | ||
it('should create custom FetchError', function() { | ||
var systemError = new Error('system'); | ||
systemError.code = 'ESOMEERROR'; | ||
var err = new FetchError('test message', 'test-error', systemError); | ||
expect(err).to.be.an.instanceof(Error); | ||
expect(err).to.be.an.instanceof(FetchError); | ||
expect(err.name).to.equal('FetchError'); | ||
expect(err.message).to.equal('test message'); | ||
expect(err.type).to.equal('test-error'); | ||
expect(err.code).to.equal('ESOMEERROR'); | ||
expect(err.errno).to.equal('ESOMEERROR'); | ||
}); | ||
it('should support https request', function() { | ||
@@ -1165,0 +1231,0 @@ this.timeout(5000); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
70984
15
1981
95