Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Avatar for KuCalc : devops.
Download
50654 views
1
var Buffer = require('buffer').Buffer,
2
s = 0,
3
S =
4
{ PARSER_UNINITIALIZED: s++,
5
START: s++,
6
START_BOUNDARY: s++,
7
HEADER_FIELD_START: s++,
8
HEADER_FIELD: s++,
9
HEADER_VALUE_START: s++,
10
HEADER_VALUE: s++,
11
HEADER_VALUE_ALMOST_DONE: s++,
12
HEADERS_ALMOST_DONE: s++,
13
PART_DATA_START: s++,
14
PART_DATA: s++,
15
PART_END: s++,
16
END: s++
17
},
18
19
f = 1,
20
F =
21
{ PART_BOUNDARY: f,
22
LAST_BOUNDARY: f *= 2
23
},
24
25
LF = 10,
26
CR = 13,
27
SPACE = 32,
28
HYPHEN = 45,
29
COLON = 58,
30
A = 97,
31
Z = 122,
32
33
lower = function(c) {
34
return c | 0x20;
35
};
36
37
for (s in S) {
38
exports[s] = S[s];
39
}
40
41
function MultipartParser() {
42
this.boundary = null;
43
this.boundaryChars = null;
44
this.lookbehind = null;
45
this.state = S.PARSER_UNINITIALIZED;
46
47
this.index = null;
48
this.flags = 0;
49
}
50
exports.MultipartParser = MultipartParser;
51
52
MultipartParser.stateToString = function(stateNumber) {
53
for (var state in S) {
54
var number = S[state];
55
if (number === stateNumber) return state;
56
}
57
};
58
59
MultipartParser.prototype.initWithBoundary = function(str) {
60
this.boundary = new Buffer(str.length+4);
61
this.boundary.write('\r\n--', 0);
62
this.boundary.write(str, 4);
63
this.lookbehind = new Buffer(this.boundary.length+8);
64
this.state = S.START;
65
66
this.boundaryChars = {};
67
for (var i = 0; i < this.boundary.length; i++) {
68
this.boundaryChars[this.boundary[i]] = true;
69
}
70
};
71
72
MultipartParser.prototype.write = function(buffer) {
73
var self = this,
74
i = 0,
75
len = buffer.length,
76
prevIndex = this.index,
77
index = this.index,
78
state = this.state,
79
flags = this.flags,
80
lookbehind = this.lookbehind,
81
boundary = this.boundary,
82
boundaryChars = this.boundaryChars,
83
boundaryLength = this.boundary.length,
84
boundaryEnd = boundaryLength - 1,
85
bufferLength = buffer.length,
86
c,
87
cl,
88
89
mark = function(name) {
90
self[name+'Mark'] = i;
91
},
92
clear = function(name) {
93
delete self[name+'Mark'];
94
},
95
callback = function(name, buffer, start, end) {
96
if (start !== undefined && start === end) {
97
return;
98
}
99
100
var callbackSymbol = 'on'+name.substr(0, 1).toUpperCase()+name.substr(1);
101
if (callbackSymbol in self) {
102
self[callbackSymbol](buffer, start, end);
103
}
104
},
105
dataCallback = function(name, clear) {
106
var markSymbol = name+'Mark';
107
if (!(markSymbol in self)) {
108
return;
109
}
110
111
if (!clear) {
112
callback(name, buffer, self[markSymbol], buffer.length);
113
self[markSymbol] = 0;
114
} else {
115
callback(name, buffer, self[markSymbol], i);
116
delete self[markSymbol];
117
}
118
};
119
120
for (i = 0; i < len; i++) {
121
c = buffer[i];
122
switch (state) {
123
case S.PARSER_UNINITIALIZED:
124
return i;
125
case S.START:
126
index = 0;
127
state = S.START_BOUNDARY;
128
case S.START_BOUNDARY:
129
if (index == boundary.length - 2) {
130
if (c == HYPHEN) {
131
flags |= F.LAST_BOUNDARY;
132
} else if (c != CR) {
133
return i;
134
}
135
index++;
136
break;
137
} else if (index - 1 == boundary.length - 2) {
138
if (flags & F.LAST_BOUNDARY && c == HYPHEN){
139
callback('end');
140
state = S.END;
141
flags = 0;
142
} else if (!(flags & F.LAST_BOUNDARY) && c == LF) {
143
index = 0;
144
callback('partBegin');
145
state = S.HEADER_FIELD_START;
146
} else {
147
return i;
148
}
149
break;
150
}
151
152
if (c != boundary[index+2]) {
153
index = -2;
154
}
155
if (c == boundary[index+2]) {
156
index++;
157
}
158
break;
159
case S.HEADER_FIELD_START:
160
state = S.HEADER_FIELD;
161
mark('headerField');
162
index = 0;
163
case S.HEADER_FIELD:
164
if (c == CR) {
165
clear('headerField');
166
state = S.HEADERS_ALMOST_DONE;
167
break;
168
}
169
170
index++;
171
if (c == HYPHEN) {
172
break;
173
}
174
175
if (c == COLON) {
176
if (index == 1) {
177
// empty header field
178
return i;
179
}
180
dataCallback('headerField', true);
181
state = S.HEADER_VALUE_START;
182
break;
183
}
184
185
cl = lower(c);
186
if (cl < A || cl > Z) {
187
return i;
188
}
189
break;
190
case S.HEADER_VALUE_START:
191
if (c == SPACE) {
192
break;
193
}
194
195
mark('headerValue');
196
state = S.HEADER_VALUE;
197
case S.HEADER_VALUE:
198
if (c == CR) {
199
dataCallback('headerValue', true);
200
callback('headerEnd');
201
state = S.HEADER_VALUE_ALMOST_DONE;
202
}
203
break;
204
case S.HEADER_VALUE_ALMOST_DONE:
205
if (c != LF) {
206
return i;
207
}
208
state = S.HEADER_FIELD_START;
209
break;
210
case S.HEADERS_ALMOST_DONE:
211
if (c != LF) {
212
return i;
213
}
214
215
callback('headersEnd');
216
state = S.PART_DATA_START;
217
break;
218
case S.PART_DATA_START:
219
state = S.PART_DATA;
220
mark('partData');
221
case S.PART_DATA:
222
prevIndex = index;
223
224
if (index === 0) {
225
// boyer-moore derrived algorithm to safely skip non-boundary data
226
i += boundaryEnd;
227
while (i < bufferLength && !(buffer[i] in boundaryChars)) {
228
i += boundaryLength;
229
}
230
i -= boundaryEnd;
231
c = buffer[i];
232
}
233
234
if (index < boundary.length) {
235
if (boundary[index] == c) {
236
if (index === 0) {
237
dataCallback('partData', true);
238
}
239
index++;
240
} else {
241
index = 0;
242
}
243
} else if (index == boundary.length) {
244
index++;
245
if (c == CR) {
246
// CR = part boundary
247
flags |= F.PART_BOUNDARY;
248
} else if (c == HYPHEN) {
249
// HYPHEN = end boundary
250
flags |= F.LAST_BOUNDARY;
251
} else {
252
index = 0;
253
}
254
} else if (index - 1 == boundary.length) {
255
if (flags & F.PART_BOUNDARY) {
256
index = 0;
257
if (c == LF) {
258
// unset the PART_BOUNDARY flag
259
flags &= ~F.PART_BOUNDARY;
260
callback('partEnd');
261
callback('partBegin');
262
state = S.HEADER_FIELD_START;
263
break;
264
}
265
} else if (flags & F.LAST_BOUNDARY) {
266
if (c == HYPHEN) {
267
callback('partEnd');
268
callback('end');
269
state = S.END;
270
flags = 0;
271
} else {
272
index = 0;
273
}
274
} else {
275
index = 0;
276
}
277
}
278
279
if (index > 0) {
280
// when matching a possible boundary, keep a lookbehind reference
281
// in case it turns out to be a false lead
282
lookbehind[index-1] = c;
283
} else if (prevIndex > 0) {
284
// if our boundary turned out to be rubbish, the captured lookbehind
285
// belongs to partData
286
callback('partData', lookbehind, 0, prevIndex);
287
prevIndex = 0;
288
mark('partData');
289
290
// reconsider the current character even so it interrupted the sequence
291
// it could be the beginning of a new sequence
292
i--;
293
}
294
295
break;
296
case S.END:
297
break;
298
default:
299
return i;
300
}
301
}
302
303
dataCallback('headerField');
304
dataCallback('headerValue');
305
dataCallback('partData');
306
307
this.index = index;
308
this.state = state;
309
this.flags = flags;
310
311
return len;
312
};
313
314
MultipartParser.prototype.end = function() {
315
var callback = function(self, name) {
316
var callbackSymbol = 'on'+name.substr(0, 1).toUpperCase()+name.substr(1);
317
if (callbackSymbol in self) {
318
self[callbackSymbol]();
319
}
320
};
321
if ((this.state == S.HEADER_FIELD_START && this.index === 0) ||
322
(this.state == S.PART_DATA && this.index == this.boundary.length)) {
323
callback(this, 'partEnd');
324
callback(this, 'end');
325
} else if (this.state != S.END) {
326
return new Error('MultipartParser.end(): stream ended unexpectedly: ' + this.explain());
327
}
328
};
329
330
MultipartParser.prototype.explain = function() {
331
return 'state = ' + MultipartParser.stateToString(this.state);
332
};
333
334