Skip to content

Commit 083c31f

Browse files
committed
Merge pull request node-fetch#90 from bitinn/prepare-new-release
Prepare new release v1.4.0
2 parents 258c036 + 14c20dd commit 083c31f

File tree

10 files changed

+462
-197
lines changed

10 files changed

+462
-197
lines changed

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ language: node_js
22
node_js:
33
- "0.10"
44
- "0.12"
5-
- "iojs"
65
- "node"
76
before_install: npm install -g npm
87
script: npm run coverage

CHANGELOG.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,17 @@ Changelog
55

66
# 1.x release
77

8-
## v1.3.3 (master)
8+
## v1.4.0 (master)
9+
10+
- Enhance: Request and Response now have `clone` method (thx to @kirill-konshin for the initial PR)
11+
- Enhance: Request and Response now have proper string and buffer body support (thx to @kirill-konshin)
12+
- Enhance: Body constructor has been refactored out (thx to @kirill-konshin)
13+
- Enhance: Headers now has `forEach` method (thx to @tricoder42)
14+
- Enhance: back to 100% code coverage
15+
- Fix: better form-data support (thx to @item4)
16+
- Fix: better character encoding detection under chunked encoding (thx to @dsuket for the initial PR)
17+
18+
## v1.3.3
919

1020
- Fix: make sure `Content-Length` header is set when body is string for POST/PUT/PATCH requests
1121
- Fix: handle body stream error, for cases such as incorrect `Content-Encoding` header

index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ var Response = require('./lib/response');
1717
var Headers = require('./lib/headers');
1818
var Request = require('./lib/request');
1919

20+
// commonjs
2021
module.exports = Fetch;
22+
// es6 default export compatibility
2123
module.exports.default = module.exports;
2224

2325
/**
@@ -170,6 +172,7 @@ function Fetch(url, opts) {
170172
var output = new Response(body, {
171173
url: options.url
172174
, status: res.statusCode
175+
, statusText: res.statusMessage
173176
, headers: headers
174177
, size: options.size
175178
, timeout: options.timeout

lib/body.js

Lines changed: 157 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
*/
66

77
var convert = require('encoding').convert;
8+
var bodyStream = require('is-stream');
9+
var PassThrough = require('stream').PassThrough;
810

911
module.exports = Body;
1012

@@ -17,14 +19,14 @@ module.exports = Body;
1719
*/
1820
function Body(body, opts) {
1921

20-
opts = opts || {};
22+
opts = opts || {};
2123

22-
this.body = body;
23-
this.bodyUsed = false;
24-
this.size = opts.size || 0;
25-
this.timeout = opts.timeout || 0;
26-
this._raw = [];
27-
this._abort = false;
24+
this.body = body;
25+
this.bodyUsed = false;
26+
this.size = opts.size || 0;
27+
this.timeout = opts.timeout || 0;
28+
this._raw = [];
29+
this._abort = false;
2830

2931
}
3032

