var Buffer = require('buffer').Buffer, 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; }; for (s in S) { exports[s] = S[s]; } function MultipartParser() { this.boundary = null; this.boundaryChars = null; this.lookbehind = null; this.state = S.PARSER_UNINITIALIZED; this.index = null; this.flags = 0; } exports.MultipartParser = MultipartParser; MultipartParser.stateToString = function(stateNumber) { for (var state in S) { var number = S[state]; if (number === stateNumber) return state; } }; MultipartParser.prototype.initWithBoundary = function(str) { this.boundary = new Buffer(str.length+4); this.boundary.write('\r\n--', 0); this.boundary.write(str, 4); this.lookbehind = new Buffer(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; } }; MultipartParser.prototype.write = function(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; }; MultipartParser.prototype.end = function() { 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()); } }; MultipartParser.prototype.explain = function() { return 'state = ' + MultipartParser.stateToString(this.state); };
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 19553 | swellard | Move and rename clients | ||
//guest/perforce_software/helix-web-services/main/source/clients/2016.1.0/javascript/node_modules/formidable/lib/multipart_parser.js | |||||
#1 | 18810 | tjuricek |
First-pass at JavaScript client SDK. JavaScript requires Node with Gulp to "browserfy" the library. It's the easiest way I found to use the swagger-js project; bundle up a wrapping method. There is no JavaScript reference guide. The swagger-js doesn't really document what they do very well, actually. Overall I'm not particularly impressed by swagger-js, it was hard to even figure out what the right method syntax was. We may want to invest time in doing it better. This required setting CORS response headers, which are currently defaulted to a fairly insecure setting. |