jspurefix 5.10.5 → 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 +70 -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/ascii/ascii-session-msg-factory.d.ts +1 -1
- package/dist/transport/ascii/ascii-session-msg-factory.js +2 -2
- package/dist/transport/ascii/ascii-session-msg-factory.js.map +1 -1
- package/dist/transport/ascii/ascii-session.d.ts +6 -0
- package/dist/transport/ascii/ascii-session.js +72 -8
- package/dist/transport/ascii/ascii-session.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/session/fix-session.d.ts +1 -1
- package/dist/transport/session/fix-session.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/ascii/ascii-session-msg-factory.ts +8 -2
- package/src/transport/ascii/ascii-session.ts +115 -7
- package/src/transport/msg-application.ts +16 -0
- package/src/transport/session/fix-session.ts +12 -2
- 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,9 @@ 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)
|
|
18
|
+
- [Accepting or refusing a logon](#accepting-or-refusing-a-logon)
|
|
16
19
|
- [TLS](#tls)
|
|
17
20
|
- [Body length padding](#body-length-padding)
|
|
18
21
|
- [Persistence & Recovery](#persistence--recovery)
|
|
@@ -168,6 +171,73 @@ Most counterparties want a tag on the Logon the standard message does not carry.
|
|
|
168
171
|
|
|
169
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.
|
|
170
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
|
+
|
|
218
|
+
### Accepting or refusing a logon
|
|
219
|
+
|
|
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)`.
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
protected onLogon (view: MsgView, user: string, password: string): boolean {
|
|
224
|
+
return user === 'js-client' && password === this.expectedPassword
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Credentials usually have to be checked against something the engine cannot wait on synchronously — an http api, a database, a secrets store. Return a promise and the session waits for it:
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
protected async onLogon (view: MsgView, user: string, password: string): Promise<boolean> {
|
|
232
|
+
const account = await this.directory.lookup(user)
|
|
233
|
+
return account?.enabled === true && await account.verify(password)
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
While that promise is pending the transport is paused and anything the peer sends is queued, then replayed in order once the verdict arrives — a counterparty that pipelines messages behind its Logon will not have them dropped or reordered. If the promise rejects, the logon is refused rather than allowed through, and the rejection message is carried to the peer.
|
|
238
|
+
|
|
239
|
+
An initiator can use the same hook: there `onLogon` fires on the acceptor's logon response, so returning `false` refuses the counterparty that answered.
|
|
240
|
+
|
|
171
241
|
### TLS
|
|
172
242
|
|
|
173
243
|
Add a `tls` block under `application.tcp`. The `ca` field is only needed for self-signed certificates; commercial vendors will supply this for you. `script/getKey.ps1` will generate a self-signed CA + client/server pair (requires `openssl` on the path).
|
|
@@ -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"]}
|
|
@@ -6,7 +6,7 @@ export declare class AsciiSessionMsgFactory extends ASessionMsgFactory {
|
|
|
6
6
|
readonly description: ISessionDescription;
|
|
7
7
|
constructor(description: ISessionDescription, mutator?: ObjectMutator | null);
|
|
8
8
|
logon(): ILooseObject;
|
|
9
|
-
logout(text: string): ILooseObject;
|
|
9
|
+
logout(msgType: string, text: string): ILooseObject;
|
|
10
10
|
header(msgType: string, seqNum: number, time: Date, overrideData?: Partial<IStandardHeader>): ILooseObject;
|
|
11
11
|
}
|
|
12
12
|
export declare class SessionMsgFactory extends AsciiSessionMsgFactory {
|
|
@@ -16,11 +16,11 @@ class AsciiSessionMsgFactory extends a_session_msg_factory_1.ASessionMsgFactory
|
|
|
16
16
|
const o = Object.assign({ Username: description.Username, Password: description.Password, HeartBtInt: description.HeartBtInt, ResetSeqNumFlag: description.ResetSeqNumFlag, EncryptMethod: repo_1.EncryptMethod.None }, description.Logon);
|
|
17
17
|
return this.mutate(o, types_1.MsgType.Logon);
|
|
18
18
|
}
|
|
19
|
-
logout(text) {
|
|
19
|
+
logout(msgType, text) {
|
|
20
20
|
const o = {
|
|
21
21
|
Text: text
|
|
22
22
|
};
|
|
23
|
-
return this.mutate(o, types_1.MsgType.Logout);
|
|
23
|
+
return this.mutate(o, msgType !== null && msgType !== void 0 ? msgType : types_1.MsgType.Logout);
|
|
24
24
|
}
|
|
25
25
|
header(msgType, seqNum, time, overrideData) {
|
|
26
26
|
var _a;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ascii-session-msg-factory.js","sourceRoot":"","sources":["../../../src/transport/ascii/ascii-session-msg-factory.ts"],"names":[],"mappings":";;;AAEA,uCAAqC;AACrC,4EAAoF;AAEpF,kDAKgC;AAEhC,MAAa,sBAAuB,SAAQ,0CAAkB;IAC5D,YAAsB,WAAgC,EAAE,UAAgC,IAAI;;QAC1F,KAAK,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;QADP,gBAAW,GAAX,WAAW,CAAqB;QAEpD,IAAI,CAAC,OAAO,GAAG,CAAA,MAAA,WAAW,CAAC,WAAW,0CAAE,QAAQ,MAAK,OAAO,CAAA;IAC9D,CAAC;IAEM,KAAK;QACV,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAA;QACpC,MAAM,CAAC,GAAW,gBAChB,QAAQ,EAAE,WAAW,CAAC,QAAQ,EAC9B,QAAQ,EAAE,WAAW,CAAC,QAAQ,EAC9B,UAAU,EAAE,WAAW,CAAC,UAAU,EAClC,eAAe,EAAE,WAAW,CAAC,eAAe,EAC5C,aAAa,EAAE,oBAAa,CAAC,IAAI,IAK9B,WAAW,CAAC,KAAK,CACX,CAAA;QACX,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,eAAO,CAAC,KAAK,CAAC,CAAA;IACtC,CAAC;
|
|
1
|
+
{"version":3,"file":"ascii-session-msg-factory.js","sourceRoot":"","sources":["../../../src/transport/ascii/ascii-session-msg-factory.ts"],"names":[],"mappings":";;;AAEA,uCAAqC;AACrC,4EAAoF;AAEpF,kDAKgC;AAEhC,MAAa,sBAAuB,SAAQ,0CAAkB;IAC5D,YAAsB,WAAgC,EAAE,UAAgC,IAAI;;QAC1F,KAAK,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;QADP,gBAAW,GAAX,WAAW,CAAqB;QAEpD,IAAI,CAAC,OAAO,GAAG,CAAA,MAAA,WAAW,CAAC,WAAW,0CAAE,QAAQ,MAAK,OAAO,CAAA;IAC9D,CAAC;IAEM,KAAK;QACV,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAA;QACpC,MAAM,CAAC,GAAW,gBAChB,QAAQ,EAAE,WAAW,CAAC,QAAQ,EAC9B,QAAQ,EAAE,WAAW,CAAC,QAAQ,EAC9B,UAAU,EAAE,WAAW,CAAC,UAAU,EAClC,eAAe,EAAE,WAAW,CAAC,eAAe,EAC5C,aAAa,EAAE,oBAAa,CAAC,IAAI,IAK9B,WAAW,CAAC,KAAK,CACX,CAAA;QACX,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,eAAO,CAAC,KAAK,CAAC,CAAA;IACtC,CAAC;IAQM,MAAM,CAAE,OAAe,EAAE,IAAY;QAC1C,MAAM,CAAC,GAAY;YACjB,IAAI,EAAE,IAAI;SACA,CAAA;QACZ,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,eAAO,CAAC,MAAM,CAAC,CAAA;IAClD,CAAC;IAEM,MAAM,CAAE,OAAe,EAAE,MAAc,EAAE,IAAU,EAAE,YAAuC;;QACjG,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAA;QACpC,MAAM,UAAU,GAAW,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAA,WAAW,CAAC,eAAe,mCAAI,CAAC,CAAC,CAAA;QACxE,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA;QACpD,MAAM,CAAC,mBACL,WAAW,EAAE,WAAW,CAAC,WAAW,EACpC,UAAU,EAAE,WAAW,EACvB,OAAO,EAAE,OAAO,EAChB,YAAY,EAAE,WAAW,CAAC,YAAY,EACtC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,IAAI,EACjB,YAAY,EAAE,WAAW,CAAC,YAAY,EACtC,WAAW,EAAE,WAAW,CAAC,WAAW,EACpC,WAAW,EAAE,WAAW,CAAC,WAAW,IACjC,YAAY,CAChB,CAAA;QACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAA;IACzC,CAAC;CACF;AAtDD,wDAsDC;AAED,MAAa,iBAAkB,SAAQ,sBAAsB;IAC3D,YAA6B,WAAgC,EAAS,UAAgC,IAAI;QACxG,KAAK,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;QADA,gBAAW,GAAX,WAAW,CAAqB;QAAS,YAAO,GAAP,OAAO,CAA6B;IAE1G,CAAC;CACF;AAJD,8CAIC","sourcesContent":["import { ISessionDescription } from '../session/session-description'\nimport { ILooseObject } from '../../collections/collection'\nimport { MsgType } from '../../types'\nimport { ASessionMsgFactory, ObjectMutator } from '../session/a-session-msg-factory'\n\nimport {\n EncryptMethod,\n ILogon,\n ILogout,\n IStandardHeader\n} from '../../types/FIX4.4/repo'\n\nexport class AsciiSessionMsgFactory extends ASessionMsgFactory {\n constructor (readonly description: ISessionDescription, mutator: ObjectMutator | null = null) {\n super(description, mutator)\n this.isAscii = description.application?.protocol === 'ascii'\n }\n\n public logon (): ILooseObject {\n const description = this.description\n const o: ILogon = {\n Username: description.Username,\n Password: description.Password,\n HeartBtInt: description.HeartBtInt,\n ResetSeqNumFlag: description.ResetSeqNumFlag,\n EncryptMethod: EncryptMethod.None,\n // a counterparty demanding a tag the standard Logon does not carry needs no\n // code at all - name it under \"Logon\" in the session description. Spread last\n // so an application can also override what the engine derived, or null out a\n // field it does not want sent. See issue #93.\n ...description.Logon\n } as ILogon\n return this.mutate(o, MsgType.Logon)\n }\n\n /**\n * ISessionMsgFactory declares logout (msgType, text) and FixSession calls it that\n * way. This override used to take the text alone, so it silently bound the msg\n * type to Text and every Logout jspurefix sent carried '58=5' in place of the\n * reason - the caller's message was dropped on the floor.\n */\n public logout (msgType: string, text: string): ILooseObject {\n const o: ILogout = {\n Text: text\n } as ILogout\n return this.mutate(o, msgType ?? MsgType.Logout)\n }\n\n public header (msgType: string, seqNum: number, time: Date, overrideData?: Partial<IStandardHeader>): ILooseObject {\n const description = this.description\n const bodyLength: number = Math.max(4, description.BodyLengthChars ?? 7)\n const placeHolder = Math.pow(10, bodyLength - 1) + 1\n const o: IStandardHeader = {\n BeginString: description.BeginString,\n BodyLength: placeHolder,\n MsgType: msgType,\n SenderCompID: description.SenderCompId,\n MsgSeqNum: seqNum,\n SendingTime: time,\n TargetCompID: description.TargetCompID,\n TargetSubID: description.TargetSubID,\n SenderSubID: description.SenderSubID,\n ...overrideData\n }\n return this.mutate(o, 'StandardHeader')\n }\n}\n\nexport class SessionMsgFactory extends AsciiSessionMsgFactory {\n constructor (public readonly description: ISessionDescription, public mutator: ObjectMutator | null = null) {\n super(description, mutator)\n }\n}\n"]}
|
|
@@ -18,6 +18,7 @@ export declare abstract class AsciiSession extends FixSession {
|
|
|
18
18
|
protected readonly wildcardTarget: boolean;
|
|
19
19
|
private identityBound;
|
|
20
20
|
private bindInProgress;
|
|
21
|
+
private logonVerdictPending;
|
|
21
22
|
private readonly pendingRx;
|
|
22
23
|
private readonly clock;
|
|
23
24
|
protected constructor(config: IJsFixConfig);
|
|
@@ -42,11 +43,16 @@ export declare abstract class AsciiSession extends FixSession {
|
|
|
42
43
|
protected onSessionMsg(msgType: string, view: MsgView): void;
|
|
43
44
|
protected onMsg(msgType: string, view: MsgView): void;
|
|
44
45
|
private onMsgBeforeIdentity;
|
|
46
|
+
private rxGated;
|
|
47
|
+
private resumeRx;
|
|
45
48
|
private drainPendingRx;
|
|
46
49
|
private bindIdentity;
|
|
47
50
|
private dispatchMsg;
|
|
48
51
|
private startTimer;
|
|
49
52
|
private peerLogon;
|
|
53
|
+
private awaitLogonVerdict;
|
|
54
|
+
private completePeerLogon;
|
|
55
|
+
private rejectLogon;
|
|
50
56
|
private sendTestRequest;
|
|
51
57
|
private sendHeartbeat;
|
|
52
58
|
private tick;
|
|
@@ -29,6 +29,7 @@ class AsciiSession extends fix_session_1.FixSession {
|
|
|
29
29
|
this.store = null;
|
|
30
30
|
this.registered = false;
|
|
31
31
|
this.bindInProgress = false;
|
|
32
|
+
this.logonVerdictPending = false;
|
|
32
33
|
this.pendingRx = [];
|
|
33
34
|
this.clock = new fix_clock_1.DefaultFixClock();
|
|
34
35
|
this.requestLogoutType = this.respondLogoutType = types_1.MsgType.Logout;
|
|
@@ -419,6 +420,11 @@ class AsciiSession extends fix_session_1.FixSession {
|
|
|
419
420
|
this.onMsgBeforeIdentity(msgType, view);
|
|
420
421
|
return;
|
|
421
422
|
}
|
|
423
|
+
if (this.logonVerdictPending) {
|
|
424
|
+
this.sessionLogger.info(`queueing '${msgType}' received while awaiting logon verdict`);
|
|
425
|
+
this.pendingRx.push({ msgType, view: view.clone() });
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
422
428
|
this.dispatchMsg(msgType, view);
|
|
423
429
|
}
|
|
424
430
|
onMsgBeforeIdentity(msgType, view) {
|
|
@@ -441,20 +447,34 @@ class AsciiSession extends fix_session_1.FixSession {
|
|
|
441
447
|
const logon = view.clone();
|
|
442
448
|
(_b = (_a = this.transport) === null || _a === void 0 ? void 0 : _a.duplex.readable) === null || _b === void 0 ? void 0 : _b.pause();
|
|
443
449
|
this.bindIdentity(peerCompId).then(() => {
|
|
444
|
-
var _a, _b;
|
|
445
450
|
this.bindInProgress = false;
|
|
446
451
|
this.identityBound = true;
|
|
447
452
|
this.dispatchMsg(types_1.MsgType.Logon, logon);
|
|
448
|
-
this.
|
|
449
|
-
(_b = (_a = this.transport) === null || _a === void 0 ? void 0 : _a.duplex.readable) === null || _b === void 0 ? void 0 : _b.resume();
|
|
453
|
+
this.resumeRx();
|
|
450
454
|
}).catch((e) => {
|
|
451
455
|
this.bindInProgress = false;
|
|
452
456
|
this.pendingRx.length = 0;
|
|
453
457
|
this.terminate(new Error(`failed to bind session identity for peer '${peerCompId}': ${e.message}`));
|
|
454
458
|
});
|
|
455
459
|
}
|
|
460
|
+
rxGated() {
|
|
461
|
+
return this.bindInProgress || this.logonVerdictPending;
|
|
462
|
+
}
|
|
463
|
+
resumeRx() {
|
|
464
|
+
var _a, _b;
|
|
465
|
+
if (this.rxGated())
|
|
466
|
+
return;
|
|
467
|
+
this.drainPendingRx();
|
|
468
|
+
if (this.rxGated())
|
|
469
|
+
return;
|
|
470
|
+
(_b = (_a = this.transport) === null || _a === void 0 ? void 0 : _a.duplex.readable) === null || _b === void 0 ? void 0 : _b.resume();
|
|
471
|
+
}
|
|
456
472
|
drainPendingRx() {
|
|
457
473
|
while (this.pendingRx.length > 0) {
|
|
474
|
+
if (this.rxGated()) {
|
|
475
|
+
this.sessionLogger.info('suspending replay - logon handshake gated again');
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
458
478
|
const queued = this.pendingRx.shift();
|
|
459
479
|
if (!queued)
|
|
460
480
|
break;
|
|
@@ -528,7 +548,7 @@ class AsciiSession extends fix_session_1.FixSession {
|
|
|
528
548
|
}, interval);
|
|
529
549
|
}
|
|
530
550
|
peerLogon(view) {
|
|
531
|
-
var _a, _b
|
|
551
|
+
var _a, _b;
|
|
532
552
|
const logger = this.sessionLogger;
|
|
533
553
|
const [heartBtInt, peerCompId, userName, password] = view.getTypedTags([types_1.MsgTag.HeartBtInt, types_1.MsgTag.SenderCompID, types_1.MsgTag.Username, types_1.MsgTag.Password]);
|
|
534
554
|
const resetSeqNumFlag = view.getTyped(types_1.MsgTag.ResetSeqNumFlag);
|
|
@@ -557,18 +577,57 @@ class AsciiSession extends fix_session_1.FixSession {
|
|
|
557
577
|
const state = this.sessionState;
|
|
558
578
|
state.peerHeartBeatSecs = view.getTyped(types_1.MsgTag.HeartBtInt);
|
|
559
579
|
state.peerCompId = view.getTyped(types_1.MsgTag.SenderCompID);
|
|
560
|
-
const
|
|
561
|
-
|
|
580
|
+
const verdict = this.onLogon(view, userName, password);
|
|
581
|
+
if (typeof verdict === 'boolean') {
|
|
582
|
+
this.completePeerLogon(view, verdict, resetSeqNumFlag);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
this.awaitLogonVerdict(view, verdict, resetSeqNumFlag);
|
|
586
|
+
}
|
|
587
|
+
awaitLogonVerdict(view, verdict, peerReset) {
|
|
588
|
+
var _a, _b;
|
|
589
|
+
const logger = this.sessionLogger;
|
|
590
|
+
logger.info('onLogon returned a promise - awaiting logon verdict');
|
|
591
|
+
this.logonVerdictPending = true;
|
|
592
|
+
const logon = view.clone();
|
|
593
|
+
(_b = (_a = this.transport) === null || _a === void 0 ? void 0 : _a.duplex.readable) === null || _b === void 0 ? void 0 : _b.pause();
|
|
594
|
+
verdict.then((accepted) => {
|
|
595
|
+
this.logonVerdictPending = false;
|
|
596
|
+
if (this.sessionState.state === tcp_1.SessionState.Stopped) {
|
|
597
|
+
logger.info('session stopped while awaiting logon verdict - discarding');
|
|
598
|
+
this.pendingRx.length = 0;
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
this.completePeerLogon(logon, accepted, peerReset);
|
|
602
|
+
if (!accepted) {
|
|
603
|
+
this.pendingRx.length = 0;
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
this.resumeRx();
|
|
607
|
+
}).catch((e) => {
|
|
608
|
+
this.logonVerdictPending = false;
|
|
609
|
+
this.pendingRx.length = 0;
|
|
610
|
+
this.rejectLogon(`onLogon failed: ${e.message}`);
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
completePeerLogon(view, accepted, peerReset) {
|
|
614
|
+
var _a;
|
|
615
|
+
const logger = this.sessionLogger;
|
|
616
|
+
logger.info(`peerLogon onLogon returns ${accepted}`);
|
|
617
|
+
if (!accepted) {
|
|
618
|
+
this.rejectLogon('logon rejected by application');
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
562
621
|
if (this.acceptor) {
|
|
563
622
|
this.setState(tcp_1.SessionState.InitiationLogonResponse);
|
|
564
623
|
logger.info('acceptor responds to logon request');
|
|
565
624
|
const weReset = this.config.description.ResetSeqNumFlag;
|
|
566
|
-
if (weReset &&
|
|
625
|
+
if (weReset && peerReset !== true) {
|
|
567
626
|
logger.info('acceptor sending ResetSeqNumFlag=Y (peer sent N), resetting sequences');
|
|
568
627
|
this.coordinator.resetAsAcceptor().catch((e) => {
|
|
569
628
|
this.sessionLogger.warning(`coordinator.resetAsAcceptor store persist failed: ${e.message}`);
|
|
570
629
|
});
|
|
571
|
-
const transmitter = (
|
|
630
|
+
const transmitter = (_a = this.transport) === null || _a === void 0 ? void 0 : _a.transmitter;
|
|
572
631
|
if (transmitter) {
|
|
573
632
|
transmitter.msgSeqNum = 1;
|
|
574
633
|
}
|
|
@@ -593,6 +652,11 @@ class AsciiSession extends fix_session_1.FixSession {
|
|
|
593
652
|
logger.info('system ready, inform app');
|
|
594
653
|
this.onReady(view);
|
|
595
654
|
}
|
|
655
|
+
rejectLogon(reason) {
|
|
656
|
+
this.sessionLogger.warning(`refusing peer logon: ${reason}`);
|
|
657
|
+
this.sendLogout(reason);
|
|
658
|
+
this.terminate(new Error(reason));
|
|
659
|
+
}
|
|
596
660
|
sendTestRequest() {
|
|
597
661
|
const factory = this.config.factory;
|
|
598
662
|
this.setState(tcp_1.SessionState.AwaitingProcessingResponseToTestRequest);
|