2
2
3
3
const Busboy = require ( 'busboy' )
4
4
const util = require ( '../core/util' )
5
- const { ReadableStreamFrom, isBlobLike, isReadableStreamLike, readableStreamClose } = require ( './util' )
5
+ const {
6
+ ReadableStreamFrom,
7
+ isBlobLike,
8
+ isReadableStreamLike,
9
+ readableStreamClose,
10
+ createDeferredPromise,
11
+ fullyReadBody
12
+ } = require ( './util' )
6
13
const { FormData } = require ( './formdata' )
7
14
const { kState } = require ( './symbols' )
8
15
const { webidl } = require ( './webidl' )
@@ -13,7 +20,6 @@ const assert = require('assert')
13
20
const { isErrored } = require ( '../core/util' )
14
21
const { isUint8Array, isArrayBuffer } = require ( 'util/types' )
15
22
const { File : UndiciFile } = require ( './file' )
16
- const { StringDecoder } = require ( 'string_decoder' )
17
23
const { parseMIMEType, serializeAMimeType } = require ( './dataURL' )
18
24
19
25
let ReadableStream = globalThis . ReadableStream
@@ -313,26 +319,45 @@ function bodyMixinMethods (instance) {
313
319
const methods = {
314
320
blob ( ) {
315
321
// The blob() method steps are to return the result of
316
- // running consume body with this and Blob.
317
- return specConsumeBody ( this , 'Blob' , instance )
322
+ // running consume body with this and the following step
323
+ // given a byte sequence bytes: return a Blob whose
324
+ // contents are bytes and whose type attribute is this’s
325
+ // MIME type.
326
+ return specConsumeBody ( this , ( bytes ) => {
327
+ let mimeType = bodyMimeType ( this )
328
+
329
+ if ( mimeType === 'failure' ) {
330
+ mimeType = ''
331
+ } else if ( mimeType ) {
332
+ mimeType = serializeAMimeType ( mimeType )
333
+ }
334
+
335
+ // Return a Blob whose contents are bytes and type attribute
336
+ // is mimeType.
337
+ return new Blob ( [ bytes ] , { type : mimeType } )
338
+ } , instance )
318
339
} ,
319
340
320
341
arrayBuffer ( ) {
321
- // The arrayBuffer() method steps are to return the
322
- // result of running consume body with this and ArrayBuffer.
323
- return specConsumeBody ( this , 'ArrayBuffer' , instance )
342
+ // The arrayBuffer() method steps are to return the result
343
+ // of running consume body with this and the following step
344
+ // given a byte sequence bytes: return a new ArrayBuffer
345
+ // whose contents are bytes.
346
+ return specConsumeBody ( this , ( bytes ) => {
347
+ return new Uint8Array ( bytes ) . buffer
348
+ } , instance )
324
349
} ,
325
350
326
351
text ( ) {
327
- // The text() method steps are to return the result of
328
- // running consume body with this and text .
329
- return specConsumeBody ( this , 'text' , instance )
352
+ // The text() method steps are to return the result of running
353
+ // consume body with this and UTF-8 decode .
354
+ return specConsumeBody ( this , utf8DecodeBytes , instance )
330
355
} ,
331
356
332
357
json ( ) {
333
- // The json() method steps are to return the result of
334
- // running consume body with this and JSON.
335
- return specConsumeBody ( this , 'JSON' , instance )
358
+ // The json() method steps are to return the result of running
359
+ // consume body with this and parse JSON from bytes .
360
+ return specConsumeBody ( this , parseJSONFromBytes , instance )
336
361
} ,
337
362
338
363
async formData ( ) {
@@ -455,8 +480,13 @@ function mixinBody (prototype) {
455
480
Object . assign ( prototype . prototype , bodyMixinMethods ( prototype ) )
456
481
}
457
482
458
- // https://fetch.spec.whatwg.org/#concept-body-consume-body
459
- async function specConsumeBody ( object , type , instance ) {
483
+ /**
484
+ * @see https://fetch.spec.whatwg.org/#concept-body-consume-body
485
+ * @param {Response|Request } object
486
+ * @param {(value: unknown) => unknown } convertBytesToJSValue
487
+ * @param {Response|Request } instance
488
+ */
489
+ async function specConsumeBody ( object , convertBytesToJSValue , instance ) {
460
490
webidl . brandCheck ( object , instance )
461
491
462
492
throwIfAborted ( object [ kState ] )
@@ -467,71 +497,37 @@ async function specConsumeBody (object, type, instance) {
467
497
throw new TypeError ( 'Body is unusable' )
468
498
}
469
499
470
- // 2. Let promise be a promise resolved with an empty byte
471
- // sequence.
472
- let promise
473
-
474
- // 3. If object’s body is non-null, then set promise to the
475
- // result of fully reading body as promise given object’s
476
- // body.
477
- if ( object [ kState ] . body != null ) {
478
- promise = await fullyReadBodyAsPromise ( object [ kState ] . body )
479
- } else {
480
- // step #2
481
- promise = { size : 0 , bytes : [ new Uint8Array ( ) ] }
500
+ // 2. Let promise be a new promise.
501
+ const promise = createDeferredPromise ( )
502
+
503
+ // 3. Let errorSteps given error be to reject promise with error.
504
+ const errorSteps = ( error ) => promise . reject ( error )
505
+
506
+ // 4. Let successSteps given a byte sequence data be to resolve
507
+ // promise with the result of running convertBytesToJSValue
508
+ // with data. If that threw an exception, then run errorSteps
509
+ // with that exception.
510
+ const successSteps = ( data ) => {
511
+ try {
512
+ promise . resolve ( convertBytesToJSValue ( data ) )
513
+ } catch ( e ) {
514
+ errorSteps ( e )
515
+ }
482
516
}
483
517
484
- // 4. Let steps be to return the result of package data with
485
- // the first argument given, type, and object’s MIME type.
486
- const mimeType = type === 'Blob' || type === 'FormData'
487
- ? bodyMimeType ( object )
488
- : undefined
489
-
490
- // 5. Return the result of upon fulfillment of promise given
491
- // steps.
492
- return packageData ( promise , type , mimeType )
493
- }
494
-
495
- /**
496
- * @see https://fetch.spec.whatwg.org/#concept-body-package-data
497
- * @param {{ size: number, bytes: Uint8Array[] } } bytes
498
- * @param {string } type
499
- * @param {ReturnType<typeof parseMIMEType>|undefined } mimeType
500
- */
501
- function packageData ( { bytes, size } , type , mimeType ) {
502
- switch ( type ) {
503
- case 'ArrayBuffer' : {
504
- // Return a new ArrayBuffer whose contents are bytes.
505
- const uint8 = new Uint8Array ( size )
506
- let offset = 0
507
-
508
- for ( const chunk of bytes ) {
509
- uint8 . set ( chunk , offset )
510
- offset += chunk . byteLength
511
- }
518
+ // 5. If object’s body is null, then run successSteps with an
519
+ // empty byte sequence.
520
+ if ( object [ kState ] . body == null ) {
521
+ successSteps ( new Uint8Array ( ) )
522
+ return promise . promise
523
+ }
512
524
513
- return uint8 . buffer
514
- }
515
- case 'Blob' : {
516
- if ( mimeType === 'failure' ) {
517
- mimeType = ''
518
- } else if ( mimeType ) {
519
- mimeType = serializeAMimeType ( mimeType )
520
- }
525
+ // 6. Otherwise, fully read object’s body given successSteps,
526
+ // errorSteps, and object’s relevant global object.
527
+ fullyReadBody ( object [ kState ] . body , successSteps , errorSteps )
521
528
522
- // Return a Blob whose contents are bytes and type attribute
523
- // is mimeType.
524
- return new Blob ( bytes , { type : mimeType } )
525
- }
526
- case 'JSON' : {
527
- // Return the result of running parse JSON from bytes on bytes.
528
- return JSON . parse ( utf8DecodeBytes ( bytes ) )
529
- }
530
- case 'text' : {
531
- // 1. Return the result of running UTF-8 decode on bytes.
532
- return utf8DecodeBytes ( bytes )
533
- }
534
- }
529
+ // 7. Return promise.
530
+ return promise . promise
535
531
}
536
532
537
533
// https://fetch.spec.whatwg.org/#body-unusable
@@ -542,73 +538,40 @@ function bodyUnusable (body) {
542
538
return body != null && ( body . stream . locked || util . isDisturbed ( body . stream ) )
543
539
}
544
540
545
- // https://fetch.spec.whatwg.org/#fully-reading-body-as-promise
546
- async function fullyReadBodyAsPromise ( body ) {
547
- // 1. Let reader be the result of getting a reader for body’s
548
- // stream. If that threw an exception, then return a promise
549
- // rejected with that exception.
550
- const reader = body . stream . getReader ( )
551
-
552
- // 2. Return the result of reading all bytes from reader.
553
- /** @type {Uint8Array[] } */
554
- const bytes = [ ]
555
- let size = 0
556
-
557
- while ( true ) {
558
- const { done, value } = await reader . read ( )
559
-
560
- if ( done ) {
561
- break
562
- }
563
-
564
- // https://streams.spec.whatwg.org/#read-loop
565
- // If chunk is not a Uint8Array object, reject promise with
566
- // a TypeError and abort these steps.
567
- if ( ! isUint8Array ( value ) ) {
568
- throw new TypeError ( 'Value is not a Uint8Array.' )
569
- }
570
-
571
- bytes . push ( value )
572
- size += value . byteLength
573
- }
574
-
575
- return { size, bytes }
576
- }
577
-
578
541
/**
579
542
* @see https://encoding.spec.whatwg.org/#utf-8-decode
580
- * @param {Uint8Array[] } ioQueue
543
+ * @param {Buffer } buffer
581
544
*/
582
- function utf8DecodeBytes ( ioQueue ) {
583
- if ( ioQueue . length === 0 ) {
545
+ function utf8DecodeBytes ( buffer ) {
546
+ if ( buffer . length === 0 ) {
584
547
return ''
585
548
}
586
549
587
- // 1. Let buffer be the result of peeking three bytes
588
- // from ioQueue, converted to a byte sequence.
589
- const buffer = ioQueue [ 0 ]
550
+ // 1. Let buffer be the result of peeking three bytes from
551
+ // ioQueue, converted to a byte sequence.
590
552
591
553
// 2. If buffer is 0xEF 0xBB 0xBF, then read three
592
554
// bytes from ioQueue. (Do nothing with those bytes.)
593
555
if ( buffer [ 0 ] === 0xEF && buffer [ 1 ] === 0xBB && buffer [ 2 ] === 0xBF ) {
594
- ioQueue [ 0 ] = ioQueue [ 0 ] . subarray ( 3 )
556
+ buffer = buffer . subarray ( 3 )
595
557
}
596
558
597
559
// 3. Process a queue with an instance of UTF-8’s
598
560
// decoder, ioQueue, output, and "replacement".
599
- const decoder = new StringDecoder ( 'utf-8' )
600
- let output = ''
601
-
602
- for ( const chunk of ioQueue ) {
603
- output += decoder . write ( chunk )
604
- }
605
-
606
- output += decoder . end ( )
561
+ const output = new TextDecoder ( ) . decode ( buffer )
607
562
608
563
// 4. Return output.
609
564
return output
610
565
}
611
566
567
+ /**
568
+ * @see https://infra.spec.whatwg.org/#parse-json-bytes-to-a-javascript-value
569
+ * @param {Uint8Array } bytes
570
+ */
571
+ function parseJSONFromBytes ( bytes ) {
572
+ return JSON . parse ( utf8DecodeBytes ( bytes ) )
573
+ }
574
+
612
575
/**
613
576
* @see https://fetch.spec.whatwg.org/#concept-body-mime-type
614
577
* @param {import('./response').Response|import('./request').Request } object
0 commit comments