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.
111 lines
2.9 KiB
111 lines
2.9 KiB
1 month ago
|
var EventEmitter = require('events').EventEmitter,
|
||
|
inherits = require('util').inherits;
|
||
|
|
||
|
var StreamSearch = require('streamsearch');
|
||
|
|
||
|
var B_DCRLF = new Buffer('\r\n\r\n'),
|
||
|
RE_CRLF = /\r\n/g,
|
||
|
RE_HDR = /^([^:]+):[ \t]?([\x00-\xFF]+)?$/,
|
||
|
MAX_HEADER_PAIRS = 2000, // from node's http.js
|
||
|
MAX_HEADER_SIZE = 80 * 1024; // from node's http_parser
|
||
|
|
||
|
function HeaderParser(cfg) {
|
||
|
EventEmitter.call(this);
|
||
|
|
||
|
var self = this;
|
||
|
this.nread = 0;
|
||
|
this.maxed = false;
|
||
|
this.npairs = 0;
|
||
|
this.maxHeaderPairs = (cfg && typeof cfg.maxHeaderPairs === 'number'
|
||
|
? cfg.maxHeaderPairs
|
||
|
: MAX_HEADER_PAIRS);
|
||
|
this.buffer = '';
|
||
|
this.header = {};
|
||
|
this.finished = false;
|
||
|
this.ss = new StreamSearch(B_DCRLF);
|
||
|
this.ss.on('info', function(isMatch, data, start, end) {
|
||
|
if (data && !self.maxed) {
|
||
|
if (self.nread + (end - start) > MAX_HEADER_SIZE) {
|
||
|
end = (MAX_HEADER_SIZE - self.nread);
|
||
|
self.nread = MAX_HEADER_SIZE;
|
||
|
} else
|
||
|
self.nread += (end - start);
|
||
|
|
||
|
if (self.nread === MAX_HEADER_SIZE)
|
||
|
self.maxed = true;
|
||
|
|
||
|
self.buffer += data.toString('binary', start, end);
|
||
|
}
|
||
|
if (isMatch)
|
||
|
self._finish();
|
||
|
});
|
||
|
}
|
||
|
inherits(HeaderParser, EventEmitter);
|
||
|
|
||
|
HeaderParser.prototype.push = function(data) {
|
||
|
var r = this.ss.push(data);
|
||
|
if (this.finished)
|
||
|
return r;
|
||
|
};
|
||
|
|
||
|
HeaderParser.prototype.reset = function() {
|
||
|
this.finished = false;
|
||
|
this.buffer = '';
|
||
|
this.header = {};
|
||
|
this.ss.reset();
|
||
|
};
|
||
|
|
||
|
HeaderParser.prototype._finish = function() {
|
||
|
if (this.buffer)
|
||
|
this._parseHeader();
|
||
|
this.ss.matches = this.ss.maxMatches;
|
||
|
var header = this.header;
|
||
|
this.header = {};
|
||
|
this.buffer = '';
|
||
|
this.finished = true;
|
||
|
this.nread = this.npairs = 0;
|
||
|
this.maxed = false;
|
||
|
this.emit('header', header);
|
||
|
};
|
||
|
|
||
|
HeaderParser.prototype._parseHeader = function() {
|
||
|
if (this.npairs === this.maxHeaderPairs)
|
||
|
return;
|
||
|
|
||
|
var lines = this.buffer.split(RE_CRLF), len = lines.length, m, h,
|
||
|
modded = false;
|
||
|
|
||
|
for (var i = 0; i < len; ++i) {
|
||
|
if (lines[i].length === 0)
|
||
|
continue;
|
||
|
if (lines[i][0] === '\t' || lines[i][0] === ' ') {
|
||
|
// folded header content
|
||
|
// RFC2822 says to just remove the CRLF and not the whitespace following
|
||
|
// it, so we follow the RFC and include the leading whitespace ...
|
||
|
this.header[h][this.header[h].length - 1] += lines[i];
|
||
|
} else {
|
||
|
m = RE_HDR.exec(lines[i]);
|
||
|
if (m) {
|
||
|
h = m[1].toLowerCase();
|
||
|
if (m[2]) {
|
||
|
if (this.header[h] === undefined)
|
||
|
this.header[h] = [m[2]];
|
||
|
else
|
||
|
this.header[h].push(m[2]);
|
||
|
} else
|
||
|
this.header[h] = [''];
|
||
|
if (++this.npairs === this.maxHeaderPairs)
|
||
|
break;
|
||
|
} else {
|
||
|
this.buffer = lines[i];
|
||
|
modded = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (!modded)
|
||
|
this.buffer = '';
|
||
|
};
|
||
|
|
||
|
module.exports = HeaderParser;
|