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.

214 lines
6.4 KiB

  1. var Decoder = require('../utils').Decoder,
  2. decodeText = require('../utils').decodeText;
  3. var RE_CHARSET = /^charset$/i;
  4. UrlEncoded.detect = /^application\/x-www-form-urlencoded/i;
  5. function UrlEncoded(boy, cfg) {
  6. if (!(this instanceof UrlEncoded))
  7. return new UrlEncoded(boy, cfg);
  8. var limits = cfg.limits,
  9. headers = cfg.headers,
  10. parsedConType = cfg.parsedConType;
  11. this.boy = boy;
  12. this.fieldSizeLimit = (limits && typeof limits.fieldSize === 'number'
  13. ? limits.fieldSize
  14. : 1 * 1024 * 1024);
  15. this.fieldNameSizeLimit = (limits && typeof limits.fieldNameSize === 'number'
  16. ? limits.fieldNameSize
  17. : 100);
  18. this.fieldsLimit = (limits && typeof limits.fields === 'number'
  19. ? limits.fields
  20. : Infinity);
  21. var charset;
  22. for (var i = 0, len = parsedConType.length; i < len; ++i) {
  23. if (Array.isArray(parsedConType[i])
  24. && RE_CHARSET.test(parsedConType[i][0])) {
  25. charset = parsedConType[i][1].toLowerCase();
  26. break;
  27. }
  28. }
  29. if (charset === undefined)
  30. charset = cfg.defCharset || 'utf8';
  31. this.decoder = new Decoder();
  32. this.charset = charset;
  33. this._fields = 0;
  34. this._state = 'key';
  35. this._checkingBytes = true;
  36. this._bytesKey = 0;
  37. this._bytesVal = 0;
  38. this._key = '';
  39. this._val = '';
  40. this._keyTrunc = false;
  41. this._valTrunc = false;
  42. this._hitlimit = false;
  43. }
  44. UrlEncoded.prototype.write = function(data, cb) {
  45. if (this._fields === this.fieldsLimit) {
  46. if (!this.boy.hitFieldsLimit) {
  47. this.boy.hitFieldsLimit = true;
  48. this.boy.emit('fieldsLimit');
  49. }
  50. return cb();
  51. }
  52. var idxeq, idxamp, i, p = 0, len = data.length;
  53. while (p < len) {
  54. if (this._state === 'key') {
  55. idxeq = idxamp = undefined;
  56. for (i = p; i < len; ++i) {
  57. if (!this._checkingBytes)
  58. ++p;
  59. if (data[i] === 0x3D/*=*/) {
  60. idxeq = i;
  61. break;
  62. } else if (data[i] === 0x26/*&*/) {
  63. idxamp = i;
  64. break;
  65. }
  66. if (this._checkingBytes && this._bytesKey === this.fieldNameSizeLimit) {
  67. this._hitLimit = true;
  68. break;
  69. } else if (this._checkingBytes)
  70. ++this._bytesKey;
  71. }
  72. if (idxeq !== undefined) {
  73. // key with assignment
  74. if (idxeq > p)
  75. this._key += this.decoder.write(data.toString('binary', p, idxeq));
  76. this._state = 'val';
  77. this._hitLimit = false;
  78. this._checkingBytes = true;
  79. this._val = '';
  80. this._bytesVal = 0;
  81. this._valTrunc = false;
  82. this.decoder.reset();
  83. p = idxeq + 1;
  84. } else if (idxamp !== undefined) {
  85. // key with no assignment
  86. ++this._fields;
  87. var key, keyTrunc = this._keyTrunc;
  88. if (idxamp > p)
  89. key = (this._key += this.decoder.write(data.toString('binary', p, idxamp)));
  90. else
  91. key = this._key;
  92. this._hitLimit = false;
  93. this._checkingBytes = true;
  94. this._key = '';
  95. this._bytesKey = 0;
  96. this._keyTrunc = false;
  97. this.decoder.reset();
  98. if (key.length) {
  99. this.boy.emit('field', decodeText(key, 'binary', this.charset),
  100. '',
  101. keyTrunc,
  102. false);
  103. }
  104. p = idxamp + 1;
  105. if (this._fields === this.fieldsLimit)
  106. return cb();
  107. } else if (this._hitLimit) {
  108. // we may not have hit the actual limit if there are encoded bytes...
  109. if (i > p)
  110. this._key += this.decoder.write(data.toString('binary', p, i));
  111. p = i;
  112. if ((this._bytesKey = this._key.length) === this.fieldNameSizeLimit) {
  113. // yep, we actually did hit the limit
  114. this._checkingBytes = false;
  115. this._keyTrunc = true;
  116. }
  117. } else {
  118. if (p < len)
  119. this._key += this.decoder.write(data.toString('binary', p));
  120. p = len;
  121. }
  122. } else {
  123. idxamp = undefined;
  124. for (i = p; i < len; ++i) {
  125. if (!this._checkingBytes)
  126. ++p;
  127. if (data[i] === 0x26/*&*/) {
  128. idxamp = i;
  129. break;
  130. }
  131. if (this._checkingBytes && this._bytesVal === this.fieldSizeLimit) {
  132. this._hitLimit = true;
  133. break;
  134. }
  135. else if (this._checkingBytes)
  136. ++this._bytesVal;
  137. }
  138. if (idxamp !== undefined) {
  139. ++this._fields;
  140. if (idxamp > p)
  141. this._val += this.decoder.write(data.toString('binary', p, idxamp));
  142. this.boy.emit('field', decodeText(this._key, 'binary', this.charset),
  143. decodeText(this._val, 'binary', this.charset),
  144. this._keyTrunc,
  145. this._valTrunc);
  146. this._state = 'key';
  147. this._hitLimit = false;
  148. this._checkingBytes = true;
  149. this._key = '';
  150. this._bytesKey = 0;
  151. this._keyTrunc = false;
  152. this.decoder.reset();
  153. p = idxamp + 1;
  154. if (this._fields === this.fieldsLimit)
  155. return cb();
  156. } else if (this._hitLimit) {
  157. // we may not have hit the actual limit if there are encoded bytes...
  158. if (i > p)
  159. this._val += this.decoder.write(data.toString('binary', p, i));
  160. p = i;
  161. if ((this._val === '' && this.fieldSizeLimit === 0)
  162. || (this._bytesVal = this._val.length) === this.fieldSizeLimit) {
  163. // yep, we actually did hit the limit
  164. this._checkingBytes = false;
  165. this._valTrunc = true;
  166. }
  167. } else {
  168. if (p < len)
  169. this._val += this.decoder.write(data.toString('binary', p));
  170. p = len;
  171. }
  172. }
  173. }
  174. cb();
  175. };
  176. UrlEncoded.prototype.end = function() {
  177. if (this.boy._done)
  178. return;
  179. if (this._state === 'key' && this._key.length > 0) {
  180. this.boy.emit('field', decodeText(this._key, 'binary', this.charset),
  181. '',
  182. this._keyTrunc,
  183. false);
  184. } else if (this._state === 'val') {
  185. this.boy.emit('field', decodeText(this._key, 'binary', this.charset),
  186. decodeText(this._val, 'binary', this.charset),
  187. this._keyTrunc,
  188. this._valTrunc);
  189. }
  190. this.boy._done = true;
  191. this.boy.emit('finish');
  192. };
  193. module.exports = UrlEncoded;