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 : }
|