UNPKG

limacharlie

Version:

An API for limacharlie.io, a cloud hosted endpoint detection and response platform.

391 lines (332 loc) 13.8 kB
"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;