jspurefix 5.10.6 → 5.11.0
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 +46 -0
- package/dist/runtime/session-launcher.d.ts +2 -0
- package/dist/runtime/session-launcher.js +13 -0
- package/dist/runtime/session-launcher.js.map +1 -1
- package/dist/transport/msg-application.d.ts +3 -0
- package/dist/transport/msg-application.js.map +1 -1
- package/dist/transport/tcp/recovering-tcp-initiator.d.ts +6 -0
- package/dist/transport/tcp/recovering-tcp-initiator.js +55 -15
- package/dist/transport/tcp/recovering-tcp-initiator.js.map +1 -1
- package/dist/transport/tcp/tcp-initiator-connector.d.ts +2 -0
- package/dist/transport/tcp/tcp-initiator-connector.js +8 -3
- package/dist/transport/tcp/tcp-initiator-connector.js.map +1 -1
- package/dist/transport/tcp/tcp-initiator.d.ts +1 -0
- package/dist/transport/tcp/tcp-initiator.js +7 -40
- package/dist/transport/tcp/tcp-initiator.js.map +1 -1
- package/jsfix.test_client.txt +62 -62
- package/jsfix.test_server.txt +72 -72
- package/package.json +1 -1
- package/src/runtime/session-launcher.ts +25 -3
- package/src/transport/msg-application.ts +16 -0
- package/src/transport/tcp/recovering-tcp-initiator.ts +74 -13
- package/src/transport/tcp/tcp-initiator-connector.ts +6 -1
- package/src/transport/tcp/tcp-initiator.ts +12 -6
package/README.md
CHANGED
|
@@ -13,6 +13,8 @@ A fast, fully native TypeScript [FIX protocol](https://www.fixtrading.org/) engi
|
|
|
13
13
|
- [Quickstart](#quickstart)
|
|
14
14
|
- [Session Configuration](#session-configuration)
|
|
15
15
|
- [Customising the Logon](#customising-the-logon)
|
|
16
|
+
- [Reconnecting initiators](#reconnecting-initiators)
|
|
17
|
+
- [Knowing when the session is up](#knowing-when-the-session-is-up)
|
|
16
18
|
- [Accepting or refusing a logon](#accepting-or-refusing-a-logon)
|
|
17
19
|
- [TLS](#tls)
|
|
18
20
|
- [Body length padding](#body-length-padding)
|
|
@@ -169,6 +171,50 @@ Most counterparties want a tag on the Logon the standard message does not carry.
|
|
|
169
171
|
|
|
170
172
|
A field named here must also be declared on `Logon` in the dictionary the session loads, or the encoder has no tag to write it to and will say so in the log. For values computed at run time, or for a header a counterparty stamps differently, supply your own session message factory. See [docs/custom-logon.md](docs/custom-logon.md) for the whole picture.
|
|
171
173
|
|
|
174
|
+
### Reconnecting initiators
|
|
175
|
+
|
|
176
|
+
By default an initiator makes one connection and its `run()` ends when that session does. Set `resilient` and it keeps the same session across transports instead — losing the connection schedules another attempt, and the session resumes with the recovery policy configured on the description (replay from the last sequence number, or a reset).
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"application": {
|
|
181
|
+
"type": "initiator",
|
|
182
|
+
"name": "test_client",
|
|
183
|
+
"resilient": true,
|
|
184
|
+
"reconnectSeconds": 10,
|
|
185
|
+
"connectTimeoutSeconds": 60,
|
|
186
|
+
"recoveryAttemptSeconds": 5,
|
|
187
|
+
"backoffFailConnectSeconds": 30,
|
|
188
|
+
"tcp": { "host": "localhost", "port": 2344 },
|
|
189
|
+
"protocol": "ascii",
|
|
190
|
+
"dictionary": "repo44"
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
| field | applies to | meaning | default |
|
|
196
|
+
| --- | --- | --- | --- |
|
|
197
|
+
| `resilient` | initiator | re-establish a lost transport and resume the same session | `false` |
|
|
198
|
+
| `reconnectSeconds` | initiator | wait between attempts whilst trying to establish a connection | `5` |
|
|
199
|
+
| `connectTimeoutSeconds` | initiator | how long to keep attempting before giving up | `60` resilient, `22` otherwise |
|
|
200
|
+
| `recoveryAttemptSeconds` | resilient only | wait after losing the transport before trying to get it back | `5` |
|
|
201
|
+
| `backoffFailConnectSeconds` | resilient only | wait after a failed recovery before trying again | `30` |
|
|
202
|
+
|
|
203
|
+
Call `stop()` on the launcher to give up: it cancels any pending recovery and ends the session, which is what lets the process exit. Without it a resilient initiator keeps trying for as long as it is running.
|
|
204
|
+
|
|
205
|
+
### Knowing when the session is up
|
|
206
|
+
|
|
207
|
+
`SessionLauncher.run()` is the lifetime of the application, not a signal that the session is ready. For an initiator it resolves when the session **ends**; it rejects if the first connection cannot be established within `connectTimeoutSeconds`. A resilient initiator does not resolve on a dropped connection at all — getting it back is the whole point — so it resolves only once you `stop()` it or the session ends for good.
|
|
208
|
+
|
|
209
|
+
To act when the session comes up, use `onReady` on your session class:
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
protected onReady (_view: MsgView): void {
|
|
213
|
+
// logged on - safe to send
|
|
214
|
+
this.send(MsgType.MarketDataRequest, this.subscription())
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
172
218
|
### Accepting or refusing a logon
|
|
173
219
|
|
|
174
220
|
`onLogon` is where an acceptor decides who gets a session. Return `true` to let the handshake finish; return `false` and the peer is sent a `Logout` explaining why and then disconnected — `onReady` is never reached and no heartbeat starts. The application learns the outcome through `onStopped(error)`.
|
|
@@ -14,6 +14,7 @@ export declare abstract class SessionLauncher {
|
|
|
14
14
|
protected makeSessionMsgFactory(_description: ISessionDescription): ISessionMsgFactory | null;
|
|
15
15
|
private empty;
|
|
16
16
|
private acceptorEntity;
|
|
17
|
+
private initiatorEntity;
|
|
17
18
|
private stopRequested;
|
|
18
19
|
protected getAcceptor(sessionContainer: DependencyContainer): Promise<any>;
|
|
19
20
|
protected getInitiator(sessionContainer: DependencyContainer): Promise<any>;
|
|
@@ -32,5 +33,6 @@ export declare abstract class SessionLauncher {
|
|
|
32
33
|
clientOrEmpty(): Promise<any>;
|
|
33
34
|
private setup;
|
|
34
35
|
private stopAcceptor;
|
|
36
|
+
private stopInitiator;
|
|
35
37
|
private loadConfig;
|
|
36
38
|
}
|
|
@@ -54,6 +54,7 @@ class SessionLauncher {
|
|
|
54
54
|
this.root = '../../';
|
|
55
55
|
this.sessionContainer = new session_container_1.SessionContainer((d) => this.makeSessionMsgFactory(d));
|
|
56
56
|
this.acceptorEntity = null;
|
|
57
|
+
this.initiatorEntity = null;
|
|
57
58
|
this.stopRequested = false;
|
|
58
59
|
this.logger = this.loggerFactory.logger('launcher');
|
|
59
60
|
this.initiatorConfig = initiatorConfig ? this.loadConfig(initiatorConfig) : null;
|
|
@@ -97,6 +98,11 @@ class SessionLauncher {
|
|
|
97
98
|
return __awaiter(this, void 0, void 0, function* () {
|
|
98
99
|
if (sessionContainer.isRegistered(di_tokens_1.DITokens.FixEntity)) {
|
|
99
100
|
const entity = sessionContainer.resolve(di_tokens_1.DITokens.FixEntity);
|
|
101
|
+
this.initiatorEntity = entity;
|
|
102
|
+
if (this.stopRequested) {
|
|
103
|
+
this.logger.info('stop requested before the initiator started - not connecting');
|
|
104
|
+
return this.empty();
|
|
105
|
+
}
|
|
100
106
|
return entity.start();
|
|
101
107
|
}
|
|
102
108
|
else {
|
|
@@ -110,6 +116,7 @@ class SessionLauncher {
|
|
|
110
116
|
stop() {
|
|
111
117
|
this.stopRequested = true;
|
|
112
118
|
this.stopAcceptor();
|
|
119
|
+
this.stopInitiator();
|
|
113
120
|
}
|
|
114
121
|
run() {
|
|
115
122
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -216,6 +223,12 @@ class SessionLauncher {
|
|
|
216
223
|
entity.stop();
|
|
217
224
|
}
|
|
218
225
|
}
|
|
226
|
+
stopInitiator() {
|
|
227
|
+
const entity = this.initiatorEntity;
|
|
228
|
+
if (entity && typeof entity.stop === 'function') {
|
|
229
|
+
entity.stop();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
219
232
|
loadConfig(config) {
|
|
220
233
|
if (typeof config === 'string') {
|
|
221
234
|
return require(path.join(this.root, config));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-launcher.js","sourceRoot":"","sources":["../../src/runtime/session-launcher.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path'\nimport { IJsFixConfig, IJsFixLogger, JsFixLoggerFactory, JsFixWinstonLogFactory, WinstonLogger } from '../config'\nimport { FixEntity, FixSession, ISessionDescription, ISessionMsgFactory } from '../transport'\nimport { DependencyContainer } from 'tsyringe'\nimport { EngineFactory } from './engine-factory'\nimport { SessionContainer } from './session-container'\nimport { DITokens } from './di-tokens'\n\nconst defaultLoggerFactory = new JsFixWinstonLogFactory(WinstonLogger.consoleOptions('info'))\n\nexport abstract class SessionLauncher {\n public root: string = '../../'\n protected readonly logger: IJsFixLogger\n public readonly initiatorConfig: ISessionDescription | null\n public readonly acceptorConfig: ISessionDescription | null\n\n protected constructor (\n initiatorConfig: string | ISessionDescription | null = null,\n acceptorConfig: string | ISessionDescription | null = null,\n private readonly loggerFactory: JsFixLoggerFactory = defaultLoggerFactory\n ) {\n this.logger = this.loggerFactory.logger('launcher')\n this.initiatorConfig = initiatorConfig ? this.loadConfig(initiatorConfig) : null\n this.acceptorConfig = acceptorConfig ? this.loadConfig(acceptorConfig) : null\n }\n\n // the arrow defers to makeSessionMsgFactory at resolve time, so a subclass need\n // only override that one method - no SessionContainer subclass required\n protected sessionContainer: SessionContainer =\n new SessionContainer((d: ISessionDescription) => this.makeSessionMsgFactory(d))\n\n /**\n * Override to supply this application's own session message factory - the place to\n * put a Logon carrying tags the standard message does not, or a header a\n * counterparty stamps differently. Return null (the default) for the stock\n * factory.\n *\n * For a Logon that only needs extra fields, prefer the \"Logon\" block in the session\n * description - it needs no code at all. Reach for a factory when the values are\n * computed at run time (a signature, a nonce, a token fetched at start up).\n */\n protected makeSessionMsgFactory (_description: ISessionDescription): ISessionMsgFactory | null {\n return null\n }\n\n private async empty (): Promise<any> {\n return await new Promise((resolve, reject) => {\n try {\n setImmediate(() => {\n this.logger.info('resolving an empty promise')\n resolve(null)\n })\n } catch (e) {\n reject(e)\n }\n })\n }\n\n private acceptorEntity: FixEntity | null = null\n private stopRequested: boolean = false\n\n protected async getAcceptor (sessionContainer: DependencyContainer): Promise<any> {\n if (sessionContainer.isRegistered<FixEntity>(DITokens.FixEntity)) {\n const entity = sessionContainer.resolve<FixEntity>(DITokens.FixEntity)\n this.acceptorEntity = entity\n // a stop can arrive while the container is still being built - the listener has\n // no existence yet to close, so honour the request by never opening it\n if (this.stopRequested) {\n this.logger.info('stop requested before the acceptor started - not listening')\n return this.empty()\n }\n return entity.start()\n } else {\n return this.empty()\n }\n }\n\n protected async getInitiator (sessionContainer: DependencyContainer): Promise<any> {\n if (sessionContainer.isRegistered<FixEntity>(DITokens.FixEntity)) {\n const entity = sessionContainer.resolve<FixEntity>(DITokens.FixEntity)\n return entity.start()\n } else {\n return this.empty()\n }\n }\n\n /**\n * provide a factory which will be invoked with a config where an instance of the application\n * should be constructed and returned.\n * @param config to be provided to the constucted application representing this session\n * @protected\n */\n protected makeFactory (config: IJsFixConfig): EngineFactory | null {\n return null\n }\n\n /**\n * Stop what this launcher started - in practice, close the acceptor's listener.\n *\n * An acceptor is meant to outlive any one counterparty, so nothing closes its\n * listener on its own. That is right for a venue and wrong for an application\n * which knows it is finished: the listening socket alone keeps node alive, long\n * after every session has logged out and every transport has been harvested.\n *\n * Until now the only caller was the both-roles branch of setup() below, which an\n * application cannot reach once it gives the acceptor a launcher of its own - the\n * usual arrangement as soon as more than one client is involved. There was then\n * no way to shut a listener down short of killing the process.\n *\n * Safe before start (the request is remembered and the listener never opens), and\n * safe to call twice. run() resolves once the listener has closed.\n */\n public stop (): void {\n this.stopRequested = true\n this.stopAcceptor()\n }\n\n public async run (): Promise<boolean> {\n return await new Promise<any>((resolve, reject) => {\n const logger = this.logger\n logger.info('launching ..')\n this.setup().then(() => {\n logger.info('.. done')\n resolve(true)\n }).catch((e: Error) => {\n logger.error(e)\n reject(e)\n })\n })\n }\n\n public exec (): void {\n this.run().then(() => {\n console.log('finished.')\n }).catch(e => {\n console.error(e)\n })\n }\n\n /**\n * is this session config representing an ascii based session\n * @param description config to be tested.\n */\n public isAscii (description: ISessionDescription): boolean {\n return this.sessionContainer.isAscii(description)\n }\n\n /**\n * is this session config representing an initiator based session\n * @param description config to be tested.\n */\n public isInitiator (description: ISessionDescription): boolean {\n return this.sessionContainer.isInitiator(description)\n }\n\n protected registerApplication (_: DependencyContainer): void {\n this.logger.info('bypass register via DI')\n }\n\n private async makeSystem (description: ISessionDescription): Promise<DependencyContainer> {\n const name = description.application?.name ?? 'na'\n const protocol = description.application?.protocol ?? 'ascii'\n this.logger.info(`creating app ${name} [protocol ${protocol}]`)\n return await this.sessionContainer.makeSystem(description)\n }\n\n private register (container: DependencyContainer): void {\n const config = container.resolve<IJsFixConfig>(DITokens.IJsFixConfig)\n const factory = this.makeFactory(config)\n if (!factory) {\n this.registerApplication(container)\n } else {\n if (factory.makeSession) {\n // resolve the config from the container doing the resolving, not the one\n // captured here. An acceptor resolves each session from a per-connection\n // child scope which re-registers IJsFixConfig, so this is how an application\n // receives that session's own description, store and message factory.\n container.register<FixSession>(DITokens.FixSession, {\n useFactory: (c) => factory.makeSession(c.resolve<IJsFixConfig>(DITokens.IJsFixConfig))\n })\n }\n }\n }\n\n private async makeClient (): Promise<any> {\n if (!this.initiatorConfig) return\n const sessionContainer = await this.makeSystem(this.initiatorConfig)\n this.register(sessionContainer)\n this.logger.info('create initiator')\n return await this.getInitiator(sessionContainer)\n }\n\n private async makeServer (): Promise<any> {\n if (!this.acceptorConfig) return\n const sessionContainer = await this.makeSystem(this.acceptorConfig)\n this.register(sessionContainer)\n this.logger.info('create acceptor')\n return await this.getAcceptor(sessionContainer)\n }\n\n async serverOrEmpty (): Promise<any> {\n return this.acceptorConfig ? this.makeServer() : this.empty()\n }\n\n async clientOrEmpty (): Promise<any> {\n return this.initiatorConfig ? this.makeClient() : this.empty()\n }\n\n private async setup (): Promise<any> {\n this.sessionContainer.registerGlobal(this.loggerFactory)\n const server = this.serverOrEmpty()\n const client = this.clientOrEmpty()\n this.logger.info('launching ....')\n if (this.acceptorConfig && this.initiatorConfig) {\n // Both mode: client drives the lifecycle. When client finishes,\n // stop the acceptor so the process can exit cleanly.\n await client\n this.logger.info('client finished, stopping acceptor')\n this.stop()\n return true\n }\n return await Promise.all([server, client])\n }\n\n private stopAcceptor (): void {\n const entity = this.acceptorEntity as any\n if (entity && typeof entity.stop === 'function') {\n entity.stop()\n }\n }\n\n private loadConfig (config: string | ISessionDescription): ISessionDescription {\n if (typeof config === 'string') {\n return require(path.join(this.root, config))\n }\n return config\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session-launcher.js","sourceRoot":"","sources":["../../src/runtime/session-launcher.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path'\nimport { IJsFixConfig, IJsFixLogger, JsFixLoggerFactory, JsFixWinstonLogFactory, WinstonLogger } from '../config'\nimport { FixEntity, FixSession, ISessionDescription, ISessionMsgFactory } from '../transport'\nimport { DependencyContainer } from 'tsyringe'\nimport { EngineFactory } from './engine-factory'\nimport { SessionContainer } from './session-container'\nimport { DITokens } from './di-tokens'\n\nconst defaultLoggerFactory = new JsFixWinstonLogFactory(WinstonLogger.consoleOptions('info'))\n\nexport abstract class SessionLauncher {\n public root: string = '../../'\n protected readonly logger: IJsFixLogger\n public readonly initiatorConfig: ISessionDescription | null\n public readonly acceptorConfig: ISessionDescription | null\n\n protected constructor (\n initiatorConfig: string | ISessionDescription | null = null,\n acceptorConfig: string | ISessionDescription | null = null,\n private readonly loggerFactory: JsFixLoggerFactory = defaultLoggerFactory\n ) {\n this.logger = this.loggerFactory.logger('launcher')\n this.initiatorConfig = initiatorConfig ? this.loadConfig(initiatorConfig) : null\n this.acceptorConfig = acceptorConfig ? this.loadConfig(acceptorConfig) : null\n }\n\n // the arrow defers to makeSessionMsgFactory at resolve time, so a subclass need\n // only override that one method - no SessionContainer subclass required\n protected sessionContainer: SessionContainer =\n new SessionContainer((d: ISessionDescription) => this.makeSessionMsgFactory(d))\n\n /**\n * Override to supply this application's own session message factory - the place to\n * put a Logon carrying tags the standard message does not, or a header a\n * counterparty stamps differently. Return null (the default) for the stock\n * factory.\n *\n * For a Logon that only needs extra fields, prefer the \"Logon\" block in the session\n * description - it needs no code at all. Reach for a factory when the values are\n * computed at run time (a signature, a nonce, a token fetched at start up).\n */\n protected makeSessionMsgFactory (_description: ISessionDescription): ISessionMsgFactory | null {\n return null\n }\n\n private async empty (): Promise<any> {\n return await new Promise((resolve, reject) => {\n try {\n setImmediate(() => {\n this.logger.info('resolving an empty promise')\n resolve(null)\n })\n } catch (e) {\n reject(e)\n }\n })\n }\n\n private acceptorEntity: FixEntity | null = null\n private initiatorEntity: FixEntity | null = null\n private stopRequested: boolean = false\n\n protected async getAcceptor (sessionContainer: DependencyContainer): Promise<any> {\n if (sessionContainer.isRegistered<FixEntity>(DITokens.FixEntity)) {\n const entity = sessionContainer.resolve<FixEntity>(DITokens.FixEntity)\n this.acceptorEntity = entity\n // a stop can arrive while the container is still being built - the listener has\n // no existence yet to close, so honour the request by never opening it\n if (this.stopRequested) {\n this.logger.info('stop requested before the acceptor started - not listening')\n return this.empty()\n }\n return entity.start()\n } else {\n return this.empty()\n }\n }\n\n protected async getInitiator (sessionContainer: DependencyContainer): Promise<any> {\n if (sessionContainer.isRegistered<FixEntity>(DITokens.FixEntity)) {\n const entity = sessionContainer.resolve<FixEntity>(DITokens.FixEntity)\n this.initiatorEntity = entity\n if (this.stopRequested) {\n this.logger.info('stop requested before the initiator started - not connecting')\n return this.empty()\n }\n return entity.start()\n } else {\n return this.empty()\n }\n }\n\n /**\n * provide a factory which will be invoked with a config where an instance of the application\n * should be constructed and returned.\n * @param config to be provided to the constucted application representing this session\n * @protected\n */\n protected makeFactory (config: IJsFixConfig): EngineFactory | null {\n return null\n }\n\n /**\n * Stop what this launcher started - close the acceptor's listener, and give up a\n * resilient initiator's attempts to get its transport back.\n *\n * An acceptor is meant to outlive any one counterparty, so nothing closes its\n * listener on its own. That is right for a venue and wrong for an application\n * which knows it is finished: the listening socket alone keeps node alive, long\n * after every session has logged out and every transport has been harvested.\n *\n * Until now the only caller was the both-roles branch of setup() below, which an\n * application cannot reach once it gives the acceptor a launcher of its own - the\n * usual arrangement as soon as more than one client is involved. There was then\n * no way to shut a listener down short of killing the process.\n *\n * A resilient initiator had the same problem from the other side: losing the\n * transport schedules another attempt, so it too kept the process alive with no way\n * to call it off. See issue #72.\n *\n * Safe before start (the request is remembered and neither is opened), and safe to\n * call twice. run() resolves once the listener has closed and the initiator has\n * given up.\n */\n public stop (): void {\n this.stopRequested = true\n this.stopAcceptor()\n this.stopInitiator()\n }\n\n public async run (): Promise<boolean> {\n return await new Promise<any>((resolve, reject) => {\n const logger = this.logger\n logger.info('launching ..')\n this.setup().then(() => {\n logger.info('.. done')\n resolve(true)\n }).catch((e: Error) => {\n logger.error(e)\n reject(e)\n })\n })\n }\n\n public exec (): void {\n this.run().then(() => {\n console.log('finished.')\n }).catch(e => {\n console.error(e)\n })\n }\n\n /**\n * is this session config representing an ascii based session\n * @param description config to be tested.\n */\n public isAscii (description: ISessionDescription): boolean {\n return this.sessionContainer.isAscii(description)\n }\n\n /**\n * is this session config representing an initiator based session\n * @param description config to be tested.\n */\n public isInitiator (description: ISessionDescription): boolean {\n return this.sessionContainer.isInitiator(description)\n }\n\n protected registerApplication (_: DependencyContainer): void {\n this.logger.info('bypass register via DI')\n }\n\n private async makeSystem (description: ISessionDescription): Promise<DependencyContainer> {\n const name = description.application?.name ?? 'na'\n const protocol = description.application?.protocol ?? 'ascii'\n this.logger.info(`creating app ${name} [protocol ${protocol}]`)\n return await this.sessionContainer.makeSystem(description)\n }\n\n private register (container: DependencyContainer): void {\n const config = container.resolve<IJsFixConfig>(DITokens.IJsFixConfig)\n const factory = this.makeFactory(config)\n if (!factory) {\n this.registerApplication(container)\n } else {\n if (factory.makeSession) {\n // resolve the config from the container doing the resolving, not the one\n // captured here. An acceptor resolves each session from a per-connection\n // child scope which re-registers IJsFixConfig, so this is how an application\n // receives that session's own description, store and message factory.\n container.register<FixSession>(DITokens.FixSession, {\n useFactory: (c) => factory.makeSession(c.resolve<IJsFixConfig>(DITokens.IJsFixConfig))\n })\n }\n }\n }\n\n private async makeClient (): Promise<any> {\n if (!this.initiatorConfig) return\n const sessionContainer = await this.makeSystem(this.initiatorConfig)\n this.register(sessionContainer)\n this.logger.info('create initiator')\n return await this.getInitiator(sessionContainer)\n }\n\n private async makeServer (): Promise<any> {\n if (!this.acceptorConfig) return\n const sessionContainer = await this.makeSystem(this.acceptorConfig)\n this.register(sessionContainer)\n this.logger.info('create acceptor')\n return await this.getAcceptor(sessionContainer)\n }\n\n async serverOrEmpty (): Promise<any> {\n return this.acceptorConfig ? this.makeServer() : this.empty()\n }\n\n async clientOrEmpty (): Promise<any> {\n return this.initiatorConfig ? this.makeClient() : this.empty()\n }\n\n private async setup (): Promise<any> {\n this.sessionContainer.registerGlobal(this.loggerFactory)\n const server = this.serverOrEmpty()\n const client = this.clientOrEmpty()\n this.logger.info('launching ....')\n if (this.acceptorConfig && this.initiatorConfig) {\n // Both mode: client drives the lifecycle. When client finishes,\n // stop the acceptor so the process can exit cleanly.\n await client\n this.logger.info('client finished, stopping acceptor')\n this.stop()\n return true\n }\n return await Promise.all([server, client])\n }\n\n private stopAcceptor (): void {\n const entity = this.acceptorEntity as any\n if (entity && typeof entity.stop === 'function') {\n entity.stop()\n }\n }\n\n // only a resilient initiator has anything to call off - a plain one ends with its\n // session, so it has no stop to find here\n private stopInitiator (): void {\n const entity = this.initiatorEntity as any\n if (entity && typeof entity.stop === 'function') {\n entity.stop()\n }\n }\n\n private loadConfig (config: string | ISessionDescription): ISessionDescription {\n if (typeof config === 'string') {\n return require(path.join(this.root, config))\n }\n return config\n }\n}\n"]}
|
|
@@ -5,6 +5,9 @@ export interface IMsgApplication {
|
|
|
5
5
|
readonly type: string;
|
|
6
6
|
readonly resilient: boolean;
|
|
7
7
|
readonly reconnectSeconds: number;
|
|
8
|
+
readonly connectTimeoutSeconds?: number;
|
|
9
|
+
readonly recoveryAttemptSeconds?: number;
|
|
10
|
+
readonly backoffFailConnectSeconds?: number;
|
|
8
11
|
readonly tcp?: ITcpTransportDescription;
|
|
9
12
|
readonly http?: IHttpTransportDescription;
|
|
10
13
|
readonly protocol: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"msg-application.js","sourceRoot":"","sources":["../../src/transport/msg-application.ts"],"names":[],"mappings":"","sourcesContent":["import { ITcpTransportDescription } from './tcp/tcp-transport-description'\nimport { IHttpTransportDescription } from './http/http-transport-description'\n\nexport interface IMsgApplication {\n readonly name: string\n readonly type: string\n readonly resilient: boolean\n readonly reconnectSeconds: number\n readonly tcp?: ITcpTransportDescription\n readonly http?: IHttpTransportDescription\n readonly protocol: string\n readonly dictionary: string\n}\n"]}
|
|
1
|
+
{"version":3,"file":"msg-application.js","sourceRoot":"","sources":["../../src/transport/msg-application.ts"],"names":[],"mappings":"","sourcesContent":["import { ITcpTransportDescription } from './tcp/tcp-transport-description'\nimport { IHttpTransportDescription } from './http/http-transport-description'\n\nexport interface IMsgApplication {\n readonly name: string\n readonly type: string\n /**\n * An initiator which re-establishes its transport after the connection drops and\n * resumes the same session, rather than ending when the socket does. Defaults to\n * false, which gives a single connection. See \"Reconnecting initiators\" in the\n * README for the retry policy below.\n */\n readonly resilient: boolean\n /** how long to wait between attempts whilst trying to establish a connection. Defaults to 5. */\n readonly reconnectSeconds: number\n /**\n * how long to keep attempting a connection before giving up. Defaults to 60 for a\n * resilient initiator and 22 for a plain one.\n */\n readonly connectTimeoutSeconds?: number\n /** resilient only: how long after losing the transport before trying to get it back. Defaults to 5. */\n readonly recoveryAttemptSeconds?: number\n /** resilient only: how long to wait after a failed recovery before trying again. Defaults to 30. */\n readonly backoffFailConnectSeconds?: number\n readonly tcp?: ITcpTransportDescription\n readonly http?: IHttpTransportDescription\n readonly protocol: string\n readonly dictionary: string\n}\n"]}
|
|
@@ -12,8 +12,13 @@ export declare class RecoveringTcpInitiator extends FixEntity {
|
|
|
12
12
|
private initiator;
|
|
13
13
|
private transport;
|
|
14
14
|
private th;
|
|
15
|
+
private stopRequested;
|
|
16
|
+
static readonly DefaultRecoveryAttemptSecs = 5;
|
|
17
|
+
static readonly DefaultBackoffFailConnectSecs = 30;
|
|
18
|
+
static readonly DefaultConnectTimeoutSecs = 60;
|
|
15
19
|
recoveryAttemptSecs: number;
|
|
16
20
|
backoffFailConnectSecs: number;
|
|
21
|
+
connectTimeoutSecs: number;
|
|
17
22
|
constructor(jsFixConfig: IJsFixConfig);
|
|
18
23
|
private createSession;
|
|
19
24
|
getState(): SessionState;
|
|
@@ -21,6 +26,7 @@ export declare class RecoveringTcpInitiator extends FixEntity {
|
|
|
21
26
|
private resetSeq;
|
|
22
27
|
private newTransport;
|
|
23
28
|
private clearTimer;
|
|
29
|
+
stop(): void;
|
|
24
30
|
private recover;
|
|
25
31
|
start(): Promise<any>;
|
|
26
32
|
run(initialTimeout?: number): Promise<any>;
|
|
@@ -20,6 +20,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
20
20
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
21
21
|
});
|
|
22
22
|
};
|
|
23
|
+
var RecoveringTcpInitiator_1;
|
|
23
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
25
|
exports.RecoveringTcpInitiator = void 0;
|
|
25
26
|
const tcp_initiator_1 = require("./tcp-initiator");
|
|
@@ -27,14 +28,16 @@ const session_state_1 = require("../session/session-state");
|
|
|
27
28
|
const tsyringe_1 = require("tsyringe");
|
|
28
29
|
const di_tokens_1 = require("../../runtime/di-tokens");
|
|
29
30
|
const fix_entity_1 = require("../fix-entity");
|
|
30
|
-
let RecoveringTcpInitiator = class RecoveringTcpInitiator extends fix_entity_1.FixEntity {
|
|
31
|
+
let RecoveringTcpInitiator = RecoveringTcpInitiator_1 = class RecoveringTcpInitiator extends fix_entity_1.FixEntity {
|
|
31
32
|
constructor(jsFixConfig) {
|
|
32
|
-
var _a, _b, _c, _d;
|
|
33
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
33
34
|
super(jsFixConfig);
|
|
34
35
|
this.jsFixConfig = jsFixConfig;
|
|
35
36
|
this.th = null;
|
|
36
|
-
this.
|
|
37
|
-
this.
|
|
37
|
+
this.stopRequested = false;
|
|
38
|
+
this.recoveryAttemptSecs = RecoveringTcpInitiator_1.DefaultRecoveryAttemptSecs;
|
|
39
|
+
this.backoffFailConnectSecs = RecoveringTcpInitiator_1.DefaultBackoffFailConnectSecs;
|
|
40
|
+
this.connectTimeoutSecs = RecoveringTcpInitiator_1.DefaultConnectTimeoutSecs;
|
|
38
41
|
this.application = (_a = this.jsFixConfig.description.application) !== null && _a !== void 0 ? _a : null;
|
|
39
42
|
const name = (_c = (_b = this.application) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : 'na';
|
|
40
43
|
this.logger = jsFixConfig.logFactory.logger(`${name}:RecoveringTcpInitiator`);
|
|
@@ -45,6 +48,11 @@ let RecoveringTcpInitiator = class RecoveringTcpInitiator extends fix_entity_1.F
|
|
|
45
48
|
if (!this.tcp) {
|
|
46
49
|
throw new Error('no tcp in session description need tcp { host: hostname, port: port }');
|
|
47
50
|
}
|
|
51
|
+
this.recoveryAttemptSecs = (_e = this.application.recoveryAttemptSeconds) !== null && _e !== void 0 ? _e : this.recoveryAttemptSecs;
|
|
52
|
+
this.backoffFailConnectSecs = (_f = this.application.backoffFailConnectSeconds) !== null && _f !== void 0 ? _f : this.backoffFailConnectSecs;
|
|
53
|
+
this.connectTimeoutSecs = (_g = this.application.connectTimeoutSeconds) !== null && _g !== void 0 ? _g : this.connectTimeoutSecs;
|
|
54
|
+
this.logger.info(`recovery policy: connectTimeout=${this.connectTimeoutSecs}s, ` +
|
|
55
|
+
`recoveryAttempt=${this.recoveryAttemptSecs}s, backoffFailConnect=${this.backoffFailConnectSecs}s`);
|
|
48
56
|
this.createSession(jsFixConfig);
|
|
49
57
|
}
|
|
50
58
|
createSession(jsFixConfig) {
|
|
@@ -58,6 +66,9 @@ let RecoveringTcpInitiator = class RecoveringTcpInitiator extends fix_entity_1.F
|
|
|
58
66
|
this.logger.info('session has permanently ended');
|
|
59
67
|
this.emit('end', this);
|
|
60
68
|
});
|
|
69
|
+
this.session.on('error', (e) => {
|
|
70
|
+
this.logger.info(`session error: ${e.message}`);
|
|
71
|
+
});
|
|
61
72
|
this.session.setState(session_state_1.SessionState.DisconnectedNoConnectionToday);
|
|
62
73
|
}
|
|
63
74
|
getState() {
|
|
@@ -99,7 +110,23 @@ let RecoveringTcpInitiator = class RecoveringTcpInitiator extends fix_entity_1.F
|
|
|
99
110
|
this.th = null;
|
|
100
111
|
}
|
|
101
112
|
}
|
|
113
|
+
stop() {
|
|
114
|
+
if (this.stopRequested)
|
|
115
|
+
return;
|
|
116
|
+
this.stopRequested = true;
|
|
117
|
+
this.logger.info('stop requested - cancelling recovery');
|
|
118
|
+
this.clearTimer();
|
|
119
|
+
if (this.session.getState() !== session_state_1.SessionState.Stopped) {
|
|
120
|
+
this.session.requestStop('initiator stopped');
|
|
121
|
+
}
|
|
122
|
+
this.emit('end', this);
|
|
123
|
+
}
|
|
102
124
|
recover() {
|
|
125
|
+
if (this.stopRequested) {
|
|
126
|
+
this.logger.info('transport lost after a stop was requested - not recovering');
|
|
127
|
+
this.emit('end', this);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
103
130
|
this.session.setState(session_state_1.SessionState.DetectBrokenNetworkConnection);
|
|
104
131
|
this.logger.info(`recover session transport - attempt in ${this.recoveryAttemptSecs} secs`);
|
|
105
132
|
if (!this.resetSeq()) {
|
|
@@ -110,7 +137,7 @@ let RecoveringTcpInitiator = class RecoveringTcpInitiator extends fix_entity_1.F
|
|
|
110
137
|
}
|
|
111
138
|
}
|
|
112
139
|
this.th = setTimeout(() => {
|
|
113
|
-
this.connect(
|
|
140
|
+
this.connect(this.connectTimeoutSecs).then(t => {
|
|
114
141
|
this.logger.info(`new transport ${t.id}`);
|
|
115
142
|
}).catch((e) => {
|
|
116
143
|
this.logger.info(`failed to re-connect ${e.message} - backoff for ${this.backoffFailConnectSecs}`);
|
|
@@ -129,16 +156,26 @@ let RecoveringTcpInitiator = class RecoveringTcpInitiator extends fix_entity_1.F
|
|
|
129
156
|
});
|
|
130
157
|
}
|
|
131
158
|
run() {
|
|
132
|
-
return __awaiter(this, arguments, void 0, function* (initialTimeout =
|
|
159
|
+
return __awaiter(this, arguments, void 0, function* (initialTimeout = this.connectTimeoutSecs) {
|
|
133
160
|
return yield new Promise((resolve, reject) => {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
161
|
+
let ended = false;
|
|
162
|
+
const finish = () => {
|
|
163
|
+
var _a, _b, _c;
|
|
164
|
+
if (ended)
|
|
165
|
+
return;
|
|
166
|
+
ended = true;
|
|
167
|
+
this.clearTimer();
|
|
168
|
+
(_a = this.initiator) === null || _a === void 0 ? void 0 : _a.end();
|
|
169
|
+
this.logger.info(`run: transport ${(_c = (_b = this.transport) === null || _b === void 0 ? void 0 : _b.id) !== null && _c !== void 0 ? _c : 'none'} ends - resolving`);
|
|
170
|
+
resolve(null);
|
|
171
|
+
};
|
|
172
|
+
this.on('end', finish);
|
|
173
|
+
this.connect(initialTimeout).catch(e => {
|
|
174
|
+
if (this.stopRequested) {
|
|
175
|
+
this.logger.info('run: first connection abandoned on stop - resolving');
|
|
176
|
+
finish();
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
142
179
|
this.logger.info(`run: failed to connect to first transport ${initialTimeout} - rejecting`);
|
|
143
180
|
reject(e);
|
|
144
181
|
});
|
|
@@ -165,7 +202,10 @@ let RecoveringTcpInitiator = class RecoveringTcpInitiator extends fix_entity_1.F
|
|
|
165
202
|
}
|
|
166
203
|
};
|
|
167
204
|
exports.RecoveringTcpInitiator = RecoveringTcpInitiator;
|
|
168
|
-
|
|
205
|
+
RecoveringTcpInitiator.DefaultRecoveryAttemptSecs = 5;
|
|
206
|
+
RecoveringTcpInitiator.DefaultBackoffFailConnectSecs = 30;
|
|
207
|
+
RecoveringTcpInitiator.DefaultConnectTimeoutSecs = 60;
|
|
208
|
+
exports.RecoveringTcpInitiator = RecoveringTcpInitiator = RecoveringTcpInitiator_1 = __decorate([
|
|
169
209
|
(0, tsyringe_1.injectable)(),
|
|
170
210
|
__param(0, (0, tsyringe_1.inject)(di_tokens_1.DITokens.IJsFixConfig)),
|
|
171
211
|
__metadata("design:paramtypes", [Object])
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recovering-tcp-initiator.js","sourceRoot":"","sources":["../../../src/transport/tcp/recovering-tcp-initiator.ts"],"names":[],"mappings":"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","sourcesContent":["import { IJsFixConfig, IJsFixLogger } from '../../config'\nimport { FixSession } from '../session/fix-session'\nimport { TcpInitiator } from './tcp-initiator'\nimport { MsgTransport } from '../factory'\nimport Timeout = NodeJS.Timeout\nimport { SessionState } from '../session/session-state'\nimport { inject, injectable } from 'tsyringe'\nimport { DITokens } from '../../runtime/di-tokens'\nimport { ITcpTransportDescription } from './tcp-transport-description'\nimport { IMsgApplication } from '../msg-application'\nimport { FixEntity } from '../fix-entity'\n\n/*\n create one application session instance - and recover a lost transport. Hence, the application\n will automatically re-connected and the \"message recovery\" policy enacted i.e. replay from\n last known sequence number or sequence reset.\n */\n\n@injectable()\nexport class RecoveringTcpInitiator extends FixEntity {\n public tcp: ITcpTransportDescription | null\n public session: FixSession\n private readonly logger: IJsFixLogger\n private readonly application: IMsgApplication | null\n private initiator: TcpInitiator\n private transport: MsgTransport\n private th: Timeout | null = null\n public recoveryAttemptSecs: number = 5\n public backoffFailConnectSecs: number = 30\n\n constructor (@inject(DITokens.IJsFixConfig) public readonly jsFixConfig: IJsFixConfig) {\n super(jsFixConfig)\n this.application = this.jsFixConfig.description.application ?? null\n const name = this.application?.name ?? 'na'\n this.logger = jsFixConfig.logFactory.logger(`${name}:RecoveringTcpInitiator`)\n if (!this.application) {\n throw new Error('no application in session description.')\n }\n this.tcp = this.application.tcp ?? null\n if (!this.tcp) {\n throw new Error('no tcp in session description need tcp { host: hostname, port: port }')\n }\n this.createSession(jsFixConfig)\n }\n\n private createSession (jsFixConfig: IJsFixConfig): void {\n this.logger.info(`creating an application session with DI token ${DITokens.FixSession}.`)\n this.session = jsFixConfig.sessionContainer.resolve<FixSession>(DITokens.FixSession)\n this.session.on('done', () => {\n this.logger.info('session has permanently ended')\n this.emit('end', this)\n })\n this.session.on('end', () => {\n this.logger.info('session has permanently ended')\n this.emit('end', this)\n })\n this.session.setState(SessionState.DisconnectedNoConnectionToday)\n }\n\n public getState (): SessionState {\n return this.session.getState()\n }\n\n public lastSentSeqNum (): number {\n return this.session.lastSentSeqNum()\n }\n\n private resetSeq (): boolean {\n return this.jsFixConfig.description.ResetSeqNumFlag\n }\n\n private newTransport (transport: MsgTransport): void {\n this.transport = transport\n this.emit('transport', transport)\n this.logger.info(`initiator connects id ${(transport.id)}`)\n const session = this.session\n if (this.resetSeq()) {\n this.logger.info('reset sequence numbers')\n session.reset()\n }\n session.setState(SessionState.NetworkConnectionEstablished)\n session.run(transport).then((id: number) => {\n if (!transport || id === transport.id) {\n this.emit('end', this)\n } else {\n this.logger.info(`old transport ${id} ends waiting on ${(transport.id)}`)\n }\n }).catch(e => {\n this.logger.info(`transport id ${(transport.id)} failed - session state ${session.getState()}`)\n this.logger.warning(e.message)\n this.recover()\n })\n this.logger.info(`running session with transport ${transport.id} state = ${session.getState()}`)\n }\n\n private clearTimer (): void {\n if (this.th) {\n clearTimeout(this.th)\n this.th = null\n }\n }\n\n // at least one connection was established so retry to establish - either\n // succeed in which case can restart session or fails in which case wait and\n // restart an attempt to connect\n\n private recover (): void {\n this.session.setState(SessionState.DetectBrokenNetworkConnection)\n this.logger.info(`recover session transport - attempt in ${this.recoveryAttemptSecs} secs`)\n if (!this.resetSeq()) {\n const lastSentSeqNum = this.lastSentSeqNum()\n if (lastSentSeqNum > 0) {\n this.logger.info(`recover session set LastSentSeqNum ${lastSentSeqNum} for new transport`)\n this.config.description.LastSentSeqNum = lastSentSeqNum\n }\n }\n this.th = setTimeout(() => {\n this.connect(60).then(t => {\n this.logger.info(`new transport ${t.id}`)\n }).catch((e) => {\n this.logger.info(`failed to re-connect ${e.message} - backoff for ${this.backoffFailConnectSecs}`)\n this.th = setTimeout(() => {\n this.logger.info('returning to recover()')\n setImmediate(() => {\n this.recover()\n })\n }, this.backoffFailConnectSecs * 1000)\n })\n }, this.recoveryAttemptSecs * 1000)\n }\n\n public async start (): Promise<any> {\n return await this.run()\n }\n\n // for first connection - reject if no initial connection established within timeout\n // once connection established, will not resolve until session is ended - i.e. lost\n // connections are re-established using the same session instance.\n\n public async run (initialTimeout: number = 60): Promise<any> {\n return await new Promise<any>((resolve, reject) => {\n this.connect(initialTimeout).then(() => {\n this.on('end', () => {\n this.clearTimer()\n this.initiator.end()\n this.logger.info(`run: transport ${this.transport.id} gracefully ends ${initialTimeout} - resolving`)\n resolve(null)\n })\n }).catch(e => {\n this.logger.info(`run: failed to connect to first transport ${initialTimeout} - rejecting`)\n reject(e)\n })\n })\n }\n\n // return a promise for new transport - or reject if no connection within timeout\n\n private async connect (timeout: number): Promise<MsgTransport> {\n return await new Promise<MsgTransport>((resolve, reject) => {\n this.logger.info(`connect: start initiator timeout ${timeout}`)\n this.session.setState(SessionState.InitiateConnection)\n this.initiator = new TcpInitiator(this.jsFixConfig)\n this.initiator.connect(timeout).then((transport: MsgTransport) => {\n this.logger.info(`connect: receive new transport ${transport.id}`)\n this.newTransport(transport)\n resolve(transport)\n }).catch(e => {\n this.logger.info(`connect: failed to connect within ${timeout} - rejecting`)\n this.session.setState(SessionState.DetectBrokenNetworkConnection)\n reject(e)\n })\n })\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"recovering-tcp-initiator.js","sourceRoot":"","sources":["../../../src/transport/tcp/recovering-tcp-initiator.ts"],"names":[],"mappings":"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","sourcesContent":["import { IJsFixConfig, IJsFixLogger } from '../../config'\nimport { FixSession } from '../session/fix-session'\nimport { TcpInitiator } from './tcp-initiator'\nimport { MsgTransport } from '../factory'\nimport Timeout = NodeJS.Timeout\nimport { SessionState } from '../session/session-state'\nimport { inject, injectable } from 'tsyringe'\nimport { DITokens } from '../../runtime/di-tokens'\nimport { ITcpTransportDescription } from './tcp-transport-description'\nimport { IMsgApplication } from '../msg-application'\nimport { FixEntity } from '../fix-entity'\n\n/*\n create one application session instance - and recover a lost transport. Hence, the application\n will automatically re-connected and the \"message recovery\" policy enacted i.e. replay from\n last known sequence number or sequence reset.\n */\n\n@injectable()\nexport class RecoveringTcpInitiator extends FixEntity {\n public tcp: ITcpTransportDescription | null\n public session: FixSession\n private readonly logger: IJsFixLogger\n private readonly application: IMsgApplication | null\n private initiator: TcpInitiator\n private transport: MsgTransport\n private th: Timeout | null = null\n private stopRequested: boolean = false\n public static readonly DefaultRecoveryAttemptSecs = 5\n public static readonly DefaultBackoffFailConnectSecs = 30\n public static readonly DefaultConnectTimeoutSecs = 60\n\n public recoveryAttemptSecs: number = RecoveringTcpInitiator.DefaultRecoveryAttemptSecs\n public backoffFailConnectSecs: number = RecoveringTcpInitiator.DefaultBackoffFailConnectSecs\n public connectTimeoutSecs: number = RecoveringTcpInitiator.DefaultConnectTimeoutSecs\n\n constructor (@inject(DITokens.IJsFixConfig) public readonly jsFixConfig: IJsFixConfig) {\n super(jsFixConfig)\n this.application = this.jsFixConfig.description.application ?? null\n const name = this.application?.name ?? 'na'\n this.logger = jsFixConfig.logFactory.logger(`${name}:RecoveringTcpInitiator`)\n if (!this.application) {\n throw new Error('no application in session description.')\n }\n this.tcp = this.application.tcp ?? null\n if (!this.tcp) {\n throw new Error('no tcp in session description need tcp { host: hostname, port: port }')\n }\n // the retry policy used to live only in these fields, which nothing set - an\n // application could not change how hard or how long the engine tried to get its\n // transport back without subclassing the launcher. See issue #72.\n this.recoveryAttemptSecs = this.application.recoveryAttemptSeconds ?? this.recoveryAttemptSecs\n this.backoffFailConnectSecs = this.application.backoffFailConnectSeconds ?? this.backoffFailConnectSecs\n this.connectTimeoutSecs = this.application.connectTimeoutSeconds ?? this.connectTimeoutSecs\n this.logger.info(`recovery policy: connectTimeout=${this.connectTimeoutSecs}s, ` +\n `recoveryAttempt=${this.recoveryAttemptSecs}s, backoffFailConnect=${this.backoffFailConnectSecs}s`)\n this.createSession(jsFixConfig)\n }\n\n private createSession (jsFixConfig: IJsFixConfig): void {\n this.logger.info(`creating an application session with DI token ${DITokens.FixSession}.`)\n this.session = jsFixConfig.sessionContainer.resolve<FixSession>(DITokens.FixSession)\n this.session.on('done', () => {\n this.logger.info('session has permanently ended')\n this.emit('end', this)\n })\n this.session.on('end', () => {\n this.logger.info('session has permanently ended')\n this.emit('end', this)\n })\n // A lost transport is this initiator's normal course of events, and recovery is\n // driven by session.run() rejecting rather than by this event. But whilst waiting\n // between attempts there is no run in flight to carry a listener, so a stop landing\n // then would emit 'error' to nobody - which node treats as fatal.\n this.session.on('error', (e: Error) => {\n this.logger.info(`session error: ${e.message}`)\n })\n this.session.setState(SessionState.DisconnectedNoConnectionToday)\n }\n\n public getState (): SessionState {\n return this.session.getState()\n }\n\n public lastSentSeqNum (): number {\n return this.session.lastSentSeqNum()\n }\n\n private resetSeq (): boolean {\n return this.jsFixConfig.description.ResetSeqNumFlag\n }\n\n private newTransport (transport: MsgTransport): void {\n this.transport = transport\n this.emit('transport', transport)\n this.logger.info(`initiator connects id ${(transport.id)}`)\n const session = this.session\n if (this.resetSeq()) {\n this.logger.info('reset sequence numbers')\n session.reset()\n }\n session.setState(SessionState.NetworkConnectionEstablished)\n session.run(transport).then((id: number) => {\n if (!transport || id === transport.id) {\n this.emit('end', this)\n } else {\n this.logger.info(`old transport ${id} ends waiting on ${(transport.id)}`)\n }\n }).catch(e => {\n this.logger.info(`transport id ${(transport.id)} failed - session state ${session.getState()}`)\n this.logger.warning(e.message)\n this.recover()\n })\n this.logger.info(`running session with transport ${transport.id} state = ${session.getState()}`)\n }\n\n private clearTimer (): void {\n if (this.th) {\n clearTimeout(this.th)\n this.th = null\n }\n }\n\n // at least one connection was established so retry to establish - either\n // succeed in which case can restart session or fails in which case wait and\n // restart an attempt to connect\n\n /**\n * Give up recovering and end the session for good.\n *\n * Without this a resilient initiator could not be shut down: losing the transport\n * schedules another attempt, and nothing cancelled that timer, so the process stayed\n * alive retrying a counterparty the application had finished with. Safe to call\n * twice, and safe whilst waiting between attempts with no transport at all.\n */\n public stop (): void {\n if (this.stopRequested) return\n this.stopRequested = true\n this.logger.info('stop requested - cancelling recovery')\n this.clearTimer()\n if (this.session.getState() !== SessionState.Stopped) {\n this.session.requestStop('initiator stopped')\n }\n this.emit('end', this)\n }\n\n private recover (): void {\n if (this.stopRequested) {\n this.logger.info('transport lost after a stop was requested - not recovering')\n this.emit('end', this)\n return\n }\n this.session.setState(SessionState.DetectBrokenNetworkConnection)\n this.logger.info(`recover session transport - attempt in ${this.recoveryAttemptSecs} secs`)\n if (!this.resetSeq()) {\n const lastSentSeqNum = this.lastSentSeqNum()\n if (lastSentSeqNum > 0) {\n this.logger.info(`recover session set LastSentSeqNum ${lastSentSeqNum} for new transport`)\n this.config.description.LastSentSeqNum = lastSentSeqNum\n }\n }\n this.th = setTimeout(() => {\n this.connect(this.connectTimeoutSecs).then(t => {\n this.logger.info(`new transport ${t.id}`)\n }).catch((e) => {\n this.logger.info(`failed to re-connect ${e.message} - backoff for ${this.backoffFailConnectSecs}`)\n this.th = setTimeout(() => {\n this.logger.info('returning to recover()')\n setImmediate(() => {\n this.recover()\n })\n }, this.backoffFailConnectSecs * 1000)\n })\n }, this.recoveryAttemptSecs * 1000)\n }\n\n public async start (): Promise<any> {\n return await this.run()\n }\n\n // for first connection - reject if no initial connection established within timeout\n // once connection established, will not resolve until session is ended - i.e. lost\n // connections are re-established using the same session instance. So this promise\n // is the lifetime of the application, not a signal that the session is up: for that,\n // use onReady on the session. See issue #72.\n\n public async run (initialTimeout: number = this.connectTimeoutSecs): Promise<any> {\n return await new Promise<any>((resolve, reject) => {\n // 'end' can arrive more than once - a stop both ends the session and calls off\n // recovery, and each path announces itself - so settle only on the first. The\n // listener goes on before connecting, not after: a stop landing whilst the first\n // connection is still being attempted has to be able to end this too, and until\n // now there was nothing listening that early to hear it.\n let ended = false\n const finish = (): void => {\n if (ended) return\n ended = true\n this.clearTimer()\n this.initiator?.end()\n this.logger.info(`run: transport ${this.transport?.id ?? 'none'} ends - resolving`)\n resolve(null)\n }\n this.on('end', finish)\n\n this.connect(initialTimeout).catch(e => {\n if (this.stopRequested) {\n this.logger.info('run: first connection abandoned on stop - resolving')\n finish()\n return\n }\n this.logger.info(`run: failed to connect to first transport ${initialTimeout} - rejecting`)\n reject(e)\n })\n })\n }\n\n // return a promise for new transport - or reject if no connection within timeout\n\n private async connect (timeout: number): Promise<MsgTransport> {\n return await new Promise<MsgTransport>((resolve, reject) => {\n this.logger.info(`connect: start initiator timeout ${timeout}`)\n this.session.setState(SessionState.InitiateConnection)\n this.initiator = new TcpInitiator(this.jsFixConfig)\n this.initiator.connect(timeout).then((transport: MsgTransport) => {\n this.logger.info(`connect: receive new transport ${transport.id}`)\n this.newTransport(transport)\n resolve(transport)\n }).catch(e => {\n this.logger.info(`connect: failed to connect within ${timeout} - rejecting`)\n this.session.setState(SessionState.DetectBrokenNetworkConnection)\n reject(e)\n })\n })\n }\n}\n"]}
|
|
@@ -3,6 +3,8 @@ import { FixSession } from '../session/fix-session';
|
|
|
3
3
|
import { FixEntity } from '../fix-entity';
|
|
4
4
|
export declare class TcpInitiatorConnector extends FixEntity {
|
|
5
5
|
readonly config: IJsFixConfig;
|
|
6
|
+
static readonly DefaultConnectTimeoutSecs = 22;
|
|
7
|
+
connectTimeoutSecs: number;
|
|
6
8
|
constructor(config: IJsFixConfig);
|
|
7
9
|
start(reconnectTimeout?: number): Promise<any>;
|
|
8
10
|
delay(p: number): Promise<any>;
|
|
@@ -20,16 +20,20 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
20
20
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
21
21
|
});
|
|
22
22
|
};
|
|
23
|
+
var TcpInitiatorConnector_1;
|
|
23
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
25
|
exports.TcpInitiatorConnector = void 0;
|
|
25
26
|
const tcp_initiator_1 = require("./tcp-initiator");
|
|
26
27
|
const tsyringe_1 = require("tsyringe");
|
|
27
28
|
const di_tokens_1 = require("../../runtime/di-tokens");
|
|
28
29
|
const fix_entity_1 = require("../fix-entity");
|
|
29
|
-
let TcpInitiatorConnector = class TcpInitiatorConnector extends fix_entity_1.FixEntity {
|
|
30
|
+
let TcpInitiatorConnector = TcpInitiatorConnector_1 = class TcpInitiatorConnector extends fix_entity_1.FixEntity {
|
|
30
31
|
constructor(config) {
|
|
32
|
+
var _a, _b;
|
|
31
33
|
super(config);
|
|
32
34
|
this.config = config;
|
|
35
|
+
this.connectTimeoutSecs = TcpInitiatorConnector_1.DefaultConnectTimeoutSecs;
|
|
36
|
+
this.connectTimeoutSecs = (_b = (_a = config.description.application) === null || _a === void 0 ? void 0 : _a.connectTimeoutSeconds) !== null && _b !== void 0 ? _b : this.connectTimeoutSecs;
|
|
33
37
|
}
|
|
34
38
|
start() {
|
|
35
39
|
return __awaiter(this, arguments, void 0, function* (reconnectTimeout = 0) {
|
|
@@ -85,7 +89,7 @@ let TcpInitiatorConnector = class TcpInitiatorConnector extends fix_entity_1.Fix
|
|
|
85
89
|
const logger = this.config.logFactory.logger('initiator');
|
|
86
90
|
const initiator = this.config.sessionContainer.resolve(tcp_initiator_1.TcpInitiator);
|
|
87
91
|
logger.info('connecting ...');
|
|
88
|
-
const initiatorTransport = yield initiator.connect(
|
|
92
|
+
const initiatorTransport = yield initiator.connect(this.connectTimeoutSecs);
|
|
89
93
|
logger.info('... connected, run session');
|
|
90
94
|
yield initiatorSession.run(initiatorTransport);
|
|
91
95
|
logger.info('ends');
|
|
@@ -94,7 +98,8 @@ let TcpInitiatorConnector = class TcpInitiatorConnector extends fix_entity_1.Fix
|
|
|
94
98
|
}
|
|
95
99
|
};
|
|
96
100
|
exports.TcpInitiatorConnector = TcpInitiatorConnector;
|
|
97
|
-
|
|
101
|
+
TcpInitiatorConnector.DefaultConnectTimeoutSecs = 22;
|
|
102
|
+
exports.TcpInitiatorConnector = TcpInitiatorConnector = TcpInitiatorConnector_1 = __decorate([
|
|
98
103
|
(0, tsyringe_1.injectable)(),
|
|
99
104
|
__param(0, (0, tsyringe_1.inject)(di_tokens_1.DITokens.IJsFixConfig)),
|
|
100
105
|
__metadata("design:paramtypes", [Object])
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tcp-initiator-connector.js","sourceRoot":"","sources":["../../../src/transport/tcp/tcp-initiator-connector.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tcp-initiator-connector.js","sourceRoot":"","sources":["../../../src/transport/tcp/tcp-initiator-connector.ts"],"names":[],"mappings":"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","sourcesContent":["import { IJsFixConfig } from '../../config'\nimport { FixInitiator } from '../fix-initiator'\nimport { TcpInitiator } from './tcp-initiator'\nimport { MsgTransport } from '../factory'\nimport { FixSession } from '../session/fix-session'\nimport { inject, injectable } from 'tsyringe'\nimport { DITokens } from '../../runtime/di-tokens'\nimport { FixEntity } from '../fix-entity'\n\n@injectable()\nexport class TcpInitiatorConnector extends FixEntity {\n public static readonly DefaultConnectTimeoutSecs = 22\n\n public connectTimeoutSecs: number = TcpInitiatorConnector.DefaultConnectTimeoutSecs\n\n constructor (@inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig) {\n super(config)\n this.connectTimeoutSecs = config.description.application?.connectTimeoutSeconds ?? this.connectTimeoutSecs\n }\n\n async start (reconnectTimeout: number = 0): Promise<any> {\n\n return await new Promise<any>(async (resolve, reject) => {\n const logger = this.config.logFactory.logger('initiator')\n const sessionContainer = this.config.sessionContainer\n if (!sessionContainer.isRegistered(DITokens.FixSession)) {\n reject(new Error(`application must register a DI token '${DITokens.FixSession}' - see src/sample`))\n }\n logger.info(`create session with DI Token ${DITokens.FixSession}`)\n const initiatorSession = sessionContainer.resolve<FixSession>(DITokens.FixSession)\n let connecting: boolean = true\n while (connecting) {\n try {\n await this.connect(initiatorSession)\n logger.info('session has ended')\n connecting = false\n resolve(true)\n } catch (e) {\n if (!reconnectTimeout) {\n connecting = false\n reject(e)\n } else {\n logger.info(`waiting ${reconnectTimeout} to reconnect following error`)\n await this.delay(reconnectTimeout)\n }\n }\n }\n })\n }\n\n async delay (p: number): Promise<any> {\n return await new Promise<any>((resolve, reject) => {\n try {\n if (!p) {\n resolve(true)\n }\n setTimeout(() => {\n resolve(true)\n }, p)\n } catch (e) {\n reject(e)\n }\n })\n }\n\n async connect (initiatorSession: FixSession): Promise<any> {\n const logger = this.config.logFactory.logger('initiator')\n const initiator: FixInitiator = this.config.sessionContainer.resolve<FixInitiator>(TcpInitiator)\n logger.info('connecting ...')\n const initiatorTransport: MsgTransport = await initiator.connect(this.connectTimeoutSecs)\n logger.info('... connected, run session')\n await initiatorSession.run(initiatorTransport)\n logger.info('ends')\n return true\n }\n}\n"]}
|
|
@@ -1,43 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
2
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
19
3
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
4
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
21
5
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
22
6
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
23
7
|
};
|
|
24
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
-
var ownKeys = function(o) {
|
|
26
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
-
var ar = [];
|
|
28
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
-
return ar;
|
|
30
|
-
};
|
|
31
|
-
return ownKeys(o);
|
|
32
|
-
};
|
|
33
|
-
return function (mod) {
|
|
34
|
-
if (mod && mod.__esModule) return mod;
|
|
35
|
-
var result = {};
|
|
36
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
-
__setModuleDefault(result, mod);
|
|
38
|
-
return result;
|
|
39
|
-
};
|
|
40
|
-
})();
|
|
41
8
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
42
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
43
10
|
};
|
|
@@ -58,7 +25,6 @@ exports.TcpInitiator = exports.InitiatorState = void 0;
|
|
|
58
25
|
const fix_initiator_1 = require("../fix-initiator");
|
|
59
26
|
const factory_1 = require("../factory");
|
|
60
27
|
const duplex_1 = require("../duplex");
|
|
61
|
-
const util = __importStar(require("util"));
|
|
62
28
|
const tls_1 = require("tls");
|
|
63
29
|
const net_1 = require("net");
|
|
64
30
|
const tls_options_factory_1 = require("./tls-options-factory");
|
|
@@ -79,6 +45,7 @@ let TcpInitiator = class TcpInitiator extends fix_initiator_1.FixInitiator {
|
|
|
79
45
|
this.jsFixConfig = jsFixConfig;
|
|
80
46
|
this.state = InitiatorState.Idle;
|
|
81
47
|
this.th = null;
|
|
48
|
+
this.timeoutTh = null;
|
|
82
49
|
const name = (_c = (_b = this.application) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : 'initiator';
|
|
83
50
|
this.logger = jsFixConfig.logFactory.logger(`${name}:TcpInitiator`);
|
|
84
51
|
if (!this.application) {
|
|
@@ -233,14 +200,16 @@ let TcpInitiator = class TcpInitiator extends fix_initiator_1.FixInitiator {
|
|
|
233
200
|
clearInterval(this.th);
|
|
234
201
|
this.th = null;
|
|
235
202
|
}
|
|
203
|
+
if (this.timeoutTh) {
|
|
204
|
+
clearTimeout(this.timeoutTh);
|
|
205
|
+
this.timeoutTh = null;
|
|
206
|
+
}
|
|
236
207
|
}
|
|
237
208
|
repeatConnect(timeoutSeconds, initialError) {
|
|
238
209
|
return __awaiter(this, void 0, void 0, function* () {
|
|
239
210
|
return yield new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
240
211
|
var _a, _b;
|
|
241
212
|
const application = this.application;
|
|
242
|
-
const promisify = util.promisify;
|
|
243
|
-
const timeoutPromise = promisify(setTimeout);
|
|
244
213
|
const reconnectSeconds = (_a = application === null || application === void 0 ? void 0 : application.reconnectSeconds) !== null && _a !== void 0 ? _a : 5;
|
|
245
214
|
let retries = 0;
|
|
246
215
|
let lastError = initialError;
|
|
@@ -257,15 +226,13 @@ let TcpInitiator = class TcpInitiator extends fix_initiator_1.FixInitiator {
|
|
|
257
226
|
this.logger.info(`${name}: retries ${retries} ${e.message}`);
|
|
258
227
|
});
|
|
259
228
|
}, reconnectSeconds * 1000);
|
|
260
|
-
|
|
229
|
+
this.timeoutTh = setTimeout(() => {
|
|
261
230
|
this.clearTimer();
|
|
262
231
|
this.state = InitiatorState.Stopped;
|
|
263
232
|
const e = lastError !== null && lastError !== void 0 ? lastError : new Error(`${name}: timeout of ${timeoutSeconds} whilst connecting`);
|
|
264
233
|
this.logger.warning(`${name}: giving up after ${timeoutSeconds}s and ${retries} retries, last error: ${e.message}`);
|
|
265
234
|
reject(e);
|
|
266
|
-
}
|
|
267
|
-
reject(e);
|
|
268
|
-
});
|
|
235
|
+
}, timeoutSeconds * 1000);
|
|
269
236
|
}));
|
|
270
237
|
});
|
|
271
238
|
}
|