1 | 'use strict';
|
2 |
|
3 | const utils = require('./utils');
|
4 | const {StripeError} = require('./Error');
|
5 |
|
6 | class StreamProcessingError extends StripeError {}
|
7 |
|
8 |
|
9 |
|
10 |
|
11 | const multipartDataGenerator = (method, data, headers) => {
|
12 | const segno = (
|
13 | Math.round(Math.random() * 1e16) + Math.round(Math.random() * 1e16)
|
14 | ).toString();
|
15 | headers['Content-Type'] = `multipart/form-data; boundary=${segno}`;
|
16 | let buffer = Buffer.alloc(0);
|
17 |
|
18 | function push(l) {
|
19 | const prevBuffer = buffer;
|
20 | const newBuffer = l instanceof Buffer ? l : Buffer.from(l);
|
21 | buffer = Buffer.alloc(prevBuffer.length + newBuffer.length + 2);
|
22 | prevBuffer.copy(buffer);
|
23 | newBuffer.copy(buffer, prevBuffer.length);
|
24 | buffer.write('\r\n', buffer.length - 2);
|
25 | }
|
26 |
|
27 | function q(s) {
|
28 | return `"${s.replace(/"|"/g, '%22').replace(/\r\n|\r|\n/g, ' ')}"`;
|
29 | }
|
30 |
|
31 | const flattenedData = utils.flattenAndStringify(data);
|
32 |
|
33 | for (const k in flattenedData) {
|
34 | const v = flattenedData[k];
|
35 | push(`--${segno}`);
|
36 | if (v.hasOwnProperty('data')) {
|
37 | push(
|
38 | `Content-Disposition: form-data; name=${q(k)}; filename=${q(
|
39 | v.name || 'blob'
|
40 | )}`
|
41 | );
|
42 | push(`Content-Type: ${v.type || 'application/octet-stream'}`);
|
43 | push('');
|
44 | push(v.data);
|
45 | } else {
|
46 | push(`Content-Disposition: form-data; name=${q(k)}`);
|
47 | push('');
|
48 | push(v);
|
49 | }
|
50 | }
|
51 | push(`--${segno}--`);
|
52 |
|
53 | return buffer;
|
54 | };
|
55 |
|
56 | const streamProcessor = (method, data, headers, callback) => {
|
57 | const bufferArray = [];
|
58 | data.file.data
|
59 | .on('data', (line) => {
|
60 | bufferArray.push(line);
|
61 | })
|
62 | .once('end', () => {
|
63 | const bufferData = Object.assign({}, data);
|
64 | bufferData.file.data = Buffer.concat(bufferArray);
|
65 | const buffer = multipartDataGenerator(method, bufferData, headers);
|
66 | callback(null, buffer);
|
67 | })
|
68 | .on('error', (err) => {
|
69 | callback(
|
70 | new StreamProcessingError({
|
71 | message:
|
72 | 'An error occurred while attempting to process the file for upload.',
|
73 | detail: err,
|
74 | }),
|
75 | null
|
76 | );
|
77 | });
|
78 | };
|
79 |
|
80 | const multipartRequestDataProcessor = (method, data, headers, callback) => {
|
81 | data = data || {};
|
82 |
|
83 | if (method !== 'POST') {
|
84 | return callback(null, utils.stringifyRequestData(data));
|
85 | }
|
86 |
|
87 | const isStream = utils.checkForStream(data);
|
88 | if (isStream) {
|
89 | return streamProcessor(method, data, headers, callback);
|
90 | }
|
91 |
|
92 | const buffer = multipartDataGenerator(method, data, headers);
|
93 | return callback(null, buffer);
|
94 | };
|
95 |
|
96 | module.exports.multipartRequestDataProcessor = multipartRequestDataProcessor;
|