• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

node-opcua / node-opcua / 22636309940

03 Mar 2026 06:03PM UTC coverage: 92.756% (+1.9%) from 90.81%
22636309940

push

github

erossignon
test: disable ENOENT on overlapping trustCertificate invocations in test environments with concurrency tests

18684 of 22141 branches covered (84.39%)

23 of 37 new or added lines in 1 file covered. (62.16%)

5775 existing lines in 228 files now uncovered.

160668 of 173216 relevant lines covered (92.76%)

875869.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.56
/packages/node-opcua-server/source/base_server.ts
1
/**
40✔
2
 * @module node-opcua-server
4✔
3
 */
4✔
4
// tslint:disable:no-console
4✔
5
import fs from "fs";
4✔
6
import path from "path";
4✔
7
import os from "os";
4✔
8
import { types } from "util";
4✔
9
import async from "async";
4✔
10
import chalk from "chalk";
4✔
11
import { assert } from "node-opcua-assert";
4✔
12
import { withLock } from "@ster5/global-mutex";
4✔
13

4✔
14
import {
4✔
15
    getDefaultCertificateManager,
4✔
16
    ICertificateManager,
4✔
17
    makeSubject,
4✔
18
    OPCUACertificateManager
4✔
19
} from "node-opcua-certificate-manager";
4✔
20
import { IOPCUASecureObjectOptions, makeApplicationUrn, OPCUASecureObject } from "node-opcua-common";
4✔
21
import { coerceLocalizedText, LocalizedText } from "node-opcua-data-model";
4✔
22
import { installPeriodicClockAdjustment, uninstallPeriodicClockAdjustment } from "node-opcua-date-time";
4✔
23
import { checkDebugFlag, make_debugLog, make_errorLog } from "node-opcua-debug";
4✔
24
import { displayTraceFromThisProjectOnly } from "node-opcua-debug";
4✔
25
import {
4✔
26
    extractFullyQualifiedDomainName,
4✔
27
    getFullyQualifiedDomainName,
4✔
28
    getHostname,
4✔
29
    resolveFullyQualifiedDomainName
4✔
30
} from "node-opcua-hostname";
4✔
31
import { Message, Response, ServerSecureChannelLayer, ServerSecureChannelParent } from "node-opcua-secure-channel";
4✔
32
import { FindServersRequest, FindServersResponse } from "node-opcua-service-discovery";
4✔
33
import { ApplicationType, GetEndpointsResponse } from "node-opcua-service-endpoints";
4✔
34
import { ApplicationDescription } from "node-opcua-service-endpoints";
4✔
35
import { ServiceFault } from "node-opcua-service-secure-channel";
4✔
36
import { StatusCode, StatusCodes } from "node-opcua-status-code";
4✔
37
import { ApplicationDescriptionOptions } from "node-opcua-types";
4✔
38
import { EndpointDescription, GetEndpointsRequest } from "node-opcua-types";
4✔
39
import { matchUri, checkFileExistsAndIsNotEmpty } from "node-opcua-utils";
4✔
40

4✔
41
import { performCertificateSanityCheck } from "node-opcua-client";
4✔
42
import { OPCUAServerEndPoint } from "./server_end_point";
4✔
43
import { IChannelData } from "./i_channel_data";
4✔
44
import { ISocketData } from "./i_socket_data";
4✔
45

4✔
46
const doDebug = checkDebugFlag(__filename);
4✔
47
const debugLog = make_debugLog(__filename);
4✔
48
const errorLog = make_errorLog(__filename);
4✔
49
const warningLog = errorLog;
4✔
50

4✔
51
const default_server_info = {
4✔
52
    // The globally unique identifier for the application instance. This URI is used as
4✔
53
    // ServerUri in Services if the application is a Server.
4✔
54
    applicationUri: makeApplicationUrn(os.hostname(), "NodeOPCUA-Server"),
4✔
55

4✔
56
    // The globally unique identifier for the product.
4✔
57
    productUri: "NodeOPCUA-Server",
4✔
58

4✔
59
    // A localized descriptive name for the application.
4✔
60
    applicationName: { text: "NodeOPCUA", locale: "en" },
4✔
61
    applicationType: ApplicationType.Server,
4✔
62
    gatewayServerUri: "",
4✔
63

4✔
64
    discoveryProfileUri: "",
4✔
65

4✔
66
    discoveryUrls: []
4✔
67
};
4✔
68

