UNPKG

2.78 kBJavaScriptView Raw
1'use strict';
2
3const utils = require('./utils');
4const {StripeError} = require('./Error');
5
6class StreamProcessingError extends StripeError {}
7
8// Method for formatting HTTP body for the multipart/form-data specification
9// Mostly taken from Fermata.js
10// https://github.com/natevw/fermata/blob/5d9732a33d776ce925013a265935facd1626cc88/fermata.js#L315-L343
11const 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
56const 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
80const 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
96module.exports.multipartRequestDataProcessor = multipartRequestDataProcessor;