5
5
*/
6
6
7
7
var convert = require ( 'encoding' ) . convert ;
8
+ var bodyStream = require ( 'is-stream' ) ;
9
+ var PassThrough = require ( 'stream' ) . PassThrough ;
8
10
9
11
module . exports = Body ;
10
12
@@ -17,14 +19,14 @@ module.exports = Body;
17
19
*/
18
20
function Body ( body , opts ) {
19
21
20
- opts = opts || { } ;
22
+ opts = opts || { } ;
21
23
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 ;
28
30
29
31
}
30
32
@@ -35,9 +37,9 @@ function Body(body, opts) {
35
37
*/
36
38
Body . prototype . json = function ( ) {
37
39
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
+ } ) ;
41
43
42
44
} ;
43
45
@@ -48,7 +50,7 @@ Body.prototype.json = function() {
48
50
*/
49
51
Body . prototype . text = function ( ) {
50
52
51
- return this . _decode ( ) ;
53
+ return this . _decode ( ) ;
52
54
53
55
} ;
54
56
@@ -59,69 +61,69 @@ Body.prototype.text = function() {
59
61
*/
60
62
Body . prototype . _decode = function ( ) {
61
63
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
+ } ) ;
125
127
126
128
} ;
127
129
@@ -134,59 +136,88 @@ Body.prototype._decode = function() {
134
136
*/
135
137
Body . prototype . _convert = function ( encoding ) {
136
138
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 = / c h a r s e t = ( [ ^ ; ] * ) / 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 = / < m e t a .+ ?c h a r s e t = ( [ ' " ] ) ( .+ ?) \1/ i. exec ( str ) ;
155
- }
156
-
157
- // html4
158
- if ( ! res && str ) {
159
- res = / < m e t a [ \s ] + ?h t t p - e q u i v = ( [ ' " ] ) c o n t e n t - t y p e \1[ \s ] + ?c o n t e n t = ( [ ' " ] ) ( .+ ?) \2/ i. exec ( str ) ;
160
-
161
- if ( res ) {
162
- res = / c h a r s e t = ( .* ) / i. exec ( res . pop ( ) ) ;
163
- }
164
- }
165
-
166
- // xml
167
- if ( ! res && str ) {
168
- res = / < \? x m l .+ ?e n c o d i n g = ( [ ' " ] ) ( .+ ?) \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 = / c h a r s e t = ( [ ^ ; ] * ) / 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 = / < m e t a .+ ?c h a r s e t = ( [ ' " ] ) ( .+ ?) \1/ i. exec ( str ) ;
163
+ }
164
+
165
+ // html4
166
+ if ( ! res && str ) {
167
+ res = / < m e t a [ \s ] + ?h t t p - e q u i v = ( [ ' " ] ) c o n t e n t - t y p e \1[ \s ] + ?c o n t e n t = ( [ ' " ] ) ( .+ ?) \2/ i. exec ( str ) ;
168
+
169
+ if ( res ) {
170
+ res = / c h a r s e t = ( .* ) / i. exec ( res . pop ( ) ) ;
171
+ }
172
+ }
173
+
174
+ // xml
175
+ if ( ! res && str ) {
176
+ res = / < \? x m l .+ ?e n c o d i n g = ( [ ' " ] ) ( .+ ?) \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 ( ) ;
188
196
189
197
} ;
190
198
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
+
191
222
// expose Promise
192
- Body . Promise = global . Promise ;
223
+ Body . Promise = global . Promise ;
0 commit comments