4✔
69
function cleanupEndpoint(endpoint: OPCUAServerEndPoint) {
584✔
70
    if (endpoint._on_new_channel) {
584✔
71
        assert(typeof endpoint._on_new_channel === "function");
576✔
72
        endpoint.removeListener("newChannel", endpoint._on_new_channel);
576✔
73
        endpoint._on_new_channel = undefined;
576✔
74
    }
576✔
75

584✔
76
    if (endpoint._on_close_channel) {
584✔
77
        assert(typeof endpoint._on_close_channel === "function");
576✔
78
        endpoint.removeListener("closeChannel", endpoint._on_close_channel);
576✔
79
        endpoint._on_close_channel = undefined;
576✔
80
    }
576✔
81
    if (endpoint._on_connectionRefused) {
584✔
82
        assert(typeof endpoint._on_connectionRefused === "function");
576✔
83
        endpoint.removeListener("connectionRefused", endpoint._on_connectionRefused);
576✔
84
        endpoint._on_connectionRefused = undefined;
576✔
85
    }
576✔
86
    if (endpoint._on_openSecureChannelFailure) {
584✔
87
        assert(typeof endpoint._on_openSecureChannelFailure === "function");
576✔
88
        endpoint.removeListener("openSecureChannelFailure", endpoint._on_openSecureChannelFailure);
576✔
89
        endpoint._on_openSecureChannelFailure = undefined;
576✔
90
    }
576✔
91
}
584✔
92

4✔
93
/**
4✔
94
 *
4✔
95
 */
4✔
96
export interface OPCUABaseServerOptions extends IOPCUASecureObjectOptions {
4✔
97
    /**
4✔
98
     * the information used in the end point description
4✔
99
     */
4✔
100
    serverInfo?: ApplicationDescriptionOptions;
4✔
101
    /**
4✔
102
     * the server Certificate Manager
4✔
103
     */
4✔
104
    serverCertificateManager?: OPCUACertificateManager;
4✔
105
}
4✔
106

4✔
107
const emptyCallback = () => {
4✔
108
    /* empty */
3,066✔
109
};
3,066✔
110

