LCOV - code coverage report
Current view: top level - lib/helper - whatwg-fetch.js (source / functions) Hit Total Coverage
Test: lcov.info Lines: 497 517 96.1 %
Date: 2025-01-24 02:02:48 Functions: 7 10 70.0 %
Branches: 56 58 96.6 %

           Branch data     Line data    Source code
       1            [ + ]:        229 : /**
       2                 :        229 :  * Fetch Living Standard — Last Updated 7 June 2023
       3                 :        229 :  * url: https://fetch.spec.whatwg.org/#forbidden-request-header
       4                 :        229 :  *
       5                 :        229 :  * @module  whatwg-fetch
       6                 :        229 :  * @desc    Helper module - Helper functions for the main module {@link module:whatwg-xhr whatwg-xhr}.
       7                 :        229 :  * @version 1.0.0
       8                 :        229 :  * @author  Essam A. El-Sherif
       9                 :        229 :  */
      10                 :        229 : 
      11                 :        229 : /* Import nodeJS core modules */
      12                 :        229 : import assert               from 'node:assert';
      13                 :        229 : import { Blob }             from 'node:buffer';
      14                 :        229 : import { Readable, Duplex } from 'node:stream';
      15                 :        229 : import { TextEncoder }      from 'node:util';
      16                 :        229 : import { URLSearchParams }  from 'node:url';
      17                 :        229 : 
      18                 :        229 : /* Import from whatwg-xhr main module */
      19                 :        229 : import { FormData }           from '../whatwg-xhr.js';
      20                 :        229 : import { disableHeaderCheck } from '../whatwg-xhr.js';
      21                 :        229 : 
      22                 :        229 : /**
      23                 :        229 :  * @const {Set} forbiddenRequestHeaderName
      24                 :        229 :  * @desc  Set of forbidden request-headers, as defined by {@link https://fetch.spec.whatwg.org/#forbidden-request-header WHATWG Fetch Living Standard}.
      25                 :        229 :  */
      26                 :        229 : const forbiddenRequestHeaderName = new Set([
      27                 :        229 :         'accept-charset',
      28                 :        229 :         'accept-encoding',
      29                 :        229 :         'access-control-request-headers',
      30                 :        229 :         'access-control-request-method',
      31                 :        229 :         'connection',
      32                 :        229 :         'content-length',
      33                 :        229 :         'cookie',
      34                 :        229 :         'cookie2',
      35                 :        229 :         'date',
      36                 :        229 :         'dnt',
      37                 :        229 :         'expect',
      38                 :        229 :         'host',
      39                 :        229 :         'keep-alive',
      40                 :        229 :         'origin',
      41                 :        229 :         'referer',
      42                 :        229 :         'set-cookie',
      43                 :        229 :         'te',
      44                 :        229 :         'trailer',
      45                 :        229 :         'transfer-encoding',
      46                 :        229 :         'upgrade',
      47                 :        229 :         'via'
      48                 :        229 : ]);
      49                 :        229 : 
      50                 :        229 : /**
      51                 :        229 :  * Fetch Living Standard — Last Updated 7 June 2023
      52                 :        229 :  * url: https://fetch.spec.whatwg.org/#forbidden-request-header
      53                 :        229 :  *
      54                 :        229 :  * A header (name, value) is forbidden request-header if these steps return true:
      55                 :        229 :  *     1. If name is a byte-case-insensitive match for one of:
      56                 :        229 :  *         `Accept-Charset`
      57                 :        229 :  *         `Accept-Encoding`
      58                 :        229 :  *         `Access-Control-Request-Headers`
      59                 :        229 :  *         `Access-Control-Request-Method`
      60                 :        229 :  *         `Connection`
      61                 :        229 :  *         `Content-Length`
      62                 :        229 :  *         `Cookie`
      63                 :        229 :  *         `Cookie2`
      64                 :        229 :  *         `Date`
      65                 :        229 :  *         `DNT`
      66                 :        229 :  *         `Expect`
      67                 :        229 :  *         `Host`
      68                 :        229 :  *         `Keep-Alive`
      69                 :        229 :  *         `Origin`
      70                 :        229 :  *         `Referer`
      71                 :        229 :  *         `Set-Cookie`
      72                 :        229 :  *         `TE`
      73                 :        229 :  *         `Trailer`
      74                 :        229 :  *         `Transfer-Encoding`
      75                 :        229 :  *         `Upgrade`
      76                 :        229 :  *         `Via`
      77                 :        229 :  *     then return true.
      78                 :        229 :  *
      79                 :        229 :  *     2. If name when byte-lowercased starts with `proxy-` or `sec-`, then return true.
      80                 :        229 :  *
      81                 :        229 :  *     3. If name is a byte-case-insensitive match for one of:
      82                 :        229 :  *         `X-HTTP-Method`
      83                 :        229 :  *         `X-HTTP-Method-Override`
      84                 :        229 :  *         `X-Method-Override`
      85                 :        229 :  *     then:
      86                 :        229 :  *         1. Let parsedValues be the result of getting, decoding, and splitting value.
      87                 :        229 :  *         2. For each method of parsedValues: if the isomorphic encoding of method is
      88                 :        229 :  *            a forbidden method, then return true.
      89                 :        229 :  *
      90                 :        229 :  *     4. Return false.
      91                 :        229 :  *
      92                 :        229 :  *     Note: These are forbidden so the user agent remains in full control over them.
      93                 :        229 :  *     Header names starting with `Sec-` are reserved to allow new headers to be minted
      94                 :        229 :  *     that are safe from APIs using fetch that allow control over headers by developers,
      95                 :        229 :  *     such as XMLHttpRequest. [XHR]
      96                 :        229 :  *     The `Set-Cookie` header is semantically a response header, so it is not useful on
      97                 :        229 :  *     requests. Because `Set-Cookie` headers cannot be combined, they require more complex
      98                 :        229 :  *     handling in the Headers object. It is forbidden here to avoid leaking this complexity
      99                 :        229 :  *     into requests.
     100                 :        229 :  *
     101                 :        229 :  * @func     forbiddenRequestHeader
     102                 :        229 :  * @static
     103                 :        229 :  * @param    {string}  name  - The name of the Http request header.
     104                 :        229 :  * @param    {string}  value - The value of the Http request header.
     105                 :        229 :  * @return   {boolean} true if the Http request header is forbidden, otherwise false.
     106                 :        229 :  * @desc     To determine if the Http request header given by name and value is forbidden by {@link https://fetch.spec.whatwg.org/#forbidden-request-header WHATWG Fetch Living Standard}.
     107                 :        229 :  * @requires module:whatwg-xhr.disableHeaderCheck
     108                 :        229 :  */
     109            [ + ]:        229 : export function forbiddenRequestHeader(name, value){
     110                 :       2354 : 
     111                 :       2354 :         if(disableHeaderCheck)
     112            [ - ]:       2354 :                 return false;
     113                 :       2354 : 
     114                 :       2354 :         // fetch.spec.1. If name is a byte-case-insensitive match for one of the forbiddenRequestHeaderName set, then return true.
     115                 :       2354 :         if(!name || forbiddenRequestHeaderName.has(name.toLowerCase()))
     116       [ + ][ + ]:       2354 :                 return true;
     117                 :       1512 : 
     118                 :       1512 :         // fetch.spec.2. If name when byte-lowercased starts with `proxy-` or `sec-`, then return true.
     119            [ + ]:       2354 :         if(name.toLowerCase().startsWith('proxy-') || name.toLowerCase().startsWith('sec-'))
     120       [ + ][ + ]:       2354 :                 return true;
     121                 :       1507 : 
     122                 :       1507 :         // fetch.spec.3. If name is a byte-case-insensitive match for one of: `X-HTTP-Method`, `X-HTTP-Method-Override`, `X-Method-Override`
     123            [ + ]:       2354 :         if(['x-http-method', 'x-http-method-override', 'x-method-override'].includes(name.toLowerCase())){
     124                 :        144 : 
     125                 :        144 :                 // fetch.spec.3.1. Let parsedValues be the result of getting, decoding, and splitting value.
     126            [ + ]:        144 :                 const parsedValues = value.split(',').map(e => e.trim());
     127                 :        144 : 
     128                 :        144 :                 // fetch.spec.3.2. For each method of parsedValues: if the isomorphic encoding of method is a forbidden method, then return true.
     129                 :        144 :                 for(let method of parsedValues)
     130            [ + ]:        144 :                         if(forbiddenRequestHeaderName.has(method.toLowerCase()))
     131                 :        138 :                                 return true;
     132            [ + ]:        144 :         }
     133                 :       1507 :         return false;
     134                 :        229 : };
     135                 :        229 : 
     136                 :        229 : /**
     137                 :        229 :  * Fetch Living Standard — Last Updated 6 March 2023
     138                 :        229 :  * url: https://fetch.spec.whatwg.org/#methods
     139                 :        229 :  *
     140                 :        229 :  * A method is a byte sequence that matches the method token production.
     141                 :        229 :  * A CORS-safelisted method is a method that is `GET`, `HEAD`, or `POST`.
     142                 :        229 :  * A forbidden method is a method that is a byte-case-insensitive match for `CONNECT`, `TRACE`, or `TRACK`.
     143                 :        229 :  *
     144                 :        229 :  * To normalize a method, if it is a byte-case-insensitive match for `DELETE`, `GET`, `HEAD`, `OPTIONS`, `POST`, or `PUT`, byte-uppercase it.
     145                 :        229 :  *
     146                 :        229 :  * Normalization is done for backwards compatibility and consistency across APIs as methods are actually "case-sensitive".
     147                 :        229 :  * Using `patch` is highly likely to result in a `405 Method Not Allowed`. `PATCH` is much more likely to succeed.
     148                 :        229 :  * There are no restrictions on methods. `CHICKEN` is perfectly acceptable (and not a misspelling of `CHECKIN`). Other than those that are normalized
     149                 :        229 :  * there are no casing restrictions either. `Egg` or `eGg` would be fine, though uppercase is encouraged for consistency.
     150                 :        229 :  *
     151                 :        229 :  * @const {Set} corsSafelistedHttpRequestMethod
     152                 :        229 :  * @static
     153                 :        229 :  * @desc   A CORS-safelisted method is a method that is `GET`, `HEAD`, or `POST`, as defined by {@link https://fetch.spec.whatwg.org/#methods WHATWG Fetch Living Standard}.
     154                 :        229 :  */
     155                 :        229 : export const corsSafelistedHttpRequestMethod = new Set(['get', 'head', 'post']);
     156                 :        229 : 
     157                 :        229 : /**
     158                 :        229 :  * @const {Set} forbiddenHttpRequestMethod
     159                 :        229 :  * @static
     160                 :        229 :  * @desc   A forbidden method is a method that is a byte-case-insensitive match for `CONNECT`, `TRACE`, or `TRACK`, as defined by {@link https://fetch.spec.whatwg.org/#methods WHATWG Fetch Living Standard}..
     161                 :        229 :  */
     162                 :        229 : export const forbiddenHttpRequestMethod = new Set(['connect', 'trace', 'track']);
     163                 :        229 : 
     164                 :        229 : /* node:coverage disable ***/
     165                 :        229 : 
     166                 :        229 : /**
     167                 :        229 :  * Fetch Living Standard — Last Updated 7 June 2023
     168                 :        229 :  * url: https://fetch.spec.whatwg.org/#bodyinit-unions
     169                 :        229 :  *
     170                 :        229 :  * BodyInit unions
     171                 :        229 :  *   typedef (Blob or BufferSource or FormData or URLSearchParams or USVString) XMLHttpRequestBodyInit;
     172                 :        229 :  *   typedef (ReadableStream or XMLHttpRequestBodyInit) BodyInit;
     173                 :        229 :  *
     174                 :        229 :  * @func      isXMLHttpRequestBodyInit
     175                 :        229 :  * @param     {Blob|ArrayBuffer|Uint8Array|DataView|FormData|URLSearchParams|string} body - The request body.
     176                 :        229 :  * @return    {boolean} true if the typeof request body is one of the above types, otherwise false.
     177                 :        229 :  * @desc      To determine the typeof request body, as defined by {@link https://fetch.spec.whatwg.org/#bodyinit-unions WHATWG Fetch Living Standard}.
     178                 :        229 :  * @requires  module:whatwg-xhr.FormData
     179                 :        229 :  */
     180                 :          0 : function isXMLHttpRequestBodyInit(body){
     181                 :          0 : 
     182                 :          0 :         return body instanceof Blob            ||
     183                 :          0 : 
     184                 :          0 :                body instanceof ArrayBuffer     ||
     185                 :          0 :                body instanceof Uint8Array      ||
     186                 :          0 :                body instanceof DataView        ||
     187                 :          0 : 
     188                 :          0 :                body instanceof FormData        ||
     189                 :          0 :                body instanceof URLSearchParams ||
     190                 :          0 : 
     191                 :          0 :                typeof body === 'string';
     192                 :          0 : }
     193                 :        229 : 
     194                 :        229 : /**
     195                 :        229 :  * @func     isXMLHttpRequestBodyInit
     196                 :        229 :  * @param    {Blob|ArrayBuffer|Uint8Array|DataView|FormData|URLSearchParams|string|ReadableStream} body - The request body.
     197                 :        229 :  * @return   {boolean} true if the typeof request body is one of the above types, otherwise false.
     198                 :        229 :  * @desc     To determine the typeof request body, as defined by {@link https://fetch.spec.whatwg.org/#bodyinit-unions WHATWG Fetch Living Standard}.
     199                 :        229 :  */
     200                 :          0 : function isBodyInit(body){
     201                 :          0 : 
     202                 :          0 :         return body instanceof Readable ||
     203                 :          0 :                isXMLHttpRequestBodyInit(body);
     204                 :          0 : }
     205                 :        229 : 
     206                 :        229 : /* node:coverage enable ***/
     207                 :        229 : 
     208                 :        229 : /**
     209                 :        229 :  * Fetch Living Standard — Last Updated 7 June 2023
     210                 :        229 :  * url: https://fetch.spec.whatwg.org/#bodyinit-safely-extract
     211                 :        229 :  *
     212                 :        229 :  * To safely extract a body with type from a byte sequence or BodyInit object object,
     213                 :        229 :  * run these steps:
     214                 :        229 :  *   1. If object is a ReadableStream object, then:
     215                 :        229 :  *      1. Assert: object is neither disturbed nor locked.
     216                 :        229 :  *   2. Return the result of extracting object.
     217                 :        229 :  * Note: The safely extract operation is a subset of the extract operation that is
     218                 :        229 :  * guaranteed to not throw an exception.
     219                 :        229 :  *
     220                 :        229 :  * To extract a body with type from a byte sequence or BodyInit object object, with an
     221                 :        229 :  * optional boolean keepalive (default false), run these steps:
     222                 :        229 :  *   1. Let stream be null.
     223                 :        229 :  *   2. If object is a ReadableStream object, then set stream to object.
     224                 :        229 :  *   3. Otherwise, if object is a Blob object, set stream to the result of running
     225                 :        229 :  *      object’s get stream.
     226                 :        229 :  *   4. Otherwise, set stream to a new ReadableStream object, and set up stream with
     227                 :        229 :  *      byte reading support.
     228                 :        229 :  *   5. Assert: stream is a ReadableStream object.
     229                 :        229 :  *   6. Let action be null.
     230                 :        229 :  *   7. Let source be null.
     231                 :        229 :  *   8. Let length be null.
     232                 :        229 :  *   9. Let type be null.
     233                 :        229 :  *  10. Switch on object:
     234                 :        229 :  *        Blob
     235                 :        229 :  *          Set source to object.
     236                 :        229 :  *          Set length to object’s size.
     237                 :        229 :  *          If object’s type attribute is not the empty byte sequence, set type to its value.
     238                 :        229 :  *        byte sequence
     239                 :        229 :  *          Set source to object.
     240                 :        229 :  *        BufferSource
     241                 :        229 :  *          Set source to a copy of the bytes held by object.
     242                 :        229 :  *        FormData
     243                 :        229 :  *          Set action to this step: run the multipart/form-data encoding algorithm, with object’s
     244                 :        229 :  *          entry list and UTF-8.
     245                 :        229 :  *          Set source to object.
     246                 :        229 :  *          Set length to unclear, see html/6424 for improving this.
     247                 :        229 :  *          Set type to `multipart/form-data; boundary=`, followed by the multipart/form-data
     248                 :        229 :  *          boundary string generated by the multipart/form-data encoding algorithm.
     249                 :        229 :  *        URLSearchParams
     250                 :        229 :  *          Set source to the result of running the application/x-www-form-urlencoded serializer
     251                 :        229 :  *          with object’s list.
     252                 :        229 :  *          Set type to `application/x-www-form-urlencoded;charset=UTF-8`.
     253                 :        229 :  *        scalar value string
     254                 :        229 :  *          Set source to the UTF-8 encoding of object.
     255                 :        229 :  *          Set type to `text/plain;charset=UTF-8`.
     256                 :        229 :  *        ReadableStream
     257                 :        229 :  *          If keepalive is true, then throw a TypeError.
     258                 :        229 :  *          If object is disturbed or locked, then throw a TypeError.
     259                 :        229 :  *  11. If source is a byte sequence, then set action to a step that returns source and
     260                 :        229 :  *      length to source’s length.
     261                 :        229 :  *  12. If action is non-null, then run these steps in parallel:
     262                 :        229 :  *          1. Run action.
     263                 :        229 :  *             Whenever one or more bytes are available and stream is not errored,
     264                 :        229 :  *             enqueue a Uint8Array wrapping an ArrayBuffer containing
     265                 :        229 :  *             the available bytes into stream.
     266                 :        229 :  *             When running action is done, close stream.
     267                 :        229 :  *  13. Let body be a body whose stream is stream, source is source, and length is length.
     268                 :        229 :  *  14. Return (body, type).
     269                 :        229 :  *
     270                 :        229 :  * @func     safelyExtractBodyWithType
     271                 :        229 :  * @static
     272                 :        229 :  * @param    {Blob|ArrayBuffer|Uint8Array|DataView|FormData|URLSearchParams|string|ReadableStream} body - The request body.
     273                 :        229 :  * @param    {boolean} keepAlive - (Optional).
     274                 :        229 :  * @return   {object}  Request body with type.
     275                 :        229 :  * @desc     To return the request body with type, as defined by {@link https://fetch.spec.whatwg.org/#bodyinit-safely-extract WHATWG Fetch Living Standard}.
     276                 :        229 :  * @requires module:whatwg-xhr.FormData
     277                 :        229 :  */
     278            [ + ]:        229 : export function safelyExtractBodyWithType(body, keepAlive = false){
     279                 :        134 : 
     280                 :        134 :         // fetch.spec.1. Let stream be null.
     281                 :        134 :         let stream = null;
     282                 :        134 : 
     283                 :        134 :         /* node:coverage disable ***/
     284                 :        134 :         // fetch.spec.2. If object is a ReadableStream object, then set stream to object.
     285            [ - ]:        134 :         if(body instanceof Readable){
     286                 :          0 :                 stream = body;
     287                 :          0 :         }
     288                 :        134 :         /* node:coverage enable ***/
     289                 :        134 :         else
     290                 :        134 :         // fetch.spec.3. Otherwise, if object is a Blob object, set stream to the result
     291                 :        134 :         //               of running object’s get stream.
     292            [ + ]:        134 :         if(body instanceof Blob){
     293                 :         17 :                 stream = Duplex.from(body);
     294            [ + ]:         17 :         }
     295                 :         68 :         else{
     296                 :         68 :         // fetch.spec.4. Otherwise, set stream to a new ReadableStream object, and set up
     297                 :         68 :         //               stream with byte reading support.
     298                 :         68 :                 stream = new Readable({
     299                 :         68 :                         read(){},
     300                 :         68 :                         objectMode: false,
     301                 :         68 :                 });
     302                 :         68 :         }
     303                 :        134 :         // fetch.spec.5. Assert: stream is a ReadableStream object.
     304                 :        134 :         {
     305                 :        134 :                 assert(stream.readable);
     306                 :        134 :         }
     307                 :        134 : 
     308                 :        134 :         // fetch.spec.6. Let action be null.
     309                 :        134 :         // fetch.spec.7. Let source be null.
     310                 :        134 :         // fetch.spec.8. Let length be null.
     311                 :        134 :         // fetch.spec.9. Let type be null.
     312                 :        134 :         let action = null, source = null, length = null, type = null;
     313                 :        134 : 
     314                 :        134 :         // @author....
     315                 :        134 :         let blobs = null;
     316                 :        134 : 
     317                 :        134 :         // fetch.spec.10. Switch on object: Blob, byte sequence, BufferSource, FormData,
     318                 :        134 :         //                URLSearchParams, scalar value string, ReadableStream
     319                 :        134 : 
     320                 :        134 :         // fetch.spec.10. Switch on object: Blob
     321            [ + ]:        134 :         if(body instanceof Blob){
     322                 :         17 :                 // stream: NOT null, action: null, source: null, length: null, type: null
     323                 :         17 : 
     324                 :         17 :                 // fetch.spec.10. Blob: Set source to object.
     325                 :         17 :                 source = body;
     326                 :         17 : 
     327                 :         17 :                 // fetch.spec.10. Blob: Set length to object’s size.
     328                 :         17 :                 length = body.size;
     329                 :         17 : 
     330                 :         17 :                 // fetch.spec.10. Blob: If object’s type attribute is not the empty byte sequence,
     331                 :         17 :                 //                      set type to its value.
     332       [ + ][ + ]:         17 :                 type = body.type ? body.type : type;
     333                 :         17 : 
     334                 :         17 :                 // stream: NOT null, action: null, source: NOT null, length: NOT null, type: <depends>
     335            [ + ]:         17 :         }
     336                 :         68 :         else
     337                 :         68 :         // fetch.spec.10. Switch on object: BufferSource
     338                 :         68 :         if(
     339            [ + ]:         68 :             body instanceof ArrayBuffer       ||
     340                 :         65 : 
     341            [ + ]:         68 :             body instanceof Uint8Array        ||
     342            [ + ]:         68 :             body instanceof Uint16Array       ||
     343            [ + ]:         68 :             body instanceof Uint32Array       ||
     344            [ + ]:         68 :             body instanceof Uint8ClampedArray ||
     345            [ + ]:         68 :             body instanceof Int8Array         ||
     346            [ + ]:         68 :             body instanceof Int16Array        ||
     347            [ + ]:         68 :             body instanceof Int32Array        ||
     348            [ + ]:         68 :             body instanceof Float32Array      ||
     349            [ + ]:         68 :             body instanceof Float64Array      ||
     350            [ + ]:         68 :             body instanceof DataView          ||
     351                 :         62 : 
     352                 :         62 :                 body instanceof Buffer
     353            [ + ]:         68 :         ){
     354                 :          6 :                 // fetch.spec.10. Set source to a copy of the bytes held by object.
     355                 :          6 :                 if(body instanceof Buffer)
     356                 :          6 :                         source = Buffer.from(body);
     357                 :          6 :                 else
     358                 :          6 :                 if(body instanceof ArrayBuffer)
     359            [ + ]:          6 :                         source = Buffer.from(Buffer.from(body));
     360                 :          3 :                 else
     361                 :          3 :                         source = Buffer.copyBytesFrom(body);
     362                 :          6 :                 // stream: NOT null, action: null, source: NOT null, length: null, type: null
     363                 :          6 : 
     364                 :          6 :                 // @revise
     365                 :          6 :                 length = source.length;
     366            [ + ]:          6 :         }
     367                 :         62 :         else
     368                 :         62 :         // fetch.spec.10. Switch on object: FormData
     369            [ + ]:         62 :         if(isFormDataLike(body)){
     370                 :          8 : 
     371                 :          8 :                 const boundary = `----formdata-whatwg-xhr-${Math.random()}`.replace('.', '').slice(0, 32);
     372                 :          8 :                 const prefix = `--${boundary}\r\nContent-Disposition: form-data`;
     373                 :          8 : 
     374                 :          8 :                 /*! formdata-polyfill. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
     375            [ + ]:          8 :                 const escape = (str) => {
     376                 :          7 :                         return str.replace(/\n/g, '%0A').replace(/\r/g, '%0D').replace(/"/g, '%22');
     377                 :          7 :                 }
     378                 :          8 : 
     379            [ + ]:          8 :                 const normalizeLinefeeds = (value) => {
     380                 :          5 :                         return value.replace(/\r?\n|\r/g, '\r\n');
     381                 :          5 :                 }
     382                 :          8 : 
     383                 :          8 :                 const enc = new TextEncoder();
     384                 :          8 :                 const blobParts = [];
     385                 :          8 :                 const rn = new Uint8Array([13, 10]);
     386                 :          8 : 
     387                 :          8 :                 length = 0;
     388                 :          8 : 
     389            [ + ]:          8 :                 for(const [name, value] of body){
     390            [ + ]:          4 :                         if(typeof value === 'string'){
     391                 :          1 :                                 const chunk = enc.encode(
     392                 :          1 :                                         `${prefix}; name="${escape(normalizeLinefeeds(name))}"` +
     393                 :          1 :                                         `\r\n\r\n${normalizeLinefeeds(value)}\r\n`
     394                 :          1 :                                 );
     395                 :          1 : 
     396                 :          1 :                                 blobParts.push(chunk);
     397                 :          1 :                                 length += chunk.byteLength;
     398            [ + ]:          1 :                         }
     399                 :          3 :                         else{
     400                 :          3 :                                 const chunk = enc.encode(
     401                 :          3 :                                         `${prefix}; name="${escape(normalizeLinefeeds(name))}"` +
     402                 :          3 :                                         (value.name ? `; filename="${escape(value.name)}"` : '') + '\r\n' +
     403                 :          3 :                                         `Content-Type: ${value.type || 'application/octet-stream'}\r\n\r\n`);
     404                 :          3 : 
     405                 :          3 :                                 blobParts.push(chunk, value, rn);
     406                 :          3 :                                 length += chunk.byteLength + value.size + rn.byteLength;
     407                 :          3 :                         }
     408                 :          4 :                 }
     409                 :          8 : 
     410                 :          8 :                 const chunk = enc.encode(`--${boundary}--`);
     411                 :          8 :                 blobParts.push(chunk);
     412                 :          8 :                 length += chunk.byteLength;
     413                 :          8 : 
     414                 :          8 :                 blobs = blobParts;
     415                 :          8 : 
     416                 :          8 :                 // fetch.spec.10. FormData: Set source to object.
     417                 :          8 :                 source = body;
     418                 :          8 : 
     419                 :          8 :                 // fetch.spec.10. FormData: Set action to this step:
     420                 :          8 :                 //                run the multipart/form-data encoding algorithm,
     421                 :          8 :                 //                with object’s entry list and UTF-8.
     422            [ + ]:          8 :                 action = async function *(){
     423            [ + ]:          8 :                         for(const part of blobParts){
     424            [ + ]:         15 :                                 if(part instanceof Blob){
     425                 :          3 :                                         yield await part.bytes();
     426            [ + ]:          3 :                                 }
     427                 :         12 :                                 else{
     428                 :         12 :                                         yield part;
     429                 :         12 :                                 }
     430            [ + ]:         15 :                         }
     431                 :          8 :                 };
     432                 :          8 : 
     433                 :          8 :                 // fetch.spec.10. FormData: Set type to `multipart/form-data; boundary=`,
     434                 :          8 :                 //                followed by the multipart/form-data boundary string
     435                 :          8 :                 //                generated by the multipart/form-data encoding algorithm.
     436                 :          8 :                 type = 'multipart/form-data; boundary=' + boundary;
     437                 :          8 : 
     438                 :          8 :                 // stream: NOT null, action: NOT null, source: NOT null, length: NOT null, type: NOT null
     439            [ + ]:          8 :         }
     440                 :         54 :         else
     441                 :         54 :         // fetch.spec.10. Switch on object: URLSearchParams
     442            [ + ]:         54 :         if(body instanceof URLSearchParams){
     443                 :          4 :                 // fetch.spec.10. Set source to the result of running the application/x-www-form-urlencoded serializer with object’s list.
     444                 :          4 :                 source = body.toString();
     445                 :          4 : 
     446                 :          4 :                 // fetch.spec.10. Set type to `application/x-www-form-urlencoded;charset=UTF-8`.
     447                 :          4 :                 type = 'application/x-www-form-urlencoded;charset=UTF-8';
     448                 :          4 :                 // stream: NOT null, action: null, source: NOT null, length: null, type: NOT null
     449                 :          4 : 
     450                 :          4 :                 // @revise
     451                 :          4 :                 length = source.length;
     452            [ + ]:          4 :         }
     453                 :         50 :         else
     454                 :         50 :         // fetch.spec.10. Switch on object: ReadableStream
     455                 :         50 :         if(body instanceof Readable){
     456                 :         50 :                 // fetch.spec.10. If keepalive is true, then throw a TypeError.
     457                 :         50 :                 // fetch.spec.10. If object is disturbed or locked, then throw a TypeError.
     458                 :         50 :                 // @todo...
     459                 :         50 :         }
     460                 :         50 :         else{
     461                 :         50 :                 // fetch.spec.10. Switch on object: scalar value string
     462                 :         50 :                 body = String(body);
     463                 :         50 : 
     464                 :         50 :                 // fetch.spec.10. Set source to the UTF-8 encoding of object.
     465                 :         50 :                 source = new TextEncoder().encode(body);
     466                 :         50 : 
     467                 :         50 :                 // fetch.spec.10. Set type to `text/plain;charset=UTF-8`.
     468                 :         50 :                 type = 'text/plain;charset=UTF-8';
     469                 :         50 : 
     470                 :         50 :                 // stream: NOT null, action: null, source: NOT null, length: null, type: NOT null
     471                 :         50 : 
     472                 :         50 :                 // @revise
     473                 :         50 :                 length = source.length;
     474                 :         50 :         }
     475                 :        134 : 
     476                 :        134 :         // fetch.spec.11. If source is a byte sequence, then set action to a step
     477                 :        134 :         //                that returns source and length to source’s length.
     478                 :        134 :                 // @todo...
     479                 :        134 : 
     480                 :        134 :         // fetch.spec.12. If action is non-null, then run these steps in parallel:
     481            [ + ]:        134 :         if(action){
     482                 :          8 :                 stream = Readable.from(action(), {objectMode: false});
     483                 :          8 :         }
     484                 :        134 : 
     485                 :        134 :         // fetch.spec.13. Let body be a body whose stream is stream, source is source,
     486                 :        134 :         //                and length is length.
     487                 :        134 :         body = {
     488                 :        134 :                 stream: stream,
     489                 :        134 :                 source: source,
     490                 :        134 :                 length: length,
     491                 :        134 :                 blobs: blobs,
     492                 :        134 :         };
     493                 :        134 : 
     494                 :        134 :         return {body, type};
     495                 :        134 : }
     496                 :        229 : 
     497                 :        229 : /**
     498                 :        229 :  * @func     isFormDataLike
     499                 :        229 :  * @param    {object} object - The object to check.
     500                 :        229 :  * @return   {boolean} true if the given object is an instanceof a FormData constructor, otherwise false.
     501                 :        229 :  * @desc     To determine if the given object is an instanceof a FormData constructor.
     502                 :        229 :  */
     503            [ + ]:         62 : function isFormDataLike(object){
     504                 :         62 :         return (
     505            [ + ]:         62 :                 object &&
     506                 :         61 : 
     507            [ + ]:         62 :                 typeof object        === 'object'   &&
     508            [ + ]:         62 :                 typeof object.append === 'function' &&
     509            [ + ]:         62 :                 typeof object.delete === 'function' &&
     510            [ + ]:         62 :                 typeof object.get    === 'function' &&
     511            [ + ]:         62 :                 typeof object.getAll === 'function' &&
     512            [ + ]:         62 :                 typeof object.has    === 'function' &&
     513            [ + ]:         62 :                 typeof object.set    === 'function' &&
     514                 :         12 : 
     515                 :         12 :                 object[Symbol.toStringTag] === 'FormData'
     516                 :         62 :         );
     517                 :         62 : }

Generated by: LCOV version 1.14