source: node_modules/node-fetch-commonjs/multipart-parser-25a14693.js

main
Last change on this file was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 9.7 KB
RevLine 
[d24f17c]1/* eslint-disable */
2'use strict';
3
4require('fs');
5require('path');
6require('node-domexception');
7var index = require('./index.js');
8require('http');
9require('https');
10require('zlib');
11require('stream');
12require('buffer');
13require('util');
14require('url');
15require('net');
16
17let s = 0;
18const S = {
19 START_BOUNDARY: s++,
20 HEADER_FIELD_START: s++,
21 HEADER_FIELD: s++,
22 HEADER_VALUE_START: s++,
23 HEADER_VALUE: s++,
24 HEADER_VALUE_ALMOST_DONE: s++,
25 HEADERS_ALMOST_DONE: s++,
26 PART_DATA_START: s++,
27 PART_DATA: s++,
28 END: s++
29};
30
31let f = 1;
32const F = {
33 PART_BOUNDARY: f,
34 LAST_BOUNDARY: f *= 2
35};
36
37const LF = 10;
38const CR = 13;
39const SPACE = 32;
40const HYPHEN = 45;
41const COLON = 58;
42const A = 97;
43const Z = 122;
44
45const lower = c => c | 0x20;
46
47const noop = () => {};
48
49class MultipartParser {
50 /**
51 * @param {string} boundary
52 */
53 constructor(boundary) {
54 this.index = 0;
55 this.flags = 0;
56
57 this.onHeaderEnd = noop;
58 this.onHeaderField = noop;
59 this.onHeadersEnd = noop;
60 this.onHeaderValue = noop;
61 this.onPartBegin = noop;
62 this.onPartData = noop;
63 this.onPartEnd = noop;
64
65 this.boundaryChars = {};
66
67 boundary = '\r\n--' + boundary;
68 const ui8a = new Uint8Array(boundary.length);
69 for (let i = 0; i < boundary.length; i++) {
70 ui8a[i] = boundary.charCodeAt(i);
71 this.boundaryChars[ui8a[i]] = true;
72 }
73
74 this.boundary = ui8a;
75 this.lookbehind = new Uint8Array(this.boundary.length + 8);
76 this.state = S.START_BOUNDARY;
77 }
78
79 /**
80 * @param {Uint8Array} data
81 */
82 write(data) {
83 let i = 0;
84 const length_ = data.length;
85 let previousIndex = this.index;
86 let {lookbehind, boundary, boundaryChars, index, state, flags} = this;
87 const boundaryLength = this.boundary.length;
88 const boundaryEnd = boundaryLength - 1;
89 const bufferLength = data.length;
90 let c;
91 let cl;
92
93 const mark = name => {
94 this[name + 'Mark'] = i;
95 };
96
97 const clear = name => {
98 delete this[name + 'Mark'];
99 };
100
101 const callback = (callbackSymbol, start, end, ui8a) => {
102 if (start === undefined || start !== end) {
103 this[callbackSymbol](ui8a && ui8a.subarray(start, end));
104 }
105 };
106
107 const dataCallback = (name, clear) => {
108 const markSymbol = name + 'Mark';
109 if (!(markSymbol in this)) {
110 return;
111 }
112
113 if (clear) {
114 callback(name, this[markSymbol], i, data);
115 delete this[markSymbol];
116 } else {
117 callback(name, this[markSymbol], data.length, data);
118 this[markSymbol] = 0;
119 }
120 };
121
122 for (i = 0; i < length_; i++) {
123 c = data[i];
124
125 switch (state) {
126 case S.START_BOUNDARY:
127 if (index === boundary.length - 2) {
128 if (c === HYPHEN) {
129 flags |= F.LAST_BOUNDARY;
130 } else if (c !== CR) {
131 return;
132 }
133
134 index++;
135 break;
136 } else if (index - 1 === boundary.length - 2) {
137 if (flags & F.LAST_BOUNDARY && c === HYPHEN) {
138 state = S.END;
139 flags = 0;
140 } else if (!(flags & F.LAST_BOUNDARY) && c === LF) {
141 index = 0;
142 callback('onPartBegin');
143 state = S.HEADER_FIELD_START;
144 } else {
145 return;
146 }
147
148 break;
149 }
150
151 if (c !== boundary[index + 2]) {
152 index = -2;
153 }
154
155 if (c === boundary[index + 2]) {
156 index++;
157 }
158
159 break;
160 case S.HEADER_FIELD_START:
161 state = S.HEADER_FIELD;
162 mark('onHeaderField');
163 index = 0;
164 // falls through
165 case S.HEADER_FIELD:
166 if (c === CR) {
167 clear('onHeaderField');
168 state = S.HEADERS_ALMOST_DONE;
169 break;
170 }
171
172 index++;
173 if (c === HYPHEN) {
174 break;
175 }
176
177 if (c === COLON) {
178 if (index === 1) {
179 // empty header field
180 return;
181 }
182
183 dataCallback('onHeaderField', true);
184 state = S.HEADER_VALUE_START;
185 break;
186 }
187
188 cl = lower(c);
189 if (cl < A || cl > Z) {
190 return;
191 }
192
193 break;
194 case S.HEADER_VALUE_START:
195 if (c === SPACE) {
196 break;
197 }
198
199 mark('onHeaderValue');
200 state = S.HEADER_VALUE;
201 // falls through
202 case S.HEADER_VALUE:
203 if (c === CR) {
204 dataCallback('onHeaderValue', true);
205 callback('onHeaderEnd');
206 state = S.HEADER_VALUE_ALMOST_DONE;
207 }
208
209 break;
210 case S.HEADER_VALUE_ALMOST_DONE:
211 if (c !== LF) {
212 return;
213 }
214
215 state = S.HEADER_FIELD_START;
216 break;
217 case S.HEADERS_ALMOST_DONE:
218 if (c !== LF) {
219 return;
220 }
221
222 callback('onHeadersEnd');
223 state = S.PART_DATA_START;
224 break;
225 case S.PART_DATA_START:
226 state = S.PART_DATA;
227 mark('onPartData');
228 // falls through
229 case S.PART_DATA:
230 previousIndex = index;
231
232 if (index === 0) {
233 // boyer-moore derrived algorithm to safely skip non-boundary data
234 i += boundaryEnd;
235 while (i < bufferLength && !(data[i] in boundaryChars)) {
236 i += boundaryLength;
237 }
238
239 i -= boundaryEnd;
240 c = data[i];
241 }
242
243 if (index < boundary.length) {
244 if (boundary[index] === c) {
245 if (index === 0) {
246 dataCallback('onPartData', true);
247 }
248
249 index++;
250 } else {
251 index = 0;
252 }
253 } else if (index === boundary.length) {
254 index++;
255 if (c === CR) {
256 // CR = part boundary
257 flags |= F.PART_BOUNDARY;
258 } else if (c === HYPHEN) {
259 // HYPHEN = end boundary
260 flags |= F.LAST_BOUNDARY;
261 } else {
262 index = 0;
263 }
264 } else if (index - 1 === boundary.length) {
265 if (flags & F.PART_BOUNDARY) {
266 index = 0;
267 if (c === LF) {
268 // unset the PART_BOUNDARY flag
269 flags &= ~F.PART_BOUNDARY;
270 callback('onPartEnd');
271 callback('onPartBegin');
272 state = S.HEADER_FIELD_START;
273 break;
274 }
275 } else if (flags & F.LAST_BOUNDARY) {
276 if (c === HYPHEN) {
277 callback('onPartEnd');
278 state = S.END;
279 flags = 0;
280 } else {
281 index = 0;
282 }
283 } else {
284 index = 0;
285 }
286 }
287
288 if (index > 0) {
289 // when matching a possible boundary, keep a lookbehind reference
290 // in case it turns out to be a false lead
291 lookbehind[index - 1] = c;
292 } else if (previousIndex > 0) {
293 // if our boundary turned out to be rubbish, the captured lookbehind
294 // belongs to partData
295 const _lookbehind = new Uint8Array(lookbehind.buffer, lookbehind.byteOffset, lookbehind.byteLength);
296 callback('onPartData', 0, previousIndex, _lookbehind);
297 previousIndex = 0;
298 mark('onPartData');
299
300 // reconsider the current character even so it interrupted the sequence
301 // it could be the beginning of a new sequence
302 i--;
303 }
304
305 break;
306 case S.END:
307 break;
308 default:
309 throw new Error(`Unexpected state entered: ${state}`);
310 }
311 }
312
313 dataCallback('onHeaderField');
314 dataCallback('onHeaderValue');
315 dataCallback('onPartData');
316
317 // Update properties for the next call
318 this.index = index;
319 this.state = state;
320 this.flags = flags;
321 }
322
323 end() {
324 if ((this.state === S.HEADER_FIELD_START && this.index === 0) ||
325 (this.state === S.PART_DATA && this.index === this.boundary.length)) {
326 this.onPartEnd();
327 } else if (this.state !== S.END) {
328 throw new Error('MultipartParser.end(): stream ended unexpectedly');
329 }
330 }
331}
332
333function _fileName(headerValue) {
334 // matches either a quoted-string or a token (RFC 2616 section 19.5.1)
335 const m = headerValue.match(/\bfilename=("(.*?)"|([^()<>@,;:\\"/[\]?={}\s\t]+))($|;\s)/i);
336 if (!m) {
337 return;
338 }
339
340 const match = m[2] || m[3] || '';
341 let filename = match.slice(match.lastIndexOf('\\') + 1);
342 filename = filename.replace(/%22/g, '"');
343 filename = filename.replace(/&#(\d{4});/g, (m, code) => {
344 return String.fromCharCode(code);
345 });
346 return filename;
347}
348
349async function toFormData(Body, ct) {
350 if (!/multipart/i.test(ct)) {
351 throw new TypeError('Failed to fetch');
352 }
353
354 const m = ct.match(/boundary=(?:"([^"]+)"|([^;]+))/i);
355
356 if (!m) {
357 throw new TypeError('no or bad content-type header, no multipart boundary');
358 }
359
360 const parser = new MultipartParser(m[1] || m[2]);
361
362 let headerField;
363 let headerValue;
364 let entryValue;
365 let entryName;
366 let contentType;
367 let filename;
368 const entryChunks = [];
369 const formData = new index.FormData();
370
371 const onPartData = ui8a => {
372 entryValue += decoder.decode(ui8a, {stream: true});
373 };
374
375 const appendToFile = ui8a => {
376 entryChunks.push(ui8a);
377 };
378
379 const appendFileToFormData = () => {
380 const file = new index.File(entryChunks, filename, {type: contentType});
381 formData.append(entryName, file);
382 };
383
384 const appendEntryToFormData = () => {
385 formData.append(entryName, entryValue);
386 };
387
388 const decoder = new TextDecoder('utf-8');
389 decoder.decode();
390
391 parser.onPartBegin = function () {
392 parser.onPartData = onPartData;
393 parser.onPartEnd = appendEntryToFormData;
394
395 headerField = '';
396 headerValue = '';
397 entryValue = '';
398 entryName = '';
399 contentType = '';
400 filename = null;
401 entryChunks.length = 0;
402 };
403
404 parser.onHeaderField = function (ui8a) {
405 headerField += decoder.decode(ui8a, {stream: true});
406 };
407
408 parser.onHeaderValue = function (ui8a) {
409 headerValue += decoder.decode(ui8a, {stream: true});
410 };
411
412 parser.onHeaderEnd = function () {
413 headerValue += decoder.decode();
414 headerField = headerField.toLowerCase();
415
416 if (headerField === 'content-disposition') {
417 // matches either a quoted-string or a token (RFC 2616 section 19.5.1)
418 const m = headerValue.match(/\bname=("([^"]*)"|([^()<>@,;:\\"/[\]?={}\s\t]+))/i);
419
420 if (m) {
421 entryName = m[2] || m[3] || '';
422 }
423
424 filename = _fileName(headerValue);
425
426 if (filename) {
427 parser.onPartData = appendToFile;
428 parser.onPartEnd = appendFileToFormData;
429 }
430 } else if (headerField === 'content-type') {
431 contentType = headerValue;
432 }
433
434 headerValue = '';
435 headerField = '';
436 };
437
438 for await (const chunk of Body) {
439 parser.write(chunk);
440 }
441
442 parser.end();
443
444 return formData;
445}
446
447exports.toFormData = toFormData;
448/* eslint-enable */
449//# sourceMappingURL=multipart-parser-25a14693.js.map
Note: See TracBrowser for help on using the repository browser.