node-opcua-server 2.163.1 → 2.165.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 (45) hide show
  1. package/dist/addressSpace_accessor.js +7 -5
  2. package/dist/addressSpace_accessor.js.map +1 -1
  3. package/dist/base_server.d.ts +95 -18
  4. package/dist/base_server.js +217 -69
  5. package/dist/base_server.js.map +1 -1
  6. package/dist/monitored_item.js +32 -32
  7. package/dist/monitored_item.js.map +1 -1
  8. package/dist/node_sampler.js +1 -1
  9. package/dist/node_sampler.js.map +1 -1
  10. package/dist/opcua_server.d.ts +171 -123
  11. package/dist/opcua_server.js +466 -223
  12. package/dist/opcua_server.js.map +1 -1
  13. package/dist/register_server_manager.d.ts +2 -2
  14. package/dist/register_server_manager.js +12 -6
  15. package/dist/register_server_manager.js.map +1 -1
  16. package/dist/register_server_manager_mdns_only.js +1 -1
  17. package/dist/register_server_manager_mdns_only.js.map +1 -1
  18. package/dist/server_end_point.d.ts +108 -12
  19. package/dist/server_end_point.js +157 -57
  20. package/dist/server_end_point.js.map +1 -1
  21. package/dist/server_engine.d.ts +32 -14
  22. package/dist/server_engine.js +160 -59
  23. package/dist/server_engine.js.map +1 -1
  24. package/dist/server_publish_engine.js +5 -5
  25. package/dist/server_publish_engine.js.map +1 -1
  26. package/dist/server_session.js +4 -4
  27. package/dist/server_session.js.map +1 -1
  28. package/dist/server_subscription.js +18 -18
  29. package/dist/server_subscription.js.map +1 -1
  30. package/dist/sessions_compatible_for_transfer.js +1 -1
  31. package/dist/sessions_compatible_for_transfer.js.map +1 -1
  32. package/package.json +47 -47
  33. package/source/addressSpace_accessor.ts +6 -3
  34. package/source/base_server.ts +252 -90
  35. package/source/monitored_item.ts +32 -32
  36. package/source/node_sampler.ts +1 -1
  37. package/source/opcua_server.ts +674 -489
  38. package/source/register_server_manager.ts +11 -5
  39. package/source/register_server_manager_mdns_only.ts +1 -1
  40. package/source/server_end_point.ts +278 -94
  41. package/source/server_engine.ts +246 -135
  42. package/source/server_publish_engine.ts +5 -5
  43. package/source/server_session.ts +4 -4
  44. package/source/server_subscription.ts +18 -18
  45. package/source/sessions_compatible_for_transfer.ts +4 -2
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * @module node-opcua-server
3
3
  */
4
- import { EventEmitter } from "events";
5
- import { OPCUACertificateManager } from "node-opcua-certificate-manager";
6
- import { Certificate, PrivateKey } from "node-opcua-crypto/web";
7
- import { MessageSecurityMode, SecurityPolicy, ServerSecureChannelLayer, ServerSecureChannelParent } from "node-opcua-secure-channel";
8
- import { UserTokenType } from "node-opcua-service-endpoints";
9
- import { EndpointDescription } from "node-opcua-service-endpoints";
10
- import { ApplicationDescription } from "node-opcua-service-endpoints";
11
- import { IHelloAckLimits } from "node-opcua-transport";
4
+ import { EventEmitter } from "node:events";
5
+ import type { OPCUACertificateManager } from "node-opcua-certificate-manager";
6
+ import { type Certificate, type PrivateKey } from "node-opcua-crypto/web";
7
+ import { MessageSecurityMode, SecurityPolicy, ServerSecureChannelLayer, type ServerSecureChannelParent } from "node-opcua-secure-channel";
8
+ import { ApplicationDescription, EndpointDescription, UserTokenType } from "node-opcua-service-endpoints";
9
+ import type { IHelloAckLimits } from "node-opcua-transport";
10
+ import type { IChannelData } from "./i_channel_data";
11
+ import type { ISocketData } from "./i_socket_data";
12
12
  export interface OPCUAServerEndPointOptions {
13
13
  /**
14
14
  * the tcp port
@@ -42,7 +42,7 @@ export interface OPCUAServerEndPointOptions {
42
42
  */
43
43
  timeout?: number;
44
44
  serverInfo: ApplicationDescription;
45
- objectFactory?: any;
45
+ objectFactory?: unknown;
46
46
  transportSettings?: IServerTransportSettings;
47
47
  }