4✔
111
export class OPCUABaseServer extends OPCUASecureObject {
4✔
112
    public static makeServiceFault = makeServiceFault;
38✔
113

38✔
114
    /**
38✔
115
     * The type of server
38✔
116
     */
38✔
117
    get serverType(): ApplicationType {
38✔
118
        return this.serverInfo.applicationType;
875✔
119
    }
875✔
120

38✔
121
    public serverInfo: ApplicationDescription;
38✔
122
    public endpoints: OPCUAServerEndPoint[];
38✔
123
    public readonly serverCertificateManager: OPCUACertificateManager;
38✔
124
    public capabilitiesForMDNS: string[];
38✔
125
    protected _preInitTask: any[];
38✔
126

38✔
127
    protected options: OPCUABaseServerOptions;
38✔
128

38✔
129
    constructor(options?: OPCUABaseServerOptions) {
38✔
130
        options = options || ({} as OPCUABaseServerOptions);
586✔
131

586✔
132
        if (!options.serverCertificateManager) {
586✔
133
            options.serverCertificateManager = getDefaultCertificateManager("PKI");
186✔
134
        }
186✔
135
        options.privateKeyFile = options.privateKeyFile || options.serverCertificateManager.privateKey;
586✔
136
        options.certificateFile =
586✔
137
            options.certificateFile || path.join(options.serverCertificateManager.rootDir, "own/certs/certificate.pem");
586✔
138

586✔
139
        super(options);
586✔
140

586✔
141
        this.serverCertificateManager = options.serverCertificateManager;
586✔
142
        this.capabilitiesForMDNS = [];
586✔
143
        this.endpoints = [];
586✔
144
        this.options = options;
586✔
145
        this._preInitTask = [];
586✔
146

586✔
147
        const serverInfo: ApplicationDescriptionOptions = {
586✔
148
            ...default_server_info,
586✔
149
            ...options.serverInfo
586✔
150
        };
586✔
151
        serverInfo.applicationName = coerceLocalizedText(serverInfo.applicationName);
586✔
152
        this.serverInfo = new ApplicationDescription(serverInfo);
586✔
153

586✔
154
        if (this.serverInfo.applicationName.toString().match(/urn:/)) {
586!
155
            errorLog("[NODE-OPCUA-E06] application name cannot be a urn", this.serverInfo.applicationName.toString());
×
UNCOV
156
        }
×
157

586✔
158
        this.serverInfo.applicationName!.locale = this.serverInfo.applicationName?.locale || "en";
586✔
159

586✔
160
        if (!this.serverInfo.applicationName?.locale) {
586!
161
            warningLog(
×
UNCOV
162
                "[NODE-OPCUA-W24] the server applicationName must have a valid locale : ",
×
UNCOV
163
                this.serverInfo.applicationName.toString()
×
UNCOV
164
            );
×
UNCOV
165
        }
×
166

586✔
167
        const __applicationUri = serverInfo.applicationUri || "";
586!
168

586✔
169
        (this.serverInfo as any).__defineGetter__("applicationUri", () => resolveFullyQualifiedDomainName(__applicationUri));
586✔
170

586✔
171
        this._preInitTask.push(async () => {
586✔
172
            const fqdn = await extractFullyQualifiedDomainName();
584✔
173
        });
586✔
174

586✔
175
        this._preInitTask.push(async () => {
586✔
176
            await this.initializeCM();
584✔
177
        });
586✔
178
    }
586✔
179

38✔
180
    protected async createDefaultCertificate(): Promise<void> {
38✔
181
        if (fs.existsSync(this.certificateFile)) {
604✔
182
            return;
506✔
183
        }
506✔
184

130✔
185
        // collect all hostnames
130✔
186
        const hostnames = [];
130✔
187
        for (const e of this.endpoints) {
604!
188
            for (const ee of e.endpointDescriptions()) {
×
UNCOV
189
                /* to do */
×
UNCOV
190
            }
×
UNCOV
191
        }
×
192
        if (!checkFileExistsAndIsNotEmpty(this.certificateFile)) {
130✔
193
            await withLock({ fileToLock: this.certificateFile + ".mutex" }, async () => {
98✔
194
                if (checkFileExistsAndIsNotEmpty(this.certificateFile)) {
98!
195
                    return;
×
UNCOV
196
                }
×
197
                const applicationUri = this.serverInfo.applicationUri!;
98✔
198
                const fqdn = getFullyQualifiedDomainName();
98✔
199
                const hostname = getHostname();
98✔
200
                const dns = [...new Set([fqdn, hostname])];
98✔
201

98✔
202
                await this.serverCertificateManager.createSelfSignedCertificate({
98✔
203
                    applicationUri,
98✔
204
                    dns,
98✔
205
                    // ip: await getIpAddresses(),
98✔
206
                    outputFile: this.certificateFile,
98✔
207

98✔
208
                    subject: makeSubject(this.serverInfo.applicationName.text!, hostname),
98✔
209

98✔
210
                    startDate: new Date(),
98✔
211
                    validity: 365 * 10 // 10 years
98✔
212
                });
98✔
213
            });
98✔
214
        }
98✔
215
    }
604✔
216

38✔
217
    public async initializeCM(): Promise<void> {
38✔
218
        await this.serverCertificateManager.initialize();
604✔
219
        await this.createDefaultCertificate();
604✔
220
        debugLog("privateKey      = ", this.privateKeyFile, this.serverCertificateManager.privateKey);
604✔
221
        debugLog("certificateFile = ", this.certificateFile);
604✔
222
        await performCertificateSanityCheck(this, "server", this.serverCertificateManager, this.serverInfo.applicationUri!);
604✔
223
    }
604✔
224

38✔
225
    /**
38✔
226
     * start all registered endPoint, in parallel, and call done when all endPoints are listening.
38✔
227
     */
38✔
228
    public start(done: (err?: Error | null) => void): void {
38✔
229
        assert(typeof done === "function");
574✔
230
        this.startAsync()
574✔
231
            .then(() => done(null))
574✔
232
            .catch((err) => done(err));
574✔
233
    }
574✔
234

38✔
235
    protected async performPreInitialization(): Promise<void> {
38✔
236
        const tasks = this._preInitTask;
1,124✔
237
        this._preInitTask = [];
1,124✔
238
        for (const task of tasks) {
1,124✔
239
            await task();
1,750✔
240
        }
1,750✔
241
    }
1,124✔
242

38✔
243
    protected async startAsync(): Promise<void> {
38✔
244
        await this.performPreInitialization();
574✔
245

574✔
246
        assert(Array.isArray(this.endpoints));
574✔
247
        assert(this.endpoints.length > 0, "We need at least one end point");
574✔
248

574✔
249
        installPeriodicClockAdjustment();
574✔
250
        // eslint-disable-next-line @typescript-eslint/no-this-alias
574✔
251
        const server = this;
574✔
252
        const _on_new_channel = function (this: OPCUAServerEndPoint, channel: ServerSecureChannelLayer) {
574✔
253
            server.emit("newChannel", channel, this);
2,948✔
254
        };
544✔
255

574✔
256
        const _on_close_channel = function (this: OPCUAServerEndPoint, channel: ServerSecureChannelLayer) {
574✔
257
            server.emit("closeChannel", channel, this);
2,912✔
258
        };
544✔
259

574✔
260
        const _on_connectionRefused = function (this: OPCUAServerEndPoint, socketData: ISocketData) {
574✔
261
            server.emit("connectionRefused", socketData, this);
34✔
262
        };
542✔
263

574✔
264
        const _on_openSecureChannelFailure = function (
574✔
265
            this: OPCUAServerEndPoint,
16✔
266
            socketData: ISocketData,
16✔
267
            channelData: IChannelData
16✔
268
        ) {
16✔
269
            server.emit("openSecureChannelFailure", socketData, channelData, this);
16✔
270
        };
554✔
271

574✔
272
        const promises: Promise<void>[] = [];
574✔
273

574✔
274
        for (const endpoint of this.endpoints) {
574✔
275
            assert(!endpoint._on_close_channel);
576✔
276

576✔
277
            endpoint._on_new_channel = _on_new_channel;
576✔
278
            endpoint.on("newChannel", endpoint._on_new_channel);
576✔
279

576✔
280
            endpoint._on_close_channel = _on_close_channel;
576✔
281
            endpoint.on("closeChannel", endpoint._on_close_channel);
576✔
282

576✔
283
            endpoint._on_connectionRefused = _on_connectionRefused;
576✔
284
            endpoint.on("connectionRefused", endpoint._on_connectionRefused);
576✔
285

576✔
286
            endpoint._on_openSecureChannelFailure = _on_openSecureChannelFailure;
576✔
287
            endpoint.on("openSecureChannelFailure", endpoint._on_openSecureChannelFailure);
576✔
288

576✔
289
            promises.push(new Promise<void>((resolve, reject) => endpoint.start((err) => (err ? reject(err) : resolve()))));
576✔
290
        }
576✔
291
        await Promise.all(promises);
574✔
292
    }
572✔
293

38✔
294
    /**
38✔
295
     * shutdown all server endPoints
38✔
296
     */
38✔
297
    public shutdown(done: (err?: Error) => void): void {
38✔
298
        assert(typeof done === "function");
582✔
299
        uninstallPeriodicClockAdjustment();
582✔
300
        this.serverCertificateManager.dispose().then(() => {
582✔
301
            debugLog("OPCUABaseServer#shutdown starting");
582✔
302
            async.forEach(
582✔
303
                this.endpoints,
582✔
304
                (endpoint: OPCUAServerEndPoint, callback: (err?: Error) => void) => {
582✔
305
                    cleanupEndpoint(endpoint);
584✔
306
                    endpoint.shutdown(callback);
584✔
307
                },
582✔
308
                (err?: Error | null) => {
582✔
309
                    debugLog("shutdown completed");
582✔
310
                    done(err!);
582✔
311
                }
582✔
312
            );
582✔
313
        });
582✔
314
    }
582✔
315

38✔
316
    public async shutdownChannels(): Promise<void>;
38✔
317
    public shutdownChannels(callback: (err?: Error | null) => void): void;
38✔
318
    public shutdownChannels(callback?: (err?: Error | null) => void): Promise<void> | void {
38✔
319
        assert(typeof callback === "function");
8✔
320
        debugLog("OPCUABaseServer#shutdownChannels");
8✔
321
        async.forEach(
8✔
322
            this.endpoints,
8✔
323
            (endpoint: OPCUAServerEndPoint, inner_callback: (err?: Error | null) => void) => {
8✔
324
                debugLog(" shutting down endpoint ", endpoint.endpointDescriptions()[0].endpointUrl);
8✔
325
                async.series(
8✔
326
                    [
8✔
327
                        // xx                  (callback2: (err?: Error| null) => void) => {
8✔
328
                        // xx                      endpoint.suspendConnection(callback2);
8✔
329
                        // xx                  },
8✔
330
                        (callback2: (err?: Error | null) => void) => {
8✔
331
                            endpoint.abruptlyInterruptChannels();
8✔
332
                            endpoint.shutdown(callback2);
8✔
333
                        }
8✔
334
                        // xx              (callback2: (err?: Error| null) => void) => {
8✔
335
                        // xx                 endpoint.restoreConnection(callback2);
8✔
336
                        // xx              }
8✔
337
                    ],
8✔
338
                    inner_callback
8✔
339
                );
8✔
340
            },
8✔
341
            callback!
8✔
342
        );
8✔
343
    }
8✔
344

38✔
345
    /**
38✔
346
     * @private
38✔
347
     */
38✔
348
    public on_request(message: Message, channel: ServerSecureChannelLayer): void {
38✔
349
        assert(message.request);
92,757✔
350
        assert(message.requestId !== 0);
92,757✔
351
        const request = message.request;
92,757✔
352

92,757✔
353
        // install channel._on_response so we can intercept its call and  emit the "response" event.
92,757✔
354
        if (!channel._on_response) {
92,757✔
355
            channel._on_response = (msg: string, response1: Response /*, inner_message: Message*/) => {
2,818✔
356
                this.emit("response", response1, channel);
93,194✔
357
            };
2,824✔
358
        }
2,818✔
359

92,757✔
360
        // prepare request
92,757✔
361
        this.prepare(message, channel);
92,757✔
362

92,757✔
363
        if (doDebug) {
92,757!
364
            debugLog(
×
UNCOV
365
                chalk.green.bold("--------------------------------------------------------"),
×
UNCOV
366
                channel.channelId,
×
UNCOV
367
                request.schema.name
×
UNCOV
368
            );
×
UNCOV
369
        }
×
370

92,757✔
371
        let errMessage: string;
92,757✔
372
        let response: Response;
92,757✔
373

92,757✔
374
        this.emit("request", request, channel);
92,757✔
375

92,757✔
376
        try {
92,757✔
377
            // handler must be named _on_ActionRequest()
92,757✔
378
            const handler = (this as any)["_on_" + request.schema.name];
92,757✔
379
            if (typeof handler === "function") {
92,757✔
380
                // eslint-disable-next-line prefer-rest-params
92,757✔
381
                handler.apply(this, arguments);
92,757✔
382
            } else {
92,757!
383
                errMessage = "[NODE-OPCUA-W07] Unsupported Service : " + request.schema.name;
×
384
                warningLog(errMessage);
×
385
                debugLog(chalk.red.bold(errMessage));
×
386
                response = makeServiceFault(StatusCodes.BadServiceUnsupported, [errMessage]);
×
387
                channel.send_response("MSG", response, message, emptyCallback);
×
UNCOV
388
            }
×
389
        } catch (err) {
92,757!
390
            /* c8 ignore next */
4✔
391
            const errMessage1 = "[NODE-OPCUA-W08] EXCEPTION CAUGHT WHILE PROCESSING REQUEST !! " + request.schema.name;
4✔
392
            warningLog(chalk.red.bold(errMessage1));
×
393
            warningLog(request.toString());
×
394
            displayTraceFromThisProjectOnly(err as Error);
×
UNCOV
395

×
396
            let additional_messages = [];
×
397
            additional_messages.push("EXCEPTION CAUGHT WHILE PROCESSING REQUEST !!! " + request.schema.name);
×
398
            if (types.isNativeError(err)) {
×
399
                additional_messages.push(err.message);
×
400
                if (err.stack) {
×
401
                    additional_messages = additional_messages.concat(err.stack.split("\n"));
×
UNCOV
402
                }
×
UNCOV
403
            }
×
404
            response = makeServiceFault(StatusCodes.BadInternalError, additional_messages);
×
UNCOV
405

×
406
            channel.send_response("MSG", response, message, emptyCallback);
×
UNCOV
407
        }
×
408
    }
92,757✔
409

38✔
410
    /**
38✔
411
     * @private
38✔
412
     */
38✔
413
    public _get_endpoints(endpointUrl?: string | null): EndpointDescription[] {
38✔
414
        let endpoints: EndpointDescription[] = [];
8,536✔
415
        for (const endPoint of this.endpoints) {
8,536✔
416
            const ep = endPoint.endpointDescriptions();
8,544✔
417
            const epFiltered = endpointUrl ? ep.filter((e) => matchUri(e.endpointUrl, endpointUrl)) : ep;
8,544✔
418
            endpoints = endpoints.concat(epFiltered);
8,544✔
419
        }
8,544✔
420
        return endpoints;
8,536✔
421
    }
8,536✔
422
    /**
38✔
423
     * get one of the possible endpointUrl
38✔
424
     */
38✔
425
    public getEndpointUrl(): string {
38✔
426
        return this._get_endpoints()[0].endpointUrl!;
1,302✔
427
    }
1,302✔
428

38✔
429
    public getDiscoveryUrls(): string[] {
38✔
430
        const discoveryUrls = this.endpoints.map((e: OPCUAServerEndPoint) => {
458✔
431
            return e.endpointDescriptions()[0].endpointUrl!;
458✔
432
        });
458✔
433
        return discoveryUrls;
458✔
434
    }
458✔
435

38✔
436
    public getServers(channel: ServerSecureChannelLayer): ApplicationDescription[] {
38✔
437
        this.serverInfo.discoveryUrls = this.getDiscoveryUrls();
8✔
438
        const servers = [this.serverInfo];
8✔
439
        return servers;
8✔
440
    }
8✔
441

38✔
442
    /**
38✔
443
     * set all the end point into a state where they do not accept further connections
38✔
444
     *
38✔
445
     * note:
38✔
446
     *     this method is useful for testing purpose
38✔
447
     *
38✔
448
     */
38✔
449
    public async suspendEndPoints(): Promise<void>;
38✔
450
    public suspendEndPoints(callback: (err?: Error) => void): void;
38✔
451
    public suspendEndPoints(callback?: (err?: Error) => void): void | Promise<void> {
38✔
452
        /* c8 ignore next */
4✔
453
        if (!callback) {
4✔
UNCOV
454
            throw new Error("Internal Error");
×
UNCOV
455
        }
×
456
        async.forEach(
8✔
457
            this.endpoints,
8✔
458
            (ep: OPCUAServerEndPoint, _inner_callback) => {
8✔
459
                /* c8 ignore next */
4✔
460
                if (doDebug) {
4✔
UNCOV
461
                    debugLog("Suspending ", ep.endpointDescriptions()[0].endpointUrl);
×
UNCOV
462
                }
×
463

8✔
464
                ep.suspendConnection((err?: Error | null) => {
8✔
465
                    /* c8 ignore next */
4✔
466
                    if (doDebug) {
4✔
UNCOV
467
                        debugLog("Suspended ", ep.endpointDescriptions()[0].endpointUrl);
×
UNCOV
468
                    }
×
469
                    _inner_callback(err);
8✔
470
                });
8✔
471
            },
8✔
472
            (err?: Error | null) => callback(err!)
8✔
473
        );
8✔
474
    }
8✔
475

38✔
476
    /**
38✔
477
     * set all the end point into a state where they do accept connections
38✔
478
     * note:
38✔
479
     *    this method is useful for testing purpose
38✔
480
     */
38✔
481
    public async resumeEndPoints(): Promise<void>;
38✔
482
    public resumeEndPoints(callback: (err?: Error) => void): void;
38✔
483
    public resumeEndPoints(callback?: (err?: Error) => void): void | Promise<void> {
38✔
484
        async.forEach(
8✔
485
            this.endpoints,
8✔
486
            (ep: OPCUAServerEndPoint, _inner_callback) => {
8✔
487
                ep.restoreConnection(_inner_callback);
8✔
488
            },
8✔
489
            (err?: Error | null) => callback!(err!)
8✔
490
        );
8✔
491
    }
8✔
492

38✔
493
    protected prepare(message: Message, channel: ServerSecureChannelLayer): void {
38✔
494
        /* empty */
1,357✔
495
    }
1,357✔
496

38✔
497
    /**
38✔
498
     * @private
38✔
499
     */
38✔
500
    protected _on_GetEndpointsRequest(message: Message, channel: ServerSecureChannelLayer): void {
38✔
501
        const request = message.request as GetEndpointsRequest;
3,036✔
502

3,036✔
503
        assert(request.schema.name === "GetEndpointsRequest");
3,036✔
504

3,036✔
505
        const response = new GetEndpointsResponse({});
3,036✔
506

3,036✔
507
        /**
3,036✔
508
         * endpointUrl        String        The network address that the Client used to access the DiscoveryEndpoint.
3,036✔
509
         *                      The Server uses this information for diagnostics and to determine what URLs to return in the response.
3,036✔
510
         *                      The Server should return a suitable default URL if it does not recognize the HostName in the URL
3,036✔
511
         * localeIds   []LocaleId        List of locales to use.
3,036✔
512
         *                          Specifies the locale to use when returning human readable strings.
3,036✔
513
         * profileUris []        String        List of Transport Profile that the returned Endpoints shall support.
3,036✔
514
         *                          OPC 10000-7 defines URIs for the Transport Profiles.
3,036✔
515
         *                          All Endpoints are returned if the list is empty.
3,036✔
516
         *                          If the URI is a URL, this URL may have a query string appended.
3,036✔
517
         *                          The Transport Profiles that support query strings are defined in OPC 10000-7.
3,036✔
518
         */
3,036✔
519
        response.endpoints = this._get_endpoints(null);
3,036✔
520
        const e = response.endpoints.map((e) => e.endpointUrl);
3,036✔
521
        if (request.endpointUrl) {
3,036✔
522
            const filtered = response.endpoints.filter(
3,036✔
523
                (endpoint: EndpointDescription) => endpoint.endpointUrl === request.endpointUrl
3,036✔
524
            );
3,036✔
525
            if (filtered.length > 0) {
3,036✔
526
                response.endpoints = filtered;
2,936✔
527
            }
2,936✔
528
        }
3,036✔
529
        response.endpoints = response.endpoints.filter((endpoint: EndpointDescription) => !(endpoint as any).restricted);
3,036✔
530

3,036✔
531
        // apply filters
3,036✔
532
        if (request.profileUris && request.profileUris.length > 0) {
3,036!
533
            response.endpoints = response.endpoints.filter((endpoint: any) => {
×
534
                return request.profileUris!.indexOf(endpoint.transportProfileUri) >= 0;
×
UNCOV
535
            });
×
UNCOV
536
        }
×
537

3,036✔
538
        // adjust locale on ApplicationName to match requested local or provide
3,036✔
539
        // a string with neutral locale (locale === null)
3,036✔
540
        // TODO: find a better way to handle this
3,036✔
541
        response.endpoints.forEach((endpoint: EndpointDescription) => {
3,036✔
542
            endpoint.server.applicationName.locale = "en-US";
23,566✔
543
        });
3,036✔
544

3,036✔
545
        channel.send_response("MSG", response, message, emptyCallback);
3,036✔
546
    }
3,036✔
547

38✔
548
    /**
38✔
549
     * @private
38✔
550
     */
38✔
551
    protected _on_FindServersRequest(message: Message, channel: ServerSecureChannelLayer): void {
38✔
552
        // Release 1.02  13  OPC Unified Architecture, Part 4 :
30✔
553
        //   This  Service  can be used without security and it is therefore vulnerable to Denial Of Service (DOS)
30✔
554
        //   attacks. A  Server  should minimize the amount of processing required to send the response for this
30✔
555
        //   Service.  This can be achieved by preparing the result in advance.   The  Server  should  also add a
30✔
556
        //   short delay before starting processing of a request during high traffic conditions.
30✔
557

30✔
558
        const shortDelay = 100; // milliseconds
30✔
559
        setTimeout(() => {
30✔
560
            const request = message.request;
30✔
561
            assert(request.schema.name === "FindServersRequest");
30✔
562
            if (!(request instanceof FindServersRequest)) {
30✔
563
                throw new Error("Invalid request type");
×
UNCOV
564
            }
×
565

30✔
566
            let servers = this.getServers(channel);
30✔
567
            // apply filters
30✔
568
            // TODO /
30✔
569
            if (request.serverUris && request.serverUris.length > 0) {
30✔
570
                // A serverUri matches the applicationUri from the ApplicationDescription define
4✔
571
                servers = servers.filter((inner_Server: ApplicationDescription) => {
4✔
572
                    return request.serverUris!.indexOf(inner_Server.applicationUri) >= 0;
4✔
573
                });
4✔
574
            }
4✔
575

30✔
576
            function adapt(applicationDescription: ApplicationDescription): ApplicationDescription {
30✔
577
                return new ApplicationDescription({
48✔
578
                    applicationName: applicationDescription.applicationName,
48✔
579
                    applicationType: applicationDescription.applicationType,
48✔
580
                    applicationUri: applicationDescription.applicationUri,
48✔
581
                    discoveryProfileUri: applicationDescription.discoveryProfileUri,
48✔
582
                    discoveryUrls: applicationDescription.discoveryUrls,
48✔
583
                    gatewayServerUri: applicationDescription.gatewayServerUri,
48✔
584
                    productUri: applicationDescription.productUri
48✔
585
                });
48✔
586
            }
48✔
587

30✔
588
            const response = new FindServersResponse({
30✔
589
                servers: servers.map(adapt)
30✔
590
            });
30✔
591

30✔
592
            channel.send_response("MSG", response, message, emptyCallback);
30✔
593
        }, shortDelay);
30✔
594
    }
30✔
595

38✔
596
    /**
38✔
597
     * returns a array of currently active channels
38✔
598
     */
38✔
599
    protected getChannels(): ServerSecureChannelLayer[] {
38✔
600
        let channels: ServerSecureChannelLayer[] = [];
16✔
601

16✔
602
        for (const endpoint of this.endpoints) {
16✔
603
            const c = endpoint.getChannels();
14✔
604
            channels = channels.concat(c);
14✔
605
        }
14✔
606
        return channels;
16✔
607
    }
16✔
608
}
38✔
609

