Dicer.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. var WritableStream = require('stream').Writable
  2. || require('readable-stream').Writable,
  3. inherits = require('util').inherits;
  4. var StreamSearch = require('streamsearch');
  5. var PartStream = require('./PartStream'),
  6. HeaderParser = require('./HeaderParser');
  7. var DASH = 45,
  8. B_ONEDASH = new Buffer('-'),
  9. B_CRLF = new Buffer('\r\n'),
  10. EMPTY_FN = function() {};
  11. function Dicer(cfg) {
  12. if (!(this instanceof Dicer))
  13. return new Dicer(cfg);
  14. WritableStream.call(this, cfg);
  15. if (!cfg || (!cfg.headerFirst && typeof cfg.boundary !== 'string'))
  16. throw new TypeError('Boundary required');
  17. if (typeof cfg.boundary === 'string')
  18. this.setBoundary(cfg.boundary);
  19. else
  20. this._bparser = undefined;
  21. this._headerFirst = cfg.headerFirst;
  22. var self = this;
  23. this._dashes = 0;
  24. this._parts = 0;
  25. this._finished = false;
  26. this._realFinish = false;
  27. this._isPreamble = true;
  28. this._justMatched = false;
  29. this._firstWrite = true;
  30. this._inHeader = true;
  31. this._part = undefined;
  32. this._cb = undefined;
  33. this._ignoreData = false;
  34. this._partOpts = (typeof cfg.partHwm === 'number'
  35. ? { highWaterMark: cfg.partHwm }
  36. : {});
  37. this._pause = false;
  38. this._hparser = new HeaderParser(cfg);
  39. this._hparser.on('header', function(header) {
  40. self._inHeader = false;
  41. self._part.emit('header', header);
  42. });
  43. }
  44. inherits(Dicer, WritableStream);
  45. Dicer.prototype.emit = function(ev) {
  46. if (ev === 'finish' && !this._realFinish) {
  47. if (!this._finished) {
  48. var self = this;
  49. process.nextTick(function() {
  50. self.emit('error', new Error('Unexpected end of multipart data'));
  51. if (self._part && !self._ignoreData) {
  52. var type = (self._isPreamble ? 'Preamble' : 'Part');
  53. self._part.emit('error', new Error(type + ' terminated early due to unexpected end of multipart data'));
  54. self._part.push(null);
  55. process.nextTick(function() {
  56. self._realFinish = true;
  57. self.emit('finish');
  58. self._realFinish = false;
  59. });
  60. return;
  61. }
  62. self._realFinish = true;
  63. self.emit('finish');
  64. self._realFinish = false;
  65. });
  66. }
  67. } else
  68. WritableStream.prototype.emit.apply(this, arguments);
  69. };
  70. Dicer.prototype._write = function(data, encoding, cb) {
  71. // ignore unexpected data (e.g. extra trailer data after finished)
  72. if (!this._hparser && !this._bparser)
  73. return cb();
  74. if (this._headerFirst && this._isPreamble) {
  75. if (!this._part) {
  76. this._part = new PartStream(this._partOpts);
  77. if (this._events.preamble)
  78. this.emit('preamble', this._part);
  79. else
  80. this._ignore();
  81. }
  82. var r = this._hparser.push(data);
  83. if (!this._inHeader && r !== undefined && r < data.length)
  84. data = data.slice(r);
  85. else
  86. return cb();
  87. }
  88. // allows for "easier" testing
  89. if (this._firstWrite) {
  90. this._bparser.push(B_CRLF);
  91. this._firstWrite = false;
  92. }
  93. this._bparser.push(data);
  94. if (this._pause)
  95. this._cb = cb;
  96. else
  97. cb();
  98. };
  99. Dicer.prototype.reset = function() {
  100. this._part = undefined;
  101. this._bparser = undefined;
  102. this._hparser = undefined;
  103. };
  104. Dicer.prototype.setBoundary = function(boundary) {
  105. var self = this;
  106. this._bparser = new StreamSearch('\r\n--' + boundary);
  107. this._bparser.on('info', function(isMatch, data, start, end) {
  108. self._oninfo(isMatch, data, start, end);
  109. });
  110. };
  111. Dicer.prototype._ignore = function() {
  112. if (this._part && !this._ignoreData) {
  113. this._ignoreData = true;
  114. this._part.on('error', EMPTY_FN);
  115. // we must perform some kind of read on the stream even though we are
  116. // ignoring the data, otherwise node's Readable stream will not emit 'end'
  117. // after pushing null to the stream
  118. this._part.resume();
  119. }
  120. };
  121. Dicer.prototype._oninfo = function(isMatch, data, start, end) {
  122. var buf, self = this, i = 0, r, ev, shouldWriteMore = true;
  123. if (!this._part && this._justMatched && data) {
  124. while (this._dashes < 2 && (start + i) < end) {
  125. if (data[start + i] === DASH) {
  126. ++i;
  127. ++this._dashes;
  128. } else {
  129. if (this._dashes)
  130. buf = B_ONEDASH;
  131. this._dashes = 0;
  132. break;
  133. }
  134. }
  135. if (this._dashes === 2) {
  136. if ((start + i) < end && this._events.trailer)
  137. this.emit('trailer', data.slice(start + i, end));
  138. this.reset();
  139. this._finished = true;
  140. // no more parts will be added
  141. if (self._parts === 0) {
  142. self._realFinish = true;
  143. self.emit('finish');
  144. self._realFinish = false;
  145. }
  146. }
  147. if (this._dashes)
  148. return;
  149. }
  150. if (this._justMatched)
  151. this._justMatched = false;
  152. if (!this._part) {
  153. this._part = new PartStream(this._partOpts);
  154. this._part._read = function(n) {
  155. self._unpause();
  156. };
  157. ev = this._isPreamble ? 'preamble' : 'part';
  158. if (this._events[ev])
  159. this.emit(ev, this._part);
  160. else
  161. this._ignore();
  162. if (!this._isPreamble)
  163. this._inHeader = true;
  164. }
  165. if (data && start < end && !this._ignoreData) {
  166. if (this._isPreamble || !this._inHeader) {
  167. if (buf)
  168. shouldWriteMore = this._part.push(buf);
  169. shouldWriteMore = this._part.push(data.slice(start, end));
  170. if (!shouldWriteMore)
  171. this._pause = true;
  172. } else if (!this._isPreamble && this._inHeader) {
  173. if (buf)
  174. this._hparser.push(buf);
  175. r = this._hparser.push(data.slice(start, end));
  176. if (!this._inHeader && r !== undefined && r < end)
  177. this._oninfo(false, data, start + r, end);
  178. }
  179. }
  180. if (isMatch) {
  181. this._hparser.reset();
  182. if (this._isPreamble)
  183. this._isPreamble = false;
  184. else {
  185. ++this._parts;
  186. this._part.on('end', function() {
  187. if (--self._parts === 0) {
  188. if (self._finished) {
  189. self._realFinish = true;
  190. self.emit('finish');
  191. self._realFinish = false;
  192. } else {
  193. self._unpause();
  194. }
  195. }
  196. });
  197. }
  198. this._part.push(null);
  199. this._part = undefined;
  200. this._ignoreData = false;
  201. this._justMatched = true;
  202. this._dashes = 0;
  203. }
  204. };
  205. Dicer.prototype._unpause = function() {
  206. if (!this._pause)
  207. return;
  208. this._pause = false;
  209. if (this._cb) {
  210. var cb = this._cb;
  211. this._cb = undefined;
  212. cb();
  213. }
  214. };
  215. module.exports = Dicer;