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.

110 lines
2.9 KiB

  1. var EventEmitter = require('events').EventEmitter,
  2. inherits = require('util').inherits;
  3. var StreamSearch = require('streamsearch');
  4. var B_DCRLF = Buffer.from('\r\n\r\n'),
  5. RE_CRLF = /\r\n/g,
  6. RE_HDR = /^([^:]+):[ \t]?([\x00-\xFF]+)?$/,
  7. MAX_HEADER_PAIRS = 2000, // from node's http.js
  8. MAX_HEADER_SIZE = 80 * 1024; // from node's http_parser
  9. function HeaderParser(cfg) {
  10. EventEmitter.call(this);
  11. var self = this;
  12. this.nread = 0;
  13. this.maxed = false;
  14. this.npairs = 0;
  15. this.maxHeaderPairs = (cfg && typeof cfg.maxHeaderPairs === 'number'
  16. ? cfg.maxHeaderPairs
  17. : MAX_HEADER_PAIRS);
  18. this.buffer = '';
  19. this.header = {};
  20. this.finished = false;
  21. this.ss = new StreamSearch(B_DCRLF);
  22. this.ss.on('info', function(isMatch, data, start, end) {
  23. if (data && !self.maxed) {
  24. if (self.nread + (end - start) > MAX_HEADER_SIZE) {
  25. end = (MAX_HEADER_SIZE - self.nread);
  26. self.nread = MAX_HEADER_SIZE;
  27. } else
  28. self.nread += (end - start);
  29. if (self.nread === MAX_HEADER_SIZE)
  30. self.maxed = true;
  31. self.buffer += data.toString('binary', start, end);
  32. }
  33. if (isMatch)
  34. self._finish();
  35. });
  36. }
  37. inherits(HeaderParser, EventEmitter);
  38. HeaderParser.prototype.push = function(data) {
  39. var r = this.ss.push(data);
  40. if (this.finished)
  41. return r;
  42. };
  43. HeaderParser.prototype.reset = function() {
  44. this.finished = false;
  45. this.buffer = '';
  46. this.header = {};
  47. this.ss.reset();
  48. };
  49. HeaderParser.prototype._finish = function() {
  50. if (this.buffer)
  51. this._parseHeader();
  52. this.ss.matches = this.ss.maxMatches;
  53. var header = this.header;
  54. this.header = {};
  55. this.buffer = '';
  56. this.finished = true;
  57. this.nread = this.npairs = 0;
  58. this.maxed = false;
  59. this.emit('header', header);
  60. };
  61. HeaderParser.prototype._parseHeader = function() {
  62. if (this.npairs === this.maxHeaderPairs)
  63. return;
  64. var lines = this.buffer.split(RE_CRLF), len = lines.length, m, h,
  65. modded = false;
  66. for (var i = 0; i < len; ++i) {
  67. if (lines[i].length === 0)
  68. continue;
  69. if (lines[i][0] === '\t' || lines[i][0] === ' ') {
  70. // folded header content
  71. // RFC2822 says to just remove the CRLF and not the whitespace following
  72. // it, so we follow the RFC and include the leading whitespace ...
  73. this.header[h][this.header[h].length - 1] += lines[i];
  74. } else {
  75. m = RE_HDR.exec(lines[i]);
  76. if (m) {
  77. h = m[1].toLowerCase();
  78. if (m[2]) {
  79. if (this.header[h] === undefined)
  80. this.header[h] = [m[2]];
  81. else
  82. this.header[h].push(m[2]);
  83. } else
  84. this.header[h] = [''];
  85. if (++this.npairs === this.maxHeaderPairs)
  86. break;
  87. } else {
  88. this.buffer = lines[i];
  89. modded = true;
  90. break;
  91. }
  92. }
  93. }
  94. if (!modded)
  95. this.buffer = '';
  96. };
  97. module.exports = HeaderParser;