48
48
  export interface IServerTransportSettings {
@@ -54,9 +54,66 @@ export interface EndpointDescriptionParams {
54
54
  resourcePath?: string;
55
55
  alternateHostname?: string[];
56
56
  hostname: string;
57
+ /**
58
+ * Override the port used in the endpoint URL.
59
+ * When set, the endpoint URL uses this port instead of the
60
+ * server's listen port. The server does NOT listen on this port.
61
+ * Useful for Docker port-mapping, reverse proxies, and NAT.
62
+ */
63
+ advertisedPort?: number;
57
64
  securityPolicies: SecurityPolicy[];
58
65
  userTokenTypes: UserTokenType[];
59
66
  }
67
+ /**
68
+ * Per-URL security overrides for advertised endpoints.
69
+ *
70
+ * When `advertisedEndpoints` contains a config object, the endpoint
71
+ * descriptions generated for that URL use the overridden security
72
+ * settings instead of inheriting from the main endpoint.
73
+ *
74
+ * Any field that is omitted falls back to the main endpoint's value.
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * advertisedEndpoints: [
79
+ * // Public: SignAndEncrypt only, no anonymous
80
+ * {
81
+ * url: "opc.tcp://public.example.com:4840",
82
+ * securityModes: [MessageSecurityMode.SignAndEncrypt],
83
+ * allowAnonymous: false
84
+ * },
85
+ * // Internal: inherits everything from main endpoint
86
+ * "opc.tcp://internal:48480"
87
+ * ]
88
+ * ```
89
+ */
90
+ export interface AdvertisedEndpointConfig {
91
+ /** The full endpoint URL, e.g. `"opc.tcp://public.example.com:4840"` */
92
+ url: string;
93
+ /** Override security modes (default: inherit from main endpoint) */
94
+ securityModes?: MessageSecurityMode[];
95
+ /** Override security policies (default: inherit from main endpoint) */
96
+ securityPolicies?: SecurityPolicy[];
97
+ /** Override anonymous access (default: inherit from main endpoint) */
98
+ allowAnonymous?: boolean;
99
+ /** Override user token types (default: inherit from main endpoint) */
100
+ userTokenTypes?: UserTokenType[];
101
+ }
102
+ /**
103
+ * An advertised endpoint entry — either a plain URL string (inherits
104
+ * all settings from the main endpoint) or a config object with
105
+ * per-URL security overrides.
106
+ */
107
+ export type AdvertisedEndpoint = string | AdvertisedEndpointConfig;
108
+ /**
109
+ * Normalize any `advertisedEndpoints` input into a uniform
110
+ * `AdvertisedEndpointConfig[]`.
111
+ *
112
+ * This coercion is done early so that all downstream code
113
+ * (endpoint generation, IP/hostname extraction) only deals
114
+ * with one type.
115
+ */
116
+ export declare function normalizeAdvertisedEndpoints(raw?: AdvertisedEndpoint | AdvertisedEndpoint[]): AdvertisedEndpointConfig[];
60
117
  export interface AddStandardEndpointDescriptionsParam {
61
118
  allowAnonymous?: boolean;
62
119
  disableDiscovery?: boolean;
@@ -68,7 +125,46 @@ export interface AddStandardEndpointDescriptionsParam {
68
125
  hostname?: string;
69
126
  securityPolicies?: SecurityPolicy[];
70
127
  userTokenTypes?: UserTokenType[];
128
+ /**
129
+ * Additional endpoint URL(s) to advertise.
130
+ *
131
+ * Use when the server is behind Docker port-mapping,
132
+ * a reverse proxy, or a NAT gateway.
133
+ *
134
+ * Each entry can be a plain URL string (inherits all security
135
+ * settings from the main endpoint) or an
136
+ * `AdvertisedEndpointConfig` object with per-URL overrides.
137
+ *
138
+ * The server still listens on `port` — these are purely
139
+ * advertised aliases.
140
+ *
141
+ * @example Simple string (inherits main settings)
142
+ * ```ts
143
+ * advertisedEndpoints: "opc.tcp://localhost:48481"
144
+ * ```
145
+ *
146
+ * @example Mixed array with per-URL security overrides
147
+ * ```ts
148
+ * advertisedEndpoints: [
149
+ * "opc.tcp://internal:48480",
150
+ * {
151
+ * url: "opc.tcp://public.example.com:4840",
152
+ * securityModes: [MessageSecurityMode.SignAndEncrypt],
153
+ * allowAnonymous: false
154
+ * }
155
+ * ]
156
+ * ```
157
+ */
158
+ advertisedEndpoints?: AdvertisedEndpoint | AdvertisedEndpoint[];
71
159
  }
160
+ /**
161
+ * Parse an `opc.tcp://hostname:port` URL and extract hostname and port.
162
+ * @internal
163
+ */
164
+ export declare function parseOpcTcpUrl(url: string): {
165
+ hostname: string;
166
+ port: number;
167
+ };
72
168
  /**
73
169
  * OPCUAServerEndPoint a Server EndPoint.
74
170
  * A sever end point is listening to one port
@@ -90,11 +186,11 @@ export declare class OPCUAServerEndPoint extends EventEmitter implements ServerS
90
186
  transactionsCountOldChannels: number;
91
187
  securityTokenCountOldChannels: number;
92
188
  serverInfo: ApplicationDescription;
93
- objectFactory: any;
189
+ objectFactory: unknown;
94
190
  _on_new_channel?: (channel: ServerSecureChannelLayer) => void;
95
191
  _on_close_channel?: (channel: ServerSecureChannelLayer) => void;
96
- _on_connectionRefused?: (socketData: any) => void;
97
- _on_openSecureChannelFailure?: (socketData: any, channelData: any) => void;
192
+ _on_connectionRefused?: (socketData: ISocketData) => void;
193
+ _on_openSecureChannelFailure?: (socketData: ISocketData, channelData: IChannelData) => void;
98
194
  private _certificateChain;
99
195
  private _privateKey;
100
196
  private _channels;
@@ -1,31 +1,30 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.OPCUAServerEndPoint = void 0;
7
2
  /* eslint-disable max-statements */
8
3
  /**
9
4
  * @module node-opcua-server
10
5
  */
11
6
  // tslint:disable:no-console
12
- const events_1 = require("events");
13
- const net_1 = __importDefault(require("net"));
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.OPCUAServerEndPoint = void 0;
12
+ exports.normalizeAdvertisedEndpoints = normalizeAdvertisedEndpoints;
13
+ exports.parseOpcTcpUrl = parseOpcTcpUrl;
14
+ const node_events_1 = require("node:events");
15
+ const node_net_1 = __importDefault(require("node:net"));
14
16
  const chalk_1 = __importDefault(require("chalk"));
15
- const async_1 = __importDefault(require("async"));
16
17
  const node_opcua_assert_1 = require("node-opcua-assert");
17
18
  const web_1 = require("node-opcua-crypto/web");
18
19
  const node_opcua_debug_1 = require("node-opcua-debug");
19
20
  const node_opcua_hostname_1 = require("node-opcua-hostname");
20
21
  const node_opcua_secure_channel_1 = require("node-opcua-secure-channel");
21
22
  const node_opcua_service_endpoints_1 = require("node-opcua-service-endpoints");
22
- const node_opcua_service_endpoints_2 = require("node-opcua-service-endpoints");
23
- const node_opcua_service_endpoints_3 = require("node-opcua-service-endpoints");
24
23
  const debugLog = (0, node_opcua_debug_1.make_debugLog)(__filename);
25
24
  const errorLog = (0, node_opcua_debug_1.make_errorLog)(__filename);
26
25
  const warningLog = (0, node_opcua_debug_1.make_warningLog)(__filename);
27
26
  const doDebug = (0, node_opcua_debug_1.checkDebugFlag)(__filename);
28
- const default_transportProfileUri = "http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary";
27
+ const UATCP_UASC_UABINARY = "http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary";
29
28
  function extractSocketData(socket, reason) {
30
29
  const { bytesRead, bytesWritten, remoteAddress, remoteFamily, remotePort, localAddress, localPort } = socket;
31
30
  const data = {
@@ -68,6 +67,7 @@ function dumpChannelInfo(channels) {
68
67
  console.log(" bytesRead = ", channel.bytesRead);
69
68
  console.log(" sessions = ", Object.keys(channel.sessionTokens).length);
70
69
  console.log(Object.values(channel.sessionTokens).map(d).join("\n"));
70
+ // biome-ignore lint/suspicious/noExplicitAny: accessing internal transport for debug dump
71
71
  const socket = channel.transport?._socket;
72
72
  if (!socket) {
73
73
  console.log(" SOCKET IS CLOSED");
@@ -79,15 +79,43 @@ function dumpChannelInfo(channels) {
79
79
  console.log("------------------------------------------------------");
80
80
  }
81
81
  const emptyCertificate = Buffer.alloc(0);
82
+ // biome-ignore lint/suspicious/noExplicitAny: deliberate null→PrivateKey sentinel
82
83
  const emptyPrivateKey = null;
83
84
  let OPCUAServerEndPointCounter = 0;
85
+ /**
86
+ * Normalize any `advertisedEndpoints` input into a uniform
87
+ * `AdvertisedEndpointConfig[]`.
88
+ *
89
+ * This coercion is done early so that all downstream code
90
+ * (endpoint generation, IP/hostname extraction) only deals
91
+ * with one type.
92
+ */
93
+ function normalizeAdvertisedEndpoints(raw) {
94
+ if (!raw)
95
+ return [];
96
+ const arr = Array.isArray(raw) ? raw : [raw];
97
+ return arr.map((entry) => (typeof entry === "string" ? { url: entry } : entry));
98
+ }
99
+ /**
100
+ * Parse an `opc.tcp://hostname:port` URL and extract hostname and port.
101
+ * @internal
102
+ */
103
+ function parseOpcTcpUrl(url) {
104
+ // URL class doesn't understand opc.tcp://, so swap to http://
105
+ const httpUrl = url.replace(/^opc\.tcp:\/\//i, "http://");
106
+ const parsed = new URL(httpUrl);
107
+ return {
108
+ hostname: parsed.hostname,
109
+ port: parsed.port ? Number.parseInt(parsed.port, 10) : 4840
110
+ };
111
+ }
84
112
  function getUniqueName(name, collection) {
85
113
  if (collection[name]) {
86
114
  let counter = 0;
87
- while (collection[name + "_" + counter.toString()]) {
115
+ while (collection[`${name}_${counter.toString()}`]) {
88
116
  counter++;
89
117
  }
90
- name = name + "_" + counter.toString();
118
+ name = `${name}_${counter.toString()}`;
91
119
  collection[name] = 1;
92
120
  return name;
93
121
  }
@@ -102,7 +130,7 @@ function getUniqueName(name, collection) {
102
130
  * note:
103
131
  * see OPCUA Release 1.03 part 4 page 108 7.1 ApplicationDescription
104
132
  */
105
- class OPCUAServerEndPoint extends events_1.EventEmitter {
133
+ class OPCUAServerEndPoint extends node_events_1.EventEmitter {
106
134
  /**
107
135
  * the tcp port
108
136
  */
@@ -165,7 +193,7 @@ class OPCUAServerEndPoint extends events_1.EventEmitter {
165
193
  this._privateKey = emptyPrivateKey;
166
194
  (0, node_opcua_assert_1.assert)(Object.keys(this._channels).length === 0, "OPCUAServerEndPoint channels must have been deleted");
167
195
  this._channels = {};
168
- this.serverInfo = new node_opcua_service_endpoints_3.ApplicationDescription({});
196
+ this.serverInfo = new node_opcua_service_endpoints_1.ApplicationDescription({});
169
197
  this._endpoints = [];
170
198
  (0, node_opcua_assert_1.assert)(this._endpoints.length === 0, "endpoints must have been deleted");
171
199
  this._endpoints = [];
@@ -225,11 +253,11 @@ class OPCUAServerEndPoint extends events_1.EventEmitter {
225
253
  return arr.length === 0 ? null : arr[0];
226
254
  }
227
255
  addEndpointDescription(securityMode, securityPolicy, options) {
228
- // istanbul ignore next
256
+ // c8 ignore next
229
257
  if (securityMode === node_opcua_secure_channel_1.MessageSecurityMode.None && securityPolicy !== node_opcua_secure_channel_1.SecurityPolicy.None) {
230
258
  throw new Error(" invalid security ");
231
259
  }
232
- // istanbul ignore next
260
+ // c8 ignore next
233
261
  if (securityMode !== node_opcua_secure_channel_1.MessageSecurityMode.None && securityPolicy === node_opcua_secure_channel_1.SecurityPolicy.None) {
234
262
  throw new Error(" invalid security ");
235
263
  }
@@ -238,9 +266,10 @@ class OPCUAServerEndPoint extends events_1.EventEmitter {
238
266
  const resourcePath = (options.resourcePath || "").replace(/\\/g, "/");
239
267
  (0, node_opcua_assert_1.assert)(resourcePath.length === 0 || resourcePath.charAt(0) === "/", "resourcePath should start with /");
240
268
  const hostname = options.hostname || (0, node_opcua_hostname_1.getFullyQualifiedDomainName)();
241
- const endpointUrl = `opc.tcp://${hostname}:${this.port}${resourcePath}`;
269
+ const effectivePort = options.advertisedPort ?? this.port;
270
+ const endpointUrl = `opc.tcp://${hostname}:${effectivePort}${resourcePath}`;
242
271
  const endpoint_desc = this.getEndpointDescription(securityMode, securityPolicy, endpointUrl);
243
- // istanbul ignore next
272
+ // c8 ignore next
244
273
  if (endpoint_desc) {
245
274
  throw new Error(" endpoint already exist");
246
275
  }
@@ -257,13 +286,14 @@ class OPCUAServerEndPoint extends events_1.EventEmitter {
257
286
  resourcePath: options.resourcePath,
258
287
  restricted: !!options.restricted,
259
288
  securityPolicies: options.securityPolicies || [],
289
+ advertisedPort: options.advertisedPort,
260
290
  userTokenTypes
261
291
  }, this));
262
292
  }
263
293
  addRestrictedEndpointDescription(options) {
264
294
  options = { ...options };
265
295
  options.restricted = true;
266
- return this.addEndpointDescription(node_opcua_secure_channel_1.MessageSecurityMode.None, node_opcua_secure_channel_1.SecurityPolicy.None, options);
296
+ this.addEndpointDescription(node_opcua_secure_channel_1.MessageSecurityMode.None, node_opcua_secure_channel_1.SecurityPolicy.None, options);
267
297
  }
268
298
  addStandardEndpointDescriptions(options) {
269
299
  options = options || {};
@@ -312,6 +342,59 @@ class OPCUAServerEndPoint extends events_1.EventEmitter {
312
342
  }
313
343
  }
314
344
  }
345
+ // ── Advertised endpoints (virtual — no TCP listener) ──────
346
+ // Normalize to AdvertisedEndpointConfig[] so downstream code
347
+ // only deals with one type.
348
+ const advertisedList = normalizeAdvertisedEndpoints(options.advertisedEndpoints);
349
+ // Main endpoint defaults (guaranteed non-null — assigned above)
350
+ const mainSecurityModes = options.securityModes || defaultSecurityModes;
351
+ const mainSecurityPolicies = options.securityPolicies || defaultSecurityPolicies;
352
+ const mainUserTokenTypes = options.userTokenTypes || defaultUserTokenTypes;
353
+ for (const config of advertisedList) {
354
+ const { hostname: advHostname, port: advPort } = parseOpcTcpUrl(config.url);
355
+ // Skip if this hostname+port combo was already covered
356
+ // by the regular hostname loop (same hostname, same port)
357
+ if (hostnames.some((h) => h.toLowerCase() === advHostname.toLowerCase()) && advPort === this.port) {
358
+ continue;
359
+ }
360
+ // Per-URL security overrides — fall back to main settings
361
+ const entrySecurityModes = config.securityModes ?? mainSecurityModes;
362
+ const entrySecurityPolicies = config.securityPolicies ?? mainSecurityPolicies;
363
+ let entryUserTokenTypes = config.userTokenTypes ?? mainUserTokenTypes;
364
+ // Handle allowAnonymous override: if explicitly false,
365
+ // filter out Anonymous even if the main config allows it
366
+ if (config.allowAnonymous === false) {
367
+ entryUserTokenTypes = entryUserTokenTypes.filter((t) => t !== node_opcua_service_endpoints_1.UserTokenType.Anonymous);
368
+ }
369
+ const optionsE = {
370
+ hostname: advHostname,
371
+ advertisedPort: advPort,
372
+ securityPolicies: entrySecurityPolicies,
373
+ userTokenTypes: entryUserTokenTypes,
374
+ allowUnsecurePassword: options.allowUnsecurePassword,
375
+ alternateHostname: options.alternateHostname,
376
+ resourcePath: options.resourcePath
377
+ };
378
+ if (entrySecurityModes.indexOf(node_opcua_secure_channel_1.MessageSecurityMode.None) >= 0) {
379
+ this.addEndpointDescription(node_opcua_secure_channel_1.MessageSecurityMode.None, node_opcua_secure_channel_1.SecurityPolicy.None, optionsE);
380
+ }
381
+ else {
382
+ if (!options.disableDiscovery) {
383
+ this.addRestrictedEndpointDescription(optionsE);
384
+ }
385
+ }
386
+ for (const securityMode of entrySecurityModes) {
387
+ if (securityMode === node_opcua_secure_channel_1.MessageSecurityMode.None) {
388
+ continue;
389
+ }
390
+ for (const securityPolicy of entrySecurityPolicies) {
391
+ if (securityPolicy === node_opcua_secure_channel_1.SecurityPolicy.None) {
392
+ continue;
393
+ }
394
+ this.addEndpointDescription(securityMode, securityPolicy, optionsE);
395
+ }
396
+ }
397
+ }
315
398
  }
316
399
  /**
317
400
  * returns the list of end point descriptions.
@@ -324,9 +407,13 @@ class OPCUAServerEndPoint extends events_1.EventEmitter {
324
407
  listen(callback) {
325
408
  (0, node_opcua_assert_1.assert)(typeof callback === "function");
326
409
  (0, node_opcua_assert_1.assert)(!this._started, "OPCUAServerEndPoint is already listening");
410
+ if (!this._server) {
411
+ callback(new Error("Server is not initialized"));
412
+ return;
413
+ }
327
414
  this._listen_callback = callback;
328
415
  this._server.on("error", (err) => {
329
- debugLog(chalk_1.default.red.bold(" error") + " port = " + this.port, err);
416
+ debugLog(`${chalk_1.default.red.bold(" error")} port = ${this.port}`, err);
330
417
  this._started = false;
331
418
  this._end_listen(err);
332
419
  });
@@ -344,8 +431,8 @@ class OPCUAServerEndPoint extends events_1.EventEmitter {
344
431
  (0, node_opcua_assert_1.assert)(!err, " cannot listen to port ");
345
432
  this._started = true;
346
433
  if (!this.port) {
347
- const add = this._server.address();
348
- this.port = typeof add !== "string" ? add.port : this.port;
434
+ const add = this._server?.address();
435
+ this.port = typeof add !== "string" ? add?.port || 0 : this.port;
349
436
  }
350
437
  this._end_listen();
351
438
  });
@@ -353,7 +440,7 @@ class OPCUAServerEndPoint extends events_1.EventEmitter {
353
440
  killClientSockets(callback) {
354
441
  for (const channel of this.getChannels()) {
355
442
  const hacked_channel = channel;
356
- if (hacked_channel.transport && hacked_channel.transport._socket) {
443
+ if (hacked_channel.transport?._socket) {
357
444
  // hacked_channel.transport._socket.close();
358
445
  hacked_channel.transport._socket.destroy();
359
446
  hacked_channel.transport._socket.emit("error", new Error("EPIPE"));
@@ -362,8 +449,9 @@ class OPCUAServerEndPoint extends events_1.EventEmitter {
362
449
  callback();
363
450
  }
364
451
  suspendConnection(callback) {
365
- if (!this._started) {
366
- return callback(new Error("Connection already suspended !!"));
452
+ if (!this._started || !this._server) {
453
+ callback(new Error("Connection already suspended !!"));
454
+ return;
367
455
  }
368
456
  // Stops the server from accepting new connections and keeps existing connections.
369
457
  // (note from nodejs doc: This function is asynchronous, the server is finally closed
@@ -373,7 +461,7 @@ class OPCUAServerEndPoint extends events_1.EventEmitter {
373
461
  // if the server was not open when it was closed.
374
462
  this._server.close(() => {
375
463
  this._started = false;
376
- debugLog("Connection has been closed !" + this.port);
464
+ debugLog(`Connection has been closed !${this.port}`);
377
465
  });
378
466
  this._started = false;
379
467
  callback();
@@ -395,15 +483,27 @@ class OPCUAServerEndPoint extends events_1.EventEmitter {
395
483
  this.suspendConnection(() => {
396
484
  // shutdown all opened channels ...
397
485
  const _channels = Object.values(this._channels);
398
- async_1.default.each(_channels, (channel, callback1) => {
399
- this.shutdown_channel(channel, callback1);
400
- }, (err) => {
401
- /* istanbul ignore next */
486
+ const promises = _channels.map((channel) => new Promise((resolve, reject) => {
487
+ this.shutdown_channel(channel, (err) => {
488
+ if (err) {
489
+ reject(err);
490
+ }
491
+ else {
492
+ resolve();
493
+ }
494
+ });
495
+ }));
496
+ Promise.all(promises)
497
+ .then(() => {
498
+ /* c8 ignore next */
402
499
  if (!(Object.keys(this._channels).length === 0)) {
403
500
  errorLog(" Bad !");
404
501
  }
405
502
  (0, node_opcua_assert_1.assert)(Object.keys(this._channels).length === 0, "channel must have unregistered themselves");
406
- callback(err || undefined);
503
+ callback();
504
+ })
505
+ .catch((err) => {
506
+ callback(err);
407
507
  });
408
508
  });
409
509
  }
@@ -449,23 +549,23 @@ class OPCUAServerEndPoint extends events_1.EventEmitter {
449
549
  return Object.keys(this._channels).length;
450
550
  }
451
551
  _dump_statistics() {
452
- this._server.getConnections((err, count) => {
552
+ this._server?.getConnections((_err, count) => {
453
553
  debugLog(chalk_1.default.cyan("CONCURRENT CONNECTION = "), count);
454
554
  });
455
- debugLog(chalk_1.default.cyan("MAX CONNECTIONS = "), this._server.maxConnections);
555
+ debugLog(chalk_1.default.cyan("MAX CONNECTIONS = "), this._server?.maxConnections);
456
556
  }
457
557
  _setup_server() {
458
558
  (0, node_opcua_assert_1.assert)(!this._server);
459
- this._server = net_1.default.createServer({ pauseOnConnect: true }, this._on_client_connection.bind(this));
559
+ this._server = node_net_1.default.createServer({ pauseOnConnect: true }, this._on_client_connection.bind(this));
460
560
  // xx console.log(" Server with max connections ", self.maxConnections);
461
561
  this._server.maxConnections = this.maxConnections + 1; // plus one extra
462
562
  this._listen_callback = undefined;
463
563
  this._server
464
564
  .on("connection", (socket) => {
465
- // istanbul ignore next
565
+ // c8 ignore next
466
566
  if (doDebug) {
467
567
  this._dump_statistics();
468
- debugLog("server connected with : " + socket.remoteAddress + ":" + socket.remotePort);
568
+ debugLog(`server connected with : ${socket.remoteAddress}:${socket.remotePort}`);
469
569
  }
470
570
  })
471
571
  .on("close", () => {
@@ -489,7 +589,7 @@ class OPCUAServerEndPoint extends events_1.EventEmitter {
489
589
  const deny_connection = () => {
490
590
  console.log(chalk_1.default.bgWhite.cyan("OPCUAServerEndPoint#_on_client_connection " +
491
591
  "The maximum number of connection has been reached - Connection is refused"));
492
- const reason = "maxConnections reached (" + this.maxConnections + ")";
592
+ const reason = `maxConnections reached (${this.maxConnections})`;
493
593
  const socketData = extractSocketData(socket, reason);
494
594
  this.emit("connectionRefused", socketData);
495
595
  socket.end();
@@ -498,7 +598,7 @@ class OPCUAServerEndPoint extends events_1.EventEmitter {
498
598
  const establish_connection = () => {
499
599
  const nbConnections = Object.keys(this._channels).length;
500
600
  if (nbConnections >= this.maxConnections) {
501
- warningLog(" nbConnections ", nbConnections, " self._server.maxConnections", this._server.maxConnections, this.maxConnections);
601
+ warningLog(" nbConnections ", nbConnections, " self._server.maxConnections", this._server?.maxConnections, this.maxConnections);
502
602
  deny_connection();
503
603
  return;
504
604
  }
@@ -517,7 +617,7 @@ class OPCUAServerEndPoint extends events_1.EventEmitter {
517
617
  this._un_pre_registerChannel(channel);
518
618
  debugLog(chalk_1.default.yellow.bold("Channel#init done"), err);
519
619
  if (err) {
520
- const reason = "openSecureChannel has Failed " + err.message;
620
+ const reason = `openSecureChannel has Failed ${err.message}`;
521
621
  const socketData = extractSocketData(socket, reason);
522
622
  const channelData = extractChannelData(channel);
523
623
  this.emit("openSecureChannelFailure", socketData, channelData);
@@ -611,7 +711,7 @@ class OPCUAServerEndPoint extends events_1.EventEmitter {
611
711
  this.bytesReadInOldChannels += channel.bytesRead;
612
712
  this.transactionsCountOldChannels += channel.transactionsCount;
613
713
  delete this._channels[channel.hashKey];
614
- // istanbul ignore next
714
+ // c8 ignore next
615
715
  if (doDebug) {
616
716
  this._dump_statistics();
617
717
  debugLog("un-registering channel - Count = ", this.currentChannelCount);
@@ -621,7 +721,6 @@ class OPCUAServerEndPoint extends events_1.EventEmitter {
621
721
  _end_listen(err) {
622
722
  if (!this._listen_callback)
623
723
  return;
624
- (0, node_opcua_assert_1.assert)(typeof this._listen_callback === "function");
625
724
  this._listen_callback(err);
626
725
  this._listen_callback = undefined;
627
726
  }
@@ -646,18 +745,19 @@ class OPCUAServerEndPoint extends events_1.EventEmitter {
646
745
  _prevent_DDOS_Attack(establish_connection, deny_connection) {
647
746
  const nbConnections = this.activeChannelCount;
648
747
  if (nbConnections >= this.maxConnections) {
649
- // istanbul ignore next
650
- errorLog(chalk_1.default.bgRed.white("PREVENTING DDOS ATTACK => maxConnection =" + this.maxConnections));
748
+ // c8 ignore next
749
+ errorLog(chalk_1.default.bgRed.white(`PREVENTING DDOS ATTACK => maxConnection =${this.maxConnections}`));
651
750
  const unused_channels = this.getChannels().filter((channel1) => {
652
751
  return !channel1.hasSession;
653
752
  });
654
753
  if (unused_channels.length === 0) {
655
- doDebug && console.log(this.getChannels()
656
- .map(({ status, isOpened, hasSession }) => `${status} ${isOpened} ${hasSession}\n`)
657
- .join(" "));
754
+ doDebug &&
755
+ console.log(this.getChannels()
756
+ .map(({ status, isOpened, hasSession }) => `${status} ${isOpened} ${hasSession}\n`)
757
+ .join(" "));
658
758
  // all channels are in used , we cannot get any
659
759
  errorLog(`All channels are in used ! we cannot cancel any ${this.getChannels().length}`);
660
- // istanbul ignore next
760
+ // c8 ignore next
661
761
  if (doDebug) {
662
762
  console.log(" - all channels are used !!!!");
663
763
  false && dumpChannelInfo(this.getChannels());
@@ -665,14 +765,14 @@ class OPCUAServerEndPoint extends events_1.EventEmitter {
665
765
  setTimeout(deny_connection, 1000);
666
766
  return;
667
767
  }
668
- // istanbul ignore next
768
+ // c8 ignore next
669
769
  if (doDebug) {
670
770
  console.log(" - Unused channels that can be clobbered", unused_channels.map((channel1) => channel1.hashKey).join(" "));
671
771
  }
672
772
  const channel = unused_channels[0];
673
773
  errorLog(`${unused_channels.length} : Forcefully closing oldest channel that have no session: ${channel.hashKey}`);
674
774
  channel.close(() => {
675
- // istanbul ignore next
775
+ // c8 ignore next
676
776
  if (doDebug) {
677
777
  console.log(" _ Unused channel has been closed ", channel.hashKey);
678
778
  }
@@ -712,7 +812,6 @@ function estimateSecurityLevel(securityMode, securityPolicy) {
712
812
  case node_opcua_secure_channel_1.SecurityPolicy.Aes256_Sha256_RsaPss:
713
813
  return 7 + offset;
714
814
  default:
715
- case node_opcua_secure_channel_1.SecurityPolicy.None:
716
815
  return 1;
717
816
  }
718
817
  }
@@ -732,7 +831,7 @@ function _makeEndpointDescription(options, parent) {
732
831
  options.securityLevel === undefined
733
832
  ? estimateSecurityLevel(options.securityMode, options.securityPolicy)
734
833
  : options.securityLevel;
735
- (0, node_opcua_assert_1.assert)(isFinite(options.securityLevel), "expecting a valid securityLevel");
834
+ (0, node_opcua_assert_1.assert)(Number.isFinite(options.securityLevel), "expecting a valid securityLevel");
736
835
  const securityPolicyUri = (0, node_opcua_secure_channel_1.toURI)(options.securityPolicy);
737
836
  const userIdentityTokens = [];
738
837
  const registerIdentity2 = (tokenType, securityPolicy, name) => {
@@ -784,7 +883,7 @@ function _makeEndpointDescription(options, parent) {
784
883
  // when channel session security is not "None",
785
884
  // userIdentityTokens can be left to null.
786
885
  // in this case this mean that secure policy will be the same as connection security policy
787
- // istanbul ignore next
886
+ // c8 ignore next
788
887
  if (process.env.NODEOPCUA_SERVER_EMULATE_SIEMENS) {
789
888
  // However, for some reason SIEMENS plc requires that password get encrypted even though
790
889
  // the secure channel is also encrypted ....
@@ -829,7 +928,7 @@ function _makeEndpointDescription(options, parent) {
829
928
  });
830
929
  }
831
930
  // return the endpoint object
832
- const endpoint = new node_opcua_service_endpoints_2.EndpointDescription({
931
+ const endpoint = new node_opcua_service_endpoints_1.EndpointDescription({
833
932
  endpointUrl: "<to be evaluated at run time>", // options.endpointUrl,
834
933
  server: undefined, // options.server,
835
934
  serverCertificate: options.serverCertificateChain,
@@ -837,13 +936,14 @@ function _makeEndpointDescription(options, parent) {
837
936
  securityPolicyUri,
838
937
  userIdentityTokens,
839
938
  securityLevel: options.securityLevel,
840
- transportProfileUri: default_transportProfileUri
939
+ transportProfileUri: UATCP_UASC_UABINARY
841
940
  });
842
941
  endpoint._parent = parent;
843
942
  // endpointUrl is dynamic as port number may be adjusted
844
943
  // when the tcp socket start listening
944
+ // biome-ignore lint/suspicious/noExplicitAny: __defineGetter__ not in standard typings
845
945
  endpoint.__defineGetter__("endpointUrl", () => {
846
- const port = endpoint._parent.port;
946
+ const port = options.advertisedPort ?? endpoint._parent.port;
847
947
  const resourcePath = options.resourcePath || "";
848
948
  const hostname = options.hostname;
849
949
  const endpointUrl = `opc.tcp://${hostname}:${port}${resourcePath}`;
@@ -862,7 +962,7 @@ function _makeEndpointDescription(options, parent) {
862
962
  *
863
963
  */
864
964
  function matching_endpoint(securityMode, securityPolicy, endpointUrl, endpoint) {
865
- (0, node_opcua_assert_1.assert)(endpoint instanceof node_opcua_service_endpoints_2.EndpointDescription);
965
+ (0, node_opcua_assert_1.assert)(endpoint instanceof node_opcua_service_endpoints_1.EndpointDescription);
866
966
  const endpoint_securityPolicy = (0, node_opcua_secure_channel_1.fromURI)(endpoint.securityPolicyUri);
867
967
  if (endpointUrl && endpoint.endpointUrl !== endpointUrl) {
868
968
  return false;