@@ -35,9 +37,9 @@ function Body(body, opts) {
3537
*/
3638
Body.prototype.json = function() {
3739

38-
return this._decode().then(function(text) {
39-
return JSON.parse(text);
40-
});
40+
return this._decode().then(function(text) {
41+
return JSON.parse(text);
42+
});
4143

4244
};
4345

@@ -48,7 +50,7 @@ Body.prototype.json = function() {
4850
*/
4951
Body.prototype.text = function() {
5052

51-
return this._decode();
53+
return this._decode();
5254

5355
};
5456

@@ -59,69 +61,69 @@ Body.prototype.text = function() {
5961
*/
6062
Body.prototype._decode = function() {
6163

62-
var self = this;
63-
64-
if (this.bodyUsed) {
65-
return Body.Promise.reject(new Error('body used already for: ' + this.url));
66-
}
67-
68-
this.bodyUsed = true;
69-
this._bytes = 0;
70-
this._abort = false;
71-
this._raw = [];
72-
73-
return new Body.Promise(function(resolve, reject) {
74-
var resTimeout;
75-
76-
if (typeof self.body === 'string') {
77-
self._bytes = self.body.length;
78-
self._raw = [new Buffer(self.body)];
79-
return resolve(self._convert());
80-
}
81-
82-
if (self.body instanceof Buffer) {
83-
self._bytes = self.body.length;
84-
self._raw = [self.body];
85-
return resolve(self._convert());
86-
}
87-
88-
// allow timeout on slow response body
89-
if (self.timeout) {
90-
resTimeout = setTimeout(function() {
91-
self._abort = true;
92-
reject(new Error('response timeout at ' + self.url + ' over limit: ' + self.timeout));
93-
}, self.timeout);
94-
}
95-
96-
// handle stream error, such as incorrect content-encoding
97-
self.body.on('error', function(err) {
98-
reject(new Error('invalid response body at: ' + self.url + ' reason: ' + err.message));
99-
});
100-
101-
self.body.on('data', function(chunk) {
102-
if (self._abort || chunk === null) {
103-
return;
104-
}
105-
106-
if (self.size && self._bytes + chunk.length > self.size) {
107-
self._abort = true;
108-
reject(new Error('content size at ' + self.url + ' over limit: ' + self.size));
109-
return;
110-
}
111-
112-
self._bytes += chunk.length;
113-
self._raw.push(chunk);
114-
});
115-
116-
self.body.on('end', function() {
117-
if (self._abort) {
118-
return;
119-
}
120-
121-
clearTimeout(resTimeout);
122-
resolve(self._convert());
123-
});
124-
});
64+
var self = this;
65+
66+
if (this.bodyUsed) {
67+
return Body.Promise.reject(new Error('body used already for: ' + this.url));
68+
}
69+
70+
this.bodyUsed = true;
71+
this._bytes = 0;
72+
this._abort = false;
73+
this._raw = [];
74+
75+
return new Body.Promise(function(resolve, reject) {
76+
var resTimeout;
77+
78+
if (typeof self.body === 'string') {
79+
self._bytes = self.body.length;
80+
self._raw = [new Buffer(self.body)];
81+
return resolve(self._convert());
82+
}
83+
84+
if (self.body instanceof Buffer) {
85+
self._bytes = self.body.length;
86+
self._raw = [self.body];
87+
return resolve(self._convert());
88+
}
89+
90+
// allow timeout on slow response body
91+
if (self.timeout) {
92+
resTimeout = setTimeout(function() {
93+
self._abort = true;
94+
reject(new Error('response timeout at ' + self.url + ' over limit: ' + self.timeout));
95+
}, self.timeout);
96+
}
97+
98+
// handle stream error, such as incorrect content-encoding
99+
self.body.on('error', function(err) {
100+
reject(new Error('invalid response body at: ' + self.url + ' reason: ' + err.message));
101+
});
102+
103+
self.body.on('data', function(chunk) {
104+
if (self._abort || chunk === null) {
105+
return;
106+
}
107+
108+
if (self.size && self._bytes + chunk.length > self.size) {
109+
self._abort = true;
110+
reject(new Error('content size at ' + self.url + ' over limit: ' + self.size));
111+
return;
112+
}
113+
114+
self._bytes += chunk.length;
115+
self._raw.push(chunk);
116+
});
117+
118+
self.body.on('end', function() {
119+
if (self._abort) {
120+
return;
121+
}
122+
123+
clearTimeout(resTimeout);
124+
resolve(self._convert());
125+
});
126+
});
125127

126128
};
127129

@@ -134,59 +136,88 @@ Body.prototype._decode = function() {
134136
*/
135137
Body.prototype._convert = function(encoding) {
136138

137-
encoding = encoding || 'utf-8';
138-
139-
var charset = 'utf-8';
140-
var res, str;
141-
142-
// header
143-
if (this.headers.has('content-type')) {
144-
res = /charset=([^;]*)/i.exec(this.headers.get('content-type'));
145-
}
146-
147-
// no charset in content type, peek at response body
148-
if (!res && this._raw.length > 0) {
149-
str = this._raw[0].toString().substr(0, 1024);
150-
}
151-
152-
// html5
153-
if (!res && str) {
154-
res = /<meta.+?charset=(['"])(.+?)\1/i.exec(str);
155-
}
156-
157-
// html4
158-
if (!res && str) {
159-
res = /<meta[\s]+?http-equiv=(['"])content-type\1[\s]+?content=(['"])(.+?)\2/i.exec(str);
160-
161-
if (res) {
162-
res = /charset=(.*)/i.exec(res.pop());
163-
}
164-
}
165-
166-
// xml
167-
if (!res && str) {
168-
res = /<\?xml.+?encoding=(['"])(.+?)\1/i.exec(str);
169-
}
170-
171-
// found charset
172-
if (res) {
173-
charset = res.pop();
174-
175-
// prevent decode issues when sites use incorrect encoding
176-
// ref: https://hsivonen.fi/encoding-menu/
177-
if (charset === 'gb2312' || charset === 'gbk') {
178-
charset = 'gb18030';
179-
}
180-
}
181-
182-
// turn raw buffers into utf-8 string
183-
return convert(
184-
Buffer.concat(this._raw)
185-
, encoding
186-
, charset
187-
).toString();
139+
encoding = encoding || 'utf-8';
140+
141+
var charset = 'utf-8';
142+
var res, str;
143+
144+
// header
145+
if (this.headers.has('content-type')) {
146+
res = /charset=([^;]*)/i.exec(this.headers.get('content-type'));
147+
}
148+
149+
// no charset in content type, peek at response body for at most 1024 bytes
150+
if (!res && this._raw.length > 0) {
151+
for (var i = 0; i < this._raw.length; i++) {
152+
str += this._raw[i].toString()
153+
if (str.length > 1024) {
154+
break;
155+
}
156+
}
157+
str = str.substr(0, 1024);
158+
}
159+
160+
// html5
161+
if (!res && str) {
162+
res = /<meta.+?charset=(['"])(.+?)\1/i.exec(str);
163+
}
164+
165+
// html4
166+
if (!res && str) {
167+
res = /<meta[\s]+?http-equiv=(['"])content-type\1[\s]+?content=(['"])(.+?)\2/i.exec(str);
168+
169+
if (res) {
170+
res = /charset=(.*)/i.exec(res.pop());
171+
}
172+
}
173+
174+
// xml
175+
if (!res && str) {
176+
res = /<\?xml.+?encoding=(['"])(.+?)\1/i.exec(str);
177+
}
178+
179+
// found charset
180+
if (res) {
181+
charset = res.pop();
182+
183+
// prevent decode issues when sites use incorrect encoding
184+
// ref: https://hsivonen.fi/encoding-menu/
185+
if (charset === 'gb2312' || charset === 'gbk') {
186+
charset = 'gb18030';
187+
}
188+
}
189+
190+
// turn raw buffers into utf-8 string
191+
return convert(
192+
Buffer.concat(this._raw)
193+
, encoding
194+
, charset
195+
).toString();
188196

189197
};
190198

199+
/**
200+
* Clone body given Res/Req instance
201+
*
202+
* @param Mixed instance Response or Request instance
203+
* @return Mixed
204+
*/
205+
Body.prototype._clone = function(instance) {
206+
var pass;
207+
var body = instance.body;
208+
209+
if (instance.bodyUsed) {
210+
throw new Error('cannot clone body after it is used');
211+
}
212+
213+
if (bodyStream(body)) {
214+
pass = new PassThrough();
215+
body.pipe(pass);
216+
body = pass;
217+
}
218+
219+
return body;
220+
}
221+
191222
// expose Promise
192-
Body.Promise = global.Promise;
223+
Body.Promise = global.Promise;

0 commit comments

Comments
 (0)