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