11cb0ef41Sopenharmony_ci'use strict'
21cb0ef41Sopenharmony_ciconst { Minipass } = require('minipass')
31cb0ef41Sopenharmony_ciconst MinipassSized = require('minipass-sized')
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ciconst Blob = require('./blob.js')
61cb0ef41Sopenharmony_ciconst { BUFFER } = Blob
71cb0ef41Sopenharmony_ciconst FetchError = require('./fetch-error.js')
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ci// optional dependency on 'encoding'
101cb0ef41Sopenharmony_cilet convert
111cb0ef41Sopenharmony_citry {
121cb0ef41Sopenharmony_ci  convert = require('encoding').convert
131cb0ef41Sopenharmony_ci} catch (e) {
141cb0ef41Sopenharmony_ci  // defer error until textConverted is called
151cb0ef41Sopenharmony_ci}
161cb0ef41Sopenharmony_ci
171cb0ef41Sopenharmony_ciconst INTERNALS = Symbol('Body internals')
181cb0ef41Sopenharmony_ciconst CONSUME_BODY = Symbol('consumeBody')
191cb0ef41Sopenharmony_ci
201cb0ef41Sopenharmony_ciclass Body {
211cb0ef41Sopenharmony_ci  constructor (bodyArg, options = {}) {
221cb0ef41Sopenharmony_ci    const { size = 0, timeout = 0 } = options
231cb0ef41Sopenharmony_ci    const body = bodyArg === undefined || bodyArg === null ? null
241cb0ef41Sopenharmony_ci      : isURLSearchParams(bodyArg) ? Buffer.from(bodyArg.toString())
251cb0ef41Sopenharmony_ci      : isBlob(bodyArg) ? bodyArg
261cb0ef41Sopenharmony_ci      : Buffer.isBuffer(bodyArg) ? bodyArg
271cb0ef41Sopenharmony_ci      : Object.prototype.toString.call(bodyArg) === '[object ArrayBuffer]'
281cb0ef41Sopenharmony_ci        ? Buffer.from(bodyArg)
291cb0ef41Sopenharmony_ci        : ArrayBuffer.isView(bodyArg)
301cb0ef41Sopenharmony_ci          ? Buffer.from(bodyArg.buffer, bodyArg.byteOffset, bodyArg.byteLength)
311cb0ef41Sopenharmony_ci          : Minipass.isStream(bodyArg) ? bodyArg
321cb0ef41Sopenharmony_ci          : Buffer.from(String(bodyArg))
331cb0ef41Sopenharmony_ci
341cb0ef41Sopenharmony_ci    this[INTERNALS] = {
351cb0ef41Sopenharmony_ci      body,
361cb0ef41Sopenharmony_ci      disturbed: false,
371cb0ef41Sopenharmony_ci      error: null,
381cb0ef41Sopenharmony_ci    }
391cb0ef41Sopenharmony_ci
401cb0ef41Sopenharmony_ci    this.size = size
411cb0ef41Sopenharmony_ci    this.timeout = timeout
421cb0ef41Sopenharmony_ci
431cb0ef41Sopenharmony_ci    if (Minipass.isStream(body)) {
441cb0ef41Sopenharmony_ci      body.on('error', er => {
451cb0ef41Sopenharmony_ci        const error = er.name === 'AbortError' ? er
461cb0ef41Sopenharmony_ci          : new FetchError(`Invalid response while trying to fetch ${
471cb0ef41Sopenharmony_ci            this.url}: ${er.message}`, 'system', er)
481cb0ef41Sopenharmony_ci        this[INTERNALS].error = error
491cb0ef41Sopenharmony_ci      })
501cb0ef41Sopenharmony_ci    }
511cb0ef41Sopenharmony_ci  }
521cb0ef41Sopenharmony_ci
531cb0ef41Sopenharmony_ci  get body () {
541cb0ef41Sopenharmony_ci    return this[INTERNALS].body
551cb0ef41Sopenharmony_ci  }
561cb0ef41Sopenharmony_ci
571cb0ef41Sopenharmony_ci  get bodyUsed () {
581cb0ef41Sopenharmony_ci    return this[INTERNALS].disturbed
591cb0ef41Sopenharmony_ci  }
601cb0ef41Sopenharmony_ci
611cb0ef41Sopenharmony_ci  arrayBuffer () {
621cb0ef41Sopenharmony_ci    return this[CONSUME_BODY]().then(buf =>
631cb0ef41Sopenharmony_ci      buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength))
641cb0ef41Sopenharmony_ci  }
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_ci  blob () {
671cb0ef41Sopenharmony_ci    const ct = this.headers && this.headers.get('content-type') || ''
681cb0ef41Sopenharmony_ci    return this[CONSUME_BODY]().then(buf => Object.assign(
691cb0ef41Sopenharmony_ci      new Blob([], { type: ct.toLowerCase() }),
701cb0ef41Sopenharmony_ci      { [BUFFER]: buf }
711cb0ef41Sopenharmony_ci    ))
721cb0ef41Sopenharmony_ci  }
731cb0ef41Sopenharmony_ci
741cb0ef41Sopenharmony_ci  async json () {
751cb0ef41Sopenharmony_ci    const buf = await this[CONSUME_BODY]()
761cb0ef41Sopenharmony_ci    try {
771cb0ef41Sopenharmony_ci      return JSON.parse(buf.toString())
781cb0ef41Sopenharmony_ci    } catch (er) {
791cb0ef41Sopenharmony_ci      throw new FetchError(
801cb0ef41Sopenharmony_ci        `invalid json response body at ${this.url} reason: ${er.message}`,
811cb0ef41Sopenharmony_ci        'invalid-json'
821cb0ef41Sopenharmony_ci      )
831cb0ef41Sopenharmony_ci    }
841cb0ef41Sopenharmony_ci  }
851cb0ef41Sopenharmony_ci
861cb0ef41Sopenharmony_ci  text () {
871cb0ef41Sopenharmony_ci    return this[CONSUME_BODY]().then(buf => buf.toString())
881cb0ef41Sopenharmony_ci  }
891cb0ef41Sopenharmony_ci
901cb0ef41Sopenharmony_ci  buffer () {
911cb0ef41Sopenharmony_ci    return this[CONSUME_BODY]()
921cb0ef41Sopenharmony_ci  }
931cb0ef41Sopenharmony_ci
941cb0ef41Sopenharmony_ci  textConverted () {
951cb0ef41Sopenharmony_ci    return this[CONSUME_BODY]().then(buf => convertBody(buf, this.headers))
961cb0ef41Sopenharmony_ci  }
971cb0ef41Sopenharmony_ci
981cb0ef41Sopenharmony_ci  [CONSUME_BODY] () {
991cb0ef41Sopenharmony_ci    if (this[INTERNALS].disturbed) {
1001cb0ef41Sopenharmony_ci      return Promise.reject(new TypeError(`body used already for: ${
1011cb0ef41Sopenharmony_ci        this.url}`))
1021cb0ef41Sopenharmony_ci    }
1031cb0ef41Sopenharmony_ci
1041cb0ef41Sopenharmony_ci    this[INTERNALS].disturbed = true
1051cb0ef41Sopenharmony_ci
1061cb0ef41Sopenharmony_ci    if (this[INTERNALS].error) {
1071cb0ef41Sopenharmony_ci      return Promise.reject(this[INTERNALS].error)
1081cb0ef41Sopenharmony_ci    }
1091cb0ef41Sopenharmony_ci
1101cb0ef41Sopenharmony_ci    // body is null
1111cb0ef41Sopenharmony_ci    if (this.body === null) {
1121cb0ef41Sopenharmony_ci      return Promise.resolve(Buffer.alloc(0))
1131cb0ef41Sopenharmony_ci    }
1141cb0ef41Sopenharmony_ci
1151cb0ef41Sopenharmony_ci    if (Buffer.isBuffer(this.body)) {
1161cb0ef41Sopenharmony_ci      return Promise.resolve(this.body)
1171cb0ef41Sopenharmony_ci    }
1181cb0ef41Sopenharmony_ci
1191cb0ef41Sopenharmony_ci    const upstream = isBlob(this.body) ? this.body.stream() : this.body
1201cb0ef41Sopenharmony_ci
1211cb0ef41Sopenharmony_ci    /* istanbul ignore if: should never happen */
1221cb0ef41Sopenharmony_ci    if (!Minipass.isStream(upstream)) {
1231cb0ef41Sopenharmony_ci      return Promise.resolve(Buffer.alloc(0))
1241cb0ef41Sopenharmony_ci    }
1251cb0ef41Sopenharmony_ci
1261cb0ef41Sopenharmony_ci    const stream = this.size && upstream instanceof MinipassSized ? upstream
1271cb0ef41Sopenharmony_ci      : !this.size && upstream instanceof Minipass &&
1281cb0ef41Sopenharmony_ci        !(upstream instanceof MinipassSized) ? upstream
1291cb0ef41Sopenharmony_ci      : this.size ? new MinipassSized({ size: this.size })
1301cb0ef41Sopenharmony_ci      : new Minipass()
1311cb0ef41Sopenharmony_ci
1321cb0ef41Sopenharmony_ci    // allow timeout on slow response body, but only if the stream is still writable. this
1331cb0ef41Sopenharmony_ci    // makes the timeout center on the socket stream from lib/index.js rather than the
1341cb0ef41Sopenharmony_ci    // intermediary minipass stream we create to receive the data
1351cb0ef41Sopenharmony_ci    const resTimeout = this.timeout && stream.writable ? setTimeout(() => {
1361cb0ef41Sopenharmony_ci      stream.emit('error', new FetchError(
1371cb0ef41Sopenharmony_ci        `Response timeout while trying to fetch ${
1381cb0ef41Sopenharmony_ci          this.url} (over ${this.timeout}ms)`, 'body-timeout'))
1391cb0ef41Sopenharmony_ci    }, this.timeout) : null
1401cb0ef41Sopenharmony_ci
1411cb0ef41Sopenharmony_ci    // do not keep the process open just for this timeout, even
1421cb0ef41Sopenharmony_ci    // though we expect it'll get cleared eventually.
1431cb0ef41Sopenharmony_ci    if (resTimeout && resTimeout.unref) {
1441cb0ef41Sopenharmony_ci      resTimeout.unref()
1451cb0ef41Sopenharmony_ci    }
1461cb0ef41Sopenharmony_ci
1471cb0ef41Sopenharmony_ci    // do the pipe in the promise, because the pipe() can send too much
1481cb0ef41Sopenharmony_ci    // data through right away and upset the MP Sized object
1491cb0ef41Sopenharmony_ci    return new Promise((resolve, reject) => {
1501cb0ef41Sopenharmony_ci      // if the stream is some other kind of stream, then pipe through a MP
1511cb0ef41Sopenharmony_ci      // so we can collect it more easily.
1521cb0ef41Sopenharmony_ci      if (stream !== upstream) {
1531cb0ef41Sopenharmony_ci        upstream.on('error', er => stream.emit('error', er))
1541cb0ef41Sopenharmony_ci        upstream.pipe(stream)
1551cb0ef41Sopenharmony_ci      }
1561cb0ef41Sopenharmony_ci      resolve()
1571cb0ef41Sopenharmony_ci    }).then(() => stream.concat()).then(buf => {
1581cb0ef41Sopenharmony_ci      clearTimeout(resTimeout)
1591cb0ef41Sopenharmony_ci      return buf
1601cb0ef41Sopenharmony_ci    }).catch(er => {
1611cb0ef41Sopenharmony_ci      clearTimeout(resTimeout)
1621cb0ef41Sopenharmony_ci      // request was aborted, reject with this Error
1631cb0ef41Sopenharmony_ci      if (er.name === 'AbortError' || er.name === 'FetchError') {
1641cb0ef41Sopenharmony_ci        throw er
1651cb0ef41Sopenharmony_ci      } else if (er.name === 'RangeError') {
1661cb0ef41Sopenharmony_ci        throw new FetchError(`Could not create Buffer from response body for ${
1671cb0ef41Sopenharmony_ci          this.url}: ${er.message}`, 'system', er)
1681cb0ef41Sopenharmony_ci      } else {
1691cb0ef41Sopenharmony_ci        // other errors, such as incorrect content-encoding or content-length
1701cb0ef41Sopenharmony_ci        throw new FetchError(`Invalid response body while trying to fetch ${
1711cb0ef41Sopenharmony_ci          this.url}: ${er.message}`, 'system', er)
1721cb0ef41Sopenharmony_ci      }
1731cb0ef41Sopenharmony_ci    })
1741cb0ef41Sopenharmony_ci  }
1751cb0ef41Sopenharmony_ci
1761cb0ef41Sopenharmony_ci  static clone (instance) {
1771cb0ef41Sopenharmony_ci    if (instance.bodyUsed) {
1781cb0ef41Sopenharmony_ci      throw new Error('cannot clone body after it is used')
1791cb0ef41Sopenharmony_ci    }
1801cb0ef41Sopenharmony_ci
1811cb0ef41Sopenharmony_ci    const body = instance.body
1821cb0ef41Sopenharmony_ci
1831cb0ef41Sopenharmony_ci    // check that body is a stream and not form-data object
1841cb0ef41Sopenharmony_ci    // NB: can't clone the form-data object without having it as a dependency
1851cb0ef41Sopenharmony_ci    if (Minipass.isStream(body) && typeof body.getBoundary !== 'function') {
1861cb0ef41Sopenharmony_ci      // create a dedicated tee stream so that we don't lose data
1871cb0ef41Sopenharmony_ci      // potentially sitting in the body stream's buffer by writing it
1881cb0ef41Sopenharmony_ci      // immediately to p1 and not having it for p2.
1891cb0ef41Sopenharmony_ci      const tee = new Minipass()
1901cb0ef41Sopenharmony_ci      const p1 = new Minipass()
1911cb0ef41Sopenharmony_ci      const p2 = new Minipass()
1921cb0ef41Sopenharmony_ci      tee.on('error', er => {
1931cb0ef41Sopenharmony_ci        p1.emit('error', er)
1941cb0ef41Sopenharmony_ci        p2.emit('error', er)
1951cb0ef41Sopenharmony_ci      })
1961cb0ef41Sopenharmony_ci      body.on('error', er => tee.emit('error', er))
1971cb0ef41Sopenharmony_ci      tee.pipe(p1)
1981cb0ef41Sopenharmony_ci      tee.pipe(p2)
1991cb0ef41Sopenharmony_ci      body.pipe(tee)
2001cb0ef41Sopenharmony_ci      // set instance body to one fork, return the other
2011cb0ef41Sopenharmony_ci      instance[INTERNALS].body = p1
2021cb0ef41Sopenharmony_ci      return p2
2031cb0ef41Sopenharmony_ci    } else {
2041cb0ef41Sopenharmony_ci      return instance.body
2051cb0ef41Sopenharmony_ci    }
2061cb0ef41Sopenharmony_ci  }
2071cb0ef41Sopenharmony_ci
2081cb0ef41Sopenharmony_ci  static extractContentType (body) {
2091cb0ef41Sopenharmony_ci    return body === null || body === undefined ? null
2101cb0ef41Sopenharmony_ci      : typeof body === 'string' ? 'text/plain;charset=UTF-8'
2111cb0ef41Sopenharmony_ci      : isURLSearchParams(body)
2121cb0ef41Sopenharmony_ci        ? 'application/x-www-form-urlencoded;charset=UTF-8'
2131cb0ef41Sopenharmony_ci        : isBlob(body) ? body.type || null
2141cb0ef41Sopenharmony_ci        : Buffer.isBuffer(body) ? null
2151cb0ef41Sopenharmony_ci        : Object.prototype.toString.call(body) === '[object ArrayBuffer]' ? null
2161cb0ef41Sopenharmony_ci        : ArrayBuffer.isView(body) ? null
2171cb0ef41Sopenharmony_ci        : typeof body.getBoundary === 'function'
2181cb0ef41Sopenharmony_ci          ? `multipart/form-data;boundary=${body.getBoundary()}`
2191cb0ef41Sopenharmony_ci          : Minipass.isStream(body) ? null
2201cb0ef41Sopenharmony_ci          : 'text/plain;charset=UTF-8'
2211cb0ef41Sopenharmony_ci  }
2221cb0ef41Sopenharmony_ci
2231cb0ef41Sopenharmony_ci  static getTotalBytes (instance) {
2241cb0ef41Sopenharmony_ci    const { body } = instance
2251cb0ef41Sopenharmony_ci    return (body === null || body === undefined) ? 0
2261cb0ef41Sopenharmony_ci      : isBlob(body) ? body.size
2271cb0ef41Sopenharmony_ci      : Buffer.isBuffer(body) ? body.length
2281cb0ef41Sopenharmony_ci      : body && typeof body.getLengthSync === 'function' && (
2291cb0ef41Sopenharmony_ci        // detect form data input from form-data module
2301cb0ef41Sopenharmony_ci        body._lengthRetrievers &&
2311cb0ef41Sopenharmony_ci        /* istanbul ignore next */ body._lengthRetrievers.length === 0 || // 1.x
2321cb0ef41Sopenharmony_ci        body.hasKnownLength && body.hasKnownLength()) // 2.x
2331cb0ef41Sopenharmony_ci        ? body.getLengthSync()
2341cb0ef41Sopenharmony_ci        : null
2351cb0ef41Sopenharmony_ci  }
2361cb0ef41Sopenharmony_ci
2371cb0ef41Sopenharmony_ci  static writeToStream (dest, instance) {
2381cb0ef41Sopenharmony_ci    const { body } = instance
2391cb0ef41Sopenharmony_ci
2401cb0ef41Sopenharmony_ci    if (body === null || body === undefined) {
2411cb0ef41Sopenharmony_ci      dest.end()
2421cb0ef41Sopenharmony_ci    } else if (Buffer.isBuffer(body) || typeof body === 'string') {
2431cb0ef41Sopenharmony_ci      dest.end(body)
2441cb0ef41Sopenharmony_ci    } else {
2451cb0ef41Sopenharmony_ci      // body is stream or blob
2461cb0ef41Sopenharmony_ci      const stream = isBlob(body) ? body.stream() : body
2471cb0ef41Sopenharmony_ci      stream.on('error', er => dest.emit('error', er)).pipe(dest)
2481cb0ef41Sopenharmony_ci    }
2491cb0ef41Sopenharmony_ci
2501cb0ef41Sopenharmony_ci    return dest
2511cb0ef41Sopenharmony_ci  }
2521cb0ef41Sopenharmony_ci}
2531cb0ef41Sopenharmony_ci
2541cb0ef41Sopenharmony_ciObject.defineProperties(Body.prototype, {
2551cb0ef41Sopenharmony_ci  body: { enumerable: true },
2561cb0ef41Sopenharmony_ci  bodyUsed: { enumerable: true },
2571cb0ef41Sopenharmony_ci  arrayBuffer: { enumerable: true },
2581cb0ef41Sopenharmony_ci  blob: { enumerable: true },
2591cb0ef41Sopenharmony_ci  json: { enumerable: true },
2601cb0ef41Sopenharmony_ci  text: { enumerable: true },
2611cb0ef41Sopenharmony_ci})
2621cb0ef41Sopenharmony_ci
2631cb0ef41Sopenharmony_ciconst isURLSearchParams = obj =>
2641cb0ef41Sopenharmony_ci  // Duck-typing as a necessary condition.
2651cb0ef41Sopenharmony_ci  (typeof obj !== 'object' ||
2661cb0ef41Sopenharmony_ci    typeof obj.append !== 'function' ||
2671cb0ef41Sopenharmony_ci    typeof obj.delete !== 'function' ||
2681cb0ef41Sopenharmony_ci    typeof obj.get !== 'function' ||
2691cb0ef41Sopenharmony_ci    typeof obj.getAll !== 'function' ||
2701cb0ef41Sopenharmony_ci    typeof obj.has !== 'function' ||
2711cb0ef41Sopenharmony_ci    typeof obj.set !== 'function') ? false
2721cb0ef41Sopenharmony_ci  // Brand-checking and more duck-typing as optional condition.
2731cb0ef41Sopenharmony_ci  : obj.constructor.name === 'URLSearchParams' ||
2741cb0ef41Sopenharmony_ci    Object.prototype.toString.call(obj) === '[object URLSearchParams]' ||
2751cb0ef41Sopenharmony_ci    typeof obj.sort === 'function'
2761cb0ef41Sopenharmony_ci
2771cb0ef41Sopenharmony_ciconst isBlob = obj =>
2781cb0ef41Sopenharmony_ci  typeof obj === 'object' &&
2791cb0ef41Sopenharmony_ci  typeof obj.arrayBuffer === 'function' &&
2801cb0ef41Sopenharmony_ci  typeof obj.type === 'string' &&
2811cb0ef41Sopenharmony_ci  typeof obj.stream === 'function' &&
2821cb0ef41Sopenharmony_ci  typeof obj.constructor === 'function' &&
2831cb0ef41Sopenharmony_ci  typeof obj.constructor.name === 'string' &&
2841cb0ef41Sopenharmony_ci  /^(Blob|File)$/.test(obj.constructor.name) &&
2851cb0ef41Sopenharmony_ci  /^(Blob|File)$/.test(obj[Symbol.toStringTag])
2861cb0ef41Sopenharmony_ci
2871cb0ef41Sopenharmony_ciconst convertBody = (buffer, headers) => {
2881cb0ef41Sopenharmony_ci  /* istanbul ignore if */
2891cb0ef41Sopenharmony_ci  if (typeof convert !== 'function') {
2901cb0ef41Sopenharmony_ci    throw new Error('The package `encoding` must be installed to use the textConverted() function')
2911cb0ef41Sopenharmony_ci  }
2921cb0ef41Sopenharmony_ci
2931cb0ef41Sopenharmony_ci  const ct = headers && headers.get('content-type')
2941cb0ef41Sopenharmony_ci  let charset = 'utf-8'
2951cb0ef41Sopenharmony_ci  let res
2961cb0ef41Sopenharmony_ci
2971cb0ef41Sopenharmony_ci  // header
2981cb0ef41Sopenharmony_ci  if (ct) {
2991cb0ef41Sopenharmony_ci    res = /charset=([^;]*)/i.exec(ct)
3001cb0ef41Sopenharmony_ci  }
3011cb0ef41Sopenharmony_ci
3021cb0ef41Sopenharmony_ci  // no charset in content type, peek at response body for at most 1024 bytes
3031cb0ef41Sopenharmony_ci  const str = buffer.slice(0, 1024).toString()
3041cb0ef41Sopenharmony_ci
3051cb0ef41Sopenharmony_ci  // html5
3061cb0ef41Sopenharmony_ci  if (!res && str) {
3071cb0ef41Sopenharmony_ci    res = /<meta.+?charset=(['"])(.+?)\1/i.exec(str)
3081cb0ef41Sopenharmony_ci  }
3091cb0ef41Sopenharmony_ci
3101cb0ef41Sopenharmony_ci  // html4
3111cb0ef41Sopenharmony_ci  if (!res && str) {
3121cb0ef41Sopenharmony_ci    res = /<meta[\s]+?http-equiv=(['"])content-type\1[\s]+?content=(['"])(.+?)\2/i.exec(str)
3131cb0ef41Sopenharmony_ci
3141cb0ef41Sopenharmony_ci    if (!res) {
3151cb0ef41Sopenharmony_ci      res = /<meta[\s]+?content=(['"])(.+?)\1[\s]+?http-equiv=(['"])content-type\3/i.exec(str)
3161cb0ef41Sopenharmony_ci      if (res) {
3171cb0ef41Sopenharmony_ci        res.pop()
3181cb0ef41Sopenharmony_ci      } // drop last quote
3191cb0ef41Sopenharmony_ci    }
3201cb0ef41Sopenharmony_ci
3211cb0ef41Sopenharmony_ci    if (res) {
3221cb0ef41Sopenharmony_ci      res = /charset=(.*)/i.exec(res.pop())
3231cb0ef41Sopenharmony_ci    }
3241cb0ef41Sopenharmony_ci  }
3251cb0ef41Sopenharmony_ci
3261cb0ef41Sopenharmony_ci  // xml
3271cb0ef41Sopenharmony_ci  if (!res && str) {
3281cb0ef41Sopenharmony_ci    res = /<\?xml.+?encoding=(['"])(.+?)\1/i.exec(str)
3291cb0ef41Sopenharmony_ci  }
3301cb0ef41Sopenharmony_ci
3311cb0ef41Sopenharmony_ci  // found charset
3321cb0ef41Sopenharmony_ci  if (res) {
3331cb0ef41Sopenharmony_ci    charset = res.pop()
3341cb0ef41Sopenharmony_ci
3351cb0ef41Sopenharmony_ci    // prevent decode issues when sites use incorrect encoding
3361cb0ef41Sopenharmony_ci    // ref: https://hsivonen.fi/encoding-menu/
3371cb0ef41Sopenharmony_ci    if (charset === 'gb2312' || charset === 'gbk') {
3381cb0ef41Sopenharmony_ci      charset = 'gb18030'
3391cb0ef41Sopenharmony_ci    }
3401cb0ef41Sopenharmony_ci  }
3411cb0ef41Sopenharmony_ci
3421cb0ef41Sopenharmony_ci  // turn raw buffers into a single utf-8 buffer
3431cb0ef41Sopenharmony_ci  return convert(
3441cb0ef41Sopenharmony_ci    buffer,
3451cb0ef41Sopenharmony_ci    'UTF-8',
3461cb0ef41Sopenharmony_ci    charset
3471cb0ef41Sopenharmony_ci  ).toString()
3481cb0ef41Sopenharmony_ci}
3491cb0ef41Sopenharmony_ci
3501cb0ef41Sopenharmony_cimodule.exports = Body
351