node-opcua-client 2.177.0 → 2.179.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/alarms_and_conditions/client_alarm_tools.js +7 -5
- package/dist/alarms_and_conditions/client_alarm_tools.js.map +1 -1
- package/dist/alarms_and_conditions/client_tools.js +38 -28
- package/dist/alarms_and_conditions/client_tools.js.map +1 -1
- package/dist/client_base.d.ts +5 -0
- package/dist/client_base.js.map +1 -1
- package/dist/client_monitored_item.d.ts +1 -1
- package/dist/client_monitored_item.js +4 -2
- package/dist/client_monitored_item.js.map +1 -1
- package/dist/client_monitored_item_group.d.ts +4 -3
- package/dist/client_monitored_item_group.js +8 -5
- package/dist/client_monitored_item_group.js.map +1 -1
- package/dist/client_monitored_item_toolbox.js +4 -2
- package/dist/client_monitored_item_toolbox.js.map +1 -1
- package/dist/client_session.d.ts +9 -2
- package/dist/client_session_keepalive_manager.d.ts +1 -1
- package/dist/client_session_keepalive_manager.js +50 -29
- package/dist/client_session_keepalive_manager.js.map +1 -1
- package/dist/client_subscription.d.ts +2 -2
- package/dist/client_subscription.js +3 -1
- package/dist/client_subscription.js.map +1 -1
- package/dist/common.d.ts +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/opcua_client.js +1 -0
- package/dist/opcua_client.js.map +1 -1
- package/dist/private/client_base_impl.js +178 -77
- package/dist/private/client_base_impl.js.map +1 -1
- package/dist/private/client_monitored_item_group_impl.d.ts +1 -0
- package/dist/private/client_monitored_item_group_impl.js +6 -8
- package/dist/private/client_monitored_item_group_impl.js.map +1 -1
- package/dist/private/client_monitored_item_impl.js +0 -4
- package/dist/private/client_monitored_item_impl.js.map +1 -1
- package/dist/private/client_publish_engine.js +62 -28
- package/dist/private/client_publish_engine.js.map +1 -1
- package/dist/private/client_session_impl.d.ts +49 -17
- package/dist/private/client_session_impl.js +240 -143
- package/dist/private/client_session_impl.js.map +1 -1
- package/dist/private/client_subscription_impl.js +15 -12
- package/dist/private/client_subscription_impl.js.map +1 -1
- package/dist/private/i_private_client.d.ts +1 -1
- package/dist/private/opcua_client_impl.js +44 -9
- package/dist/private/opcua_client_impl.js.map +1 -1
- package/dist/private/reconnection/client_publish_engine_reconnection.js +17 -7
- package/dist/private/reconnection/client_publish_engine_reconnection.js.map +1 -1
- package/dist/private/reconnection/client_subscription_reconnection.js +11 -7
- package/dist/private/reconnection/client_subscription_reconnection.js.map +1 -1
- package/dist/private/reconnection/reconnection.js +31 -14
- package/dist/private/reconnection/reconnection.js.map +1 -1
- package/dist/reverse/client_reverse_connect.js.map +1 -1
- package/dist/tools/findservers.js.map +1 -1
- package/dist/tools/read_history_server_capabilities.d.ts +2 -1
- package/dist/tools/read_history_server_capabilities.js +6 -3
- package/dist/tools/read_history_server_capabilities.js.map +1 -1
- package/package.json +50 -50
- package/source/alarms_and_conditions/client_alarm_tools.ts +10 -8
- package/source/alarms_and_conditions/client_tools.ts +71 -30
- package/source/client_base.ts +5 -1
- package/source/client_monitored_item.ts +10 -9
- package/source/client_monitored_item_base.ts +2 -8
- package/source/client_monitored_item_group.ts +13 -10
- package/source/client_monitored_item_toolbox.ts +4 -2
- package/source/client_session.ts +10 -3
- package/source/client_session_keepalive_manager.ts +72 -55
- package/source/client_subscription.ts +6 -3
- package/source/common.ts +4 -0
- package/source/index.ts +1 -1
- package/source/opcua_client.ts +1 -0
- package/source/private/client_base_impl.ts +244 -113
- package/source/private/client_monitored_item_group_impl.ts +17 -18
- package/source/private/client_monitored_item_impl.ts +0 -4
- package/source/private/client_publish_engine.ts +78 -40
- package/source/private/client_session_impl.ts +393 -236
- package/source/private/client_subscription_impl.ts +20 -25
- package/source/private/i_private_client.ts +1 -1
- package/source/private/opcua_client_impl.ts +51 -9
- package/source/private/reconnection/client_publish_engine_reconnection.ts +22 -21
- package/source/private/reconnection/client_subscription_reconnection.ts +12 -8
- package/source/private/reconnection/reconnection.ts +35 -18
- package/source/reverse/client_reverse_connect.ts +11 -3
- package/source/tools/findservers.ts +0 -1
- package/source/tools/read_history_server_capabilities.ts +9 -7
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module node-opcua-client-private
|
|
3
3
|
*/
|
|
4
|
-
// tslint:disable:no-unused-expression
|
|
5
4
|
import fs from "node:fs";
|
|
6
5
|
import path from "node:path";
|
|
7
6
|
|
|
@@ -10,7 +9,16 @@ import { withLock } from "@ster5/global-mutex";
|
|
|
10
9
|
import chalk from "chalk";
|
|
11
10
|
import { assert } from "node-opcua-assert";
|
|
12
11
|
import { getDefaultCertificateManager, type OPCUACertificateManager } from "node-opcua-certificate-manager";
|
|
13
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
type ICertificateStore,
|
|
14
|
+
InMemoryCertificateStore,
|
|
15
|
+
type IOPCUASecureObjectOptions,
|
|
16
|
+
makeApplicationUrn,
|
|
17
|
+
makeSubject,
|
|
18
|
+
OPCUASecureObject,
|
|
19
|
+
resolvePrivateKeyProviderIfNeeded
|
|
20
|
+
} from "node-opcua-common";
|
|
21
|
+
import { PrivateKeyPassphraseRequiredError } from "node-opcua-crypto";
|
|
14
22
|
import { type Certificate, makeSHA1Thumbprint, split_der } from "node-opcua-crypto/web";
|
|
15
23
|
import { installPeriodicClockAdjustment, periodicClockAdjustment, uninstallPeriodicClockAdjustment } from "node-opcua-date-time";
|
|
16
24
|
import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
|
|
@@ -27,7 +35,6 @@ import {
|
|
|
27
35
|
type Response as Response1,
|
|
28
36
|
type SecurityPolicy
|
|
29
37
|
} from "node-opcua-secure-channel";
|
|
30
|
-
import { type IAcceptedReverseConnection, type IClientTransportFactory, makeReverseClientTransportFactory } from "node-opcua-transport";
|
|
31
38
|
import {
|
|
32
39
|
FindServersOnNetworkRequest,
|
|
33
40
|
type FindServersOnNetworkRequestOptions,
|
|
@@ -45,6 +52,11 @@ import {
|
|
|
45
52
|
import { type ChannelSecurityToken, coerceMessageSecurityMode, MessageSecurityMode } from "node-opcua-service-secure-channel";
|
|
46
53
|
import { CloseSessionRequest, type CloseSessionResponse } from "node-opcua-service-session";
|
|
47
54
|
import { type ErrorCallback, StatusCodes } from "node-opcua-status-code";
|
|
55
|
+
import {
|
|
56
|
+
type IAcceptedReverseConnection,
|
|
57
|
+
type IClientTransportFactory,
|
|
58
|
+
makeReverseClientTransportFactory
|
|
59
|
+
} from "node-opcua-transport";
|
|
48
60
|
import { checkFileExistsAndIsNotEmpty, matchUri } from "node-opcua-utils";
|
|
49
61
|
import {
|
|
50
62
|
type CreateSecureChannelCallbackFunc,
|
|
@@ -116,7 +128,15 @@ function __findEndpoint(this: ClientBaseImpl, endpointUrl: string, params: FindE
|
|
|
116
128
|
// maxRetry: 0 /* no- retry */,
|
|
117
129
|
// maxDelay: 2000
|
|
118
130
|
// },
|
|
119
|
-
privateKeyFile: params.privateKeyFile
|
|
131
|
+
privateKeyFile: params.privateKeyFile,
|
|
132
|
+
|
|
133
|
+
// TmpClient.connect() (below) calls _connectStep2() directly and never
|
|
134
|
+
// runs initializeCM() — so it never resolves a passphrase-encrypted
|
|
135
|
+
// private key on its own. Hand it the master client's own provider
|
|
136
|
+
// (already resolved, if needed) instead of letting it build a fresh
|
|
137
|
+
// disk provider from certificateFile/privateKeyFile, which would
|
|
138
|
+
// re-read the file and fail synchronously on an encrypted key.
|
|
139
|
+
certificateKeyPairProvider: masterClient.getProvider()
|
|
120
140
|
};
|
|
121
141
|
|
|
122
142
|
const client = new TmpClient(options);
|
|
@@ -205,10 +225,10 @@ function __findEndpoint(this: ClientBaseImpl, endpointUrl: string, params: FindE
|
|
|
205
225
|
return callback(
|
|
206
226
|
new Error(
|
|
207
227
|
"Cannot find an Endpoint matching " +
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
228
|
+
" security mode: " +
|
|
229
|
+
securityMode.toString() +
|
|
230
|
+
" policy: " +
|
|
231
|
+
securityPolicy.toString()
|
|
212
232
|
)
|
|
213
233
|
);
|
|
214
234
|
}
|
|
@@ -246,16 +266,19 @@ async function _verify_serverCertificate(certificateManager: ICertificateStore,
|
|
|
246
266
|
}
|
|
247
267
|
|
|
248
268
|
const forceEndpointDiscoveryOnConnect = !!parseInt(process.env.NODEOPCUA_CLIENT_FORCE_ENDPOINT_DISCOVERY || "0", 10);
|
|
249
|
-
|
|
269
|
+
// c8 ignore next
|
|
270
|
+
doDebug && debugLog("forceEndpointDiscoveryOnConnect = ", forceEndpointDiscoveryOnConnect);
|
|
250
271
|
|
|
251
272
|
class ClockAdjustment {
|
|
252
273
|
constructor() {
|
|
253
|
-
|
|
274
|
+
// c8 ignore next
|
|
275
|
+
doDebug && debugLog("installPeriodicClockAdjustment ", periodicClockAdjustment.timerInstallationCount);
|
|
254
276
|
installPeriodicClockAdjustment();
|
|
255
277
|
}
|
|
256
278
|
dispose() {
|
|
257
279
|
uninstallPeriodicClockAdjustment();
|
|
258
|
-
|
|
280
|
+
// c8 ignore next
|
|
281
|
+
doDebug && debugLog("uninstallPeriodicClockAdjustment ", periodicClockAdjustment.timerInstallationCount);
|
|
259
282
|
}
|
|
260
283
|
}
|
|
261
284
|
|
|
@@ -294,10 +317,10 @@ let g_ClientCounter = 0;
|
|
|
294
317
|
/**
|
|
295
318
|
* @internal
|
|
296
319
|
*/
|
|
297
|
-
// tslint:disable-next-line: max-classes-per-file
|
|
298
320
|
export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBaseEvents>
|
|
299
321
|
extends OPCUASecureObject<Events>
|
|
300
|
-
implements OPCUAClientBase<Events>, IClientBase
|
|
322
|
+
implements OPCUAClientBase<Events>, IClientBase
|
|
323
|
+
{
|
|
301
324
|
/**
|
|
302
325
|
* total number of requests that been canceled due to timeout
|
|
303
326
|
*/
|
|
@@ -396,6 +419,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
396
419
|
private _reverseConnectRegistration?: ICancelableRegistration;
|
|
397
420
|
|
|
398
421
|
public clientCertificateManager: ICertificateStore;
|
|
422
|
+
/** true when the caller supplied a certificateKeyPairProvider — the private-key resolution escape hatch. */
|
|
423
|
+
#hasUserProvidedProvider = false;
|
|
399
424
|
|
|
400
425
|
public isUnusable() {
|
|
401
426
|
return (
|
|
@@ -438,6 +463,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
438
463
|
constructor(options?: OPCUAClientBaseOptions) {
|
|
439
464
|
options = options || {};
|
|
440
465
|
|
|
466
|
+
const hasUserProvidedProvider = !!options.certificateKeyPairProvider;
|
|
467
|
+
|
|
441
468
|
if (options.certificateKeyPairProvider) {
|
|
442
469
|
// In-memory path — use a lightweight in-memory store
|
|
443
470
|
// when no cert manager was explicitly provided.
|
|
@@ -452,11 +479,12 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
452
479
|
}
|
|
453
480
|
const cm = options.clientCertificateManager as OPCUACertificateManager;
|
|
454
481
|
options.privateKeyFile = options.privateKeyFile || cm.privateKey;
|
|
455
|
-
options.certificateFile =
|
|
456
|
-
options.certificateFile || path.join(cm.rootDir, "own/certs/client_certificate.pem");
|
|
482
|
+
options.certificateFile = options.certificateFile || path.join(cm.rootDir, "own/certs/client_certificate.pem");
|
|
457
483
|
super(options as IOPCUASecureObjectOptions);
|
|
458
484
|
}
|
|
459
485
|
|
|
486
|
+
this.#hasUserProvidedProvider = hasUserProvidedProvider;
|
|
487
|
+
|
|
460
488
|
this._setInternalState("uninitialized");
|
|
461
489
|
|
|
462
490
|
this._instanceNumber = g_ClientCounter++;
|
|
@@ -468,7 +496,10 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
468
496
|
// we need to delay _applicationUri initialization
|
|
469
497
|
this._applicationUri = options.applicationUri || this._getBuiltApplicationUri();
|
|
470
498
|
|
|
471
|
-
|
|
499
|
+
if (!options.clientCertificateManager) {
|
|
500
|
+
throw new Error("internal error: clientCertificateManager should have been set above");
|
|
501
|
+
}
|
|
502
|
+
this.clientCertificateManager = options.clientCertificateManager;
|
|
472
503
|
this.clientCertificateManager.referenceCounter++;
|
|
473
504
|
|
|
474
505
|
this._secureChannel = null;
|
|
@@ -532,13 +563,16 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
532
563
|
warningLog("internal error: _cancel_reconnection should only be used when reconnecting is in progress");
|
|
533
564
|
}
|
|
534
565
|
|
|
535
|
-
|
|
566
|
+
// c8 ignore next
|
|
567
|
+
doDebug && debugLog("canceling reconnection : ", this.clientName);
|
|
536
568
|
|
|
537
569
|
this._reconnectionIsCanceled = true;
|
|
538
570
|
|
|
539
571
|
// c8 ignore next
|
|
540
572
|
if (!this._secureChannel) {
|
|
541
|
-
|
|
573
|
+
// c8 ignore next
|
|
574
|
+
doDebug &&
|
|
575
|
+
debugLog("_cancel_reconnection: Nothing to do for !", this.clientName, " because secure channel doesn't exist");
|
|
542
576
|
return callback(); // nothing to do
|
|
543
577
|
}
|
|
544
578
|
|
|
@@ -549,10 +583,12 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
549
583
|
}
|
|
550
584
|
|
|
551
585
|
public _recreate_secure_channel(callback: ErrorCallback): void {
|
|
552
|
-
|
|
586
|
+
// c8 ignore next
|
|
587
|
+
doDebug && debugLog("_recreate_secure_channel... while internalState is", this._internalState);
|
|
553
588
|
|
|
554
589
|
if (!this.knowsServerEndpoint) {
|
|
555
|
-
|
|
590
|
+
// c8 ignore next
|
|
591
|
+
doDebug && debugLog("Cannot reconnect , server endpoint is unknown");
|
|
556
592
|
callback(new Error("Cannot reconnect, server endpoint is unknown - this.knowsServerEndpoint = false"));
|
|
557
593
|
return;
|
|
558
594
|
}
|
|
@@ -579,7 +615,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
579
615
|
};
|
|
580
616
|
|
|
581
617
|
const _failAndRetry = (err: Error, message: string, callback: ErrorCallback) => {
|
|
582
|
-
|
|
618
|
+
// c8 ignore next
|
|
619
|
+
doDebug && debugLog("failAndRetry; ", message);
|
|
583
620
|
if (this._reconnectionIsCanceled) {
|
|
584
621
|
return _when_reconnectionIsCanceled(callback);
|
|
585
622
|
}
|
|
@@ -595,13 +632,15 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
595
632
|
this.emit("after_reconnection", null); // send after callback
|
|
596
633
|
assert(this._secureChannel, "expecting a secureChannel here ");
|
|
597
634
|
// a new channel has be created and a new connection is established
|
|
598
|
-
|
|
635
|
+
// c8 ignore next
|
|
636
|
+
doDebug && debugLog(chalk.bgWhite.red("ClientBaseImpl: RECONNECTED !!!"));
|
|
599
637
|
this._setInternalState("reconnecting_newchannel_connected");
|
|
600
638
|
return callback();
|
|
601
639
|
};
|
|
602
640
|
|
|
603
641
|
const _attempt_to_recreate_secure_channel = (callback: ErrorCallback) => {
|
|
604
|
-
|
|
642
|
+
// c8 ignore next
|
|
643
|
+
doDebug && debugLog("attempt to recreate a new secure channel");
|
|
605
644
|
if (this._reconnectionIsCanceled) {
|
|
606
645
|
return _when_reconnectionIsCanceled(callback);
|
|
607
646
|
}
|
|
@@ -704,11 +743,13 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
704
743
|
assert(this._secureChannel === null);
|
|
705
744
|
assert(typeof this.endpointUrl === "string");
|
|
706
745
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
746
|
+
// c8 ignore next
|
|
747
|
+
doDebug &&
|
|
748
|
+
debugLog(
|
|
749
|
+
"_internal_create_secure_channel creating new ClientSecureChannelLayer _internalState =",
|
|
750
|
+
this._internalState,
|
|
751
|
+
this.clientName
|
|
752
|
+
);
|
|
712
753
|
const secureChannel = new ClientSecureChannelLayer({
|
|
713
754
|
connectionStrategy,
|
|
714
755
|
defaultSecureTokenLifetime: this.defaultSecureTokenLifetime,
|
|
@@ -738,12 +779,15 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
738
779
|
|
|
739
780
|
const step2_openSecureChannel = (): Promise<void> => {
|
|
740
781
|
return new Promise<void>((resolve, reject) => {
|
|
741
|
-
|
|
782
|
+
// c8 ignore next
|
|
783
|
+
doDebug && debugLog("_internal_create_secure_channel before secureChannel.create");
|
|
742
784
|
|
|
743
785
|
secureChannel.create(this.endpointUrl, (err?: Error) => {
|
|
744
|
-
|
|
786
|
+
// c8 ignore next
|
|
787
|
+
doDebug && debugLog("_internal_create_secure_channel after secureChannel.create");
|
|
745
788
|
if (!this._secureChannel) {
|
|
746
|
-
|
|
789
|
+
// c8 ignore next
|
|
790
|
+
doDebug && debugLog("_secureChannel has been closed during the transaction !");
|
|
747
791
|
return reject(new Error("Secure Channel Closed"));
|
|
748
792
|
}
|
|
749
793
|
if (err) {
|
|
@@ -763,7 +807,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
763
807
|
|
|
764
808
|
this.getEndpoints((err: Error | null /*, endpoints?: EndpointDescription[]*/) => {
|
|
765
809
|
if (!this._secureChannel) {
|
|
766
|
-
|
|
810
|
+
// c8 ignore next
|
|
811
|
+
doDebug && debugLog("_secureChannel has been closed during the transaction ! (while getEndpoints)");
|
|
767
812
|
return reject(new Error("Secure Channel Closed"));
|
|
768
813
|
}
|
|
769
814
|
if (err) {
|
|
@@ -847,13 +892,25 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
847
892
|
this.applicationName,
|
|
848
893
|
this._getBuiltApplicationUri()
|
|
849
894
|
);
|
|
850
|
-
|
|
895
|
+
// c8 ignore next
|
|
896
|
+
doDebug && debugLog("privateKey = ", this.privateKeyFile);
|
|
851
897
|
if ("privateKey" in this.clientCertificateManager) {
|
|
852
|
-
|
|
898
|
+
// c8 ignore next
|
|
899
|
+
doDebug &&
|
|
900
|
+
debugLog(
|
|
901
|
+
" = ",
|
|
902
|
+
(this.clientCertificateManager as unknown as OPCUACertificateManager).privateKey
|
|
903
|
+
);
|
|
853
904
|
}
|
|
854
|
-
|
|
905
|
+
// c8 ignore next
|
|
906
|
+
doDebug && debugLog("certificateFile = ", this.certificateFile);
|
|
855
907
|
const _certificate = this.getCertificate();
|
|
856
|
-
|
|
908
|
+
// Note: do NOT eagerly call this.getPrivateKey() here. A freshly
|
|
909
|
+
// generated key may already be passphrase-encrypted on disk (when
|
|
910
|
+
// clientCertificateManager is configured with privateKeyPassphrase),
|
|
911
|
+
// and the disk provider's getPrivateKey() is synchronous — it cannot
|
|
912
|
+
// decrypt. initializeCM() resolves the private key asynchronously
|
|
913
|
+
// (once) right after this method returns.
|
|
857
914
|
});
|
|
858
915
|
}
|
|
859
916
|
(this as unknown as { _inCreateDefaultCertificate: boolean })._inCreateDefaultCertificate = false;
|
|
@@ -872,22 +929,48 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
872
929
|
// disconnect
|
|
873
930
|
errorLog(
|
|
874
931
|
"[NODE-OPCUA-E08] initializeCM: clientCertificateManager is null\n" +
|
|
875
|
-
|
|
876
|
-
|
|
932
|
+
" This happen when you disconnected the client, to free resources.\n" +
|
|
933
|
+
" Please create a new OPCUAClient instance if you want to reconnect"
|
|
877
934
|
);
|
|
878
935
|
return;
|
|
879
936
|
}
|
|
880
|
-
|
|
937
|
+
// The pki-level initialize() itself already decrypts (or re-encrypts)
|
|
938
|
+
// the on-disk key when clientCertificateManager is configured with a
|
|
939
|
+
// privateKeyPassphrase, and fails closed with
|
|
940
|
+
// PrivateKeyPassphraseRequiredError on a mismatch — so this whole
|
|
941
|
+
// sequence, not just the resolve step below, needs the friendlier
|
|
942
|
+
// error message.
|
|
943
|
+
try {
|
|
944
|
+
await this.clientCertificateManager.initialize();
|
|
945
|
+
|
|
946
|
+
if (this.certificateFile === "<in-memory>" || this.certificateFile === "<unknown>") {
|
|
947
|
+
// In-memory provider — cert already available, skip disk operations
|
|
948
|
+
} else {
|
|
949
|
+
// Disk path — create default cert if missing
|
|
950
|
+
await this.createDefaultCertificate();
|
|
951
|
+
// c8 ignore next
|
|
952
|
+
if (!fs.existsSync(this.privateKeyFile)) {
|
|
953
|
+
throw new Error(` cannot locate private key file ${this.privateKeyFile}`);
|
|
954
|
+
}
|
|
881
955
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
956
|
+
// Resolve the (possibly passphrase-encrypted) private key once,
|
|
957
|
+
// asynchronously, and install it as this client's provider — so
|
|
958
|
+
// that every later, synchronous getPrivateKey() call (secure
|
|
959
|
+
// channel layer, ...) succeeds without touching disk again.
|
|
960
|
+
// No-ops when the caller supplied its own
|
|
961
|
+
// certificateKeyPairProvider, or when clientCertificateManager
|
|
962
|
+
// doesn't expose an async getPrivateKey() (e.g. a bare
|
|
963
|
+
// ICertificateStore).
|
|
964
|
+
await resolvePrivateKeyProviderIfNeeded(this, this.clientCertificateManager, this.#hasUserProvidedProvider);
|
|
890
965
|
}
|
|
966
|
+
} catch (err) {
|
|
967
|
+
if (err instanceof PrivateKeyPassphraseRequiredError) {
|
|
968
|
+
throw new Error(
|
|
969
|
+
`[NODE-OPCUA-E31] private key ${this.privateKeyFile} is encrypted; ` +
|
|
970
|
+
"set privateKeyPassphrase on the OPCUACertificateManager passed as clientCertificateManager"
|
|
971
|
+
);
|
|
972
|
+
}
|
|
973
|
+
throw err;
|
|
891
974
|
}
|
|
892
975
|
if (this.isUnusable()) return;
|
|
893
976
|
|
|
@@ -901,7 +984,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
901
984
|
protected _internalState: InternalClientState = "uninitialized";
|
|
902
985
|
|
|
903
986
|
protected _handleUnrecoverableConnectionFailure(err: Error, callback: ErrorCallback): void {
|
|
904
|
-
|
|
987
|
+
// c8 ignore next
|
|
988
|
+
doDebug && debugLog(err.message);
|
|
905
989
|
// Terminal connect failure: make sure the client is removed from the leak registry. A
|
|
906
990
|
// synchronous throw out of _connectStep2 (e.g. an invalid SecureChannel config) lands here
|
|
907
991
|
// WITHOUT the error-branch unregister ever running, which would otherwise leak the client.
|
|
@@ -912,7 +996,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
912
996
|
callback(err);
|
|
913
997
|
}
|
|
914
998
|
private _handleDisconnectionWhileConnecting(err: Error, callback: ErrorCallback) {
|
|
915
|
-
|
|
999
|
+
// c8 ignore next
|
|
1000
|
+
doDebug && debugLog(err.message);
|
|
916
1001
|
// see _handleUnrecoverableConnectionFailure: idempotent unregister guards against a leaked
|
|
917
1002
|
// client when a connect attempt fails before the error-branch unregister runs.
|
|
918
1003
|
OPCUAClientBase.registry.unregister(this);
|
|
@@ -921,7 +1006,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
921
1006
|
callback(err);
|
|
922
1007
|
}
|
|
923
1008
|
private _handleSuccessfulConnection(callback: ErrorCallback) {
|
|
924
|
-
|
|
1009
|
+
// c8 ignore next
|
|
1010
|
+
doDebug && debugLog(" Connected successfully to ", this.endpointUrl);
|
|
925
1011
|
this.emit("connected");
|
|
926
1012
|
this._setInternalState("connected");
|
|
927
1013
|
callback();
|
|
@@ -959,7 +1045,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
959
1045
|
|
|
960
1046
|
this.initializeCM()
|
|
961
1047
|
.then(() => {
|
|
962
|
-
|
|
1048
|
+
// c8 ignore next
|
|
1049
|
+
doDebug && debugLog("ClientBaseImpl#connect ", endpointUrl, this.clientName);
|
|
963
1050
|
if (this._internalState === "disconnecting" || this._internalState === "disconnected") {
|
|
964
1051
|
return this._handleDisconnectionWhileConnecting(new Error("premature disconnection 1"), callback);
|
|
965
1052
|
}
|
|
@@ -968,7 +1055,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
968
1055
|
!this.serverCertificate &&
|
|
969
1056
|
(forceEndpointDiscoveryOnConnect || this.securityMode !== MessageSecurityMode.None)
|
|
970
1057
|
) {
|
|
971
|
-
|
|
1058
|
+
// c8 ignore next
|
|
1059
|
+
doDebug && debugLog("Fetching certificates from endpoints");
|
|
972
1060
|
this.fetchServerCertificate(endpointUrl, (err: Error | null, adjustedEndpointUrl?: string) => {
|
|
973
1061
|
if (err) {
|
|
974
1062
|
return this._handleUnrecoverableConnectionFailure(err, callback);
|
|
@@ -977,10 +1065,13 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
977
1065
|
return this._handleDisconnectionWhileConnecting(new Error("premature disconnection 2"), callback);
|
|
978
1066
|
}
|
|
979
1067
|
if (forceEndpointDiscoveryOnConnect) {
|
|
980
|
-
|
|
1068
|
+
// c8 ignore next
|
|
1069
|
+
doDebug &&
|
|
1070
|
+
debugLog("connecting with adjusted endpoint : ", adjustedEndpointUrl, " was =", endpointUrl);
|
|
981
1071
|
this._connectStep2(adjustedEndpointUrl || "", callback);
|
|
982
1072
|
} else {
|
|
983
|
-
|
|
1073
|
+
// c8 ignore next
|
|
1074
|
+
doDebug && debugLog("connecting with endpoint : ", endpointUrl);
|
|
984
1075
|
this._connectStep2(endpointUrl, callback);
|
|
985
1076
|
}
|
|
986
1077
|
});
|
|
@@ -995,7 +1086,11 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
995
1086
|
|
|
996
1087
|
public connectReverse(reverseConnect: ClientReverseConnect, expectation?: ReverseConnectExpectation): Promise<void>;
|
|
997
1088
|
public connectReverse(reverseConnect: ClientReverseConnect, callback: ErrorCallback): void;
|
|
998
|
-
public connectReverse(
|
|
1089
|
+
public connectReverse(
|
|
1090
|
+
reverseConnect: ClientReverseConnect,
|
|
1091
|
+
expectation: ReverseConnectExpectation,
|
|
1092
|
+
callback: ErrorCallback
|
|
1093
|
+
): void;
|
|
999
1094
|
public connectReverse(...args: unknown[]): Promise<void> | void {
|
|
1000
1095
|
const reverseConnect = args[0] as ClientReverseConnect;
|
|
1001
1096
|
const callback = args[args.length - 1] as ErrorCallback;
|
|
@@ -1068,7 +1163,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1068
1163
|
|
|
1069
1164
|
this.initializeCM()
|
|
1070
1165
|
.then(() => {
|
|
1071
|
-
|
|
1166
|
+
// c8 ignore next
|
|
1167
|
+
doDebug && debugLog("ClientBaseImpl#connectReverse ", placeholderEndpointUrl, this.clientName);
|
|
1072
1168
|
if (this._internalState === "disconnecting" || this._internalState === "disconnected") {
|
|
1073
1169
|
return this._handleDisconnectionWhileConnecting(new Error("premature disconnection 1"), callback);
|
|
1074
1170
|
}
|
|
@@ -1092,7 +1188,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1092
1188
|
this._clockAdjuster = this._clockAdjuster || new ClockAdjustment();
|
|
1093
1189
|
OPCUAClientBase.registry.register(this);
|
|
1094
1190
|
|
|
1095
|
-
|
|
1191
|
+
// c8 ignore next
|
|
1192
|
+
doDebug && debugLog("__connectStep2 ", this._internalState);
|
|
1096
1193
|
|
|
1097
1194
|
this._internal_create_secure_channel(this.connectionStrategy, (err: Error | null) => {
|
|
1098
1195
|
if (!err) {
|
|
@@ -1103,19 +1200,22 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1103
1200
|
this._clockAdjuster.dispose();
|
|
1104
1201
|
this._clockAdjuster = undefined;
|
|
1105
1202
|
}
|
|
1106
|
-
|
|
1203
|
+
// c8 ignore next
|
|
1204
|
+
doDebug && debugLog(chalk.red("SecureChannel creation has failed with error :", err.message));
|
|
1107
1205
|
if (err.message.match(/ECONNABORTED/)) {
|
|
1108
|
-
|
|
1206
|
+
// c8 ignore next
|
|
1207
|
+
doDebug && debugLog(chalk.yellow(`- The client cannot to :${endpointUrl}. Connection has been aborted.`));
|
|
1109
1208
|
err = new Error("The connection has been aborted");
|
|
1110
1209
|
this._handleUnrecoverableConnectionFailure(err, callback);
|
|
1111
1210
|
} else if (err.message.match(/ECONNREF/)) {
|
|
1112
|
-
|
|
1211
|
+
// c8 ignore next
|
|
1212
|
+
doDebug && debugLog(chalk.yellow(`- The client cannot to :${endpointUrl}. Server is not reachable.`));
|
|
1113
1213
|
err = new Error(
|
|
1114
1214
|
`The connection cannot be established with server ${endpointUrl} .\n` +
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1215
|
+
"Please check that the server is up and running or your network configuration.\n" +
|
|
1216
|
+
"Err = (" +
|
|
1217
|
+
err.message +
|
|
1218
|
+
")"
|
|
1119
1219
|
);
|
|
1120
1220
|
this._handleUnrecoverableConnectionFailure(err, callback);
|
|
1121
1221
|
} else if (err.message.match(/disconnecting/)) {
|
|
@@ -1136,7 +1236,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1136
1236
|
callback(
|
|
1137
1237
|
new Error(
|
|
1138
1238
|
"performMessageTransaction: No SecureChannel , connection may have been canceled abruptly by server" +
|
|
1139
|
-
|
|
1239
|
+
" while performing " +
|
|
1240
|
+
request.schema.name
|
|
1140
1241
|
)
|
|
1141
1242
|
);
|
|
1142
1243
|
return;
|
|
@@ -1150,9 +1251,9 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1150
1251
|
callback(
|
|
1151
1252
|
new Error(
|
|
1152
1253
|
"performMessageTransaction: Invalid client state = " +
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1254
|
+
this._internalState +
|
|
1255
|
+
" while performing a transaction " +
|
|
1256
|
+
request.schema.name
|
|
1156
1257
|
)
|
|
1157
1258
|
);
|
|
1158
1259
|
return;
|
|
@@ -1351,7 +1452,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1351
1452
|
return callback(null);
|
|
1352
1453
|
}
|
|
1353
1454
|
|
|
1354
|
-
|
|
1455
|
+
// c8 ignore next
|
|
1456
|
+
doDebug && debugLog(chalk.bgWhite.green("_closeSession ") + this._secureChannel.channelId);
|
|
1355
1457
|
|
|
1356
1458
|
const request = new CloseSessionRequest({
|
|
1357
1459
|
deleteSubscriptions
|
|
@@ -1390,7 +1492,6 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1390
1492
|
|
|
1391
1493
|
public disconnect(): Promise<void>;
|
|
1392
1494
|
public disconnect(callback: ErrorCallback): void;
|
|
1393
|
-
// eslint-disable-next-line max-statements
|
|
1394
1495
|
public disconnect(...args: unknown[]): undefined | Promise<void> {
|
|
1395
1496
|
const callback = args[0] as ErrorCallback;
|
|
1396
1497
|
assert(typeof callback === "function", "expecting a callback function here");
|
|
@@ -1421,16 +1522,20 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1421
1522
|
callback();
|
|
1422
1523
|
return undefined;
|
|
1423
1524
|
}
|
|
1424
|
-
|
|
1525
|
+
// c8 ignore next
|
|
1526
|
+
doDebug && debugLog("disconnecting client! (will set reconnectionIsCanceled to true");
|
|
1425
1527
|
this._reconnectionIsCanceled = true;
|
|
1426
1528
|
|
|
1427
|
-
|
|
1529
|
+
// c8 ignore next
|
|
1530
|
+
doDebug && debugLog("ClientBaseImpl#disconnect", this.endpointUrl);
|
|
1428
1531
|
|
|
1429
1532
|
if (this.isReconnecting && !this._reconnectionIsCanceled) {
|
|
1430
|
-
|
|
1533
|
+
// c8 ignore next
|
|
1534
|
+
doDebug && debugLog("ClientBaseImpl#disconnect called while reconnection is in progress");
|
|
1431
1535
|
// let's abort the reconnection process
|
|
1432
1536
|
this._cancel_reconnection((err?: Error) => {
|
|
1433
|
-
|
|
1537
|
+
// c8 ignore next
|
|
1538
|
+
doDebug && debugLog("ClientBaseImpl#disconnect reconnection has been canceled", this.applicationName);
|
|
1434
1539
|
assert(!err, " why would this fail ?");
|
|
1435
1540
|
// sessions cannot be cancelled properly and must be discarded.
|
|
1436
1541
|
this.disconnect(callback);
|
|
@@ -1439,7 +1544,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1439
1544
|
}
|
|
1440
1545
|
|
|
1441
1546
|
if (this._sessions.length && !this.keepPendingSessionsOnDisconnect) {
|
|
1442
|
-
|
|
1547
|
+
// c8 ignore next
|
|
1548
|
+
doDebug && debugLog("warning : disconnection : closing pending sessions");
|
|
1443
1549
|
// disconnect has been called whereas living session exists
|
|
1444
1550
|
// we need to close them first .... (unless keepPendingSessionsOnDisconnect)
|
|
1445
1551
|
this._close_pending_sessions((/*err*/) => {
|
|
@@ -1448,13 +1554,15 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1448
1554
|
return undefined;
|
|
1449
1555
|
}
|
|
1450
1556
|
|
|
1451
|
-
|
|
1557
|
+
// c8 ignore next
|
|
1558
|
+
doDebug && debugLog("Disconnecting !");
|
|
1452
1559
|
this._setInternalState("disconnecting");
|
|
1453
1560
|
if (this.clientCertificateManager) {
|
|
1454
1561
|
const tmp = this.clientCertificateManager;
|
|
1455
1562
|
// (this as any).clientCertificateManager = null;
|
|
1456
1563
|
tmp.dispose().catch((err: Error) => {
|
|
1457
|
-
|
|
1564
|
+
// c8 ignore next
|
|
1565
|
+
doDebug && debugLog("Error disposing clientCertificateManager", err.message);
|
|
1458
1566
|
});
|
|
1459
1567
|
}
|
|
1460
1568
|
|
|
@@ -1477,7 +1585,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1477
1585
|
if (this._secureChannel) {
|
|
1478
1586
|
let tmpChannel: ClientSecureChannelLayer | null = this._secureChannel;
|
|
1479
1587
|
this._secureChannel = null;
|
|
1480
|
-
|
|
1588
|
+
// c8 ignore next
|
|
1589
|
+
doDebug && debugLog("Closing channel");
|
|
1481
1590
|
tmpChannel.close(() => {
|
|
1482
1591
|
this._secureChannel = tmpChannel;
|
|
1483
1592
|
tmpChannel = null;
|
|
@@ -1549,7 +1658,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1549
1658
|
|
|
1550
1659
|
private fetchServerCertificate(endpointUrl: string, callback: (err: Error | null, adjustedEndpointUrl?: string) => void): void {
|
|
1551
1660
|
const discoveryUrl = this.discoveryUrl.length > 0 ? this.discoveryUrl : endpointUrl;
|
|
1552
|
-
|
|
1661
|
+
// c8 ignore next
|
|
1662
|
+
doDebug && debugLog("OPCUAClientImpl : getting serverCertificate");
|
|
1553
1663
|
// we have not been given the serverCertificate but this certificate
|
|
1554
1664
|
// is required as the connection is to be secured.
|
|
1555
1665
|
//
|
|
@@ -1602,9 +1712,9 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1602
1712
|
// no matching end point can be found ...
|
|
1603
1713
|
const err1 = new Error(
|
|
1604
1714
|
"cannot find endpoint for securityMode=" +
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1715
|
+
MessageSecurityMode[this.securityMode] +
|
|
1716
|
+
" policy = " +
|
|
1717
|
+
this.securityPolicy
|
|
1608
1718
|
);
|
|
1609
1719
|
callback(err1);
|
|
1610
1720
|
return;
|
|
@@ -1620,7 +1730,10 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1620
1730
|
.catch((err1: Error) => {
|
|
1621
1731
|
warningLog("[NODE-OPCUA-W25] client's server certificate verification has failed ", err1.message);
|
|
1622
1732
|
if ("rootDir" in this.clientCertificateManager) {
|
|
1623
|
-
warningLog(
|
|
1733
|
+
warningLog(
|
|
1734
|
+
" clientCertificateManager.rootDir = ",
|
|
1735
|
+
(this.clientCertificateManager as unknown as { rootDir: string }).rootDir
|
|
1736
|
+
);
|
|
1624
1737
|
}
|
|
1625
1738
|
|
|
1626
1739
|
const chain = split_der(endpoint.serverCertificate);
|
|
@@ -1637,8 +1750,12 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1637
1750
|
const isCA = tbs.extensions?.basicConstraints?.cA === true;
|
|
1638
1751
|
const isSelfSigned = JSON.stringify(tbs.subject) === JSON.stringify(tbs.issuer);
|
|
1639
1752
|
const certType = isCA
|
|
1640
|
-
?
|
|
1641
|
-
|
|
1753
|
+
? isSelfSigned
|
|
1754
|
+
? "Root CA"
|
|
1755
|
+
: "Intermediate CA"
|
|
1756
|
+
: isSelfSigned
|
|
1757
|
+
? "Application (self-signed)"
|
|
1758
|
+
: "Application (CA-signed)";
|
|
1642
1759
|
const ski = tbs.extensions?.subjectKeyIdentifier ?? "-";
|
|
1643
1760
|
const aki = tbs.extensions?.authorityKeyIdentifier?.keyIdentifier ?? "-";
|
|
1644
1761
|
const notBefore = tbs.validity.notBefore.toISOString();
|
|
@@ -1650,13 +1767,15 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1650
1767
|
warningLog(` AKI: ${aki}`);
|
|
1651
1768
|
warningLog(` validity: ${notBefore} → ${notAfter}`);
|
|
1652
1769
|
} catch {
|
|
1653
|
-
warningLog(
|
|
1770
|
+
warningLog(
|
|
1771
|
+
` [${i + 1}/${chain.length}] thumbprint: ${thumbprint} (details unavailable)`
|
|
1772
|
+
);
|
|
1654
1773
|
}
|
|
1655
1774
|
}
|
|
1656
1775
|
if (chain.length > 1) {
|
|
1657
1776
|
warningLog(
|
|
1658
1777
|
" verify also that the issuer certificate is trusted and the issuer's certificate is present in the issuer.cert folder\n" +
|
|
1659
|
-
|
|
1778
|
+
" of the client certificate manager located in ",
|
|
1660
1779
|
"rootDir" in this.clientCertificateManager
|
|
1661
1780
|
? (this.clientCertificateManager as unknown as { rootDir: string }).rootDir
|
|
1662
1781
|
: "<in-memory>"
|
|
@@ -1718,7 +1837,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1718
1837
|
resolved = true;
|
|
1719
1838
|
if (err) {
|
|
1720
1839
|
const msg = session.authenticationToken ? session.authenticationToken.toString() : "";
|
|
1721
|
-
|
|
1840
|
+
// c8 ignore next
|
|
1841
|
+
doDebug && debugLog(` failing to close session ${msg}`);
|
|
1722
1842
|
}
|
|
1723
1843
|
resolve();
|
|
1724
1844
|
});
|
|
@@ -1730,11 +1850,13 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1730
1850
|
.then(() => {
|
|
1731
1851
|
// c8 ignore next
|
|
1732
1852
|
if (this._sessions.length > 0) {
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
.
|
|
1737
|
-
|
|
1853
|
+
// c8 ignore next
|
|
1854
|
+
doDebug &&
|
|
1855
|
+
debugLog(
|
|
1856
|
+
this._sessions
|
|
1857
|
+
.map((s: ClientSessionImpl) => (s.authenticationToken ? s.authenticationToken.toString() : ""))
|
|
1858
|
+
.join(" ")
|
|
1859
|
+
);
|
|
1738
1860
|
}
|
|
1739
1861
|
assert(this._sessions.length === 0, " failed to disconnect exiting sessions ");
|
|
1740
1862
|
callback();
|
|
@@ -1783,13 +1905,15 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1783
1905
|
|
|
1784
1906
|
secureChannel.on("lifetime_75", (token: ChannelSecurityToken) => {
|
|
1785
1907
|
// secureChannel requests a new token
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1908
|
+
// c8 ignore next
|
|
1909
|
+
doDebug &&
|
|
1910
|
+
debugLog(
|
|
1911
|
+
"SecureChannel Security Token ",
|
|
1912
|
+
token.tokenId,
|
|
1913
|
+
"live time was =",
|
|
1914
|
+
token.revisedLifetime,
|
|
1915
|
+
" is about to expired , it's time to request a new token"
|
|
1916
|
+
);
|
|
1793
1917
|
// forward message to upper level
|
|
1794
1918
|
this.emit("lifetime_75", token);
|
|
1795
1919
|
});
|
|
@@ -1803,7 +1927,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1803
1927
|
if (err) {
|
|
1804
1928
|
this._setInternalState("panic");
|
|
1805
1929
|
}
|
|
1806
|
-
|
|
1930
|
+
// c8 ignore next
|
|
1931
|
+
doDebug && debugLog(chalk.yellow.bold(" ClientBaseImpl emitting close"), err?.message);
|
|
1807
1932
|
this._destroy_secure_channel();
|
|
1808
1933
|
if (!err || !this.reconnectOnFailure) {
|
|
1809
1934
|
if (err) {
|
|
@@ -1820,7 +1945,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1820
1945
|
this._internalState !== "reconnecting" &&
|
|
1821
1946
|
this._internalState !== "reconnecting_newchannel_connected"
|
|
1822
1947
|
) {
|
|
1823
|
-
|
|
1948
|
+
// c8 ignore next
|
|
1949
|
+
doDebug && debugLog(" ClientBaseImpl emitting connection_lost");
|
|
1824
1950
|
this._setInternalState("reconnecting");
|
|
1825
1951
|
/**
|
|
1826
1952
|
* @event connection_lost
|
|
@@ -1871,7 +1997,8 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1871
1997
|
private __innerRepairConnection() {
|
|
1872
1998
|
if (this.isUnusable()) return;
|
|
1873
1999
|
|
|
1874
|
-
|
|
2000
|
+
// c8 ignore next
|
|
2001
|
+
doDebug && debugLog("Entering _repairConnection ", this._internalState);
|
|
1875
2002
|
if (this.#insideRepairConnection) {
|
|
1876
2003
|
errorLog(
|
|
1877
2004
|
"_repairConnection already in progress internal state = ",
|
|
@@ -1884,11 +2011,14 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1884
2011
|
}
|
|
1885
2012
|
this.emit("repairConnectionStarted");
|
|
1886
2013
|
this.#insideRepairConnection = true;
|
|
1887
|
-
|
|
2014
|
+
// c8 ignore next
|
|
2015
|
+
doDebug && debugLog("recreating new secure channel ", this._internalState);
|
|
1888
2016
|
this._recreate_secure_channel((err1?: Error) => {
|
|
1889
|
-
|
|
2017
|
+
// c8 ignore next
|
|
2018
|
+
doDebug && debugLog("secureChannel#on(close) => _recreate_secure_channel returns ", err1 ? err1.message : "OK");
|
|
1890
2019
|
if (err1) {
|
|
1891
|
-
|
|
2020
|
+
// c8 ignore next
|
|
2021
|
+
doDebug && debugLog("_recreate_secure_channel has failed: err = ", err1.message);
|
|
1892
2022
|
this.emit("close", err1);
|
|
1893
2023
|
this.#insideRepairConnection = false;
|
|
1894
2024
|
if (this.#shouldRepairAgain) {
|
|
@@ -1908,8 +2038,11 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1908
2038
|
debugLog("err= ", err2.message);
|
|
1909
2039
|
}
|
|
1910
2040
|
// we still need to retry connecting here !!!
|
|
1911
|
-
|
|
1912
|
-
|
|
2041
|
+
// c8 ignore next
|
|
2042
|
+
if (doDebug) {
|
|
2043
|
+
debugLog("Disconnected following reconnection failure", err2.message);
|
|
2044
|
+
debugLog(`I will retry OPCUA client reconnection in ${OPCUAClientBase.retryDelay / 1000} seconds`);
|
|
2045
|
+
}
|
|
1913
2046
|
this.#insideRepairConnection = false;
|
|
1914
2047
|
this.#shouldRepairAgain = false;
|
|
1915
2048
|
|
|
@@ -1964,7 +2097,6 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
|
|
|
1964
2097
|
}
|
|
1965
2098
|
}
|
|
1966
2099
|
|
|
1967
|
-
// tslint:disable-next-line: max-classes-per-file
|
|
1968
2100
|
class TmpClient extends ClientBaseImpl {
|
|
1969
2101
|
constructor(options: OPCUAClientBaseOptions) {
|
|
1970
2102
|
options.clientName = `${options.clientName || ""}_TmpClient`;
|
|
@@ -1974,7 +2106,8 @@ class TmpClient extends ClientBaseImpl {
|
|
|
1974
2106
|
async connect(endpoint: string): Promise<void>;
|
|
1975
2107
|
connect(endpoint: string, callback: ErrorCallback): void;
|
|
1976
2108
|
connect(endpoint: string, callback?: ErrorCallback): Promise<void> | void {
|
|
1977
|
-
|
|
2109
|
+
// c8 ignore next
|
|
2110
|
+
doDebug && debugLog("connecting to TmpClient");
|
|
1978
2111
|
|
|
1979
2112
|
// c8 ignore next
|
|
1980
2113
|
if (this._internalState !== "disconnected") {
|
|
@@ -1985,7 +2118,7 @@ class TmpClient extends ClientBaseImpl {
|
|
|
1985
2118
|
this._setInternalState("connecting");
|
|
1986
2119
|
this._connectStep2(endpoint, (err?: Error) => {
|
|
1987
2120
|
if (this.isUnusable()) {
|
|
1988
|
-
this._handleUnrecoverableConnectionFailure(new Error("premature disconnection 4"), callback || (() => {
|
|
2121
|
+
this._handleUnrecoverableConnectionFailure(new Error("premature disconnection 4"), callback || (() => {}));
|
|
1989
2122
|
return;
|
|
1990
2123
|
}
|
|
1991
2124
|
callback?.(err);
|
|
@@ -1993,8 +2126,6 @@ class TmpClient extends ClientBaseImpl {
|
|
|
1993
2126
|
}
|
|
1994
2127
|
}
|
|
1995
2128
|
|
|
1996
|
-
// tslint:disable:no-var-requires
|
|
1997
|
-
// tslint:disable:max-line-length
|
|
1998
2129
|
import { withCallback } from "thenify-ex";
|
|
1999
2130
|
|
|
2000
2131
|
ClientBaseImpl.prototype.connect = withCallback(ClientBaseImpl.prototype.connect);
|