You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

324 lines
8.0 KiB

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