1+ /* istanbul ignore file: TODO add more coverage */
2+
3+ // Ported from https://github.com/nodejs/undici/pull/907
4+
15'use strict'
26
7+ const assert = require ( 'assert' )
38const { Readable } = require ( 'stream' )
4- const { InvalidArgumentError } = require ( '../core/errors' )
9+ const { InvalidArgumentError, RequestAbortedError } = require ( '../core/errors' )
510
611let StringDecoder
712let Blob
813
914const kConsume = Symbol ( 'kConsume' )
1015const kReading = Symbol ( 'kReading' )
16+ const kBody = Symbol ( 'kBody' )
1117
12- const kWebStreamType = 1
13- const kTextType = 2
14- const kBlobType = 3
15- const kArrayBufferType = 4
16- const kJSONType = 5
17-
18- class AbortError extends Error {
19- constructor ( message ) {
20- super ( message )
21- Error . captureStackTrace ( this , AbortError )
22- this . name = 'AbortError'
23- this . message = 'aborted'
24- this . code = 'UND_ERR_ABORTED'
25- }
26- }
18+ const kTextType = 1
19+ const kBlobType = 2
20+ const kArrayBufferType = 3
21+ const kJSONType = 4
2722
2823module . exports = class BodyReadable extends Readable {
2924 constructor ( opts ) {
@@ -32,12 +27,24 @@ module.exports = class BodyReadable extends Readable {
3227 this . _readableState . dataEmitted = false
3328
3429 this [ kConsume ] = null
30+ this [ kBody ] = null
3531 this [ kReading ] = false // Is stream being consumed through Readable API?
3632 }
3733
34+ destroy ( err ) {
35+ if ( this . destroyed ) {
36+ // Node < 16
37+ return this
38+ }
39+ return super . destroy ( err )
40+ }
41+
3842 emit ( ev , ...args ) {
3943 if ( ev === 'data' ) {
4044 this . _readableState . dataEmitted = true
45+ } else if ( ev === 'error' ) {
46+ // Node < 16
47+ this . _readableState . errorEmitted = true
4148 }
4249 return super . emit ( ev , ...args )
4350 }
@@ -49,6 +56,13 @@ module.exports = class BodyReadable extends Readable {
4956 return super . on ( ev , ...args )
5057 }
5158
59+ addListener ( ev , ...args ) {
60+ if ( ev === 'data' || ev === 'readable' ) {
61+ this [ kReading ] = true
62+ }
63+ return super . addListener ( ev , ...args )
64+ }
65+
5266 push ( chunk , encoding ) {
5367 if ( this [ kConsume ] && chunk !== null && ! this [ kReading ] ) {
5468 // Fast path.
@@ -90,12 +104,16 @@ module.exports = class BodyReadable extends Readable {
90104 return isDisturbed ( this )
91105 }
92106
107+ // https://fetch.spec.whatwg.org/#dom-body-body
93108 get body ( ) {
94- if ( this [ kConsume ] && this [ kConsume ] . type === kWebStreamType ) {
95- return this [ kConsume ] . stream
109+ if ( ! this [ kBody ] ) {
110+ if ( isUnusable ( this ) ) {
111+ throw new TypeError ( 'unusable' )
112+ }
113+ this [ kBody ] = Readable . toWeb ( this )
96114 }
97115
98- return consume ( this , kWebStreamType )
116+ return this [ kBody ]
99117 }
100118}
101119
@@ -123,32 +141,17 @@ function isUnusable (self) {
123141 return isDisturbed ( self ) || isLocked ( self )
124142}
125143
126- async function consume ( parent , type ) {
127- if ( isUnusable ( parent ) ) {
128- // eslint-disable-next-line no-restricted-syntax
144+ async function consume ( stream , type ) {
145+ if ( isUnusable ( stream ) ) {
129146 throw new TypeError ( 'unusable' )
130147 }
131148
132- if ( parent [ kConsume ] ) {
133- // TODO: Should multiple consume in same tick be possible?
134- // eslint-disable-next-line no-restricted-syntax
135- throw new TypeError ( 'unusable' )
136- }
137-
138- if ( type === kWebStreamType ) {
139- const consume = parent [ kConsume ] = {
140- type,
141- // TODO: Optimized implementation for web streams.
142- stream : Readable . toWeb ( parent )
143- }
144-
145- return consume . stream
146- }
149+ assert ( ! stream [ kConsume ] )
147150
148151 return new Promise ( ( resolve , reject ) => {
149- parent [ kConsume ] = {
152+ stream [ kConsume ] = {
150153 type,
151- parent ,
154+ stream ,
152155 resolve,
153156 reject,
154157 length : 0 ,
@@ -159,17 +162,18 @@ async function consume (parent, type) {
159162 ended : false
160163 }
161164
162- parent
165+ stream
163166 . once ( 'error' , function ( err ) {
164167 consumeFinish ( this [ kConsume ] , err )
165168 } )
166169 . once ( 'close' , function ( ) {
167170 if ( this [ kConsume ] . body !== null ) {
168- consumeFinish ( this [ kConsume ] , new AbortError ( ) )
171+ // TODO: Use Node error?
172+ consumeFinish ( this [ kConsume ] , new RequestAbortedError ( ) )
169173 }
170174 } )
171175
172- process . nextTick ( consumeStart , parent [ kConsume ] )
176+ process . nextTick ( consumeStart , stream [ kConsume ] )
173177 } )
174178}
175179
@@ -178,7 +182,7 @@ function consumeStart (consume) {
178182 return
179183 }
180184
181- const { _readableState : state } = consume . parent
185+ const { _readableState : state } = consume . stream
182186
183187 for ( const chunk of state . buffer ) {
184188 consumePush ( consume , chunk )
@@ -187,20 +191,18 @@ function consumeStart (consume) {
187191 if ( state . endEmitted ) {
188192 consumeEnd ( this [ kConsume ] )
189193 } else {
190- consume . parent . once ( 'end' , function ( ) {
194+ consume . stream . once ( 'end' , function ( ) {
191195 consumeEnd ( this [ kConsume ] )
192196 } )
193197 }
194198
195- if ( consume . parent . isPaused ( ) ) {
196- consume . parent . resume ( )
197- }
199+ consume . stream . resume ( )
198200
199- while ( consume . parent . read ( ) != null ) ;
201+ while ( consume . stream . read ( ) != null ) ;
200202}
201203
202204function consumeEnd ( consume ) {
203- const { type, body, resolve, decoder, parent , length } = consume
205+ const { type, body, resolve, decoder, stream , length } = consume
204206
205207 try {
206208 if ( type === kTextType ) {
@@ -226,7 +228,7 @@ function consumeEnd (consume) {
226228
227229 consumeFinish ( consume )
228230 } catch ( err ) {
229- parent . destroy ( err )
231+ stream . destroy ( err )
230232 }
231233}
232234
@@ -241,7 +243,7 @@ function consumePush (consume, chunk, encoding) {
241243
242244 if ( chunk === null ) {
243245 consume . ended = true
244- consume . parent . read ( )
246+ consume . stream . read ( )
245247 return false
246248 }
247249
@@ -253,7 +255,7 @@ function consumePush (consume, chunk, encoding) {
253255 consumePushBuffer ( consume , chunk , encoding )
254256 }
255257
256- if ( ! consume . parent [ kReading ] && ! consume . reading ) {
258+ if ( ! consume . stream [ kReading ] && ! consume . reading ) {
257259 consume . reading = true
258260 process . nextTick ( consumeReadMore , consume )
259261 }
@@ -283,6 +285,7 @@ function consumePushString (consume, chunk, encoding) {
283285 } else {
284286 // TODO: What if objectMode? Should we just fail consume
285287 // or throw?
288+ // TODO: Use Node error?
286289 throw new InvalidArgumentError ( 'chunk' )
287290 }
288291
@@ -300,6 +303,7 @@ function consumePushBuffer (consume, chunk, encoding) {
300303 } else if ( ! ArrayBuffer . isView ( chunk ) ) {
301304 // TODO: What if objectMode? Should we just fail consume
302305 // or throw?
306+ // TODO: Use Node error?
303307 throw new InvalidArgumentError ( 'chunk' )
304308 }
305309
@@ -308,21 +312,22 @@ function consumePushBuffer (consume, chunk, encoding) {
308312}
309313
310314function consumeReadMore ( consume ) {
311- if ( consume . parent [ kReading ] ) {
312- consume . reading = false
313- return
314- }
315-
316315 consume . pushed = true
317- while ( consume . pushed ) {
316+
317+ while ( consume . pushed && ! consume . stream [ kReading ] ) {
318318 consume . pushed = false
319- consume . parent . _read ( consume . parent )
319+ consume . stream . _read ( consume . stream )
320320 }
321321
322+ consume . pushed = false
322323 consume . reading = false
323324}
324325
325326function consumeFinish ( consume , err ) {
327+ if ( consume . body === null ) {
328+ return
329+ }
330+
326331 if ( err ) {
327332 consume . reject ( err )
328333 } else {
0 commit comments