nntp-fast
Version:
A lightweight nntp client for node made with attention to speed and ease of use. Works with promises and streams.
202 lines (201 loc) • 8.53 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var events_1 = require("events");
var DelimiterSearchState;
(function (DelimiterSearchState) {
DelimiterSearchState[DelimiterSearchState["START"] = 0] = "START";
DelimiterSearchState[DelimiterSearchState["CR"] = 1] = "CR";
DelimiterSearchState[DelimiterSearchState["CR_LF"] = 2] = "CR_LF";
DelimiterSearchState[DelimiterSearchState["CR_LF_DOT"] = 3] = "CR_LF_DOT";
DelimiterSearchState[DelimiterSearchState["CR_LF_DOT_CR"] = 4] = "CR_LF_DOT_CR";
})(DelimiterSearchState || (DelimiterSearchState = {}));
var Delimiter;
(function (Delimiter) {
Delimiter[Delimiter["CRLF"] = 0] = "CRLF";
Delimiter[Delimiter["MLDB"] = 1] = "MLDB"; // multiline datablock \r\n.\r\n
})(Delimiter || (Delimiter = {}));
var CR = "\r".charCodeAt(0);
var LF = "\n".charCodeAt(0);
var DOT = ".".charCodeAt(0);
/**
* Searches for needle in stream while also dot-unstuffing.
*/
var DotUnstuffingStreamSearch = /** @class */ (function (_super) {
__extends(DotUnstuffingStreamSearch, _super);
/**
*
* @param needle Must me `Buffer.from("\r\n.\r\n")`
*/
function DotUnstuffingStreamSearch(needle) {
var _this = _super.call(this) || this;
if (needle.toString() != "\r\n.\r\n") {
"unsupported CRLF needle in DotUnstuffingStreamSearch";
}
_this._needle = Delimiter.MLDB;
_this.maxMatches = Infinity;
_this.reset();
return _this;
}
/**
* Resets internal state
*/
DotUnstuffingStreamSearch.prototype.reset = function () {
this._bufpos = 0;
this._searchState = DelimiterSearchState.START;
this.matches = 0;
};
/**
* Processes `chunk`.
* @param chunk
* @param pos
*
* @fires info
*
* @returns The last processed index in chunk + 1.
*/
DotUnstuffingStreamSearch.prototype.push = function (chunk, pos) {
var r = 0;
if (!Buffer.isBuffer(chunk))
chunk = Buffer.from(chunk, 'binary');
var chlen = chunk.length;
this._bufpos = pos || 0;
while (r != chlen && this.matches < this.maxMatches) {
r = this._feed(chunk);
}
return r;
};
/**
* @private
* @param data
*/
DotUnstuffingStreamSearch.prototype._feed = function (data) {
var startData = this._bufpos;
var lastData = this._bufpos - 1;
while (this._bufpos < data.length) {
var symbol = data[this._bufpos];
switch (this._searchState) {
case DelimiterSearchState.START:
if (symbol == CR) {
this._searchState = DelimiterSearchState.CR;
}
else {
lastData = this._bufpos;
}
break;
case DelimiterSearchState.CR:
if (symbol == LF) {
if (this._needle == Delimiter.CRLF) {
// Found CRLF
console.log("unsupported CRLF needl in DotUnstuffingStreamSearch");
this._searchState = DelimiterSearchState.START;
}
else {
this._searchState = DelimiterSearchState.CR_LF;
}
}
else {
console.log("Malformed Data: Standalone CR character not allowed");
// Allow anyway
if (startData <= lastData) {
this.emit("info", false, data, startData, lastData + 1);
}
this.emit("info", false, Buffer.from("\r"), 0, 1);
if (symbol == CR) {
this._searchState = DelimiterSearchState.CR;
}
else {
startData = this._bufpos;
lastData = this._bufpos;
this._searchState = DelimiterSearchState.START;
}
}
break;
case DelimiterSearchState.CR_LF:
if (symbol == DOT) {
this._searchState = DelimiterSearchState.CR_LF_DOT;
}
else {
if (symbol == CR) {
this._searchState = DelimiterSearchState.CR;
}
else {
//false alarm
if (startData <= lastData) {
this.emit("info", false, data, startData, lastData + 1);
}
this.emit("info", false, Buffer.from("\r\n"), 0, 2);
startData = this._bufpos;
lastData = this._bufpos;
this._searchState = DelimiterSearchState.START;
}
}
break;
case DelimiterSearchState.CR_LF_DOT:
if (symbol == CR) {
this._searchState = DelimiterSearchState.CR_LF_DOT_CR;
}
else {
this._searchState = DelimiterSearchState.START;
// dot stuffing must be undone https://tools.ietf.org/html/rfc3977#section-3.1.1
if (startData <= lastData) {
this.emit("info", false, data, startData, lastData + 1);
}
this.emit("info", false, Buffer.from("\r\n"), 0, 2);
return this._bufpos;
}
break;
case DelimiterSearchState.CR_LF_DOT_CR:
if (symbol == LF) {
// Found MLDB delimiter
this.matches++;
this._searchState = DelimiterSearchState.START;
this.emit("info", true, data, startData, lastData + 1);
startData = this._bufpos + 1;
lastData = this._bufpos;
this._bufpos++;
return this._bufpos;
}
else {
console.log("Malformed Data: Standalone CR character not allowed (after dot)");
// Allow anyway
//false alarm
if (startData <= lastData) {
this.emit("info", false, data, startData, lastData + 1);
}
this.emit("info", false, Buffer.from("\r"), 0, 1);
if (symbol == CR) {
this._searchState = DelimiterSearchState.CR;
}
else {
this._searchState = DelimiterSearchState.START;
startData = this._bufpos;
lastData = this._bufpos;
}
}
break;
default:
break;
}
this._bufpos++;
}
if (startData <= lastData) {
this.emit("info", false, data, startData, lastData + 1);
}
return this._bufpos;
};
return DotUnstuffingStreamSearch;
}(events_1.EventEmitter));
exports.default = DotUnstuffingStreamSearch;