File Explorer

/var/runtime/node_modules/@aws-sdk/node_modules/undici/lib/web/fetch

This explorer reads the filesystem of the server it runs on, so /workspace/user isn't present here. Browsing and the terminal still work against this server's own disk from /.

body.js17.1 KB · 510 lines
'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}