var s = 0, S = { PARSER_UNINITIALIZED: s++, START: s++, START_BOUNDARY: s++, HEADER_FIELD_START: s++, HEADER_FIELD: s++, HEADER_VALUE_START: s++, HEADER_VALUE: s++, HEADER_VALUE_ALMOST_DONE: s++, HEADERS_ALMOST_DONE: s++, PART_DATA_START: s++, PART_DATA: s++, PART_END: s++, END: s++ }, f = 1, F = { PART_BOUNDARY: f, LAST_BOUNDARY: (f *= 2) }, LF = 10, CR = 13, SPACE = 32, HYPHEN = 45, COLON = 58, A = 97, Z = 122, lower = function(c) { return c | 0x20 } export class MultipartParser { boundary = null boundaryChars = null lookbehind = null state = S.PARSER_UNINITIALIZED index = null flags = 0 static stateToString(stateNumber) { for (var state in S) { var number = S[state] if (number === stateNumber) return state } } initWithBoundary(str) { this.boundary = Buffer.alloc(str.length + 4) this.boundary.write('\r\n--', 0) this.boundary.write(str, 4) this.lookbehind = Buffer.alloc(this.boundary.length + 8) this.state = S.START this.boundaryChars = {} for (var i = 0; i < this.boundary.length; i++) { this.boundaryChars[this.boundary[i]] = true } } write(buffer) { var self = this, i = 0, len = buffer.length, prevIndex = this.index, index = this.index, state = this.state, flags = this.flags, lookbehind = this.lookbehind, boundary = this.boundary, boundaryChars = this.boundaryChars, boundaryLength = this.boundary.length, boundaryEnd = boundaryLength - 1, bufferLength = buffer.length, c, cl, mark = function(name) { self[name + 'Mark'] = i }, clear = function(name) { delete self[name + 'Mark'] }, callback = function(name, buffer, start, end) { if (start !== undefined && start === end) { return } var callbackSymbol = 'on' + name.substr(0, 1).toUpperCase() + name.substr(1) if (callbackSymbol in self) { self[callbackSymbol](buffer, start, end) } }, dataCallback = function(name, clear) { var markSymbol = name + 'Mark' if (!(markSymbol in self)) { return } if (!clear) { callback(name, buffer, self[markSymbol], buffer.length) self[markSymbol] = 0 } else { callback(name, buffer, self[markSymbol], i) delete self[markSymbol] } } for (i = 0; i < len; i++) { c = buffer[i] switch (state) { case S.PARSER_UNINITIALIZED: return i case S.START: index = 0 state = S.START_BOUNDARY case S.START_BOUNDARY: if (index == boundary.length - 2) { if (c == HYPHEN) { flags |= F.LAST_BOUNDARY } else if (c != CR) { return i } index++ break } else if (index - 1 == boundary.length - 2) { if (flags & F.LAST_BOUNDARY && c == HYPHEN) { callback('end') state = S.END flags = 0 } else if (!(flags & F.LAST_BOUNDARY) && c == LF) { index = 0 callback('partBegin') state = S.HEADER_FIELD_START } else { return i } break } if (c != boundary[index + 2]) { index = -2 } if (c == boundary[index + 2]) { index++ } break case S.HEADER_FIELD_START: state = S.HEADER_FIELD mark('headerField') index = 0 case S.HEADER_FIELD: if (c == CR) { clear('headerField') state = S.HEADERS_ALMOST_DONE break } index++ if (c == HYPHEN) { break } if (c == COLON) { if (index == 1) { // empty header field return i } dataCallback('headerField', true) state = S.HEADER_VALUE_START break } cl = lower(c) if (cl < A || cl > Z) { return i } break case S.HEADER_VALUE_START: if (c == SPACE) { break } mark('headerValue') state = S.HEADER_VALUE case S.HEADER_VALUE: if (c == CR) { dataCallback('headerValue', true) callback('headerEnd') state = S.HEADER_VALUE_ALMOST_DONE } break case S.HEADER_VALUE_ALMOST_DONE: if (c != LF) { return i } state = S.HEADER_FIELD_START break case S.HEADERS_ALMOST_DONE: if (c != LF) { return i } callback('headersEnd') state = S.PART_DATA_START break case S.PART_DATA_START: state = S.PART_DATA mark('partData') case S.PART_DATA: prevIndex = index if (index === 0) { // boyer-moore derrived algorithm to safely skip non-boundary data i += boundaryEnd while (i < bufferLength && !(buffer[i] in boundaryChars)) { i += boundaryLength } i -= boundaryEnd c = buffer[i] } if (index < boundary.length) { if (boundary[index] == c) { if (index === 0) { dataCallback('partData', true) } index++ } else { index = 0 } } else if (index == boundary.length) { index++ if (c == CR) { // CR = part boundary flags |= F.PART_BOUNDARY } else if (c == HYPHEN) { // HYPHEN = end boundary flags |= F.LAST_BOUNDARY } else { index = 0 } } else if (index - 1 == boundary.length) { if (flags & F.PART_BOUNDARY) { index = 0 if (c == LF) { // unset the PART_BOUNDARY flag flags &= ~F.PART_BOUNDARY callback('partEnd') callback('partBegin') state = S.HEADER_FIELD_START break } } else if (flags & F.LAST_BOUNDARY) { if (c == HYPHEN) { callback('partEnd') callback('end') state = S.END flags = 0 } else { index = 0 } } else { index = 0 } } if (index > 0) { // when matching a possible boundary, keep a lookbehind reference // in case it turns out to be a false lead lookbehind[index - 1] = c } else if (prevIndex > 0) { // if our boundary turned out to be rubbish, the captured lookbehind // belongs to partData callback('partData', lookbehind, 0, prevIndex) prevIndex = 0 mark('partData') // reconsider the current character even so it interrupted the sequence // it could be the beginning of a new sequence i-- } break case S.END: break default: return i } } dataCallback('headerField') dataCallback('headerValue') dataCallback('partData') this.index = index this.state = state this.flags = flags return len } end() { var callback = function(self, name) { var callbackSymbol = 'on' + name.substr(0, 1).toUpperCase() + name.substr(1) if (callbackSymbol in self) { self[callbackSymbol]() } } if ( (this.state == S.HEADER_FIELD_START && this.index === 0) || (this.state == S.PART_DATA && this.index == this.boundary.length) ) { callback(this, 'partEnd') callback(this, 'end') } else if (this.state != S.END) { return new Error( 'MultipartParser.end(): stream ended unexpectedly: ' + this.explain() ) } } explain() { return 'state = ' + MultipartParser.stateToString(this.state) } }