UNPKG

jspurefix

Version:
376 lines 16.4 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FixSession = void 0; const fix_session_state_1 = require("./fix-session-state"); const types_1 = require("../../types"); const events = require("events"); const session_state_1 = require("./session-state"); const segment_type_1 = require("../../buffer/segment/segment-type"); class FixSession extends events.EventEmitter { constructor(config) { var _a, _b, _c; super(); this.config = config; this.logReceivedMsgs = false; this.timer = null; this.transport = null; this.manageSession = true; this.checkMsgIntegrity = false; const description = config.description; this.me = (_b = (_a = description === null || description === void 0 ? void 0 : description.application) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : 'me'; this.sessionState = new fix_session_state_1.FixSessionState({ heartBeat: config.description.HeartBtInt, lastPeerMsgSeqNum: config.description.LastReceivedSeqNum }); this.sessionLogger = config.logFactory.logger(`${this.me}:FixSession`); this.initiator = ((_c = description === null || description === void 0 ? void 0 : description.application) === null || _c === void 0 ? void 0 : _c.type) === 'initiator'; this.acceptor = !this.initiator; this.checkMsgIntegrity = this.acceptor; this.sessionState.compId = description.SenderCompId; } stateStr(theState) { return session_state_1.SessionState[theState]; } assignState(newState) { const currentState = this.sessionState.state; const currentStateStr = this.stateStr(currentState); const logger = this.sessionLogger; const msg = `current state ${currentStateStr} (${currentState}) moves to ${session_state_1.SessionState[newState]} (${newState})`; logger.info(msg); this.sessionState.state = newState; } setState(state) { const logger = this.sessionLogger; const currentState = this.sessionState.state; const currentStateStr = this.stateStr(currentState); if (state === currentState) return; switch (currentState) { case session_state_1.SessionState.ConfirmingLogout: case session_state_1.SessionState.Stopped: if (state !== session_state_1.SessionState.NetworkConnectionEstablished) { logger.info(`ignoring request to change state as now already in ${currentStateStr}`); } else { this.assignState(state); } break; default: { this.assignState(state); } } } getState() { return this.sessionState.state; } lastSentSeqNum() { return this.sessionState.lastSentSeqNum(); } lastPeerSeqNum() { return this.sessionState.lastPeerMsgSeqNum; } sendLogon() { var _a; const lo = (_a = this.config.factory) === null || _a === void 0 ? void 0 : _a.logon(); if (lo) { this.send(this.requestLogonType, lo); } } waitPromise() { return __awaiter(this, void 0, void 0, function* () { const logger = this.sessionLogger; return yield new Promise((resolve, reject) => { if (this.initiator) { logger.debug(`initiator sending logon state = ${this.stateString()}`); this.sendLogon(); this.setState(session_state_1.SessionState.InitiationLogonSent); } else { logger.debug(`acceptor waits for logon state = ${this.stateString()}`); this.setState(session_state_1.SessionState.WaitingForALogon); } this.on('error', (e) => { logger.error(e); reject(e); }); this.on('done', () => { var _a; resolve((_a = this.transport) === null || _a === void 0 ? void 0 : _a.id); }); }); }); } run(transport) { return __awaiter(this, void 0, void 0, function* () { const logger = this.sessionLogger; if (this.transport) { logger.info(`reset from previous transport. state ${this.stateString()}`); this.reset(); } this.transport = transport; this.subscribe(); return yield this.waitPromise(); }); } expectedEndState() { switch (this.sessionState.state) { case session_state_1.SessionState.Stopped: case session_state_1.SessionState.ConfirmingLogout: return true; default: return false; } } rxOnEnd() { const logger = this.sessionLogger; logger.info(`rx end received sessionState = [${this.sessionState.toString()}]`); const expectedState = this.expectedEndState(); if (expectedState) { logger.info(`rx receives end state = ${this.stateString()} - stop session`); this.stop(); } else { this.setState(session_state_1.SessionState.DetectBrokenNetworkConnection); const e = new Error(`unexpected state - transport failed? = ${this.stateString()}`); logger.info(`rx error ${e.message}`); this.terminate(e); } } rxOnMsg(msgType, view) { var _a, _b; const logger = this.sessionLogger; if (this.logReceivedMsgs) { const name = view.segment.type !== segment_type_1.SegmentType.Unknown ? (_b = (_a = view === null || view === void 0 ? void 0 : view.segment) === null || _a === void 0 ? void 0 : _a.set) === null || _b === void 0 ? void 0 : _b.name : 'unknown'; logger.info(`${msgType}: ${name}`); logger.info(`${view.toString()}`); } this.sessionState.lastReceivedAt = new Date(); if (this.manageSession) { this.onMsg(msgType, view); } else { this.checkForwardMsg(msgType, view); } } rxOnDone() { const logger = this.sessionLogger; logger.info('rx done received'); this.done(); } rxOnError(e) { const logger = this.sessionLogger; logger.warning(`rx error event: ${e.message} ${JSON.stringify(e)}`); this.terminate(e); } rxOnDecoded(msgType, data, ptr) { const logger = this.sessionLogger; logger.debug(`rx: [${msgType}] ${ptr} bytes`); this.onDecoded(msgType, data.toString(ptr)); } txOnError(e) { const logger = this.sessionLogger; logger.warning(`tx error event: ${e.message} ${JSON.stringify(e)}`); this.terminate(e); } txOnEncoded(msgType, data, hdr) { const logger = this.sessionLogger; this.sessionState.lastHeader = hdr; logger.debug(`tx: [${msgType}] ${data.length} bytes seqNo = ${this.lastSentSeqNum()}`); this.onEncoded(msgType, data); } unsubscribe() { const logger = this.sessionLogger; logger.info(`unsubscribe sessionState = [${this.sessionState.toString()}]`); const transport = this.transport; const rx = transport === null || transport === void 0 ? void 0 : transport.receiver; const tx = transport === null || transport === void 0 ? void 0 : transport.transmitter; rx === null || rx === void 0 ? void 0 : rx.removeListener('msg', this.rxOnMsg); rx === null || rx === void 0 ? void 0 : rx.removeListener('error', this.rxOnError); rx === null || rx === void 0 ? void 0 : rx.removeListener('done', this.rxOnDone); rx === null || rx === void 0 ? void 0 : rx.removeListener('end', this.rxOnEnd); rx === null || rx === void 0 ? void 0 : rx.removeListener('decoded', this.rxOnDecoded); tx === null || tx === void 0 ? void 0 : tx.removeListener('error', this.txOnError); tx === null || tx === void 0 ? void 0 : tx.removeListener('encoded', this.txOnEncoded); } subscribe() { const transport = this.transport; const rx = transport === null || transport === void 0 ? void 0 : transport.receiver; const tx = transport === null || transport === void 0 ? void 0 : transport.transmitter; const inst = this; rx === null || rx === void 0 ? void 0 : rx.on('msg', (msgType, view) => { inst.rxOnMsg(msgType, view); }); rx === null || rx === void 0 ? void 0 : rx.on('error', (e) => { inst.rxOnError(e); }); rx === null || rx === void 0 ? void 0 : rx.on('done', () => { inst.rxOnDone(); }); rx === null || rx === void 0 ? void 0 : rx.on('end', () => { inst.rxOnEnd(); }); rx === null || rx === void 0 ? void 0 : rx.on('decoded', (msgType, data, ptr) => { inst.rxOnDecoded(msgType, data, ptr); }); tx === null || tx === void 0 ? void 0 : tx.on('error', (e) => { inst.txOnError(e); }); tx === null || tx === void 0 ? void 0 : tx.on('encoded', (msgType, data, hdr) => { inst.txOnEncoded(msgType, data, hdr); }); } validStateApplicationMsg() { switch (this.sessionState.state) { case session_state_1.SessionState.Idle: case session_state_1.SessionState.InitiateConnection: case session_state_1.SessionState.InitiationLogonSent: case session_state_1.SessionState.WaitingForALogon: case session_state_1.SessionState.HandleResendRequest: case session_state_1.SessionState.AwaitingProcessingResponseToTestRequest: case session_state_1.SessionState.AwaitingProcessingResponseToResendRequest: return false; default: return true; } } stateString() { return session_state_1.SessionState[this.sessionState.state]; } checkForwardMsg(msgType, view) { this.sessionLogger.info(`forwarding msgType = '${msgType}' to application`); this.setState(session_state_1.SessionState.ActiveNormalSession); this.onApplicationMsg(msgType, view); } stopTimer() { if (this.timer) { this.sessionLogger.info('stopTimer'); clearInterval(this.timer); this.timer = null; } } terminate(error) { if (this.sessionState.state === session_state_1.SessionState.Stopped) return; this.sessionLogger.error(error); this.stopTimer(); if (this.transport) { this.transport.end(); } this.transport = null; this.setState(session_state_1.SessionState.Stopped); this.emit('error', error); } peerLogout(view) { const msg = view.getString(types_1.MsgTag.Text); const state = this.sessionState.state; switch (state) { case session_state_1.SessionState.WaitingLogoutConfirm: { this.sessionLogger.info(`peer confirms logout Text = '${msg}'`); this.stop(); break; } case session_state_1.SessionState.InitiationLogonResponse: case session_state_1.SessionState.ActiveNormalSession: case session_state_1.SessionState.InitiationLogonReceived: { this.setState(session_state_1.SessionState.ConfirmingLogout); this.sessionLogger.info(`peer initiates logout Text = '${msg}'`); this.sessionLogout(); } } } send(msgType, obj) { var _a; const state = this.sessionState.state; switch (state) { case session_state_1.SessionState.Stopped: { this.sessionLogger.warning(`can't send in state ${this.stateString()}`); break; } default: { this.sessionState.LastSentAt = new Date(); (_a = this.transport) === null || _a === void 0 ? void 0 : _a.transmitter.send(msgType, obj); break; } } } sendLogout(msg) { const factory = this.config.factory; this.sessionLogger.info(`sending logout with ${msg}`); const lo = factory === null || factory === void 0 ? void 0 : factory.logout(this.requestLogoutType, msg); if (lo) { this.send(this.requestLogoutType, lo); } } sessionLogout() { const sessionState = this.sessionState; if (sessionState.logoutSentAt) { return; } switch (sessionState.state) { case session_state_1.SessionState.ActiveNormalSession: case session_state_1.SessionState.InitiationLogonResponse: case session_state_1.SessionState.InitiationLogonReceived: { this.setState(session_state_1.SessionState.WaitingLogoutConfirm); sessionState.logoutSentAt = new Date(); const msg = `${this.me} initiate logout`; this.sessionLogger.info(msg); this.sendLogout(msg); break; } case session_state_1.SessionState.ConfirmingLogout: { this.setState(session_state_1.SessionState.ConfirmingLogout); sessionState.logoutSentAt = new Date(); const msg = `${this.me} confirming logout`; this.sessionLogger.info(msg); this.sendLogout(msg); break; } default: { this.sessionLogger.info(`sessionLogout ignored as in state ${sessionState.state}`); } } } done() { switch (this.sessionState.state) { case session_state_1.SessionState.InitiationLogonResponse: case session_state_1.SessionState.ActiveNormalSession: case session_state_1.SessionState.InitiationLogonReceived: { this.sessionLogout(); break; } case session_state_1.SessionState.Stopped: this.sessionLogger.info('done. session is now stopped'); break; default: { this.stop(); break; } } this.sessionLogger.info(`done. check logout sequence state ${this.stateString()}`); } reset(resetSeqNum) { this.stopTimer(); this.transport = null; const resetFlag = this.config.description.ResetSeqNumFlag; const seqNum = resetFlag ? 0 : resetSeqNum !== null && resetSeqNum !== void 0 ? resetSeqNum : this.sessionState.lastPeerMsgSeqNum; this.sessionState.reset(seqNum); this.setState(session_state_1.SessionState.NetworkConnectionEstablished); } stop(error = null) { var _a; if (this.sessionState.state === session_state_1.SessionState.Stopped) { return; } this.stopTimer(); this.unsubscribe(); this.sessionLogger.info('stop: kill transport'); (_a = this.transport) === null || _a === void 0 ? void 0 : _a.end(); if (error) { this.sessionLogger.info(`stop: emit error ${error.message}`); this.emit('error', error); } else { this.emit('done'); } this.setState(session_state_1.SessionState.Stopped); this.onStopped(error !== null && error !== void 0 ? error : undefined); this.transport = null; } } exports.FixSession = FixSession; //# sourceMappingURL=fix-session.js.map