source: node_modules/undici/lib/fetch/request.js

main
Last change on this file was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 29.8 KB
Line 
1/* globals AbortController */
2
3'use strict'
4
5const { extractBody, mixinBody, cloneBody } = require('./body')
6const { Headers, fill: fillHeaders, HeadersList } = require('./headers')
7const { FinalizationRegistry } = require('../compat/dispatcher-weakref')()
8const util = require('../core/util')
9const {
10 isValidHTTPToken,
11 sameOrigin,
12 normalizeMethod,
13 makePolicyContainer,
14 normalizeMethodRecord
15} = require('./util')
16const {
17 forbiddenMethodsSet,
18 corsSafeListedMethodsSet,
19 referrerPolicy,
20 requestRedirect,
21 requestMode,
22 requestCredentials,
23 requestCache,
24 requestDuplex
25} = require('./constants')
26const { kEnumerableProperty } = util
27const { kHeaders, kSignal, kState, kGuard, kRealm } = require('./symbols')
28const { webidl } = require('./webidl')
29const { getGlobalOrigin } = require('./global')
30const { URLSerializer } = require('./dataURL')
31const { kHeadersList, kConstruct } = require('../core/symbols')
32const assert = require('assert')
33const { getMaxListeners, setMaxListeners, getEventListeners, defaultMaxListeners } = require('events')
34
35let TransformStream = globalThis.TransformStream
36
37const kAbortController = Symbol('abortController')
38
39const requestFinalizer = new FinalizationRegistry(({ signal, abort }) => {
40 signal.removeEventListener('abort', abort)
41})
42
43// https://fetch.spec.whatwg.org/#request-class
44class Request {
45 // https://fetch.spec.whatwg.org/#dom-request
46 constructor (input, init = {}) {
47 if (input === kConstruct) {
48 return
49 }
50
51 webidl.argumentLengthCheck(arguments, 1, { header: 'Request constructor' })
52
53 input = webidl.converters.RequestInfo(input)
54 init = webidl.converters.RequestInit(init)
55
56 // https://html.spec.whatwg.org/multipage/webappapis.html#environment-settings-object
57 this[kRealm] = {
58 settingsObject: {
59 baseUrl: getGlobalOrigin(),
60 get origin () {
61 return this.baseUrl?.origin
62 },
63 policyContainer: makePolicyContainer()
64 }
65 }
66
67 // 1. Let request be null.
68 let request = null
69
70 // 2. Let fallbackMode be null.
71 let fallbackMode = null
72
73 // 3. Let baseURL be this’s relevant settings object’s API base URL.
74 const baseUrl = this[kRealm].settingsObject.baseUrl
75
76 // 4. Let signal be null.
77 let signal = null
78
79 // 5. If input is a string, then:
80 if (typeof input === 'string') {
81 // 1. Let parsedURL be the result of parsing input with baseURL.
82 // 2. If parsedURL is failure, then throw a TypeError.
83 let parsedURL
84 try {
85 parsedURL = new URL(input, baseUrl)
86 } catch (err) {
87 throw new TypeError('Failed to parse URL from ' + input, { cause: err })
88 }
89
90 // 3. If parsedURL includes credentials, then throw a TypeError.
91 if (parsedURL.username || parsedURL.password) {
92 throw new TypeError(
93 'Request cannot be constructed from a URL that includes credentials: ' +
94 input
95 )
96 }
97
98 // 4. Set request to a new request whose URL is parsedURL.
99 request = makeRequest({ urlList: [parsedURL] })
100
101 // 5. Set fallbackMode to "cors".
102 fallbackMode = 'cors'
103 } else {
104 // 6. Otherwise:
105
106 // 7. Assert: input is a Request object.
107 assert(input instanceof Request)
108
109 // 8. Set request to input’s request.
110 request = input[kState]
111
112 // 9. Set signal to input’s signal.
113 signal = input[kSignal]
114 }
115
116 // 7. Let origin be this’s relevant settings object’s origin.
117 const origin = this[kRealm].settingsObject.origin
118
119 // 8. Let window be "client".
120 let window = 'client'
121
122 // 9. If request’s window is an environment settings object and its origin
123 // is same origin with origin, then set window to request’s window.
124 if (
125 request.window?.constructor?.name === 'EnvironmentSettingsObject' &&
126 sameOrigin(request.window, origin)
127 ) {
128 window = request.window
129 }
130
131 // 10. If init["window"] exists and is non-null, then throw a TypeError.
132 if (init.window != null) {
133 throw new TypeError(`'window' option '${window}' must be null`)
134 }
135
136 // 11. If init["window"] exists, then set window to "no-window".
137 if ('window' in init) {
138 window = 'no-window'
139 }
140
141 // 12. Set request to a new request with the following properties:
142 request = makeRequest({
143 // URL request’s URL.
144 // undici implementation note: this is set as the first item in request's urlList in makeRequest
145 // method request’s method.
146 method: request.method,
147 // header list A copy of request’s header list.
148 // undici implementation note: headersList is cloned in makeRequest
149 headersList: request.headersList,
150 // unsafe-request flag Set.
151 unsafeRequest: request.unsafeRequest,
152 // client This’s relevant settings object.
153 client: this[kRealm].settingsObject,
154 // window window.
155 window,
156 // priority request’s priority.
157 priority: request.priority,
158 // origin request’s origin. The propagation of the origin is only significant for navigation requests
159 // being handled by a service worker. In this scenario a request can have an origin that is different
160 // from the current client.
161 origin: request.origin,
162 // referrer request’s referrer.
163 referrer: request.referrer,
164 // referrer policy request’s referrer policy.
165 referrerPolicy: request.referrerPolicy,
166 // mode request’s mode.
167 mode: request.mode,
168 // credentials mode request’s credentials mode.
169 credentials: request.credentials,
170 // cache mode request’s cache mode.
171 cache: request.cache,
172 // redirect mode request’s redirect mode.
173 redirect: request.redirect,
174 // integrity metadata request’s integrity metadata.
175 integrity: request.integrity,
176 // keepalive request’s keepalive.
177 keepalive: request.keepalive,
178 // reload-navigation flag request’s reload-navigation flag.
179 reloadNavigation: request.reloadNavigation,
180 // history-navigation flag request’s history-navigation flag.
181 historyNavigation: request.historyNavigation,
182 // URL list A clone of request’s URL list.
183 urlList: [...request.urlList]
184 })
185
186 const initHasKey = Object.keys(init).length !== 0
187
188 // 13. If init is not empty, then:
189 if (initHasKey) {
190 // 1. If request’s mode is "navigate", then set it to "same-origin".
191 if (request.mode === 'navigate') {
192 request.mode = 'same-origin'
193 }
194
195 // 2. Unset request’s reload-navigation flag.
196 request.reloadNavigation = false
197
198 // 3. Unset request’s history-navigation flag.
199 request.historyNavigation = false
200
201 // 4. Set request’s origin to "client".
202 request.origin = 'client'
203
204 // 5. Set request’s referrer to "client"
205 request.referrer = 'client'
206
207 // 6. Set request’s referrer policy to the empty string.
208 request.referrerPolicy = ''
209
210 // 7. Set request’s URL to request’s current URL.
211 request.url = request.urlList[request.urlList.length - 1]
212
213 // 8. Set request’s URL list to « request’s URL ».
214 request.urlList = [request.url]
215 }
216
217 // 14. If init["referrer"] exists, then:
218 if (init.referrer !== undefined) {
219 // 1. Let referrer be init["referrer"].
220 const referrer = init.referrer
221
222 // 2. If referrer is the empty string, then set request’s referrer to "no-referrer".
223 if (referrer === '') {
224 request.referrer = 'no-referrer'
225 } else {
226 // 1. Let parsedReferrer be the result of parsing referrer with
227 // baseURL.
228 // 2. If parsedReferrer is failure, then throw a TypeError.
229 let parsedReferrer
230 try {
231 parsedReferrer = new URL(referrer, baseUrl)
232 } catch (err) {
233 throw new TypeError(`Referrer "${referrer}" is not a valid URL.`, { cause: err })
234 }
235
236 // 3. If one of the following is true
237 // - parsedReferrer’s scheme is "about" and path is the string "client"
238 // - parsedReferrer’s origin is not same origin with origin
239 // then set request’s referrer to "client".
240 if (
241 (parsedReferrer.protocol === 'about:' && parsedReferrer.hostname === 'client') ||
242 (origin && !sameOrigin(parsedReferrer, this[kRealm].settingsObject.baseUrl))
243 ) {
244 request.referrer = 'client'
245 } else {
246 // 4. Otherwise, set request’s referrer to parsedReferrer.
247 request.referrer = parsedReferrer
248 }
249 }
250 }
251
252 // 15. If init["referrerPolicy"] exists, then set request’s referrer policy
253 // to it.
254 if (init.referrerPolicy !== undefined) {
255 request.referrerPolicy = init.referrerPolicy
256 }
257
258 // 16. Let mode be init["mode"] if it exists, and fallbackMode otherwise.
259 let mode
260 if (init.mode !== undefined) {
261 mode = init.mode
262 } else {
263 mode = fallbackMode
264 }
265
266 // 17. If mode is "navigate", then throw a TypeError.
267 if (mode === 'navigate') {
268 throw webidl.errors.exception({
269 header: 'Request constructor',
270 message: 'invalid request mode navigate.'
271 })
272 }
273
274 // 18. If mode is non-null, set request’s mode to mode.
275 if (mode != null) {
276 request.mode = mode
277 }
278
279 // 19. If init["credentials"] exists, then set request’s credentials mode
280 // to it.
281 if (init.credentials !== undefined) {
282 request.credentials = init.credentials
283 }
284
285 // 18. If init["cache"] exists, then set request’s cache mode to it.
286 if (init.cache !== undefined) {
287 request.cache = init.cache
288 }
289
290 // 21. If request’s cache mode is "only-if-cached" and request’s mode is
291 // not "same-origin", then throw a TypeError.
292 if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') {
293 throw new TypeError(
294 "'only-if-cached' can be set only with 'same-origin' mode"
295 )
296 }
297
298 // 22. If init["redirect"] exists, then set request’s redirect mode to it.
299 if (init.redirect !== undefined) {
300 request.redirect = init.redirect
301 }
302
303 // 23. If init["integrity"] exists, then set request’s integrity metadata to it.
304 if (init.integrity != null) {
305 request.integrity = String(init.integrity)
306 }
307
308 // 24. If init["keepalive"] exists, then set request’s keepalive to it.
309 if (init.keepalive !== undefined) {
310 request.keepalive = Boolean(init.keepalive)
311 }
312
313 // 25. If init["method"] exists, then:
314 if (init.method !== undefined) {
315 // 1. Let method be init["method"].
316 let method = init.method
317
318 // 2. If method is not a method or method is a forbidden method, then
319 // throw a TypeError.
320 if (!isValidHTTPToken(method)) {
321 throw new TypeError(`'${method}' is not a valid HTTP method.`)
322 }
323
324 if (forbiddenMethodsSet.has(method.toUpperCase())) {
325 throw new TypeError(`'${method}' HTTP method is unsupported.`)
326 }
327
328 // 3. Normalize method.
329 method = normalizeMethodRecord[method] ?? normalizeMethod(method)
330
331 // 4. Set request’s method to method.
332 request.method = method
333 }
334
335 // 26. If init["signal"] exists, then set signal to it.
336 if (init.signal !== undefined) {
337 signal = init.signal
338 }
339
340 // 27. Set this’s request to request.
341 this[kState] = request
342
343 // 28. Set this’s signal to a new AbortSignal object with this’s relevant
344 // Realm.
345 // TODO: could this be simplified with AbortSignal.any
346 // (https://dom.spec.whatwg.org/#dom-abortsignal-any)
347 const ac = new AbortController()
348 this[kSignal] = ac.signal
349 this[kSignal][kRealm] = this[kRealm]
350
351 // 29. If signal is not null, then make this’s signal follow signal.
352 if (signal != null) {
353 if (
354 !signal ||
355 typeof signal.aborted !== 'boolean' ||
356 typeof signal.addEventListener !== 'function'
357 ) {
358 throw new TypeError(
359 "Failed to construct 'Request': member signal is not of type AbortSignal."
360 )
361 }
362
363 if (signal.aborted) {
364 ac.abort(signal.reason)
365 } else {
366 // Keep a strong ref to ac while request object
367 // is alive. This is needed to prevent AbortController
368 // from being prematurely garbage collected.
369 // See, https://github.com/nodejs/undici/issues/1926.
370 this[kAbortController] = ac
371
372 const acRef = new WeakRef(ac)
373 const abort = function () {
374 const ac = acRef.deref()
375 if (ac !== undefined) {
376 ac.abort(this.reason)
377 }
378 }
379
380 // Third-party AbortControllers may not work with these.
381 // See, https://github.com/nodejs/undici/pull/1910#issuecomment-1464495619.
382 try {
383 // If the max amount of listeners is equal to the default, increase it
384 // This is only available in node >= v19.9.0
385 if (typeof getMaxListeners === 'function' && getMaxListeners(signal) === defaultMaxListeners) {
386 setMaxListeners(100, signal)
387 } else if (getEventListeners(signal, 'abort').length >= defaultMaxListeners) {
388 setMaxListeners(100, signal)
389 }
390 } catch {}
391
392 util.addAbortListener(signal, abort)
393 requestFinalizer.register(ac, { signal, abort })
394 }
395 }
396
397 // 30. Set this’s headers to a new Headers object with this’s relevant
398 // Realm, whose header list is request’s header list and guard is
399 // "request".
400 this[kHeaders] = new Headers(kConstruct)
401 this[kHeaders][kHeadersList] = request.headersList
402 this[kHeaders][kGuard] = 'request'
403 this[kHeaders][kRealm] = this[kRealm]
404
405 // 31. If this’s request’s mode is "no-cors", then:
406 if (mode === 'no-cors') {
407 // 1. If this’s request’s method is not a CORS-safelisted method,
408 // then throw a TypeError.
409 if (!corsSafeListedMethodsSet.has(request.method)) {
410 throw new TypeError(
411 `'${request.method} is unsupported in no-cors mode.`
412 )
413 }
414
415 // 2. Set this’s headers’s guard to "request-no-cors".
416 this[kHeaders][kGuard] = 'request-no-cors'
417 }
418
419 // 32. If init is not empty, then:
420 if (initHasKey) {
421 /** @type {HeadersList} */
422 const headersList = this[kHeaders][kHeadersList]
423 // 1. Let headers be a copy of this’s headers and its associated header
424 // list.
425 // 2. If init["headers"] exists, then set headers to init["headers"].
426 const headers = init.headers !== undefined ? init.headers : new HeadersList(headersList)
427
428 // 3. Empty this’s headers’s header list.
429 headersList.clear()
430
431 // 4. If headers is a Headers object, then for each header in its header
432 // list, append header’s name/header’s value to this’s headers.
433 if (headers instanceof HeadersList) {
434 for (const [key, val] of headers) {
435 headersList.append(key, val)
436 }
437 // Note: Copy the `set-cookie` meta-data.
438 headersList.cookies = headers.cookies
439 } else {
440 // 5. Otherwise, fill this’s headers with headers.
441 fillHeaders(this[kHeaders], headers)
442 }
443 }
444
445 // 33. Let inputBody be input’s request’s body if input is a Request
446 // object; otherwise null.
447 const inputBody = input instanceof Request ? input[kState].body : null
448
449 // 34. If either init["body"] exists and is non-null or inputBody is
450 // non-null, and request’s method is `GET` or `HEAD`, then throw a
451 // TypeError.
452 if (
453 (init.body != null || inputBody != null) &&
454 (request.method === 'GET' || request.method === 'HEAD')
455 ) {
456 throw new TypeError('Request with GET/HEAD method cannot have body.')
457 }
458
459 // 35. Let initBody be null.
460 let initBody = null
461
462 // 36. If init["body"] exists and is non-null, then:
463 if (init.body != null) {
464 // 1. Let Content-Type be null.
465 // 2. Set initBody and Content-Type to the result of extracting
466 // init["body"], with keepalive set to request’s keepalive.
467 const [extractedBody, contentType] = extractBody(
468 init.body,
469 request.keepalive
470 )
471 initBody = extractedBody
472
473 // 3, If Content-Type is non-null and this’s headers’s header list does
474 // not contain `Content-Type`, then append `Content-Type`/Content-Type to
475 // this’s headers.
476 if (contentType && !this[kHeaders][kHeadersList].contains('content-type')) {
477 this[kHeaders].append('content-type', contentType)
478 }
479 }
480
481 // 37. Let inputOrInitBody be initBody if it is non-null; otherwise
482 // inputBody.
483 const inputOrInitBody = initBody ?? inputBody
484
485 // 38. If inputOrInitBody is non-null and inputOrInitBody’s source is
486 // null, then:
487 if (inputOrInitBody != null && inputOrInitBody.source == null) {
488 // 1. If initBody is non-null and init["duplex"] does not exist,
489 // then throw a TypeError.
490 if (initBody != null && init.duplex == null) {
491 throw new TypeError('RequestInit: duplex option is required when sending a body.')
492 }
493
494 // 2. If this’s request’s mode is neither "same-origin" nor "cors",
495 // then throw a TypeError.
496 if (request.mode !== 'same-origin' && request.mode !== 'cors') {
497 throw new TypeError(
498 'If request is made from ReadableStream, mode should be "same-origin" or "cors"'
499 )
500 }
501
502 // 3. Set this’s request’s use-CORS-preflight flag.
503 request.useCORSPreflightFlag = true
504 }
505
506 // 39. Let finalBody be inputOrInitBody.
507 let finalBody = inputOrInitBody
508
509 // 40. If initBody is null and inputBody is non-null, then:
510 if (initBody == null && inputBody != null) {
511 // 1. If input is unusable, then throw a TypeError.
512 if (util.isDisturbed(inputBody.stream) || inputBody.stream.locked) {
513 throw new TypeError(
514 'Cannot construct a Request with a Request object that has already been used.'
515 )
516 }
517
518 // 2. Set finalBody to the result of creating a proxy for inputBody.
519 if (!TransformStream) {
520 TransformStream = require('stream/web').TransformStream
521 }
522
523 // https://streams.spec.whatwg.org/#readablestream-create-a-proxy
524 const identityTransform = new TransformStream()
525 inputBody.stream.pipeThrough(identityTransform)
526 finalBody = {
527 source: inputBody.source,
528 length: inputBody.length,
529 stream: identityTransform.readable
530 }
531 }
532
533 // 41. Set this’s request’s body to finalBody.
534 this[kState].body = finalBody
535 }
536
537 // Returns request’s HTTP method, which is "GET" by default.
538 get method () {
539 webidl.brandCheck(this, Request)
540
541 // The method getter steps are to return this’s request’s method.
542 return this[kState].method
543 }
544
545 // Returns the URL of request as a string.
546 get url () {
547 webidl.brandCheck(this, Request)
548
549 // The url getter steps are to return this’s request’s URL, serialized.
550 return URLSerializer(this[kState].url)
551 }
552
553 // Returns a Headers object consisting of the headers associated with request.
554 // Note that headers added in the network layer by the user agent will not
555 // be accounted for in this object, e.g., the "Host" header.
556 get headers () {
557 webidl.brandCheck(this, Request)
558
559 // The headers getter steps are to return this’s headers.
560 return this[kHeaders]
561 }
562
563 // Returns the kind of resource requested by request, e.g., "document"
564 // or "script".
565 get destination () {
566 webidl.brandCheck(this, Request)
567
568 // The destination getter are to return this’s request’s destination.
569 return this[kState].destination
570 }
571
572 // Returns the referrer of request. Its value can be a same-origin URL if
573 // explicitly set in init, the empty string to indicate no referrer, and
574 // "about:client" when defaulting to the global’s default. This is used
575 // during fetching to determine the value of the `Referer` header of the
576 // request being made.
577 get referrer () {
578 webidl.brandCheck(this, Request)
579
580 // 1. If this’s request’s referrer is "no-referrer", then return the
581 // empty string.
582 if (this[kState].referrer === 'no-referrer') {
583 return ''
584 }
585
586 // 2. If this’s request’s referrer is "client", then return
587 // "about:client".
588 if (this[kState].referrer === 'client') {
589 return 'about:client'
590 }
591
592 // Return this’s request’s referrer, serialized.
593 return this[kState].referrer.toString()
594 }
595
596 // Returns the referrer policy associated with request.
597 // This is used during fetching to compute the value of the request’s
598 // referrer.
599 get referrerPolicy () {
600 webidl.brandCheck(this, Request)
601
602 // The referrerPolicy getter steps are to return this’s request’s referrer policy.
603 return this[kState].referrerPolicy
604 }
605
606 // Returns the mode associated with request, which is a string indicating
607 // whether the request will use CORS, or will be restricted to same-origin
608 // URLs.
609 get mode () {
610 webidl.brandCheck(this, Request)
611
612 // The mode getter steps are to return this’s request’s mode.
613 return this[kState].mode
614 }
615
616 // Returns the credentials mode associated with request,
617 // which is a string indicating whether credentials will be sent with the
618 // request always, never, or only when sent to a same-origin URL.
619 get credentials () {
620 // The credentials getter steps are to return this’s request’s credentials mode.
621 return this[kState].credentials
622 }
623
624 // Returns the cache mode associated with request,
625 // which is a string indicating how the request will
626 // interact with the browser’s cache when fetching.
627 get cache () {
628 webidl.brandCheck(this, Request)
629
630 // The cache getter steps are to return this’s request’s cache mode.
631 return this[kState].cache
632 }
633
634 // Returns the redirect mode associated with request,
635 // which is a string indicating how redirects for the
636 // request will be handled during fetching. A request
637 // will follow redirects by default.
638 get redirect () {
639 webidl.brandCheck(this, Request)
640
641 // The redirect getter steps are to return this’s request’s redirect mode.
642 return this[kState].redirect
643 }
644
645 // Returns request’s subresource integrity metadata, which is a
646 // cryptographic hash of the resource being fetched. Its value
647 // consists of multiple hashes separated by whitespace. [SRI]
648 get integrity () {
649 webidl.brandCheck(this, Request)
650
651 // The integrity getter steps are to return this’s request’s integrity
652 // metadata.
653 return this[kState].integrity
654 }
655
656 // Returns a boolean indicating whether or not request can outlive the
657 // global in which it was created.
658 get keepalive () {
659 webidl.brandCheck(this, Request)
660
661 // The keepalive getter steps are to return this’s request’s keepalive.
662 return this[kState].keepalive
663 }
664
665 // Returns a boolean indicating whether or not request is for a reload
666 // navigation.
667 get isReloadNavigation () {
668 webidl.brandCheck(this, Request)
669
670 // The isReloadNavigation getter steps are to return true if this’s
671 // request’s reload-navigation flag is set; otherwise false.
672 return this[kState].reloadNavigation
673 }
674
675 // Returns a boolean indicating whether or not request is for a history
676 // navigation (a.k.a. back-foward navigation).
677 get isHistoryNavigation () {
678 webidl.brandCheck(this, Request)
679
680 // The isHistoryNavigation getter steps are to return true if this’s request’s
681 // history-navigation flag is set; otherwise false.
682 return this[kState].historyNavigation
683 }
684
685 // Returns the signal associated with request, which is an AbortSignal
686 // object indicating whether or not request has been aborted, and its
687 // abort event handler.
688 get signal () {
689 webidl.brandCheck(this, Request)
690
691 // The signal getter steps are to return this’s signal.
692 return this[kSignal]
693 }
694
695 get body () {
696 webidl.brandCheck(this, Request)
697
698 return this[kState].body ? this[kState].body.stream : null
699 }
700
701 get bodyUsed () {
702 webidl.brandCheck(this, Request)
703
704 return !!this[kState].body && util.isDisturbed(this[kState].body.stream)
705 }
706
707 get duplex () {
708 webidl.brandCheck(this, Request)
709
710 return 'half'
711 }
712
713 // Returns a clone of request.
714 clone () {
715 webidl.brandCheck(this, Request)
716
717 // 1. If this is unusable, then throw a TypeError.
718 if (this.bodyUsed || this.body?.locked) {
719 throw new TypeError('unusable')
720 }
721
722 // 2. Let clonedRequest be the result of cloning this’s request.
723 const clonedRequest = cloneRequest(this[kState])
724
725 // 3. Let clonedRequestObject be the result of creating a Request object,
726 // given clonedRequest, this’s headers’s guard, and this’s relevant Realm.
727 const clonedRequestObject = new Request(kConstruct)
728 clonedRequestObject[kState] = clonedRequest
729 clonedRequestObject[kRealm] = this[kRealm]
730 clonedRequestObject[kHeaders] = new Headers(kConstruct)
731 clonedRequestObject[kHeaders][kHeadersList] = clonedRequest.headersList
732 clonedRequestObject[kHeaders][kGuard] = this[kHeaders][kGuard]
733 clonedRequestObject[kHeaders][kRealm] = this[kHeaders][kRealm]
734
735 // 4. Make clonedRequestObject’s signal follow this’s signal.
736 const ac = new AbortController()
737 if (this.signal.aborted) {
738 ac.abort(this.signal.reason)
739 } else {
740 util.addAbortListener(
741 this.signal,
742 () => {
743 ac.abort(this.signal.reason)
744 }
745 )
746 }
747 clonedRequestObject[kSignal] = ac.signal
748
749 // 4. Return clonedRequestObject.
750 return clonedRequestObject
751 }
752}
753
754mixinBody(Request)
755
756function makeRequest (init) {
757 // https://fetch.spec.whatwg.org/#requests
758 const request = {
759 method: 'GET',
760 localURLsOnly: false,
761 unsafeRequest: false,
762 body: null,
763 client: null,
764 reservedClient: null,
765 replacesClientId: '',
766 window: 'client',
767 keepalive: false,
768 serviceWorkers: 'all',
769 initiator: '',
770 destination: '',
771 priority: null,
772 origin: 'client',
773 policyContainer: 'client',
774 referrer: 'client',
775 referrerPolicy: '',
776 mode: 'no-cors',
777 useCORSPreflightFlag: false,
778 credentials: 'same-origin',
779 useCredentials: false,
780 cache: 'default',
781 redirect: 'follow',
782 integrity: '',
783 cryptoGraphicsNonceMetadata: '',
784 parserMetadata: '',
785 reloadNavigation: false,
786 historyNavigation: false,
787 userActivation: false,
788 taintedOrigin: false,
789 redirectCount: 0,
790 responseTainting: 'basic',
791 preventNoCacheCacheControlHeaderModification: false,
792 done: false,
793 timingAllowFailed: false,
794 ...init,
795 headersList: init.headersList
796 ? new HeadersList(init.headersList)
797 : new HeadersList()
798 }
799 request.url = request.urlList[0]
800 return request
801}
802
803// https://fetch.spec.whatwg.org/#concept-request-clone
804function cloneRequest (request) {
805 // To clone a request request, run these steps:
806
807 // 1. Let newRequest be a copy of request, except for its body.
808 const newRequest = makeRequest({ ...request, body: null })
809
810 // 2. If request’s body is non-null, set newRequest’s body to the
811 // result of cloning request’s body.
812 if (request.body != null) {
813 newRequest.body = cloneBody(request.body)
814 }
815
816 // 3. Return newRequest.
817 return newRequest
818}
819
820Object.defineProperties(Request.prototype, {
821 method: kEnumerableProperty,
822 url: kEnumerableProperty,
823 headers: kEnumerableProperty,
824 redirect: kEnumerableProperty,
825 clone: kEnumerableProperty,
826 signal: kEnumerableProperty,
827 duplex: kEnumerableProperty,
828 destination: kEnumerableProperty,
829 body: kEnumerableProperty,
830 bodyUsed: kEnumerableProperty,
831 isHistoryNavigation: kEnumerableProperty,
832 isReloadNavigation: kEnumerableProperty,
833 keepalive: kEnumerableProperty,
834 integrity: kEnumerableProperty,
835 cache: kEnumerableProperty,
836 credentials: kEnumerableProperty,
837 attribute: kEnumerableProperty,
838 referrerPolicy: kEnumerableProperty,
839 referrer: kEnumerableProperty,
840 mode: kEnumerableProperty,
841 [Symbol.toStringTag]: {
842 value: 'Request',
843 configurable: true
844 }
845})
846
847webidl.converters.Request = webidl.interfaceConverter(
848 Request
849)
850
851// https://fetch.spec.whatwg.org/#requestinfo
852webidl.converters.RequestInfo = function (V) {
853 if (typeof V === 'string') {
854 return webidl.converters.USVString(V)
855 }
856
857 if (V instanceof Request) {
858 return webidl.converters.Request(V)
859 }
860
861 return webidl.converters.USVString(V)
862}
863
864webidl.converters.AbortSignal = webidl.interfaceConverter(
865 AbortSignal
866)
867
868// https://fetch.spec.whatwg.org/#requestinit
869webidl.converters.RequestInit = webidl.dictionaryConverter([
870 {
871 key: 'method',
872 converter: webidl.converters.ByteString
873 },
874 {
875 key: 'headers',
876 converter: webidl.converters.HeadersInit
877 },
878 {
879 key: 'body',
880 converter: webidl.nullableConverter(
881 webidl.converters.BodyInit
882 )
883 },
884 {
885 key: 'referrer',
886 converter: webidl.converters.USVString
887 },
888 {
889 key: 'referrerPolicy',
890 converter: webidl.converters.DOMString,
891 // https://w3c.github.io/webappsec-referrer-policy/#referrer-policy
892 allowedValues: referrerPolicy
893 },
894 {
895 key: 'mode',
896 converter: webidl.converters.DOMString,
897 // https://fetch.spec.whatwg.org/#concept-request-mode
898 allowedValues: requestMode
899 },
900 {
901 key: 'credentials',
902 converter: webidl.converters.DOMString,
903 // https://fetch.spec.whatwg.org/#requestcredentials
904 allowedValues: requestCredentials
905 },
906 {
907 key: 'cache',
908 converter: webidl.converters.DOMString,
909 // https://fetch.spec.whatwg.org/#requestcache
910 allowedValues: requestCache
911 },
912 {
913 key: 'redirect',
914 converter: webidl.converters.DOMString,
915 // https://fetch.spec.whatwg.org/#requestredirect
916 allowedValues: requestRedirect
917 },
918 {
919 key: 'integrity',
920 converter: webidl.converters.DOMString
921 },
922 {
923 key: 'keepalive',
924 converter: webidl.converters.boolean
925 },
926 {
927 key: 'signal',
928 converter: webidl.nullableConverter(
929 (signal) => webidl.converters.AbortSignal(
930 signal,
931 { strict: false }
932 )
933 )
934 },
935 {
936 key: 'window',
937 converter: webidl.converters.any
938 },
939 {
940 key: 'duplex',
941 converter: webidl.converters.DOMString,
942 allowedValues: requestDuplex
943 }
944])
945
946module.exports = { Request, makeRequest }
Note: See TracBrowser for help on using the repository browser.