jspurefix
Version:
pure node js fix engine
376 lines • 16.4 kB
JavaScript
;
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