node-opcua-client 2.76.0 → 2.77.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.
Files changed (68) hide show
  1. package/dist/alarms_and_conditions/client_alarm.d.ts +49 -49
  2. package/dist/alarms_and_conditions/client_alarm.js +109 -109
  3. package/dist/alarms_and_conditions/client_alarm_list.d.ts +25 -25
  4. package/dist/alarms_and_conditions/client_alarm_list.js +97 -97
  5. package/dist/alarms_and_conditions/client_alarm_tools.d.ts +4 -4
  6. package/dist/alarms_and_conditions/client_alarm_tools.js +156 -156
  7. package/dist/alarms_and_conditions/client_alarm_tools_acknowledge_all_conditions.d.ts +18 -18
  8. package/dist/alarms_and_conditions/client_alarm_tools_acknowledge_all_conditions.js +192 -192
  9. package/dist/alarms_and_conditions/client_alarm_tools_dump_event.d.ts +9 -9
  10. package/dist/alarms_and_conditions/client_alarm_tools_dump_event.js +73 -73
  11. package/dist/alarms_and_conditions/client_alarm_tools_extractConditionFields.d.ts +3 -3
  12. package/dist/alarms_and_conditions/client_alarm_tools_extractConditionFields.js +118 -118
  13. package/dist/alarms_and_conditions/client_tools.d.ts +7 -7
  14. package/dist/alarms_and_conditions/client_tools.js +180 -180
  15. package/dist/client_base.d.ts +277 -277
  16. package/dist/client_base.js +14 -14
  17. package/dist/client_monitored_item.d.ts +21 -21
  18. package/dist/client_monitored_item.js +11 -11
  19. package/dist/client_monitored_item_base.d.ts +39 -39
  20. package/dist/client_monitored_item_base.js +2 -2
  21. package/dist/client_monitored_item_group.d.ts +18 -18
  22. package/dist/client_monitored_item_group.js +13 -13
  23. package/dist/client_monitored_item_toolbox.d.ts +15 -15
  24. package/dist/client_monitored_item_toolbox.js +117 -117
  25. package/dist/client_session.d.ts +485 -484
  26. package/dist/client_session.js +10 -10
  27. package/dist/client_session_keepalive_manager.d.ts +34 -34
  28. package/dist/client_session_keepalive_manager.js +150 -150
  29. package/dist/client_subscription.d.ts +241 -241
  30. package/dist/client_subscription.js +14 -14
  31. package/dist/client_utils.d.ts +22 -22
  32. package/dist/client_utils.js +88 -88
  33. package/dist/common.d.ts +17 -17
  34. package/dist/common.js +5 -5
  35. package/dist/index.d.ts +55 -55
  36. package/dist/index.js +93 -93
  37. package/dist/opcua_client.d.ts +132 -132
  38. package/dist/opcua_client.js +33 -33
  39. package/dist/private/client_base_impl.d.ts +157 -157
  40. package/dist/private/client_base_impl.js +1337 -1337
  41. package/dist/private/client_monitored_item_base_impl.d.ts +3 -3
  42. package/dist/private/client_monitored_item_base_impl.js +4 -4
  43. package/dist/private/client_monitored_item_group_impl.d.ts +53 -53
  44. package/dist/private/client_monitored_item_group_impl.js +159 -159
  45. package/dist/private/client_monitored_item_impl.d.ts +93 -93
  46. package/dist/private/client_monitored_item_impl.js +305 -305
  47. package/dist/private/client_publish_engine.d.ts +62 -62
  48. package/dist/private/client_publish_engine.js +408 -408
  49. package/dist/private/client_session_impl.d.ts +621 -621
  50. package/dist/private/client_session_impl.js +1302 -1302
  51. package/dist/private/client_session_impl.js.map +1 -1
  52. package/dist/private/client_subscription_impl.d.ts +93 -93
  53. package/dist/private/client_subscription_impl.js +748 -748
  54. package/dist/private/i_private_client.d.ts +18 -18
  55. package/dist/private/i_private_client.js +2 -2
  56. package/dist/private/opcua_client_impl.d.ts +169 -169
  57. package/dist/private/opcua_client_impl.js +981 -981
  58. package/dist/reconnection.d.ts +9 -9
  59. package/dist/reconnection.js +408 -408
  60. package/dist/tools/findservers.d.ts +20 -20
  61. package/dist/tools/findservers.js +61 -61
  62. package/dist/tools/read_history_server_capabilities.d.ts +6 -6
  63. package/dist/tools/read_history_server_capabilities.js +99 -99
  64. package/dist/verify.d.ts +5 -5
  65. package/dist/verify.js +134 -134
  66. package/package.json +45 -45
  67. package/source/client_session.ts +1 -1
  68. package/source/private/client_session_impl.ts +1 -1
