UNPKG

jspurefix

Version:
244 lines 10.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const enum_1 = require("../../types/enum"); const fix_session_state_1 = require("../fix-session-state"); const segment_description_1 = require("../../buffer/segment-description"); const fix_session_1 = require("../fix-session"); class AsciiSession extends fix_session_1.FixSession { constructor(config) { super(config); this.config = config; this.heartbeat = true; this.requestLogoutType = this.respondLogoutType = enum_1.MsgType.Logout; this.requestLogonType = enum_1.MsgType.Logon; } static asPiped(txt) { return txt.replace(/\x01/g, '|'); } checkSeqNo(msgType, view) { switch (msgType) { case enum_1.MsgType.TestRequest: case enum_1.MsgType.SequenceReset: case enum_1.MsgType.ResendRequest: { return true; } default: { const state = this.sessionState; const lastSeq = state.lastPeerMsgSeqNum; const seqNo = view.getTyped(enum_1.MsgTag.MsgSeqNum); let ret = false; const seqDelta = seqNo - lastSeq; if (seqDelta <= 0) { this.sessionLogger.warning(`terminate as seqDelta (${seqDelta}) < 0 lastSeq = ${lastSeq} seqNo = ${seqNo}`); this.stop(); } else if (seqDelta > 1) { const resend = this.config.factory.resendRequest(lastSeq, seqNo); this.sessionLogger.warning(`sending resend last received ${lastSeq} seqNo ${seqNo}`); this.send(enum_1.MsgType.ResendRequest, resend); } else { ret = true; state.lastPeerMsgSeqNum = seqNo; } return ret; } } } sendReject(msgType, seqNo, msg, reason) { const factory = this.config.factory; const reject = factory.reject(msgType, seqNo, msg, reason); this.sessionLogger.warning(`rejecting with ${JSON.stringify(reject)}`); this.send(enum_1.MsgType.Reject, reject); } checkIntegrity(msgType, view) { const state = this.sessionState; const seqNum = view.getTyped(enum_1.MsgTag.MsgSeqNum); const received = parseInt(view.getString(enum_1.MsgTag.CheckSum), 10); const computed = view.checksum(); if (received !== computed) { const msg = `msgType ${msgType} checksum failed. received = ${received} computed = ${computed}`; this.sendReject(msgType, seqNum, msg, enum_1.SessionRejectReason.ValueIsIncorrect); return false; } if (view.segment.type === segment_description_1.SegmentType.Unknown) { const msg = `msgType ${msgType} unknown`; this.sendReject(msgType, seqNum, msg, enum_1.SessionRejectReason.InvalidMsgType); return false; } const invalid = view.invalid(); if (invalid.length > 0) { const msg = `msgType ${msgType} invalid tag${invalid.length > 1 ? 's' : ''} ${invalid.join(', ')}`; this.sendReject(msgType, seqNum, msg, enum_1.SessionRejectReason.InvalidTagNumber); return false; } const undefinedMsg = view.undefinedForMsg(); if (undefinedMsg) { const msg = `msgType ${msgType} ${undefinedMsg}`; this.sendReject(msgType, seqNum, msg, enum_1.SessionRejectReason.TagNotDefinedForThisMessageType); return false; } const missingRequired = view.missing(); if (missingRequired.length > 0) { const msg = `msgType ${msgType} missing required tag${missingRequired.length > 1 ? 's' : ''} ${missingRequired.join(', ')}`; this.sendReject(msgType, seqNum, msg, enum_1.SessionRejectReason.RequiredTagMissing); return false; } switch (state.state) { case fix_session_state_1.SessionState.PeerLoggedOn: { const targetCompId = view.getString(enum_1.MsgTag.TargetCompID); if (targetCompId !== state.compId) { const msg = `msgType ${msgType} unexpected TargetCompID ${targetCompId}`; this.sendReject(msgType, seqNum, msg, enum_1.SessionRejectReason.CompIDProblem); return false; } const peerCompId = view.getString(enum_1.MsgTag.SenderCompID); if (peerCompId !== state.peerCompId) { const msg = `msgType ${msgType} unexpected SenderCompID ${peerCompId}`; this.sendReject(msgType, seqNum, msg, enum_1.SessionRejectReason.CompIDProblem); return false; } } break; default: { break; } } return true; } onSessionMsg(msgType, view) { const factory = this.config.factory; const logger = this.sessionLogger; switch (msgType) { case enum_1.MsgType.Logon: { this.peerLogon(view); break; } case enum_1.MsgType.Logout: { this.peerLogout(view); break; } case enum_1.MsgType.TestRequest: { const req = view.getString(enum_1.MsgTag.TestReqID); this.send(enum_1.MsgType.Heartbeat, factory.heartbeat(req)); break; } case enum_1.MsgType.Heartbeat: { this.sessionState.lastTestRequestAt = null; break; } case enum_1.MsgType.ResendRequest: { logger.info(`peer sends '${msgType}' resend reset.`); const endSeqNo = view.getTyped(enum_1.MsgTag.EndSeqNo); const resend = factory.sequenceReset(endSeqNo); this.send(enum_1.MsgType.SequenceReset, resend); break; } case enum_1.MsgType.SequenceReset: { const newSeqNo = view.getTyped(enum_1.MsgTag.NewSeqNo); logger.info(`peer sends '${msgType}' sequence reset. newSeqNo = ${newSeqNo}`); this.sessionState.lastPeerMsgSeqNum = newSeqNo; break; } case enum_1.MsgType.Reject: { logger.info(`peer rejects type '${msgType}' with text '${view.getTyped(enum_1.MsgTag.Text)}'`); break; } } } onMsg(msgType, view) { if (!this.checkSeqNo(msgType, view)) { this.sessionLogger.info(`message '${msgType}' failed checkSeqNo.`); return; } if (this.checkMsgIntegrity && !this.checkIntegrity(msgType, view)) { this.sessionLogger.info(`message '${msgType}' failed checkIntegrity.`); switch (msgType) { case enum_1.MsgType.Logon: { this.sessionState.state = fix_session_state_1.SessionState.PeerLogonRejected; this.timer = setInterval(() => { this.tick(); }, 200); break; } } return; } switch (msgType) { case enum_1.MsgType.Logon: case enum_1.MsgType.Logout: case enum_1.MsgType.TestRequest: case enum_1.MsgType.Reject: case enum_1.MsgType.SequenceReset: case enum_1.MsgType.Heartbeat: case enum_1.MsgType.ResendRequest: { this.onSessionMsg(msgType, view); break; } default: { this.checkForwardMsg(msgType, view); break; } } } peerLogon(view) { const logger = this.sessionLogger; const heartBtInt = view.getTyped(enum_1.MsgTag.HeartBtInt); const peerCompId = view.getTyped(enum_1.MsgTag.SenderCompID); const userName = view.getString(enum_1.MsgTag.Username); logger.info(`peerLogon Username = ${userName}, heartBtInt = ${heartBtInt}, peerCompId = ${peerCompId}, userName = ${userName}`); const state = this.sessionState; state.state = fix_session_state_1.SessionState.PeerLoggedOn; state.peerHeartBeatSecs = view.getTyped(enum_1.MsgTag.HeartBtInt); state.peerCompId = view.getTyped(enum_1.MsgTag.SenderCompID); if (this.acceptor) { this.send(enum_1.MsgType.Logon, this.config.factory.logon()); } if (this.heartbeat) { logger.debug(`start heartbeat timer.`); this.timer = setInterval(() => { this.tick(); }, 200); } logger.info(`system ready, inform app`); this.onReady(view); } tick() { const sessionState = this.sessionState; const action = sessionState.calcAction(new Date()); const application = this.transport.config.description.application; const factory = this.config.factory; const logger = this.sessionLogger; switch (action) { case fix_session_state_1.TickAction.Nothing: { break; } case fix_session_state_1.TickAction.TestRequest: { logger.debug(`send test req. state = ${sessionState.toString()}`); this.send(enum_1.MsgType.TestRequest, factory.testRequest()); break; } case fix_session_state_1.TickAction.Heartbeat: { logger.debug(`send heartbeat. state = ${sessionState.toString()}`); this.send(enum_1.MsgType.Heartbeat, factory.heartbeat(sessionState.now.toUTCString())); break; } case fix_session_state_1.TickAction.TerminateOnError: { logger.info(sessionState.toString()); this.terminate(new Error(`${application.name}: peer not responding`)); break; } case fix_session_state_1.TickAction.Stop: { logger.info(sessionState.toString()); logger.info('stopping'); this.stop(); break; } default: throw new Error(`unexpected action`); } } } exports.AsciiSession = AsciiSession; //# sourceMappingURL=ascii-session.js.map