jspurefix 5.8.5 → 5.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +4 -0
- package/BACKPORT_PLAN.md +71 -0
- package/DEMO_PORT_PLAN.md +10 -0
- package/README.md +3 -0
- package/dist/config/js-fix-config.d.ts +4 -0
- package/dist/config/js-fix-config.js.map +1 -1
- package/dist/runtime/session-launcher.js +1 -1
- package/dist/runtime/session-launcher.js.map +1 -1
- package/dist/runtime/session-scope.d.ts +3 -0
- package/dist/runtime/session-scope.js +47 -0
- package/dist/runtime/session-scope.js.map +1 -0
- package/dist/sample/http/oms/app.js +3 -3
- package/dist/sample/http/oms/app.js.map +1 -1
- package/dist/sample/tcp/qf-md/app.js +3 -3
- package/dist/sample/tcp/qf-md/app.js.map +1 -1
- package/dist/sample/tcp/skeleton/app.js +1 -1
- package/dist/sample/tcp/skeleton/app.js.map +1 -1
- package/dist/sample/tcp/trade-capture/app-launcher.js +3 -3
- package/dist/sample/tcp/trade-capture/app-launcher.js.map +1 -1
- package/dist/transport/ascii/ascii-session.d.ts +17 -3
- package/dist/transport/ascii/ascii-session.js +121 -8
- package/dist/transport/ascii/ascii-session.js.map +1 -1
- package/dist/transport/duplex/fix-duplex.d.ts +1 -0
- package/dist/transport/duplex/fix-duplex.js +3 -0
- package/dist/transport/duplex/fix-duplex.js.map +1 -1
- package/dist/transport/duplex/tcp-duplex.d.ts +1 -0
- package/dist/transport/duplex/tcp-duplex.js +5 -0
- package/dist/transport/duplex/tcp-duplex.js.map +1 -1
- package/dist/transport/factory/msg-transport.d.ts +2 -0
- package/dist/transport/factory/msg-transport.js +17 -2
- package/dist/transport/factory/msg-transport.js.map +1 -1
- package/dist/transport/session/a-session-msg-factory.d.ts +1 -0
- package/dist/transport/session/a-session-msg-factory.js +4 -0
- package/dist/transport/session/a-session-msg-factory.js.map +1 -1
- package/dist/transport/session/clone-session-description.d.ts +6 -0
- package/dist/transport/session/clone-session-description.js +11 -0
- package/dist/transport/session/clone-session-description.js.map +1 -0
- package/dist/transport/session/fix-session.d.ts +5 -1
- package/dist/transport/session/fix-session.js +93 -24
- package/dist/transport/session/fix-session.js.map +1 -1
- package/dist/transport/session/index.d.ts +2 -0
- package/dist/transport/session/index.js +2 -0
- package/dist/transport/session/index.js.map +1 -1
- package/dist/transport/session/session-msg-factory.d.ts +1 -0
- package/dist/transport/session/session-msg-factory.js.map +1 -1
- package/dist/transport/session/session-registry.d.ts +23 -0
- package/dist/transport/session/session-registry.js +59 -0
- package/dist/transport/session/session-registry.js.map +1 -0
- package/dist/transport/tcp/tcp-acceptor-listener.d.ts +3 -0
- package/dist/transport/tcp/tcp-acceptor-listener.js +26 -4
- package/dist/transport/tcp/tcp-acceptor-listener.js.map +1 -1
- package/dist/transport/tcp/tcp-acceptor.d.ts +5 -0
- package/dist/transport/tcp/tcp-acceptor.js +74 -20
- package/dist/transport/tcp/tcp-acceptor.js.map +1 -1
- package/dist/transport/tcp/tcp-initiator.js +15 -5
- package/dist/transport/tcp/tcp-initiator.js.map +1 -1
- package/dist/transport/tcp/tcp-transport-description.d.ts +1 -0
- package/dist/transport/tcp/tcp-transport-description.js.map +1 -1
- package/dist/transport/tcp/tls-diagnostics.d.ts +5 -0
- package/dist/transport/tcp/tls-diagnostics.js +64 -0
- package/dist/transport/tcp/tls-diagnostics.js.map +1 -0
- package/dist/transport/tcp/tls-options-factory.d.ts +1 -0
- package/dist/transport/tcp/tls-options-factory.js +24 -2
- package/dist/transport/tcp/tls-options-factory.js.map +1 -1
- package/docs/acceptor.md +207 -0
- package/jsfix.test_client.txt +62 -75
- package/jsfix.test_server.txt +72 -72
- package/package.json +11 -10
- package/script/pack-local.js +68 -0
- package/src/config/js-fix-config.ts +8 -0
- package/src/runtime/session-launcher.ts +5 -1
- package/src/runtime/session-scope.ts +84 -0
- package/src/sample/http/oms/app.ts +3 -3
- package/src/sample/tcp/qf-md/app.ts +3 -3
- package/src/sample/tcp/skeleton/app.ts +1 -1
- package/src/sample/tcp/trade-capture/app-launcher.ts +3 -3
- package/src/transport/ascii/ascii-session.ts +186 -11
- package/src/transport/duplex/fix-duplex.ts +9 -0
- package/src/transport/duplex/tcp-duplex.ts +10 -0
- package/src/transport/factory/msg-transport.ts +20 -0
- package/src/transport/session/a-session-msg-factory.ts +18 -0
- package/src/transport/session/clone-session-description.ts +41 -0
- package/src/transport/session/fix-session.ts +136 -25
- package/src/transport/session/index.ts +2 -0
- package/src/transport/session/session-msg-factory.ts +13 -0
- package/src/transport/session/session-registry.ts +112 -0
- package/src/transport/tcp/tcp-acceptor-listener.ts +43 -5
- package/src/transport/tcp/tcp-acceptor.ts +92 -18
- package/src/transport/tcp/tcp-initiator.ts +26 -5
- package/src/transport/tcp/tcp-transport-description.ts +7 -0
- package/src/transport/tcp/tls-diagnostics.ts +76 -0
- package/src/transport/tcp/tls-options-factory.ts +34 -2
|
@@ -4,6 +4,16 @@ exports.TlsOptionsFactory = void 0;
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
class TlsOptionsFactory {
|
|
7
|
+
static asBoolean(value) {
|
|
8
|
+
if (typeof value === 'string') {
|
|
9
|
+
const lowered = value.toLowerCase();
|
|
10
|
+
if (lowered === 'true')
|
|
11
|
+
return true;
|
|
12
|
+
if (lowered === 'false')
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
7
17
|
static read(filePath) {
|
|
8
18
|
const root = path.join(__dirname, '../../../');
|
|
9
19
|
const fullPath = path.join(root, filePath);
|
|
@@ -15,8 +25,8 @@ class TlsOptionsFactory {
|
|
|
15
25
|
let tlsOptions = null;
|
|
16
26
|
if (tls) {
|
|
17
27
|
tlsOptions = {
|
|
18
|
-
requestCert: tls.requestCert,
|
|
19
|
-
rejectUnauthorized: tls.rejectUnauthorized
|
|
28
|
+
requestCert: TlsOptionsFactory.asBoolean(tls.requestCert),
|
|
29
|
+
rejectUnauthorized: TlsOptionsFactory.asBoolean(tls.rejectUnauthorized)
|
|
20
30
|
};
|
|
21
31
|
if (tls.key) {
|
|
22
32
|
tlsOptions.key = TlsOptionsFactory.read(tls.key);
|
|
@@ -53,8 +63,20 @@ class TlsOptionsFactory {
|
|
|
53
63
|
if (tcp.tls.sessionTimeout) {
|
|
54
64
|
connectionOptions.sessionTimeout = tcp.tls.sessionTimeout;
|
|
55
65
|
}
|
|
66
|
+
if (tls.requestCert !== undefined) {
|
|
67
|
+
connectionOptions.requestCert = TlsOptionsFactory.asBoolean(tls.requestCert);
|
|
68
|
+
}
|
|
69
|
+
if (tls.rejectUnauthorized !== undefined) {
|
|
70
|
+
connectionOptions.rejectUnauthorized = TlsOptionsFactory.asBoolean(tls.rejectUnauthorized);
|
|
71
|
+
}
|
|
56
72
|
if (tls.nodeTlsConnectionOptions) {
|
|
57
73
|
connectionOptions = Object.assign(Object.assign({}, connectionOptions), tls.nodeTlsConnectionOptions);
|
|
74
|
+
if (connectionOptions.rejectUnauthorized !== undefined) {
|
|
75
|
+
connectionOptions.rejectUnauthorized = TlsOptionsFactory.asBoolean(connectionOptions.rejectUnauthorized);
|
|
76
|
+
}
|
|
77
|
+
if (connectionOptions.requestCert !== undefined) {
|
|
78
|
+
connectionOptions.requestCert = TlsOptionsFactory.asBoolean(connectionOptions.requestCert);
|
|
79
|
+
}
|
|
58
80
|
}
|
|
59
81
|
}
|
|
60
82
|
return connectionOptions;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tls-options-factory.js","sourceRoot":"","sources":["../../../src/transport/tcp/tls-options-factory.ts"],"names":[],"mappings":";;;AAGA,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;AAC5B,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;AAExB,MAAa,iBAAiB;
|
|
1
|
+
{"version":3,"file":"tls-options-factory.js","sourceRoot":"","sources":["../../../src/transport/tcp/tls-options-factory.ts"],"names":[],"mappings":"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","sourcesContent":["import { ConnectionOptions, TlsOptions } from 'tls'\nimport { ITlsOptions } from './tls-options'\nimport { ITcpTransportDescription } from './tcp-transport-description'\nconst path = require('path')\nconst fs = require('fs')\n\nexport class TlsOptionsFactory {\n /*\n session descriptions are routinely hand written json, where a boolean flag is easily\n quoted by mistake i.e. \"rejectUnauthorized\": \"false\". node treats that string as truthy\n and so does exactly the opposite of what was intended, silently. accept the two string\n spellings rather than inverting the caller's meaning - anything else is left alone so it\n shows up as an obviously wrong value rather than being guessed at.\n */\n static asBoolean (value: any): any {\n if (typeof value === 'string') {\n const lowered = value.toLowerCase()\n if (lowered === 'true') return true\n if (lowered === 'false') return false\n }\n return value\n }\n\n static read (filePath: string): string {\n const root: string = path.join(__dirname, '../../../')\n const fullPath = path.join(root, filePath)\n return fs.readFileSync(fullPath,\n {\n encoding: 'utf8', flag: 'r'\n })\n }\n\n static getTlsOptions (tls: ITlsOptions): TlsOptions | null {\n let tlsOptions: TlsOptions | null = null\n if (tls) {\n tlsOptions = {\n requestCert: TlsOptionsFactory.asBoolean(tls.requestCert),\n rejectUnauthorized: TlsOptionsFactory.asBoolean(tls.rejectUnauthorized)\n } as TlsOptions\n\n if (tls.key) {\n tlsOptions.key = TlsOptionsFactory.read(tls.key)\n tlsOptions.cert = tls?.cert ? TlsOptionsFactory.read(tls?.cert) : undefined\n }\n\n if (tls.ca && tls.ca.length > 0) {\n tlsOptions.ca = tls.ca.map(i => TlsOptionsFactory.read(i))\n }\n\n if (tls.nodeTlsServerOptions) {\n tlsOptions = {\n ...tlsOptions,\n ...tls.nodeTlsServerOptions\n }\n }\n }\n return tlsOptions\n }\n\n static getTlsConnectionOptions (tcp: ITcpTransportDescription): ConnectionOptions | null {\n let connectionOptions: ConnectionOptions | null = null\n const tls = tcp.tls\n if (tls) {\n connectionOptions = {\n port: tcp.port,\n host: tcp.host\n } as ConnectionOptions\n if (tls.key) {\n connectionOptions.key = TlsOptionsFactory.read(tcp.tls?.key ?? '')\n connectionOptions.cert = tcp.tls?.cert ? TlsOptionsFactory.read(tcp?.tls?.cert) : undefined\n }\n if (tcp.tls.ca && tcp.tls.ca.length > 0) {\n connectionOptions.ca = tcp.tls.ca.map(i => TlsOptionsFactory.read(i))\n }\n if (tcp.tls.timeout) {\n connectionOptions.timeout = tcp.tls.timeout\n }\n if (tcp.tls.sessionTimeout) {\n connectionOptions.sessionTimeout = tcp.tls.sessionTimeout\n }\n // https://github.com/TimelordUK/jspurefix/issues/151 - ITlsOptions declares these and\n // getTlsOptions honours them on the server path, so map them here too rather than making\n // nodeTlsConnectionOptions the only spelling that works for an initiator. Applied before\n // the spread below so an explicit nodeTlsConnectionOptions value still wins.\n if (tls.requestCert !== undefined) {\n connectionOptions.requestCert = TlsOptionsFactory.asBoolean(tls.requestCert)\n }\n if (tls.rejectUnauthorized !== undefined) {\n connectionOptions.rejectUnauthorized = TlsOptionsFactory.asBoolean(tls.rejectUnauthorized)\n }\n if (tls.nodeTlsConnectionOptions) {\n connectionOptions = {\n ...connectionOptions,\n ...tls.nodeTlsConnectionOptions\n }\n if (connectionOptions.rejectUnauthorized !== undefined) {\n connectionOptions.rejectUnauthorized = TlsOptionsFactory.asBoolean(connectionOptions.rejectUnauthorized)\n }\n if (connectionOptions.requestCert !== undefined) {\n connectionOptions.requestCert = TlsOptionsFactory.asBoolean(connectionOptions.requestCert)\n }\n }\n }\n return connectionOptions\n }\n}\n"]}
|
package/docs/acceptor.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Running a FIX acceptor
|
|
2
|
+
|
|
3
|
+
The acceptor in jspurefix began as a smoke-test harness for the initiator — enough
|
|
4
|
+
to answer a Logon so the client side could be exercised. It is now used to host real
|
|
5
|
+
counterparties, and this document describes the model it follows, what it guarantees,
|
|
6
|
+
and what the log tells you.
|
|
7
|
+
|
|
8
|
+
Much of this is a back-port of
|
|
9
|
+
[cspurefix](https://github.com/TimelordUK/cspurefix), where the acceptor was hardened
|
|
10
|
+
against real brokers.
|
|
11
|
+
|
|
12
|
+
## Transports and sessions are not the same thing
|
|
13
|
+
|
|
14
|
+
A **transport** is one TCP connection. A **session** is one FIX conversation,
|
|
15
|
+
identified by `SessionId` = `BeginString`-`SenderCompID`-`TargetCompID`, and owning
|
|
16
|
+
the sequence numbers and the message store behind that conversation.
|
|
17
|
+
|
|
18
|
+
The relationship is one session per transport, but a session **outlives** the
|
|
19
|
+
connection it started on: a counterparty that drops and reconnects is resuming the
|
|
20
|
+
same FIX session, from the same persisted sequence state, on a new transport.
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
listener (TcpAcceptorListener)
|
|
24
|
+
└── acceptor (TcpAcceptor) one net/tls Server, one SessionRegistry
|
|
25
|
+
├── transport 1 ──► session scope ──► FixSession ──► SessionId A ──► store A
|
|
26
|
+
├── transport 2 ──► session scope ──► FixSession ──► SessionId B ──► store B
|
|
27
|
+
└── transport 3 ──► session scope ──► FixSession ──► SessionId A ──► store A
|
|
28
|
+
(replaces the first)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Session scopes
|
|
32
|
+
|
|
33
|
+
Every accepted connection gets its own DI child container — a *session scope* —
|
|
34
|
+
carrying its own:
|
|
35
|
+
|
|
36
|
+
- parse buffer and transmit buffer (`ElasticBuffer`)
|
|
37
|
+
- `ISessionDescription`, and therefore its own `SessionId` and message store
|
|
38
|
+
- session message factory, so outbound headers carry that session's comp ids
|
|
39
|
+
- `IJsFixConfig`, which is what the application's session receives
|
|
40
|
+
|
|
41
|
+
This matters because those are otherwise process-wide singletons on the session
|
|
42
|
+
container. Two clients on one listener would parse into the same buffer and compute
|
|
43
|
+
the same `SessionId`.
|
|
44
|
+
|
|
45
|
+
Applications opt in by using the config they are **handed**, not the one they
|
|
46
|
+
captured:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
protected override makeFactory (config: IJsFixConfig): EngineFactory {
|
|
50
|
+
const isInitiator = this.isInitiator(config.description)
|
|
51
|
+
return {
|
|
52
|
+
// sessionConfig is this connection's own scope
|
|
53
|
+
makeSession: (sessionConfig: IJsFixConfig) => isInitiator
|
|
54
|
+
? new MyClient(sessionConfig)
|
|
55
|
+
: new MyServer(sessionConfig)
|
|
56
|
+
} as EngineFactory
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Writing `makeSession: () => new MyServer(config)` closes over the launcher's config
|
|
61
|
+
and opts the application out of per-session isolation. It still works for a single
|
|
62
|
+
counterparty; it does not work for several.
|
|
63
|
+
|
|
64
|
+
### Custom session message factories
|
|
65
|
+
|
|
66
|
+
If you supply your own `ISessionMsgFactory` — usually by overriding
|
|
67
|
+
`SessionContainer.makeSessionFactory` to stamp extra header fields — the scope needs
|
|
68
|
+
a copy of *your* factory bound to that session's description, not a stock one.
|
|
69
|
+
|
|
70
|
+
Extending `ASessionMsgFactory` (or `AsciiSessionMsgFactory` / `FixmlSessionMsgFactory`)
|
|
71
|
+
gets this for free: the default `cloneFor` reconstructs through the concrete
|
|
72
|
+
constructor, so a subclass taking `(description, mutator)` needs no extra work.
|
|
73
|
+
Override `cloneFor` if your constructor takes anything else:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
class BrokerMsgFactory extends AsciiSessionMsgFactory {
|
|
77
|
+
constructor (description: ISessionDescription, private readonly desk: string) {
|
|
78
|
+
super(description)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// the default clone would not know about `desk`
|
|
82
|
+
public override cloneFor (description: ISessionDescription): ISessionMsgFactory {
|
|
83
|
+
return new BrokerMsgFactory(description, this.desk)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## The session registry
|
|
89
|
+
|
|
90
|
+
FIX permits exactly one active session per `SessionId`. `SessionRegistry` enforces
|
|
91
|
+
that: a session claims its `SessionId` once its store is initialised, and any
|
|
92
|
+
*different* session already holding it is stopped first.
|
|
93
|
+
|
|
94
|
+
This is what answers the half-open socket problem. A counterparty whose network path
|
|
95
|
+
disappears without FIN or RST leaves the acceptor holding a socket it believes is
|
|
96
|
+
alive. When that counterparty reconnects and logs on, the registry stops the stale
|
|
97
|
+
session before the new one proceeds — otherwise two sessions write to one store and
|
|
98
|
+
the sequence numbers diverge beyond recovery.
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
[SessionRegistry] FOUND EXISTING SESSION for FIX4.4-accept-comp-init-comp - this is a reconnection scenario
|
|
102
|
+
[SessionRegistry] stopping old session to prevent stale transport writes
|
|
103
|
+
[...FixSession] requestStop: replaced by new connection - stopping old session with stale transport
|
|
104
|
+
[SessionRegistry] session NOT unregistered (already replaced by new connection or not found): ...
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
That last line is expected, not an error: the displaced session unregisters on its
|
|
108
|
+
way out, and the identity check stops it evicting its successor.
|
|
109
|
+
|
|
110
|
+
The listener creates a registry per listener. Supply your own via
|
|
111
|
+
`config.sessionRegistry` if you want to inspect it — `count`, `keys()` and `get()`
|
|
112
|
+
are read-only views over the live population.
|
|
113
|
+
|
|
114
|
+
An application can stop a session itself with `session.requestStop(reason)`.
|
|
115
|
+
|
|
116
|
+
## Wildcard TargetCompID
|
|
117
|
+
|
|
118
|
+
An acceptor that sets `"TargetCompID": "*"` does not need to know its counterparties
|
|
119
|
+
in advance. On the first Logon the session adopts the peer's `SenderCompID` as its
|
|
120
|
+
target and only then builds the identity-derived state: `SessionId`, message store,
|
|
121
|
+
sequence coordinator, registry claim, and its logger name.
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"SenderCompId": "accept-comp",
|
|
126
|
+
"TargetCompID": "*",
|
|
127
|
+
"store": { "type": "file", "directory": "store/acceptor" }
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Binding is deferred rather than done at construction so that each counterparty gets
|
|
132
|
+
its **own** store file — `FIX4.4-accept-comp-<peer>.seqnums` — which is what makes
|
|
133
|
+
persisted recovery work for a multi-client acceptor. (cspurefix binds in the
|
|
134
|
+
constructor and so keys a wildcard store on the literal `*`; that is fine for
|
|
135
|
+
in-memory stores only.)
|
|
136
|
+
|
|
137
|
+
Messages that arrive while the bind is in flight are queued and replayed in order.
|
|
138
|
+
`*` on an initiator is rejected at construction — an initiator has to know who it is
|
|
139
|
+
calling.
|
|
140
|
+
|
|
141
|
+
## Detecting a peer that has gone
|
|
142
|
+
|
|
143
|
+
Three independent mechanisms, because no single one is sufficient:
|
|
144
|
+
|
|
145
|
+
| Level | Mechanism | Detects |
|
|
146
|
+
|-------|-----------|---------|
|
|
147
|
+
| Transport | TCP keep-alive (`tcp.keepAliveMs`, default 30s, `0` disables) | a peer whose host or network path has vanished |
|
|
148
|
+
| Protocol | `TestRequest` on heartbeat timeout, then terminate | a peer that is connected but not responding |
|
|
149
|
+
| Session | `SessionRegistry` replacing on the next Logon | a peer that has already reconnected |
|
|
150
|
+
|
|
151
|
+
`MsgTransport.end()` performs a graceful close and then destroys the socket after
|
|
152
|
+
`MsgTransport.lingerMs` (default 5s), so an unanswered FIN cannot pin the handle
|
|
153
|
+
indefinitely. `TcpAcceptor.close()` releases live connections rather than waiting on
|
|
154
|
+
them — `net.Server.close` otherwise blocks on whoever happens to be connected.
|
|
155
|
+
|
|
156
|
+
## Reading the log
|
|
157
|
+
|
|
158
|
+
The lifecycle of one connection, in order:
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
[TcpAcceptor] net creates session 2
|
|
162
|
+
[TcpAcceptor] transport 2 keep-alive enabled, initial delay 30000ms
|
|
163
|
+
[TcpAcceptor] transport 2 from ::1:55642 given its own session scope
|
|
164
|
+
[TcpAcceptor] new transport id = 2 from ::1:55642 created total transports = 2
|
|
165
|
+
[acceptor] transport 2: creating session via DI token FixSession [accept-comp -> *]
|
|
166
|
+
[acceptor] acceptor census: transports=2 [...] sessions=1 [...]
|
|
167
|
+
[...] wildcard acceptor: awaiting peer Logon to establish session identity
|
|
168
|
+
[...] wildcard acceptor: binding session identity to peer SenderCompID 'init-comp_2'
|
|
169
|
+
[...] session identity bound: FIX4.4-accept-comp-init-comp_2 nextSender=1, expectedTarget=1
|
|
170
|
+
[SessionRegistry] no existing session found - registering new session: FIX4.4-accept-comp-init-comp_2
|
|
171
|
+
...
|
|
172
|
+
[SessionRegistry] successfully unregistered session: ..., remainingActiveSessions=1
|
|
173
|
+
[TcpAcceptor] transport 2 from ::1:55642 harvested after 18s (socket closed) total transports = 1
|
|
174
|
+
[acceptor] acceptor census: transports=1 [...] sessions=1 [...]
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The **census** line is emitted whenever the population changes and answers "how many
|
|
178
|
+
counterparties are connected, and which sessions are claimed" from the log alone.
|
|
179
|
+
Transports and sessions can legitimately differ for a moment: a connection that has
|
|
180
|
+
not logged on yet has a transport and no session; a session being replaced has both
|
|
181
|
+
counted until its socket is released.
|
|
182
|
+
|
|
183
|
+
Once a wildcard session has bound, its log prefix carries the counterparty
|
|
184
|
+
(`[my_server:init-comp_2:FixSession]`), so a multi-client server log can be read one
|
|
185
|
+
counterparty at a time.
|
|
186
|
+
|
|
187
|
+
## Server-side recovery from a persisted store
|
|
188
|
+
|
|
189
|
+
With `"store": { "type": "file", "directory": "..." }` the acceptor persists, per
|
|
190
|
+
`SessionId`, the next sender sequence and the last processed peer sequence, plus the
|
|
191
|
+
sent message bodies for replay.
|
|
192
|
+
|
|
193
|
+
On a reconnect the session reads that store before responding to the Logon, so it
|
|
194
|
+
knows what it last sent and what it last received. If the peer's Logon sequence is
|
|
195
|
+
ahead of what the store expects, the acceptor issues a `ResendRequest`; if the peer
|
|
196
|
+
asks for a resend, the stored bodies are replayed with `PossDupFlag=Y`.
|
|
197
|
+
`ResetSeqNumFlag=Y` from either side clears the store and restarts at 1.
|
|
198
|
+
|
|
199
|
+
Because the store is keyed on `SessionId`, a wildcard acceptor recovers each
|
|
200
|
+
counterparty independently.
|
|
201
|
+
|
|
202
|
+
## See also
|
|
203
|
+
|
|
204
|
+
- [`src/transport/tcp/tcp-acceptor.ts`](../src/transport/tcp/tcp-acceptor.ts) — listener, transport lifecycle
|
|
205
|
+
- [`src/transport/session/session-registry.ts`](../src/transport/session/session-registry.ts) — one session per SessionId
|
|
206
|
+
- [`src/runtime/session-scope.ts`](../src/runtime/session-scope.ts) — per-connection DI scope
|
|
207
|
+
- [jspf-demo](https://github.com/TimelordUK/jspf-demo) — `multi-client` and `stale-transport` scenarios
|
package/jsfix.test_client.txt
CHANGED
|
@@ -1,75 +1,62 @@
|
|
|
1
|
-
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=
|
|
2
|
-
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=
|
|
3
|
-
8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=
|
|
4
|
-
8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=
|
|
5
|
-
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=
|
|
6
|
-
8=FIX.4.4|9=
|
|
7
|
-
8=FIX.4.4|9=0000117|35=A|49=
|
|
8
|
-
8=FIX.4.4|9=
|
|
9
|
-
8=FIX.4.4|9=
|
|
10
|
-
8=FIX.4.4|9=
|
|
11
|
-
8=FIX.4.4|9=
|
|
12
|
-
8=FIX.4.4|9=
|
|
13
|
-
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=
|
|
14
|
-
8=FIX.4.4|9=0000117|35=A|49=
|
|
15
|
-
8=FIX.4.4|9=0000117|35=A|49=
|
|
16
|
-
8=FIX.4.4|9=
|
|
17
|
-
8=FIX.4.4|9=
|
|
18
|
-
8=FIX.4.4|9=
|
|
19
|
-
8=FIX.4.4|9=
|
|
20
|
-
8=FIX.4.4|9=0000075|35=5|49=
|
|
21
|
-
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=
|
|
22
|
-
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=
|
|
23
|
-
8=FIX.4.4|9=0000075|35=5|49=
|
|
24
|
-
8=FIX.4.4|9=
|
|
25
|
-
8=FIX.4.4|9=0000117|35=A|49=
|
|
26
|
-
8=FIX.4.4|9=
|
|
27
|
-
8=FIX.4.4|9=
|
|
28
|
-
8=FIX.4.4|9=
|
|
29
|
-
8=FIX.4.4|9=0000117|35=A|49=
|
|
30
|
-
8=FIX.4.4|9=
|
|
31
|
-
8=FIX.4.4|9=
|
|
32
|
-
8=FIX.4.4|9=
|
|
33
|
-
8=FIX.4.4|9=
|
|
34
|
-
8=FIX.4.4|9=0000117|35=A|49=
|
|
35
|
-
8=FIX.4.4|9=
|
|
36
|
-
8=FIX.4.4|9=
|
|
37
|
-
8=FIX.4.4|9=
|
|
38
|
-
8=FIX.4.4|9=
|
|
39
|
-
8=FIX.4.4|9=0000117|35=A|49=
|
|
40
|
-
8=FIX.4.4|9=
|
|
41
|
-
8=FIX.4.4|9=
|
|
42
|
-
8=FIX.4.4|9=
|
|
43
|
-
8=FIX.4.4|9=
|
|
44
|
-
8=FIX.4.4|9=
|
|
45
|
-
8=FIX.4.4|9=
|
|
46
|
-
8=FIX.4.4|9=
|
|
47
|
-
8=FIX.4.4|9=0000117|35=A|49=
|
|
48
|
-
8=FIX.4.4|9=
|
|
49
|
-
8=FIX.4.4|9=
|
|
50
|
-
8=FIX.4.4|9=
|
|
51
|
-
8=FIX.4.4|9=
|
|
52
|
-
8=FIX.4.4|9=
|
|
53
|
-
8=FIX.4.4|9=
|
|
54
|
-
8=FIX.4.4|9=
|
|
55
|
-
8=FIX.4.4|9=
|
|
56
|
-
8=FIX.4.4|9=
|
|
57
|
-
8=FIX.4.4|9=
|
|
58
|
-
8=FIX.4.4|9=
|
|
59
|
-
8=FIX.4.4|9=
|
|
60
|
-
8=FIX.4.4|9=
|
|
61
|
-
8=FIX.4.4|9=
|
|
62
|
-
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=
|
|
63
|
-
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=20260702-18:45:14.503|58=5|10=208|
|
|
64
|
-
8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260702-18:45:17.227|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=053|
|
|
65
|
-
8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260702-18:45:17.229|98=0|108=5|141=Y|553=js-server|554=pwd-server|10=106|
|
|
66
|
-
8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260702-18:45:19.235|112=Thu, 02 Jul 2026 18:45:19 GMT|10=169|
|
|
67
|
-
8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260702-18:45:21.238|112=Thu, 02 Jul 2026 18:45:21 GMT|10=159|
|
|
68
|
-
8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260702-18:45:22.241|112=Thu, 02 Jul 2026 18:45:22 GMT|10=154|
|
|
69
|
-
8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=4|57=fix|52=20260702-18:45:23.241|112=Thu, 02 Jul 2026 18:45:23 GMT|10=158|
|
|
70
|
-
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260702-18:45:25.231|58=5|10=209|
|
|
71
|
-
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=5|57=fix|52=20260702-18:45:25.230|58=5|10=210|
|
|
72
|
-
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260702-18:46:56.521|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=104|
|
|
73
|
-
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260702-18:46:56.531|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=153|
|
|
74
|
-
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260702-18:46:57.536|58=5|10=222|
|
|
75
|
-
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=20260702-18:46:57.534|58=5|10=220|
|
|
1
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:15.489|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=104|
|
|
2
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:15.494|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=148|
|
|
3
|
+
8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260801-14:51:15.495|7=1|16=1|10=236|
|
|
4
|
+
8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260801-14:51:15.500|122=20260801-14:51:15.500|123=Y|36=2|10=077|
|
|
5
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:51:17.499|58=5|10=218|
|
|
6
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:20.313|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=086|
|
|
7
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:20.315|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=136|
|
|
8
|
+
8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260801-14:51:20.315|7=1|16=0|10=222|
|
|
9
|
+
8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260801-14:51:20.318|122=20260801-14:51:20.318|123=Y|36=2|10=083|
|
|
10
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:51:22.318|58=5|10=204|
|
|
11
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:24.804|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=095|
|
|
12
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:24.806|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=145|
|
|
13
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=2|57=fix|52=20260801-14:51:24.806|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=098|
|
|
14
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:25.344|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=095|
|
|
15
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:25.345|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=144|
|
|
16
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:51:26.347|58=5|10=210|
|
|
17
|
+
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260801-14:51:26.807|58=5|10=212|
|
|
18
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:29.063|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=097|
|
|
19
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:29.065|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=147|
|
|
20
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:51:30.068|58=5|10=205|
|
|
21
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:32.552|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=094|
|
|
22
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:32.553|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=143|
|
|
23
|
+
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=0|57=fix|52=20260801-14:51:33.554|58=5|10=206|
|
|
24
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:36.035|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=094|
|
|
25
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=5|57=fix|52=20260801-14:51:36.036|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=147|
|
|
26
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:51:37.039|58=5|10=210|
|
|
27
|
+
8=FIX.4.4|9=0000110|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:39.686|98=0|141=Y|553=js-client|554=pwd-client|10=177|
|
|
28
|
+
8=FIX.4.4|9=0000125|35=3|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:39.687|45=1|372=A|373=1|58=msgType A missing required tag 108|10=011|
|
|
29
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:44.447|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=100|
|
|
30
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:44.449|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=150|
|
|
31
|
+
8=FIX.4.4|9=0000111|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:51:44.451|45=2|372=ZZ|373=11|58=msgType ZZ unknown|10=144|
|
|
32
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260801-14:51:46.451|58=5|10=209|
|
|
33
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:48.955|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=108|
|
|
34
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:48.956|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=157|
|
|
35
|
+
8=FIX.4.4|9=0000146|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:51:48.958|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 143|10=081|
|
|
36
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260801-14:51:50.959|58=5|10=217|
|
|
37
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:52.957|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=105|
|
|
38
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:57.567|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=107|
|
|
39
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:57.568|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=156|
|
|
40
|
+
8=FIX.4.4|9=0000145|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:51:57.569|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 59|10=036|
|
|
41
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260801-14:51:59.570|58=5|10=215|
|
|
42
|
+
8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:52:02.309|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=042|
|
|
43
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:52:02.310|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=132|
|
|
44
|
+
8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260801-14:52:04.315|112=Sat, 01 Aug 2026 14:52:04 GMT|10=120|
|
|
45
|
+
8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260801-14:52:06.317|112=Sat, 01 Aug 2026 14:52:06 GMT|10=127|
|
|
46
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:52:08.313|58=5|10=204|
|
|
47
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:52:10.828|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=097|
|
|
48
|
+
8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:52:10.829|98=0|108=2|141=Y|553=js-server|554=pwd-server|10=096|
|
|
49
|
+
8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:52:12.833|112=Sat, 01 Aug 2026 14:52:12 GMT|10=123|
|
|
50
|
+
8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=3|57=fix|52=20260801-14:52:14.838|112=Sat, 01 Aug 2026 14:52:14 GMT|10=133|
|
|
51
|
+
8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=4|57=fix|52=20260801-14:52:16.841|112=Sat, 01 Aug 2026 14:52:16 GMT|10=132|
|
|
52
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=5|57=fix|52=20260801-14:52:18.831|58=5|10=213|
|
|
53
|
+
8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:52:21.327|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=043|
|
|
54
|
+
8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:52:21.328|98=0|108=5|141=Y|553=js-server|554=pwd-server|10=095|
|
|
55
|
+
8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260801-14:52:23.334|112=Sat, 01 Aug 2026 14:52:23 GMT|10=123|
|
|
56
|
+
8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260801-14:52:25.336|112=Sat, 01 Aug 2026 14:52:25 GMT|10=130|
|
|
57
|
+
8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:52:26.339|112=Sat, 01 Aug 2026 14:52:26 GMT|10=134|
|
|
58
|
+
8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=4|57=fix|52=20260801-14:52:27.338|112=Sat, 01 Aug 2026 14:52:27 GMT|10=137|
|
|
59
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260801-14:52:29.330|58=5|10=207|
|
|
60
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:54:22.785|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=104|
|
|
61
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:54:22.794|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=152|
|
|
62
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:54:23.799|58=5|10=221|
|