aboutsummaryrefslogtreecommitdiffstats
path: root/vanilla/node_modules/undici/lib/web/fetch/body.js
diff options
context:
space:
mode:
Diffstat (limited to 'vanilla/node_modules/undici/lib/web/fetch/body.js')
-rw-r--r--vanilla/node_modules/undici/lib/web/fetch/body.js509
1 files changed, 0 insertions, 509 deletions
diff --git a/vanilla/node_modules/undici/lib/web/fetch/body.js b/vanilla/node_modules/undici/lib/web/fetch/body.js
deleted file mode 100644
index 5d17249..0000000
--- a/vanilla/node_modules/undici/lib/web/fetch/body.js
+++ /dev/null
@@ -1,509 +0,0 @@
-'use strict'
-
-const util = require('../../core/util')
-const {
- ReadableStreamFrom,
- readableStreamClose,
- fullyReadBody,
- extractMimeType
-} = require('./util')
-const { FormData, setFormDataState } = require('./formdata')
-const { webidl } = require('../webidl')
-const assert = require('node:assert')
-const { isErrored, isDisturbed } = require('node:stream')
-const { isUint8Array } = require('node:util/types')
-const { serializeAMimeType } = require('./data-url')
-const { multipartFormDataParser } = require('./formdata-parser')
-const { createDeferredPromise } = require('../../util/promise')
-const { parseJSONFromBytes } = require('../infra')
-const { utf8DecodeBytes } = require('../../encoding')
-const { runtimeFeatures } = require('../../util/runtime-features.js')
-
-const random = runtimeFeatures.has('crypto')
- ? require('node:crypto').randomInt
- : (max) => Math.floor(Math.random() * max)
-
-const textEncoder = new TextEncoder()
-function noop () {}
-
-const streamRegistry = new FinalizationRegistry((weakRef) => {
- const stream = weakRef.deref()
- if (stream && !stream.locked && !isDisturbed(stream) && !isErrored(stream)) {
- stream.cancel('Response object has been garbage collected').catch(noop)
- }
-})
-
-/**
- * Extract a body with type from a byte sequence or BodyInit object
- *
- * @param {import('../../../types').BodyInit} object - The BodyInit object to extract from
- * @param {boolean} [keepalive=false] - If true, indicates that the body
- * @returns {[{stream: ReadableStream, source: any, length: number | null}, string | null]} - Returns a tuple containing the body and its type
- *
- * @see https://fetch.spec.whatwg.org/#concept-bodyinit-extract
- */
-function extractBody (object, keepalive = false) {
- // 1. Let stream be null.
- let stream = null
- let controller = null
-
- // 2. If object is a ReadableStream object, then set stream to object.
- if (webidl.is.ReadableStream(object)) {
- stream = object
- } else if (webidl.is.Blob(object)) {
- // 3. Otherwise, if object is a Blob object, set stream to the
- // result of running object’s get stream.
- stream = object.stream()
- } else {
- // 4. Otherwise, set stream to a new ReadableStream object, and set
- // up stream with byte reading support.
- stream = new ReadableStream({
- pull () {},
- start (c) {
- controller = c
- },
- cancel () {},
- type: 'bytes'
- })
- }
-
- // 5. Assert: stream is a ReadableStream object.
- assert(webidl.is.ReadableStream(stream))
-
- // 6. Let action be null.
- let action = null
-
- // 7. Let source be null.
- let source = null
-
- // 8. Let length be null.
- let length = null
-
- // 9. Let type be null.
- let type = null
-
- // 10. Switch on object:
- if (typeof object === 'string') {
- // Set source to the UTF-8 encoding of object.
- // Note: setting source to a Uint8Array here breaks some mocking assumptions.
- source = object
-
- // Set type to `text/plain;charset=UTF-8`.
- type = 'text/plain;charset=UTF-8'
- } else if (webidl.is.URLSearchParams(object)) {
- // URLSearchParams
-
- // spec says to run application/x-www-form-urlencoded on body.list
- // this is implemented in Node.js as apart of an URLSearchParams instance toString method
- // See: https://github.com/nodejs/node/blob/e46c680bf2b211bbd52cf959ca17ee98c7f657f5/lib/internal/url.js#L490
- // and https://github.com/nodejs/node/blob/e46c680bf2b211bbd52cf959ca17ee98c7f657f5/lib/internal/url.js#L1100
-
- // Set source to the result of running the application/x-www-form-urlencoded serializer with object’s list.
- source = object.toString()
-
- // Set type to `application/x-www-form-urlencoded;charset=UTF-8`.
- type = 'application/x-www-form-urlencoded;charset=UTF-8'
- } else if (webidl.is.BufferSource(object)) {
- // Set source to a copy of the bytes held by object.
- source = webidl.util.getCopyOfBytesHeldByBufferSource(object)
- } else if (webidl.is.FormData(object)) {
- const boundary = `----formdata-undici-0${`${random(1e11)}`.padStart(11, '0')}`
- const prefix = `--${boundary}\r\nContent-Disposition: form-data`
-
- /*! formdata-polyfill. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
- const formdataEscape = (str) =>
- str.replace(/\n/g, '%0A').replace(/\r/g, '%0D').replace(/"/g, '%22')
- const normalizeLinefeeds = (value) => value.replace(/\r?\n|\r/g, '\r\n')
-
- // Set action to this step: run the multipart/form-data
- // encoding algorithm, with object’s entry list and UTF-8.
- // - This ensures that the body is immutable and can't be changed afterwords
- // - That the content-length is calculated in advance.
- // - And that all parts are pre-encoded and ready to be sent.
-
- const blobParts = []
- const rn = new Uint8Array([13, 10]) // '\r\n'
- length = 0
- let hasUnknownSizeValue = false
-
- for (const [name, value] of object) {
- if (typeof value === 'string') {
- const chunk = textEncoder.encode(prefix +
- `; name="${formdataEscape(normalizeLinefeeds(name))}"` +
- `\r\n\r\n${normalizeLinefeeds(value)}\r\n`)
- blobParts.push(chunk)
- length += chunk.byteLength
- } else {
- const chunk = textEncoder.encode(`${prefix}; name="${formdataEscape(normalizeLinefeeds(name))}"` +
- (value.name ? `; filename="${formdataEscape(value.name)}"` : '') + '\r\n' +
- `Content-Type: ${
- value.type || 'application/octet-stream'
- }\r\n\r\n`)
- blobParts.push(chunk, value, rn)
- if (typeof value.size === 'number') {
- length += chunk.byteLength + value.size + rn.byteLength
- } else {
- hasUnknownSizeValue = true
- }
- }
- }
-
- // CRLF is appended to the body to function with legacy servers and match other implementations.
- // https://github.com/curl/curl/blob/3434c6b46e682452973972e8313613dfa58cd690/lib/mime.c#L1029-L1030
- // https://github.com/form-data/form-data/issues/63
- const chunk = textEncoder.encode(`--${boundary}--\r\n`)
- blobParts.push(chunk)
- length += chunk.byteLength
- if (hasUnknownSizeValue) {
- length = null
- }
-
- // Set source to object.
- source = object
-
- action = async function * () {
- for (const part of blobParts) {
- if (part.stream) {
- yield * part.stream()
- } else {
- yield part
- }
- }
- }
-
- // Set type to `multipart/form-data; boundary=`,
- // followed by the multipart/form-data boundary string generated
- // by the multipart/form-data encoding algorithm.
- type = `multipart/form-data; boundary=${boundary}`
- } else if (webidl.is.Blob(object)) {
- // Blob
-
- // Set source to object.
- source = object
-
- // Set length to object’s size.
- length = object.size
-
- // If object’s type attribute is not the empty byte sequence, set
- // type to its value.
- if (object.type) {
- type = object.type
- }
- } else if (typeof object[Symbol.asyncIterator] === 'function') {
- // If keepalive is true, then throw a TypeError.
- if (keepalive) {
- throw new TypeError('keepalive')
- }
-
- // If object is disturbed or locked, then throw a TypeError.
- if (util.isDisturbed(object) || object.locked) {
- throw new TypeError(
- 'Response body object should not be disturbed or locked'
- )
- }
-
- stream =
- webidl.is.ReadableStream(object) ? object : ReadableStreamFrom(object)
- }
-
- // 11. If source is a byte sequence, then set action to a
- // step that returns source and length to source’s length.
- if (typeof source === 'string' || isUint8Array(source)) {
- action = () => {
- length = typeof source === 'string' ? Buffer.byteLength(source) : source.length
- return source
- }
- }
-
- // 12. If action is non-null, then run these steps in parallel:
- if (action != null) {
- ;(async () => {
- // 1. Run action.
- const result = action()
-
- // 2. Whenever one or more bytes are available and stream is not errored,
- // enqueue the result of creating a Uint8Array from the available bytes into stream.
- const iterator = result?.[Symbol.asyncIterator]?.()
- if (iterator) {
- for await (const bytes of iterator) {
- if (isErrored(stream)) break
- if (bytes.length) {
- controller.enqueue(new Uint8Array(bytes))
- }
- }
- } else if (result?.length && !isErrored(stream)) {
- controller.enqueue(typeof result === 'string' ? textEncoder.encode(result) : new Uint8Array(result))
- }
-
- // 3. When running action is done, close stream.
- queueMicrotask(() => readableStreamClose(controller))
- })()
- }
-
- // 13. Let body be a body whose stream is stream, source is source,
- // and length is length.
- const body = { stream, source, length }
-
- // 14. Return (body, type).
- return [body, type]
-}
-
-/**
- * @typedef {object} ExtractBodyResult
- * @property {ReadableStream<Uint8Array<ArrayBuffer>>} stream - The ReadableStream containing the body data
- * @property {any} source - The original source of the body data
- * @property {number | null} length - The length of the body data, or null
- */
-
-/**
- * Safely extract a body with type from a byte sequence or BodyInit object.
- *
- * @param {import('../../../types').BodyInit} object - The BodyInit object to extract from
- * @param {boolean} [keepalive=false] - If true, indicates that the body
- * @returns {[ExtractBodyResult, string | null]} - Returns a tuple containing the body and its type
- *
- * @see https://fetch.spec.whatwg.org/#bodyinit-safely-extract
- */
-function safelyExtractBody (object, keepalive = false) {
- // To safely extract a body and a `Content-Type` value from
- // a byte sequence or BodyInit object object, run these steps:
-
- // 1. If object is a ReadableStream object, then:
- if (webidl.is.ReadableStream(object)) {
- // Assert: object is neither disturbed nor locked.
- assert(!util.isDisturbed(object), 'The body has already been consumed.')
- assert(!object.locked, 'The stream is locked.')
- }
-
- // 2. Return the results of extracting object.
- return extractBody(object, keepalive)
-}
-
-function cloneBody (body) {
- // To clone a body body, run these steps:
-
- // https://fetch.spec.whatwg.org/#concept-body-clone
-
- // 1. Let « out1, out2 » be the result of teeing body’s stream.
- const { 0: out1, 1: out2 } = body.stream.tee()
-
- // 2. Set body’s stream to out1.
- body.stream = out1
-
- // 3. Return a body whose stream is out2 and other members are copied from body.
- return {
- stream: out2,
- length: body.length,
- source: body.source
- }
-}
-
-function bodyMixinMethods (instance, getInternalState) {
- const methods = {
- blob () {
- // The blob() method steps are to return the result of
- // running consume body with this and the following step
- // given a byte sequence bytes: return a Blob whose
- // contents are bytes and whose type attribute is this’s
- // MIME type.
- return consumeBody(this, (bytes) => {
- let mimeType = bodyMimeType(getInternalState(this))
-
- if (mimeType === null) {
- mimeType = ''
- } else if (mimeType) {
- mimeType = serializeAMimeType(mimeType)
- }
-
- // Return a Blob whose contents are bytes and type attribute
- // is mimeType.
- return new Blob([bytes], { type: mimeType })
- }, instance, getInternalState)
- },
-
- arrayBuffer () {
- // The arrayBuffer() method steps are to return the result
- // of running consume body with this and the following step
- // given a byte sequence bytes: return a new ArrayBuffer
- // whose contents are bytes.
- return consumeBody(this, (bytes) => {
- return new Uint8Array(bytes).buffer
- }, instance, getInternalState)
- },
-
- text () {
- // The text() method steps are to return the result of running
- // consume body with this and UTF-8 decode.
- return consumeBody(this, utf8DecodeBytes, instance, getInternalState)
- },
-
- json () {
- // The json() method steps are to return the result of running
- // consume body with this and parse JSON from bytes.
- return consumeBody(this, parseJSONFromBytes, instance, getInternalState)
- },
-
- formData () {
- // The formData() method steps are to return the result of running
- // consume body with this and the following step given a byte sequence bytes:
- return consumeBody(this, (value) => {
- // 1. Let mimeType be the result of get the MIME type with this.
- const mimeType = bodyMimeType(getInternalState(this))
-
- // 2. If mimeType is non-null, then switch on mimeType’s essence and run
- // the corresponding steps:
- if (mimeType !== null) {
- switch (mimeType.essence) {
- case 'multipart/form-data': {
- // 1. ... [long step]
- // 2. If that fails for some reason, then throw a TypeError.
- const parsed = multipartFormDataParser(value, mimeType)
-
- // 3. Return a new FormData object, appending each entry,
- // resulting from the parsing operation, to its entry list.
- const fd = new FormData()
- setFormDataState(fd, parsed)
-
- return fd
- }
- case 'application/x-www-form-urlencoded': {
- // 1. Let entries be the result of parsing bytes.
- const entries = new URLSearchParams(value.toString())
-
- // 2. If entries is failure, then throw a TypeError.
-
- // 3. Return a new FormData object whose entry list is entries.
- const fd = new FormData()
-
- for (const [name, value] of entries) {
- fd.append(name, value)
- }
-
- return fd
- }
- }
- }
-
- // 3. Throw a TypeError.
- throw new TypeError(
- 'Content-Type was not one of "multipart/form-data" or "application/x-www-form-urlencoded".'
- )
- }, instance, getInternalState)
- },
-
- bytes () {
- // The bytes() method steps are to return the result of running consume body
- // with this and the following step given a byte sequence bytes: return the
- // result of creating a Uint8Array from bytes in this’s relevant realm.
- return consumeBody(this, (bytes) => {
- return new Uint8Array(bytes)
- }, instance, getInternalState)
- }
- }
-
- return methods
-}
-
-function mixinBody (prototype, getInternalState) {
- Object.assign(prototype.prototype, bodyMixinMethods(prototype, getInternalState))
-}
-
-/**
- * @see https://fetch.spec.whatwg.org/#concept-body-consume-body
- * @param {any} object internal state
- * @param {(value: unknown) => unknown} convertBytesToJSValue
- * @param {any} instance
- * @param {(target: any) => any} getInternalState
- */
-function consumeBody (object, convertBytesToJSValue, instance, getInternalState) {
- try {
- webidl.brandCheck(object, instance)
- } catch (e) {
- return Promise.reject(e)
- }
-
- object = getInternalState(object)
-
- // 1. If object is unusable, then return a promise rejected
- // with a TypeError.
- if (bodyUnusable(object)) {
- return Promise.reject(new TypeError('Body is unusable: Body has already been read'))
- }
-
- // 2. Let promise be a new promise.
- const promise = createDeferredPromise()
-
- // 3. Let errorSteps given error be to reject promise with error.
- const errorSteps = promise.reject
-
- // 4. Let successSteps given a byte sequence data be to resolve
- // promise with the result of running convertBytesToJSValue
- // with data. If that threw an exception, then run errorSteps
- // with that exception.
- const successSteps = (data) => {
- try {
- promise.resolve(convertBytesToJSValue(data))
- } catch (e) {
- errorSteps(e)
- }
- }
-
- // 5. If object’s body is null, then run successSteps with an
- // empty byte sequence.
- if (object.body == null) {
- successSteps(Buffer.allocUnsafe(0))
- return promise.promise
- }
-
- // 6. Otherwise, fully read object’s body given successSteps,
- // errorSteps, and object’s relevant global object.
- fullyReadBody(object.body, successSteps, errorSteps)
-
- // 7. Return promise.
- return promise.promise
-}
-
-/**
- * @see https://fetch.spec.whatwg.org/#body-unusable
- * @param {any} object internal state
- */
-function bodyUnusable (object) {
- const body = object.body
-
- // An object including the Body interface mixin is
- // said to be unusable if its body is non-null and
- // its body’s stream is disturbed or locked.
- return body != null && (body.stream.locked || util.isDisturbed(body.stream))
-}
-
-/**
- * @see https://fetch.spec.whatwg.org/#concept-body-mime-type
- * @param {any} requestOrResponse internal state
- */
-function bodyMimeType (requestOrResponse) {
- // 1. Let headers be null.
- // 2. If requestOrResponse is a Request object, then set headers to requestOrResponse’s request’s header list.
- // 3. Otherwise, set headers to requestOrResponse’s response’s header list.
- /** @type {import('./headers').HeadersList} */
- const headers = requestOrResponse.headersList
-
- // 4. Let mimeType be the result of extracting a MIME type from headers.
- const mimeType = extractMimeType(headers)
-
- // 5. If mimeType is failure, then return null.
- if (mimeType === 'failure') {
- return null
- }
-
- // 6. Return mimeType.
- return mimeType
-}
-
-module.exports = {
- extractBody,
- safelyExtractBody,
- cloneBody,
- mixinBody,
- streamRegistry,
- bodyUnusable
-}