4✔
610
/**
4✔
611
 * construct a service Fault response
4✔
612
 */
4✔
UNCOV
613
function makeServiceFault(statusCode: StatusCode, messages: string[]): ServiceFault {
×
614
    const response = new ServiceFault();
×
615
    response.responseHeader.serviceResult = statusCode;
×
UNCOV
616
    // xx response.serviceDiagnostics.push( new DiagnosticInfo({ additionalInfo: messages.join("\n")}));
×
UNCOV
617

×
618
    assert(Array.isArray(messages));
×
619
    assert(typeof messages[0] === "string");
×
UNCOV
620

×
621
    response.responseHeader.stringTable = messages;
×
UNCOV
622
    // tslint:disable:no-console
×
623
    warningLog(chalk.cyan(" messages "), messages.join("\n"));
×
624
    return response;
×
UNCOV
625
}
×
626

4✔
627
// tslint:disable:no-var-requires
4✔
628
import { withCallback } from "thenify-ex";
4✔
629
const opts = { multiArgs: false };
4✔
630
OPCUABaseServer.prototype.resumeEndPoints = withCallback(OPCUABaseServer.prototype.resumeEndPoints, opts);
4✔
631
OPCUABaseServer.prototype.suspendEndPoints = withCallback(OPCUABaseServer.prototype.suspendEndPoints, opts);
4✔
632
OPCUABaseServer.prototype.shutdownChannels = withCallback(OPCUABaseServer.prototype.shutdownChannels, opts);
4!
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc