firstValues.cjs 10 KB

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