[d24f17c] | 1 | 'use strict'
|
---|
| 2 |
|
---|
| 3 | const { Headers, HeadersList, fill } = require('./headers')
|
---|
| 4 | const { extractBody, cloneBody, mixinBody } = require('./body')
|
---|
| 5 | const util = require('../core/util')
|
---|
| 6 | const { kEnumerableProperty } = util
|
---|
| 7 | const {
|
---|
| 8 | isValidReasonPhrase,
|
---|
| 9 | isCancelled,
|
---|
| 10 | isAborted,
|
---|
| 11 | isBlobLike,
|
---|
| 12 | serializeJavascriptValueToJSONString,
|
---|
| 13 | isErrorLike,
|
---|
| 14 | isomorphicEncode
|
---|
| 15 | } = require('./util')
|
---|
| 16 | const {
|
---|
| 17 | redirectStatusSet,
|
---|
| 18 | nullBodyStatus,
|
---|
| 19 | DOMException
|
---|
| 20 | } = require('./constants')
|
---|
| 21 | const { kState, kHeaders, kGuard, kRealm } = require('./symbols')
|
---|
| 22 | const { webidl } = require('./webidl')
|
---|
| 23 | const { FormData } = require('./formdata')
|
---|
| 24 | const { getGlobalOrigin } = require('./global')
|
---|
| 25 | const { URLSerializer } = require('./dataURL')
|
---|
| 26 | const { kHeadersList, kConstruct } = require('../core/symbols')
|
---|
| 27 | const assert = require('assert')
|
---|
| 28 | const { types } = require('util')
|
---|
| 29 |
|
---|
| 30 | const ReadableStream = globalThis.ReadableStream || require('stream/web').ReadableStream
|
---|
| 31 | const textEncoder = new TextEncoder('utf-8')
|
---|
| 32 |
|
---|
| 33 | // https://fetch.spec.whatwg.org/#response-class
|
---|
| 34 | class Response {
|
---|
| 35 | // Creates network error Response.
|
---|
| 36 | static error () {
|
---|
| 37 | // TODO
|
---|
| 38 | const relevantRealm = { settingsObject: {} }
|
---|
| 39 |
|
---|
| 40 | // The static error() method steps are to return the result of creating a
|
---|
| 41 | // Response object, given a new network error, "immutable", and this’s
|
---|
| 42 | // relevant Realm.
|
---|
| 43 | const responseObject = new Response()
|
---|
| 44 | responseObject[kState] = makeNetworkError()
|
---|
| 45 | responseObject[kRealm] = relevantRealm
|
---|
| 46 | responseObject[kHeaders][kHeadersList] = responseObject[kState].headersList
|
---|
| 47 | responseObject[kHeaders][kGuard] = 'immutable'
|
---|
| 48 | responseObject[kHeaders][kRealm] = relevantRealm
|
---|
| 49 | return responseObject
|
---|
| 50 | }
|
---|
| 51 |
|
---|
| 52 | // https://fetch.spec.whatwg.org/#dom-response-json
|
---|
| 53 | static json (data, init = {}) {
|
---|
| 54 | webidl.argumentLengthCheck(arguments, 1, { header: 'Response.json' })
|
---|
| 55 |
|
---|
| 56 | if (init !== null) {
|
---|
| 57 | init = webidl.converters.ResponseInit(init)
|
---|
| 58 | }
|
---|
| 59 |
|
---|
| 60 | // 1. Let bytes the result of running serialize a JavaScript value to JSON bytes on data.
|
---|
| 61 | const bytes = textEncoder.encode(
|
---|
| 62 | serializeJavascriptValueToJSONString(data)
|
---|
| 63 | )
|
---|
| 64 |
|
---|
| 65 | // 2. Let body be the result of extracting bytes.
|
---|
| 66 | const body = extractBody(bytes)
|
---|
| 67 |
|
---|
| 68 | // 3. Let responseObject be the result of creating a Response object, given a new response,
|
---|
| 69 | // "response", and this’s relevant Realm.
|
---|
| 70 | const relevantRealm = { settingsObject: {} }
|
---|
| 71 | const responseObject = new Response()
|
---|
| 72 | responseObject[kRealm] = relevantRealm
|
---|
| 73 | responseObject[kHeaders][kGuard] = 'response'
|
---|
| 74 | responseObject[kHeaders][kRealm] = relevantRealm
|
---|
| 75 |
|
---|
| 76 | // 4. Perform initialize a response given responseObject, init, and (body, "application/json").
|
---|
| 77 | initializeResponse(responseObject, init, { body: body[0], type: 'application/json' })
|
---|
| 78 |
|
---|
| 79 | // 5. Return responseObject.
|
---|
| 80 | return responseObject
|
---|
| 81 | }
|
---|
| 82 |
|
---|
| 83 | // Creates a redirect Response that redirects to url with status status.
|
---|
| 84 | static redirect (url, status = 302) {
|
---|
| 85 | const relevantRealm = { settingsObject: {} }
|
---|
| 86 |
|
---|
| 87 | webidl.argumentLengthCheck(arguments, 1, { header: 'Response.redirect' })
|
---|
| 88 |
|
---|
| 89 | url = webidl.converters.USVString(url)
|
---|
| 90 | status = webidl.converters['unsigned short'](status)
|
---|
| 91 |
|
---|
| 92 | // 1. Let parsedURL be the result of parsing url with current settings
|
---|
| 93 | // object’s API base URL.
|
---|
| 94 | // 2. If parsedURL is failure, then throw a TypeError.
|
---|
| 95 | // TODO: base-URL?
|
---|
| 96 | let parsedURL
|
---|
| 97 | try {
|
---|
| 98 | parsedURL = new URL(url, getGlobalOrigin())
|
---|
| 99 | } catch (err) {
|
---|
| 100 | throw Object.assign(new TypeError('Failed to parse URL from ' + url), {
|
---|
| 101 | cause: err
|
---|
| 102 | })
|
---|
| 103 | }
|
---|
| 104 |
|
---|
| 105 | // 3. If status is not a redirect status, then throw a RangeError.
|
---|
| 106 | if (!redirectStatusSet.has(status)) {
|
---|
| 107 | throw new RangeError('Invalid status code ' + status)
|
---|
| 108 | }
|
---|
| 109 |
|
---|
| 110 | // 4. Let responseObject be the result of creating a Response object,
|
---|
| 111 | // given a new response, "immutable", and this’s relevant Realm.
|
---|
| 112 | const responseObject = new Response()
|
---|
| 113 | responseObject[kRealm] = relevantRealm
|
---|
| 114 | responseObject[kHeaders][kGuard] = 'immutable'
|
---|
| 115 | responseObject[kHeaders][kRealm] = relevantRealm
|
---|
| 116 |
|
---|
| 117 | // 5. Set responseObject’s response’s status to status.
|
---|
| 118 | responseObject[kState].status = status
|
---|
| 119 |
|
---|
| 120 | // 6. Let value be parsedURL, serialized and isomorphic encoded.
|
---|
| 121 | const value = isomorphicEncode(URLSerializer(parsedURL))
|
---|
| 122 |
|
---|
| 123 | // 7. Append `Location`/value to responseObject’s response’s header list.
|
---|
| 124 | responseObject[kState].headersList.append('location', value)
|
---|
| 125 |
|
---|
| 126 | // 8. Return responseObject.
|
---|
| 127 | return responseObject
|
---|
| 128 | }
|
---|
| 129 |
|
---|
| 130 | // https://fetch.spec.whatwg.org/#dom-response
|
---|
| 131 | constructor (body = null, init = {}) {
|
---|
| 132 | if (body !== null) {
|
---|
| 133 | body = webidl.converters.BodyInit(body)
|
---|
| 134 | }
|
---|
| 135 |
|
---|
| 136 | init = webidl.converters.ResponseInit(init)
|
---|
| 137 |
|
---|
| 138 | // TODO
|
---|
| 139 | this[kRealm] = { settingsObject: {} }
|
---|
| 140 |
|
---|
| 141 | // 1. Set this’s response to a new response.
|
---|
| 142 | this[kState] = makeResponse({})
|
---|
| 143 |
|
---|
| 144 | // 2. Set this’s headers to a new Headers object with this’s relevant
|
---|
| 145 | // Realm, whose header list is this’s response’s header list and guard
|
---|
| 146 | // is "response".
|
---|
| 147 | this[kHeaders] = new Headers(kConstruct)
|
---|
| 148 | this[kHeaders][kGuard] = 'response'
|
---|
| 149 | this[kHeaders][kHeadersList] = this[kState].headersList
|
---|
| 150 | this[kHeaders][kRealm] = this[kRealm]
|
---|
| 151 |
|
---|
| 152 | // 3. Let bodyWithType be null.
|
---|
| 153 | let bodyWithType = null
|
---|
| 154 |
|
---|
| 155 | // 4. If body is non-null, then set bodyWithType to the result of extracting body.
|
---|
| 156 | if (body != null) {
|
---|
| 157 | const [extractedBody, type] = extractBody(body)
|
---|
| 158 | bodyWithType = { body: extractedBody, type }
|
---|
| 159 | }
|
---|
| 160 |
|
---|
| 161 | // 5. Perform initialize a response given this, init, and bodyWithType.
|
---|
| 162 | initializeResponse(this, init, bodyWithType)
|
---|
| 163 | }
|
---|
| 164 |
|
---|
| 165 | // Returns response’s type, e.g., "cors".
|
---|
| 166 | get type () {
|
---|
| 167 | webidl.brandCheck(this, Response)
|
---|
| 168 |
|
---|
| 169 | // The type getter steps are to return this’s response’s type.
|
---|
| 170 | return this[kState].type
|
---|
| 171 | }
|
---|
| 172 |
|
---|
| 173 | // Returns response’s URL, if it has one; otherwise the empty string.
|
---|
| 174 | get url () {
|
---|
| 175 | webidl.brandCheck(this, Response)
|
---|
| 176 |
|
---|
| 177 | const urlList = this[kState].urlList
|
---|
| 178 |
|
---|
| 179 | // The url getter steps are to return the empty string if this’s
|
---|
| 180 | // response’s URL is null; otherwise this’s response’s URL,
|
---|
| 181 | // serialized with exclude fragment set to true.
|
---|
| 182 | const url = urlList[urlList.length - 1] ?? null
|
---|
| 183 |
|
---|
| 184 | if (url === null) {
|
---|
| 185 | return ''
|
---|
| 186 | }
|
---|
| 187 |
|
---|
| 188 | return URLSerializer(url, true)
|
---|
| 189 | }
|
---|
| 190 |
|
---|
| 191 | // Returns whether response was obtained through a redirect.
|
---|
| 192 | get redirected () {
|
---|
| 193 | webidl.brandCheck(this, Response)
|
---|
| 194 |
|
---|
| 195 | // The redirected getter steps are to return true if this’s response’s URL
|
---|
| 196 | // list has more than one item; otherwise false.
|
---|
| 197 | return this[kState].urlList.length > 1
|
---|
| 198 | }
|
---|
| 199 |
|
---|
| 200 | // Returns response’s status.
|
---|
| 201 | get status () {
|
---|
| 202 | webidl.brandCheck(this, Response)
|
---|
| 203 |
|
---|
| 204 | // The status getter steps are to return this’s response’s status.
|
---|
| 205 | return this[kState].status
|
---|
| 206 | }
|
---|
| 207 |
|
---|
| 208 | // Returns whether response’s status is an ok status.
|
---|
| 209 | get ok () {
|
---|
| 210 | webidl.brandCheck(this, Response)
|
---|
| 211 |
|
---|
| 212 | // The ok getter steps are to return true if this’s response’s status is an
|
---|
| 213 | // ok status; otherwise false.
|
---|
| 214 | return this[kState].status >= 200 && this[kState].status <= 299
|
---|
| 215 | }
|
---|
| 216 |
|
---|
| 217 | // Returns response’s status message.
|
---|
| 218 | get statusText () {
|
---|
| 219 | webidl.brandCheck(this, Response)
|
---|
| 220 |
|
---|
| 221 | // The statusText getter steps are to return this’s response’s status
|
---|
| 222 | // message.
|
---|
| 223 | return this[kState].statusText
|
---|
| 224 | }
|
---|
| 225 |
|
---|
| 226 | // Returns response’s headers as Headers.
|
---|
| 227 | get headers () {
|
---|
| 228 | webidl.brandCheck(this, Response)
|
---|
| 229 |
|
---|
| 230 | // The headers getter steps are to return this’s headers.
|
---|
| 231 | return this[kHeaders]
|
---|
| 232 | }
|
---|
| 233 |
|
---|
| 234 | get body () {
|
---|
| 235 | webidl.brandCheck(this, Response)
|
---|
| 236 |
|
---|
| 237 | return this[kState].body ? this[kState].body.stream : null
|
---|
| 238 | }
|
---|
| 239 |
|
---|
| 240 | get bodyUsed () {
|
---|
| 241 | webidl.brandCheck(this, Response)
|
---|
| 242 |
|
---|
| 243 | return !!this[kState].body && util.isDisturbed(this[kState].body.stream)
|
---|
| 244 | }
|
---|
| 245 |
|
---|
| 246 | // Returns a clone of response.
|
---|
| 247 | clone () {
|
---|
| 248 | webidl.brandCheck(this, Response)
|
---|
| 249 |
|
---|
| 250 | // 1. If this is unusable, then throw a TypeError.
|
---|
| 251 | if (this.bodyUsed || (this.body && this.body.locked)) {
|
---|
| 252 | throw webidl.errors.exception({
|
---|
| 253 | header: 'Response.clone',
|
---|
| 254 | message: 'Body has already been consumed.'
|
---|
| 255 | })
|
---|
| 256 | }
|
---|
| 257 |
|
---|
| 258 | // 2. Let clonedResponse be the result of cloning this’s response.
|
---|
| 259 | const clonedResponse = cloneResponse(this[kState])
|
---|
| 260 |
|
---|
| 261 | // 3. Return the result of creating a Response object, given
|
---|
| 262 | // clonedResponse, this’s headers’s guard, and this’s relevant Realm.
|
---|
| 263 | const clonedResponseObject = new Response()
|
---|
| 264 | clonedResponseObject[kState] = clonedResponse
|
---|
| 265 | clonedResponseObject[kRealm] = this[kRealm]
|
---|
| 266 | clonedResponseObject[kHeaders][kHeadersList] = clonedResponse.headersList
|
---|
| 267 | clonedResponseObject[kHeaders][kGuard] = this[kHeaders][kGuard]
|
---|
| 268 | clonedResponseObject[kHeaders][kRealm] = this[kHeaders][kRealm]
|
---|
| 269 |
|
---|
| 270 | return clonedResponseObject
|
---|
| 271 | }
|
---|
| 272 | }
|
---|
| 273 |
|
---|
| 274 | mixinBody(Response)
|
---|
| 275 |
|
---|
| 276 | Object.defineProperties(Response.prototype, {
|
---|
| 277 | type: kEnumerableProperty,
|
---|
| 278 | url: kEnumerableProperty,
|
---|
| 279 | status: kEnumerableProperty,
|
---|
| 280 | ok: kEnumerableProperty,
|
---|
| 281 | redirected: kEnumerableProperty,
|
---|
| 282 | statusText: kEnumerableProperty,
|
---|
| 283 | headers: kEnumerableProperty,
|
---|
| 284 | clone: kEnumerableProperty,
|
---|
| 285 | body: kEnumerableProperty,
|
---|
| 286 | bodyUsed: kEnumerableProperty,
|
---|
| 287 | [Symbol.toStringTag]: {
|
---|
| 288 | value: 'Response',
|
---|
| 289 | configurable: true
|
---|
| 290 | }
|
---|
| 291 | })
|
---|
| 292 |
|
---|
| 293 | Object.defineProperties(Response, {
|
---|
| 294 | json: kEnumerableProperty,
|
---|
| 295 | redirect: kEnumerableProperty,
|
---|
| 296 | error: kEnumerableProperty
|
---|
| 297 | })
|
---|
| 298 |
|
---|
| 299 | // https://fetch.spec.whatwg.org/#concept-response-clone
|
---|
| 300 | function cloneResponse (response) {
|
---|
| 301 | // To clone a response response, run these steps:
|
---|
| 302 |
|
---|
| 303 | // 1. If response is a filtered response, then return a new identical
|
---|
| 304 | // filtered response whose internal response is a clone of response’s
|
---|
| 305 | // internal response.
|
---|
| 306 | if (response.internalResponse) {
|
---|
| 307 | return filterResponse(
|
---|
| 308 | cloneResponse(response.internalResponse),
|
---|
| 309 | response.type
|
---|
| 310 | )
|
---|
| 311 | }
|
---|
| 312 |
|
---|
| 313 | // 2. Let newResponse be a copy of response, except for its body.
|
---|
| 314 | const newResponse = makeResponse({ ...response, body: null })
|
---|
| 315 |
|
---|
| 316 | // 3. If response’s body is non-null, then set newResponse’s body to the
|
---|
| 317 | // result of cloning response’s body.
|
---|
| 318 | if (response.body != null) {
|
---|
| 319 | newResponse.body = cloneBody(response.body)
|
---|
| 320 | }
|
---|
| 321 |
|
---|
| 322 | // 4. Return newResponse.
|
---|
| 323 | return newResponse
|
---|
| 324 | }
|
---|
| 325 |
|
---|
| 326 | function makeResponse (init) {
|
---|
| 327 | return {
|
---|
| 328 | aborted: false,
|
---|
| 329 | rangeRequested: false,
|
---|
| 330 | timingAllowPassed: false,
|
---|
| 331 | requestIncludesCredentials: false,
|
---|
| 332 | type: 'default',
|
---|
| 333 | status: 200,
|
---|
| 334 | timingInfo: null,
|
---|
| 335 | cacheState: '',
|
---|
| 336 | statusText: '',
|
---|
| 337 | ...init,
|
---|
| 338 | headersList: init.headersList
|
---|
| 339 | ? new HeadersList(init.headersList)
|
---|
| 340 | : new HeadersList(),
|
---|
| 341 | urlList: init.urlList ? [...init.urlList] : []
|
---|
| 342 | }
|
---|
| 343 | }
|
---|
| 344 |
|
---|
| 345 | function makeNetworkError (reason) {
|
---|
| 346 | const isError = isErrorLike(reason)
|
---|
| 347 | return makeResponse({
|
---|
| 348 | type: 'error',
|
---|
| 349 | status: 0,
|
---|
| 350 | error: isError
|
---|
| 351 | ? reason
|
---|
| 352 | : new Error(reason ? String(reason) : reason),
|
---|
| 353 | aborted: reason && reason.name === 'AbortError'
|
---|
| 354 | })
|
---|
| 355 | }
|
---|
| 356 |
|
---|
| 357 | function makeFilteredResponse (response, state) {
|
---|
| 358 | state = {
|
---|
| 359 | internalResponse: response,
|
---|
| 360 | ...state
|
---|
| 361 | }
|
---|
| 362 |
|
---|
| 363 | return new Proxy(response, {
|
---|
| 364 | get (target, p) {
|
---|
| 365 | return p in state ? state[p] : target[p]
|
---|
| 366 | },
|
---|
| 367 | set (target, p, value) {
|
---|
| 368 | assert(!(p in state))
|
---|
| 369 | target[p] = value
|
---|
| 370 | return true
|
---|
| 371 | }
|
---|
| 372 | })
|
---|
| 373 | }
|
---|
| 374 |
|
---|
| 375 | // https://fetch.spec.whatwg.org/#concept-filtered-response
|
---|
| 376 | function filterResponse (response, type) {
|
---|
| 377 | // Set response to the following filtered response with response as its
|
---|
| 378 | // internal response, depending on request’s response tainting:
|
---|
| 379 | if (type === 'basic') {
|
---|
| 380 | // A basic filtered response is a filtered response whose type is "basic"
|
---|
| 381 | // and header list excludes any headers in internal response’s header list
|
---|
| 382 | // whose name is a forbidden response-header name.
|
---|
| 383 |
|
---|
| 384 | // Note: undici does not implement forbidden response-header names
|
---|
| 385 | return makeFilteredResponse(response, {
|
---|
| 386 | type: 'basic',
|
---|
| 387 | headersList: response.headersList
|
---|
| 388 | })
|
---|
| 389 | } else if (type === 'cors') {
|
---|
| 390 | // A CORS filtered response is a filtered response whose type is "cors"
|
---|
| 391 | // and header list excludes any headers in internal response’s header
|
---|
| 392 | // list whose name is not a CORS-safelisted response-header name, given
|
---|
| 393 | // internal response’s CORS-exposed header-name list.
|
---|
| 394 |
|
---|
| 395 | // Note: undici does not implement CORS-safelisted response-header names
|
---|
| 396 | return makeFilteredResponse(response, {
|
---|
| 397 | type: 'cors',
|
---|
| 398 | headersList: response.headersList
|
---|
| 399 | })
|
---|
| 400 | } else if (type === 'opaque') {
|
---|
| 401 | // An opaque filtered response is a filtered response whose type is
|
---|
| 402 | // "opaque", URL list is the empty list, status is 0, status message
|
---|
| 403 | // is the empty byte sequence, header list is empty, and body is null.
|
---|
| 404 |
|
---|
| 405 | return makeFilteredResponse(response, {
|
---|
| 406 | type: 'opaque',
|
---|
| 407 | urlList: Object.freeze([]),
|
---|
| 408 | status: 0,
|
---|
| 409 | statusText: '',
|
---|
| 410 | body: null
|
---|
| 411 | })
|
---|
| 412 | } else if (type === 'opaqueredirect') {
|
---|
| 413 | // An opaque-redirect filtered response is a filtered response whose type
|
---|
| 414 | // is "opaqueredirect", status is 0, status message is the empty byte
|
---|
| 415 | // sequence, header list is empty, and body is null.
|
---|
| 416 |
|
---|
| 417 | return makeFilteredResponse(response, {
|
---|
| 418 | type: 'opaqueredirect',
|
---|
| 419 | status: 0,
|
---|
| 420 | statusText: '',
|
---|
| 421 | headersList: [],
|
---|
| 422 | body: null
|
---|
| 423 | })
|
---|
| 424 | } else {
|
---|
| 425 | assert(false)
|
---|
| 426 | }
|
---|
| 427 | }
|
---|
| 428 |
|
---|
| 429 | // https://fetch.spec.whatwg.org/#appropriate-network-error
|
---|
| 430 | function makeAppropriateNetworkError (fetchParams, err = null) {
|
---|
| 431 | // 1. Assert: fetchParams is canceled.
|
---|
| 432 | assert(isCancelled(fetchParams))
|
---|
| 433 |
|
---|
| 434 | // 2. Return an aborted network error if fetchParams is aborted;
|
---|
| 435 | // otherwise return a network error.
|
---|
| 436 | return isAborted(fetchParams)
|
---|
| 437 | ? makeNetworkError(Object.assign(new DOMException('The operation was aborted.', 'AbortError'), { cause: err }))
|
---|
| 438 | : makeNetworkError(Object.assign(new DOMException('Request was cancelled.'), { cause: err }))
|
---|
| 439 | }
|
---|
| 440 |
|
---|
| 441 | // https://whatpr.org/fetch/1392.html#initialize-a-response
|
---|
| 442 | function initializeResponse (response, init, body) {
|
---|
| 443 | // 1. If init["status"] is not in the range 200 to 599, inclusive, then
|
---|
| 444 | // throw a RangeError.
|
---|
| 445 | if (init.status !== null && (init.status < 200 || init.status > 599)) {
|
---|
| 446 | throw new RangeError('init["status"] must be in the range of 200 to 599, inclusive.')
|
---|
| 447 | }
|
---|
| 448 |
|
---|
| 449 | // 2. If init["statusText"] does not match the reason-phrase token production,
|
---|
| 450 | // then throw a TypeError.
|
---|
| 451 | if ('statusText' in init && init.statusText != null) {
|
---|
| 452 | // See, https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2:
|
---|
| 453 | // reason-phrase = *( HTAB / SP / VCHAR / obs-text )
|
---|
| 454 | if (!isValidReasonPhrase(String(init.statusText))) {
|
---|
| 455 | throw new TypeError('Invalid statusText')
|
---|
| 456 | }
|
---|
| 457 | }
|
---|
| 458 |
|
---|
| 459 | // 3. Set response’s response’s status to init["status"].
|
---|
| 460 | if ('status' in init && init.status != null) {
|
---|
| 461 | response[kState].status = init.status
|
---|
| 462 | }
|
---|
| 463 |
|
---|
| 464 | // 4. Set response’s response’s status message to init["statusText"].
|
---|
| 465 | if ('statusText' in init && init.statusText != null) {
|
---|
| 466 | response[kState].statusText = init.statusText
|
---|
| 467 | }
|
---|
| 468 |
|
---|
| 469 | // 5. If init["headers"] exists, then fill response’s headers with init["headers"].
|
---|
| 470 | if ('headers' in init && init.headers != null) {
|
---|
| 471 | fill(response[kHeaders], init.headers)
|
---|
| 472 | }
|
---|
| 473 |
|
---|
| 474 | // 6. If body was given, then:
|
---|
| 475 | if (body) {
|
---|
| 476 | // 1. If response's status is a null body status, then throw a TypeError.
|
---|
| 477 | if (nullBodyStatus.includes(response.status)) {
|
---|
| 478 | throw webidl.errors.exception({
|
---|
| 479 | header: 'Response constructor',
|
---|
| 480 | message: 'Invalid response status code ' + response.status
|
---|
| 481 | })
|
---|
| 482 | }
|
---|
| 483 |
|
---|
| 484 | // 2. Set response's body to body's body.
|
---|
| 485 | response[kState].body = body.body
|
---|
| 486 |
|
---|
| 487 | // 3. If body's type is non-null and response's header list does not contain
|
---|
| 488 | // `Content-Type`, then append (`Content-Type`, body's type) to response's header list.
|
---|
| 489 | if (body.type != null && !response[kState].headersList.contains('Content-Type')) {
|
---|
| 490 | response[kState].headersList.append('content-type', body.type)
|
---|
| 491 | }
|
---|
| 492 | }
|
---|
| 493 | }
|
---|
| 494 |
|
---|
| 495 | webidl.converters.ReadableStream = webidl.interfaceConverter(
|
---|
| 496 | ReadableStream
|
---|
| 497 | )
|
---|
| 498 |
|
---|
| 499 | webidl.converters.FormData = webidl.interfaceConverter(
|
---|
| 500 | FormData
|
---|
| 501 | )
|
---|
| 502 |
|
---|
| 503 | webidl.converters.URLSearchParams = webidl.interfaceConverter(
|
---|
| 504 | URLSearchParams
|
---|
| 505 | )
|
---|
| 506 |
|
---|
| 507 | // https://fetch.spec.whatwg.org/#typedefdef-xmlhttprequestbodyinit
|
---|
| 508 | webidl.converters.XMLHttpRequestBodyInit = function (V) {
|
---|
| 509 | if (typeof V === 'string') {
|
---|
| 510 | return webidl.converters.USVString(V)
|
---|
| 511 | }
|
---|
| 512 |
|
---|
| 513 | if (isBlobLike(V)) {
|
---|
| 514 | return webidl.converters.Blob(V, { strict: false })
|
---|
| 515 | }
|
---|
| 516 |
|
---|
| 517 | if (types.isArrayBuffer(V) || types.isTypedArray(V) || types.isDataView(V)) {
|
---|
| 518 | return webidl.converters.BufferSource(V)
|
---|
| 519 | }
|
---|
| 520 |
|
---|
| 521 | if (util.isFormDataLike(V)) {
|
---|
| 522 | return webidl.converters.FormData(V, { strict: false })
|
---|
| 523 | }
|
---|
| 524 |
|
---|
| 525 | if (V instanceof URLSearchParams) {
|
---|
| 526 | return webidl.converters.URLSearchParams(V)
|
---|
| 527 | }
|
---|
| 528 |
|
---|
| 529 | return webidl.converters.DOMString(V)
|
---|
| 530 | }
|
---|
| 531 |
|
---|
| 532 | // https://fetch.spec.whatwg.org/#bodyinit
|
---|
| 533 | webidl.converters.BodyInit = function (V) {
|
---|
| 534 | if (V instanceof ReadableStream) {
|
---|
| 535 | return webidl.converters.ReadableStream(V)
|
---|
| 536 | }
|
---|
| 537 |
|
---|
| 538 | // Note: the spec doesn't include async iterables,
|
---|
| 539 | // this is an undici extension.
|
---|
| 540 | if (V?.[Symbol.asyncIterator]) {
|
---|
| 541 | return V
|
---|
| 542 | }
|
---|
| 543 |
|
---|
| 544 | return webidl.converters.XMLHttpRequestBodyInit(V)
|
---|
| 545 | }
|
---|
| 546 |
|
---|
| 547 | webidl.converters.ResponseInit = webidl.dictionaryConverter([
|
---|
| 548 | {
|
---|
| 549 | key: 'status',
|
---|
| 550 | converter: webidl.converters['unsigned short'],
|
---|
| 551 | defaultValue: 200
|
---|
| 552 | },
|
---|
| 553 | {
|
---|
| 554 | key: 'statusText',
|
---|
| 555 | converter: webidl.converters.ByteString,
|
---|
| 556 | defaultValue: ''
|
---|
| 557 | },
|
---|
| 558 | {
|
---|
| 559 | key: 'headers',
|
---|
| 560 | converter: webidl.converters.HeadersInit
|
---|
| 561 | }
|
---|
| 562 | ])
|
---|
| 563 |
|
---|
| 564 | module.exports = {
|
---|
| 565 | makeNetworkError,
|
---|
| 566 | makeResponse,
|
---|
| 567 | makeAppropriateNetworkError,
|
---|
| 568 | filterResponse,
|
---|
| 569 | Response,
|
---|
| 570 | cloneResponse
|
---|
| 571 | }
|
---|