| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 | import util from 'util';import {Readable} from 'stream';import utils from "../utils.js";import readBlob from "./readBlob.js";import platform from "../platform/index.js";const BOUNDARY_ALPHABET = platform.ALPHABET.ALPHA_DIGIT + '-_';const textEncoder = typeof TextEncoder === 'function' ? new TextEncoder() : new util.TextEncoder();const CRLF = '\r\n';const CRLF_BYTES = textEncoder.encode(CRLF);const CRLF_BYTES_COUNT = 2;class FormDataPart {  constructor(name, value) {    const {escapeName} = this.constructor;    const isStringValue = utils.isString(value);    let headers = `Content-Disposition: form-data; name="${escapeName(name)}"${      !isStringValue && value.name ? `; filename="${escapeName(value.name)}"` : ''    }${CRLF}`;    if (isStringValue) {      value = textEncoder.encode(String(value).replace(/\r?\n|\r\n?/g, CRLF));    } else {      headers += `Content-Type: ${value.type || "application/octet-stream"}${CRLF}`    }    this.headers = textEncoder.encode(headers + CRLF);    this.contentLength = isStringValue ? value.byteLength : value.size;    this.size = this.headers.byteLength + this.contentLength + CRLF_BYTES_COUNT;    this.name = name;    this.value = value;  }  async *encode(){    yield this.headers;    const {value} = this;    if(utils.isTypedArray(value)) {      yield value;    } else {      yield* readBlob(value);    }    yield CRLF_BYTES;  }  static escapeName(name) {      return String(name).replace(/[\r\n"]/g, (match) => ({        '\r' : '%0D',        '\n' : '%0A',        '"' : '%22',      }[match]));  }}const formDataToStream = (form, headersHandler, options) => {  const {    tag = 'form-data-boundary',    size = 25,    boundary = tag + '-' + platform.generateString(size, BOUNDARY_ALPHABET)  } = options || {};  if(!utils.isFormData(form)) {    throw TypeError('FormData instance required');  }  if (boundary.length < 1 || boundary.length > 70) {    throw Error('boundary must be 10-70 characters long')  }  const boundaryBytes = textEncoder.encode('--' + boundary + CRLF);  const footerBytes = textEncoder.encode('--' + boundary + '--' + CRLF + CRLF);  let contentLength = footerBytes.byteLength;  const parts = Array.from(form.entries()).map(([name, value]) => {    const part = new FormDataPart(name, value);    contentLength += part.size;    return part;  });  contentLength += boundaryBytes.byteLength * parts.length;  contentLength = utils.toFiniteNumber(contentLength);  const computedHeaders = {    'Content-Type': `multipart/form-data; boundary=${boundary}`  }  if (Number.isFinite(contentLength)) {    computedHeaders['Content-Length'] = contentLength;  }  headersHandler && headersHandler(computedHeaders);  return Readable.from((async function *() {    for(const part of parts) {      yield boundaryBytes;      yield* part.encode();    }    yield footerBytes;  })());};export default formDataToStream;
 |