limacharlie
Version:
An API for limacharlie.io, a cloud hosted endpoint detection and response platform.
391 lines (332 loc) • 13.8 kB
JavaScript
"use strict";
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var request = require("request");
var JSONStream = require("JSONStream");
var Spout = function () {
function Spout(man, dataType, dataCb, errorCb, invId, tag, cat, sid, isReOpen) {
var _this = this;
_classCallCheck(this, Spout);
this._man = man;
this._errorCb = errorCb;
this._dataType = dataType;
this._invId = invId;
this._tag = tag;
this._cat = cat;
this._sid = sid;
this._spoutUrl = null;
this._dataCb = dataCb;
this.dropped = 0;
this.hasConnected = false;
this._specificCallbacks = {};
this._cleanupTimer = setInterval(function () {
_this._cleanup();
}, 30000);
this._isRetrying = false;
this._refreshSpout();
}
_createClass(Spout, [{
key: "_sleep",
value: function _sleep(ms) {
return new Promise(function (resolve) {
return setTimeout(resolve, ms);
});
}
}, {
key: "_refreshSpout",
value: function () {
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee4(isRetry) {
var _this2 = this;
var url, spoutConf, isNode;
return regeneratorRuntime.wrap(function _callee4$(_context4) {
while (1) {
switch (_context4.prev = _context4.next) {
case 0:
url = "https://stream.limacharlie.io/" + this._man._oid;
spoutConf = {
type: this._dataType
};
if (this._man._secretApiKey) {
spoutConf["api_key"] = this._man._secretApiKey;
} else {
spoutConf["jwt"] = this._man._jwt;
}
if (this._invId) {
spoutConf["inv_id"] = this._invId;
}
if (this._tag) {
spoutConf["tag"] = this._tag;
}
if (this._cat) {
spoutConf["cat"] = this._cat;
}
if (this._sid) {
spoutConf["sid"] = this._sid;
}
isNode = false;
if (typeof window === "undefined") {
isNode = true;
}
// The Node HTTP POST and the browser one behave differently.
// It is IMPOSSIBLE to prevent the browser from following the
// redirect and since a stream is at that redirect we would
// hang forever.
if (!isNode) {
_context4.next = 28;
break;
}
_context4.prev = 10;
request.post(url).form(spoutConf).pipe(JSONStream.parse()).on("data", function (data) {
_this2._processData(data);
}).on("error", function () {
var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(error) {
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
if (!_this2._isRetrying) {
_context.next = 2;
break;
}
return _context.abrupt("return");
case 2:
_this2._isRetrying = true;
_this2._stream.destroy();
if (!isRetry) {
_context.next = 7;
break;
}
_context.next = 7;
return _this2._sleep(1000 * 2);
case 7:
_context.next = 9;
return _this2._refreshSpout(true);
case 9:
if (_this2._errorCb) {
_this2._errorCb(error);
} else if (_this2._man.onError) {
_this2._man.onError(error);
} else {
// eslint-disable-next-line no-console
console.error(error);
}
case 10:
case "end":
return _context.stop();
}
}
}, _callee, _this2);
}));
return function (_x2) {
return _ref2.apply(this, arguments);
};
}());
_context4.next = 26;
break;
case 14:
_context4.prev = 14;
_context4.t0 = _context4["catch"](10);
if (!this._isRetrying) {
_context4.next = 18;
break;
}
return _context4.abrupt("return");
case 18:
this._isRetrying = true;
this._stream.destroy();
if (!isRetry) {
_context4.next = 23;
break;
}
_context4.next = 23;
return this._sleep(1000 * 2);
case 23:
_context4.next = 25;
return this._refreshSpout(true);
case 25:
if (this._errorCb) {
this._errorCb(_context4.t0);
} else if (this._man.onError) {
this._man.onError(_context4.t0);
} else {
// eslint-disable-next-line no-console
console.error(_context4.t0);
}
case 26:
_context4.next = 29;
break;
case 28:
this._stream = request.post(url).form(spoutConf).on("error", function () {
var _ref3 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2(error) {
return regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
if (!_this2._isRetrying) {
_context2.next = 2;
break;
}
return _context2.abrupt("return");
case 2:
_this2._isRetrying = true;
_this2._stream.destroy();
if (!isRetry) {
_context2.next = 7;
break;
}
_context2.next = 7;
return _this2._sleep(1000 * 2);
case 7:
_context2.next = 9;
return _this2._refreshSpout(true);
case 9:
if (_this2._errorCb) {
_this2._errorCb(error);
} else if (_this2._man.onError) {
_this2._man.onError(error);
} else {
// eslint-disable-next-line no-console
console.error(error);
}
case 10:
case "end":
return _context2.stop();
}
}
}, _callee2, _this2);
}));
return function (_x3) {
return _ref3.apply(this, arguments);
};
}()).pipe(JSONStream.parse()).on("data", function (data) {
_this2._processData(data);
}).on("error", function () {
var _ref4 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3(error) {
return regeneratorRuntime.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
if (!_this2._isRetrying) {
_context3.next = 2;
break;
}
return _context3.abrupt("return");
case 2:
_this2._isRetrying = true;
_this2._stream.destroy();
if (!isRetry) {
_context3.next = 7;
break;
}
_context3.next = 7;
return _this2._sleep(1000 * 2);
case 7:
_context3.next = 9;
return _this2._refreshSpout(true);
case 9:
if (_this2._errorCb) {
_this2._errorCb(error);
} else if (_this2._man.onError) {
_this2._man.onError(error);
} else {
// eslint-disable-next-line no-console
console.error(error);
}
case 10:
case "end":
return _context3.stop();
}
}
}, _callee3, _this2);
}));
return function (_x4) {
return _ref4.apply(this, arguments);
};
}());
case 29:
this._isRetrying = false;
case 30:
case "end":
return _context4.stop();
}
}
}, _callee4, this, [[10, 14]]);
}));
function _refreshSpout(_x) {
return _ref.apply(this, arguments);
}
return _refreshSpout;
}()
}, {
key: "registerSpecificCallback",
value: function registerSpecificCallback(trackingId, ttl, cb) {
this._specificCallbacks[trackingId] = {
cb: cb,
ttl: ttl + new Date()
};
}
}, {
key: "_processData",
value: function _processData(data) {
if ("__trace" in data) {
if (data.__trace.includes("dropped")) {
this.dropped += data.n;
} else if (data.__trace === "connected") {
this.hasConnected = true;
}
} else if (Object.keys(data).length === 1 && "error" in data) {
var error = data.error;
if (this._errorCb) {
this._errorCb(error);
} else if (this._man.onError) {
this._man.onError(error);
} else {
// eslint-disable-next-line no-console
console.error(error);
}
} else {
var tracking = data.routing.investigation_id;
if (tracking && tracking in this._specificCallbacks) {
// When a specific callback is registered, we do not feed
// the genecal callback.
this._specificCallbacks[tracking].cb(data);
} else if (this._dataCb) {
this._dataCb(data);
} else {
// No specific callback for this tracking and no
// general callback registered, so ignore.
}
}
}
}, {
key: "_cleanup",
value: function _cleanup() {
var now = 0 + new Date();
for (var tracking in this._specificCallbacks) {
var record = this._specificCallbacks[tracking];
if (record.ttl < now) {
delete this._specificCallbacks[tracking];
}
}
}
}, {
key: "shutdown",
value: function shutdown() {
if (this._cleanupTimer) {
clearInterval(this._cleanupTimer);
}
if (this._stream) {
try {
this._stream.destroy();
} catch (e) {
// eslint-disable-next-line no-console
console.error(e);
}
this._stream = null;
}
}
}]);
return Spout;
}();
module.exports = Spout;