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.
Files changed (182) hide show
  1. package/README.md +5 -2
  2. package/data/session/genkey.ps1 +179 -0
  3. package/dist/buffer/ascii/ascii-encoder.d.ts +1 -1
  4. package/dist/buffer/ascii/ascii-encoder.js +42 -30
  5. package/dist/buffer/ascii/ascii-encoder.js.map +1 -1
  6. package/dist/buffer/ascii/ascii-view.d.ts +2 -2
  7. package/dist/buffer/ascii/ascii-view.js.map +1 -1
  8. package/dist/buffer/ascii/time-formatter.js.map +1 -1
  9. package/dist/buffer/elastic-buffer.js +1 -1
  10. package/dist/buffer/elastic-buffer.js.map +1 -1
  11. package/dist/buffer/encode-proxy.js.map +1 -1
  12. package/dist/buffer/fixml/fixml-encoder.js +1 -1
  13. package/dist/buffer/fixml/fixml-encoder.js.map +1 -1
  14. package/dist/buffer/fixml/fixml-view.d.ts +2 -0
  15. package/dist/buffer/fixml/fixml-view.js +3 -0
  16. package/dist/buffer/fixml/fixml-view.js.map +1 -1
  17. package/dist/buffer/msg-view.d.ts +2 -0
  18. package/dist/buffer/msg-view.js +2 -2
  19. package/dist/buffer/msg-view.js.map +1 -1
  20. package/dist/dict-parser.js +9 -9
  21. package/dist/dict-parser.js.map +1 -1
  22. package/dist/dictionary/compiler/msg-compiler.js +2 -2
  23. package/dist/dictionary/compiler/msg-compiler.js.map +1 -1
  24. package/dist/dictionary/parser/fix-repository/repository-xml-parser.js +1 -1
  25. package/dist/dictionary/parser/fix-repository/repository-xml-parser.js.map +1 -1
  26. package/dist/dictionary/parser/quickfix/quick-fix-xml-file-parser.js +1 -1
  27. package/dist/dictionary/parser/quickfix/quick-fix-xml-file-parser.js.map +1 -1
  28. package/dist/jsfix-cmd.js +3 -3
  29. package/dist/jsfix-cmd.js.map +1 -1
  30. package/dist/sample/http/oms/app.js +2 -2
  31. package/dist/sample/http/oms/app.js.map +1 -1
  32. package/dist/sample/launcher.js +2 -2
  33. package/dist/sample/launcher.js.map +1 -1
  34. package/dist/sample/tcp/qf-md/app.js +2 -2
  35. package/dist/sample/tcp/qf-md/app.js.map +1 -1
  36. package/dist/sample/tcp/recovering-skeleton/respawn-2.d.ts +2 -0
  37. package/dist/sample/tcp/recovering-skeleton/respawn-2.js +3 -0
  38. package/dist/sample/tcp/recovering-skeleton/respawn-2.js.map +1 -0
  39. package/dist/sample/tcp/recovering-skeleton/respawn-acceptor.js +1 -1
  40. package/dist/sample/tcp/recovering-skeleton/respawn-acceptor.js.map +1 -1
  41. package/dist/sample/tcp/skeleton/app.js +2 -2
  42. package/dist/sample/tcp/skeleton/app.js.map +1 -1
  43. package/dist/sample/tcp/skeleton/skeleton-session.d.ts +5 -1
  44. package/dist/sample/tcp/skeleton/skeleton-session.js +21 -2
  45. package/dist/sample/tcp/skeleton/skeleton-session.js.map +1 -1
  46. package/dist/sample/tcp/tls-trade-capture/app.js +2 -2
  47. package/dist/sample/tcp/tls-trade-capture/app.js.map +1 -1
  48. package/dist/sample/tcp/trade-capture/app.js +2 -2
  49. package/dist/sample/tcp/trade-capture/app.js.map +1 -1
  50. package/dist/sample/tcp/trade-capture/trade-capture-client.d.ts +1 -0
  51. package/dist/sample/tcp/trade-capture/trade-capture-client.js +7 -3
  52. package/dist/sample/tcp/trade-capture/trade-capture-client.js.map +1 -1
  53. package/dist/store/fix-msg-ascii-store-recovery.d.ts +12 -0
  54. package/dist/store/fix-msg-ascii-store-recovery.js +60 -0
  55. package/dist/store/fix-msg-ascii-store-recovery.js.map +1 -0
  56. package/dist/store/fix-msg-ascii-store-replay.d.ts +13 -0
  57. package/dist/store/fix-msg-ascii-store-replay.js +60 -0
  58. package/dist/store/fix-msg-ascii-store-replay.js.map +1 -0
  59. package/dist/store/fix-msg-ascii-store-resend.js +3 -2
  60. package/dist/store/fix-msg-ascii-store-resend.js.map +1 -1
  61. package/dist/store/fix-msg-store-record.js.map +1 -1
  62. package/dist/store/fix-replay-record.d.ts +12 -0
  63. package/dist/store/fix-replay-record.js +12 -0
  64. package/dist/store/fix-replay-record.js.map +1 -0
  65. package/dist/store/fix-resend-record.d.ts +12 -0
  66. package/dist/store/fix-resend-record.js +12 -0
  67. package/dist/store/fix-resend-record.js.map +1 -0
  68. package/dist/store/replay-record.d.ts +6 -0
  69. package/dist/store/replay-record.js +3 -0
  70. package/dist/store/replay-record.js.map +1 -0
  71. package/dist/store/store-replay-record.d.ts +12 -0
  72. package/dist/store/store-replay-record.js +12 -0
  73. package/dist/store/store-replay-record.js.map +1 -0
  74. package/dist/tcp/tls-options.d.ts +5 -0
  75. package/dist/tcp/tls-options.js +45 -0
  76. package/dist/tcp/tls-options.js.map +1 -0
  77. package/dist/test/ascii-encode.test.d.ts +1 -0
  78. package/dist/test/ascii-encode.test.js +416 -0
  79. package/dist/test/ascii-encode.test.js.map +1 -0
  80. package/dist/test/ascii-encoder.test.js +2 -2
  81. package/dist/test/ascii-encoder.test.js.map +1 -1
  82. package/dist/test/ascii-parser.test.js +9 -7
  83. package/dist/test/ascii-parser.test.js.map +1 -1
  84. package/dist/test/ascii-segment.test.js +7 -6
  85. package/dist/test/ascii-segment.test.js.map +1 -1
  86. package/dist/test/ascii-store-recovery.test.d.ts +1 -0
  87. package/dist/test/ascii-store-recovery.test.js +50 -0
  88. package/dist/test/ascii-store-recovery.test.js.map +1 -0
  89. package/dist/test/ascii-store-replay.test.js +2 -2
  90. package/dist/test/ascii-store-replay.test.js.map +1 -1
  91. package/dist/test/ascii-tag-pos.test.js +2 -2
  92. package/dist/test/ascii-tag-pos.test.js.map +1 -1
  93. package/dist/test/encode-proxy.test.js +1 -1
  94. package/dist/test/encode-proxy.test.js.map +1 -1
  95. package/dist/test/execution-report.test.js +2 -2
  96. package/dist/test/execution-report.test.js.map +1 -1
  97. package/dist/test/fix-log-replay.test.js +2 -2
  98. package/dist/test/fix-log-replay.test.js.map +1 -1
  99. package/dist/test/fix-repo-dict.test.js +1 -1
  100. package/dist/test/fix-repo-dict.test.js.map +1 -1
  101. package/dist/test/logon.test.js +2 -2
  102. package/dist/test/logon.test.js.map +1 -1
  103. package/dist/test/memory-store.test.js +2 -2
  104. package/dist/test/memory-store.test.js.map +1 -1
  105. package/dist/test/qf-full-msg.test.js +14 -13
  106. package/dist/test/qf-full-msg.test.js.map +1 -1
  107. package/dist/test/repo-full-ascii-msg.test.js +10 -9
  108. package/dist/test/repo-full-ascii-msg.test.js.map +1 -1
  109. package/dist/test/repo-full-fixml-msg.test.js +15 -15
  110. package/dist/test/repo-full-fixml-msg.test.js.map +1 -1
  111. package/dist/test/session.test.js +262 -143
  112. package/dist/test/session.test.js.map +1 -1
  113. package/dist/test/to-views.js +1 -1
  114. package/dist/test/to-views.js.map +1 -1
  115. package/dist/test/view-decode.test.js +2 -2
  116. package/dist/test/view-decode.test.js.map +1 -1
  117. package/dist/transport/ascii/ascii-msg-transmitter.js +6 -3
  118. package/dist/transport/ascii/ascii-msg-transmitter.js.map +1 -1
  119. package/dist/transport/ascii/ascii-session.d.ts +8 -0
  120. package/dist/transport/ascii/ascii-session.js +67 -19
  121. package/dist/transport/ascii/ascii-session.js.map +1 -1
  122. package/dist/transport/duplex/http-duplex.js +2 -2
  123. package/dist/transport/duplex/http-duplex.js.map +1 -1
  124. package/dist/transport/fix-session-state.js +4 -1
  125. package/dist/transport/fix-session-state.js.map +1 -1
  126. package/dist/transport/fix-session.d.ts +6 -0
  127. package/dist/transport/fix-session.js +91 -38
  128. package/dist/transport/fix-session.js.map +1 -1
  129. package/dist/transport/http/http-acceptor.js +8 -7
  130. package/dist/transport/http/http-acceptor.js.map +1 -1
  131. package/dist/transport/make-config.js +1 -1
  132. package/dist/transport/make-config.js.map +1 -1
  133. package/dist/transport/session-msg-factory.d.ts +24 -2
  134. package/dist/transport/session-msg-factory.js +143 -3
  135. package/dist/transport/session-msg-factory.js.map +1 -1
  136. package/dist/transport/tcp/recovering-initiator.d.ts +17 -0
  137. package/dist/transport/tcp/recovering-initiator.js +30 -0
  138. package/dist/transport/tcp/recovering-initiator.js.map +1 -0
  139. package/dist/transport/tcp/recovering_initiator.d.ts +12 -0
  140. package/dist/transport/tcp/recovering_initiator.js +25 -0
  141. package/dist/transport/tcp/recovering_initiator.js.map +1 -0
  142. package/dist/transport/tcp/resilient-initiator.d.ts +12 -0
  143. package/dist/transport/tcp/resilient-initiator.js +41 -0
  144. package/dist/transport/tcp/resilient-initiator.js.map +1 -0
  145. package/dist/transport/tcp/tcp-acceptor.d.ts +7 -0
  146. package/dist/transport/tcp/tcp-acceptor.js +57 -38
  147. package/dist/transport/tcp/tcp-acceptor.js.map +1 -1
  148. package/dist/transport/tcp/tcp-initiator.d.ts +2 -0
  149. package/dist/transport/tcp/tcp-initiator.js +59 -32
  150. package/dist/transport/tcp/tcp-initiator.js.map +1 -1
  151. package/dist/types/FIX4.4/quickfix/set/header.d.ts +31 -0
  152. package/dist/types/FIX4.4/quickfix/set/header.js +3 -0
  153. package/dist/types/FIX4.4/quickfix/set/header.js.map +1 -0
  154. package/dist/types/FIX4.4/quickfix/set/trailer.d.ts +6 -0
  155. package/dist/types/FIX4.4/quickfix/set/trailer.js +3 -0
  156. package/dist/types/FIX4.4/quickfix/set/trailer.js.map +1 -0
  157. package/dist/util/buffer-helper.js +1 -1
  158. package/dist/util/buffer-helper.js.map +1 -1
  159. package/dist/util/json-helper.js +2 -2
  160. package/dist/util/json-helper.js.map +1 -1
  161. package/dist/util/message-generator.js +1 -1
  162. package/dist/util/message-generator.js.map +1 -1
  163. package/package.json +16 -15
  164. package/script/genkey.ps1 +179 -179
  165. package/src/buffer/ascii/ascii-encoder.ts +71 -33
  166. package/src/buffer/ascii/ascii-view.ts +2 -2
  167. package/src/buffer/ascii/time-formatter.ts +2 -2
  168. package/src/buffer/encode-proxy.ts +2 -2
  169. package/src/buffer/fixml/fixml-view.ts +5 -0
  170. package/src/buffer/msg-view.ts +3 -0
  171. package/src/sample/tcp/skeleton/skeleton-session.ts +24 -2
  172. package/src/sample/tcp/trade-capture/trade-capture-client.ts +8 -3
  173. package/src/store/fix-msg-ascii-store-resend.ts +3 -2
  174. package/src/store/fix-msg-store-record.ts +1 -0
  175. package/src/transport/ascii/ascii-msg-transmitter.ts +6 -3
  176. package/src/transport/ascii/ascii-session.ts +75 -19
  177. package/src/transport/duplex/http-duplex.ts +2 -2
  178. package/src/transport/fix-session-state.ts +4 -1
  179. package/src/transport/fix-session.ts +98 -37
  180. package/src/transport/http/http-acceptor.ts +7 -6
  181. package/src/transport/tcp/tcp-acceptor.ts +59 -35
  182. 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
