@homaiohq/napi-quickfix 0.1.0 → 0.3.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/CMakeLists.txt +5 -1
- package/README.md +344 -18
- package/cpp/acceptor_wrap.cpp +253 -19
- package/cpp/acceptor_wrap.h +60 -1
- package/cpp/addon.cpp +10 -6
- package/cpp/application_bridge.cpp +114 -30
- package/cpp/application_bridge.h +57 -4
- package/cpp/data_dictionary_wrap.cpp +89 -3
- package/cpp/data_dictionary_wrap.h +27 -5
- package/cpp/engine_common.h +108 -0
- package/cpp/engine_workers.h +99 -18
- package/cpp/field_map_ops.h +492 -0
- package/cpp/group_wrap.cpp +221 -0
- package/cpp/group_wrap.h +76 -0
- package/cpp/initiator_wrap.cpp +262 -25
- package/cpp/initiator_wrap.h +37 -2
- package/cpp/message_wrap.cpp +171 -78
- package/cpp/message_wrap.h +47 -5
- package/cpp/session_op_gate.h +201 -0
- package/cpp/session_static.cpp +139 -12
- package/cpp/session_wrap.cpp +438 -0
- package/cpp/session_wrap.h +144 -0
- package/dist/cjs/acceptor.d.ts +0 -1
- package/dist/cjs/acceptor.js +0 -1
- package/dist/cjs/application.d.ts +0 -1
- package/dist/cjs/application.js +0 -1
- package/dist/cjs/data-dictionary.d.ts +21 -3
- package/dist/cjs/data-dictionary.js +32 -4
- package/dist/cjs/engine.d.ts +42 -4
- package/dist/cjs/engine.js +50 -5
- package/dist/cjs/enums.d.ts +48 -14
- package/dist/cjs/enums.js +20 -25
- package/dist/cjs/field-value.d.ts +12 -0
- package/dist/cjs/field-value.js +40 -0
- package/dist/cjs/generated/fields.d.ts +0 -1
- package/dist/cjs/generated/fields.js +0 -1
- package/dist/cjs/generated/values.d.ts +8548 -0
- package/dist/cjs/generated/values.js +8580 -0
- package/dist/cjs/group.d.ts +126 -0
- package/dist/cjs/group.js +185 -0
- package/dist/cjs/index.d.ts +28 -5
- package/dist/cjs/index.js +30 -28
- package/dist/cjs/initiator.d.ts +0 -1
- package/dist/cjs/initiator.js +0 -1
- package/dist/cjs/message.d.ts +129 -5
- package/dist/cjs/message.js +146 -9
- package/dist/cjs/native.d.ts +179 -17
- package/dist/cjs/native.js +0 -1
- package/dist/cjs/session-id.d.ts +12 -1
- package/dist/cjs/session-id.js +21 -1
- package/dist/cjs/session-settings.d.ts +0 -1
- package/dist/cjs/session-settings.js +0 -1
- package/dist/cjs/session.d.ts +213 -0
- package/dist/cjs/session.js +337 -0
- package/dist/cjs/tls-tunnel.d.ts +143 -0
- package/dist/cjs/tls-tunnel.js +261 -0
- package/dist/esm/acceptor.d.ts +0 -1
- package/dist/esm/acceptor.js +0 -1
- package/dist/esm/application.d.ts +0 -1
- package/dist/esm/application.js +0 -1
- package/dist/esm/data-dictionary.d.ts +21 -3
- package/dist/esm/data-dictionary.js +32 -4
- package/dist/esm/engine.d.ts +42 -4
- package/dist/esm/engine.js +51 -6
- package/dist/esm/enums.d.ts +48 -14
- package/dist/esm/enums.js +14 -24
- package/dist/esm/field-value.d.ts +12 -0
- package/dist/esm/field-value.js +37 -0
- package/dist/esm/generated/fields.d.ts +0 -1
- package/dist/esm/generated/fields.js +0 -1
- package/dist/esm/generated/values.d.ts +8548 -0
- package/dist/esm/generated/values.js +8564 -0
- package/dist/esm/group.d.ts +126 -0
- package/dist/esm/group.js +181 -0
- package/dist/esm/index.d.ts +28 -5
- package/dist/esm/index.js +18 -26
- package/dist/esm/initiator.d.ts +0 -1
- package/dist/esm/initiator.js +0 -1
- package/dist/esm/message.d.ts +129 -5
- package/dist/esm/message.js +146 -9
- package/dist/esm/native.d.ts +179 -17
- package/dist/esm/native.js +0 -1
- package/dist/esm/session-id.d.ts +12 -1
- package/dist/esm/session-id.js +20 -1
- package/dist/esm/session-settings.d.ts +0 -1
- package/dist/esm/session-settings.js +0 -1
- package/dist/esm/session.d.ts +213 -0
- package/dist/esm/session.js +329 -0
- package/dist/esm/tls-tunnel.d.ts +143 -0
- package/dist/esm/tls-tunnel.js +256 -0
- package/package.json +13 -1
- package/prebuilds/darwin-arm64/node.napi.node +0 -0
- package/prebuilds/darwin-x64/node.napi.node +0 -0
- package/prebuilds/linux-x64/node.napi.glibc.node +0 -0
- package/prebuilds/linux-x64/node.napi.musl.node +0 -0
- package/prebuilds/win32-x64/node.napi.node +0 -0
- package/cpp/enums.cpp +0 -66
- package/dist/cjs/acceptor.d.ts.map +0 -1
- package/dist/cjs/acceptor.js.map +0 -1
- package/dist/cjs/application.d.ts.map +0 -1
- package/dist/cjs/application.js.map +0 -1
- package/dist/cjs/data-dictionary.d.ts.map +0 -1
- package/dist/cjs/data-dictionary.js.map +0 -1
- package/dist/cjs/engine.d.ts.map +0 -1
- package/dist/cjs/engine.js.map +0 -1
- package/dist/cjs/enums.d.ts.map +0 -1
- package/dist/cjs/enums.js.map +0 -1
- package/dist/cjs/generated/fields.d.ts.map +0 -1
- package/dist/cjs/generated/fields.js.map +0 -1
- package/dist/cjs/index.d.ts.map +0 -1
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/initiator.d.ts.map +0 -1
- package/dist/cjs/initiator.js.map +0 -1
- package/dist/cjs/message.d.ts.map +0 -1
- package/dist/cjs/message.js.map +0 -1
- package/dist/cjs/native.d.ts.map +0 -1
- package/dist/cjs/native.js.map +0 -1
- package/dist/cjs/session-id.d.ts.map +0 -1
- package/dist/cjs/session-id.js.map +0 -1
- package/dist/cjs/session-settings.d.ts.map +0 -1
- package/dist/cjs/session-settings.js.map +0 -1
- package/dist/esm/acceptor.d.ts.map +0 -1
- package/dist/esm/acceptor.js.map +0 -1
- package/dist/esm/application.d.ts.map +0 -1
- package/dist/esm/application.js.map +0 -1
- package/dist/esm/data-dictionary.d.ts.map +0 -1
- package/dist/esm/data-dictionary.js.map +0 -1
- package/dist/esm/engine.d.ts.map +0 -1
- package/dist/esm/engine.js.map +0 -1
- package/dist/esm/enums.d.ts.map +0 -1
- package/dist/esm/enums.js.map +0 -1
- package/dist/esm/generated/fields.d.ts.map +0 -1
- package/dist/esm/generated/fields.js.map +0 -1
- package/dist/esm/index.d.ts.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/initiator.d.ts.map +0 -1
- package/dist/esm/initiator.js.map +0 -1
- package/dist/esm/message.d.ts.map +0 -1
- package/dist/esm/message.js.map +0 -1
- package/dist/esm/native.d.ts.map +0 -1
- package/dist/esm/native.js.map +0 -1
- package/dist/esm/session-id.d.ts.map +0 -1
- package/dist/esm/session-id.js.map +0 -1
- package/dist/esm/session-settings.d.ts.map +0 -1
- package/dist/esm/session-settings.js.map +0 -1
package/cpp/session_static.cpp
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
#include <napi.h>
|
|
2
2
|
|
|
3
|
+
#include <memory>
|
|
4
|
+
#include <set>
|
|
5
|
+
#include <string>
|
|
3
6
|
#include <utility>
|
|
4
7
|
|
|
5
8
|
#include "errors.h"
|
|
9
|
+
#include "field_map_ops.h"
|
|
6
10
|
#include "message_wrap.h"
|
|
11
|
+
#include "quickfix/Exceptions.h"
|
|
7
12
|
#include "quickfix/Message.h"
|
|
8
13
|
#include "quickfix/Session.h"
|
|
9
14
|
#include "quickfix/SessionID.h"
|
|
10
15
|
#include "session_id_wrap.h"
|
|
16
|
+
#include "session_op_gate.h"
|
|
17
|
+
#include "session_wrap.h"
|
|
11
18
|
|
|
12
19
|
namespace napi_quickfix {
|
|
13
20
|
|
|
@@ -19,22 +26,46 @@ namespace {
|
|
|
19
26
|
// deadlock (main blocked in sendToTarget -> toApp -> BlockingCall -> waits for
|
|
20
27
|
// main to run CallJs, which it can't).
|
|
21
28
|
//
|
|
22
|
-
// The FIX::Message
|
|
23
|
-
//
|
|
24
|
-
//
|
|
29
|
+
// The FIX::Message (a fieldmap::CloneMessage, nested group entries kept
|
|
30
|
+
// typed) and FIX::SessionID are COPIED on the JS thread before Queue()ing, so
|
|
31
|
+
// Execute() never touches any JS object. toApp may mutate the worker's private
|
|
32
|
+
// copy during send — that's fine, it's QuickFIX's business.
|
|
33
|
+
//
|
|
34
|
+
// Two upstream overloads are supported: by explicit SessionID, or by qualifier
|
|
35
|
+
// (QuickFIX then resolves the session from the message's own header fields:
|
|
36
|
+
// BeginString / SenderCompID / TargetCompID).
|
|
37
|
+
//
|
|
38
|
+
// Session::sendToTarget looks the session up in the process-wide registry and
|
|
39
|
+
// uses the raw pointer, so Execute() runs under the owning engine's
|
|
40
|
+
// SessionOpGate::OpScope to keep that engine alive until the send has returned.
|
|
41
|
+
// That gate is resolved by SendToTarget() on the JS thread, before the worker
|
|
42
|
+
// is queued (for the qualifier form after deriving the SessionID from the
|
|
43
|
+
// message header, exactly as the upstream overload does): the send is thereby
|
|
44
|
+
// bound to the engine that owned the session when it was called, and can
|
|
45
|
+
// never run through an engine registered for the same id afterwards -- not
|
|
46
|
+
// even if it sat in the libuv queue across the owner's stop() and that
|
|
47
|
+
// engine's construction. Execute() only re-checks that the gate is still the
|
|
48
|
+
// owner once the scope is held (see session_op_gate.h).
|
|
25
49
|
class SendToTargetWorker : public Napi::AsyncWorker {
|
|
26
50
|
public:
|
|
27
|
-
SendToTargetWorker(Napi::Env env, FIX::Message message, FIX::SessionID id
|
|
51
|
+
SendToTargetWorker(Napi::Env env, FIX::Message message, FIX::SessionID id,
|
|
52
|
+
std::shared_ptr<SessionOpGate> gate)
|
|
28
53
|
: Napi::AsyncWorker(env),
|
|
29
54
|
deferred_(Napi::Promise::Deferred::New(env)),
|
|
30
55
|
message_(std::move(message)),
|
|
31
|
-
id_(std::move(id))
|
|
56
|
+
id_(std::move(id)),
|
|
57
|
+
gate_(std::move(gate)) {}
|
|
32
58
|
|
|
33
59
|
Napi::Promise Promise() { return deferred_.Promise(); }
|
|
34
60
|
|
|
35
61
|
// Worker thread. NO Napi/JS access.
|
|
36
62
|
void Execute() override {
|
|
37
63
|
try {
|
|
64
|
+
std::unique_ptr<SessionOpGate::OpScope> inflight =
|
|
65
|
+
SessionOpGate::Acquire(gate_, id_);
|
|
66
|
+
if (!inflight) {
|
|
67
|
+
throw FIX::SessionNotFound(id_.toString());
|
|
68
|
+
}
|
|
38
69
|
result_ = FIX::Session::sendToTarget(message_, id_);
|
|
39
70
|
} catch (const std::exception& e) {
|
|
40
71
|
err_.Capture(e);
|
|
@@ -64,30 +95,126 @@ class SendToTargetWorker : public Napi::AsyncWorker {
|
|
|
64
95
|
Napi::Promise::Deferred deferred_;
|
|
65
96
|
FIX::Message message_;
|
|
66
97
|
FIX::SessionID id_;
|
|
98
|
+
std::shared_ptr<SessionOpGate> gate_;
|
|
67
99
|
bool result_ = false;
|
|
68
100
|
CapturedError err_;
|
|
69
101
|
};
|
|
70
102
|
|
|
71
|
-
} // namespace
|
|
72
|
-
|
|
73
103
|
// sendToTarget(message: Message, sessionID: SessionID): Promise<boolean>
|
|
74
|
-
|
|
104
|
+
// sendToTarget(message: Message, qualifier?: string): Promise<boolean>
|
|
105
|
+
Napi::Value SendToTarget(const Napi::CallbackInfo& info) {
|
|
75
106
|
Napi::Env env = info.Env();
|
|
76
107
|
MessageWrap* msg = MessageWrap::UnwrapArg(env, info[0], "message");
|
|
77
|
-
SessionIDWrap* id = SessionIDWrap::UnwrapArg(env, info[1], "sessionID");
|
|
78
108
|
|
|
79
|
-
// Copy the message and session id by value on the JS thread so the worker
|
|
109
|
+
// Copy the message (and session id) by value on the JS thread so the worker
|
|
80
110
|
// never touches JS-owned objects.
|
|
81
|
-
|
|
82
|
-
|
|
111
|
+
FIX::Message message = fieldmap::CloneMessage(msg->Message());
|
|
112
|
+
const bool byQualifier = info.Length() < 2 || info[1].IsUndefined() ||
|
|
113
|
+
info[1].IsString();
|
|
114
|
+
FIX::SessionID id;
|
|
115
|
+
if (!byQualifier) {
|
|
116
|
+
id = SessionIDWrap::UnwrapArg(env, info[1], "sessionID")->SessionID();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Resolve the owning engine HERE, on the JS thread (see the worker's
|
|
120
|
+
// comment): a session that no engine owns is refused right away, with the
|
|
121
|
+
// same error the worker reports when the engine goes away under it.
|
|
122
|
+
std::shared_ptr<SessionOpGate> gate;
|
|
123
|
+
try {
|
|
124
|
+
if (byQualifier) {
|
|
125
|
+
// Mirrors FIX::Session::sendToTarget(Message&, const std::string&):
|
|
126
|
+
// a header missing tag 8/49/56 is a SessionNotFound.
|
|
127
|
+
const std::string qualifier =
|
|
128
|
+
info[1].IsString() ? info[1].As<Napi::String>().Utf8Value()
|
|
129
|
+
: std::string();
|
|
130
|
+
try {
|
|
131
|
+
id = message.getSessionID(qualifier);
|
|
132
|
+
} catch (const FIX::FieldNotFound&) {
|
|
133
|
+
throw FIX::SessionNotFound();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
gate = SessionOpGate::Lookup(id);
|
|
137
|
+
if (!gate) {
|
|
138
|
+
throw FIX::SessionNotFound(id.toString());
|
|
139
|
+
}
|
|
140
|
+
} catch (const FIX::Exception& e) {
|
|
141
|
+
CapturedError err;
|
|
142
|
+
err.Capture(e);
|
|
143
|
+
auto deferred = Napi::Promise::Deferred::New(env);
|
|
144
|
+
deferred.Reject(err.ToError(env).Value());
|
|
145
|
+
return deferred.Promise();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
auto* worker = new SendToTargetWorker(env, std::move(message), std::move(id),
|
|
149
|
+
std::move(gate));
|
|
83
150
|
Napi::Promise promise = worker->Promise();
|
|
84
151
|
worker->Queue();
|
|
85
152
|
return promise;
|
|
86
153
|
}
|
|
87
154
|
|
|
155
|
+
// lookupSession(sessionID: SessionID): Session | undefined
|
|
156
|
+
Napi::Value LookupSession(const Napi::CallbackInfo& info) {
|
|
157
|
+
Napi::Env env = info.Env();
|
|
158
|
+
SessionIDWrap* id = SessionIDWrap::UnwrapArg(env, info[0], "sessionID");
|
|
159
|
+
NQ_TRY(env) {
|
|
160
|
+
// The gate of the engine owning the id; registered by every wrap
|
|
161
|
+
// constructor right after it created its sessions, unregistered right
|
|
162
|
+
// before they are destroyed, so (both on the JS thread) a session that
|
|
163
|
+
// exists has exactly one. No gate means no session.
|
|
164
|
+
std::shared_ptr<SessionOpGate> gate = SessionOpGate::Lookup(id->SessionID());
|
|
165
|
+
if (!gate || !FIX::Session::doesSessionExist(id->SessionID())) {
|
|
166
|
+
return env.Undefined();
|
|
167
|
+
}
|
|
168
|
+
return SessionWrap::NewInstance(env, id->SessionID(), std::move(gate));
|
|
169
|
+
} NQ_CATCH(env)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// doesSessionExist(sessionID: SessionID): boolean
|
|
173
|
+
Napi::Value DoesSessionExist(const Napi::CallbackInfo& info) {
|
|
174
|
+
Napi::Env env = info.Env();
|
|
175
|
+
SessionIDWrap* id = SessionIDWrap::UnwrapArg(env, info[0], "sessionID");
|
|
176
|
+
NQ_TRY(env) {
|
|
177
|
+
return Napi::Boolean::New(env,
|
|
178
|
+
FIX::Session::doesSessionExist(id->SessionID()));
|
|
179
|
+
} NQ_CATCH(env)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// getSessions(): SessionID[] -- every session registered in this process.
|
|
183
|
+
Napi::Value GetSessions(const Napi::CallbackInfo& info) {
|
|
184
|
+
Napi::Env env = info.Env();
|
|
185
|
+
NQ_TRY(env) {
|
|
186
|
+
const std::set<FIX::SessionID> ids = FIX::Session::getSessions();
|
|
187
|
+
Napi::Array out = Napi::Array::New(env, ids.size());
|
|
188
|
+
uint32_t i = 0;
|
|
189
|
+
for (const FIX::SessionID& id : ids) {
|
|
190
|
+
out.Set(i++, SessionIDWrap::NewInstance(env, id));
|
|
191
|
+
}
|
|
192
|
+
return out;
|
|
193
|
+
} NQ_CATCH(env)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// numSessions(): number
|
|
197
|
+
Napi::Value NumSessions(const Napi::CallbackInfo& info) {
|
|
198
|
+
Napi::Env env = info.Env();
|
|
199
|
+
NQ_TRY(env) {
|
|
200
|
+
return Napi::Number::New(env,
|
|
201
|
+
static_cast<double>(FIX::Session::numSessions()));
|
|
202
|
+
} NQ_CATCH(env)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
} // namespace
|
|
206
|
+
|
|
88
207
|
void RegisterSessionStatic(Napi::Env env, Napi::Object exports) {
|
|
89
208
|
exports.Set("sendToTarget",
|
|
90
209
|
Napi::Function::New(env, SendToTarget, "sendToTarget"));
|
|
210
|
+
exports.Set("lookupSession",
|
|
211
|
+
Napi::Function::New(env, LookupSession, "lookupSession"));
|
|
212
|
+
exports.Set("doesSessionExist",
|
|
213
|
+
Napi::Function::New(env, DoesSessionExist, "doesSessionExist"));
|
|
214
|
+
exports.Set("getSessions",
|
|
215
|
+
Napi::Function::New(env, GetSessions, "getSessions"));
|
|
216
|
+
exports.Set("numSessions",
|
|
217
|
+
Napi::Function::New(env, NumSessions, "numSessions"));
|
|
91
218
|
}
|
|
92
219
|
|
|
93
220
|
} // namespace napi_quickfix
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
#include "session_wrap.h"
|
|
2
|
+
|
|
3
|
+
#include <cmath>
|
|
4
|
+
#include <ctime>
|
|
5
|
+
#include <limits>
|
|
6
|
+
#include <memory>
|
|
7
|
+
#include <string>
|
|
8
|
+
#include <utility>
|
|
9
|
+
|
|
10
|
+
#include "errors.h"
|
|
11
|
+
#include "quickfix/Exceptions.h"
|
|
12
|
+
#include "quickfix/FieldTypes.h"
|
|
13
|
+
#include "session_id_wrap.h"
|
|
14
|
+
|
|
15
|
+
namespace napi_quickfix {
|
|
16
|
+
|
|
17
|
+
Napi::FunctionReference SessionWrap::constructor_;
|
|
18
|
+
|
|
19
|
+
Napi::Object SessionWrap::Init(Napi::Env env, Napi::Object exports) {
|
|
20
|
+
Napi::Function func = DefineClass(
|
|
21
|
+
env, "Session",
|
|
22
|
+
{
|
|
23
|
+
InstanceMethod("getSessionID", &SessionWrap::GetSessionID),
|
|
24
|
+
|
|
25
|
+
InstanceMethod("isLoggedOn", &SessionWrap::IsLoggedOn),
|
|
26
|
+
InstanceMethod("isEnabled", &SessionWrap::IsEnabled),
|
|
27
|
+
InstanceMethod("sentLogon", &SessionWrap::SentLogon),
|
|
28
|
+
InstanceMethod("sentLogout", &SessionWrap::SentLogout),
|
|
29
|
+
InstanceMethod("receivedLogon", &SessionWrap::ReceivedLogon),
|
|
30
|
+
InstanceMethod("isInitiator", &SessionWrap::IsInitiator),
|
|
31
|
+
InstanceMethod("isAcceptor", &SessionWrap::IsAcceptor),
|
|
32
|
+
InstanceMethod("isSessionTime", &SessionWrap::IsSessionTime),
|
|
33
|
+
InstanceMethod("isLogonTime", &SessionWrap::IsLogonTime),
|
|
34
|
+
|
|
35
|
+
InstanceMethod("getExpectedSenderNum",
|
|
36
|
+
&SessionWrap::GetExpectedSenderNum),
|
|
37
|
+
InstanceMethod("getExpectedTargetNum",
|
|
38
|
+
&SessionWrap::GetExpectedTargetNum),
|
|
39
|
+
InstanceMethod("setNextSenderMsgSeqNum",
|
|
40
|
+
&SessionWrap::SetNextSenderMsgSeqNum),
|
|
41
|
+
InstanceMethod("setNextTargetMsgSeqNum",
|
|
42
|
+
&SessionWrap::SetNextTargetMsgSeqNum),
|
|
43
|
+
|
|
44
|
+
InstanceMethod("getResetOnLogon", &SessionWrap::GetResetOnLogon),
|
|
45
|
+
InstanceMethod("setResetOnLogon", &SessionWrap::SetResetOnLogon),
|
|
46
|
+
InstanceMethod("getResetOnLogout", &SessionWrap::GetResetOnLogout),
|
|
47
|
+
InstanceMethod("setResetOnLogout", &SessionWrap::SetResetOnLogout),
|
|
48
|
+
InstanceMethod("getResetOnDisconnect",
|
|
49
|
+
&SessionWrap::GetResetOnDisconnect),
|
|
50
|
+
InstanceMethod("setResetOnDisconnect",
|
|
51
|
+
&SessionWrap::SetResetOnDisconnect),
|
|
52
|
+
InstanceMethod("getRefreshOnLogon", &SessionWrap::GetRefreshOnLogon),
|
|
53
|
+
InstanceMethod("setRefreshOnLogon", &SessionWrap::SetRefreshOnLogon),
|
|
54
|
+
InstanceMethod("getCheckCompId", &SessionWrap::GetCheckCompId),
|
|
55
|
+
InstanceMethod("setCheckCompId", &SessionWrap::SetCheckCompId),
|
|
56
|
+
InstanceMethod("getCheckLatency", &SessionWrap::GetCheckLatency),
|
|
57
|
+
InstanceMethod("setCheckLatency", &SessionWrap::SetCheckLatency),
|
|
58
|
+
InstanceMethod("getPersistMessages",
|
|
59
|
+
&SessionWrap::GetPersistMessages),
|
|
60
|
+
InstanceMethod("setPersistMessages",
|
|
61
|
+
&SessionWrap::SetPersistMessages),
|
|
62
|
+
InstanceMethod("getSendRedundantResendRequests",
|
|
63
|
+
&SessionWrap::GetSendRedundantResendRequests),
|
|
64
|
+
InstanceMethod("setSendRedundantResendRequests",
|
|
65
|
+
&SessionWrap::SetSendRedundantResendRequests),
|
|
66
|
+
InstanceMethod("getValidateLengthAndChecksum",
|
|
67
|
+
&SessionWrap::GetValidateLengthAndChecksum),
|
|
68
|
+
InstanceMethod("setValidateLengthAndChecksum",
|
|
69
|
+
&SessionWrap::SetValidateLengthAndChecksum),
|
|
70
|
+
InstanceMethod("getSendNextExpectedMsgSeqNum",
|
|
71
|
+
&SessionWrap::GetSendNextExpectedMsgSeqNum),
|
|
72
|
+
InstanceMethod("setSendNextExpectedMsgSeqNum",
|
|
73
|
+
&SessionWrap::SetSendNextExpectedMsgSeqNum),
|
|
74
|
+
InstanceMethod("getIsNonStopSession",
|
|
75
|
+
&SessionWrap::GetIsNonStopSession),
|
|
76
|
+
InstanceMethod("setIsNonStopSession",
|
|
77
|
+
&SessionWrap::SetIsNonStopSession),
|
|
78
|
+
InstanceMethod("getLogonTimeout", &SessionWrap::GetLogonTimeout),
|
|
79
|
+
InstanceMethod("setLogonTimeout", &SessionWrap::SetLogonTimeout),
|
|
80
|
+
InstanceMethod("getLogoutTimeout", &SessionWrap::GetLogoutTimeout),
|
|
81
|
+
InstanceMethod("setLogoutTimeout", &SessionWrap::SetLogoutTimeout),
|
|
82
|
+
InstanceMethod("getMaxLatency", &SessionWrap::GetMaxLatency),
|
|
83
|
+
InstanceMethod("setMaxLatency", &SessionWrap::SetMaxLatency),
|
|
84
|
+
InstanceMethod("getTimestampPrecision",
|
|
85
|
+
&SessionWrap::GetTimestampPrecision),
|
|
86
|
+
InstanceMethod("setTimestampPrecision",
|
|
87
|
+
&SessionWrap::SetTimestampPrecision),
|
|
88
|
+
|
|
89
|
+
InstanceMethod("logon", &SessionWrap::Logon),
|
|
90
|
+
InstanceMethod("logout", &SessionWrap::Logout),
|
|
91
|
+
InstanceMethod("refresh", &SessionWrap::Refresh),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
constructor_ = Napi::Persistent(func);
|
|
95
|
+
constructor_.SuppressDestruct();
|
|
96
|
+
|
|
97
|
+
exports.Set("SessionWrap", func);
|
|
98
|
+
return exports;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
Napi::Object SessionWrap::NewInstance(Napi::Env env, const FIX::SessionID& id,
|
|
102
|
+
std::shared_ptr<SessionOpGate> gate) {
|
|
103
|
+
Napi::Object obj = constructor_.New({SessionIDWrap::NewInstance(env, id)});
|
|
104
|
+
Unwrap(obj)->gate_ = std::move(gate);
|
|
105
|
+
return obj;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
SessionWrap::SessionWrap(const Napi::CallbackInfo& info)
|
|
109
|
+
: Napi::ObjectWrap<SessionWrap>(info) {
|
|
110
|
+
Napi::Env env = info.Env();
|
|
111
|
+
if (info.Length() < 1) {
|
|
112
|
+
throw Napi::TypeError::New(
|
|
113
|
+
env,
|
|
114
|
+
"Session cannot be constructed directly; use lookupSession() or "
|
|
115
|
+
"engine.getSession()");
|
|
116
|
+
}
|
|
117
|
+
// Copy by value: the wrap must outlive any SessionID JS object.
|
|
118
|
+
id_ = SessionIDWrap::UnwrapArg(env, info[0], "sessionID")->SessionID();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Engines are created and destroyed on the JS thread only, and so is this
|
|
122
|
+
// call, so the owner check and the lookup cannot interleave with an engine
|
|
123
|
+
// stop. During a graceful stop() the gate stays registered until the engine is
|
|
124
|
+
// destroyed (Freeze() -> Unregister() -> delete, all before stop() settles), so
|
|
125
|
+
// a 'logout' listener fired by the stop still resolves the session.
|
|
126
|
+
FIX::Session& SessionWrap::Resolve() {
|
|
127
|
+
if (!gate_ || SessionOpGate::Lookup(id_) != gate_) {
|
|
128
|
+
throw FIX::SessionNotFound(id_.toString());
|
|
129
|
+
}
|
|
130
|
+
FIX::Session* session = FIX::Session::lookupSession(id_);
|
|
131
|
+
if (session == nullptr) {
|
|
132
|
+
throw FIX::SessionNotFound(id_.toString());
|
|
133
|
+
}
|
|
134
|
+
return *session;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
Napi::Value SessionWrap::GetSessionID(const Napi::CallbackInfo& info) {
|
|
138
|
+
return SessionIDWrap::NewInstance(info.Env(), id_);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// --- generic helpers ---------------------------------------------------------
|
|
142
|
+
|
|
143
|
+
template <typename Fn>
|
|
144
|
+
Napi::Value SessionWrap::BoolGet(const Napi::CallbackInfo& info, Fn fn) {
|
|
145
|
+
Napi::Env env = info.Env();
|
|
146
|
+
NQ_TRY(env) {
|
|
147
|
+
return Napi::Boolean::New(env, (Resolve().*fn)());
|
|
148
|
+
} NQ_CATCH(env)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
template <typename Fn>
|
|
152
|
+
Napi::Value SessionWrap::IntGet(const Napi::CallbackInfo& info, Fn fn) {
|
|
153
|
+
Napi::Env env = info.Env();
|
|
154
|
+
NQ_TRY(env) {
|
|
155
|
+
return Napi::Number::New(env, static_cast<double>((Resolve().*fn)()));
|
|
156
|
+
} NQ_CATCH(env)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
template <typename Fn>
|
|
160
|
+
Napi::Value SessionWrap::BoolSet(const Napi::CallbackInfo& info, Fn fn,
|
|
161
|
+
const char* name) {
|
|
162
|
+
Napi::Env env = info.Env();
|
|
163
|
+
if (info.Length() < 1 || !info[0].IsBoolean()) {
|
|
164
|
+
throw Napi::TypeError::New(env, std::string(name) + "(value: boolean)");
|
|
165
|
+
}
|
|
166
|
+
const bool value = info[0].As<Napi::Boolean>().Value();
|
|
167
|
+
NQ_TRY(env) {
|
|
168
|
+
(Resolve().*fn)(value);
|
|
169
|
+
return env.Undefined();
|
|
170
|
+
} NQ_CATCH(env)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// The FIX::Session setters take a plain `int`; a double outside its range is
|
|
174
|
+
// undefined behaviour to cast (and on x86-64 lands on INT_MIN, which would e.g.
|
|
175
|
+
// make a MaxLatency reject every inbound message). Bound it here and report
|
|
176
|
+
// a RangeError, matching SeqNumSet / SetTimestampPrecision.
|
|
177
|
+
template <typename Fn>
|
|
178
|
+
Napi::Value SessionWrap::IntSet(const Napi::CallbackInfo& info, Fn fn,
|
|
179
|
+
const char* name) {
|
|
180
|
+
Napi::Env env = info.Env();
|
|
181
|
+
if (info.Length() < 1 || !info[0].IsNumber()) {
|
|
182
|
+
throw Napi::TypeError::New(env, std::string(name) + "(value: number)");
|
|
183
|
+
}
|
|
184
|
+
const double raw = info[0].As<Napi::Number>().DoubleValue();
|
|
185
|
+
constexpr double kIntMin = static_cast<double>(std::numeric_limits<int>::min());
|
|
186
|
+
constexpr double kIntMax = static_cast<double>(std::numeric_limits<int>::max());
|
|
187
|
+
if (!std::isfinite(raw) || std::floor(raw) != raw || raw < kIntMin ||
|
|
188
|
+
raw > kIntMax) {
|
|
189
|
+
throw Napi::RangeError::New(
|
|
190
|
+
env, std::string(name) + ": value must be an integer in the int32 range");
|
|
191
|
+
}
|
|
192
|
+
NQ_TRY(env) {
|
|
193
|
+
(Resolve().*fn)(static_cast<int>(raw));
|
|
194
|
+
return env.Undefined();
|
|
195
|
+
} NQ_CATCH(env)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// isSessionTime(nowMs?) / isLogonTime(nowMs?): `nowMs` is an epoch-millisecond
|
|
199
|
+
// number (Date.getTime()); omitted => the engine's current UTC clock.
|
|
200
|
+
Napi::Value SessionWrap::TimeCheck(const Napi::CallbackInfo& info,
|
|
201
|
+
bool logonTime) {
|
|
202
|
+
Napi::Env env = info.Env();
|
|
203
|
+
bool hasNow = info.Length() >= 1 && !info[0].IsUndefined();
|
|
204
|
+
double nowMs = 0;
|
|
205
|
+
if (hasNow) {
|
|
206
|
+
if (!info[0].IsNumber()) {
|
|
207
|
+
throw Napi::TypeError::New(
|
|
208
|
+
env, logonTime ? "isLogonTime(nowMs?: number)"
|
|
209
|
+
: "isSessionTime(nowMs?: number)");
|
|
210
|
+
}
|
|
211
|
+
nowMs = info[0].As<Napi::Number>().DoubleValue();
|
|
212
|
+
// Bound to the range a JS Date can hold (+/- 100M days around the epoch)
|
|
213
|
+
// before the floating-to-integer cast below: converting a finite double
|
|
214
|
+
// that does not fit in time_t is undefined behaviour, not a saturation.
|
|
215
|
+
constexpr double kMaxDateMs = 8.64e15;
|
|
216
|
+
if (!std::isfinite(nowMs) || std::fabs(nowMs) > kMaxDateMs) {
|
|
217
|
+
throw Napi::RangeError::New(
|
|
218
|
+
env, "nowMs must be an epoch-millisecond number in the Date range");
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
NQ_TRY(env) {
|
|
222
|
+
FIX::Session& session = Resolve();
|
|
223
|
+
FIX::UtcTimeStamp now = FIX::UtcTimeStamp::now();
|
|
224
|
+
if (hasNow) {
|
|
225
|
+
const double secs = std::floor(nowMs / 1000.0);
|
|
226
|
+
const int millis = static_cast<int>(nowMs - secs * 1000.0);
|
|
227
|
+
now = FIX::UtcTimeStamp(static_cast<time_t>(secs), millis);
|
|
228
|
+
}
|
|
229
|
+
return Napi::Boolean::New(env, logonTime ? session.isLogonTime(now)
|
|
230
|
+
: session.isSessionTime(now));
|
|
231
|
+
} NQ_CATCH(env)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Sequence numbers are FIX::SEQNUM (uint64_t). JS numbers are exact up to
|
|
235
|
+
// 2^53, which is far beyond any practical MsgSeqNum; reject anything that is
|
|
236
|
+
// not a positive safe integer. MsgSeqNum (tag 34) is 1-based: QuickFIX's own
|
|
237
|
+
// SessionState::reset() and MemoryStore start at 1, and a peer answers 34=0
|
|
238
|
+
// with a Logout ("MsgSeqNum too low").
|
|
239
|
+
Napi::Value SessionWrap::SeqNumSet(const Napi::CallbackInfo& info, bool sender,
|
|
240
|
+
const char* name) {
|
|
241
|
+
Napi::Env env = info.Env();
|
|
242
|
+
if (info.Length() < 1 || !info[0].IsNumber()) {
|
|
243
|
+
throw Napi::TypeError::New(env, std::string(name) + "(seqNum: number)");
|
|
244
|
+
}
|
|
245
|
+
const double raw = info[0].As<Napi::Number>().DoubleValue();
|
|
246
|
+
constexpr double kMaxSafe = 9007199254740991.0; // Number.MAX_SAFE_INTEGER
|
|
247
|
+
if (!std::isfinite(raw) || std::floor(raw) != raw || raw < 1 ||
|
|
248
|
+
raw > kMaxSafe) {
|
|
249
|
+
throw Napi::RangeError::New(
|
|
250
|
+
env, std::string(name) + ": seqNum must be a positive safe integer");
|
|
251
|
+
}
|
|
252
|
+
const FIX::SEQNUM num = static_cast<FIX::SEQNUM>(raw);
|
|
253
|
+
NQ_TRY(env) {
|
|
254
|
+
// These touch the MessageStore (may throw IOException) but take only the
|
|
255
|
+
// SessionState mutex, which QuickFIX never holds across a callback -- that
|
|
256
|
+
// is what makes them safe to run here. It also means they are NOT
|
|
257
|
+
// serialised against the engine: Session::sendRaw holds m_mutex across
|
|
258
|
+
// fill() (reads the next sender num into the header), toApp/toAdmin and
|
|
259
|
+
// persist() (incrNextSenderMsgSeqNum from the store), and Session::next()
|
|
260
|
+
// likewise across the target-num verification, fromApp/fromAdmin and
|
|
261
|
+
// incrNextTargetMsgSeqNum. A setter landing inside that window -- from a
|
|
262
|
+
// handler, or while a sendToTarget() / heartbeat is in flight on this
|
|
263
|
+
// session -- is overwritten by the increment that follows, and the wire
|
|
264
|
+
// is left one off from the peer. The setters are meant for a quiescent
|
|
265
|
+
// session (before start(), from a 'logout' event, or when the caller
|
|
266
|
+
// knows nothing is in flight); documented on the JS API, not fixable here
|
|
267
|
+
// (m_mutex is private and must not be taken from the JS thread anyway).
|
|
268
|
+
if (sender) {
|
|
269
|
+
Resolve().setNextSenderMsgSeqNum(num);
|
|
270
|
+
} else {
|
|
271
|
+
Resolve().setNextTargetMsgSeqNum(num);
|
|
272
|
+
}
|
|
273
|
+
return env.Undefined();
|
|
274
|
+
} NQ_CATCH(env)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// --- state getters -----------------------------------------------------------
|
|
278
|
+
|
|
279
|
+
Napi::Value SessionWrap::IsLoggedOn(const Napi::CallbackInfo& info) {
|
|
280
|
+
return BoolGet(info, &FIX::Session::isLoggedOn);
|
|
281
|
+
}
|
|
282
|
+
Napi::Value SessionWrap::IsEnabled(const Napi::CallbackInfo& info) {
|
|
283
|
+
return BoolGet(info, &FIX::Session::isEnabled);
|
|
284
|
+
}
|
|
285
|
+
Napi::Value SessionWrap::SentLogon(const Napi::CallbackInfo& info) {
|
|
286
|
+
return BoolGet(info, &FIX::Session::sentLogon);
|
|
287
|
+
}
|
|
288
|
+
Napi::Value SessionWrap::SentLogout(const Napi::CallbackInfo& info) {
|
|
289
|
+
return BoolGet(info, &FIX::Session::sentLogout);
|
|
290
|
+
}
|
|
291
|
+
Napi::Value SessionWrap::ReceivedLogon(const Napi::CallbackInfo& info) {
|
|
292
|
+
return BoolGet(info, &FIX::Session::receivedLogon);
|
|
293
|
+
}
|
|
294
|
+
Napi::Value SessionWrap::IsInitiator(const Napi::CallbackInfo& info) {
|
|
295
|
+
return BoolGet(info, &FIX::Session::isInitiator);
|
|
296
|
+
}
|
|
297
|
+
Napi::Value SessionWrap::IsAcceptor(const Napi::CallbackInfo& info) {
|
|
298
|
+
return BoolGet(info, &FIX::Session::isAcceptor);
|
|
299
|
+
}
|
|
300
|
+
Napi::Value SessionWrap::IsSessionTime(const Napi::CallbackInfo& info) {
|
|
301
|
+
return TimeCheck(info, /*logonTime=*/false);
|
|
302
|
+
}
|
|
303
|
+
Napi::Value SessionWrap::IsLogonTime(const Napi::CallbackInfo& info) {
|
|
304
|
+
return TimeCheck(info, /*logonTime=*/true);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// --- sequence numbers --------------------------------------------------------
|
|
308
|
+
|
|
309
|
+
Napi::Value SessionWrap::GetExpectedSenderNum(const Napi::CallbackInfo& info) {
|
|
310
|
+
return IntGet(info, &FIX::Session::getExpectedSenderNum);
|
|
311
|
+
}
|
|
312
|
+
Napi::Value SessionWrap::GetExpectedTargetNum(const Napi::CallbackInfo& info) {
|
|
313
|
+
return IntGet(info, &FIX::Session::getExpectedTargetNum);
|
|
314
|
+
}
|
|
315
|
+
Napi::Value SessionWrap::SetNextSenderMsgSeqNum(
|
|
316
|
+
const Napi::CallbackInfo& info) {
|
|
317
|
+
return SeqNumSet(info, /*sender=*/true, "setNextSenderMsgSeqNum");
|
|
318
|
+
}
|
|
319
|
+
Napi::Value SessionWrap::SetNextTargetMsgSeqNum(
|
|
320
|
+
const Napi::CallbackInfo& info) {
|
|
321
|
+
return SeqNumSet(info, /*sender=*/false, "setNextTargetMsgSeqNum");
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// --- runtime options ---------------------------------------------------------
|
|
325
|
+
|
|
326
|
+
#define NQ_BOOL_OPTION(Name) \
|
|
327
|
+
Napi::Value SessionWrap::Get##Name(const Napi::CallbackInfo& info) { \
|
|
328
|
+
return BoolGet(info, &FIX::Session::get##Name); \
|
|
329
|
+
} \
|
|
330
|
+
Napi::Value SessionWrap::Set##Name(const Napi::CallbackInfo& info) { \
|
|
331
|
+
return BoolSet(info, &FIX::Session::set##Name, "set" #Name); \
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
#define NQ_INT_OPTION(Name) \
|
|
335
|
+
Napi::Value SessionWrap::Get##Name(const Napi::CallbackInfo& info) { \
|
|
336
|
+
return IntGet(info, &FIX::Session::get##Name); \
|
|
337
|
+
} \
|
|
338
|
+
Napi::Value SessionWrap::Set##Name(const Napi::CallbackInfo& info) { \
|
|
339
|
+
return IntSet(info, &FIX::Session::set##Name, "set" #Name); \
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
NQ_BOOL_OPTION(ResetOnLogon)
|
|
343
|
+
NQ_BOOL_OPTION(ResetOnLogout)
|
|
344
|
+
NQ_BOOL_OPTION(ResetOnDisconnect)
|
|
345
|
+
NQ_BOOL_OPTION(RefreshOnLogon)
|
|
346
|
+
NQ_BOOL_OPTION(CheckCompId)
|
|
347
|
+
NQ_BOOL_OPTION(CheckLatency)
|
|
348
|
+
NQ_BOOL_OPTION(PersistMessages)
|
|
349
|
+
NQ_BOOL_OPTION(SendRedundantResendRequests)
|
|
350
|
+
NQ_BOOL_OPTION(ValidateLengthAndChecksum)
|
|
351
|
+
NQ_BOOL_OPTION(SendNextExpectedMsgSeqNum)
|
|
352
|
+
NQ_BOOL_OPTION(IsNonStopSession)
|
|
353
|
+
NQ_INT_OPTION(LogonTimeout)
|
|
354
|
+
NQ_INT_OPTION(LogoutTimeout)
|
|
355
|
+
NQ_INT_OPTION(MaxLatency)
|
|
356
|
+
|
|
357
|
+
#undef NQ_BOOL_OPTION
|
|
358
|
+
#undef NQ_INT_OPTION
|
|
359
|
+
|
|
360
|
+
Napi::Value SessionWrap::GetTimestampPrecision(const Napi::CallbackInfo& info) {
|
|
361
|
+
return IntGet(info, &FIX::Session::getTimestampPrecision);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// FIX::Session::setTimestampPrecision silently ignores values outside 0..9;
|
|
365
|
+
// surface that as a RangeError instead of a no-op.
|
|
366
|
+
Napi::Value SessionWrap::SetTimestampPrecision(const Napi::CallbackInfo& info) {
|
|
367
|
+
Napi::Env env = info.Env();
|
|
368
|
+
if (info.Length() < 1 || !info[0].IsNumber()) {
|
|
369
|
+
throw Napi::TypeError::New(env, "setTimestampPrecision(precision: number)");
|
|
370
|
+
}
|
|
371
|
+
const double raw = info[0].As<Napi::Number>().DoubleValue();
|
|
372
|
+
if (!std::isfinite(raw) || std::floor(raw) != raw || raw < 0 || raw > 9) {
|
|
373
|
+
throw Napi::RangeError::New(
|
|
374
|
+
env, "setTimestampPrecision: precision must be an integer in 0..9");
|
|
375
|
+
}
|
|
376
|
+
NQ_TRY(env) {
|
|
377
|
+
Resolve().setTimestampPrecision(static_cast<int>(raw));
|
|
378
|
+
return env.Undefined();
|
|
379
|
+
} NQ_CATCH(env)
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// --- control ------------------------------------------------------------------
|
|
383
|
+
// FIX::Session::logon()/logout() only flip SessionState's `enabled` flag and
|
|
384
|
+
// `logoutReason` (the latter under SessionState's own mutex), and refresh()
|
|
385
|
+
// re-reads the store under that same mutex; none of them take
|
|
386
|
+
// FIX::Session::m_mutex or fire a callback, so they cannot deadlock the JS
|
|
387
|
+
// thread against a QuickFIX thread blocked in a handler. That is the property
|
|
388
|
+
// that lets them run here. What they are NOT is race-free: `enabled` is a
|
|
389
|
+
// plain bool that SessionState::enabled(bool) stores with no lock and that
|
|
390
|
+
// the engine's network thread reads in Session::next() under Session::m_mutex
|
|
391
|
+
// -- formally a data race (ThreadSanitizer reports it), the same one
|
|
392
|
+
// FIX::Initiator::stop() makes when it calls logout()/logon() on every session
|
|
393
|
+
// from the stopping thread. In practice the network thread observes the flag
|
|
394
|
+
// on its next pass; this binding does not patch QuickFIX to make it atomic.
|
|
395
|
+
// Keeping them synchronous also keeps them ORDERED: as AsyncWorkers,
|
|
396
|
+
// `logout(); logon();` could have run on two pool threads in either order.
|
|
397
|
+
//
|
|
398
|
+
// FIX::Session::disconnect() and reset() are deliberately NOT exposed. Through
|
|
399
|
+
// the session's responder they reach FIX::SocketMonitor::drop(), which mutates
|
|
400
|
+
// the monitor's socket sets with no lock while the engine's network thread
|
|
401
|
+
// iterates them in SocketMonitor::block(); QuickFIX only ever calls them from
|
|
402
|
+
// that thread itself (upstream issue #346, closed "not planned"). Any thread
|
|
403
|
+
// this binding could run them on -- JS or libuv -- races it. logout() is the
|
|
404
|
+
// supported way to bring a session down from the application.
|
|
405
|
+
|
|
406
|
+
Napi::Value SessionWrap::Logon(const Napi::CallbackInfo& info) {
|
|
407
|
+
Napi::Env env = info.Env();
|
|
408
|
+
NQ_TRY(env) {
|
|
409
|
+
Resolve().logon();
|
|
410
|
+
return env.Undefined();
|
|
411
|
+
} NQ_CATCH(env)
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
Napi::Value SessionWrap::Logout(const Napi::CallbackInfo& info) {
|
|
415
|
+
Napi::Env env = info.Env();
|
|
416
|
+
std::string reason;
|
|
417
|
+
if (info.Length() >= 1 && !info[0].IsUndefined()) {
|
|
418
|
+
if (!info[0].IsString()) {
|
|
419
|
+
throw Napi::TypeError::New(env, "logout(reason?: string)");
|
|
420
|
+
}
|
|
421
|
+
reason = info[0].As<Napi::String>().Utf8Value();
|
|
422
|
+
}
|
|
423
|
+
NQ_TRY(env) {
|
|
424
|
+
Resolve().logout(reason);
|
|
425
|
+
return env.Undefined();
|
|
426
|
+
} NQ_CATCH(env)
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// May throw IOException from the message store (mapped to QuickFixError).
|
|
430
|
+
Napi::Value SessionWrap::Refresh(const Napi::CallbackInfo& info) {
|
|
431
|
+
Napi::Env env = info.Env();
|
|
432
|
+
NQ_TRY(env) {
|
|
433
|
+
Resolve().refresh();
|
|
434
|
+
return env.Undefined();
|
|
435
|
+
} NQ_CATCH(env)
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
} // namespace napi_quickfix
|