request.js 34 KB

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