- setTimeout(() => {
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 gapFill: ISequenceReset = this.config.factory.sequenceReset(newSeq, true) as ISequenceReset
76
- gapFill.StandardHeader = this.config.factory.header(MsgType.SequenceReset, startGap) as IStandardHeader
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
- this.header = config.definitions.component.get('StandardHeader')
25
- this.trailer = config.definitions.component.get('StandardTrailer')
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 hdr: ILooseObject = factory.header(msgType, this.msgSeqNum, this.time || new Date(), headerProps)
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
- const endSeqNo: number = view.getTyped(MsgTag.EndSeqNo)
142
- const resend = this.config.factory.sequenceReset(endSeqNo, true)
143
- this.send(MsgType.SequenceReset, resend)
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
- this.peerLogon(view)
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.send(MsgType.Heartbeat, factory.heartbeat(req))
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.timer = setInterval(() => {
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.getTyped(MsgTag.HeartBtInt)
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
- this.send(MsgType.Logon, this.config.factory.logon())
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.timer = setInterval(() => {
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.send(MsgType.TestRequest, factory.testRequest())
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.send(MsgType.Heartbeat, factory.heartbeat(sessionState.now.toUTCString()))
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 requestPromise from 'request-promise'
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
- requestPromise(options).then((message: any) => {
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
- logger.info(`current state ${SessionState[prevState]} (${prevState}) moves to ${SessionState[state]} (${state})`)
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 run (transport: MsgTransport): Promise<number> {
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('initiator sending logon')
62
- this.send(this.requestLogonType, this.config.factory.logon())
60
+ logger.debug(`initiator sending logon state = ${this.stateString()}`)
61
+ this.sendLogon()
63
62
  this.setState(SessionState.InitiationLogonSent)
64
63
  } else {
65
- logger.debug('acceptor waits for logon')
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
- switch (this.sessionState.state) {
114
- case SessionState.ReceiveLogout:
115
- case SessionState.Stopped:
116
- case SessionState.ConfirmingLogout: {
117
- logger.info(`rx graceful end state = ${SessionState[this.sessionState.state]}`)
118
- this.done()
119
- }
120
- break
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
- clearInterval(this.timer)
155
- this.transport.end()
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.sessionState.state = SessionState.Stopped
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
- switch (this.sessionState.state) {
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
- switch (this.sessionState.state) {
227
+ const state = this.sessionState.state
228
+ switch (state) {
181
229
  case SessionState.Stopped: {
182
- this.sessionLogger.warning(`can't send in stopped state`)
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
- const factory = this.config.factory
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.send(this.requestLogoutType, factory.logout(this.requestLogoutType,msg))
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.send(this.respondLogoutType, factory.logout(this.respondLogoutType,msg))
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
- clearInterval(this.timer)
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 = uuidv3(app.http.uri, uuidv3.URL)
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
  }