-
Notifications
You must be signed in to change notification settings - Fork 723
/
multipart.ts
104 lines (91 loc) · 2.98 KB
/
multipart.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import utils = require('./utils');
import _Error = require('./Error');
const {StripeError} = _Error;
class StreamProcessingError extends StripeError {}
// Method for formatting HTTP body for the multipart/form-data specification
// Mostly taken from Fermata.js
// https://github.com/natevw/fermata/blob/5d9732a33d776ce925013a265935facd1626cc88/fermata.js#L315-L343
const multipartDataGenerator = (method, data, headers) => {
const segno = (
Math.round(Math.random() * 1e16) + Math.round(Math.random() * 1e16)
).toString();
headers['Content-Type'] = `multipart/form-data; boundary=${segno}`;
let buffer = Buffer.alloc(0);
function push(l) {
const prevBuffer = buffer;
const newBuffer = l instanceof Buffer ? l : Buffer.from(l);
buffer = Buffer.alloc(prevBuffer.length + newBuffer.length + 2);
prevBuffer.copy(buffer);
newBuffer.copy(buffer, prevBuffer.length);
buffer.write('\r\n', buffer.length - 2);
}
function q(s) {
return `"${s.replace(/"|"/g, '%22').replace(/\r\n|\r|\n/g, ' ')}"`;
}
const flattenedData = utils.flattenAndStringify(data);
for (const k in flattenedData) {
const v = flattenedData[k];
push(`--${segno}`);
if (Object.prototype.hasOwnProperty.call(v, 'data')) {
// eslint-disable-next-line no-warning-comments
// TODO: I don't think we ever hit this branch
const typedEntry: {
name: string;
data: string;
type: string;
} = v as any;
push(
`Content-Disposition: form-data; name=${q(k)}; filename=${q(
typedEntry.name || 'blob'
)}`
);
push(`Content-Type: ${typedEntry.type || 'application/octet-stream'}`);
push('');
push(typedEntry.data);
} else {
push(`Content-Disposition: form-data; name=${q(k)}`);
push('');
push(v);
}
}
push(`--${segno}--`);
return buffer;
};
const streamProcessor = (method, data, headers, callback) => {
const bufferArray = [];
data.file.data
.on('data', (line) => {
bufferArray.push(line);
})
.once('end', () => {
const bufferData = Object.assign({}, data);
bufferData.file.data = Buffer.concat(bufferArray);
const buffer = multipartDataGenerator(method, bufferData, headers);
callback(null, buffer);
})
.on('error', (err) => {
callback(
new StreamProcessingError({
message:
'An error occurred while attempting to process the file for upload.',
detail: err,
}),
null
);
});
};
const multipartRequestDataProcessor = (method, data, headers, callback) => {
data = data || {};
if (method !== 'POST') {
return callback(null, utils.stringifyRequestData(data));
}
const isStream = utils.checkForStream(data);
if (isStream) {
return streamProcessor(method, data, headers, callback);
}
const buffer = multipartDataGenerator(method, data, headers);
return callback(null, buffer);
};
export = {
multipartRequestDataProcessor: multipartRequestDataProcessor,
};