node-opcua-client 2.72.2 → 2.73.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/client_base.d.ts +23 -40
- package/dist/client_base.js +1 -0
- package/dist/client_base.js.map +1 -1
- package/dist/client_session.d.ts +2 -2
- package/dist/client_session.js.map +1 -1
- package/dist/private/client_base_impl.d.ts +5 -17
- package/dist/private/client_base_impl.js +144 -131
- package/dist/private/client_base_impl.js.map +1 -1
- package/dist/private/client_session_impl.js.map +1 -1
- package/dist/private/opcua_client_impl.js +10 -7
- package/dist/private/opcua_client_impl.js.map +1 -1
- package/dist/reconnection.js +57 -11
- package/dist/reconnection.js.map +1 -1
- package/dist/tools/findservers.js +2 -2
- package/dist/tools/findservers.js.map +1 -1
- package/package.json +34 -34
- package/source/client_base.ts +23 -40
- package/source/client_session.ts +4 -2
- package/source/private/client_base_impl.ts +181 -150
- package/source/private/client_session_impl.ts +1 -3
- package/source/private/opcua_client_impl.ts +14 -10
- package/source/reconnection.ts +65 -12
- package/source/tools/findservers.ts +2 -2
|
@@ -23,6 +23,8 @@ import {
|
|
|
23
23
|
coerceSecurityPolicy,
|
|
24
24
|
ConnectionStrategy,
|
|
25
25
|
ConnectionStrategyOptions,
|
|
26
|
+
Request as Request1,
|
|
27
|
+
Response as Response1,
|
|
26
28
|
SecurityPolicy,
|
|
27
29
|
SecurityToken
|
|
28
30
|
} from "node-opcua-secure-channel";
|
|
@@ -59,7 +61,8 @@ import {
|
|
|
59
61
|
FindServersRequestLike,
|
|
60
62
|
GetEndpointsOptions,
|
|
61
63
|
OPCUAClientBase,
|
|
62
|
-
OPCUAClientBaseOptions
|
|
64
|
+
OPCUAClientBaseOptions,
|
|
65
|
+
TransportSettings
|
|
63
66
|
} from "../client_base";
|
|
64
67
|
import { performCertificateSanityCheck } from "../verify";
|
|
65
68
|
import { ClientSessionImpl } from "./client_session_impl";
|
|
@@ -259,7 +262,14 @@ class ClockAdjustment {
|
|
|
259
262
|
}
|
|
260
263
|
}
|
|
261
264
|
|
|
262
|
-
type InternalClientState =
|
|
265
|
+
type InternalClientState =
|
|
266
|
+
| "uninitialized"
|
|
267
|
+
| "disconnected"
|
|
268
|
+
| "connecting"
|
|
269
|
+
| "connected"
|
|
270
|
+
| "reconnecting"
|
|
271
|
+
| "reconnecting_newchannel_connected"
|
|
272
|
+
| "disconnecting";
|
|
263
273
|
|
|
264
274
|
/*
|
|
265
275
|
* "disconnected" ---[connect]----------------------> "connecting"
|
|
@@ -272,7 +282,9 @@ type InternalClientState = "uninitialized" | "disconnected" | "connecting" | "co
|
|
|
272
282
|
*
|
|
273
283
|
* "connecting" ---[lost of connection]-----------> "reconnecting" ->[reconnection]
|
|
274
284
|
*
|
|
275
|
-
* "reconnecting" ---[reconnection successful]------> "
|
|
285
|
+
* "reconnecting" ---[reconnection successful]------> "reconnecting_newchannel_connected"
|
|
286
|
+
*
|
|
287
|
+
* "reconnecting_newchannel_connected" --(session failure) -->"reconnecting"
|
|
276
288
|
*
|
|
277
289
|
* "reconnecting" ---[reconnection failure]---------> [reconnection] ---> "reconnecting"
|
|
278
290
|
*
|
|
@@ -280,6 +292,7 @@ type InternalClientState = "uninitialized" | "disconnected" | "connecting" | "co
|
|
|
280
292
|
*/
|
|
281
293
|
|
|
282
294
|
let g_ClientCounter = 0;
|
|
295
|
+
|
|
283
296
|
/**
|
|
284
297
|
* @internal
|
|
285
298
|
*/
|
|
@@ -287,8 +300,6 @@ let g_ClientCounter = 0;
|
|
|
287
300
|
export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase, IClientBase {
|
|
288
301
|
/**
|
|
289
302
|
* total number of requests that been canceled due to timeout
|
|
290
|
-
* @property timedOutRequestCount
|
|
291
|
-
* @type {Number}
|
|
292
303
|
*/
|
|
293
304
|
public get timedOutRequestCount(): number {
|
|
294
305
|
return this._timedOutRequestCount + (this._secureChannel ? this._secureChannel.timedOutRequestCount : 0);
|
|
@@ -296,17 +307,13 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
296
307
|
|
|
297
308
|
/**
|
|
298
309
|
* total number of transactions performed by the client
|
|
299
|
-
|
|
300
|
-
* @type {Number}
|
|
301
|
-
*/
|
|
310
|
+
x */
|
|
302
311
|
public get transactionsPerformed(): number {
|
|
303
312
|
return this._transactionsPerformed + (this._secureChannel ? this._secureChannel.transactionsPerformed : 0);
|
|
304
313
|
}
|
|
305
314
|
|
|
306
315
|
/**
|
|
307
316
|
* is true when the client has already requested the server end points.
|
|
308
|
-
* @property knowsServerEndpoint
|
|
309
|
-
* @type boolean
|
|
310
317
|
*/
|
|
311
318
|
get knowsServerEndpoint(): boolean {
|
|
312
319
|
return this._serverEndpoints && this._serverEndpoints.length > 0;
|
|
@@ -314,8 +321,6 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
314
321
|
|
|
315
322
|
/**
|
|
316
323
|
* true if the client is trying to reconnect to the server after a connection break.
|
|
317
|
-
* @property isReconnecting
|
|
318
|
-
* @type {Boolean}
|
|
319
324
|
*/
|
|
320
325
|
get isReconnecting(): boolean {
|
|
321
326
|
return !!(this._secureChannel && this._secureChannel.isConnecting) || this._internalState !== "connected";
|
|
@@ -323,8 +328,6 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
323
328
|
|
|
324
329
|
/**
|
|
325
330
|
* true if the connection strategy is set to automatically try to reconnect in case of failure
|
|
326
|
-
* @property reconnectOnFailure
|
|
327
|
-
* @type {Boolean}
|
|
328
331
|
*/
|
|
329
332
|
get reconnectOnFailure(): boolean {
|
|
330
333
|
return this.connectionStrategy.maxRetry > 0 || this.connectionStrategy.maxRetry === -1;
|
|
@@ -332,8 +335,6 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
332
335
|
|
|
333
336
|
/**
|
|
334
337
|
* total number of bytes read by the client
|
|
335
|
-
* @property bytesRead
|
|
336
|
-
* @type {Number}
|
|
337
338
|
*/
|
|
338
339
|
get bytesRead(): number {
|
|
339
340
|
return this._byteRead + (this._secureChannel ? this._secureChannel.bytesRead : 0);
|
|
@@ -341,8 +342,6 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
341
342
|
|
|
342
343
|
/**
|
|
343
344
|
* total number of bytes written by the client
|
|
344
|
-
* @property bytesWritten
|
|
345
|
-
* @type {Number}
|
|
346
345
|
*/
|
|
347
346
|
public get bytesWritten(): number {
|
|
348
347
|
return this._byteWritten + (this._secureChannel ? this._secureChannel.bytesWritten : 0);
|
|
@@ -379,10 +378,11 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
379
378
|
private _timedOutRequestCount: number;
|
|
380
379
|
|
|
381
380
|
private _transactionsPerformed: number;
|
|
382
|
-
private
|
|
381
|
+
private _reconnectionIsCanceled: boolean;
|
|
383
382
|
private _clockAdjuster?: ClockAdjustment;
|
|
384
383
|
private _tmpClient?: OPCUAClientBase;
|
|
385
384
|
private _instanceNumber: number;
|
|
385
|
+
private _transportSettings: TransportSettings;
|
|
386
386
|
|
|
387
387
|
public clientCertificateManager: OPCUACertificateManager;
|
|
388
388
|
|
|
@@ -430,7 +430,7 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
430
430
|
|
|
431
431
|
this._secureChannel = null;
|
|
432
432
|
|
|
433
|
-
this.
|
|
433
|
+
this._reconnectionIsCanceled = false;
|
|
434
434
|
|
|
435
435
|
this.endpointUrl = "";
|
|
436
436
|
|
|
@@ -469,14 +469,18 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
469
469
|
this.discoveryUrl = options.discoveryUrl || "";
|
|
470
470
|
|
|
471
471
|
this._setInternalState("disconnected");
|
|
472
|
+
|
|
473
|
+
this._transportSettings = options.transportSettings || {};
|
|
472
474
|
}
|
|
473
475
|
|
|
474
476
|
private _cancel_reconnection(callback: ErrorCallback) {
|
|
475
477
|
// _cancel_reconnection is invoked during disconnection
|
|
476
478
|
// when we detect that a reconnection is in progress..
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
479
|
+
if (!this.isReconnecting) {
|
|
480
|
+
warningLog("internal error : _cancel_reconnection should be used when reconnecting is in progress");
|
|
481
|
+
}
|
|
482
|
+
debugLog("canceling reconnection");
|
|
483
|
+
this._reconnectionIsCanceled = true;
|
|
480
484
|
// istanbul ignore next
|
|
481
485
|
if (!this._secureChannel) {
|
|
482
486
|
debugLog("_cancel_reconnection: Nothing to do !");
|
|
@@ -493,58 +497,79 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
493
497
|
}
|
|
494
498
|
|
|
495
499
|
public _recreate_secure_channel(callback: ErrorCallback): void {
|
|
496
|
-
debugLog("_recreate_secure_channel...", this._internalState);
|
|
500
|
+
debugLog("_recreate_secure_channel... while internalState is", this._internalState);
|
|
497
501
|
|
|
498
502
|
if (!this.knowsServerEndpoint) {
|
|
499
503
|
debugLog("Cannot reconnect , server endpoint is unknown");
|
|
500
504
|
return callback(new Error("Cannot reconnect, server endpoint is unknown"));
|
|
501
505
|
}
|
|
502
506
|
assert(this.knowsServerEndpoint);
|
|
503
|
-
|
|
504
|
-
/**
|
|
505
|
-
* notifies the observer that the OPCUA is now trying to reestablish the connection
|
|
506
|
-
* after having received a connection break...
|
|
507
|
-
* @event start_reconnection
|
|
508
|
-
*
|
|
509
|
-
*/
|
|
507
|
+
|
|
510
508
|
this._setInternalState("reconnecting");
|
|
511
509
|
this.emit("start_reconnection"); // send after callback
|
|
512
510
|
|
|
513
|
-
this._destroy_secure_channel();
|
|
514
|
-
assert(!this._secureChannel);
|
|
515
|
-
|
|
516
511
|
const infiniteConnectionRetry: ConnectionStrategyOptions = {
|
|
517
512
|
initialDelay: this.connectionStrategy.initialDelay,
|
|
518
513
|
maxDelay: this.connectionStrategy.maxDelay,
|
|
519
514
|
maxRetry: -1
|
|
520
515
|
};
|
|
521
516
|
|
|
522
|
-
const
|
|
517
|
+
const _when_internal_error = (err: Error, callback: ErrorCallback) => {
|
|
518
|
+
errorLog("INTERNAL ERROR", err.message);
|
|
519
|
+
callback(err);
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
const _when_reconnectionIsCanceled = (callback: ErrorCallback) => {
|
|
523
|
+
warningLog("attempt to recreate a new secure channel : suspended becasue reconnection is canceled !");
|
|
524
|
+
this.emit("reconnection_canceled");
|
|
525
|
+
return callback(new Error("Reconnection has been canceled - " + this.clientName));
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
const _failAndRetry = (err: Error, message: string, callback: ErrorCallback) => {
|
|
523
529
|
debugLog("failAndRetry; ", message);
|
|
524
|
-
if (this.
|
|
525
|
-
|
|
526
|
-
return callback(new Error("Reconnection has been canceled - " + this.clientName));
|
|
530
|
+
if (this._reconnectionIsCanceled) {
|
|
531
|
+
return _when_reconnectionIsCanceled(callback);
|
|
527
532
|
}
|
|
533
|
+
this._destroy_secure_channel();
|
|
528
534
|
warningLog("client = ", this.clientName, message, err.message);
|
|
529
535
|
// else
|
|
530
536
|
// let retry a little bit later
|
|
531
537
|
this.emit("reconnection_attempt_has_failed", err, message); // send after callback
|
|
532
|
-
|
|
538
|
+
setImmediate(_attempt_to_recreate_secure_channel, callback);
|
|
533
539
|
};
|
|
534
540
|
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
541
|
+
const _when_connected = (callback: ErrorCallback) => {
|
|
542
|
+
this.emit("after_reconnection", null); // send after callback
|
|
543
|
+
assert(this._secureChannel, "expecting a secureChannel here ");
|
|
544
|
+
// a new channel has be created and a new connection is established
|
|
545
|
+
debugLog(chalk.bgWhite.red("ClientBaseImpl: RECONNECTED !!!"));
|
|
546
|
+
this._setInternalState("reconnecting_newchannel_connected");
|
|
547
|
+
return callback();
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
const _attempt_to_recreate_secure_channel = (callback: ErrorCallback) => {
|
|
551
|
+
debugLog("attempt to recreate a new secure channel");
|
|
552
|
+
if (this._reconnectionIsCanceled) {
|
|
553
|
+
return _when_reconnectionIsCanceled(callback);
|
|
539
554
|
}
|
|
555
|
+
assert(!this._secureChannel, "_attempt_to_recreate_secure_channel, expecting this._secureChannel not to exist");
|
|
556
|
+
|
|
540
557
|
this._internal_create_secure_channel(infiniteConnectionRetry, (err?: Error | null) => {
|
|
541
558
|
if (err) {
|
|
559
|
+
// istanbul ignore next
|
|
560
|
+
if (this._secureChannel) {
|
|
561
|
+
const err = new Error("_internal_create_secure_channel failed, expecting this._secureChannel not to exist");
|
|
562
|
+
return _when_internal_error(err, callback);
|
|
563
|
+
}
|
|
564
|
+
|
|
542
565
|
if (err.message.match(/ECONNREFUSED|ECONNABORTED/)) {
|
|
543
|
-
return
|
|
566
|
+
return _failAndRetry(err, "create secure channel failed with ECONNREFUSED|ECONNABORTED", callback);
|
|
544
567
|
}
|
|
568
|
+
|
|
545
569
|
if (err.message.match("Backoff aborted.")) {
|
|
546
|
-
return
|
|
570
|
+
return _failAndRetry(err!, "cannot create secure channel (backoff aborted)", callback);
|
|
547
571
|
}
|
|
572
|
+
|
|
548
573
|
if (
|
|
549
574
|
err!.message.match("BadCertificateInvalid") ||
|
|
550
575
|
err!.message.match(/_socket has been disconnected by third party/)
|
|
@@ -560,51 +585,30 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
560
585
|
// the server may have shut down the channel because its certificate
|
|
561
586
|
// has changed ....
|
|
562
587
|
// let request the server certificate again ....
|
|
563
|
-
debugLog(
|
|
564
|
-
chalk.bgWhite.red(
|
|
565
|
-
"ClientBaseImpl: Server Certificate has changed." + " we need to retrieve server certificate again"
|
|
566
|
-
)
|
|
567
|
-
);
|
|
568
|
-
|
|
569
588
|
return this.fetchServerCertificate(this.endpointUrl, (err1?: Error | null) => {
|
|
570
589
|
if (err1) {
|
|
571
|
-
|
|
572
|
-
return failAndRetry(err1, "trying to fetch new server certificate");
|
|
590
|
+
return _failAndRetry(err1, "Failing to fetch new server certificate", callback);
|
|
573
591
|
}
|
|
574
592
|
warningLog("new server certificate ", makeSHA1Thumbprint(this.serverCertificate!).toString("hex"));
|
|
575
593
|
this._internal_create_secure_channel(infiniteConnectionRetry, (err3?: Error | null) => {
|
|
576
594
|
if (err3) {
|
|
577
|
-
return
|
|
595
|
+
return _failAndRetry(err3, "trying to create new channel with new certificate", callback);
|
|
578
596
|
}
|
|
579
|
-
|
|
580
|
-
this.emit("connected");
|
|
581
|
-
callback();
|
|
597
|
+
return _when_connected(callback);
|
|
582
598
|
});
|
|
583
599
|
});
|
|
600
|
+
} else {
|
|
601
|
+
return _failAndRetry(err, "cannot create secure channel", callback);
|
|
584
602
|
}
|
|
585
|
-
debugLog(chalk.bgWhite.red("ClientBaseImpl: cannot reconnect .."));
|
|
586
|
-
failAndRetry(err!, "cannot create secure channel");
|
|
587
603
|
} else {
|
|
588
|
-
|
|
589
|
-
* notify the observers that the reconnection process has been completed
|
|
590
|
-
* @event after_reconnection
|
|
591
|
-
* @param err
|
|
592
|
-
*/
|
|
593
|
-
this.emit("after_reconnection", err); // send after callback
|
|
594
|
-
assert(this._secureChannel, "expecting a secureChannel here ");
|
|
595
|
-
// a new channel has be created and a new connection is established
|
|
596
|
-
debugLog(chalk.bgWhite.red("ClientBaseImpl: RECONNECTED !!!"));
|
|
597
|
-
this._setInternalState("connected");
|
|
598
|
-
return callback();
|
|
604
|
+
return _when_connected(callback);
|
|
599
605
|
}
|
|
600
606
|
});
|
|
601
607
|
};
|
|
602
608
|
|
|
603
609
|
// create a secure channel
|
|
604
610
|
// a new secure channel must be established
|
|
605
|
-
|
|
606
|
-
_attempt_to_recreate_secure_channel();
|
|
607
|
-
});
|
|
611
|
+
_attempt_to_recreate_secure_channel(callback);
|
|
608
612
|
}
|
|
609
613
|
|
|
610
614
|
public _internal_create_secure_channel(
|
|
@@ -614,7 +618,7 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
614
618
|
assert(this._secureChannel === null);
|
|
615
619
|
assert(typeof this.endpointUrl === "string");
|
|
616
620
|
|
|
617
|
-
debugLog("_internal_create_secure_channel creating new ClientSecureChannelLayer");
|
|
621
|
+
debugLog("_internal_create_secure_channel creating new ClientSecureChannelLayer _internalState =", this._internalState);
|
|
618
622
|
const secureChannel = new ClientSecureChannelLayer({
|
|
619
623
|
connectionStrategy,
|
|
620
624
|
defaultSecureTokenLifetime: this.defaultSecureTokenLifetime,
|
|
@@ -622,10 +626,18 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
622
626
|
securityMode: this.securityMode,
|
|
623
627
|
securityPolicy: this.securityPolicy,
|
|
624
628
|
serverCertificate: this.serverCertificate,
|
|
625
|
-
tokenRenewalInterval: this.tokenRenewalInterval
|
|
626
|
-
|
|
629
|
+
tokenRenewalInterval: this.tokenRenewalInterval,
|
|
630
|
+
transportSettings: this._transportSettings
|
|
627
631
|
// transportTimeout:
|
|
628
632
|
});
|
|
633
|
+
secureChannel.on("backoff", (count: number, delay: number) => {
|
|
634
|
+
this.emit("backoff", count, delay);
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
secureChannel.on("abort", () => {
|
|
638
|
+
this.emit("abort");
|
|
639
|
+
});
|
|
640
|
+
|
|
629
641
|
secureChannel.protocolVersion = this.protocolVersion;
|
|
630
642
|
|
|
631
643
|
this._secureChannel = secureChannel;
|
|
@@ -633,30 +645,20 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
633
645
|
async.series(
|
|
634
646
|
[
|
|
635
647
|
// ------------------------------------------------- STEP 2 : OpenSecureChannel
|
|
636
|
-
(
|
|
648
|
+
(innerCallback: ErrorCallback) => {
|
|
637
649
|
debugLog("_internal_create_secure_channel before secureChannel.create");
|
|
650
|
+
|
|
638
651
|
secureChannel.create(this.endpointUrl, (err?: Error) => {
|
|
639
652
|
debugLog("_internal_create_secure_channel after secureChannel.create");
|
|
640
653
|
if (!this._secureChannel) {
|
|
641
654
|
debugLog("_secureChannel has been closed during the transaction !");
|
|
642
|
-
|
|
655
|
+
assert(this.disconnecting);
|
|
656
|
+
return innerCallback(new Error("Secure Channel Closed"));
|
|
643
657
|
}
|
|
644
|
-
if (err) {
|
|
645
|
-
debugLog(chalk.yellow("Cannot create secureChannel"), err.message ? chalk.cyan(err.message) : "");
|
|
646
|
-
this._destroy_secure_channel();
|
|
647
|
-
} else {
|
|
648
|
-
assert(this._secureChannel !== null);
|
|
658
|
+
if (!err) {
|
|
649
659
|
this._install_secure_channel_event_handlers(secureChannel);
|
|
650
660
|
}
|
|
651
|
-
|
|
652
|
-
});
|
|
653
|
-
|
|
654
|
-
secureChannel.on("backoff", (count: number, delay: number) => {
|
|
655
|
-
this.emit("backoff", count, delay);
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
secureChannel.on("abort", () => {
|
|
659
|
-
this.emit("abort");
|
|
661
|
+
innerCallback(err);
|
|
660
662
|
});
|
|
661
663
|
},
|
|
662
664
|
// ------------------------------------------------- STEP 3 : GetEndpointsRequest
|
|
@@ -664,9 +666,10 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
664
666
|
assert(this._secureChannel !== null);
|
|
665
667
|
if (!this.knowsServerEndpoint) {
|
|
666
668
|
this.getEndpoints((err: Error | null /*, endpoints?: EndpointDescription[]*/) => {
|
|
667
|
-
if (this._secureChannel
|
|
669
|
+
if (!this._secureChannel) {
|
|
670
|
+
debugLog("_secureChannel has been closed during the transaction ! (while getEndpoints)");
|
|
668
671
|
assert(this.disconnecting);
|
|
669
|
-
innerCallback(new Error("
|
|
672
|
+
return innerCallback(new Error("Secure Channel Closed"));
|
|
670
673
|
}
|
|
671
674
|
innerCallback(err ? err : undefined);
|
|
672
675
|
});
|
|
@@ -679,8 +682,14 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
679
682
|
(err) => {
|
|
680
683
|
if (err) {
|
|
681
684
|
debugLog("Inner create secure channel has failed", err.message);
|
|
682
|
-
this._secureChannel
|
|
683
|
-
|
|
685
|
+
if (this._secureChannel) {
|
|
686
|
+
this._secureChannel!.abortConnection(() => {
|
|
687
|
+
this._destroy_secure_channel();
|
|
688
|
+
callback(err);
|
|
689
|
+
});
|
|
690
|
+
} else {
|
|
691
|
+
callback(err);
|
|
692
|
+
}
|
|
684
693
|
} else {
|
|
685
694
|
assert(this._secureChannel !== null);
|
|
686
695
|
callback(null, secureChannel);
|
|
@@ -922,10 +931,24 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
922
931
|
if (!this._secureChannel) {
|
|
923
932
|
// this may happen if the Server has closed the connection abruptly for some unknown reason
|
|
924
933
|
// or if the tcp connection has been broken.
|
|
925
|
-
return callback(
|
|
934
|
+
return callback(
|
|
935
|
+
new Error("performMessageTransaction: No SecureChannel , connection may have been canceled abruptly by server")
|
|
936
|
+
);
|
|
926
937
|
}
|
|
927
|
-
if (
|
|
928
|
-
|
|
938
|
+
if (
|
|
939
|
+
this._internalState !== "connected" &&
|
|
940
|
+
this._internalState !== "reconnecting_newchannel_connected" &&
|
|
941
|
+
this._internalState !== "connecting" &&
|
|
942
|
+
this._internalState !== "reconnecting"
|
|
943
|
+
) {
|
|
944
|
+
return callback(
|
|
945
|
+
new Error(
|
|
946
|
+
"performMessageTransaction: Invalid client state " +
|
|
947
|
+
this._internalState +
|
|
948
|
+
" while performing a transaction " +
|
|
949
|
+
request.constructor.name
|
|
950
|
+
)
|
|
951
|
+
);
|
|
929
952
|
}
|
|
930
953
|
assert(this._secureChannel);
|
|
931
954
|
assert(request);
|
|
@@ -1180,8 +1203,8 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
1180
1203
|
}
|
|
1181
1204
|
return callback();
|
|
1182
1205
|
}
|
|
1183
|
-
|
|
1184
|
-
this.
|
|
1206
|
+
debugLog("disconnecting client! (will set reconnectionIsCanceled to true");
|
|
1207
|
+
this._reconnectionIsCanceled = true;
|
|
1185
1208
|
this.disconnecting = true;
|
|
1186
1209
|
|
|
1187
1210
|
if (this._tmpClient) {
|
|
@@ -1194,7 +1217,7 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
1194
1217
|
return;
|
|
1195
1218
|
}
|
|
1196
1219
|
debugLog("ClientBaseImpl#disconnect", this.endpointUrl);
|
|
1197
|
-
if (this.isReconnecting && !this.
|
|
1220
|
+
if (this.isReconnecting && !this._reconnectionIsCanceled) {
|
|
1198
1221
|
debugLog("ClientBaseImpl#disconnect called while reconnection is in progress");
|
|
1199
1222
|
// let's abort the reconnection process
|
|
1200
1223
|
this._cancel_reconnection((err?: Error) => {
|
|
@@ -1416,12 +1439,11 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
1416
1439
|
")"
|
|
1417
1440
|
);
|
|
1418
1441
|
}
|
|
1419
|
-
|
|
1420
1442
|
this._accumulate_statistics();
|
|
1421
|
-
|
|
1422
|
-
this._secureChannel.dispose();
|
|
1423
|
-
this._secureChannel.removeAllListeners();
|
|
1443
|
+
const secureChannel = this._secureChannel;
|
|
1424
1444
|
this._secureChannel = null;
|
|
1445
|
+
secureChannel.dispose();
|
|
1446
|
+
secureChannel.removeAllListeners();
|
|
1425
1447
|
}
|
|
1426
1448
|
}
|
|
1427
1449
|
|
|
@@ -1486,7 +1508,7 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
1486
1508
|
this.emit("receive_chunk", chunk);
|
|
1487
1509
|
});
|
|
1488
1510
|
|
|
1489
|
-
secureChannel.on("send_request", (message:
|
|
1511
|
+
secureChannel.on("send_request", (message: Request1) => {
|
|
1490
1512
|
/**
|
|
1491
1513
|
* notify the observer that a request has been sent to the server.
|
|
1492
1514
|
* @event send_request
|
|
@@ -1495,7 +1517,7 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
1495
1517
|
this.emit("send_request", message);
|
|
1496
1518
|
});
|
|
1497
1519
|
|
|
1498
|
-
secureChannel.on("receive_response", (message:
|
|
1520
|
+
secureChannel.on("receive_response", (message: Response1) => {
|
|
1499
1521
|
/**
|
|
1500
1522
|
* notify the observer that a response has been received from the server.
|
|
1501
1523
|
* @event receive_response
|
|
@@ -1522,8 +1544,9 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
1522
1544
|
this.emit("security_token_renewed", secureChannel);
|
|
1523
1545
|
});
|
|
1524
1546
|
|
|
1525
|
-
secureChannel.on("close", (err?: Error) => {
|
|
1547
|
+
secureChannel.on("close", (err?: Error | null) => {
|
|
1526
1548
|
debugLog(chalk.yellow.bold(" ClientBaseImpl emitting close"), err?.message);
|
|
1549
|
+
this._destroy_secure_channel();
|
|
1527
1550
|
if (!err || !this.reconnectOnFailure) {
|
|
1528
1551
|
// this is a normal close operation initiated by us
|
|
1529
1552
|
/**
|
|
@@ -1531,17 +1554,20 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
1531
1554
|
* @param error
|
|
1532
1555
|
*/
|
|
1533
1556
|
this.emit("close", err);
|
|
1534
|
-
setImmediate(() => this._destroy_secure_channel());
|
|
1535
1557
|
} else {
|
|
1536
1558
|
/**
|
|
1537
1559
|
* @event connection_lost
|
|
1538
1560
|
*/
|
|
1539
|
-
this.emit("
|
|
1540
|
-
this.
|
|
1561
|
+
// this.emit("close", err);
|
|
1562
|
+
if (this.reconnectOnFailure && this._internalState !== "reconnecting") {
|
|
1563
|
+
debugLog(" ClientBaseImpl emitting connection_lost");
|
|
1564
|
+
this.emit("connection_lost", err?.message); // instead of "close"
|
|
1565
|
+
this._repairConnection();
|
|
1566
|
+
}
|
|
1541
1567
|
}
|
|
1542
1568
|
});
|
|
1543
1569
|
|
|
1544
|
-
secureChannel.on("timed_out_request", (request:
|
|
1570
|
+
secureChannel.on("timed_out_request", (request: Request1) => {
|
|
1545
1571
|
/**
|
|
1546
1572
|
* send when a request has timed out without receiving a response
|
|
1547
1573
|
* @event timed_out_request
|
|
@@ -1550,46 +1576,51 @@ export class ClientBaseImpl extends OPCUASecureObject implements OPCUAClientBase
|
|
|
1550
1576
|
this.emit("timed_out_request", request);
|
|
1551
1577
|
});
|
|
1552
1578
|
}
|
|
1553
|
-
private _repairConnection() {
|
|
1554
|
-
setImmediate(() => {
|
|
1555
|
-
debugLog("recreating new secure channel ");
|
|
1556
|
-
this._recreate_secure_channel((err1?: Error) => {
|
|
1557
|
-
debugLog("secureChannel#on(close) => _recreate_secure_channel returns ", err1 ? err1.message : "OK");
|
|
1558
|
-
|
|
1559
|
-
if (err1) {
|
|
1560
|
-
// xx assert(!this._secureChannel);
|
|
1561
|
-
debugLog("_recreate_secure_channel has failed");
|
|
1562
1579
|
|
|
1563
|
-
|
|
1564
|
-
this._setInternalState("disconnected");
|
|
1580
|
+
public _inside_repairConnection = false;
|
|
1565
1581
|
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1582
|
+
private _repairConnection() {
|
|
1583
|
+
if (this._inside_repairConnection) {
|
|
1584
|
+
errorLog("_repairConnection already in progress ", this._internalState);
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1587
|
+
this._inside_repairConnection = true;
|
|
1588
|
+
debugLog("recreating new secure channel ", this._internalState);
|
|
1589
|
+
this._recreate_secure_channel((err1?: Error) => {
|
|
1590
|
+
debugLog("secureChannel#on(close) => _recreate_secure_channel returns ", err1 ? err1.message : "OK");
|
|
1591
|
+
|
|
1592
|
+
if (err1) {
|
|
1593
|
+
errorLog("_recreate_secure_channel has failed: err = ", err1.message);
|
|
1594
|
+
this.emit("close", err1);
|
|
1595
|
+
this._setInternalState("disconnected");
|
|
1596
|
+
this._inside_repairConnection = false;
|
|
1597
|
+
return;
|
|
1598
|
+
} else {
|
|
1599
|
+
this._finalReconnectionStep((err2?: Error | null) => {
|
|
1600
|
+
if (err2) {
|
|
1601
|
+
// istanbul ignore next
|
|
1602
|
+
if (doDebug) {
|
|
1603
|
+
debugLog("connection_reestablished has failed");
|
|
1604
|
+
debugLog("err= ", err2.message);
|
|
1589
1605
|
}
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1606
|
+
// we still need to retry connecting here !!!
|
|
1607
|
+
debugLog("Disconnected following reconnection failure", err2.message);
|
|
1608
|
+
debugLog(`I will retry OPCUA client reconnection in ${OPCUAClientBase.retryDelay / 1000} seconds`);
|
|
1609
|
+
this._inside_repairConnection = false;
|
|
1610
|
+
this._destroy_secure_channel();
|
|
1611
|
+
setTimeout(() => this._repairConnection(), OPCUAClientBase.retryDelay);
|
|
1612
|
+
return;
|
|
1613
|
+
} else {
|
|
1614
|
+
/**
|
|
1615
|
+
* @event connection_reestablished
|
|
1616
|
+
* send when the connection is reestablished after a connection break
|
|
1617
|
+
*/
|
|
1618
|
+
this.emit("connection_reestablished");
|
|
1619
|
+
this._setInternalState("connected");
|
|
1620
|
+
this._inside_repairConnection = false;
|
|
1621
|
+
}
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1593
1624
|
});
|
|
1594
1625
|
}
|
|
1595
1626
|
private _finalReconnectionStep(callback: ErrorCallback) {
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
getExtensionObjectConstructor,
|
|
12
12
|
getExtraDataTypeManager,
|
|
13
13
|
promoteOpaqueStructure,
|
|
14
|
-
resolveDynamicExtensionObject
|
|
15
14
|
} from "node-opcua-client-dynamic-extension-object";
|
|
16
15
|
import { Certificate, Nonce } from "node-opcua-crypto";
|
|
17
16
|
import { attributeNameById, BrowseDirection, LocalizedTextLike } from "node-opcua-data-model";
|
|
@@ -21,7 +20,7 @@ import { ExtensionObject } from "node-opcua-extension-object";
|
|
|
21
20
|
import { coerceNodeId, NodeId, NodeIdLike, resolveNodeId } from "node-opcua-nodeid";
|
|
22
21
|
import { getBuiltInDataType, getArgumentDefinitionHelper, IBasicSession } from "node-opcua-pseudo-session";
|
|
23
22
|
import { AnyConstructorFunc } from "node-opcua-schemas";
|
|
24
|
-
import {
|
|
23
|
+
import { requestHandleNotSetValue, SignatureData } from "node-opcua-secure-channel";
|
|
25
24
|
import { BrowseDescription, BrowseRequest, BrowseResponse, BrowseResult } from "node-opcua-service-browse";
|
|
26
25
|
import { CallMethodRequest, CallMethodResult, CallRequest, CallResponse } from "node-opcua-service-call";
|
|
27
26
|
import { EndpointDescription } from "node-opcua-service-endpoints";
|
|
@@ -115,7 +114,6 @@ import { repair_client_session } from "../reconnection";
|
|
|
115
114
|
|
|
116
115
|
import { ClientSidePublishEngine } from "./client_publish_engine";
|
|
117
116
|
import { ClientSubscriptionImpl } from "./client_subscription_impl";
|
|
118
|
-
import { OPCUAClientImpl } from "./opcua_client_impl";
|
|
119
117
|
import { IClientBase } from "./i_private_client";
|
|
120
118
|
|
|
121
119
|
export type ResponseCallback<T> = (err: Error | null, response?: T) => void;
|