jspurefix 1.3.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/data/session/genkey.ps1 +179 -0
- package/dist/buffer/ascii/ascii-encoder.d.ts +1 -1
- package/dist/buffer/ascii/ascii-encoder.js +42 -30
- package/dist/buffer/ascii/ascii-encoder.js.map +1 -1
- package/dist/buffer/ascii/ascii-view.d.ts +2 -2
- package/dist/buffer/ascii/ascii-view.js.map +1 -1
- package/dist/buffer/ascii/time-formatter.js.map +1 -1
- package/dist/buffer/elastic-buffer.js +1 -1
- package/dist/buffer/elastic-buffer.js.map +1 -1
- package/dist/buffer/encode-proxy.js.map +1 -1
- package/dist/buffer/fixml/fixml-encoder.js +1 -1
- package/dist/buffer/fixml/fixml-encoder.js.map +1 -1
- package/dist/buffer/fixml/fixml-view.d.ts +2 -0
- package/dist/buffer/fixml/fixml-view.js +3 -0
- package/dist/buffer/fixml/fixml-view.js.map +1 -1
- package/dist/buffer/msg-view.d.ts +2 -0
- package/dist/buffer/msg-view.js +2 -2
- package/dist/buffer/msg-view.js.map +1 -1
- package/dist/dict-parser.js +9 -9
- package/dist/dict-parser.js.map +1 -1
- package/dist/dictionary/compiler/msg-compiler.js +2 -2
- package/dist/dictionary/compiler/msg-compiler.js.map +1 -1
- package/dist/dictionary/parser/fix-repository/repository-xml-parser.js +1 -1
- package/dist/dictionary/parser/fix-repository/repository-xml-parser.js.map +1 -1
- package/dist/dictionary/parser/quickfix/quick-fix-xml-file-parser.js +1 -1
- package/dist/dictionary/parser/quickfix/quick-fix-xml-file-parser.js.map +1 -1
- package/dist/jsfix-cmd.js +3 -3
- package/dist/jsfix-cmd.js.map +1 -1
- package/dist/sample/http/oms/app.js +2 -2
- package/dist/sample/http/oms/app.js.map +1 -1
- package/dist/sample/launcher.js +2 -2
- package/dist/sample/launcher.js.map +1 -1
- package/dist/sample/tcp/qf-md/app.js +2 -2
- package/dist/sample/tcp/qf-md/app.js.map +1 -1
- package/dist/sample/tcp/recovering-skeleton/respawn-2.d.ts +2 -0
- package/dist/sample/tcp/recovering-skeleton/respawn-2.js +3 -0
- package/dist/sample/tcp/recovering-skeleton/respawn-2.js.map +1 -0
- package/dist/sample/tcp/recovering-skeleton/respawn-acceptor.js +1 -1
- package/dist/sample/tcp/recovering-skeleton/respawn-acceptor.js.map +1 -1
- package/dist/sample/tcp/skeleton/app.js +2 -2
- package/dist/sample/tcp/skeleton/app.js.map +1 -1
- package/dist/sample/tcp/skeleton/skeleton-session.d.ts +5 -1
- package/dist/sample/tcp/skeleton/skeleton-session.js +21 -2
- package/dist/sample/tcp/skeleton/skeleton-session.js.map +1 -1
- package/dist/sample/tcp/tls-trade-capture/app.js +2 -2
- package/dist/sample/tcp/tls-trade-capture/app.js.map +1 -1
- package/dist/sample/tcp/trade-capture/app.js +2 -2
- package/dist/sample/tcp/trade-capture/app.js.map +1 -1
- package/dist/sample/tcp/trade-capture/trade-capture-client.d.ts +1 -0
- package/dist/sample/tcp/trade-capture/trade-capture-client.js +7 -3
- package/dist/sample/tcp/trade-capture/trade-capture-client.js.map +1 -1
- package/dist/store/fix-msg-ascii-store-recovery.d.ts +12 -0
- package/dist/store/fix-msg-ascii-store-recovery.js +60 -0
- package/dist/store/fix-msg-ascii-store-recovery.js.map +1 -0
- package/dist/store/fix-msg-ascii-store-replay.d.ts +13 -0
- package/dist/store/fix-msg-ascii-store-replay.js +60 -0
- package/dist/store/fix-msg-ascii-store-replay.js.map +1 -0
- package/dist/store/fix-msg-ascii-store-resend.js +3 -2
- package/dist/store/fix-msg-ascii-store-resend.js.map +1 -1
- package/dist/store/fix-msg-store-record.js.map +1 -1
- package/dist/store/fix-replay-record.d.ts +12 -0
- package/dist/store/fix-replay-record.js +12 -0
- package/dist/store/fix-replay-record.js.map +1 -0
- package/dist/store/fix-resend-record.d.ts +12 -0
- package/dist/store/fix-resend-record.js +12 -0
- package/dist/store/fix-resend-record.js.map +1 -0
- package/dist/store/replay-record.d.ts +6 -0
- package/dist/store/replay-record.js +3 -0
- package/dist/store/replay-record.js.map +1 -0
- package/dist/store/store-replay-record.d.ts +12 -0
- package/dist/store/store-replay-record.js +12 -0
- package/dist/store/store-replay-record.js.map +1 -0
- package/dist/tcp/tls-options.d.ts +5 -0
- package/dist/tcp/tls-options.js +45 -0
- package/dist/tcp/tls-options.js.map +1 -0
- package/dist/test/ascii-encode.test.d.ts +1 -0
- package/dist/test/ascii-encode.test.js +416 -0
- package/dist/test/ascii-encode.test.js.map +1 -0
- package/dist/test/ascii-encoder.test.js +2 -2
- package/dist/test/ascii-encoder.test.js.map +1 -1
- package/dist/test/ascii-parser.test.js +9 -7
- package/dist/test/ascii-parser.test.js.map +1 -1
- package/dist/test/ascii-segment.test.js +7 -6
- package/dist/test/ascii-segment.test.js.map +1 -1
- package/dist/test/ascii-store-recovery.test.d.ts +1 -0
- package/dist/test/ascii-store-recovery.test.js +50 -0
- package/dist/test/ascii-store-recovery.test.js.map +1 -0
- package/dist/test/ascii-store-replay.test.js +2 -2
- package/dist/test/ascii-store-replay.test.js.map +1 -1
- package/dist/test/ascii-tag-pos.test.js +2 -2
- package/dist/test/ascii-tag-pos.test.js.map +1 -1
- package/dist/test/encode-proxy.test.js +1 -1
- package/dist/test/encode-proxy.test.js.map +1 -1
- package/dist/test/execution-report.test.js +2 -2
- package/dist/test/execution-report.test.js.map +1 -1
- package/dist/test/fix-log-replay.test.js +2 -2
- package/dist/test/fix-log-replay.test.js.map +1 -1
- package/dist/test/fix-repo-dict.test.js +1 -1
- package/dist/test/fix-repo-dict.test.js.map +1 -1
- package/dist/test/logon.test.js +2 -2
- package/dist/test/logon.test.js.map +1 -1
- package/dist/test/memory-store.test.js +2 -2
- package/dist/test/memory-store.test.js.map +1 -1
- package/dist/test/qf-full-msg.test.js +14 -13
- package/dist/test/qf-full-msg.test.js.map +1 -1
- package/dist/test/repo-full-ascii-msg.test.js +10 -9
- package/dist/test/repo-full-ascii-msg.test.js.map +1 -1
- package/dist/test/repo-full-fixml-msg.test.js +15 -15
- package/dist/test/repo-full-fixml-msg.test.js.map +1 -1
- package/dist/test/session.test.js +262 -143
- package/dist/test/session.test.js.map +1 -1
- package/dist/test/to-views.js +1 -1
- package/dist/test/to-views.js.map +1 -1
- package/dist/test/view-decode.test.js +2 -2
- package/dist/test/view-decode.test.js.map +1 -1
- package/dist/transport/ascii/ascii-msg-transmitter.js +6 -3
- package/dist/transport/ascii/ascii-msg-transmitter.js.map +1 -1
- package/dist/transport/ascii/ascii-session.d.ts +8 -0
- package/dist/transport/ascii/ascii-session.js +67 -19
- package/dist/transport/ascii/ascii-session.js.map +1 -1
- package/dist/transport/duplex/http-duplex.js +2 -2
- package/dist/transport/duplex/http-duplex.js.map +1 -1
- package/dist/transport/fix-session-state.js +4 -1
- package/dist/transport/fix-session-state.js.map +1 -1
- package/dist/transport/fix-session.d.ts +6 -0
- package/dist/transport/fix-session.js +91 -38
- package/dist/transport/fix-session.js.map +1 -1
- package/dist/transport/http/http-acceptor.js +8 -7
- package/dist/transport/http/http-acceptor.js.map +1 -1
- package/dist/transport/make-config.js +1 -1
- package/dist/transport/make-config.js.map +1 -1
- package/dist/transport/session-msg-factory.d.ts +24 -2
- package/dist/transport/session-msg-factory.js +143 -3
- package/dist/transport/session-msg-factory.js.map +1 -1
- package/dist/transport/tcp/recovering-initiator.d.ts +17 -0
- package/dist/transport/tcp/recovering-initiator.js +30 -0
- package/dist/transport/tcp/recovering-initiator.js.map +1 -0
- package/dist/transport/tcp/recovering_initiator.d.ts +12 -0
- package/dist/transport/tcp/recovering_initiator.js +25 -0
- package/dist/transport/tcp/recovering_initiator.js.map +1 -0
- package/dist/transport/tcp/resilient-initiator.d.ts +12 -0
- package/dist/transport/tcp/resilient-initiator.js +41 -0
- package/dist/transport/tcp/resilient-initiator.js.map +1 -0
- package/dist/transport/tcp/tcp-acceptor.d.ts +7 -0
- package/dist/transport/tcp/tcp-acceptor.js +57 -38
- package/dist/transport/tcp/tcp-acceptor.js.map +1 -1
- package/dist/transport/tcp/tcp-initiator.d.ts +2 -0
- package/dist/transport/tcp/tcp-initiator.js +59 -32
- package/dist/transport/tcp/tcp-initiator.js.map +1 -1
- package/dist/types/FIX4.4/quickfix/set/header.d.ts +31 -0
- package/dist/types/FIX4.4/quickfix/set/header.js +3 -0
- package/dist/types/FIX4.4/quickfix/set/header.js.map +1 -0
- package/dist/types/FIX4.4/quickfix/set/trailer.d.ts +6 -0
- package/dist/types/FIX4.4/quickfix/set/trailer.js +3 -0
- package/dist/types/FIX4.4/quickfix/set/trailer.js.map +1 -0
- package/dist/util/buffer-helper.js +1 -1
- package/dist/util/buffer-helper.js.map +1 -1
- package/dist/util/json-helper.js +2 -2
- package/dist/util/json-helper.js.map +1 -1
- package/dist/util/message-generator.js +1 -1
- package/dist/util/message-generator.js.map +1 -1
- package/package.json +16 -15
- package/script/genkey.ps1 +179 -179
- package/src/buffer/ascii/ascii-encoder.ts +71 -33
- package/src/buffer/ascii/ascii-view.ts +2 -2
- package/src/buffer/ascii/time-formatter.ts +2 -2
- package/src/buffer/encode-proxy.ts +2 -2
- package/src/buffer/fixml/fixml-view.ts +5 -0
- package/src/buffer/msg-view.ts +3 -0
- package/src/sample/tcp/skeleton/skeleton-session.ts +24 -2
- package/src/sample/tcp/trade-capture/trade-capture-client.ts +8 -3
- package/src/store/fix-msg-ascii-store-resend.ts +3 -2
- package/src/store/fix-msg-store-record.ts +1 -0
- package/src/transport/ascii/ascii-msg-transmitter.ts +6 -3
- package/src/transport/ascii/ascii-session.ts +75 -19
- package/src/transport/duplex/http-duplex.ts +2 -2
- package/src/transport/fix-session-state.ts +4 -1
- package/src/transport/fix-session.ts +98 -37
- package/src/transport/http/http-acceptor.ts +7 -6
- package/src/transport/tcp/tcp-acceptor.ts +59 -35
- package/src/transport/tcp/tcp-initiator.ts +63 -38
|
@@ -52,6 +52,12 @@ export class TradeCaptureClient extends AsciiSession {
|
|
|
52
52
|
this.fixLog.info(txt)
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
private logoutTimer (logoutSeconds: number = 32) {
|
|
56
|
+
setTimeout(() => {
|
|
57
|
+
this.done()
|
|
58
|
+
}, logoutSeconds * 1000)
|
|
59
|
+
}
|
|
60
|
+
|
|
55
61
|
protected onReady (view: MsgView): void {
|
|
56
62
|
this.logger.info('ready')
|
|
57
63
|
const tcr: ITradeCaptureReportRequest = TradeFactory.tradeCaptureReportRequest('all-trades', new Date())
|
|
@@ -59,12 +65,11 @@ export class TradeCaptureClient extends AsciiSession {
|
|
|
59
65
|
this.send(MsgType.TradeCaptureReportRequest, tcr)
|
|
60
66
|
const logoutSeconds = 32
|
|
61
67
|
this.logger.info(`will logout after ${logoutSeconds}`)
|
|
62
|
-
|
|
63
|
-
this.done()
|
|
64
|
-
}, logoutSeconds * 1000)
|
|
68
|
+
this.logoutTimer()
|
|
65
69
|
}
|
|
66
70
|
|
|
67
71
|
protected onLogon (view: MsgView, user: string, password: string): boolean {
|
|
72
|
+
this.logger.info(`onLogon user ${user}`)
|
|
68
73
|
return true
|
|
69
74
|
}
|
|
70
75
|
}
|
|
@@ -72,8 +72,9 @@ export class FixMsgAsciiStoreResend {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
public sequenceResetGap (startGap: number, newSeq: number): IFixMsgStoreRecord {
|
|
75
|
-
const
|
|
76
|
-
gapFill
|
|
75
|
+
const factory = this.config.factory
|
|
76
|
+
const gapFill: ISequenceReset = factory.sequenceReset(newSeq, true) as ISequenceReset
|
|
77
|
+
gapFill.StandardHeader = factory.header(MsgType.SequenceReset, startGap) as IStandardHeader
|
|
77
78
|
gapFill.StandardHeader.PossDupFlag = true
|
|
78
79
|
gapFill.NewSeqNo = newSeq
|
|
79
80
|
return new FixMsgStoreRecord(
|
|
@@ -16,6 +16,7 @@ export class FixMsgStoreRecord implements IFixMsgStoreRecord {
|
|
|
16
16
|
public obj?: ILooseObject,
|
|
17
17
|
public readonly encoded?: string) {
|
|
18
18
|
}
|
|
19
|
+
|
|
19
20
|
clone (): IFixMsgStoreRecord {
|
|
20
21
|
return new FixMsgStoreRecord(this.msgType, this.timestamp, this.seqNum, this.obj, this.encoded)
|
|
21
22
|
}
|
|
@@ -21,8 +21,9 @@ export class AsciiMsgTransmitter extends MsgTransmitter {
|
|
|
21
21
|
this.encoder = new AsciiEncoder(buffer, config.definitions, tf,
|
|
22
22
|
config.delimiter || AsciiChars.Soh,
|
|
23
23
|
config.logDelimiter || AsciiChars.Pipe)
|
|
24
|
-
|
|
25
|
-
this.
|
|
24
|
+
const components = config.definitions.component
|
|
25
|
+
this.header = components.get('StandardHeader')
|
|
26
|
+
this.trailer = components.get('StandardTrailer')
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
private checksum (): number {
|
|
@@ -46,9 +47,11 @@ export class AsciiMsgTransmitter extends MsgTransmitter {
|
|
|
46
47
|
if (StandardHeader) {
|
|
47
48
|
const { BeginString, BodyLength, MsgType, SenderCompID, SendingTime, TargetCompID, TargetSubID, ...hp } = StandardHeader
|
|
48
49
|
headerProps = hp // pick up any optional applied by application
|
|
50
|
+
headerProps.OrigSendingTime = SendingTime // when first sent
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
const
|
|
53
|
+
const sendingTime = this.time || new Date()
|
|
54
|
+
const hdr: ILooseObject = factory.header(msgType, this.msgSeqNum, sendingTime, headerProps)
|
|
52
55
|
|
|
53
56
|
// Only increment sequence number if this is not a duplicate message.
|
|
54
57
|
if (!headerProps.PossDupFlag) {
|
|
@@ -4,15 +4,20 @@ import { IJsFixConfig } from '../../config'
|
|
|
4
4
|
import { IMsgApplication } from '../session-description'
|
|
5
5
|
import { SessionState, TickAction } from '../fix-session-state'
|
|
6
6
|
import { FixSession } from '../fix-session'
|
|
7
|
+
import { FixMsgAsciiStoreResend, FixMsgMemoryStore, IFixMsgStore, IFixMsgStoreRecord } from '../../store'
|
|
7
8
|
|
|
8
9
|
export abstract class AsciiSession extends FixSession {
|
|
9
10
|
|
|
10
11
|
public heartbeat: boolean = true
|
|
12
|
+
protected store: IFixMsgStore = null
|
|
13
|
+
protected resender: FixMsgAsciiStoreResend
|
|
11
14
|
|
|
12
15
|
protected constructor (public readonly config: IJsFixConfig) {
|
|
13
16
|
super(config)
|
|
14
17
|
this.requestLogoutType = this.respondLogoutType = MsgType.Logout
|
|
15
18
|
this.requestLogonType = MsgType.Logon
|
|
19
|
+
this.store = new FixMsgMemoryStore(this.config.description.SenderCompId, this.config)
|
|
20
|
+
this.resender = new FixMsgAsciiStoreResend(this.store, this.config)
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
private checkSeqNo (msgType: string, view: MsgView): boolean {
|
|
@@ -54,6 +59,17 @@ export abstract class AsciiSession extends FixSession {
|
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
|
|
62
|
+
protected checkForwardMsg (msgType: string, view: MsgView): void {
|
|
63
|
+
const okToForward = this.validStateApplicationMsg()
|
|
64
|
+
if (okToForward) {
|
|
65
|
+
this.sessionLogger.info(`ascii forwarding msgType = '${msgType}' to application`)
|
|
66
|
+
this.setState(SessionState.ActiveNormalSession)
|
|
67
|
+
this.onApplicationMsg(msgType, view)
|
|
68
|
+
} else {
|
|
69
|
+
this.terminate(new Error(`msgType ${msgType} received in state ${this.stateString()}`))
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
57
73
|
private sendReject (msgType: string, seqNo: number, msg: string, reason: number): void {
|
|
58
74
|
const factory = this.config.factory
|
|
59
75
|
const reject = factory.reject(msgType, seqNo, msg, reason)
|
|
@@ -138,19 +154,43 @@ export abstract class AsciiSession extends FixSession {
|
|
|
138
154
|
* @protected
|
|
139
155
|
*/
|
|
140
156
|
protected onResendRequest (view: MsgView) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
157
|
+
// if no records are in store then send a gap fill for entire sequence
|
|
158
|
+
this.setState(SessionState.HandleResendRequest)
|
|
159
|
+
const [beginSeqNo, endSeqNo] = view.getTypedTags([MsgTag.BeginSeqNo, MsgTag.EndSeqNo])
|
|
160
|
+
this.sessionLogger.info(`onResendRequest getResendRequest beginSeqNo = ${beginSeqNo}, endSeqNo = ${endSeqNo}`)
|
|
161
|
+
this.resender.getResendRequest(beginSeqNo, endSeqNo).then((records: IFixMsgStoreRecord[]) => {
|
|
162
|
+
const validRecords = records.filter(rec => rec.obj !== null)
|
|
163
|
+
this.sessionLogger.info(`sending ${validRecords.length}`)
|
|
164
|
+
validRecords.forEach(rec => {
|
|
165
|
+
this.send(rec.msgType, rec.obj)
|
|
166
|
+
})
|
|
167
|
+
this.setState(SessionState.ActiveNormalSession)
|
|
168
|
+
}).catch(e => {
|
|
169
|
+
this.sessionLogger.error(e)
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
okForLogon (): boolean {
|
|
174
|
+
const state = this.sessionState.state
|
|
175
|
+
if (this.acceptor) {
|
|
176
|
+
return state === SessionState.WaitingForALogon
|
|
177
|
+
}
|
|
178
|
+
return state === SessionState.InitiationLogonSent
|
|
144
179
|
}
|
|
145
180
|
|
|
146
181
|
protected onSessionMsg (msgType: string, view: MsgView): void {
|
|
147
182
|
|
|
148
|
-
const factory = this.config.factory
|
|
149
183
|
const logger = this.sessionLogger
|
|
150
184
|
|
|
151
185
|
switch (msgType) {
|
|
152
186
|
case MsgType.Logon: {
|
|
153
|
-
|
|
187
|
+
// only valid to receive a logon when in LogonSent or WaitingALogon
|
|
188
|
+
// else will drop connection immediately.
|
|
189
|
+
if (this.okForLogon()) {
|
|
190
|
+
this.peerLogon(view)
|
|
191
|
+
} else {
|
|
192
|
+
this.terminate(new Error(`state ${this.stateString()} is illegal for Logon`))
|
|
193
|
+
}
|
|
154
194
|
break
|
|
155
195
|
}
|
|
156
196
|
|
|
@@ -161,12 +201,13 @@ export abstract class AsciiSession extends FixSession {
|
|
|
161
201
|
|
|
162
202
|
case MsgType.TestRequest: {
|
|
163
203
|
const req: string = view.getString(MsgTag.TestReqID)
|
|
164
|
-
this.
|
|
204
|
+
this.sendHeartbeat(req)
|
|
165
205
|
break
|
|
166
206
|
}
|
|
167
207
|
|
|
168
208
|
case MsgType.Heartbeat: {
|
|
169
209
|
this.sessionState.lastTestRequestAt = null
|
|
210
|
+
this.setState(SessionState.ActiveNormalSession)
|
|
170
211
|
break
|
|
171
212
|
}
|
|
172
213
|
|
|
@@ -203,9 +244,7 @@ export abstract class AsciiSession extends FixSession {
|
|
|
203
244
|
switch (msgType) {
|
|
204
245
|
case MsgType.Logon: {
|
|
205
246
|
this.setState(SessionState.PeerLogonRejected)
|
|
206
|
-
this.
|
|
207
|
-
this.tick()
|
|
208
|
-
}, 200)
|
|
247
|
+
this.startTimer()
|
|
209
248
|
break
|
|
210
249
|
}
|
|
211
250
|
}
|
|
@@ -231,36 +270,53 @@ export abstract class AsciiSession extends FixSession {
|
|
|
231
270
|
}
|
|
232
271
|
}
|
|
233
272
|
|
|
273
|
+
private startTimer (interval: number = 200) {
|
|
274
|
+
this.timer = setInterval(() => {
|
|
275
|
+
this.tick()
|
|
276
|
+
}, interval)
|
|
277
|
+
}
|
|
278
|
+
|
|
234
279
|
private peerLogon (view: MsgView) {
|
|
235
280
|
const logger = this.sessionLogger
|
|
236
|
-
const heartBtInt = view.
|
|
237
|
-
const peerCompId = view.getTyped(MsgTag.SenderCompID)
|
|
238
|
-
const userName = view.getString(MsgTag.Username)
|
|
281
|
+
const [heartBtInt, peerCompId, userName, password] = view.getTypedTags([MsgTag.HeartBtInt, MsgTag.SenderCompID, MsgTag.Username, MsgTag.Password])
|
|
239
282
|
logger.info(`peerLogon Username = ${userName}, heartBtInt = ${heartBtInt}, peerCompId = ${peerCompId}, userName = ${userName}`)
|
|
240
283
|
const state = this.sessionState
|
|
241
284
|
state.peerHeartBeatSecs = view.getTyped(MsgTag.HeartBtInt)
|
|
242
285
|
state.peerCompId = view.getTyped(MsgTag.SenderCompID)
|
|
286
|
+
const res = this.onLogon(view, userName, password)
|
|
287
|
+
// currently not using this.
|
|
288
|
+
logger.info(`peerLogon onLogon returns ${res}`)
|
|
243
289
|
if (this.acceptor) {
|
|
244
290
|
this.setState(SessionState.InitiationLogonResponse)
|
|
245
|
-
|
|
291
|
+
logger.info('acceptor responds to logon request')
|
|
292
|
+
this.sendLogon() // if res send response else reject, terminate
|
|
246
293
|
} else { // as an initiator the acceptor has responded
|
|
294
|
+
logger.info('initiator receives logon response')
|
|
247
295
|
this.setState(SessionState.InitiationLogonReceived)
|
|
248
296
|
}
|
|
249
297
|
if (this.heartbeat) {
|
|
250
298
|
logger.debug(`start heartbeat timer.`)
|
|
251
|
-
this.
|
|
252
|
-
this.tick()
|
|
253
|
-
}, 200)
|
|
299
|
+
this.startTimer()
|
|
254
300
|
}
|
|
255
301
|
logger.info(`system ready, inform app`)
|
|
256
302
|
this.onReady(view)
|
|
257
303
|
}
|
|
258
304
|
|
|
305
|
+
private sendTestRequest () {
|
|
306
|
+
const factory = this.config.factory
|
|
307
|
+
this.setState(SessionState.AwaitingProcessingResponseToTestRequest)
|
|
308
|
+
this.send(MsgType.TestRequest, factory.testRequest())
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
private sendHeartbeat (testReqId: string) {
|
|
312
|
+
const factory = this.config.factory
|
|
313
|
+
this.send(MsgType.Heartbeat, factory.heartbeat(testReqId))
|
|
314
|
+
}
|
|
315
|
+
|
|
259
316
|
private tick (): void {
|
|
260
317
|
const sessionState = this.sessionState
|
|
261
318
|
const action: TickAction = sessionState.calcAction(new Date())
|
|
262
319
|
const application: IMsgApplication = this.transport.config.description.application
|
|
263
|
-
const factory = this.config.factory
|
|
264
320
|
const logger = this.sessionLogger
|
|
265
321
|
|
|
266
322
|
switch (action) {
|
|
@@ -271,13 +327,13 @@ export abstract class AsciiSession extends FixSession {
|
|
|
271
327
|
|
|
272
328
|
case TickAction.TestRequest: {
|
|
273
329
|
logger.debug(`send test req. state = ${sessionState.toString()}`)
|
|
274
|
-
this.
|
|
330
|
+
this.sendTestRequest()
|
|
275
331
|
break
|
|
276
332
|
}
|
|
277
333
|
|
|
278
334
|
case TickAction.Heartbeat: {
|
|
279
335
|
logger.debug(`send heartbeat. state = ${sessionState.toString()}`)
|
|
280
|
-
this.
|
|
336
|
+
this.sendHeartbeat(sessionState.now.toUTCString())
|
|
281
337
|
break
|
|
282
338
|
}
|
|
283
339
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FixDuplex } from './fix-duplex'
|
|
2
2
|
import { Readable, Writable } from 'stream'
|
|
3
3
|
import { IHttpAdapter } from '../session-description'
|
|
4
|
-
import * as
|
|
4
|
+
import * as rp from 'request-promise-native'
|
|
5
5
|
|
|
6
6
|
export class HttpDuplex extends FixDuplex {
|
|
7
7
|
public constructor (public readonly adapter: IHttpAdapter) {
|
|
@@ -28,7 +28,7 @@ export class HttpDuplex extends FixDuplex {
|
|
|
28
28
|
try {
|
|
29
29
|
const adapter = this.adapter
|
|
30
30
|
const options = adapter.getOptions(data)
|
|
31
|
-
|
|
31
|
+
rp(options).then((message: any) => {
|
|
32
32
|
const body = adapter.endMessage(message)
|
|
33
33
|
forward.push(body)
|
|
34
34
|
done()
|
|
@@ -74,6 +74,7 @@ export class FixSessionState {
|
|
|
74
74
|
this.secondsSinceReceive = -1
|
|
75
75
|
this.peerHeartBeatSecs = 0
|
|
76
76
|
this.logoutSentAt = null
|
|
77
|
+
this.nextTickAction = TickAction.Nothing
|
|
77
78
|
if (resetSeqNo) {
|
|
78
79
|
this.lastPeerMsgSeqNum = 0
|
|
79
80
|
}
|
|
@@ -105,7 +106,7 @@ export class FixSessionState {
|
|
|
105
106
|
buffer.writeString(`compId = ${this.compId}, `)
|
|
106
107
|
buffer.writeString(`heartBeat = ${this.heartBeat}, `)
|
|
107
108
|
buffer.writeString(`state = ${SessionState[this.state]} (${this.state}), `)
|
|
108
|
-
buffer.writeString(`nextTickAction = ${this.nextTickAction}, `)
|
|
109
|
+
buffer.writeString(`nextTickAction = ${TickAction[this.nextTickAction]} (${this.nextTickAction}), `)
|
|
109
110
|
buffer.writeString(`now = ${FixSessionState.dateAsString(this.now)}, `)
|
|
110
111
|
buffer.writeString(`timeToDie = ${this.timeToDie()}, `)
|
|
111
112
|
buffer.writeString(`timeToHeartbeat = ${this.timeToHeartbeat()}, `)
|
|
@@ -146,6 +147,8 @@ export class FixSessionState {
|
|
|
146
147
|
break
|
|
147
148
|
}
|
|
148
149
|
|
|
150
|
+
case SessionState.ActiveNormalSession:
|
|
151
|
+
case SessionState.AwaitingProcessingResponseToTestRequest:
|
|
149
152
|
case SessionState.InitiationLogonReceived:
|
|
150
153
|
case SessionState.InitiationLogonResponse : {
|
|
151
154
|
if (this.timeToHeartbeat()) {
|
|
@@ -40,7 +40,8 @@ export abstract class FixSession extends events.EventEmitter {
|
|
|
40
40
|
if (state === this.sessionState.state) return
|
|
41
41
|
const logger = this.sessionLogger
|
|
42
42
|
const prevState = this.sessionState.state
|
|
43
|
-
|
|
43
|
+
const msg = `current state ${SessionState[prevState]} (${prevState}) moves to ${SessionState[state]} (${state})`
|
|
44
|
+
logger.info(msg)
|
|
44
45
|
this.sessionState.state = state
|
|
45
46
|
}
|
|
46
47
|
|
|
@@ -48,21 +49,19 @@ export abstract class FixSession extends events.EventEmitter {
|
|
|
48
49
|
return this.sessionState.state
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
public
|
|
52
|
+
public sendLogon () {
|
|
53
|
+
this.send(this.requestLogonType, this.config.factory.logon())
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private waitPromise (): Promise<any> {
|
|
52
57
|
const logger = this.sessionLogger
|
|
53
|
-
if (this.transport) {
|
|
54
|
-
logger.info('reset from previous transport.')
|
|
55
|
-
this.reset()
|
|
56
|
-
}
|
|
57
|
-
this.transport = transport
|
|
58
|
-
this.subscribe()
|
|
59
58
|
return new Promise<any>((accept, reject) => {
|
|
60
59
|
if (this.initiator) {
|
|
61
|
-
logger.debug(
|
|
62
|
-
this.
|
|
60
|
+
logger.debug(`initiator sending logon state = ${this.stateString()}`)
|
|
61
|
+
this.sendLogon()
|
|
63
62
|
this.setState(SessionState.InitiationLogonSent)
|
|
64
63
|
} else {
|
|
65
|
-
logger.debug(
|
|
64
|
+
logger.debug(`acceptor waits for logon state = ${this.stateString()}`)
|
|
66
65
|
this.setState(SessionState.WaitingForALogon)
|
|
67
66
|
}
|
|
68
67
|
|
|
@@ -70,12 +69,40 @@ export abstract class FixSession extends events.EventEmitter {
|
|
|
70
69
|
logger.error(e)
|
|
71
70
|
reject(e)
|
|
72
71
|
})
|
|
72
|
+
|
|
73
73
|
this.on('done', () => {
|
|
74
74
|
accept(this.transport.id)
|
|
75
75
|
})
|
|
76
76
|
})
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
public run (transport: MsgTransport): Promise<number> {
|
|
80
|
+
const logger = this.sessionLogger
|
|
81
|
+
if (this.transport) {
|
|
82
|
+
logger.info(`reset from previous transport. state ${this.stateString()}`)
|
|
83
|
+
this.reset()
|
|
84
|
+
}
|
|
85
|
+
this.transport = transport
|
|
86
|
+
this.subscribe()
|
|
87
|
+
return this.waitPromise()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
protected expectedState (): boolean {
|
|
91
|
+
switch (this.sessionState.state) {
|
|
92
|
+
case SessionState.ActiveNormalSession:
|
|
93
|
+
case SessionState.ReceiveLogout:
|
|
94
|
+
case SessionState.Stopped:
|
|
95
|
+
case SessionState.ConfirmingLogout:
|
|
96
|
+
case SessionState.HandleResendRequest:
|
|
97
|
+
case SessionState.AwaitingProcessingResponseToTestRequest:
|
|
98
|
+
case SessionState.AwaitingProcessingResponseToResendRequest:
|
|
99
|
+
return true
|
|
100
|
+
|
|
101
|
+
default:
|
|
102
|
+
return false
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
79
106
|
protected subscribe () {
|
|
80
107
|
|
|
81
108
|
const transport = this.transport
|
|
@@ -110,21 +137,14 @@ export abstract class FixSession extends events.EventEmitter {
|
|
|
110
137
|
|
|
111
138
|
rx.on('end', () => {
|
|
112
139
|
logger.info(`rx end received sessionState = [${this.sessionState.toString()}]`)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
default: {
|
|
123
|
-
const e = new Error(`unexpected state - transport failed? = ${SessionState[this.sessionState.state]}`)
|
|
124
|
-
logger.info(`rx error ${e.message}`)
|
|
125
|
-
this.terminate(e)
|
|
126
|
-
}
|
|
127
|
-
break
|
|
140
|
+
const expectedState = this.expectedState()
|
|
141
|
+
if (expectedState) {
|
|
142
|
+
logger.info(`rx graceful end state = ${this.stateString()}`)
|
|
143
|
+
this.done()
|
|
144
|
+
} else {
|
|
145
|
+
const e = new Error(`unexpected state - transport failed? = ${this.stateString()}`)
|
|
146
|
+
logger.info(`rx error ${e.message}`)
|
|
147
|
+
this.terminate(e)
|
|
128
148
|
}
|
|
129
149
|
})
|
|
130
150
|
|
|
@@ -144,23 +164,49 @@ export abstract class FixSession extends events.EventEmitter {
|
|
|
144
164
|
})
|
|
145
165
|
}
|
|
146
166
|
|
|
167
|
+
protected validStateApplicationMsg (): boolean {
|
|
168
|
+
switch (this.sessionState.state) {
|
|
169
|
+
case SessionState.Idle:
|
|
170
|
+
case SessionState.InitiateConnection:
|
|
171
|
+
case SessionState.InitiationLogonSent:
|
|
172
|
+
case SessionState.WaitingForALogon:
|
|
173
|
+
case SessionState.HandleResendRequest:
|
|
174
|
+
case SessionState.AwaitingProcessingResponseToTestRequest:
|
|
175
|
+
case SessionState.AwaitingProcessingResponseToResendRequest:
|
|
176
|
+
return false
|
|
177
|
+
default:
|
|
178
|
+
return true
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
protected stateString (): string {
|
|
183
|
+
return SessionState[this.sessionState.state]
|
|
184
|
+
}
|
|
185
|
+
|
|
147
186
|
protected checkForwardMsg (msgType: string, view: MsgView): void {
|
|
148
187
|
this.sessionLogger.info(`forwarding msgType = '${msgType}' to application`)
|
|
188
|
+
this.setState(SessionState.ActiveNormalSession)
|
|
149
189
|
this.onApplicationMsg(msgType, view)
|
|
150
190
|
}
|
|
151
191
|
|
|
152
192
|
protected terminate (error: Error): void {
|
|
193
|
+
if (this.sessionState.state === SessionState.Stopped) return
|
|
153
194
|
this.sessionLogger.error(error)
|
|
154
|
-
|
|
155
|
-
|
|
195
|
+
if (this.timer) {
|
|
196
|
+
clearInterval(this.timer)
|
|
197
|
+
}
|
|
198
|
+
if (this.transport) {
|
|
199
|
+
this.transport.end()
|
|
200
|
+
}
|
|
156
201
|
this.transport = null
|
|
157
|
-
this.
|
|
202
|
+
this.setState(SessionState.Stopped)
|
|
158
203
|
this.emit('error', error)
|
|
159
204
|
}
|
|
160
205
|
|
|
161
206
|
protected peerLogout (view: MsgView) {
|
|
162
207
|
const msg = view.getString(MsgTag.Text)
|
|
163
|
-
|
|
208
|
+
const state = this.sessionState.state
|
|
209
|
+
switch (state) {
|
|
164
210
|
case SessionState.WaitingLogoutConfirm: {
|
|
165
211
|
this.sessionLogger.info(`peer confirms logout Text = '${msg}'`)
|
|
166
212
|
this.stop()
|
|
@@ -168,6 +214,7 @@ export abstract class FixSession extends events.EventEmitter {
|
|
|
168
214
|
}
|
|
169
215
|
|
|
170
216
|
case SessionState.InitiationLogonResponse:
|
|
217
|
+
case SessionState.ActiveNormalSession:
|
|
171
218
|
case SessionState.InitiationLogonReceived: {
|
|
172
219
|
this.setState(SessionState.ConfirmingLogout)
|
|
173
220
|
this.sessionLogger.info(`peer initiates logout Text = '${msg}'`)
|
|
@@ -177,9 +224,10 @@ export abstract class FixSession extends events.EventEmitter {
|
|
|
177
224
|
}
|
|
178
225
|
|
|
179
226
|
protected send (msgType: string, obj: ILooseObject) {
|
|
180
|
-
|
|
227
|
+
const state = this.sessionState.state
|
|
228
|
+
switch (state) {
|
|
181
229
|
case SessionState.Stopped: {
|
|
182
|
-
this.sessionLogger.warning(`can't send in
|
|
230
|
+
this.sessionLogger.warning(`can't send in state ${this.stateString()}`)
|
|
183
231
|
break
|
|
184
232
|
}
|
|
185
233
|
|
|
@@ -191,13 +239,20 @@ export abstract class FixSession extends events.EventEmitter {
|
|
|
191
239
|
}
|
|
192
240
|
}
|
|
193
241
|
|
|
242
|
+
protected sendLogout (msg: string) {
|
|
243
|
+
const factory = this.config.factory
|
|
244
|
+
this.sessionLogger.info(`sending logout with ${msg}`)
|
|
245
|
+
this.send(this.requestLogoutType, factory.logout(this.requestLogoutType, msg))
|
|
246
|
+
}
|
|
247
|
+
|
|
194
248
|
protected sessionLogout (): void {
|
|
195
249
|
const sessionState = this.sessionState
|
|
196
250
|
if (sessionState.logoutSentAt) {
|
|
197
251
|
return
|
|
198
252
|
}
|
|
199
|
-
|
|
253
|
+
|
|
200
254
|
switch (sessionState.state) {
|
|
255
|
+
case SessionState.ActiveNormalSession:
|
|
201
256
|
case SessionState.InitiationLogonResponse:
|
|
202
257
|
case SessionState.InitiationLogonReceived: {
|
|
203
258
|
// this instance initiates logout
|
|
@@ -205,7 +260,7 @@ export abstract class FixSession extends events.EventEmitter {
|
|
|
205
260
|
sessionState.logoutSentAt = new Date()
|
|
206
261
|
const msg = `${this.me} initiate logout`
|
|
207
262
|
this.sessionLogger.info(msg)
|
|
208
|
-
this.
|
|
263
|
+
this.sendLogout(msg)
|
|
209
264
|
break
|
|
210
265
|
}
|
|
211
266
|
|
|
@@ -214,7 +269,7 @@ export abstract class FixSession extends events.EventEmitter {
|
|
|
214
269
|
sessionState.logoutSentAt = new Date()
|
|
215
270
|
const msg = `${this.me} confirming logout`
|
|
216
271
|
this.sessionLogger.info(msg)
|
|
217
|
-
this.
|
|
272
|
+
this.sendLogout(msg)
|
|
218
273
|
break
|
|
219
274
|
}
|
|
220
275
|
|
|
@@ -227,6 +282,7 @@ export abstract class FixSession extends events.EventEmitter {
|
|
|
227
282
|
public done (): void {
|
|
228
283
|
switch (this.sessionState.state) {
|
|
229
284
|
case SessionState.InitiationLogonResponse:
|
|
285
|
+
case SessionState.ActiveNormalSession:
|
|
230
286
|
case SessionState.InitiationLogonReceived: {
|
|
231
287
|
this.sessionLogout()
|
|
232
288
|
break
|
|
@@ -241,10 +297,13 @@ export abstract class FixSession extends events.EventEmitter {
|
|
|
241
297
|
break
|
|
242
298
|
}
|
|
243
299
|
}
|
|
244
|
-
this.sessionLogger.info(`done. check logout sequence`)
|
|
300
|
+
this.sessionLogger.info(`done. check logout sequence state ${this.stateString()}`)
|
|
245
301
|
}
|
|
246
302
|
|
|
247
303
|
public reset (): void {
|
|
304
|
+
if (this.timer) {
|
|
305
|
+
clearInterval(this.timer)
|
|
306
|
+
}
|
|
248
307
|
this.transport = null
|
|
249
308
|
this.sessionState.reset(true) // from header def ... eventually
|
|
250
309
|
this.setState(SessionState.NetworkConnectionEstablished)
|
|
@@ -254,7 +313,9 @@ export abstract class FixSession extends events.EventEmitter {
|
|
|
254
313
|
if (this.sessionState.state === SessionState.Stopped) {
|
|
255
314
|
return
|
|
256
315
|
}
|
|
257
|
-
|
|
316
|
+
if (this.timer) {
|
|
317
|
+
clearInterval(this.timer)
|
|
318
|
+
}
|
|
258
319
|
this.sessionLogger.info(`stop: kill transport`)
|
|
259
320
|
this.transport.end()
|
|
260
321
|
if (error) {
|
|
@@ -8,6 +8,7 @@ import { Dictionary } from '../../collections'
|
|
|
8
8
|
import * as express from 'express'
|
|
9
9
|
import * as bodyParser from 'body-parser'
|
|
10
10
|
import * as http from 'http'
|
|
11
|
+
import { v4 as uuidv4 } from 'uuid'
|
|
11
12
|
|
|
12
13
|
export class HttpAcceptor extends FixAcceptor {
|
|
13
14
|
private app: express.Express = express()
|
|
@@ -49,11 +50,9 @@ export class HttpAcceptor extends FixAcceptor {
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
private saveTransport (tid: number, transport: MsgTransport): string {
|
|
52
|
-
const uuidv3 = require('uuid/v3')
|
|
53
53
|
this.transports[tid] = transport
|
|
54
|
-
const app = this.config.description.application
|
|
55
54
|
const keys: string[] = Object.keys(this.transports)
|
|
56
|
-
const a =
|
|
55
|
+
const a = uuidv4()
|
|
57
56
|
this.keys.addUpdate(a, transport)
|
|
58
57
|
this.logger.info(`new transport id = ${tid} token = ${a} created total transports = ${keys.length}`)
|
|
59
58
|
this.emit('transport', transport)
|
|
@@ -134,13 +133,13 @@ export class HttpAcceptor extends FixAcceptor {
|
|
|
134
133
|
const authorise = `${root}authorise`
|
|
135
134
|
const query = `${root}query`
|
|
136
135
|
this.logger.info(`uri: authorise ${authorise}, query ${query}`)
|
|
137
|
-
router.post(authorise, (req: express.Request, res: express.Response) => {
|
|
136
|
+
router.post(authorise, async (req: express.Request, res: express.Response) => {
|
|
138
137
|
if (!req.headers.authorization) {
|
|
139
138
|
this.logger.info('logon')
|
|
140
|
-
this.logon(req, res)
|
|
139
|
+
await this.logon(req, res)
|
|
141
140
|
} else {
|
|
142
141
|
this.logger.info('logout')
|
|
143
|
-
this.logout(req, res)
|
|
142
|
+
await this.logout(req, res)
|
|
144
143
|
}
|
|
145
144
|
})
|
|
146
145
|
|
|
@@ -157,6 +156,8 @@ export class HttpAcceptor extends FixAcceptor {
|
|
|
157
156
|
const d = t.duplex
|
|
158
157
|
this.respond(d, res).then(() => {
|
|
159
158
|
this.logger.info(`responded to ${req.url}`)
|
|
159
|
+
}).catch(e => {
|
|
160
|
+
res.send(e)
|
|
160
161
|
})
|
|
161
162
|
d.readable.push(body.fixml)
|
|
162
163
|
}
|