@@ -1,1338 +1,1338 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.ClientBaseImpl = void 0;
13
- /**
14
- * @module node-opcua-client-private
15
- */
16
- // tslint:disable:no-unused-expression
17
- const fs = require("fs");
18
- const path = require("path");
19
- const async = require("async");
20
- const chalk = require("chalk");
21
- const global_mutex_1 = require("@ster5/global-mutex");
22
- const node_opcua_assert_1 = require("node-opcua-assert");
23
- const node_opcua_common_1 = require("node-opcua-common");
24
- const node_opcua_crypto_1 = require("node-opcua-crypto");
25
- const node_opcua_date_time_1 = require("node-opcua-date-time");
26
- const node_opcua_debug_1 = require("node-opcua-debug");
27
- const node_opcua_common_2 = require("node-opcua-common");
28
- const node_opcua_hostname_1 = require("node-opcua-hostname");
29
- const node_opcua_secure_channel_1 = require("node-opcua-secure-channel");
30
- const node_opcua_service_discovery_1 = require("node-opcua-service-discovery");
31
- const node_opcua_service_endpoints_1 = require("node-opcua-service-endpoints");
32
- const node_opcua_service_secure_channel_1 = require("node-opcua-service-secure-channel");
33
- const node_opcua_status_code_1 = require("node-opcua-status-code");
34
- const node_opcua_utils_1 = require("node-opcua-utils");
35
- const node_opcua_certificate_manager_1 = require("node-opcua-certificate-manager");
36
- const node_opcua_service_session_1 = require("node-opcua-service-session");
37
- const client_base_1 = require("../client_base");
38
- const verify_1 = require("../verify");
39
- // tslint:disable-next-line:no-var-requires
40
- const once = require("once");
41
- const debugLog = (0, node_opcua_debug_1.make_debugLog)(__filename);
42
- const doDebug = (0, node_opcua_debug_1.checkDebugFlag)(__filename);
43
- const errorLog = (0, node_opcua_debug_1.make_errorLog)(__filename);
44
- const warningLog = (0, node_opcua_debug_1.make_warningLog)(__filename);
45
- const defaultConnectionStrategy = {
46
- initialDelay: 1000,
47
- maxDelay: 20 * 1000,
48
- maxRetry: -1,
49
- randomisationFactor: 0.1
50
- };
51
- function __findEndpoint(endpointUrl, params, _callback) {
52
- const masterClient = this;
53
- debugLog("findEndpoint : endpointUrl = ", endpointUrl);
54
- debugLog(" params ", params);
55
- (0, node_opcua_assert_1.assert)(!masterClient._tmpClient);
56
- const callback = (err, result) => {
57
- masterClient._tmpClient = undefined;
58
- _callback(err, result);
59
- };
60
- const securityMode = params.securityMode;
61
- const securityPolicy = params.securityPolicy;
62
- const connectionStrategy = params.connectionStrategy;
63
- const options = {
64
- applicationName: params.applicationName,
65
- applicationUri: params.applicationUri,
66
- certificateFile: params.certificateFile,
67
- clientCertificateManager: params.clientCertificateManager,
68
- clientName: "EndpointFetcher",
69
- connectionStrategy: {
70
- maxRetry: 0 /* no- retry */,
71
- maxDelay: 2000
72
- },
73
- privateKeyFile: params.privateKeyFile
74
- };
75
- const client = new TmpClient(options);
76
- masterClient._tmpClient = client;
77
- let selectedEndpoint;
78
- const allEndpoints = [];
79
- const tasks = [
80
- (innerCallback) => {
81
- // rebind backoff handler
82
- masterClient.listeners("backoff").forEach((handler) => client.on("backoff", handler));
83
- if (doDebug) {
84
- client.on("backoff", (retryCount, delay) => {
85
- debugLog("finding Endpoint => reconnecting ", " retry count", retryCount, " next attempt in ", delay / 1000, "seconds");
86
- });
87
- }
88
- client.connect(endpointUrl, (err) => {
89
- if (err) {
90
- // let's improve the error message with meaningful info
91
- err.message =
92
- "Fail to connect to server at " +
93
- endpointUrl +
94
- " to collect server's certificate (in findEndpoint) \n" +
95
- " (err =" +
96
- err.message +
97
- ")";
98
- debugLog(err.message);
99
- }
100
- return innerCallback(err);
101
- });
102
- },
103
- (innerCallback) => {
104
- client.getEndpoints((err, endpoints) => {
105
- if (err) {
106
- err.message = "error in getEndpoints \n" + err.message;
107
- return innerCallback(err);
108
- }
109
- // istanbul ignore next
110
- if (!endpoints) {
111
- return innerCallback(new Error("Internal Error"));
112
- }
113
- for (const endpoint of endpoints) {
114
- if (endpoint.securityMode === securityMode && endpoint.securityPolicyUri === securityPolicy) {
115
- if (selectedEndpoint) {
116
- errorLog("Warning more than one endpoint matching !", endpoint.endpointUrl, selectedEndpoint.endpointUrl);
117
- }
118
- selectedEndpoint = endpoint; // found it
119
- }
120
- }
121
- innerCallback();
122
- });
123
- },
124
- (innerCallback) => {
125
- client.disconnect(innerCallback);
126
- }
127
- ];
128
- async.series(tasks, (err) => {
129
- if (err) {
130
- client.disconnect(() => {
131
- callback(err);
132
- });
133
- return;
134
- }
135
- if (!selectedEndpoint) {
136
- return callback(new Error("Cannot find an Endpoint matching " +
137
- " security mode: " +
138
- securityMode.toString() +
139
- " policy: " +
140
- securityPolicy.toString()));
141
- }
142
- // istanbul ignore next
143
- if (doDebug) {
144
- debugLog(chalk.bgWhite.red("xxxxxxxxxxxxxxxxxxxxx => selected EndPoint = "), selectedEndpoint.toString());
145
- }
146
- const result = {
147
- endpoints: allEndpoints,
148
- selectedEndpoint
149
- };
150
- callback(null, result);
151
- });
152
- }
153
- /**
154
- * check if certificate is trusted or untrusted
155
- */
156
- function _verify_serverCertificate(certificateManager, serverCertificate, callback) {
157
- certificateManager.checkCertificate(serverCertificate, (err, status) => {
158
- if (err) {
159
- return callback(err);
160
- }
161
- if (status !== node_opcua_status_code_1.StatusCodes.Good) {
162
- // do it again for debug purposes
163
- if (doDebug) {
164
- certificateManager.verifyCertificate(serverCertificate, (err1, status1) => {
165
- debugLog(status1);
166
- });
167
- }
168
- warningLog("serverCertificate = ", (0, node_opcua_crypto_1.makeSHA1Thumbprint)(serverCertificate).toString("hex"));
169
- warningLog("serverCertificate = ", serverCertificate.toString("base64"));
170
- return callback(new Error("server Certificate verification failed with err " + (status === null || status === void 0 ? void 0 : status.toString())));
171
- }
172
- callback();
173
- });
174
- }
175
- const forceEndpointDiscoveryOnConnect = !!parseInt(process.env.NODEOPCUA_CLIENT_FORCE_ENDPOINT_DISCOVERY || "0", 10);
176
- debugLog("forceEndpointDiscoveryOnConnect = ", forceEndpointDiscoveryOnConnect);
177
- class ClockAdjustment {
178
- constructor() {
179
- debugLog("installPeriodicClockAdjustment ", node_opcua_date_time_1.periodicClockAdjustment.timerInstallationCount);
180
- (0, node_opcua_date_time_1.installPeriodicClockAdjustment)();
181
- }
182
- dispose() {
183
- (0, node_opcua_date_time_1.uninstallPeriodicClockAdjustment)();
184
- debugLog("uninstallPeriodicClockAdjustment ", node_opcua_date_time_1.periodicClockAdjustment.timerInstallationCount);
185
- }
186
- }
187
- /*
188
- * "disconnected" ---[connect]----------------------> "connecting"
189
- *
190
- * "connecting" ---[(connection successful)]------> "connected"
191
- *
192
- * "connecting" ---[(connection failure)]---------> "disconnected"
193
- *
194
- * "connecting" ---[disconnect]-------------------> "disconnecting" --> "disconnected"
195
- *
196
- * "connecting" ---[lost of connection]-----------> "reconnecting" ->[reconnection]
197
- *
198
- * "reconnecting" ---[reconnection successful]------> "reconnecting_newchannel_connected"
199
- *
200
- * "reconnecting_newchannel_connected" --(session failure) -->"reconnecting"
201
- *
202
- * "reconnecting" ---[reconnection failure]---------> [reconnection] ---> "reconnecting"
203
- *
204
- * "reconnecting" ---[disconnect]-------------------> "disconnecting" --> "disconnected"
205
- */
206
- let g_ClientCounter = 0;
207
- /**
208
- * @internal
209
- */
210
- // tslint:disable-next-line: max-classes-per-file
211
- class ClientBaseImpl extends node_opcua_common_1.OPCUASecureObject {
212
- constructor(options) {
213
- options = options || {};
214
- if (!options.clientCertificateManager) {
215
- options.clientCertificateManager = (0, node_opcua_certificate_manager_1.getDefaultCertificateManager)("PKI");
216
- }
217
- options.privateKeyFile = options.privateKeyFile || options.clientCertificateManager.privateKey;
218
- options.certificateFile =
219
- options.certificateFile || path.join(options.clientCertificateManager.rootDir, "own/certs/client_certificate.pem");
220
- super(options);
221
- this._inside_repairConnection = false;
222
- this._instanceNumber = g_ClientCounter++;
223
- this._internalState = "uninitialized";
224
- this.applicationName = options.applicationName || "NodeOPCUA-Client";
225
- (0, node_opcua_assert_1.assert)(!this.applicationName.match(/^locale=/), "applicationName badly converted from LocalizedText");
226
- (0, node_opcua_assert_1.assert)(!this.applicationName.match(/urn:/), "applicationName should not be a URI");
227
- // we need to delay _applicationUri initialization
228
- this._applicationUri = options.applicationUri || this._getBuiltApplicationUri();
229
- this.disconnecting = false;
230
- this.clientCertificateManager = options.clientCertificateManager;
231
- this._secureChannel = null;
232
- this._reconnectionIsCanceled = false;
233
- this.endpointUrl = "";
234
- this.clientName = options.clientName || "ClientSession";
235
- // must be ZERO with Spec 1.0.2
236
- this.protocolVersion = 0;
237
- this._sessions = [];
238
- this._serverEndpoints = [];
239
- this.defaultSecureTokenLifetime = options.defaultSecureTokenLifetime || 600000;
240
- this.tokenRenewalInterval = options.tokenRenewalInterval || 0;
241
- (0, node_opcua_assert_1.assert)(isFinite(this.tokenRenewalInterval) && this.tokenRenewalInterval >= 0);
242
- this.securityMode = (0, node_opcua_service_secure_channel_1.coerceMessageSecurityMode)(options.securityMode);
243
- this.securityPolicy = (0, node_opcua_secure_channel_1.coerceSecurityPolicy)(options.securityPolicy);
244
- this.serverCertificate = options.serverCertificate;
245
- this.keepSessionAlive = typeof options.keepSessionAlive === "boolean" ? options.keepSessionAlive : false;
246
- // statistics...
247
- this._byteRead = 0;
248
- this._byteWritten = 0;
249
- this._transactionsPerformed = 0;
250
- this._timedOutRequestCount = 0;
251
- this.connectionStrategy = (0, node_opcua_secure_channel_1.coerceConnectionStrategy)(options.connectionStrategy || defaultConnectionStrategy);
252
- /***
253
- * @property keepPendingSessionsOnDisconnect²
254
- * @type {boolean}
255
- */
256
- this.keepPendingSessionsOnDisconnect = options.keepPendingSessionsOnDisconnect || false;
257
- this.discoveryUrl = options.discoveryUrl || "";
258
- this._setInternalState("disconnected");
259
- this._transportSettings = options.transportSettings || {};
260
- }
261
- /**
262
- * total number of requests that been canceled due to timeout
263
- */
264
- get timedOutRequestCount() {
265
- return this._timedOutRequestCount + (this._secureChannel ? this._secureChannel.timedOutRequestCount : 0);
266
- }
267
- /**
268
- * total number of transactions performed by the client
269
- x */
270
- get transactionsPerformed() {
271
- return this._transactionsPerformed + (this._secureChannel ? this._secureChannel.transactionsPerformed : 0);
272
- }
273
- /**
274
- * is true when the client has already requested the server end points.
275
- */
276
- get knowsServerEndpoint() {
277
- return this._serverEndpoints && this._serverEndpoints.length > 0;
278
- }
279
- /**
280
- * true if the client is trying to reconnect to the server after a connection break.
281
- */
282
- get isReconnecting() {
283
- return !!(this._secureChannel && this._secureChannel.isConnecting) || this._internalState !== "connected";
284
- }
285
- /**
286
- * true if the connection strategy is set to automatically try to reconnect in case of failure
287
- */
288
- get reconnectOnFailure() {
289
- return this.connectionStrategy.maxRetry > 0 || this.connectionStrategy.maxRetry === -1;
290
- }
291
- /**
292
- * total number of bytes read by the client
293
- */
294
- get bytesRead() {
295
- return this._byteRead + (this._secureChannel ? this._secureChannel.bytesRead : 0);
296
- }
297
- /**
298
- * total number of bytes written by the client
299
- */
300
- get bytesWritten() {
301
- return this._byteWritten + (this._secureChannel ? this._secureChannel.bytesWritten : 0);
302
- }
303
- _setInternalState(internalState) {
304
- const previousState = this._internalState;
305
- if (doDebug) {
306
- debugLog(chalk.cyan(` Client ${this._instanceNumber} ${this.clientName} from `), chalk.yellow(previousState), "to", chalk.yellow(internalState));
307
- }
308
- this._internalState = internalState;
309
- }
310
- emit(eventName, ...others) {
311
- if (doDebug) {
312
- debugLog(chalk.cyan(` Client ${this._instanceNumber} ${this.clientName} emitting `), chalk.magentaBright(eventName));
313
- }
314
- return super.emit(eventName, ...others);
315
- }
316
- _cancel_reconnection(callback) {
317
- // _cancel_reconnection is invoked during disconnection
318
- // when we detect that a reconnection is in progress..
319
- if (!this.isReconnecting) {
320
- warningLog("internal error : _cancel_reconnection should be used when reconnecting is in progress");
321
- }
322
- debugLog("canceling reconnection");
323
- this._reconnectionIsCanceled = true;
324
- // istanbul ignore next
325
- if (!this._secureChannel) {
326
- debugLog("_cancel_reconnection: Nothing to do !");
327
- return callback(); // nothing to do
328
- }
329
- this._secureChannel.abortConnection(( /*err?: Error*/) => {
330
- this._secureChannel = null;
331
- callback();
332
- });
333
- this.once("reconnection_canceled", () => {
334
- /* empty */
335
- });
336
- }
337
- _recreate_secure_channel(callback) {
338
- debugLog("_recreate_secure_channel... while internalState is", this._internalState);
339
- if (!this.knowsServerEndpoint) {
340
- debugLog("Cannot reconnect , server endpoint is unknown");
341
- return callback(new Error("Cannot reconnect, server endpoint is unknown"));
342
- }
343
- (0, node_opcua_assert_1.assert)(this.knowsServerEndpoint);
344
- this._setInternalState("reconnecting");
345
- this.emit("start_reconnection"); // send after callback
346
- const infiniteConnectionRetry = {
347
- initialDelay: this.connectionStrategy.initialDelay,
348
- maxDelay: this.connectionStrategy.maxDelay,
349
- maxRetry: -1
350
- };
351
- const _when_internal_error = (err, callback) => {
352
- errorLog("INTERNAL ERROR", err.message);
353
- callback(err);
354
- };
355
- const _when_reconnectionIsCanceled = (callback) => {
356
- warningLog("attempt to recreate a new secure channel : suspended becasue reconnection is canceled !");
357
- this.emit("reconnection_canceled");
358
- return callback(new Error("Reconnection has been canceled - " + this.clientName));
359
- };
360
- const _failAndRetry = (err, message, callback) => {
361
- debugLog("failAndRetry; ", message);
362
- if (this._reconnectionIsCanceled) {
363
- return _when_reconnectionIsCanceled(callback);
364
- }
365
- this._destroy_secure_channel();
366
- warningLog("client = ", this.clientName, message, err.message);
367
- // else
368
- // let retry a little bit later
369
- this.emit("reconnection_attempt_has_failed", err, message); // send after callback
370
- setImmediate(_attempt_to_recreate_secure_channel, callback);
371
- };
372
- const _when_connected = (callback) => {
373
- this.emit("after_reconnection", null); // send after callback
374
- (0, node_opcua_assert_1.assert)(this._secureChannel, "expecting a secureChannel here ");
375
- // a new channel has be created and a new connection is established
376
- debugLog(chalk.bgWhite.red("ClientBaseImpl: RECONNECTED !!!"));
377
- this._setInternalState("reconnecting_newchannel_connected");
378
- return callback();
379
- };
380
- const _attempt_to_recreate_secure_channel = (callback) => {
381
- debugLog("attempt to recreate a new secure channel");
382
- if (this._reconnectionIsCanceled) {
383
- return _when_reconnectionIsCanceled(callback);
384
- }
385
- (0, node_opcua_assert_1.assert)(!this._secureChannel, "_attempt_to_recreate_secure_channel, expecting this._secureChannel not to exist");
386
- this._internal_create_secure_channel(infiniteConnectionRetry, (err) => {
387
- if (err) {
388
- // istanbul ignore next
389
- if (this._secureChannel) {
390
- const err = new Error("_internal_create_secure_channel failed, expecting this._secureChannel not to exist");
391
- return _when_internal_error(err, callback);
392
- }
393
- if (err.message.match(/ECONNREFUSED|ECONNABORTED/)) {
394
- return _failAndRetry(err, "create secure channel failed with ECONNREFUSED|ECONNABORTED", callback);
395
- }
396
- if (err.message.match("Backoff aborted.")) {
397
- return _failAndRetry(err, "cannot create secure channel (backoff aborted)", callback);
398
- }
399
- if (err.message.match("BadCertificateInvalid") ||
400
- err.message.match(/_socket has been disconnected by third party/)) {
401
- warningLog("the server certificate has changed, we need to retrieve server certificate again: ", err.message);
402
- warningLog("old server certificate ", this.serverCertificate ? (0, node_opcua_crypto_1.makeSHA1Thumbprint)(this.serverCertificate).toString("hex") : "undefined");
403
- // the server may have shut down the channel because its certificate
404
- // has changed ....
405
- // let request the server certificate again ....
406
- return this.fetchServerCertificate(this.endpointUrl, (err1) => {
407
- if (err1) {
408
- return _failAndRetry(err1, "Failing to fetch new server certificate", callback);
409
- }
410
- warningLog("new server certificate ", (0, node_opcua_crypto_1.makeSHA1Thumbprint)(this.serverCertificate).toString("hex"));
411
- this._internal_create_secure_channel(infiniteConnectionRetry, (err3) => {
412
- if (err3) {
413
- return _failAndRetry(err3, "trying to create new channel with new certificate", callback);
414
- }
415
- return _when_connected(callback);
416
- });
417
- });
418
- }
419
- else {
420
- return _failAndRetry(err, "cannot create secure channel", callback);
421
- }
422
- }
423
- else {
424
- return _when_connected(callback);
425
- }
426
- });
427
- };
428
- // create a secure channel
429
- // a new secure channel must be established
430
- _attempt_to_recreate_secure_channel(callback);
431
- }
432
- _internal_create_secure_channel(connectionStrategy, callback) {
433
- (0, node_opcua_assert_1.assert)(this._secureChannel === null);
434
- (0, node_opcua_assert_1.assert)(typeof this.endpointUrl === "string");
435
- debugLog("_internal_create_secure_channel creating new ClientSecureChannelLayer _internalState =", this._internalState);
436
- const secureChannel = new node_opcua_secure_channel_1.ClientSecureChannelLayer({
437
- connectionStrategy,
438
- defaultSecureTokenLifetime: this.defaultSecureTokenLifetime,
439
- parent: this,
440
- securityMode: this.securityMode,
441
- securityPolicy: this.securityPolicy,
442
- serverCertificate: this.serverCertificate,
443
- tokenRenewalInterval: this.tokenRenewalInterval,
444
- transportSettings: this._transportSettings
445
- // transportTimeout:
446
- });
447
- secureChannel.on("backoff", (count, delay) => {
448
- this.emit("backoff", count, delay);
449
- });
450
- secureChannel.on("abort", () => {
451
- this.emit("abort");
452
- });
453
- secureChannel.protocolVersion = this.protocolVersion;
454
- this._secureChannel = secureChannel;
455
- async.series([
456
- // ------------------------------------------------- STEP 2 : OpenSecureChannel
457
- (innerCallback) => {
458
- debugLog("_internal_create_secure_channel before secureChannel.create");
459
- secureChannel.create(this.endpointUrl, (err) => {
460
- debugLog("_internal_create_secure_channel after secureChannel.create");
461
- if (!this._secureChannel) {
462
- debugLog("_secureChannel has been closed during the transaction !");
463
- (0, node_opcua_assert_1.assert)(this.disconnecting);
464
- return innerCallback(new Error("Secure Channel Closed"));
465
- }
466
- if (!err) {
467
- this._install_secure_channel_event_handlers(secureChannel);
468
- }
469
- innerCallback(err);
470
- });
471
- },
472
- // ------------------------------------------------- STEP 3 : GetEndpointsRequest
473
- (innerCallback) => {
474
- (0, node_opcua_assert_1.assert)(this._secureChannel !== null);
475
- if (!this.knowsServerEndpoint) {
476
- this.getEndpoints((err /*, endpoints?: EndpointDescription[]*/) => {
477
- if (!this._secureChannel) {
478
- debugLog("_secureChannel has been closed during the transaction ! (while getEndpoints)");
479
- (0, node_opcua_assert_1.assert)(this.disconnecting);
480
- return innerCallback(new Error("Secure Channel Closed"));
481
- }
482
- innerCallback(err ? err : undefined);
483
- });
484
- }
485
- else {
486
- // end points are already known
487
- innerCallback();
488
- }
489
- }
490
- ], (err) => {
491
- if (err) {
492
- debugLog("Inner create secure channel has failed", err.message);
493
- if (this._secureChannel) {
494
- this._secureChannel.abortConnection(() => {
495
- this._destroy_secure_channel();
496
- callback(err);
497
- });
498
- }
499
- else {
500
- callback(err);
501
- }
502
- }
503
- else {
504
- (0, node_opcua_assert_1.assert)(this._secureChannel !== null);
505
- callback(null, secureChannel);
506
- }
507
- });
508
- }
509
- static createCertificate(clientCertificateManager, certificateFile, applicationName, applicationUri) {
510
- return __awaiter(this, void 0, void 0, function* () {
511
- if (!fs.existsSync(certificateFile)) {
512
- const hostname = (0, node_opcua_hostname_1.getHostname)();
513
- // this.serverInfo.applicationUri!;
514
- yield clientCertificateManager.createSelfSignedCertificate({
515
- applicationUri,
516
- dns: [hostname],
517
- // ip: await getIpAddresses(),
518
- outputFile: certificateFile,
519
- subject: (0, node_opcua_certificate_manager_1.makeSubject)(applicationName, hostname),
520
- startDate: new Date(),
521
- validity: 365 * 10 // 10 years
522
- });
523
- }
524
- // istanbul ignore next
525
- if (!fs.existsSync(certificateFile)) {
526
- throw new Error(" cannot locate certificate file " + certificateFile);
527
- }
528
- });
529
- }
530
- createDefaultCertificate() {
531
- return __awaiter(this, void 0, void 0, function* () {
532
- // istanbul ignore next
533
- if (this._inCreateDefaultCertificate) {
534
- errorLog("Internal error : re-entrancy in createDefaultCertificate!");
535
- }
536
- this._inCreateDefaultCertificate = true;
537
- if (!fs.existsSync(this.certificateFile)) {
538
- const lockfile = path.join(this.certificateFile + ".lock");
539
- yield (0, global_mutex_1.withLock)({ lockfile: lockfile, maxStaleDuration: 60 * 1000, retryInterval: 100 }, () => __awaiter(this, void 0, void 0, function* () {
540
- if (this.disconnecting)
541
- return;
542
- yield ClientBaseImpl.createCertificate(this.clientCertificateManager, this.certificateFile, this.applicationName, this._getBuiltApplicationUri());
543
- debugLog("privateKey = ", this.privateKeyFile);
544
- debugLog(" = ", this.clientCertificateManager.privateKey);
545
- debugLog("certificateFile = ", this.certificateFile);
546
- const certificate = this.getCertificate();
547
- const privateKey = this.getPrivateKey();
548
- }));
549
- }
550
- this._inCreateDefaultCertificate = false;
551
- });
552
- }
553
- _getBuiltApplicationUri() {
554
- if (!this._applicationUri) {
555
- this._applicationUri = (0, node_opcua_common_2.makeApplicationUrn)((0, node_opcua_hostname_1.getHostname)(), this.applicationName);
556
- }
557
- return this._applicationUri;
558
- }
559
- initializeCM() {
560
- return __awaiter(this, void 0, void 0, function* () {
561
- if (!this.clientCertificateManager) {
562
- // this usually happen when the client has been already disconnected,
563
- // disconnect
564
- errorLog("[NODE-OPCUA-E08] initializeCM: clientCertificateManager is null\n" +
565
- " This happen when you disconnected the client, to free resources.\n" +
566
- " Please create a new OPCUAClient instance if you want to reconnect");
567
- return;
568
- }
569
- yield this.clientCertificateManager.initialize();
570
- yield this.createDefaultCertificate();
571
- // istanbul ignore next
572
- if (!fs.existsSync(this.privateKeyFile)) {
573
- throw new Error(" cannot locate private key file " + this.privateKeyFile);
574
- }
575
- if (this.disconnecting)
576
- return;
577
- const lockfile = path.join(this.certificateFile + ".lock");
578
- yield (0, global_mutex_1.withLock)({ lockfile: lockfile, maxStaleDuration: 60 * 1000, retryInterval: 100 }, () => __awaiter(this, void 0, void 0, function* () {
579
- if (this.disconnecting)
580
- return;
581
- yield verify_1.performCertificateSanityCheck.call(this, "client", this.clientCertificateManager, this._getBuiltApplicationUri());
582
- }));
583
- });
584
- }
585
- _handleUnrecoverableConnectionFailure(err, callback) {
586
- debugLog(err.message);
587
- this.emit("connection_failed");
588
- this._setInternalState("disconnected");
589
- return callback(err);
590
- }
591
- _handleDisconnectionWhileConnecting(err, callback) {
592
- debugLog(err.message);
593
- this.emit("connection_failed");
594
- this._setInternalState("disconnected");
595
- return callback(err);
596
- }
597
- _handleSuccessfulConnection(callback) {
598
- debugLog(" Connected successfully to ", this.endpointUrl);
599
- this.emit("connected");
600
- this._setInternalState("connected");
601
- callback();
602
- }
603
- connect(...args) {
604
- const endpointUrl = args[0];
605
- const callback = args[1];
606
- (0, node_opcua_assert_1.assert)(typeof callback === "function", "expecting a callback");
607
- if (typeof endpointUrl !== "string" || endpointUrl.length <= 0) {
608
- errorLog("[NODE-OPCUA-E03] OPCUAClient#connect expects a valid endpoint : " + endpointUrl);
609
- callback(new Error("Invalid endpoint"));
610
- return;
611
- }
612
- (0, node_opcua_assert_1.assert)(typeof endpointUrl === "string" && endpointUrl.length > 0);
613
- // istanbul ignore next
614
- if (this._internalState !== "disconnected") {
615
- callback(new Error("invalid internal state = " + this._internalState));
616
- return;
617
- }
618
- // prevent illegal call to connect
619
- if (this._secureChannel !== null) {
620
- setImmediate(() => callback(new Error("connect already called")));
621
- return;
622
- }
623
- this._setInternalState("connecting");
624
- this.disconnecting = false;
625
- this.initializeCM()
626
- .then(() => {
627
- debugLog("ClientBaseImpl#connect ", endpointUrl);
628
- if (this.disconnecting) {
629
- return this._handleDisconnectionWhileConnecting(new Error("premature disconnection 1"), callback);
630
- }
631
- if (!this.serverCertificate &&
632
- (forceEndpointDiscoveryOnConnect || this.securityMode !== node_opcua_service_secure_channel_1.MessageSecurityMode.None)) {
633
- debugLog("Fetching certificates from endpoints");
634
- this.fetchServerCertificate(endpointUrl, (err, adjustedEndpointUrl) => {
635
- if (err) {
636
- return this._handleUnrecoverableConnectionFailure(err, callback);
637
- }
638
- if (this.disconnecting) {
639
- return this._handleDisconnectionWhileConnecting(new Error("premature disconnection 2"), callback);
640
- }
641
- if (forceEndpointDiscoveryOnConnect) {
642
- debugLog("connecting with adjusted endpoint : ", adjustedEndpointUrl, " was =", endpointUrl);
643
- this._connectStep2(adjustedEndpointUrl, callback);
644
- }
645
- else {
646
- debugLog("connecting with endpoint : ", endpointUrl);
647
- this._connectStep2(endpointUrl, callback);
648
- }
649
- });
650
- }
651
- else {
652
- this._connectStep2(endpointUrl, callback);
653
- }
654
- })
655
- .catch((err) => {
656
- return this._handleUnrecoverableConnectionFailure(err, callback);
657
- });
658
- }
659
- /**
660
- * @private
661
- */
662
- _connectStep2(endpointUrl, callback) {
663
- // prevent illegal call to connect
664
- (0, node_opcua_assert_1.assert)(this._secureChannel === null);
665
- this.endpointUrl = endpointUrl;
666
- this._clockAdjuster = this._clockAdjuster || new ClockAdjustment();
667
- client_base_1.OPCUAClientBase.registry.register(this);
668
- debugLog("__connectStep2");
669
- this._internal_create_secure_channel(this.connectionStrategy, (err) => {
670
- if (!err) {
671
- this._handleSuccessfulConnection(callback);
672
- }
673
- else {
674
- client_base_1.OPCUAClientBase.registry.unregister(this);
675
- if (this._clockAdjuster) {
676
- this._clockAdjuster.dispose();
677
- this._clockAdjuster = undefined;
678
- }
679
- debugLog(chalk.red("SecureChannel creation has failed with error :", err.message));
680
- if (err.message.match(/ECONNABORTED/)) {
681
- debugLog(chalk.yellow("- The client cannot to :" + endpointUrl + ". Connection has been aborted."));
682
- err = new Error("The connection has been aborted");
683
- this._handleUnrecoverableConnectionFailure(err, callback);
684
- }
685
- else if (err.message.match(/ECONNREF/)) {
686
- debugLog(chalk.yellow("- The client cannot to :" + endpointUrl + ". Server is not reachable."));
687
- err = new Error("The connection cannot be established with server " +
688
- endpointUrl +
689
- " .\n" +
690
- "Please check that the server is up and running or your network configuration.\n" +
691
- "Err = (" +
692
- err.message +
693
- ")");
694
- this._handleUnrecoverableConnectionFailure(err, callback);
695
- }
696
- else if (err.message.match(/disconnecting/)) {
697
- /* */
698
- this._handleDisconnectionWhileConnecting(err, callback);
699
- }
700
- else {
701
- err = new Error("The connection may have been rejected by server,\n" + "Err = (" + err.message + ")");
702
- this._handleUnrecoverableConnectionFailure(err, callback);
703
- }
704
- }
705
- });
706
- }
707
- getClientNonce() {
708
- return this._secureChannel ? this._secureChannel.getClientNonce() : null;
709
- }
710
- performMessageTransaction(request, callback) {
711
- if (!this._secureChannel) {
712
- // this may happen if the Server has closed the connection abruptly for some unknown reason
713
- // or if the tcp connection has been broken.
714
- return callback(new Error("performMessageTransaction: No SecureChannel , connection may have been canceled abruptly by server"));
715
- }
716
- if (this._internalState !== "connected" &&
717
- this._internalState !== "reconnecting_newchannel_connected" &&
718
- this._internalState !== "connecting" &&
719
- this._internalState !== "reconnecting") {
720
- return callback(new Error("performMessageTransaction: Invalid client state " +
721
- this._internalState +
722
- " while performing a transaction " +
723
- request.constructor.name));
724
- }
725
- (0, node_opcua_assert_1.assert)(this._secureChannel);
726
- (0, node_opcua_assert_1.assert)(request);
727
- (0, node_opcua_assert_1.assert)(request.requestHeader);
728
- (0, node_opcua_assert_1.assert)(typeof callback === "function");
729
- this._secureChannel.performMessageTransaction(request, callback);
730
- }
731
- /**
732
- *
733
- * return the endpoint information matching security mode and security policy.
734
- * @method findEndpoint
735
- */
736
- findEndpointForSecurity(securityMode, securityPolicy) {
737
- securityMode = (0, node_opcua_service_secure_channel_1.coerceMessageSecurityMode)(securityMode);
738
- securityPolicy = (0, node_opcua_secure_channel_1.coerceSecurityPolicy)(securityPolicy);
739
- (0, node_opcua_assert_1.assert)(this.knowsServerEndpoint, "Server end point are not known yet");
740
- return this._serverEndpoints.find((endpoint) => {
741
- return endpoint.securityMode === securityMode && endpoint.securityPolicyUri === securityPolicy;
742
- });
743
- }
744
- /**
745
- *
746
- * return the endpoint information matching the specified url , security mode and security policy.
747
- * @method findEndpoint
748
- */
749
- findEndpoint(endpointUrl, securityMode, securityPolicy) {
750
- (0, node_opcua_assert_1.assert)(this.knowsServerEndpoint, "Server end point are not known yet");
751
- if (!this._serverEndpoints || this._serverEndpoints.length === 0) {
752
- return undefined;
753
- }
754
- return this._serverEndpoints.find((endpoint) => {
755
- return ((0, node_opcua_utils_1.matchUri)(endpoint.endpointUrl, endpointUrl) &&
756
- endpoint.securityMode === securityMode &&
757
- endpoint.securityPolicyUri === securityPolicy);
758
- });
759
- }
760
- getEndpoints(...args) {
761
- if (args.length === 1) {
762
- return this.getEndpoints({}, args[0]);
763
- }
764
- const options = args[0];
765
- const callback = args[1];
766
- (0, node_opcua_assert_1.assert)(typeof callback === "function");
767
- options.localeIds = options.localeIds || [];
768
- options.profileUris = options.profileUris || [];
769
- const request = new node_opcua_service_endpoints_1.GetEndpointsRequest({
770
- endpointUrl: options.endpointUrl || this.endpointUrl,
771
- localeIds: options.localeIds,
772
- profileUris: options.profileUris,
773
- requestHeader: {
774
- auditEntryId: null
775
- }
776
- });
777
- this.performMessageTransaction(request, (err, response) => {
778
- this._serverEndpoints = [];
779
- if (err) {
780
- return callback(err);
781
- }
782
- // istanbul ignore next
783
- if (!response || !(response instanceof node_opcua_service_endpoints_1.GetEndpointsResponse)) {
784
- return callback(new Error("Internal Error"));
785
- }
786
- if (response && response.endpoints) {
787
- this._serverEndpoints = response.endpoints;
788
- }
789
- callback(null, this._serverEndpoints);
790
- });
791
- }
792
- /**
793
- * @deprecated
794
- */
795
- getEndpointsRequest(options, callback) {
796
- warningLog("note: ClientBaseImpl#getEndpointsRequest is deprecated, use ClientBaseImpl#getEndpoints instead");
797
- return this.getEndpoints(options, callback);
798
- }
799
- findServers(...args) {
800
- if (!this._secureChannel) {
801
- setImmediate(() => {
802
- callback(new Error("Invalid Secure Channel"));
803
- });
804
- return;
805
- }
806
- if (args.length === 1) {
807
- return this.findServers({}, args[0]);
808
- }
809
- const options = args[0];
810
- const callback = args[1];
811
- const request = new node_opcua_service_discovery_1.FindServersRequest({
812
- endpointUrl: options.endpointUrl || this.endpointUrl,
813
- localeIds: options.localeIds || [],
814
- serverUris: options.serverUris || []
815
- });
816
- this.performMessageTransaction(request, (err, response) => {
817
- if (err) {
818
- return callback(err);
819
- }
820
- /* istanbul ignore next */
821
- if (!response || !(response instanceof node_opcua_service_discovery_1.FindServersResponse)) {
822
- return callback(new Error("Internal Error"));
823
- }
824
- response.servers = response.servers || [];
825
- callback(null, response.servers);
826
- });
827
- }
828
- findServersOnNetwork(...args) {
829
- if (args.length === 1) {
830
- return this.findServersOnNetwork({}, args[0]);
831
- }
832
- const options = args[0];
833
- const callback = args[1];
834
- if (!this._secureChannel) {
835
- setImmediate(() => {
836
- callback(new Error("Invalid Secure Channel"));
837
- });
838
- return;
839
- }
840
- const request = new node_opcua_service_discovery_1.FindServersOnNetworkRequest(options);
841
- this.performMessageTransaction(request, (err, response) => {
842
- if (err) {
843
- return callback(err);
844
- }
845
- /* istanbul ignore next */
846
- if (!response || !(response instanceof node_opcua_service_discovery_1.FindServersOnNetworkResponse)) {
847
- return new Error("Internal Error");
848
- }
849
- response.servers = response.servers || [];
850
- callback(null, response.servers);
851
- });
852
- }
853
- _removeSession(session) {
854
- const index = this._sessions.indexOf(session);
855
- if (index >= 0) {
856
- const s = this._sessions.splice(index, 1)[0];
857
- (0, node_opcua_assert_1.assert)(s === session);
858
- (0, node_opcua_assert_1.assert)(session._client === this);
859
- session._client = null;
860
- }
861
- (0, node_opcua_assert_1.assert)(this._sessions.indexOf(session) === -1);
862
- }
863
- _closeSession(session, deleteSubscriptions, callback) {
864
- (0, node_opcua_assert_1.assert)(typeof callback === "function");
865
- (0, node_opcua_assert_1.assert)(typeof deleteSubscriptions === "boolean");
866
- // istanbul ignore next
867
- if (!this._secureChannel) {
868
- return callback(null); // new Error("no channel"));
869
- }
870
- (0, node_opcua_assert_1.assert)(this._secureChannel);
871
- if (!this._secureChannel.isValid()) {
872
- return callback(null);
873
- }
874
- debugLog(chalk.bgWhite.green("_closeSession ") + this._secureChannel.channelId);
875
- if (this.isReconnecting) {
876
- errorLog("OPCUAClientImpl#_closeSession called while reconnection in progress ! What shall we do");
877
- return callback(null);
878
- }
879
- const request = new node_opcua_service_session_1.CloseSessionRequest({
880
- deleteSubscriptions
881
- });
882
- session.performMessageTransaction(request, (err, response) => {
883
- if (err) {
884
- callback(err);
885
- }
886
- else {
887
- callback(err, response);
888
- }
889
- });
890
- }
891
- closeSession(...args) {
892
- const session = args[0];
893
- const deleteSubscriptions = args[1];
894
- const callback = args[2];
895
- (0, node_opcua_assert_1.assert)(typeof deleteSubscriptions === "boolean");
896
- (0, node_opcua_assert_1.assert)(typeof callback === "function");
897
- (0, node_opcua_assert_1.assert)(session);
898
- (0, node_opcua_assert_1.assert)(session._client === this, "session must be attached to this");
899
- session._closed = true;
900
- // todo : send close session on secure channel
901
- this._closeSession(session, deleteSubscriptions, (err, response) => {
902
- session.emitCloseEvent(node_opcua_status_code_1.StatusCodes.Good);
903
- this._removeSession(session);
904
- session.dispose();
905
- (0, node_opcua_assert_1.assert)(this._sessions.indexOf(session) === -1);
906
- (0, node_opcua_assert_1.assert)(session._closed, "session must indicate it is closed");
907
- callback(err ? err : undefined);
908
- });
909
- }
910
- // eslint-disable-next-line max-statements
911
- disconnect(...args) {
912
- const callback = args[0];
913
- (0, node_opcua_assert_1.assert)(typeof callback === "function", "expecting a callback function here");
914
- if (this._internalState === "disconnected" || this._internalState === "disconnecting") {
915
- if (this._internalState === "disconnecting") {
916
- warningLog("[NODE-OPCUA-W26] OPCUAClient#disconnect called while already disconnecting");
917
- }
918
- return callback();
919
- }
920
- debugLog("disconnecting client! (will set reconnectionIsCanceled to true");
921
- this._reconnectionIsCanceled = true;
922
- this.disconnecting = true;
923
- if (this._tmpClient) {
924
- this._tmpClient.disconnect((err) => {
925
- this._tmpClient = undefined;
926
- (0, node_opcua_assert_1.assert)(!this._tmpClient);
927
- // retry disconnect on main client
928
- this.disconnect(callback);
929
- });
930
- return;
931
- }
932
- debugLog("ClientBaseImpl#disconnect", this.endpointUrl);
933
- if (this.isReconnecting && !this._reconnectionIsCanceled) {
934
- debugLog("ClientBaseImpl#disconnect called while reconnection is in progress");
935
- // let's abort the reconnection process
936
- this._cancel_reconnection((err) => {
937
- debugLog("ClientBaseImpl#disconnect reconnection has been canceled", this.applicationName);
938
- (0, node_opcua_assert_1.assert)(!err, " why would this fail ?");
939
- // sessions cannot be cancelled properly and must be discarded.
940
- this.disconnect(callback);
941
- });
942
- return;
943
- }
944
- if (this._sessions.length && !this.keepPendingSessionsOnDisconnect) {
945
- debugLog("warning : disconnection : closing pending sessions");
946
- // disconnect has been called whereas living session exists
947
- // we need to close them first .... (unless keepPendingSessionsOnDisconnect)
948
- this._close_pending_sessions(( /*err*/) => {
949
- this.disconnect(callback);
950
- });
951
- return;
952
- }
953
- debugLog("Disconnecting !");
954
- this._setInternalState("disconnecting");
955
- if (this.clientCertificateManager) {
956
- const tmp = this.clientCertificateManager;
957
- // (this as any).clientCertificateManager = null;
958
- tmp.dispose();
959
- }
960
- if (this._sessions.length) {
961
- // transfer active session to orphan and detach them from channel
962
- const tmp = [...this._sessions];
963
- for (const session of tmp) {
964
- this._removeSession(session);
965
- }
966
- this._sessions = [];
967
- }
968
- (0, node_opcua_assert_1.assert)(this._sessions.length === 0, " attempt to disconnect a client with live sessions ");
969
- client_base_1.OPCUAClientBase.registry.unregister(this);
970
- if (this._clockAdjuster) {
971
- this._clockAdjuster.dispose();
972
- this._clockAdjuster = undefined;
973
- }
974
- if (this._secureChannel) {
975
- let tmpChannel = this._secureChannel;
976
- this._secureChannel = null;
977
- debugLog("Closing channel");
978
- tmpChannel.close(() => {
979
- this._secureChannel = tmpChannel;
980
- tmpChannel = null;
981
- this._destroy_secure_channel();
982
- this._setInternalState("disconnected");
983
- setImmediate(callback);
984
- });
985
- }
986
- else {
987
- this.emit("close", null);
988
- this._setInternalState("disconnected");
989
- setImmediate(callback);
990
- }
991
- }
992
- // override me !
993
- _on_connection_reestablished(callback) {
994
- callback();
995
- }
996
- toString() {
997
- let str = "\n";
998
- str += " defaultSecureTokenLifetime.... " + this.defaultSecureTokenLifetime + "\n";
999
- str += " securityMode.................. " + node_opcua_service_secure_channel_1.MessageSecurityMode[this.securityMode] + "\n";
1000
- str += " securityPolicy................ " + this.securityPolicy.toString() + "\n";
1001
- str += " certificate fingerprint....... " + (0, node_opcua_crypto_1.makeSHA1Thumbprint)(this.getCertificate()).toString("hex") + "\n";
1002
- str +=
1003
- " server certificate fingerprint " +
1004
- (this.serverCertificate ? (0, node_opcua_crypto_1.makeSHA1Thumbprint)(this.serverCertificate).toString("hex") : "") +
1005
- "\n";
1006
- // this.serverCertificate = options.serverCertificate || null + "\n";
1007
- str += " keepSessionAlive.............. " + this.keepSessionAlive + "\n";
1008
- str += " bytesRead..................... " + this.bytesRead + "\n";
1009
- str += " bytesWritten.................. " + this.bytesWritten + "\n";
1010
- str += " transactionsPerformed......... " + this.transactionsPerformed + "\n";
1011
- str += " timedOutRequestCount.......... " + this.timedOutRequestCount + "\n";
1012
- str += " connectionStrategy." + "\n";
1013
- str += " .maxRetry............... " + this.connectionStrategy.maxRetry + "\n";
1014
- str += " .initialDelay........... " + this.connectionStrategy.initialDelay + "\n";
1015
- str += " .maxDelay............... " + this.connectionStrategy.maxDelay + "\n";
1016
- str += " .randomisationFactor.... " + this.connectionStrategy.randomisationFactor + "\n";
1017
- str += " keepSessionAlive.............. " + this.keepSessionAlive + "\n";
1018
- str += " applicationName............... " + this.applicationName + "\n";
1019
- str += " applicationUri................ " + this._getBuiltApplicationUri() + "\n";
1020
- str += " clientName.................... " + this.clientName + "\n";
1021
- if (this._secureChannel) {
1022
- str += "secureChannel:\n" + this._secureChannel.toString();
1023
- }
1024
- return str;
1025
- }
1026
- getSessions() {
1027
- return this._sessions;
1028
- }
1029
- _addSession(session) {
1030
- (0, node_opcua_assert_1.assert)(!session._client || session._client === this);
1031
- (0, node_opcua_assert_1.assert)(this._sessions.indexOf(session) === -1, "session already added");
1032
- session._client = this;
1033
- this._sessions.push(session);
1034
- }
1035
- fetchServerCertificate(endpointUrl, callback) {
1036
- const discoveryUrl = this.discoveryUrl.length > 0 ? this.discoveryUrl : endpointUrl;
1037
- debugLog("OPCUAClientImpl : getting serverCertificate");
1038
- // we have not been given the serverCertificate but this certificate
1039
- // is required as the connection is to be secured.
1040
- //
1041
- // Let's explore the server endpoint that matches our security settings
1042
- // This will give us the missing Certificate as well from the server.
1043
- // todo :
1044
- // Once we have the certificate, we cannot trust it straight away
1045
- // we have to verify that the certificate is valid and not outdated and not revoked.
1046
- // if the certificate is self-signed the certificate must appear in the trust certificate
1047
- // list.
1048
- // if the certificate has been certified by an Certificate Authority we have to
1049
- // verify that the certificates in the chain are valid and not revoked.
1050
- //
1051
- const certificateFile = this.certificateFile;
1052
- const privateKeyFile = this.privateKeyFile;
1053
- const applicationName = this.applicationName;
1054
- const applicationUri = this._applicationUri;
1055
- const params = {
1056
- connectionStrategy: this.connectionStrategy,
1057
- endpointMustExist: false,
1058
- securityMode: this.securityMode,
1059
- securityPolicy: this.securityPolicy,
1060
- applicationName,
1061
- applicationUri,
1062
- certificateFile,
1063
- privateKeyFile,
1064
- clientCertificateManager: this.clientCertificateManager
1065
- };
1066
- return __findEndpoint.call(this, discoveryUrl, params, (err, result) => {
1067
- if (err) {
1068
- return callback(err);
1069
- }
1070
- // istanbul ignore next
1071
- if (!result) {
1072
- const err1 = new Error("internal error");
1073
- return callback(err1);
1074
- }
1075
- const endpoint = result.selectedEndpoint;
1076
- if (!endpoint) {
1077
- // no matching end point can be found ...
1078
- const err1 = new Error("cannot find endpoint for securityMode=" +
1079
- node_opcua_service_secure_channel_1.MessageSecurityMode[this.securityMode] +
1080
- " policy = " +
1081
- this.securityPolicy);
1082
- return callback(err1);
1083
- }
1084
- (0, node_opcua_assert_1.assert)(endpoint);
1085
- _verify_serverCertificate(this.clientCertificateManager, endpoint.serverCertificate, (err1) => {
1086
- if (err1) {
1087
- warningLog("[NODE-OPCUA-W25] client's server certificate verification has failed ", err1.message);
1088
- warningLog(" ", this.clientCertificateManager.rootDir);
1089
- warningLog(" ", endpoint.serverCertificate.toString("base64").replace(/(.{80})/g, "$1\n "));
1090
- warningLog(" verify that server certificate is trusted or that server certificate issuer's certificate is present in the issuer folder");
1091
- return callback(err1);
1092
- }
1093
- this.serverCertificate = endpoint.serverCertificate;
1094
- callback(null, endpoint.endpointUrl);
1095
- });
1096
- });
1097
- }
1098
- _accumulate_statistics() {
1099
- if (this._secureChannel) {
1100
- // keep accumulated statistics
1101
- this._byteWritten += this._secureChannel.bytesWritten;
1102
- this._byteRead += this._secureChannel.bytesRead;
1103
- this._transactionsPerformed += this._secureChannel.transactionsPerformed;
1104
- this._timedOutRequestCount += this._secureChannel.timedOutRequestCount;
1105
- if (doDebug) {
1106
- debugLog(chalk.cyan(` Client ${this._instanceNumber} ${this.clientName} byteWritten = `), this._byteWritten);
1107
- debugLog(chalk.cyan(` Client ${this._instanceNumber} ${this.clientName} byteRead = `), this._byteRead);
1108
- debugLog(chalk.cyan(` Client ${this._instanceNumber} ${this.clientName} transactions = `), this._transactionsPerformed);
1109
- debugLog(chalk.cyan(` Client ${this._instanceNumber} ${this.clientName} timedOutRequestCount = `), this._timedOutRequestCount);
1110
- }
1111
- }
1112
- }
1113
- _destroy_secure_channel() {
1114
- if (this._secureChannel) {
1115
- if (doDebug) {
1116
- debugLog(" DESTROYING SECURE CHANNEL (isTransactionInProgress ?", this._secureChannel.isTransactionInProgress(), ")");
1117
- }
1118
- this._accumulate_statistics();
1119
- const secureChannel = this._secureChannel;
1120
- this._secureChannel = null;
1121
- secureChannel.dispose();
1122
- secureChannel.removeAllListeners();
1123
- }
1124
- }
1125
- _close_pending_sessions(callback) {
1126
- const sessions = [...this._sessions];
1127
- async.map(sessions, (session, next) => {
1128
- (0, node_opcua_assert_1.assert)(session._client === this);
1129
- // note: to prevent next to be call twice
1130
- let _next_already_call = false;
1131
- session.close((err) => {
1132
- if (_next_already_call) {
1133
- return;
1134
- }
1135
- _next_already_call = true;
1136
- // We should not bother if we have an error here
1137
- // Session may fail to close , if they haven't been activate and forcefully closed by server
1138
- // in a attempt to preserve resources in the case of a DDOS attack for instance.
1139
- if (err) {
1140
- const msg = session.authenticationToken ? session.authenticationToken.toString() : "";
1141
- debugLog(" failing to close session " + msg);
1142
- }
1143
- next();
1144
- });
1145
- }, (err) => {
1146
- // istanbul ignore next
1147
- if (this._sessions.length > 0) {
1148
- debugLog(this._sessions
1149
- .map((s) => (s.authenticationToken ? s.authenticationToken.toString() : ""))
1150
- .join(" "));
1151
- }
1152
- (0, node_opcua_assert_1.assert)(this._sessions.length === 0, " failed to disconnect exiting sessions ");
1153
- callback(err);
1154
- });
1155
- }
1156
- _install_secure_channel_event_handlers(secureChannel) {
1157
- (0, node_opcua_assert_1.assert)(this instanceof ClientBaseImpl);
1158
- secureChannel.on("send_chunk", (chunk) => {
1159
- /**
1160
- * notify the observer that a message_chunk has been sent
1161
- * @event send_chunk
1162
- * @param message_chunk
1163
- */
1164
- this.emit("send_chunk", chunk);
1165
- });
1166
- secureChannel.on("receive_chunk", (chunk) => {
1167
- /**
1168
- * notify the observer that a message_chunk has been received
1169
- * @event receive_chunk
1170
- * @param message_chunk
1171
- */
1172
- this.emit("receive_chunk", chunk);
1173
- });
1174
- secureChannel.on("send_request", (message) => {
1175
- /**
1176
- * notify the observer that a request has been sent to the server.
1177
- * @event send_request
1178
- * @param message
1179
- */
1180
- this.emit("send_request", message);
1181
- });
1182
- secureChannel.on("receive_response", (message) => {
1183
- /**
1184
- * notify the observer that a response has been received from the server.
1185
- * @event receive_response
1186
- * @param message
1187
- */
1188
- this.emit("receive_response", message);
1189
- });
1190
- secureChannel.on("lifetime_75", (token) => {
1191
- // secureChannel requests a new token
1192
- debugLog("SecureChannel Security Token ", token.tokenId, "live time was =", token.revisedLifetime, " is about to expired , it's time to request a new token");
1193
- // forward message to upper level
1194
- this.emit("lifetime_75", token, secureChannel);
1195
- });
1196
- secureChannel.on("security_token_renewed", () => {
1197
- // forward message to upper level
1198
- this.emit("security_token_renewed", secureChannel);
1199
- });
1200
- secureChannel.on("close", (err) => {
1201
- debugLog(chalk.yellow.bold(" ClientBaseImpl emitting close"), err === null || err === void 0 ? void 0 : err.message);
1202
- this._destroy_secure_channel();
1203
- if (!err || !this.reconnectOnFailure) {
1204
- // this is a normal close operation initiated by us
1205
- /**
1206
- * @event close
1207
- * @param error
1208
- */
1209
- this.emit("close", err);
1210
- }
1211
- else {
1212
- /**
1213
- * @event connection_lost
1214
- */
1215
- // this.emit("close", err);
1216
- if (this.reconnectOnFailure && this._internalState !== "reconnecting") {
1217
- debugLog(" ClientBaseImpl emitting connection_lost");
1218
- this.emit("connection_lost", err === null || err === void 0 ? void 0 : err.message); // instead of "close"
1219
- this._repairConnection();
1220
- }
1221
- }
1222
- });
1223
- secureChannel.on("timed_out_request", (request) => {
1224
- /**
1225
- * send when a request has timed out without receiving a response
1226
- * @event timed_out_request
1227
- * @param request
1228
- */
1229
- this.emit("timed_out_request", request);
1230
- });
1231
- }
1232
- _repairConnection() {
1233
- if (this._inside_repairConnection) {
1234
- errorLog("_repairConnection already in progress ", this._internalState);
1235
- return;
1236
- }
1237
- this._inside_repairConnection = true;
1238
- debugLog("recreating new secure channel ", this._internalState);
1239
- this._recreate_secure_channel((err1) => {
1240
- debugLog("secureChannel#on(close) => _recreate_secure_channel returns ", err1 ? err1.message : "OK");
1241
- if (err1) {
1242
- errorLog("_recreate_secure_channel has failed: err = ", err1.message);
1243
- this.emit("close", err1);
1244
- this._setInternalState("disconnected");
1245
- this._inside_repairConnection = false;
1246
- return;
1247
- }
1248
- else {
1249
- this._finalReconnectionStep((err2) => {
1250
- if (err2) {
1251
- // istanbul ignore next
1252
- if (doDebug) {
1253
- debugLog("connection_reestablished has failed");
1254
- debugLog("err= ", err2.message);
1255
- }
1256
- // we still need to retry connecting here !!!
1257
- debugLog("Disconnected following reconnection failure", err2.message);
1258
- debugLog(`I will retry OPCUA client reconnection in ${client_base_1.OPCUAClientBase.retryDelay / 1000} seconds`);
1259
- this._inside_repairConnection = false;
1260
- this._destroy_secure_channel();
1261
- setTimeout(() => this._repairConnection(), client_base_1.OPCUAClientBase.retryDelay);
1262
- return;
1263
- }
1264
- else {
1265
- /**
1266
- * @event connection_reestablished
1267
- * send when the connection is reestablished after a connection break
1268
- */
1269
- this.emit("connection_reestablished");
1270
- this._setInternalState("connected");
1271
- this._inside_repairConnection = false;
1272
- }
1273
- });
1274
- }
1275
- });
1276
- }
1277
- _finalReconnectionStep(callback) {
1278
- // now delegate to upper class the
1279
- if (this._on_connection_reestablished) {
1280
- (0, node_opcua_assert_1.assert)(typeof this._on_connection_reestablished === "function");
1281
- this._on_connection_reestablished((err2) => {
1282
- callback(err2);
1283
- });
1284
- }
1285
- else {
1286
- callback();
1287
- }
1288
- }
1289
- /**
1290
- *
1291
- * @internal
1292
- * @private
1293
- */
1294
- __createSession_step2(session, callback) {
1295
- throw new Error("Please override");
1296
- }
1297
- _activateSession(session, callback) {
1298
- throw new Error("Please override");
1299
- }
1300
- }
1301
- exports.ClientBaseImpl = ClientBaseImpl;
1302
- // tslint:disable-next-line: max-classes-per-file
1303
- class TmpClient extends ClientBaseImpl {
1304
- constructor(options) {
1305
- options.clientName = (options.clientName || "") + "_TmpClient";
1306
- super(options);
1307
- }
1308
- connect(endpoint, callback) {
1309
- debugLog("connecting to TmpClient");
1310
- // istanbul ignore next
1311
- if (this._internalState !== "disconnected") {
1312
- callback(new Error("TmpClient#connect: invalid internal state " + this._internalState));
1313
- return;
1314
- }
1315
- if (this.disconnecting) {
1316
- return this._handleUnrecoverableConnectionFailure(new Error("premature disconnection 3"), callback);
1317
- }
1318
- this._setInternalState("connecting");
1319
- this._connectStep2(endpoint, (err) => {
1320
- if (this.disconnecting) {
1321
- return this._handleUnrecoverableConnectionFailure(new Error("premature disconnection 4"), callback);
1322
- }
1323
- callback(err);
1324
- });
1325
- }
1326
- }
1327
- // tslint:disable:no-var-requires
1328
- // tslint:disable:max-line-length
1329
- const thenify = require("thenify");
1330
- ClientBaseImpl.prototype.connect = thenify.withCallback(ClientBaseImpl.prototype.connect);
1331
- ClientBaseImpl.prototype.disconnect = thenify.withCallback(ClientBaseImpl.prototype.disconnect);
1332
- ClientBaseImpl.prototype.getEndpoints = thenify.withCallback(ClientBaseImpl.prototype.getEndpoints);
1333
- ClientBaseImpl.prototype.findServers = thenify.withCallback(ClientBaseImpl.prototype.findServers);
1334
- ClientBaseImpl.prototype.findServersOnNetwork = thenify.withCallback(ClientBaseImpl.prototype.findServersOnNetwork);
1335
- client_base_1.OPCUAClientBase.create = (options) => {
1336
- return new ClientBaseImpl(options);
1337
- };
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ClientBaseImpl = void 0;
13
+ /**
14
+ * @module node-opcua-client-private
15
+ */
16
+ // tslint:disable:no-unused-expression
17
+ const fs = require("fs");
18
+ const path = require("path");
19
+ const async = require("async");
20
+ const chalk = require("chalk");
21
+ const global_mutex_1 = require("@ster5/global-mutex");
22
+ const node_opcua_assert_1 = require("node-opcua-assert");
23
+ const node_opcua_common_1 = require("node-opcua-common");
24
+ const node_opcua_crypto_1 = require("node-opcua-crypto");
25
+ const node_opcua_date_time_1 = require("node-opcua-date-time");
26
+ const node_opcua_debug_1 = require("node-opcua-debug");
27
+ const node_opcua_common_2 = require("node-opcua-common");
28
+ const node_opcua_hostname_1 = require("node-opcua-hostname");
29
+ const node_opcua_secure_channel_1 = require("node-opcua-secure-channel");
30
+ const node_opcua_service_discovery_1 = require("node-opcua-service-discovery");
31
+ const node_opcua_service_endpoints_1 = require("node-opcua-service-endpoints");
32
+ const node_opcua_service_secure_channel_1 = require("node-opcua-service-secure-channel");
33
+ const node_opcua_status_code_1 = require("node-opcua-status-code");
34
+ const node_opcua_utils_1 = require("node-opcua-utils");
35
+ const node_opcua_certificate_manager_1 = require("node-opcua-certificate-manager");
36
+ const node_opcua_service_session_1 = require("node-opcua-service-session");
37
+ const client_base_1 = require("../client_base");
38
+ const verify_1 = require("../verify");
39
+ // tslint:disable-next-line:no-var-requires
40
+ const once = require("once");
41
+ const debugLog = (0, node_opcua_debug_1.make_debugLog)(__filename);
42
+ const doDebug = (0, node_opcua_debug_1.checkDebugFlag)(__filename);
43
+ const errorLog = (0, node_opcua_debug_1.make_errorLog)(__filename);
44
+ const warningLog = (0, node_opcua_debug_1.make_warningLog)(__filename);
45
+ const defaultConnectionStrategy = {
46
+ initialDelay: 1000,
47
+ maxDelay: 20 * 1000,
48
+ maxRetry: -1,
49
+ randomisationFactor: 0.1
50
+ };
51
+ function __findEndpoint(endpointUrl, params, _callback) {
52
+ const masterClient = this;
53
+ debugLog("findEndpoint : endpointUrl = ", endpointUrl);
54
+ debugLog(" params ", params);
55
+ (0, node_opcua_assert_1.assert)(!masterClient._tmpClient);
56
+ const callback = (err, result) => {
57
+ masterClient._tmpClient = undefined;
58
+ _callback(err, result);
59
+ };
60
+ const securityMode = params.securityMode;
61
+ const securityPolicy = params.securityPolicy;
62
+ const connectionStrategy = params.connectionStrategy;
63
+ const options = {
64
+ applicationName: params.applicationName,
65
+ applicationUri: params.applicationUri,
66
+ certificateFile: params.certificateFile,
67
+ clientCertificateManager: params.clientCertificateManager,
68
+ clientName: "EndpointFetcher",
69
+ connectionStrategy: {
70
+ maxRetry: 0 /* no- retry */,
71
+ maxDelay: 2000
72
+ },
73
+ privateKeyFile: params.privateKeyFile
74
+ };
75
+ const client = new TmpClient(options);
76
+ masterClient._tmpClient = client;
77
+ let selectedEndpoint;
78
+ const allEndpoints = [];
79
+ const tasks = [
80
+ (innerCallback) => {
81
+ // rebind backoff handler
82
+ masterClient.listeners("backoff").forEach((handler) => client.on("backoff", handler));
83
+ if (doDebug) {
84
+ client.on("backoff", (retryCount, delay) => {
85
+ debugLog("finding Endpoint => reconnecting ", " retry count", retryCount, " next attempt in ", delay / 1000, "seconds");
86
+ });
87
+ }
88
+ client.connect(endpointUrl, (err) => {
89
+ if (err) {
90
+ // let's improve the error message with meaningful info
91
+ err.message =
92
+ "Fail to connect to server at " +
93
+ endpointUrl +
94
+ " to collect server's certificate (in findEndpoint) \n" +
95
+ " (err =" +
96
+ err.message +
97
+ ")";
98
+ debugLog(err.message);
99
+ }
100
+ return innerCallback(err);
101
+ });
102
+ },
103
+ (innerCallback) => {
104
+ client.getEndpoints((err, endpoints) => {
105
+ if (err) {
106
+ err.message = "error in getEndpoints \n" + err.message;
107
+ return innerCallback(err);
108
+ }
109
+ // istanbul ignore next
110
+ if (!endpoints) {
111
+ return innerCallback(new Error("Internal Error"));
112
+ }
113
+ for (const endpoint of endpoints) {
114
+ if (endpoint.securityMode === securityMode && endpoint.securityPolicyUri === securityPolicy) {
115
+ if (selectedEndpoint) {
116
+ errorLog("Warning more than one endpoint matching !", endpoint.endpointUrl, selectedEndpoint.endpointUrl);
117
+ }
118
+ selectedEndpoint = endpoint; // found it
119
+ }
120
+ }
121
+ innerCallback();
122
+ });
123
+ },
124
+ (innerCallback) => {
125
+ client.disconnect(innerCallback);
126
+ }
127
+ ];
128
+ async.series(tasks, (err) => {
129
+ if (err) {
130
+ client.disconnect(() => {
131
+ callback(err);
132
+ });
133
+ return;
134
+ }
135
+ if (!selectedEndpoint) {
136
+ return callback(new Error("Cannot find an Endpoint matching " +
137
+ " security mode: " +
138
+ securityMode.toString() +
139
+ " policy: " +
140
+ securityPolicy.toString()));
141
+ }
142
+ // istanbul ignore next
143
+ if (doDebug) {
144
+ debugLog(chalk.bgWhite.red("xxxxxxxxxxxxxxxxxxxxx => selected EndPoint = "), selectedEndpoint.toString());
145
+ }
146
+ const result = {
147
+ endpoints: allEndpoints,
148
+ selectedEndpoint
149
+ };
150
+ callback(null, result);
151
+ });
152
+ }
153
+ /**
154
+ * check if certificate is trusted or untrusted
155
+ */
156
+ function _verify_serverCertificate(certificateManager, serverCertificate, callback) {
157
+ certificateManager.checkCertificate(serverCertificate, (err, status) => {
158
+ if (err) {
159
+ return callback(err);
160
+ }
161
+ if (status !== node_opcua_status_code_1.StatusCodes.Good) {
162
+ // do it again for debug purposes
163
+ if (doDebug) {
164
+ certificateManager.verifyCertificate(serverCertificate, (err1, status1) => {
165
+ debugLog(status1);
166
+ });
167
+ }
168
+ warningLog("serverCertificate = ", (0, node_opcua_crypto_1.makeSHA1Thumbprint)(serverCertificate).toString("hex"));
169
+ warningLog("serverCertificate = ", serverCertificate.toString("base64"));
170
+ return callback(new Error("server Certificate verification failed with err " + (status === null || status === void 0 ? void 0 : status.toString())));
171
+ }
172
+ callback();
173
+ });
174
+ }
175
+ const forceEndpointDiscoveryOnConnect = !!parseInt(process.env.NODEOPCUA_CLIENT_FORCE_ENDPOINT_DISCOVERY || "0", 10);
176
+ debugLog("forceEndpointDiscoveryOnConnect = ", forceEndpointDiscoveryOnConnect);
177
+ class ClockAdjustment {
178
+ constructor() {
179
+ debugLog("installPeriodicClockAdjustment ", node_opcua_date_time_1.periodicClockAdjustment.timerInstallationCount);
180
+ (0, node_opcua_date_time_1.installPeriodicClockAdjustment)();
181
+ }
182
+ dispose() {
183
+ (0, node_opcua_date_time_1.uninstallPeriodicClockAdjustment)();
184
+ debugLog("uninstallPeriodicClockAdjustment ", node_opcua_date_time_1.periodicClockAdjustment.timerInstallationCount);
185
+ }
186
+ }
187
+ /*
188
+ * "disconnected" ---[connect]----------------------> "connecting"
189
+ *
190
+ * "connecting" ---[(connection successful)]------> "connected"
191
+ *
192
+ * "connecting" ---[(connection failure)]---------> "disconnected"
193
+ *
194
+ * "connecting" ---[disconnect]-------------------> "disconnecting" --> "disconnected"
195
+ *
196
+ * "connecting" ---[lost of connection]-----------> "reconnecting" ->[reconnection]
197
+ *
198
+ * "reconnecting" ---[reconnection successful]------> "reconnecting_newchannel_connected"
199
+ *
200
+ * "reconnecting_newchannel_connected" --(session failure) -->"reconnecting"
201
+ *
202
+ * "reconnecting" ---[reconnection failure]---------> [reconnection] ---> "reconnecting"
203
+ *
204
+ * "reconnecting" ---[disconnect]-------------------> "disconnecting" --> "disconnected"
205
+ */
206
+ let g_ClientCounter = 0;
207
+ /**
208
+ * @internal
209
+ */
210
+ // tslint:disable-next-line: max-classes-per-file
211
+ class ClientBaseImpl extends node_opcua_common_1.OPCUASecureObject {
212
+ constructor(options) {
213
+ options = options || {};
214
+ if (!options.clientCertificateManager) {
215
+ options.clientCertificateManager = (0, node_opcua_certificate_manager_1.getDefaultCertificateManager)("PKI");
216
+ }
217
+ options.privateKeyFile = options.privateKeyFile || options.clientCertificateManager.privateKey;
218
+ options.certificateFile =
219
+ options.certificateFile || path.join(options.clientCertificateManager.rootDir, "own/certs/client_certificate.pem");
220
+ super(options);
221
+ this._inside_repairConnection = false;
222
+ this._instanceNumber = g_ClientCounter++;
223
+ this._internalState = "uninitialized";
224
+ this.applicationName = options.applicationName || "NodeOPCUA-Client";
225
+ (0, node_opcua_assert_1.assert)(!this.applicationName.match(/^locale=/), "applicationName badly converted from LocalizedText");
226
+ (0, node_opcua_assert_1.assert)(!this.applicationName.match(/urn:/), "applicationName should not be a URI");
227
+ // we need to delay _applicationUri initialization
228
+ this._applicationUri = options.applicationUri || this._getBuiltApplicationUri();
229
+ this.disconnecting = false;
230
+ this.clientCertificateManager = options.clientCertificateManager;
231
+ this._secureChannel = null;
232
+ this._reconnectionIsCanceled = false;
233
+ this.endpointUrl = "";
234
+ this.clientName = options.clientName || "ClientSession";
235
+ // must be ZERO with Spec 1.0.2
236
+ this.protocolVersion = 0;
237
+ this._sessions = [];
238
+ this._serverEndpoints = [];
239
+ this.defaultSecureTokenLifetime = options.defaultSecureTokenLifetime || 600000;
240
+ this.tokenRenewalInterval = options.tokenRenewalInterval || 0;
241
+ (0, node_opcua_assert_1.assert)(isFinite(this.tokenRenewalInterval) && this.tokenRenewalInterval >= 0);
242
+ this.securityMode = (0, node_opcua_service_secure_channel_1.coerceMessageSecurityMode)(options.securityMode);
243
+ this.securityPolicy = (0, node_opcua_secure_channel_1.coerceSecurityPolicy)(options.securityPolicy);
244
+ this.serverCertificate = options.serverCertificate;
245
+ this.keepSessionAlive = typeof options.keepSessionAlive === "boolean" ? options.keepSessionAlive : false;
246
+ // statistics...
247
+ this._byteRead = 0;
248
+ this._byteWritten = 0;
249
+ this._transactionsPerformed = 0;
250
+ this._timedOutRequestCount = 0;
251
+ this.connectionStrategy = (0, node_opcua_secure_channel_1.coerceConnectionStrategy)(options.connectionStrategy || defaultConnectionStrategy);
252
+ /***
253
+ * @property keepPendingSessionsOnDisconnect²
254
+ * @type {boolean}
255
+ */
256
+ this.keepPendingSessionsOnDisconnect = options.keepPendingSessionsOnDisconnect || false;
257
+ this.discoveryUrl = options.discoveryUrl || "";
258
+ this._setInternalState("disconnected");
259
+ this._transportSettings = options.transportSettings || {};
260
+ }
261
+ /**
262
+ * total number of requests that been canceled due to timeout
263
+ */
264
+ get timedOutRequestCount() {
265
+ return this._timedOutRequestCount + (this._secureChannel ? this._secureChannel.timedOutRequestCount : 0);
266
+ }
267
+ /**
268
+ * total number of transactions performed by the client
269
+ x */
270
+ get transactionsPerformed() {
271
+ return this._transactionsPerformed + (this._secureChannel ? this._secureChannel.transactionsPerformed : 0);
272
+ }
273
+ /**
274
+ * is true when the client has already requested the server end points.
275
+ */
276
+ get knowsServerEndpoint() {
277
+ return this._serverEndpoints && this._serverEndpoints.length > 0;
278
+ }
279
+ /**
280
+ * true if the client is trying to reconnect to the server after a connection break.
281
+ */
282
+ get isReconnecting() {
283
+ return !!(this._secureChannel && this._secureChannel.isConnecting) || this._internalState !== "connected";
284
+ }
285
+ /**
286
+ * true if the connection strategy is set to automatically try to reconnect in case of failure
287
+ */
288
+ get reconnectOnFailure() {
289
+ return this.connectionStrategy.maxRetry > 0 || this.connectionStrategy.maxRetry === -1;
290
+ }
291
+ /**
292
+ * total number of bytes read by the client
293
+ */
294
+ get bytesRead() {
295
+ return this._byteRead + (this._secureChannel ? this._secureChannel.bytesRead : 0);
296
+ }
297
+ /**
298
+ * total number of bytes written by the client
299
+ */
300
+ get bytesWritten() {
301
+ return this._byteWritten + (this._secureChannel ? this._secureChannel.bytesWritten : 0);
302
+ }
303
+ _setInternalState(internalState) {
304
+ const previousState = this._internalState;
305
+ if (doDebug) {
306
+ debugLog(chalk.cyan(` Client ${this._instanceNumber} ${this.clientName} from `), chalk.yellow(previousState), "to", chalk.yellow(internalState));
307
+ }
308
+ this._internalState = internalState;
309
+ }
310
+ emit(eventName, ...others) {
311
+ if (doDebug) {
312
+ debugLog(chalk.cyan(` Client ${this._instanceNumber} ${this.clientName} emitting `), chalk.magentaBright(eventName));
313
+ }
314
+ return super.emit(eventName, ...others);
315
+ }
316
+ _cancel_reconnection(callback) {
317
+ // _cancel_reconnection is invoked during disconnection
318
+ // when we detect that a reconnection is in progress..
319
+ if (!this.isReconnecting) {
320
+ warningLog("internal error : _cancel_reconnection should be used when reconnecting is in progress");
321
+ }
322
+ debugLog("canceling reconnection");
323
+ this._reconnectionIsCanceled = true;
324
+ // istanbul ignore next
325
+ if (!this._secureChannel) {
326
+ debugLog("_cancel_reconnection: Nothing to do !");
327
+ return callback(); // nothing to do
328
+ }
329
+ this._secureChannel.abortConnection(( /*err?: Error*/) => {
330
+ this._secureChannel = null;
331
+ callback();
332
+ });
333
+ this.once("reconnection_canceled", () => {
334
+ /* empty */
335
+ });
336
+ }
337
+ _recreate_secure_channel(callback) {
338
+ debugLog("_recreate_secure_channel... while internalState is", this._internalState);
339
+ if (!this.knowsServerEndpoint) {
340
+ debugLog("Cannot reconnect , server endpoint is unknown");
341
+ return callback(new Error("Cannot reconnect, server endpoint is unknown"));
342
+ }
343
+ (0, node_opcua_assert_1.assert)(this.knowsServerEndpoint);
344
+ this._setInternalState("reconnecting");
345
+ this.emit("start_reconnection"); // send after callback
346
+ const infiniteConnectionRetry = {
347
+ initialDelay: this.connectionStrategy.initialDelay,
348
+ maxDelay: this.connectionStrategy.maxDelay,
349
+ maxRetry: -1
350
+ };
351
+ const _when_internal_error = (err, callback) => {
352
+ errorLog("INTERNAL ERROR", err.message);
353
+ callback(err);
354
+ };
355
+ const _when_reconnectionIsCanceled = (callback) => {
356
+ warningLog("attempt to recreate a new secure channel : suspended becasue reconnection is canceled !");
357
+ this.emit("reconnection_canceled");
358
+ return callback(new Error("Reconnection has been canceled - " + this.clientName));
359
+ };
360
+ const _failAndRetry = (err, message, callback) => {
361
+ debugLog("failAndRetry; ", message);
362
+ if (this._reconnectionIsCanceled) {
363
+ return _when_reconnectionIsCanceled(callback);
364
+ }
365
+ this._destroy_secure_channel();
366
+ warningLog("client = ", this.clientName, message, err.message);
367
+ // else
368
+ // let retry a little bit later
369
+ this.emit("reconnection_attempt_has_failed", err, message); // send after callback
370
+ setImmediate(_attempt_to_recreate_secure_channel, callback);
371
+ };
372
+ const _when_connected = (callback) => {
373
+ this.emit("after_reconnection", null); // send after callback
374
+ (0, node_opcua_assert_1.assert)(this._secureChannel, "expecting a secureChannel here ");
375
+ // a new channel has be created and a new connection is established
376
+ debugLog(chalk.bgWhite.red("ClientBaseImpl: RECONNECTED !!!"));
377
+ this._setInternalState("reconnecting_newchannel_connected");
378
+ return callback();
379
+ };
380
+ const _attempt_to_recreate_secure_channel = (callback) => {
381
+ debugLog("attempt to recreate a new secure channel");
382
+ if (this._reconnectionIsCanceled) {
383
+ return _when_reconnectionIsCanceled(callback);
384
+ }
385
+ (0, node_opcua_assert_1.assert)(!this._secureChannel, "_attempt_to_recreate_secure_channel, expecting this._secureChannel not to exist");
386
+ this._internal_create_secure_channel(infiniteConnectionRetry, (err) => {
387
+ if (err) {
388
+ // istanbul ignore next
389
+ if (this._secureChannel) {
390
+ const err = new Error("_internal_create_secure_channel failed, expecting this._secureChannel not to exist");
391
+ return _when_internal_error(err, callback);
392
+ }
393
+ if (err.message.match(/ECONNREFUSED|ECONNABORTED/)) {
394
+ return _failAndRetry(err, "create secure channel failed with ECONNREFUSED|ECONNABORTED", callback);
395
+ }
396
+ if (err.message.match("Backoff aborted.")) {
397
+ return _failAndRetry(err, "cannot create secure channel (backoff aborted)", callback);
398
+ }
399
+ if (err.message.match("BadCertificateInvalid") ||
400
+ err.message.match(/_socket has been disconnected by third party/)) {
401
+ warningLog("the server certificate has changed, we need to retrieve server certificate again: ", err.message);
402
+ warningLog("old server certificate ", this.serverCertificate ? (0, node_opcua_crypto_1.makeSHA1Thumbprint)(this.serverCertificate).toString("hex") : "undefined");
403
+ // the server may have shut down the channel because its certificate
404
+ // has changed ....
405
+ // let request the server certificate again ....
406
+ return this.fetchServerCertificate(this.endpointUrl, (err1) => {
407
+ if (err1) {
408
+ return _failAndRetry(err1, "Failing to fetch new server certificate", callback);
409
+ }
410
+ warningLog("new server certificate ", (0, node_opcua_crypto_1.makeSHA1Thumbprint)(this.serverCertificate).toString("hex"));
411
+ this._internal_create_secure_channel(infiniteConnectionRetry, (err3) => {
412
+ if (err3) {
413
+ return _failAndRetry(err3, "trying to create new channel with new certificate", callback);
414
+ }
415
+ return _when_connected(callback);
416
+ });
417
+ });
418
+ }
419
+ else {
420
+ return _failAndRetry(err, "cannot create secure channel", callback);
421
+ }
422
+ }
423
+ else {
424
+ return _when_connected(callback);
425
+ }
426
+ });
427
+ };
428
+ // create a secure channel
429
+ // a new secure channel must be established
430
+ _attempt_to_recreate_secure_channel(callback);
431
+ }
432
+ _internal_create_secure_channel(connectionStrategy, callback) {
433
+ (0, node_opcua_assert_1.assert)(this._secureChannel === null);
434
+ (0, node_opcua_assert_1.assert)(typeof this.endpointUrl === "string");
435
+ debugLog("_internal_create_secure_channel creating new ClientSecureChannelLayer _internalState =", this._internalState);
436
+ const secureChannel = new node_opcua_secure_channel_1.ClientSecureChannelLayer({
437
+ connectionStrategy,
438
+ defaultSecureTokenLifetime: this.defaultSecureTokenLifetime,
439
+ parent: this,
440
+ securityMode: this.securityMode,
441
+ securityPolicy: this.securityPolicy,
442
+ serverCertificate: this.serverCertificate,
443
+ tokenRenewalInterval: this.tokenRenewalInterval,
444
+ transportSettings: this._transportSettings
445
+ // transportTimeout:
446
+ });
447
+ secureChannel.on("backoff", (count, delay) => {
448
+ this.emit("backoff", count, delay);
449
+ });
450
+ secureChannel.on("abort", () => {
451
+ this.emit("abort");
452
+ });
453
+ secureChannel.protocolVersion = this.protocolVersion;
454
+ this._secureChannel = secureChannel;
455
+ async.series([
456
+ // ------------------------------------------------- STEP 2 : OpenSecureChannel
457
+ (innerCallback) => {
458
+ debugLog("_internal_create_secure_channel before secureChannel.create");
459
+ secureChannel.create(this.endpointUrl, (err) => {
460
+ debugLog("_internal_create_secure_channel after secureChannel.create");
461
+ if (!this._secureChannel) {
462
+ debugLog("_secureChannel has been closed during the transaction !");
463
+ (0, node_opcua_assert_1.assert)(this.disconnecting);
464
+ return innerCallback(new Error("Secure Channel Closed"));
465
+ }
466
+ if (!err) {
467
+ this._install_secure_channel_event_handlers(secureChannel);
468
+ }
469
+ innerCallback(err);
470
+ });
471
+ },
472
+ // ------------------------------------------------- STEP 3 : GetEndpointsRequest
473
+ (innerCallback) => {
474
+ (0, node_opcua_assert_1.assert)(this._secureChannel !== null);
475
+ if (!this.knowsServerEndpoint) {
476
+ this.getEndpoints((err /*, endpoints?: EndpointDescription[]*/) => {
477
+ if (!this._secureChannel) {
478
+ debugLog("_secureChannel has been closed during the transaction ! (while getEndpoints)");
479
+ (0, node_opcua_assert_1.assert)(this.disconnecting);
480
+ return innerCallback(new Error("Secure Channel Closed"));
481
+ }
482
+ innerCallback(err ? err : undefined);
483
+ });
484
+ }
485
+ else {
486
+ // end points are already known
487
+ innerCallback();
488
+ }
489
+ }
490
+ ], (err) => {
491
+ if (err) {
492
+ debugLog("Inner create secure channel has failed", err.message);
493
+ if (this._secureChannel) {
494
+ this._secureChannel.abortConnection(() => {
495
+ this._destroy_secure_channel();
496
+ callback(err);
497
+ });
498
+ }
499
+ else {
500
+ callback(err);
501
+ }
502
+ }
503
+ else {
504
+ (0, node_opcua_assert_1.assert)(this._secureChannel !== null);
505
+ callback(null, secureChannel);
506
+ }
507
+ });
508
+ }
509
+ static createCertificate(clientCertificateManager, certificateFile, applicationName, applicationUri) {
510
+ return __awaiter(this, void 0, void 0, function* () {
511
+ if (!fs.existsSync(certificateFile)) {
512
+ const hostname = (0, node_opcua_hostname_1.getHostname)();
513
+ // this.serverInfo.applicationUri!;
514
+ yield clientCertificateManager.createSelfSignedCertificate({
515
+ applicationUri,
516
+ dns: [hostname],
517
+ // ip: await getIpAddresses(),
518
+ outputFile: certificateFile,
519
+ subject: (0, node_opcua_certificate_manager_1.makeSubject)(applicationName, hostname),
520
+ startDate: new Date(),
521
+ validity: 365 * 10 // 10 years
522
+ });
523
+ }
524
+ // istanbul ignore next
525
+ if (!fs.existsSync(certificateFile)) {
526
+ throw new Error(" cannot locate certificate file " + certificateFile);
527
+ }
528
+ });
529
+ }
530
+ createDefaultCertificate() {
531
+ return __awaiter(this, void 0, void 0, function* () {
532
+ // istanbul ignore next
533
+ if (this._inCreateDefaultCertificate) {
534
+ errorLog("Internal error : re-entrancy in createDefaultCertificate!");
535
+ }
536
+ this._inCreateDefaultCertificate = true;
537
+ if (!fs.existsSync(this.certificateFile)) {
538
+ const lockfile = path.join(this.certificateFile + ".lock");
539
+ yield (0, global_mutex_1.withLock)({ lockfile: lockfile, maxStaleDuration: 60 * 1000, retryInterval: 100 }, () => __awaiter(this, void 0, void 0, function* () {
540
+ if (this.disconnecting)
541
+ return;
542
+ yield ClientBaseImpl.createCertificate(this.clientCertificateManager, this.certificateFile, this.applicationName, this._getBuiltApplicationUri());
543
+ debugLog("privateKey = ", this.privateKeyFile);
544
+ debugLog(" = ", this.clientCertificateManager.privateKey);
545
+ debugLog("certificateFile = ", this.certificateFile);
546
+ const certificate = this.getCertificate();
547
+ const privateKey = this.getPrivateKey();
548
+ }));
549
+ }
550
+ this._inCreateDefaultCertificate = false;
551
+ });
552
+ }
553
+ _getBuiltApplicationUri() {
554
+ if (!this._applicationUri) {
555
+ this._applicationUri = (0, node_opcua_common_2.makeApplicationUrn)((0, node_opcua_hostname_1.getHostname)(), this.applicationName);
556
+ }
557
+ return this._applicationUri;
558
+ }
559
+ initializeCM() {
560
+ return __awaiter(this, void 0, void 0, function* () {
561
+ if (!this.clientCertificateManager) {
562
+ // this usually happen when the client has been already disconnected,
563
+ // disconnect
564
+ errorLog("[NODE-OPCUA-E08] initializeCM: clientCertificateManager is null\n" +
565
+ " This happen when you disconnected the client, to free resources.\n" +
566
+ " Please create a new OPCUAClient instance if you want to reconnect");
567
+ return;
568
+ }
569
+ yield this.clientCertificateManager.initialize();
570
+ yield this.createDefaultCertificate();
571
+ // istanbul ignore next
572
+ if (!fs.existsSync(this.privateKeyFile)) {
573
+ throw new Error(" cannot locate private key file " + this.privateKeyFile);
574
+ }
575
+ if (this.disconnecting)
576
+ return;
577
+ const lockfile = path.join(this.certificateFile + ".lock");
578
+ yield (0, global_mutex_1.withLock)({ lockfile: lockfile, maxStaleDuration: 60 * 1000, retryInterval: 100 }, () => __awaiter(this, void 0, void 0, function* () {
579
+ if (this.disconnecting)
580
+ return;
581
+ yield verify_1.performCertificateSanityCheck.call(this, "client", this.clientCertificateManager, this._getBuiltApplicationUri());
582
+ }));
583
+ });
584
+ }
585
+ _handleUnrecoverableConnectionFailure(err, callback) {
586
+ debugLog(err.message);
587
+ this.emit("connection_failed");
588
+ this._setInternalState("disconnected");
589
+ return callback(err);
590
+ }
591
+ _handleDisconnectionWhileConnecting(err, callback) {
592
+ debugLog(err.message);
593
+ this.emit("connection_failed");
594
+ this._setInternalState("disconnected");
595
+ return callback(err);
596
+ }
597
+ _handleSuccessfulConnection(callback) {
598
+ debugLog(" Connected successfully to ", this.endpointUrl);
599
+ this.emit("connected");
600
+ this._setInternalState("connected");
601
+ callback();
602
+ }
603
+ connect(...args) {
604
+ const endpointUrl = args[0];
605
+ const callback = args[1];
606
+ (0, node_opcua_assert_1.assert)(typeof callback === "function", "expecting a callback");
607
+ if (typeof endpointUrl !== "string" || endpointUrl.length <= 0) {
608
+ errorLog("[NODE-OPCUA-E03] OPCUAClient#connect expects a valid endpoint : " + endpointUrl);
609
+ callback(new Error("Invalid endpoint"));
610
+ return;
611
+ }
612
+ (0, node_opcua_assert_1.assert)(typeof endpointUrl === "string" && endpointUrl.length > 0);
613
+ // istanbul ignore next
614
+ if (this._internalState !== "disconnected") {
615
+ callback(new Error("invalid internal state = " + this._internalState));
616
+ return;
617
+ }
618
+ // prevent illegal call to connect
619
+ if (this._secureChannel !== null) {
620
+ setImmediate(() => callback(new Error("connect already called")));
621
+ return;
622
+ }
623
+ this._setInternalState("connecting");
624
+ this.disconnecting = false;
625
+ this.initializeCM()
626
+ .then(() => {
627
+ debugLog("ClientBaseImpl#connect ", endpointUrl);
628
+ if (this.disconnecting) {
629
+ return this._handleDisconnectionWhileConnecting(new Error("premature disconnection 1"), callback);
630
+ }
631
+ if (!this.serverCertificate &&
632
+ (forceEndpointDiscoveryOnConnect || this.securityMode !== node_opcua_service_secure_channel_1.MessageSecurityMode.None)) {
633
+ debugLog("Fetching certificates from endpoints");
634
+ this.fetchServerCertificate(endpointUrl, (err, adjustedEndpointUrl) => {
635
+ if (err) {
636
+ return this._handleUnrecoverableConnectionFailure(err, callback);
637
+ }
638
+ if (this.disconnecting) {
639
+ return this._handleDisconnectionWhileConnecting(new Error("premature disconnection 2"), callback);
640
+ }
641
+ if (forceEndpointDiscoveryOnConnect) {
642
+ debugLog("connecting with adjusted endpoint : ", adjustedEndpointUrl, " was =", endpointUrl);
643
+ this._connectStep2(adjustedEndpointUrl, callback);
644
+ }
645
+ else {
646
+ debugLog("connecting with endpoint : ", endpointUrl);
647
+ this._connectStep2(endpointUrl, callback);
648
+ }
649
+ });
650
+ }
651
+ else {
652
+ this._connectStep2(endpointUrl, callback);
653
+ }
654
+ })
655
+ .catch((err) => {
656
+ return this._handleUnrecoverableConnectionFailure(err, callback);
657
+ });
658
+ }
659
+ /**
660
+ * @private
661
+ */
662
+ _connectStep2(endpointUrl, callback) {
663
+ // prevent illegal call to connect
664
+ (0, node_opcua_assert_1.assert)(this._secureChannel === null);
665
+ this.endpointUrl = endpointUrl;
666
+ this._clockAdjuster = this._clockAdjuster || new ClockAdjustment();
667
+ client_base_1.OPCUAClientBase.registry.register(this);
668
+ debugLog("__connectStep2");
669
+ this._internal_create_secure_channel(this.connectionStrategy, (err) => {
670
+ if (!err) {
671
+ this._handleSuccessfulConnection(callback);
672
+ }
673
+ else {
674
+ client_base_1.OPCUAClientBase.registry.unregister(this);
675
+ if (this._clockAdjuster) {
676
+ this._clockAdjuster.dispose();
677
+ this._clockAdjuster = undefined;
678
+ }
679
+ debugLog(chalk.red("SecureChannel creation has failed with error :", err.message));
680
+ if (err.message.match(/ECONNABORTED/)) {
681
+ debugLog(chalk.yellow("- The client cannot to :" + endpointUrl + ". Connection has been aborted."));
682
+ err = new Error("The connection has been aborted");
683
+ this._handleUnrecoverableConnectionFailure(err, callback);
684
+ }
685
+ else if (err.message.match(/ECONNREF/)) {
686
+ debugLog(chalk.yellow("- The client cannot to :" + endpointUrl + ". Server is not reachable."));
687
+ err = new Error("The connection cannot be established with server " +
688
+ endpointUrl +
689
+ " .\n" +
690
+ "Please check that the server is up and running or your network configuration.\n" +
691
+ "Err = (" +
692
+ err.message +
693
+ ")");
694
+ this._handleUnrecoverableConnectionFailure(err, callback);
695
+ }
696
+ else if (err.message.match(/disconnecting/)) {
697
+ /* */
698
+ this._handleDisconnectionWhileConnecting(err, callback);
699
+ }
700
+ else {
701
+ err = new Error("The connection may have been rejected by server,\n" + "Err = (" + err.message + ")");
702
+ this._handleUnrecoverableConnectionFailure(err, callback);
703
+ }
704
+ }
705
+ });
706
+ }
707
+ getClientNonce() {
708
+ return this._secureChannel ? this._secureChannel.getClientNonce() : null;
709
+ }
710
+ performMessageTransaction(request, callback) {
711
+ if (!this._secureChannel) {
712
+ // this may happen if the Server has closed the connection abruptly for some unknown reason
713
+ // or if the tcp connection has been broken.
714
+ return callback(new Error("performMessageTransaction: No SecureChannel , connection may have been canceled abruptly by server"));
715
+ }
716
+ if (this._internalState !== "connected" &&
717
+ this._internalState !== "reconnecting_newchannel_connected" &&
718
+ this._internalState !== "connecting" &&
719
+ this._internalState !== "reconnecting") {
720
+ return callback(new Error("performMessageTransaction: Invalid client state " +
721
+ this._internalState +
722
+ " while performing a transaction " +
723
+ request.constructor.name));
724
+ }
725
+ (0, node_opcua_assert_1.assert)(this._secureChannel);
726
+ (0, node_opcua_assert_1.assert)(request);
727
+ (0, node_opcua_assert_1.assert)(request.requestHeader);
728
+ (0, node_opcua_assert_1.assert)(typeof callback === "function");
729
+ this._secureChannel.performMessageTransaction(request, callback);
730
+ }
731
+ /**
732
+ *
733
+ * return the endpoint information matching security mode and security policy.
734
+ * @method findEndpoint
735
+ */
736
+ findEndpointForSecurity(securityMode, securityPolicy) {
737
+ securityMode = (0, node_opcua_service_secure_channel_1.coerceMessageSecurityMode)(securityMode);
738
+ securityPolicy = (0, node_opcua_secure_channel_1.coerceSecurityPolicy)(securityPolicy);
739
+ (0, node_opcua_assert_1.assert)(this.knowsServerEndpoint, "Server end point are not known yet");
740
+ return this._serverEndpoints.find((endpoint) => {
741
+ return endpoint.securityMode === securityMode && endpoint.securityPolicyUri === securityPolicy;
742
+ });
743
+ }
744
+ /**
745
+ *
746
+ * return the endpoint information matching the specified url , security mode and security policy.
747
+ * @method findEndpoint
748
+ */
749
+ findEndpoint(endpointUrl, securityMode, securityPolicy) {
750
+ (0, node_opcua_assert_1.assert)(this.knowsServerEndpoint, "Server end point are not known yet");
751
+ if (!this._serverEndpoints || this._serverEndpoints.length === 0) {
752
+ return undefined;
753
+ }
754
+ return this._serverEndpoints.find((endpoint) => {
755
+ return ((0, node_opcua_utils_1.matchUri)(endpoint.endpointUrl, endpointUrl) &&
756
+ endpoint.securityMode === securityMode &&
757
+ endpoint.securityPolicyUri === securityPolicy);
758
+ });
759
+ }
760
+ getEndpoints(...args) {
761
+ if (args.length === 1) {
762
+ return this.getEndpoints({}, args[0]);
763
+ }
764
+ const options = args[0];
765
+ const callback = args[1];
766
+ (0, node_opcua_assert_1.assert)(typeof callback === "function");
767
+ options.localeIds = options.localeIds || [];
768
+ options.profileUris = options.profileUris || [];
769
+ const request = new node_opcua_service_endpoints_1.GetEndpointsRequest({
770
+ endpointUrl: options.endpointUrl || this.endpointUrl,
771
+ localeIds: options.localeIds,
772
+ profileUris: options.profileUris,
773
+ requestHeader: {
774
+ auditEntryId: null
775
+ }
776
+ });
777
+ this.performMessageTransaction(request, (err, response) => {
778
+ this._serverEndpoints = [];
779
+ if (err) {
780
+ return callback(err);
781
+ }
782
+ // istanbul ignore next
783
+ if (!response || !(response instanceof node_opcua_service_endpoints_1.GetEndpointsResponse)) {
784
+ return callback(new Error("Internal Error"));
785
+ }
786
+ if (response && response.endpoints) {
787
+ this._serverEndpoints = response.endpoints;
788
+ }
789
+ callback(null, this._serverEndpoints);
790
+ });
791
+ }
792
+ /**
793
+ * @deprecated
794
+ */
795
+ getEndpointsRequest(options, callback) {
796
+ warningLog("note: ClientBaseImpl#getEndpointsRequest is deprecated, use ClientBaseImpl#getEndpoints instead");
797
+ return this.getEndpoints(options, callback);
798
+ }
799
+ findServers(...args) {
800
+ if (!this._secureChannel) {
801
+ setImmediate(() => {
802
+ callback(new Error("Invalid Secure Channel"));
803
+ });
804
+ return;
805
+ }
806
+ if (args.length === 1) {
807
+ return this.findServers({}, args[0]);
808
+ }
809
+ const options = args[0];
810
+ const callback = args[1];
811
+ const request = new node_opcua_service_discovery_1.FindServersRequest({
812
+ endpointUrl: options.endpointUrl || this.endpointUrl,
813
+ localeIds: options.localeIds || [],
814
+ serverUris: options.serverUris || []
815
+ });
816
+ this.performMessageTransaction(request, (err, response) => {
817
+ if (err) {
818
+ return callback(err);
819
+ }
820
+ /* istanbul ignore next */
821
+ if (!response || !(response instanceof node_opcua_service_discovery_1.FindServersResponse)) {
822
+ return callback(new Error("Internal Error"));
823
+ }
824
+ response.servers = response.servers || [];
825
+ callback(null, response.servers);
826
+ });
827
+ }
828
+ findServersOnNetwork(...args) {
829
+ if (args.length === 1) {
830
+ return this.findServersOnNetwork({}, args[0]);
831
+ }
832
+ const options = args[0];
833
+ const callback = args[1];
834
+ if (!this._secureChannel) {
835
+ setImmediate(() => {
836
+ callback(new Error("Invalid Secure Channel"));
837
+ });
838
+ return;
839
+ }
840
+ const request = new node_opcua_service_discovery_1.FindServersOnNetworkRequest(options);
841
+ this.performMessageTransaction(request, (err, response) => {
842
+ if (err) {
843
+ return callback(err);
844
+ }
845
+ /* istanbul ignore next */
846
+ if (!response || !(response instanceof node_opcua_service_discovery_1.FindServersOnNetworkResponse)) {
847
+ return new Error("Internal Error");
848
+ }
849
+ response.servers = response.servers || [];
850
+ callback(null, response.servers);
851
+ });
852
+ }
853
+ _removeSession(session) {
854
+ const index = this._sessions.indexOf(session);
855
+ if (index >= 0) {
856
+ const s = this._sessions.splice(index, 1)[0];
857
+ (0, node_opcua_assert_1.assert)(s === session);
858
+ (0, node_opcua_assert_1.assert)(session._client === this);
859
+ session._client = null;
860
+ }
861
+ (0, node_opcua_assert_1.assert)(this._sessions.indexOf(session) === -1);
862
+ }
863
+ _closeSession(session, deleteSubscriptions, callback) {
864
+ (0, node_opcua_assert_1.assert)(typeof callback === "function");
865
+ (0, node_opcua_assert_1.assert)(typeof deleteSubscriptions === "boolean");
866
+ // istanbul ignore next
867
+ if (!this._secureChannel) {
868
+ return callback(null); // new Error("no channel"));
869
+ }
870
+ (0, node_opcua_assert_1.assert)(this._secureChannel);
871
+ if (!this._secureChannel.isValid()) {
872
+ return callback(null);
873
+ }
874
+ debugLog(chalk.bgWhite.green("_closeSession ") + this._secureChannel.channelId);
875
+ if (this.isReconnecting) {
876
+ errorLog("OPCUAClientImpl#_closeSession called while reconnection in progress ! What shall we do");
877
+ return callback(null);
878
+ }
879
+ const request = new node_opcua_service_session_1.CloseSessionRequest({
880
+ deleteSubscriptions
881
+ });
882
+ session.performMessageTransaction(request, (err, response) => {
883
+ if (err) {
884
+ callback(err);
885
+ }
886
+ else {
887
+ callback(err, response);
888
+ }
889
+ });
890
+ }
891
+ closeSession(...args) {
892
+ const session = args[0];
893
+ const deleteSubscriptions = args[1];
894
+ const callback = args[2];
895
+ (0, node_opcua_assert_1.assert)(typeof deleteSubscriptions === "boolean");
896
+ (0, node_opcua_assert_1.assert)(typeof callback === "function");
897
+ (0, node_opcua_assert_1.assert)(session);
898
+ (0, node_opcua_assert_1.assert)(session._client === this, "session must be attached to this");
899
+ session._closed = true;
900
+ // todo : send close session on secure channel
901
+ this._closeSession(session, deleteSubscriptions, (err, response) => {
902
+ session.emitCloseEvent(node_opcua_status_code_1.StatusCodes.Good);
903
+ this._removeSession(session);
904
+ session.dispose();
905
+ (0, node_opcua_assert_1.assert)(this._sessions.indexOf(session) === -1);
906
+ (0, node_opcua_assert_1.assert)(session._closed, "session must indicate it is closed");
907
+ callback(err ? err : undefined);
908
+ });
909
+ }
910
+ // eslint-disable-next-line max-statements
911
+ disconnect(...args) {
912
+ const callback = args[0];
913
+ (0, node_opcua_assert_1.assert)(typeof callback === "function", "expecting a callback function here");
914
+ if (this._internalState === "disconnected" || this._internalState === "disconnecting") {
915
+ if (this._internalState === "disconnecting") {
916
+ warningLog("[NODE-OPCUA-W26] OPCUAClient#disconnect called while already disconnecting");
917
+ }
918
+ return callback();
919
+ }
920
+ debugLog("disconnecting client! (will set reconnectionIsCanceled to true");
921
+ this._reconnectionIsCanceled = true;
922
+ this.disconnecting = true;
923
+ if (this._tmpClient) {
924
+ this._tmpClient.disconnect((err) => {
925
+ this._tmpClient = undefined;
926
+ (0, node_opcua_assert_1.assert)(!this._tmpClient);
927
+ // retry disconnect on main client
928
+ this.disconnect(callback);
929
+ });
930
+ return;
931
+ }
932
+ debugLog("ClientBaseImpl#disconnect", this.endpointUrl);
933
+ if (this.isReconnecting && !this._reconnectionIsCanceled) {
934
+ debugLog("ClientBaseImpl#disconnect called while reconnection is in progress");
935
+ // let's abort the reconnection process
936
+ this._cancel_reconnection((err) => {
937
+ debugLog("ClientBaseImpl#disconnect reconnection has been canceled", this.applicationName);
938
+ (0, node_opcua_assert_1.assert)(!err, " why would this fail ?");
939
+ // sessions cannot be cancelled properly and must be discarded.
940
+ this.disconnect(callback);
941
+ });
942
+ return;
943
+ }
944
+ if (this._sessions.length && !this.keepPendingSessionsOnDisconnect) {
945
+ debugLog("warning : disconnection : closing pending sessions");
946
+ // disconnect has been called whereas living session exists
947
+ // we need to close them first .... (unless keepPendingSessionsOnDisconnect)
948
+ this._close_pending_sessions(( /*err*/) => {
949
+ this.disconnect(callback);
950
+ });
951
+ return;
952
+ }
953
+ debugLog("Disconnecting !");
954
+ this._setInternalState("disconnecting");
955
+ if (this.clientCertificateManager) {
956
+ const tmp = this.clientCertificateManager;
957
+ // (this as any).clientCertificateManager = null;
958
+ tmp.dispose();
959
+ }
960
+ if (this._sessions.length) {
961
+ // transfer active session to orphan and detach them from channel
962
+ const tmp = [...this._sessions];
963
+ for (const session of tmp) {
964
+ this._removeSession(session);
965
+ }
966
+ this._sessions = [];
967
+ }
968
+ (0, node_opcua_assert_1.assert)(this._sessions.length === 0, " attempt to disconnect a client with live sessions ");
969
+ client_base_1.OPCUAClientBase.registry.unregister(this);
970
+ if (this._clockAdjuster) {
971
+ this._clockAdjuster.dispose();
972
+ this._clockAdjuster = undefined;
973
+ }
974
+ if (this._secureChannel) {
975
+ let tmpChannel = this._secureChannel;
976
+ this._secureChannel = null;
977
+ debugLog("Closing channel");
978
+ tmpChannel.close(() => {
979
+ this._secureChannel = tmpChannel;
980
+ tmpChannel = null;
981
+ this._destroy_secure_channel();
982
+ this._setInternalState("disconnected");
983
+ setImmediate(callback);
984
+ });
985
+ }
986
+ else {
987
+ this.emit("close", null);
988
+ this._setInternalState("disconnected");
989
+ setImmediate(callback);
990
+ }
991
+ }
992
+ // override me !
993
+ _on_connection_reestablished(callback) {
994
+ callback();
995
+ }
996
+ toString() {
997
+ let str = "\n";
998
+ str += " defaultSecureTokenLifetime.... " + this.defaultSecureTokenLifetime + "\n";
999
+ str += " securityMode.................. " + node_opcua_service_secure_channel_1.MessageSecurityMode[this.securityMode] + "\n";
1000
+ str += " securityPolicy................ " + this.securityPolicy.toString() + "\n";
1001
+ str += " certificate fingerprint....... " + (0, node_opcua_crypto_1.makeSHA1Thumbprint)(this.getCertificate()).toString("hex") + "\n";
1002
+ str +=
1003
+ " server certificate fingerprint " +
1004
+ (this.serverCertificate ? (0, node_opcua_crypto_1.makeSHA1Thumbprint)(this.serverCertificate).toString("hex") : "") +
1005
+ "\n";
1006
+ // this.serverCertificate = options.serverCertificate || null + "\n";
1007
+ str += " keepSessionAlive.............. " + this.keepSessionAlive + "\n";
1008
+ str += " bytesRead..................... " + this.bytesRead + "\n";
1009
+ str += " bytesWritten.................. " + this.bytesWritten + "\n";
1010
+ str += " transactionsPerformed......... " + this.transactionsPerformed + "\n";
1011
+ str += " timedOutRequestCount.......... " + this.timedOutRequestCount + "\n";
1012
+ str += " connectionStrategy." + "\n";
1013
+ str += " .maxRetry............... " + this.connectionStrategy.maxRetry + "\n";
1014
+ str += " .initialDelay........... " + this.connectionStrategy.initialDelay + "\n";
1015
+ str += " .maxDelay............... " + this.connectionStrategy.maxDelay + "\n";
1016
+ str += " .randomisationFactor.... " + this.connectionStrategy.randomisationFactor + "\n";
1017
+ str += " keepSessionAlive.............. " + this.keepSessionAlive + "\n";
1018
+ str += " applicationName............... " + this.applicationName + "\n";
1019
+ str += " applicationUri................ " + this._getBuiltApplicationUri() + "\n";
1020
+ str += " clientName.................... " + this.clientName + "\n";
1021
+ if (this._secureChannel) {
1022
+ str += "secureChannel:\n" + this._secureChannel.toString();
1023
+ }
1024
+ return str;
1025
+ }
1026
+ getSessions() {
1027
+ return this._sessions;
1028
+ }
1029
+ _addSession(session) {
1030
+ (0, node_opcua_assert_1.assert)(!session._client || session._client === this);
1031
+ (0, node_opcua_assert_1.assert)(this._sessions.indexOf(session) === -1, "session already added");
1032
+ session._client = this;
1033
+ this._sessions.push(session);
1034
+ }
1035
+ fetchServerCertificate(endpointUrl, callback) {
1036
+ const discoveryUrl = this.discoveryUrl.length > 0 ? this.discoveryUrl : endpointUrl;
1037
+ debugLog("OPCUAClientImpl : getting serverCertificate");
1038
+ // we have not been given the serverCertificate but this certificate
1039
+ // is required as the connection is to be secured.
1040
+ //
1041
+ // Let's explore the server endpoint that matches our security settings
1042
+ // This will give us the missing Certificate as well from the server.
1043
+ // todo :
1044
+ // Once we have the certificate, we cannot trust it straight away
1045
+ // we have to verify that the certificate is valid and not outdated and not revoked.
1046
+ // if the certificate is self-signed the certificate must appear in the trust certificate
1047
+ // list.
1048
+ // if the certificate has been certified by an Certificate Authority we have to
1049
+ // verify that the certificates in the chain are valid and not revoked.
1050
+ //
1051
+ const certificateFile = this.certificateFile;
1052
+ const privateKeyFile = this.privateKeyFile;
1053
+ const applicationName = this.applicationName;
1054
+ const applicationUri = this._applicationUri;
1055
+ const params = {
1056
+ connectionStrategy: this.connectionStrategy,
1057
+ endpointMustExist: false,
1058
+ securityMode: this.securityMode,
1059
+ securityPolicy: this.securityPolicy,
1060
+ applicationName,
1061
+ applicationUri,
1062
+ certificateFile,
1063
+ privateKeyFile,
1064
+ clientCertificateManager: this.clientCertificateManager
1065
+ };
1066
+ return __findEndpoint.call(this, discoveryUrl, params, (err, result) => {
1067
+ if (err) {
1068
+ return callback(err);
1069
+ }
1070
+ // istanbul ignore next
1071
+ if (!result) {
1072
+ const err1 = new Error("internal error");
1073
+ return callback(err1);
1074
+ }
1075
+ const endpoint = result.selectedEndpoint;
1076
+ if (!endpoint) {
1077
+ // no matching end point can be found ...
1078
+ const err1 = new Error("cannot find endpoint for securityMode=" +
1079
+ node_opcua_service_secure_channel_1.MessageSecurityMode[this.securityMode] +
1080
+ " policy = " +
1081
+ this.securityPolicy);
1082
+ return callback(err1);
1083
+ }
1084
+ (0, node_opcua_assert_1.assert)(endpoint);
1085
+ _verify_serverCertificate(this.clientCertificateManager, endpoint.serverCertificate, (err1) => {
1086
+ if (err1) {
1087
+ warningLog("[NODE-OPCUA-W25] client's server certificate verification has failed ", err1.message);
1088
+ warningLog(" ", this.clientCertificateManager.rootDir);
1089
+ warningLog(" ", endpoint.serverCertificate.toString("base64").replace(/(.{80})/g, "$1\n "));
1090
+ warningLog(" verify that server certificate is trusted or that server certificate issuer's certificate is present in the issuer folder");
1091
+ return callback(err1);
1092
+ }
1093
+ this.serverCertificate = endpoint.serverCertificate;
1094
+ callback(null, endpoint.endpointUrl);
1095
+ });
1096
+ });
1097
+ }
1098
+ _accumulate_statistics() {
1099
+ if (this._secureChannel) {
1100
+ // keep accumulated statistics
1101
+ this._byteWritten += this._secureChannel.bytesWritten;
1102
+ this._byteRead += this._secureChannel.bytesRead;
1103
+ this._transactionsPerformed += this._secureChannel.transactionsPerformed;
1104
+ this._timedOutRequestCount += this._secureChannel.timedOutRequestCount;
1105
+ if (doDebug) {
1106
+ debugLog(chalk.cyan(` Client ${this._instanceNumber} ${this.clientName} byteWritten = `), this._byteWritten);
1107
+ debugLog(chalk.cyan(` Client ${this._instanceNumber} ${this.clientName} byteRead = `), this._byteRead);
1108
+ debugLog(chalk.cyan(` Client ${this._instanceNumber} ${this.clientName} transactions = `), this._transactionsPerformed);
1109
+ debugLog(chalk.cyan(` Client ${this._instanceNumber} ${this.clientName} timedOutRequestCount = `), this._timedOutRequestCount);
1110
+ }
1111
+ }
1112
+ }
1113
+ _destroy_secure_channel() {
1114
+ if (this._secureChannel) {
1115
+ if (doDebug) {
1116
+ debugLog(" DESTROYING SECURE CHANNEL (isTransactionInProgress ?", this._secureChannel.isTransactionInProgress(), ")");
1117
+ }
1118
+ this._accumulate_statistics();
1119
+ const secureChannel = this._secureChannel;
1120
+ this._secureChannel = null;
1121
+ secureChannel.dispose();
1122
+ secureChannel.removeAllListeners();
1123
+ }
1124
+ }
1125
+ _close_pending_sessions(callback) {
1126
+ const sessions = [...this._sessions];
1127
+ async.map(sessions, (session, next) => {
1128
+ (0, node_opcua_assert_1.assert)(session._client === this);
1129
+ // note: to prevent next to be call twice
1130
+ let _next_already_call = false;
1131
+ session.close((err) => {
1132
+ if (_next_already_call) {
1133
+ return;
1134
+ }
1135
+ _next_already_call = true;
1136
+ // We should not bother if we have an error here
1137
+ // Session may fail to close , if they haven't been activate and forcefully closed by server
1138
+ // in a attempt to preserve resources in the case of a DDOS attack for instance.
1139
+ if (err) {
1140
+ const msg = session.authenticationToken ? session.authenticationToken.toString() : "";
1141
+ debugLog(" failing to close session " + msg);
1142
+ }
1143
+ next();
1144
+ });
1145
+ }, (err) => {
1146
+ // istanbul ignore next
1147
+ if (this._sessions.length > 0) {
1148
+ debugLog(this._sessions
1149
+ .map((s) => (s.authenticationToken ? s.authenticationToken.toString() : ""))
1150
+ .join(" "));
1151
+ }
1152
+ (0, node_opcua_assert_1.assert)(this._sessions.length === 0, " failed to disconnect exiting sessions ");
1153
+ callback(err);
1154
+ });
1155
+ }
1156
+ _install_secure_channel_event_handlers(secureChannel) {
1157
+ (0, node_opcua_assert_1.assert)(this instanceof ClientBaseImpl);
1158
+ secureChannel.on("send_chunk", (chunk) => {
1159
+ /**
1160
+ * notify the observer that a message_chunk has been sent
1161
+ * @event send_chunk
1162
+ * @param message_chunk
1163
+ */
1164
+ this.emit("send_chunk", chunk);
1165
+ });
1166
+ secureChannel.on("receive_chunk", (chunk) => {
1167
+ /**
1168
+ * notify the observer that a message_chunk has been received
1169
+ * @event receive_chunk
1170
+ * @param message_chunk
1171
+ */
1172
+ this.emit("receive_chunk", chunk);
1173
+ });
1174
+ secureChannel.on("send_request", (message) => {
1175
+ /**
1176
+ * notify the observer that a request has been sent to the server.
1177
+ * @event send_request
1178
+ * @param message
1179
+ */
1180
+ this.emit("send_request", message);
1181
+ });
1182
+ secureChannel.on("receive_response", (message) => {
1183
+ /**
1184
+ * notify the observer that a response has been received from the server.
1185
+ * @event receive_response
1186
+ * @param message
1187
+ */
1188
+ this.emit("receive_response", message);
1189
+ });
1190
+ secureChannel.on("lifetime_75", (token) => {
1191
+ // secureChannel requests a new token
1192
+ debugLog("SecureChannel Security Token ", token.tokenId, "live time was =", token.revisedLifetime, " is about to expired , it's time to request a new token");
1193
+ // forward message to upper level
1194
+ this.emit("lifetime_75", token, secureChannel);
1195
+ });
1196
+ secureChannel.on("security_token_renewed", () => {
1197
+ // forward message to upper level
1198
+ this.emit("security_token_renewed", secureChannel);
1199
+ });
1200
+ secureChannel.on("close", (err) => {
1201
+ debugLog(chalk.yellow.bold(" ClientBaseImpl emitting close"), err === null || err === void 0 ? void 0 : err.message);
1202
+ this._destroy_secure_channel();
1203
+ if (!err || !this.reconnectOnFailure) {
1204
+ // this is a normal close operation initiated by us
1205
+ /**
1206
+ * @event close
1207
+ * @param error
1208
+ */
1209
+ this.emit("close", err);
1210
+ }
1211
+ else {
1212
+ /**
1213
+ * @event connection_lost
1214
+ */
1215
+ // this.emit("close", err);
1216
+ if (this.reconnectOnFailure && this._internalState !== "reconnecting") {
1217
+ debugLog(" ClientBaseImpl emitting connection_lost");
1218
+ this.emit("connection_lost", err === null || err === void 0 ? void 0 : err.message); // instead of "close"
1219
+ this._repairConnection();
1220
+ }
1221
+ }
1222
+ });
1223
+ secureChannel.on("timed_out_request", (request) => {
1224
+ /**
1225
+ * send when a request has timed out without receiving a response
1226
+ * @event timed_out_request
1227
+ * @param request
1228
+ */
1229
+ this.emit("timed_out_request", request);
1230
+ });
1231
+ }
1232
+ _repairConnection() {
1233
+ if (this._inside_repairConnection) {
1234
+ errorLog("_repairConnection already in progress ", this._internalState);
1235
+ return;
1236
+ }
1237
+ this._inside_repairConnection = true;
1238
+ debugLog("recreating new secure channel ", this._internalState);
1239
+ this._recreate_secure_channel((err1) => {
1240
+ debugLog("secureChannel#on(close) => _recreate_secure_channel returns ", err1 ? err1.message : "OK");
1241
+ if (err1) {
1242
+ errorLog("_recreate_secure_channel has failed: err = ", err1.message);
1243
+ this.emit("close", err1);
1244
+ this._setInternalState("disconnected");
1245
+ this._inside_repairConnection = false;
1246
+ return;
1247
+ }
1248
+ else {
1249
+ this._finalReconnectionStep((err2) => {
1250
+ if (err2) {
1251
+ // istanbul ignore next
1252
+ if (doDebug) {
1253
+ debugLog("connection_reestablished has failed");
1254
+ debugLog("err= ", err2.message);
1255
+ }
1256
+ // we still need to retry connecting here !!!
1257
+ debugLog("Disconnected following reconnection failure", err2.message);
1258
+ debugLog(`I will retry OPCUA client reconnection in ${client_base_1.OPCUAClientBase.retryDelay / 1000} seconds`);
1259
+ this._inside_repairConnection = false;
1260
+ this._destroy_secure_channel();
1261
+ setTimeout(() => this._repairConnection(), client_base_1.OPCUAClientBase.retryDelay);
1262
+ return;
1263
+ }
1264
+ else {
1265
+ /**
1266
+ * @event connection_reestablished
1267
+ * send when the connection is reestablished after a connection break
1268
+ */
1269
+ this.emit("connection_reestablished");
1270
+ this._setInternalState("connected");
1271
+ this._inside_repairConnection = false;
1272
+ }
1273
+ });
1274
+ }
1275
+ });
1276
+ }
1277
+ _finalReconnectionStep(callback) {
1278
+ // now delegate to upper class the
1279
+ if (this._on_connection_reestablished) {
1280
+ (0, node_opcua_assert_1.assert)(typeof this._on_connection_reestablished === "function");
1281
+ this._on_connection_reestablished((err2) => {
1282
+ callback(err2);
1283
+ });
1284
+ }
1285
+ else {
1286
+ callback();
1287
+ }
1288
+ }
1289
+ /**
1290
+ *
1291
+ * @internal
1292
+ * @private
1293
+ */
1294
+ __createSession_step2(session, callback) {
1295
+ throw new Error("Please override");
1296
+ }
1297
+ _activateSession(session, callback) {
1298
+ throw new Error("Please override");
1299
+ }
1300
+ }
1301
+ exports.ClientBaseImpl = ClientBaseImpl;
1302
+ // tslint:disable-next-line: max-classes-per-file
1303
+ class TmpClient extends ClientBaseImpl {
1304
+ constructor(options) {
1305
+ options.clientName = (options.clientName || "") + "_TmpClient";
1306
+ super(options);
1307
+ }
1308
+ connect(endpoint, callback) {
1309
+ debugLog("connecting to TmpClient");
1310
+ // istanbul ignore next
1311
+ if (this._internalState !== "disconnected") {
1312
+ callback(new Error("TmpClient#connect: invalid internal state " + this._internalState));
1313
+ return;
1314
+ }
1315
+ if (this.disconnecting) {
1316
+ return this._handleUnrecoverableConnectionFailure(new Error("premature disconnection 3"), callback);
1317
+ }
1318
+ this._setInternalState("connecting");
1319
+ this._connectStep2(endpoint, (err) => {
1320
+ if (this.disconnecting) {
1321
+ return this._handleUnrecoverableConnectionFailure(new Error("premature disconnection 4"), callback);
1322
+ }
1323
+ callback(err);
1324
+ });
1325
+ }
1326
+ }
1327
+ // tslint:disable:no-var-requires
1328
+ // tslint:disable:max-line-length
1329
+ const thenify = require("thenify");
1330
+ ClientBaseImpl.prototype.connect = thenify.withCallback(ClientBaseImpl.prototype.connect);
1331
+ ClientBaseImpl.prototype.disconnect = thenify.withCallback(ClientBaseImpl.prototype.disconnect);
1332
+ ClientBaseImpl.prototype.getEndpoints = thenify.withCallback(ClientBaseImpl.prototype.getEndpoints);
1333
+ ClientBaseImpl.prototype.findServers = thenify.withCallback(ClientBaseImpl.prototype.findServers);
1334
+ ClientBaseImpl.prototype.findServersOnNetwork = thenify.withCallback(ClientBaseImpl.prototype.findServersOnNetwork);
1335
+ client_base_1.OPCUAClientBase.create = (options) => {
1336
+ return new ClientBaseImpl(options);
1337
+ };
1338
1338
  //# sourceMappingURL=client_base_impl.js.map