undici 7.11.0 → 7.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -11
- package/docs/docs/api/DiagnosticsChannel.md +7 -4
- package/docs/docs/api/Dispatcher.md +2 -2
- package/docs/docs/api/ProxyAgent.md +1 -1
- package/docs/docs/api/SnapshotAgent.md +616 -0
- package/docs/docs/api/WebSocket.md +27 -0
- package/index.js +5 -1
- package/lib/api/readable.js +49 -29
- package/lib/core/request.js +6 -1
- package/lib/core/tree.js +1 -1
- package/lib/core/util.js +0 -1
- package/lib/dispatcher/client-h1.js +8 -17
- package/lib/dispatcher/proxy-agent.js +67 -71
- package/lib/handler/cache-handler.js +4 -1
- package/lib/handler/redirect-handler.js +12 -2
- package/lib/interceptor/cache.js +2 -2
- package/lib/interceptor/dump.js +2 -1
- package/lib/interceptor/redirect.js +1 -1
- package/lib/mock/mock-agent.js +10 -4
- package/lib/mock/snapshot-agent.js +333 -0
- package/lib/mock/snapshot-recorder.js +517 -0
- package/lib/util/cache.js +1 -1
- package/lib/util/promise.js +28 -0
- package/lib/web/cache/cache.js +10 -8
- package/lib/web/fetch/body.js +35 -24
- package/lib/web/fetch/formdata-parser.js +0 -3
- package/lib/web/fetch/formdata.js +0 -4
- package/lib/web/fetch/index.js +221 -225
- package/lib/web/fetch/request.js +15 -7
- package/lib/web/fetch/response.js +5 -3
- package/lib/web/fetch/util.js +21 -23
- package/lib/web/webidl/index.js +1 -1
- package/lib/web/websocket/connection.js +0 -9
- package/lib/web/websocket/receiver.js +2 -12
- package/lib/web/websocket/stream/websocketstream.js +7 -4
- package/lib/web/websocket/websocket.js +57 -1
- package/package.json +2 -2
- package/types/agent.d.ts +0 -4
- package/types/client.d.ts +0 -2
- package/types/dispatcher.d.ts +0 -6
- package/types/h2c-client.d.ts +0 -2
- package/types/index.d.ts +3 -1
- package/types/mock-interceptor.d.ts +0 -1
- package/types/snapshot-agent.d.ts +107 -0
- package/types/webidl.d.ts +10 -0
- package/types/websocket.d.ts +2 -0
- package/lib/web/fetch/dispatcher-weakref.js +0 -5
package/lib/web/fetch/index.js
CHANGED
|
@@ -30,7 +30,6 @@ const {
|
|
|
30
30
|
crossOriginResourcePolicyCheck,
|
|
31
31
|
determineRequestsReferrer,
|
|
32
32
|
coarsenedSharedCurrentTime,
|
|
33
|
-
createDeferredPromise,
|
|
34
33
|
sameOrigin,
|
|
35
34
|
isCancelled,
|
|
36
35
|
isAborted,
|
|
@@ -63,6 +62,7 @@ const { dataURLProcessor, serializeAMimeType, minimizeSupportedMimeType } = requ
|
|
|
63
62
|
const { getGlobalDispatcher } = require('../../global')
|
|
64
63
|
const { webidl } = require('../webidl')
|
|
65
64
|
const { STATUS_CODES } = require('node:http')
|
|
65
|
+
const { createDeferredPromise } = require('../../util/promise')
|
|
66
66
|
const GET_OR_HEAD = ['GET', 'HEAD']
|
|
67
67
|
|
|
68
68
|
const defaultUserAgent = typeof __UNDICI_IS_NODE__ !== 'undefined' || typeof esbuildDetection !== 'undefined'
|
|
@@ -507,257 +507,258 @@ function fetching ({
|
|
|
507
507
|
}
|
|
508
508
|
|
|
509
509
|
// 16. Run main fetch given fetchParams.
|
|
510
|
-
mainFetch(fetchParams)
|
|
511
|
-
.catch(err => {
|
|
512
|
-
fetchParams.controller.terminate(err)
|
|
513
|
-
})
|
|
510
|
+
mainFetch(fetchParams, false)
|
|
514
511
|
|
|
515
512
|
// 17. Return fetchParam's controller
|
|
516
513
|
return fetchParams.controller
|
|
517
514
|
}
|
|
518
515
|
|
|
519
516
|
// https://fetch.spec.whatwg.org/#concept-main-fetch
|
|
520
|
-
async function mainFetch (fetchParams, recursive
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
// 2. Let response be null.
|
|
525
|
-
let response = null
|
|
517
|
+
async function mainFetch (fetchParams, recursive) {
|
|
518
|
+
try {
|
|
519
|
+
// 1. Let request be fetchParams’s request.
|
|
520
|
+
const request = fetchParams.request
|
|
526
521
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
if (request.localURLsOnly && !urlIsLocal(requestCurrentURL(request))) {
|
|
530
|
-
response = makeNetworkError('local URLs only')
|
|
531
|
-
}
|
|
522
|
+
// 2. Let response be null.
|
|
523
|
+
let response = null
|
|
532
524
|
|
|
533
|
-
|
|
534
|
-
|
|
525
|
+
// 3. If request’s local-URLs-only flag is set and request’s current URL is
|
|
526
|
+
// not local, then set response to a network error.
|
|
527
|
+
if (request.localURLsOnly && !urlIsLocal(requestCurrentURL(request))) {
|
|
528
|
+
response = makeNetworkError('local URLs only')
|
|
529
|
+
}
|
|
535
530
|
|
|
536
|
-
|
|
537
|
-
|
|
531
|
+
// 4. Run report Content Security Policy violations for request.
|
|
532
|
+
// TODO
|
|
538
533
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
// Security Policy returns blocked, then set response to a network error.
|
|
542
|
-
if (requestBadPort(request) === 'blocked') {
|
|
543
|
-
response = makeNetworkError('bad port')
|
|
544
|
-
}
|
|
545
|
-
// TODO: should fetching request be blocked as mixed content?
|
|
546
|
-
// TODO: should request be blocked by Content Security Policy?
|
|
534
|
+
// 5. Upgrade request to a potentially trustworthy URL, if appropriate.
|
|
535
|
+
tryUpgradeRequestToAPotentiallyTrustworthyURL(request)
|
|
547
536
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
request
|
|
552
|
-
|
|
537
|
+
// 6. If should request be blocked due to a bad port, should fetching request
|
|
538
|
+
// be blocked as mixed content, or should request be blocked by Content
|
|
539
|
+
// Security Policy returns blocked, then set response to a network error.
|
|
540
|
+
if (requestBadPort(request) === 'blocked') {
|
|
541
|
+
response = makeNetworkError('bad port')
|
|
542
|
+
}
|
|
543
|
+
// TODO: should fetching request be blocked as mixed content?
|
|
544
|
+
// TODO: should request be blocked by Content Security Policy?
|
|
553
545
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
546
|
+
// 7. If request’s referrer policy is the empty string, then set request’s
|
|
547
|
+
// referrer policy to request’s policy container’s referrer policy.
|
|
548
|
+
if (request.referrerPolicy === '') {
|
|
549
|
+
request.referrerPolicy = request.policyContainer.referrerPolicy
|
|
550
|
+
}
|
|
559
551
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
// Matching results in either a superdomain match with an asserted
|
|
566
|
-
// includeSubDomains directive or a congruent match (with or without an
|
|
567
|
-
// asserted includeSubDomains directive). [HSTS]
|
|
568
|
-
// TODO
|
|
552
|
+
// 8. If request’s referrer is not "no-referrer", then set request’s
|
|
553
|
+
// referrer to the result of invoking determine request’s referrer.
|
|
554
|
+
if (request.referrer !== 'no-referrer') {
|
|
555
|
+
request.referrer = determineRequestsReferrer(request)
|
|
556
|
+
}
|
|
569
557
|
|
|
570
|
-
|
|
571
|
-
|
|
558
|
+
// 9. Set request’s current URL’s scheme to "https" if all of the following
|
|
559
|
+
// conditions are true:
|
|
560
|
+
// - request’s current URL’s scheme is "http"
|
|
561
|
+
// - request’s current URL’s host is a domain
|
|
562
|
+
// - Matching request’s current URL’s host per Known HSTS Host Domain Name
|
|
563
|
+
// Matching results in either a superdomain match with an asserted
|
|
564
|
+
// includeSubDomains directive or a congruent match (with or without an
|
|
565
|
+
// asserted includeSubDomains directive). [HSTS]
|
|
566
|
+
// TODO
|
|
572
567
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
if (response === null) {
|
|
576
|
-
const currentURL = requestCurrentURL(request)
|
|
577
|
-
if (
|
|
578
|
-
// - request’s current URL’s origin is same origin with request’s origin,
|
|
579
|
-
// and request’s response tainting is "basic"
|
|
580
|
-
(sameOrigin(currentURL, request.url) && request.responseTainting === 'basic') ||
|
|
581
|
-
// request’s current URL’s scheme is "data"
|
|
582
|
-
(currentURL.protocol === 'data:') ||
|
|
583
|
-
// - request’s mode is "navigate" or "websocket"
|
|
584
|
-
(request.mode === 'navigate' || request.mode === 'websocket')
|
|
585
|
-
) {
|
|
586
|
-
// 1. Set request’s response tainting to "basic".
|
|
587
|
-
request.responseTainting = 'basic'
|
|
588
|
-
|
|
589
|
-
// 2. Return the result of running scheme fetch given fetchParams.
|
|
590
|
-
response = await schemeFetch(fetchParams)
|
|
591
|
-
|
|
592
|
-
// request’s mode is "same-origin"
|
|
593
|
-
} else if (request.mode === 'same-origin') {
|
|
594
|
-
// 1. Return a network error.
|
|
595
|
-
response = makeNetworkError('request mode cannot be "same-origin"')
|
|
596
|
-
|
|
597
|
-
// request’s mode is "no-cors"
|
|
598
|
-
} else if (request.mode === 'no-cors') {
|
|
599
|
-
// 1. If request’s redirect mode is not "follow", then return a network
|
|
600
|
-
// error.
|
|
601
|
-
if (request.redirect !== 'follow') {
|
|
602
|
-
response = makeNetworkError(
|
|
603
|
-
'redirect mode cannot be "follow" for "no-cors" request'
|
|
604
|
-
)
|
|
605
|
-
} else {
|
|
606
|
-
// 2. Set request’s response tainting to "opaque".
|
|
607
|
-
request.responseTainting = 'opaque'
|
|
568
|
+
// 10. If recursive is false, then run the remaining steps in parallel.
|
|
569
|
+
// TODO
|
|
608
570
|
|
|
609
|
-
|
|
571
|
+
// 11. If response is null, then set response to the result of running
|
|
572
|
+
// the steps corresponding to the first matching statement:
|
|
573
|
+
if (response === null) {
|
|
574
|
+
const currentURL = requestCurrentURL(request)
|
|
575
|
+
if (
|
|
576
|
+
// - request’s current URL’s origin is same origin with request’s origin,
|
|
577
|
+
// and request’s response tainting is "basic"
|
|
578
|
+
(sameOrigin(currentURL, request.url) && request.responseTainting === 'basic') ||
|
|
579
|
+
// request’s current URL’s scheme is "data"
|
|
580
|
+
(currentURL.protocol === 'data:') ||
|
|
581
|
+
// - request’s mode is "navigate" or "websocket"
|
|
582
|
+
(request.mode === 'navigate' || request.mode === 'websocket')
|
|
583
|
+
) {
|
|
584
|
+
// 1. Set request’s response tainting to "basic".
|
|
585
|
+
request.responseTainting = 'basic'
|
|
586
|
+
|
|
587
|
+
// 2. Return the result of running scheme fetch given fetchParams.
|
|
610
588
|
response = await schemeFetch(fetchParams)
|
|
611
|
-
}
|
|
612
|
-
// request’s current URL’s scheme is not an HTTP(S) scheme
|
|
613
|
-
} else if (!urlIsHttpHttpsScheme(requestCurrentURL(request))) {
|
|
614
|
-
// Return a network error.
|
|
615
|
-
response = makeNetworkError('URL scheme must be a HTTP(S) scheme')
|
|
616
|
-
|
|
617
|
-
// - request’s use-CORS-preflight flag is set
|
|
618
|
-
// - request’s unsafe-request flag is set and either request’s method is
|
|
619
|
-
// not a CORS-safelisted method or CORS-unsafe request-header names with
|
|
620
|
-
// request’s header list is not empty
|
|
621
|
-
// 1. Set request’s response tainting to "cors".
|
|
622
|
-
// 2. Let corsWithPreflightResponse be the result of running HTTP fetch
|
|
623
|
-
// given fetchParams and true.
|
|
624
|
-
// 3. If corsWithPreflightResponse is a network error, then clear cache
|
|
625
|
-
// entries using request.
|
|
626
|
-
// 4. Return corsWithPreflightResponse.
|
|
627
|
-
// TODO
|
|
628
|
-
|
|
629
|
-
// Otherwise
|
|
630
|
-
} else {
|
|
631
|
-
// 1. Set request’s response tainting to "cors".
|
|
632
|
-
request.responseTainting = 'cors'
|
|
633
589
|
|
|
634
|
-
//
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
590
|
+
// request’s mode is "same-origin"
|
|
591
|
+
} else if (request.mode === 'same-origin') {
|
|
592
|
+
// 1. Return a network error.
|
|
593
|
+
response = makeNetworkError('request mode cannot be "same-origin"')
|
|
594
|
+
|
|
595
|
+
// request’s mode is "no-cors"
|
|
596
|
+
} else if (request.mode === 'no-cors') {
|
|
597
|
+
// 1. If request’s redirect mode is not "follow", then return a network
|
|
598
|
+
// error.
|
|
599
|
+
if (request.redirect !== 'follow') {
|
|
600
|
+
response = makeNetworkError(
|
|
601
|
+
'redirect mode cannot be "follow" for "no-cors" request'
|
|
602
|
+
)
|
|
603
|
+
} else {
|
|
604
|
+
// 2. Set request’s response tainting to "opaque".
|
|
605
|
+
request.responseTainting = 'opaque'
|
|
638
606
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
607
|
+
// 3. Return the result of running scheme fetch given fetchParams.
|
|
608
|
+
response = await schemeFetch(fetchParams)
|
|
609
|
+
}
|
|
610
|
+
// request’s current URL’s scheme is not an HTTP(S) scheme
|
|
611
|
+
} else if (!urlIsHttpHttpsScheme(requestCurrentURL(request))) {
|
|
612
|
+
// Return a network error.
|
|
613
|
+
response = makeNetworkError('URL scheme must be a HTTP(S) scheme')
|
|
614
|
+
|
|
615
|
+
// - request’s use-CORS-preflight flag is set
|
|
616
|
+
// - request’s unsafe-request flag is set and either request’s method is
|
|
617
|
+
// not a CORS-safelisted method or CORS-unsafe request-header names with
|
|
618
|
+
// request’s header list is not empty
|
|
619
|
+
// 1. Set request’s response tainting to "cors".
|
|
620
|
+
// 2. Let corsWithPreflightResponse be the result of running HTTP fetch
|
|
621
|
+
// given fetchParams and true.
|
|
622
|
+
// 3. If corsWithPreflightResponse is a network error, then clear cache
|
|
623
|
+
// entries using request.
|
|
624
|
+
// 4. Return corsWithPreflightResponse.
|
|
625
|
+
// TODO
|
|
626
|
+
|
|
627
|
+
// Otherwise
|
|
628
|
+
} else {
|
|
629
|
+
// 1. Set request’s response tainting to "cors".
|
|
630
|
+
request.responseTainting = 'cors'
|
|
631
|
+
|
|
632
|
+
// 2. Return the result of running HTTP fetch given fetchParams.
|
|
633
|
+
response = await httpFetch(fetchParams)
|
|
634
|
+
}
|
|
659
635
|
}
|
|
660
636
|
|
|
661
|
-
//
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
response = filterResponse(response, 'basic')
|
|
665
|
-
} else if (request.responseTainting === 'cors') {
|
|
666
|
-
response = filterResponse(response, 'cors')
|
|
667
|
-
} else if (request.responseTainting === 'opaque') {
|
|
668
|
-
response = filterResponse(response, 'opaque')
|
|
669
|
-
} else {
|
|
670
|
-
assert(false)
|
|
637
|
+
// 12. If recursive is true, then return response.
|
|
638
|
+
if (recursive) {
|
|
639
|
+
return response
|
|
671
640
|
}
|
|
672
|
-
}
|
|
673
641
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
642
|
+
// 13. If response is not a network error and response is not a filtered
|
|
643
|
+
// response, then:
|
|
644
|
+
if (response.status !== 0 && !response.internalResponse) {
|
|
645
|
+
// If request’s response tainting is "cors", then:
|
|
646
|
+
if (request.responseTainting === 'cors') {
|
|
647
|
+
// 1. Let headerNames be the result of extracting header list values
|
|
648
|
+
// given `Access-Control-Expose-Headers` and response’s header list.
|
|
649
|
+
// TODO
|
|
650
|
+
// 2. If request’s credentials mode is not "include" and headerNames
|
|
651
|
+
// contains `*`, then set response’s CORS-exposed header-name list to
|
|
652
|
+
// all unique header names in response’s header list.
|
|
653
|
+
// TODO
|
|
654
|
+
// 3. Otherwise, if headerNames is not null or failure, then set
|
|
655
|
+
// response’s CORS-exposed header-name list to headerNames.
|
|
656
|
+
// TODO
|
|
657
|
+
}
|
|
678
658
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
659
|
+
// Set response to the following filtered response with response as its
|
|
660
|
+
// internal response, depending on request’s response tainting:
|
|
661
|
+
if (request.responseTainting === 'basic') {
|
|
662
|
+
response = filterResponse(response, 'basic')
|
|
663
|
+
} else if (request.responseTainting === 'cors') {
|
|
664
|
+
response = filterResponse(response, 'cors')
|
|
665
|
+
} else if (request.responseTainting === 'opaque') {
|
|
666
|
+
response = filterResponse(response, 'opaque')
|
|
667
|
+
} else {
|
|
668
|
+
assert(false)
|
|
669
|
+
}
|
|
670
|
+
}
|
|
684
671
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
}
|
|
672
|
+
// 14. Let internalResponse be response, if response is a network error,
|
|
673
|
+
// and response’s internal response otherwise.
|
|
674
|
+
let internalResponse =
|
|
675
|
+
response.status === 0 ? response : response.internalResponse
|
|
690
676
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
// - should internalResponse to request be blocked due to nosniff
|
|
697
|
-
// TODO
|
|
677
|
+
// 15. If internalResponse’s URL list is empty, then set it to a clone of
|
|
678
|
+
// request’s URL list.
|
|
679
|
+
if (internalResponse.urlList.length === 0) {
|
|
680
|
+
internalResponse.urlList.push(...request.urlList)
|
|
681
|
+
}
|
|
698
682
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
response.type === 'opaque' &&
|
|
705
|
-
internalResponse.status === 206 &&
|
|
706
|
-
internalResponse.rangeRequested &&
|
|
707
|
-
!request.headers.contains('range', true)
|
|
708
|
-
) {
|
|
709
|
-
response = internalResponse = makeNetworkError()
|
|
710
|
-
}
|
|
683
|
+
// 16. If request’s timing allow failed flag is unset, then set
|
|
684
|
+
// internalResponse’s timing allow passed flag.
|
|
685
|
+
if (!request.timingAllowFailed) {
|
|
686
|
+
response.timingAllowPassed = true
|
|
687
|
+
}
|
|
711
688
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
request.method === 'CONNECT' ||
|
|
720
|
-
nullBodyStatus.includes(internalResponse.status))
|
|
721
|
-
) {
|
|
722
|
-
internalResponse.body = null
|
|
723
|
-
fetchParams.controller.dump = true
|
|
724
|
-
}
|
|
689
|
+
// 17. If response is not a network error and any of the following returns
|
|
690
|
+
// blocked
|
|
691
|
+
// - should internalResponse to request be blocked as mixed content
|
|
692
|
+
// - should internalResponse to request be blocked by Content Security Policy
|
|
693
|
+
// - should internalResponse to request be blocked due to its MIME type
|
|
694
|
+
// - should internalResponse to request be blocked due to nosniff
|
|
695
|
+
// TODO
|
|
725
696
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
//
|
|
729
|
-
//
|
|
730
|
-
|
|
731
|
-
|
|
697
|
+
// 18. If response’s type is "opaque", internalResponse’s status is 206,
|
|
698
|
+
// internalResponse’s range-requested flag is set, and request’s header
|
|
699
|
+
// list does not contain `Range`, then set response and internalResponse
|
|
700
|
+
// to a network error.
|
|
701
|
+
if (
|
|
702
|
+
response.type === 'opaque' &&
|
|
703
|
+
internalResponse.status === 206 &&
|
|
704
|
+
internalResponse.rangeRequested &&
|
|
705
|
+
!request.headers.contains('range', true)
|
|
706
|
+
) {
|
|
707
|
+
response = internalResponse = makeNetworkError()
|
|
708
|
+
}
|
|
732
709
|
|
|
733
|
-
//
|
|
734
|
-
//
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
710
|
+
// 19. If response is not a network error and either request’s method is
|
|
711
|
+
// `HEAD` or `CONNECT`, or internalResponse’s status is a null body status,
|
|
712
|
+
// set internalResponse’s body to null and disregard any enqueuing toward
|
|
713
|
+
// it (if any).
|
|
714
|
+
if (
|
|
715
|
+
response.status !== 0 &&
|
|
716
|
+
(request.method === 'HEAD' ||
|
|
717
|
+
request.method === 'CONNECT' ||
|
|
718
|
+
nullBodyStatus.includes(internalResponse.status))
|
|
719
|
+
) {
|
|
720
|
+
internalResponse.body = null
|
|
721
|
+
fetchParams.controller.dump = true
|
|
738
722
|
}
|
|
739
723
|
|
|
740
|
-
//
|
|
741
|
-
|
|
742
|
-
// 1.
|
|
743
|
-
//
|
|
744
|
-
|
|
745
|
-
|
|
724
|
+
// 20. If request’s integrity metadata is not the empty string, then:
|
|
725
|
+
if (request.integrity) {
|
|
726
|
+
// 1. Let processBodyError be this step: run fetch finale given fetchParams
|
|
727
|
+
// and a network error.
|
|
728
|
+
const processBodyError = (reason) =>
|
|
729
|
+
fetchFinale(fetchParams, makeNetworkError(reason))
|
|
730
|
+
|
|
731
|
+
// 2. If request’s response tainting is "opaque", or response’s body is null,
|
|
732
|
+
// then run processBodyError and abort these steps.
|
|
733
|
+
if (request.responseTainting === 'opaque' || response.body == null) {
|
|
734
|
+
processBodyError(response.error)
|
|
746
735
|
return
|
|
747
736
|
}
|
|
748
737
|
|
|
749
|
-
//
|
|
750
|
-
|
|
738
|
+
// 3. Let processBody given bytes be these steps:
|
|
739
|
+
const processBody = (bytes) => {
|
|
740
|
+
// 1. If bytes do not match request’s integrity metadata,
|
|
741
|
+
// then run processBodyError and abort these steps. [SRI]
|
|
742
|
+
if (!bytesMatch(bytes, request.integrity)) {
|
|
743
|
+
processBodyError('integrity mismatch')
|
|
744
|
+
return
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// 2. Set response’s body to bytes as a body.
|
|
748
|
+
response.body = safelyExtractBody(bytes)[0]
|
|
751
749
|
|
|
752
|
-
|
|
750
|
+
// 3. Run fetch finale given fetchParams and response.
|
|
751
|
+
fetchFinale(fetchParams, response)
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// 4. Fully read response’s body given processBody and processBodyError.
|
|
755
|
+
fullyReadBody(response.body, processBody, processBodyError)
|
|
756
|
+
} else {
|
|
757
|
+
// 21. Otherwise, run fetch finale given fetchParams and response.
|
|
753
758
|
fetchFinale(fetchParams, response)
|
|
754
759
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
await fullyReadBody(response.body, processBody, processBodyError)
|
|
758
|
-
} else {
|
|
759
|
-
// 21. Otherwise, run fetch finale given fetchParams and response.
|
|
760
|
-
fetchFinale(fetchParams, response)
|
|
760
|
+
} catch (err) {
|
|
761
|
+
fetchParams.controller.terminate(err)
|
|
761
762
|
}
|
|
762
763
|
}
|
|
763
764
|
|
|
@@ -1909,15 +1910,11 @@ async function httpNetworkFetch (
|
|
|
1909
1910
|
// cancelAlgorithm set to cancelAlgorithm.
|
|
1910
1911
|
const stream = new ReadableStream(
|
|
1911
1912
|
{
|
|
1912
|
-
|
|
1913
|
+
start (controller) {
|
|
1913
1914
|
fetchParams.controller.controller = controller
|
|
1914
1915
|
},
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
},
|
|
1918
|
-
async cancel (reason) {
|
|
1919
|
-
await cancelAlgorithm(reason)
|
|
1920
|
-
},
|
|
1916
|
+
pull: pullAlgorithm,
|
|
1917
|
+
cancel: cancelAlgorithm,
|
|
1921
1918
|
type: 'bytes'
|
|
1922
1919
|
}
|
|
1923
1920
|
)
|
|
@@ -2055,7 +2052,7 @@ async function httpNetworkFetch (
|
|
|
2055
2052
|
|
|
2056
2053
|
function dispatch ({ body }) {
|
|
2057
2054
|
const url = requestCurrentURL(request)
|
|
2058
|
-
/** @type {import('
|
|
2055
|
+
/** @type {import('../../..').Agent} */
|
|
2059
2056
|
const agent = fetchParams.controller.dispatcher
|
|
2060
2057
|
|
|
2061
2058
|
return new Promise((resolve, reject) => agent.dispatch(
|
|
@@ -2104,12 +2101,11 @@ async function httpNetworkFetch (
|
|
|
2104
2101
|
|
|
2105
2102
|
onHeaders (status, rawHeaders, resume, statusText) {
|
|
2106
2103
|
if (status < 200) {
|
|
2107
|
-
return
|
|
2104
|
+
return false
|
|
2108
2105
|
}
|
|
2109
2106
|
|
|
2110
2107
|
/** @type {string[]} */
|
|
2111
2108
|
let codings = []
|
|
2112
|
-
let location = ''
|
|
2113
2109
|
|
|
2114
2110
|
const headersList = new HeadersList()
|
|
2115
2111
|
|
|
@@ -2122,7 +2118,7 @@ async function httpNetworkFetch (
|
|
|
2122
2118
|
// "All content-coding values are case-insensitive..."
|
|
2123
2119
|
codings = contentEncoding.toLowerCase().split(',').map((x) => x.trim())
|
|
2124
2120
|
}
|
|
2125
|
-
location = headersList.get('location', true)
|
|
2121
|
+
const location = headersList.get('location', true)
|
|
2126
2122
|
|
|
2127
2123
|
this.body = new Readable({ read: resume })
|
|
2128
2124
|
|
package/lib/web/fetch/request.js
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
const { extractBody, mixinBody, cloneBody, bodyUnusable } = require('./body')
|
|
6
6
|
const { Headers, fill: fillHeaders, HeadersList, setHeadersGuard, getHeadersGuard, setHeadersList, getHeadersList } = require('./headers')
|
|
7
|
-
const { FinalizationRegistry } = require('./dispatcher-weakref')()
|
|
8
7
|
const util = require('../../core/util')
|
|
9
8
|
const nodeUtil = require('node:util')
|
|
10
9
|
const {
|
|
@@ -109,8 +108,8 @@ class Request {
|
|
|
109
108
|
const prefix = 'Request constructor'
|
|
110
109
|
webidl.argumentLengthCheck(arguments, 1, prefix)
|
|
111
110
|
|
|
112
|
-
input = webidl.converters.RequestInfo(input
|
|
113
|
-
init = webidl.converters.RequestInit(init
|
|
111
|
+
input = webidl.converters.RequestInfo(input)
|
|
112
|
+
init = webidl.converters.RequestInit(init)
|
|
114
113
|
|
|
115
114
|
// 1. Let request be null.
|
|
116
115
|
let request = null
|
|
@@ -937,7 +936,7 @@ function cloneRequest (request) {
|
|
|
937
936
|
// 2. If request’s body is non-null, set newRequest’s body to the
|
|
938
937
|
// result of cloning request’s body.
|
|
939
938
|
if (request.body != null) {
|
|
940
|
-
newRequest.body = cloneBody(
|
|
939
|
+
newRequest.body = cloneBody(request.body)
|
|
941
940
|
}
|
|
942
941
|
|
|
943
942
|
// 3. Return newRequest.
|
|
@@ -993,8 +992,13 @@ Object.defineProperties(Request.prototype, {
|
|
|
993
992
|
|
|
994
993
|
webidl.is.Request = webidl.util.MakeTypeAssertion(Request)
|
|
995
994
|
|
|
996
|
-
|
|
997
|
-
|
|
995
|
+
/**
|
|
996
|
+
* @param {*} V
|
|
997
|
+
* @returns {import('../../../types/fetch').Request|string}
|
|
998
|
+
*
|
|
999
|
+
* @see https://fetch.spec.whatwg.org/#requestinfo
|
|
1000
|
+
*/
|
|
1001
|
+
webidl.converters.RequestInfo = function (V) {
|
|
998
1002
|
if (typeof V === 'string') {
|
|
999
1003
|
return webidl.converters.USVString(V)
|
|
1000
1004
|
}
|
|
@@ -1006,7 +1010,11 @@ webidl.converters.RequestInfo = function (V, prefix, argument) {
|
|
|
1006
1010
|
return webidl.converters.USVString(V)
|
|
1007
1011
|
}
|
|
1008
1012
|
|
|
1009
|
-
|
|
1013
|
+
/**
|
|
1014
|
+
* @param {*} V
|
|
1015
|
+
* @returns {import('../../../types/fetch').RequestInit}
|
|
1016
|
+
* @see https://fetch.spec.whatwg.org/#requestinit
|
|
1017
|
+
*/
|
|
1010
1018
|
webidl.converters.RequestInit = webidl.dictionaryConverter([
|
|
1011
1019
|
{
|
|
1012
1020
|
key: 'method',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { Headers, HeadersList, fill, getHeadersGuard, setHeadersGuard, setHeadersList } = require('./headers')
|
|
4
|
-
const { extractBody, cloneBody, mixinBody,
|
|
4
|
+
const { extractBody, cloneBody, mixinBody, streamRegistry, bodyUnusable } = require('./body')
|
|
5
5
|
const util = require('../../core/util')
|
|
6
6
|
const nodeUtil = require('node:util')
|
|
7
7
|
const { kEnumerableProperty } = util
|
|
@@ -352,7 +352,9 @@ function cloneResponse (response) {
|
|
|
352
352
|
// 3. If response’s body is non-null, then set newResponse’s body to the
|
|
353
353
|
// result of cloning response’s body.
|
|
354
354
|
if (response.body != null) {
|
|
355
|
-
newResponse.body = cloneBody(
|
|
355
|
+
newResponse.body = cloneBody(response.body)
|
|
356
|
+
|
|
357
|
+
streamRegistry.register(newResponse, new WeakRef(response.body.stream))
|
|
356
358
|
}
|
|
357
359
|
|
|
358
360
|
// 4. Return newResponse.
|
|
@@ -552,7 +554,7 @@ function fromInnerResponse (innerResponse, guard) {
|
|
|
552
554
|
setHeadersList(headers, innerResponse.headersList)
|
|
553
555
|
setHeadersGuard(headers, guard)
|
|
554
556
|
|
|
555
|
-
if (
|
|
557
|
+
if (innerResponse.body?.stream) {
|
|
556
558
|
// If the target (response) is reclaimed, the cleanup callback may be called at some point with
|
|
557
559
|
// the held value provided for it (innerResponse.body.stream). The held value can be any value:
|
|
558
560
|
// a primitive or an object, even undefined. If the held value is an object, the registry keeps
|