clone method support
This commit is contained in:
parent
00fa867914
commit
362aa087ca
1
index.js
1
index.js
|
@ -172,6 +172,7 @@ function Fetch(url, opts) {
|
|||
var output = new Response(body, {
|
||||
url: options.url
|
||||
, status: res.statusCode
|
||||
, statusText: res.statusMessage
|
||||
, headers: headers
|
||||
, size: options.size
|
||||
, timeout: options.timeout
|
||||
|
|
25
lib/body.js
25
lib/body.js
|
@ -5,6 +5,8 @@
|
|||
*/
|
||||
|
||||
var convert = require('encoding').convert;
|
||||
var bodyStream = require('is-stream');
|
||||
var PassThrough = require('stream').PassThrough;
|
||||
|
||||
module.exports = Body;
|
||||
|
||||
|
@ -194,5 +196,28 @@ Body.prototype._convert = function(encoding) {
|
|||
|
||||
};
|
||||
|
||||
/**
|
||||
* Clone body given Res/Req instance
|
||||
*
|
||||
* @param Mixed instance Response or Request instance
|
||||
* @return Mixed
|
||||
*/
|
||||
Body.prototype._clone = function(instance) {
|
||||
var pass;
|
||||
var body = instance.body;
|
||||
|
||||
if (instance.bodyUsed) {
|
||||
throw new Error('cannot clone body after it is used');
|
||||
}
|
||||
|
||||
if (bodyStream(body)) {
|
||||
pass = new PassThrough();
|
||||
body.pipe(pass);
|
||||
body = pass;
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
// expose Promise
|
||||
Body.Promise = global.Promise;
|
||||
|
|
|
@ -50,13 +50,13 @@ function Request(input, init) {
|
|||
this.follow = init.follow !== undefined ?
|
||||
init.follow : input.follow !== undefined ?
|
||||
input.follow : 20;
|
||||
this.counter = init.counter || input.follow || 0;
|
||||
this.compress = init.compress !== undefined ?
|
||||
init.compress : input.compress !== undefined ?
|
||||
input.compress : true;
|
||||
this.agent = init.agent || input.agent;
|
||||
this.counter = init.counter || input.counter || input.follow || 0;
|
||||
this.agent = init.agent || input.agent || input.agent;
|
||||
|
||||
Body.call(this, init.body || input.body, {
|
||||
Body.call(this, init.body || this._clone(input), {
|
||||
timeout: init.timeout || input.timeout || 0,
|
||||
size: init.size || input.size || 0
|
||||
});
|
||||
|
@ -70,3 +70,12 @@ function Request(input, init) {
|
|||
}
|
||||
|
||||
Request.prototype = Object.create(Body.prototype);
|
||||
|
||||
/**
|
||||
* Clone this request
|
||||
*
|
||||
* @return Request
|
||||
*/
|
||||
Request.prototype.clone = function() {
|
||||
return new Request(this);
|
||||
};
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
* Response class provides content decoding
|
||||
*/
|
||||
|
||||
var http = require('http');
|
||||
var convert = require('encoding').convert;
|
||||
var Headers = require('./headers');
|
||||
var Body = require('./body');
|
||||
|
||||
|
@ -24,7 +22,7 @@ function Response(body, opts) {
|
|||
|
||||
this.url = opts.url;
|
||||
this.status = opts.status;
|
||||
this.statusText = http.STATUS_CODES[this.status];
|
||||
this.statusText = opts.statusText;
|
||||
this.headers = new Headers(opts.headers);
|
||||
this.ok = this.status >= 200 && this.status < 300;
|
||||
|
||||
|
@ -33,3 +31,18 @@ function Response(body, opts) {
|
|||
}
|
||||
|
||||
Response.prototype = Object.create(Body.prototype);
|
||||
|
||||
/**
|
||||
* Clone this response
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
Response.prototype.clone = function() {
|
||||
return new Response(this._clone(this), {
|
||||
url: this.url
|
||||
, status: this.status
|
||||
, statusText: this.statusText
|
||||
, headers: this.headers
|
||||
, ok: this.ok
|
||||
});
|
||||
};
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
"resumer": "0.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"encoding": "^0.1.11"
|
||||
"encoding": "^0.1.11",
|
||||
"is-stream": "^1.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -179,6 +179,17 @@ TestServer.prototype.router = function(req, res) {
|
|||
res.end(convert('<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS" /><div>日本語</div>', 'Shift_JIS'));
|
||||
}
|
||||
|
||||
if (p === '/encoding/invalid') {
|
||||
res.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.setHeader('Transfer-Encoding', 'chunked');
|
||||
var padding = 'a'.repeat(120);
|
||||
for (var i = 0; i < 10; i++) {
|
||||
res.write(padding);
|
||||
}
|
||||
res.end(convert('中文', 'gbk'));
|
||||
}
|
||||
|
||||
if (p === '/redirect/301') {
|
||||
res.statusCode = 301;
|
||||
res.setHeader('Location', '/inspect');
|
||||
|
|
118
test/test.js
118
test/test.js
|
@ -10,6 +10,7 @@ var spawn = require('child_process').spawn;
|
|||
var stream = require('stream');
|
||||
var resumer = require('resumer');
|
||||
var FormData = require('form-data');
|
||||
var http = require('http');
|
||||
|
||||
var TestServer = require('./server');
|
||||
|
||||
|
@ -723,6 +724,17 @@ describe('node-fetch', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should only do encoding detection up to 1024 bytes', function() {
|
||||
url = base + '/encoding/invalid';
|
||||
return fetch(url).then(function(res) {
|
||||
expect(res.status).to.equal(200);
|
||||
var padding = 'a'.repeat(1200);
|
||||
return res.text().then(function(result) {
|
||||
expect(result).to.not.equal(padding + '中文');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow piping response body as stream', function(done) {
|
||||
url = base + '/hello';
|
||||
fetch(url).then(function(res) {
|
||||
|
@ -739,6 +751,62 @@ describe('node-fetch', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should allow cloning a response, and use both as stream', function(done) {
|
||||
url = base + '/hello';
|
||||
return fetch(url).then(function(res) {
|
||||
var counter = 0;
|
||||
var r1 = res.clone();
|
||||
expect(res.body).to.be.an.instanceof(stream.Transform);
|
||||
expect(r1.body).to.be.an.instanceof(stream.Transform);
|
||||
res.body.on('data', function(chunk) {
|
||||
if (chunk === null) {
|
||||
return;
|
||||
}
|
||||
expect(chunk.toString()).to.equal('world');
|
||||
});
|
||||
res.body.on('end', function() {
|
||||
counter++;
|
||||
if (counter == 2) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
r1.body.on('data', function(chunk) {
|
||||
if (chunk === null) {
|
||||
return;
|
||||
}
|
||||
expect(chunk.toString()).to.equal('world');
|
||||
});
|
||||
r1.body.on('end', function() {
|
||||
counter++;
|
||||
if (counter == 2) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow cloning a json response, and log it as text response', function() {
|
||||
url = base + '/json';
|
||||
return fetch(url).then(function(res) {
|
||||
var r1 = res.clone();
|
||||
return fetch.Promise.all([r1.text(), res.json()]).then(function(results) {
|
||||
expect(results[0]).to.equal('{"name":"value"}');
|
||||
expect(results[1]).to.deep.equal({name: 'value'});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not allow cloning a response after its been used', function() {
|
||||
url = base + '/hello';
|
||||
return fetch(url).then(function(res) {
|
||||
return res.text().then(function(result) {
|
||||
expect(function() {
|
||||
var r1 = res.clone();
|
||||
}).to.throw(Error);
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
it('should allow get all responses of a header', function() {
|
||||
url = base + '/cookie';
|
||||
return fetch(url).then(function(res) {
|
||||
|
@ -768,7 +836,7 @@ describe('node-fetch', function() {
|
|||
, ["b", "3"]
|
||||
, ["c", "4"]
|
||||
];
|
||||
expect(result).to.be.deep.equal(expected);
|
||||
expect(result).to.deep.equal(expected);
|
||||
});
|
||||
|
||||
it('should allow deleting header', function() {
|
||||
|
@ -925,6 +993,26 @@ describe('node-fetch', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should support clone() method in Response constructor', function() {
|
||||
var res = new Response('a=1', {
|
||||
headers: {
|
||||
a: '1'
|
||||
}
|
||||
, url: base
|
||||
, status: 346
|
||||
, statusText: 'production'
|
||||
});
|
||||
var cl = res.clone();
|
||||
expect(cl.headers.get('a')).to.equal('1');
|
||||
expect(cl.url).to.equal(base);
|
||||
expect(cl.status).to.equal(346);
|
||||
expect(cl.statusText).to.equal('production');
|
||||
expect(cl.ok).to.be.false;
|
||||
return cl.text().then(function(result) {
|
||||
expect(result).to.equal('a=1');
|
||||
});
|
||||
});
|
||||
|
||||
it('should support stream as body in Response constructor', function() {
|
||||
var body = resumer().queue('a=1').end();
|
||||
body = body.pipe(new stream.PassThrough());
|
||||
|
@ -981,6 +1069,34 @@ describe('node-fetch', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should support clone() method in Request constructor', function() {
|
||||
url = base;
|
||||
var agent = new http.Agent();
|
||||
var req = new Request(url, {
|
||||
body: 'a=1'
|
||||
, method: 'POST'
|
||||
, headers: {
|
||||
b: '2'
|
||||
}
|
||||
, follow: 3
|
||||
, compress: false
|
||||
, agent: agent
|
||||
});
|
||||
var cl = req.clone();
|
||||
expect(cl.url).to.equal(url);
|
||||
expect(cl.method).to.equal('POST');
|
||||
expect(cl.headers.get('b')).to.equal('2');
|
||||
expect(cl.follow).to.equal(3);
|
||||
expect(cl.compress).to.equal(false);
|
||||
expect(cl.method).to.equal('POST');
|
||||
expect(cl.counter).to.equal(3);
|
||||
expect(cl.agent).to.equal(agent);
|
||||
return fetch.Promise.all([cl.text(), req.text()]).then(function(results) {
|
||||
expect(results[0]).to.equal('a=1');
|
||||
expect(results[1]).to.equal('a=1');
|
||||
});
|
||||
});
|
||||
|
||||
it('should support text() and json() method in Body constructor', function() {
|
||||
var body = new Body('a=1');
|
||||
expect(body).to.have.property('text');
|
||||
|
|
Loading…
Reference in New Issue